"use client" /** * Record detail for any foundational object. * * The section that justifies the whole console is Used across products. Before * it, every product held its own copy of the roster and none of them knew about * the others, so nobody could answer "if I retire this course or deactivate * this person, what breaks?" The answer has to live next to the record, not in * a report someone runs afterwards. * * Each reference links into the product that owns it, which is a hard product * switch by design (multi-product routing, rule 6): the theme, nav and scope * chrome all have to follow, and pretending otherwise would strand the user in * an Admin shell showing another product's data. */ import * as React from "react" import { Link } from "react-router" import { ListHubStatusBadge } from "@/components/list-hub-status-badge" import { PageHeader } from "@/components/page-header" import { PrimaryPageTemplate } from "@/components/templates/primary-page-template" import { AvatarInitials } from "@/components/ui/avatar" import { Button } from "@/components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { Tip } from "@/components/ui/tip" import { STATUS_BADGE_TONE_CLASS } from "@/lib/list-status-badges" import { ADMIN_SOURCE_ICON, ADMIN_SOURCE_LABEL, ADMIN_STATUS_LABEL, type AdminRecordStatus, type AdminRecordUsage, type AdminSource, } from "@/lib/mock/admin-directory" const STATUS_TINT: Record = { active: STATUS_BADGE_TONE_CLASS.success, invited: STATUS_BADGE_TONE_CLASS.warning, inactive: STATUS_BADGE_TONE_CLASS.neutral, } const STATUS_ICON: Record = { active: "fa-circle-check", invited: "fa-paper-plane", inactive: "fa-circle-minus", } export interface AdminRecordField { label: string value: React.ReactNode /** Render in mono, for workspace IDs and course codes. */ mono?: boolean } export interface AdminRecordDetailProps { /** Hub this record belongs to, for the single way back. */ backTo: { label: string; href: string } title: string /** Line under the title: the record in one phrase. */ subtitle: string /** Shown when the record represents a person. */ initials?: string status: AdminRecordStatus source: AdminSource lastSyncedOn: string fields: AdminRecordField[] usage: AdminRecordUsage[] /** Why this record has no product references, in the record's own terms. */ usageEmptyState: string } function formatDay(iso: string) { return new Date(`${iso}T00:00:00`).toLocaleDateString("en-US", { month: "long", day: "numeric", year: "numeric", }) } export function AdminRecordDetail({ backTo, title, subtitle, initials, status, source, lastSyncedOn, fields, usage, usageEmptyState, }: AdminRecordDetailProps) { return ( {initials ? : null} {title} } subtitle={subtitle} // No back button here: the breadcrumb above already carries the way // back, and P1 allows exactly one. actions={