import Link from "next/link"; import { sql } from "@/db"; import { requireUserPage } from "@/lib/auth/guard"; import { fmtDate } from "@/lib/utils"; import { USE_STATES } from "@/lib/content/schema"; import { ProjectsPanel } from "@/components/admin/projects-panel"; export default async function Owner() { const s = await requireUserPage(); if (s.role !== "owner") return

Owner only.

; const rows = await sql`SELECT t.id, t.slug, t.name, t.summary, t.visibility, t.editorial_status AS "editorialStatus", o.use_state AS "useState", o.rating, o.priority, o.cluster_compat AS "clusterCompat", o.subscription_status AS "subscriptionStatus", o.review_at AS "reviewAt", o.notes, o.deployment_urls AS "deploymentUrls", (SELECT coalesce(array_agg(p.name ORDER BY p.name), '{}') FROM project_tools pt JOIN projects p ON p.id=pt.project_id WHERE pt.tool_id=t.id) AS projects FROM owner_notes o JOIN tools t ON t.id=o.tool_id ORDER BY o.priority='high' DESC, o.priority='medium' DESC, t.name`; const projects = await sql`SELECT p.slug, p.name, p.description, (SELECT count(*)::int FROM project_tools pt WHERE pt.project_id=p.id) AS tools FROM projects p ORDER BY p.name`; const due = rows.filter((r) => r.reviewAt && new Date(r.reviewAt) <= new Date()); return (

Owner dashboard private

Your personal inventory. Nothing on this page is ever served by public routes or the public database role.

{due.length > 0 &&

Review reminders due ({due.length})

}
{USE_STATES.map((st) => { const items = rows.filter((r) => r.useState === st); return (

{st}{items.length}

    {items.map((r) =>
  • {r.name}{r.visibility === "private" && private}{r.rating && {"★".repeat(r.rating)}}

    {r.priority} priority · cluster {r.clusterCompat}{r.subscriptionStatus ? ` · ${r.subscriptionStatus}` : ""}{r.projects.length ? ` · ${r.projects.join(", ")}` : ""}

    {r.notes &&

    {r.notes}

    }{(r.deploymentUrls as any[])?.length > 0 &&

    {(r.deploymentUrls as any[]).map((d) => {d.label})}

    }
  • )}{!items.length &&
  • —
  • }
); })}

Projects

); }