feat(ui): chair approval center — one page for every pending human-gate (#63 follow-up)
Dafna asked for a single page under the prod site listing everything she needs
to approve, so nothing is forgotten — the visible embodiment of INV-G10 (human
gates) and INV-QA1 (halacha backlog must be visible).
Backend — GET /api/chair/pending aggregates every pending chair gate, each as a
direct source query (count + sample + action link):
- halachot review backlog (review_status='pending_review') + oldest
- open missing precedents
- unresolved chair_feedback
- qa_failed cases
- gold-set review (FU-5, file-based, best-effort: total vs source='chair')
Frontend — /approvals page ("מרכז אישורים"):
- src/lib/api/chair.ts — usePendingApprovals() (hand-typed until next api:types)
- src/app/approvals/page.tsx — card per category, severity-coloured count, sample
rows, oldest-pending date, link to where each is handled; live (60s refetch)
- app-shell nav: "מרכז אישורים" in the work group + total-pending badge (quiet at 0)
Live counts at build time surfaced the value immediately: 226 open missing
precedents, 178 pending halachot, 20 unapplied feedback notes, 1 qa_failed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,7 @@ import {
|
||||
import { GlobalSearch } from "@/components/global-search";
|
||||
import { headerSubtitle } from "@/components/header-context";
|
||||
import { useMissingPrecedentsOpenCount } from "@/lib/api/missing-precedents";
|
||||
import { usePendingApprovals } from "@/lib/api/chair";
|
||||
|
||||
/**
|
||||
* Ezer Mishpati navigation shell — two-row header.
|
||||
@@ -39,8 +40,9 @@ const NAV_GROUPS: NavGroup[] = [
|
||||
{
|
||||
id: "work",
|
||||
items: [
|
||||
{ href: "/", label: "בית" },
|
||||
{ href: "/archive", label: "ארכיון" },
|
||||
{ href: "/", label: "בית" },
|
||||
{ href: "/approvals", label: "מרכז אישורים" },
|
||||
{ href: "/archive", label: "ארכיון" },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -244,6 +246,7 @@ function NavLink({ item, active }: { item: NavItem; active: boolean }) {
|
||||
>
|
||||
<span>{item.label}</span>
|
||||
{item.href === "/missing-precedents" ? <MissingPrecedentsBadge /> : null}
|
||||
{item.href === "/approvals" ? <ApprovalsBadge /> : null}
|
||||
{active && (
|
||||
<span
|
||||
className="absolute -bottom-[19px] inset-x-2 h-[2px] bg-gold"
|
||||
@@ -254,6 +257,23 @@ function NavLink({ item, active }: { item: NavItem; active: boolean }) {
|
||||
);
|
||||
}
|
||||
|
||||
/* Total pending-approvals badge next to "מרכז אישורים" — Dafna's outstanding
|
||||
* human-gate items (halachot + missing precedents + feedback + qa_failed).
|
||||
* Renders only when >0 so the nav stays quiet when everything is cleared. */
|
||||
function ApprovalsBadge() {
|
||||
const { data } = usePendingApprovals();
|
||||
const total = data?.total_pending ?? 0;
|
||||
if (!total) return null;
|
||||
return (
|
||||
<span
|
||||
className="ms-1 inline-flex items-center justify-center min-w-[1.25rem] h-4 px-1 rounded-full bg-gold text-navy text-[0.65rem] font-semibold"
|
||||
aria-label={`${total} פריטים ממתינים לאישורך`}
|
||||
>
|
||||
{total}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
/* Small open-count badge next to "פסיקה חסרה" — only renders when >0
|
||||
* so the nav stays quiet in normal operation. */
|
||||
function MissingPrecedentsBadge() {
|
||||
|
||||
Reference in New Issue
Block a user