SPB Git
1.9 KB · 11 lines tsx
1"use client";2import { useState } from "react";3import { useRouter } from "next/navigation";4import { api } from "./api";5export function ProjectsPanel({ projects }: { projects: { slug: string; name: string; description: string | null; tools: number }[] }) {6  const r = useRouter(); const [form, setForm] = useState({ slug: "", name: "", description: "" }); const [msg, setMsg] = useState("");7  return <div className="mt-2 grid gap-4 lg:grid-cols-[1fr_20rem]">8    <ul className="grid gap-2 sm:grid-cols-2">{projects.map((p) => <li key={p.slug} className="card flex items-start justify-between gap-2 p-3 text-sm"><div><p className="font-medium">{p.name}</p><p className="text-xs text-fg-3">{p.slug} · {p.tools} tools</p>{p.description && <p className="mt-1 text-xs text-fg-2">{p.description}</p>}</div><button onClick={async () => { if (confirm(`Delete project ${p.name}?`)) { await api("/api/admin/projects", { method: "DELETE", body: JSON.stringify({ slug: p.slug }) }); r.refresh(); } }} className="btn-ghost h-7 px-2 text-xs">Delete</button></li>)}</ul>9    <form className="card space-y-2 p-3" onSubmit={async (e) => { e.preventDefault(); try { await api("/api/admin/projects", { method: "POST", body: JSON.stringify(form) }); setForm({ slug: "", name: "", description: "" }); setMsg("Saved"); r.refresh(); } catch (e) { setMsg((e as Error).message); } }}><h3 className="text-sm font-semibold">Add / update project</h3><input placeholder="slug" value={form.slug} onChange={(e) => setForm({ ...form, slug: e.target.value })} className="input" required pattern="[a-z0-9-]+" /><input placeholder="Name" value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} className="input" required /><textarea placeholder="Description" value={form.description} onChange={(e) => setForm({ ...form, description: e.target.value })} className="input" rows={2} /><button className="btn-ghost w-full">Save</button>{msg && <p className="text-xs text-fg-2">{msg}</p>}</form></div>;10}11
Files · 19c0ff748ee94e0979c734f5fb3afeefc11ee9e1
Menu

Theme