feat(ui): E31 S4a — rail rows are links, the channel page opens on unwatched, users and audit tidied
- Nav rail page rows are real links (/?page=<id>): a middle-click or Ctrl-click opens the page in a new tab; a plain click stays in-app. - A fresh channel page shows the unwatched videos; a picked watch state is a remembered preference (channelShow). - Users: the role, suspend and delete move into the row's one menu (the "…", right-click, Shift+F10); locked ones keep their reason. - Audit log: back-to-back successful runs of one job fold into one row that opens to the earlier ones; a failed run reads in the danger colour. - DESIGN.md decision log: the E31 picks.
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import clsx from "clsx";
|
||||
import { ChevronRight } from "lucide-react";
|
||||
import type { AuditRow } from "../lib/api";
|
||||
import { Button } from "./ui/Button";
|
||||
import { RelativeTime } from "./ui/RelativeTime";
|
||||
|
||||
/** A folded run of one job's successful runs (lib/auditGroups, E31): the row shows the newest, this
|
||||
* opens the earlier ones, each with its moment and its result (`detailOf`, the row's own wording). */
|
||||
export default function AuditEarlierRuns({
|
||||
runs,
|
||||
detailOf,
|
||||
}: {
|
||||
runs: AuditRow[];
|
||||
detailOf: (run: AuditRow) => string | null;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [open, setOpen] = useState(false);
|
||||
const earlier = runs.slice(1);
|
||||
return (
|
||||
<div className="mt-1">
|
||||
<Button
|
||||
variant="link-muted"
|
||||
aria-expanded={open}
|
||||
onClick={() => setOpen(!open)}
|
||||
icon={<ChevronRight className={clsx("transition-transform", open && "rotate-90")} />}
|
||||
>
|
||||
{t("audit.earlierRuns", { count: earlier.length })}
|
||||
</Button>
|
||||
{open && (
|
||||
<ul className="mt-1 space-y-0.5 text-2xs text-muted">
|
||||
{earlier.map((run) => (
|
||||
<li key={run.id} className="flex gap-2">
|
||||
<RelativeTime
|
||||
at={run.created_at}
|
||||
seconds
|
||||
empty="—"
|
||||
className="shrink-0 tabular-nums"
|
||||
/>
|
||||
<span className="min-w-0 wrap-break-word">{detailOf(run)}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user