sourcebot/packages/web/src/app/[domain]/settings/connections/page.tsx

77 lines
2.8 KiB
TypeScript
Raw Normal View History

2025-10-28 22:14:02 +00:00
import { sew } from "@/actions";
import { ServiceErrorException } from "@/lib/serviceError";
import { CodeHostType, isServiceError } from "@/lib/utils";
import { withAuthV2 } from "@/withAuthV2";
import Link from "next/link";
import { ConnectionsTable } from "./components/connectionsTable";
2025-10-29 00:21:59 +00:00
import { ConnectionSyncJobStatus } from "@prisma/client";
2025-10-28 22:14:02 +00:00
const DOCS_URL = "https://docs.sourcebot.dev/docs/connections/overview";
export default async function ConnectionsPage() {
2025-10-29 00:21:59 +00:00
const _connections = await getConnectionsWithLatestJob();
if (isServiceError(_connections)) {
throw new ServiceErrorException(_connections);
2025-10-28 22:14:02 +00:00
}
2025-10-29 00:21:59 +00:00
// Sort connections so that first time syncs are at the top.
const connections = _connections
.map((connection) => ({
...connection,
isFirstTimeSync: connection.syncedAt === null && connection.syncJobs.filter((job) => job.status === ConnectionSyncJobStatus.PENDING || job.status === ConnectionSyncJobStatus.IN_PROGRESS).length > 0,
latestJobStatus: connection.syncJobs.length > 0 ? connection.syncJobs[0].status : null,
}))
.sort((a, b) => {
if (a.isFirstTimeSync && !b.isFirstTimeSync) {
return -1;
}
if (!a.isFirstTimeSync && b.isFirstTimeSync) {
return 1;
}
return a.name.localeCompare(b.name);
});
2025-10-28 22:14:02 +00:00
return (
<div className="flex flex-col gap-6">
<div>
<h3 className="text-lg font-medium">Code Host Connections</h3>
<p className="text-sm text-muted-foreground">Manage your connections to external code hosts. <Link href={DOCS_URL} target="_blank" className="text-link hover:underline">Learn more</Link></p>
</div>
<ConnectionsTable data={connections.map((connection) => ({
id: connection.id,
name: connection.name,
codeHostType: connection.connectionType as CodeHostType,
syncedAt: connection.syncedAt,
2025-10-29 00:21:59 +00:00
latestJobStatus: connection.latestJobStatus,
isFirstTimeSync: connection.isFirstTimeSync,
2025-10-28 22:14:02 +00:00
}))} />
</div>
)
}
const getConnectionsWithLatestJob = async () => sew(() =>
2025-10-29 03:25:21 +00:00
withAuthV2(async ({ prisma, org }) => {
2025-10-28 22:14:02 +00:00
const connections = await prisma.connection.findMany({
2025-10-29 03:25:21 +00:00
where: {
orgId: org.id,
},
2025-10-28 22:14:02 +00:00
include: {
2025-10-29 00:21:59 +00:00
_count: {
select: {
syncJobs: true,
}
},
2025-10-28 22:14:02 +00:00
syncJobs: {
orderBy: {
createdAt: 'desc'
},
take: 1
2025-10-29 00:21:59 +00:00
},
2025-10-28 22:14:02 +00:00
},
orderBy: {
name: 'asc'
2025-10-29 00:21:59 +00:00
},
2025-10-28 22:14:02 +00:00
});
2025-10-29 00:21:59 +00:00
2025-10-28 22:14:02 +00:00
return connections;
}));