From 5cd74762fe05a380671e28c8a52b9e9026fb1632 Mon Sep 17 00:00:00 2001 From: Dave Date: Wed, 16 Sep 2026 11:29:08 +0200 Subject: [PATCH] feat(admin): projects page seeding a full board on create --- src/app/admin/projects/page.tsx | 59 ++++++++++ src/components/admin/project-form.tsx | 158 ++++++++++++++++++++++++++ 2 files changed, 217 insertions(+) create mode 100644 src/app/admin/projects/page.tsx create mode 100644 src/components/admin/project-form.tsx diff --git a/src/app/admin/projects/page.tsx b/src/app/admin/projects/page.tsx new file mode 100644 index 0000000..9bf3cc2 --- /dev/null +++ b/src/app/admin/projects/page.tsx @@ -0,0 +1,59 @@ +import Link from "next/link"; + +import { AppShell } from "@/components/shell/app-shell"; +import { TopBar } from "@/components/shell/top-bar"; +import { AdminTable, AdminCell } from "@/components/admin/admin-table"; +import { NewProjectForm } from "@/components/admin/project-form"; +import { Pill } from "@/components/ui/pill"; +import { loadClientOptions, loadProjectsAdmin } from "@/lib/data/admin"; + +export default async function AdminProjectsPage() { + const [rows, clients] = await Promise.all([ + loadProjectsAdmin(), + loadClientOptions(), + ]); + + return ( + + + +
+ + + + {rows.map(({ project, client, cardCount }) => ( + + + + {project.name} + + + {client.name} + + {project.repo_owner && project.repo_name ? ( + + {project.repo_owner}/{project.repo_name} + + ) : ( + Not linked + )} + + {cardCount} + + + {project.status} + + + + ))} + +
+
+ ); +} \ No newline at end of file diff --git a/src/components/admin/project-form.tsx b/src/components/admin/project-form.tsx new file mode 100644 index 0000000..ffcacb6 --- /dev/null +++ b/src/components/admin/project-form.tsx @@ -0,0 +1,158 @@ +"use client"; + +import { useState, useTransition } from "react"; +import { Plus } from "lucide-react"; + +import { createProject } from "@/app/actions/projects"; +import type { ClientRecord } from "@/lib/pb/types"; + +export function NewProjectForm({ clients }: { clients: ClientRecord[] }) { + const [open, setOpen] = useState(false); + const [clientId, setClientId] = useState(clients[0]?.id ?? ""); + const [name, setName] = useState(""); + const [description, setDescription] = useState(""); + const [repoOwner, setRepoOwner] = useState(""); + const [repoName, setRepoName] = useState(""); + const [pending, startTransition] = useTransition(); + const [error, setError] = useState(null); + + if (clients.length === 0) { + return ( +

Create a client first.

+ ); + } + + function submit(event: React.FormEvent) { + event.preventDefault(); + if (!name.trim() || !clientId) return; + setError(null); + + startTransition(async () => { + try { + await createProject({ + clientId, + name, + description, + repoOwner, + repoName, + color: "#2f6dad", + }); + setName(""); + setDescription(""); + setRepoOwner(""); + setRepoName(""); + setOpen(false); + } catch (e) { + setError(e instanceof Error ? e.message : "Could not create that project"); + } + }); + } + + if (!open) { + return ( + + ); + } + + const field = + "w-full rounded-lg border border-border-subtle bg-panel px-3 py-2 text-sm text-text-primary outline-none focus:border-brand"; + + return ( +
+
+ + + +
+ +