Skip to content
63 linesCodeBlameRaw
1import type { DocPage } from "@g1t/contracts";
2import { AlertTriangle } from "lucide-react";
3import { Link, data } from "react-router";
4
5import type { Route } from "./+types/stale";
6import { useDocsData } from "../../../components/docs/actions";
7import { Faces } from "../../../components/docs/parts";
8import { PageIcon } from "../../../components/docs/sidebar";
9import { EmptyState, TimeAgo } from "../../../components/ui";
10import { page } from "../../../lib/meta";
11import { docs } from "../../../lib/services.server";
12import { requireUser, roleIn } from "../../../lib/session.server";
13
14export function meta({ params, ...args }: Route.MetaArgs) {
15 return page(args, { title: `Possibly stale · Docs · ${params.owner} · g1t` });
16}
17
18export async function loader({ params, context, request }: Route.LoaderArgs): Promise<{ pages: DocPage[] | null; repo: string | null }> {
19 const viewer = requireUser(context, request);
20 if (!roleIn(viewer, params.owner)) throw data(null, { status: 404 });
21 const repo = new URL(request.url).searchParams.get("repo") || null;
22 const found = await docs.stalePages(params.owner.toLowerCase(), viewer, { repo }).catch(() => null);
23 return { pages: found?.ok ? found.value : null, repo };
24}
25
26/** Pages whose cited code changed since someone last marked them current, most recently flagged first. */
27export default function DocsStale({ loaderData }: Route.ComponentProps) {
28 const { pages, repo } = loaderData;
29 const layout = useDocsData();
30 const spaces = new Map((layout?.sidebar?.spaces ?? []).map((s) => [s.id, s.name]));
31 return (
32 <div className="mx-auto max-w-3xl">
33 <h1 className="flex items-center gap-2 text-xl font-semibold tracking-tight">
34 <AlertTriangle size={18} className="text-warn" /> Possibly stale{repo ? ` · ${repo}` : ""}
35 </h1>
36 <p className="mt-1 text-sm text-muted">A merged pull request or a push to a default branch changed code these pages cite. Read what changed, update the page (or ask an agent to), then mark it current.</p>
37 <div className="mt-6">
38 {pages === null ? (
39 <EmptyState title="Docs didn't answer">Try again in a moment.</EmptyState>
40 ) : pages.length === 0 ? (
41 <EmptyState title="Everything is current">No page you can read cites code that changed since it was last checked.</EmptyState>
42 ) : (
43 <ul className="divide-y divide-line overflow-hidden rounded-xl border border-line bg-surface">
44 {pages.map((p) => (
45 <li key={p.id}>
46 <Link to={p.path} prefetch="intent" className="flex items-center gap-3 px-4 py-2.5 transition-colors hover:bg-raised">
47 <PageIcon icon={p.icon} />
48 <span className="min-w-0 grow">
49 <span className="block truncate text-sm">{p.title || "Untitled"}</span>
50 <span className="block text-xs text-faint">
51 {spaces.get(p.space_id) ?? p.space_slug} · edited <TimeAgo at={p.updated_at} />
52 </span>
53 </span>
54 {p.owners.length > 0 && <Faces people={p.owners} size={18} />}
55 </Link>
56 </li>
57 ))}
58 </ul>
59 )}
60 </div>
61 </div>
62 );
63}