diff --git a/apps/dashboard/app/(app)/projects/_components/create-project/create-project-dialog.tsx b/apps/dashboard/app/(app)/projects/_components/create-project/create-project-dialog.tsx index 9f2abffe54..8efa472b85 100644 --- a/apps/dashboard/app/(app)/projects/_components/create-project/create-project-dialog.tsx +++ b/apps/dashboard/app/(app)/projects/_components/create-project/create-project-dialog.tsx @@ -42,6 +42,12 @@ export const CreateProjectDialog = () => { liveDeploymentId: null, updatedAt: null, id: "will-be-replace-by-server", + author: "will-be-replace-by-server", + branch: "will-be-replace-by-server", + commitTimestamp: Date.now(), + commitTitle: "will-be-replace-by-server", + domain: "will-be-replace-by-server", + regions: [], }); await tx.isPersisted.promise; diff --git a/apps/dashboard/app/(app)/projects/_components/list/index.tsx b/apps/dashboard/app/(app)/projects/_components/list/index.tsx index cd6562aff6..b4ee4ef028 100644 --- a/apps/dashboard/app/(app)/projects/_components/list/index.tsx +++ b/apps/dashboard/app/(app)/projects/_components/list/index.tsx @@ -1,4 +1,4 @@ -import { collection, collectionManager } from "@/lib/collections"; +import { collection } from "@/lib/collections"; import { ilike, useLiveQuery } from "@tanstack/react-db"; import { BookBookmark, Dots } from "@unkey/icons"; import { Button, Empty } from "@unkey/ui"; @@ -17,32 +17,11 @@ export const ProjectsList = () => { (q) => q .from({ project: collection.projects }) - .orderBy(({ project }) => project.updatedAt, "desc") .where(({ project }) => ilike(project.name, `%${projectName}%`)), [projectName], ); - // Get deployments and domains for each project - const deploymentQueries = projects.data.map((project) => { - const collections = collectionManager.getProjectCollections(project.id); - return useLiveQuery((q) => q.from({ deployment: collections.deployments }), [project.id]); - }); - - const domainQueries = projects.data.map((project) => { - const collections = collectionManager.getProjectCollections(project.id); - return useLiveQuery((q) => q.from({ domain: collections.domains }), [project.id]); - }); - - // Flatten the results - const allDeployments = deploymentQueries.flatMap((query) => query.data || []); - const allDomains = domainQueries.flatMap((query) => query.data || []); - - const isLoading = - projects.isLoading || - deploymentQueries.some((q) => q.isLoading) || - domainQueries.some((q) => q.isLoading); - - if (isLoading) { + if (projects.isLoading) { return (
{ No Projects Found - There are no projects configured yet. Create your first project to start deploying and - managing your applications. + {projectName + ? `No projects found matching "${projectName}". Try a different search term.` + : "There are no projects configured yet. Create your first project to start deploying and managing your applications."} { } return ( - <> -
-
- {projects.data.map((project) => { - // Find active deployment and associated domain for this project - const activeDeployment = project.liveDeploymentId - ? allDeployments.find((d) => d.id === project.liveDeploymentId) - : null; - - // Find domain for this project - const projectDomain = allDomains.find((d) => d.projectId === project.id); - - // Extract deployment regions for display - const regions = activeDeployment?.runtimeConfig?.regions?.map((r) => r.region) ?? []; - - return ( - 0 ? regions : ["No deployments"]} - repository={project.gitRepositoryUrl || undefined} - actions={ - - - - } - /> - ); - })} -
+
+
+ {projects.data.map((project) => ( + + + + } + /> + ))}
- +
); }; diff --git a/apps/dashboard/app/(app)/projects/_components/list/projects-card.tsx b/apps/dashboard/app/(app)/projects/_components/list/projects-card.tsx index 9cb0edb8c0..15a80fd4fe 100644 --- a/apps/dashboard/app/(app)/projects/_components/list/projects-card.tsx +++ b/apps/dashboard/app/(app)/projects/_components/list/projects-card.tsx @@ -9,7 +9,7 @@ type ProjectCardProps = { name: string; domain: string; commitTitle: string; - commitTimestamp?: number; + commitTimestamp?: number | null; branch: string; author: string; regions: string[]; diff --git a/apps/dashboard/lib/collections/deploy/projects.ts b/apps/dashboard/lib/collections/deploy/projects.ts index ee5a22ec24..8ff351c45d 100644 --- a/apps/dashboard/lib/collections/deploy/projects.ts +++ b/apps/dashboard/lib/collections/deploy/projects.ts @@ -11,6 +11,14 @@ const schema = z.object({ gitRepositoryUrl: z.string().nullable(), updatedAt: z.number().int().nullable(), liveDeploymentId: z.string().nullable(), + // Flattened deployment fields for UI + commitTitle: z.string(), + branch: z.string(), + author: z.string(), + commitTimestamp: z.number().int().nullable(), + regions: z.array(z.string()), + // Domain field + domain: z.string(), }); export const createProjectRequestSchema = z.object({ diff --git a/apps/dashboard/lib/trpc/routers/deploy/project/list.ts b/apps/dashboard/lib/trpc/routers/deploy/project/list.ts index 5c279b5acd..b465ba1eed 100644 --- a/apps/dashboard/lib/trpc/routers/deploy/project/list.ts +++ b/apps/dashboard/lib/trpc/routers/deploy/project/list.ts @@ -1,30 +1,68 @@ -import { db } from "@/lib/db"; +import type { Deployment } from "@/lib/collections/deploy/deployments"; +import type { Project } from "@/lib/collections/deploy/projects"; +import { db, sql } from "@/lib/db"; import { ratelimit, requireUser, requireWorkspace, t, withRatelimit } from "@/lib/trpc/trpc"; -import { TRPCError } from "@trpc/server"; +import { deployments, domains, projects } from "@unkey/db/src/schema"; + +type ProjectRow = { + id: string; + name: string; + slug: string; + updated_at: number | null; + git_repository_url: string | null; + live_deployment_id: string | null; + git_commit_message: string | null; + git_branch: string | null; + git_commit_author_name: string | null; + git_commit_timestamp: number | null; + runtime_config: Deployment["runtimeConfig"] | null; + domain: string | null; +}; export const listProjects = t.procedure .use(requireUser) .use(requireWorkspace) .use(withRatelimit(ratelimit.read)) .query(async ({ ctx }) => { - return await db.query.projects - .findMany({ - where: (table, { eq }) => eq(table.workspaceId, ctx.workspace.id), - columns: { - id: true, - name: true, - slug: true, - updatedAt: true, - gitRepositoryUrl: true, - liveDeploymentId: true, - }, - }) - .catch((error) => { - console.error("Error querying projects:", error); - throw new TRPCError({ - code: "INTERNAL_SERVER_ERROR", - message: - "Failed to retrieve projects due to an error. If this issue persists, please contact support.", - }); - }); + const result = await db.execute(sql` + SELECT + ${projects.id}, + ${projects.name}, + ${projects.slug}, + ${projects.updatedAt}, + ${projects.gitRepositoryUrl}, + ${projects.liveDeploymentId}, + ${deployments.gitCommitMessage}, + ${deployments.gitBranch}, + ${deployments.gitCommitAuthorName}, + ${deployments.gitCommitTimestamp}, + ${deployments.runtimeConfig}, + ${domains.domain} + FROM ${projects} + LEFT JOIN ${deployments} + ON ${projects.liveDeploymentId} = ${deployments.id} + AND ${deployments.workspaceId} = ${ctx.workspace.id} + LEFT JOIN ${domains} + ON ${projects.id} = ${domains.projectId} + AND ${domains.workspaceId} = ${ctx.workspace.id} + WHERE ${projects.workspaceId} = ${ctx.workspace.id} + ORDER BY ${projects.updatedAt} DESC + `); + + return (result.rows as ProjectRow[]).map( + (row): Project => ({ + id: row.id, + name: row.name, + slug: row.slug, + updatedAt: row.updated_at, + gitRepositoryUrl: row.git_repository_url, + liveDeploymentId: row.live_deployment_id, + commitTitle: row.git_commit_message ?? "[DUMMY] Initial commit", + branch: row.git_branch ?? "main", + author: row.git_commit_author_name ?? "[DUMMY] Unknown Author", + commitTimestamp: row.git_commit_timestamp ?? Date.now() - 86400000, + regions: row.runtime_config?.regions?.map((r) => r.region) ?? ["us-east-1"], + domain: row.domain ?? "project-temp.unkey.app", + }), + ); });