"use client" /** * Column definitions for the four Exxat One hubs. * * Cells come from `@/components/data-views` rather than being inlined * (`exxat-table-column-cells`), every data column carries a `cellKind` so the * filter bar can infer its control, and record codes are the only thing set in * `font-mono` (`exxat-mono-ids`). */ import * as React from "react" import type { ColumnDef } from "@/components/data-table/types" import { NumericCell, PersonIdentityCell, PillCell, ProgressCell, RowActionsCell, } from "@/components/data-views" import { initialsFromDisplayName } from "@/lib/initials-from-name" import { ListHubStatusBadge } from "@/components/list-hub-status-badge" import { STATUS_BADGE_TONE_CLASS } from "@/lib/list-status-badges" import { ONE_CLEARANCE_LABEL, ONE_REQUEST_LABEL, type OneClearanceState, type OneLocation, type OneRequestState, type OneSlotRequest, } from "@/lib/mock/one-hubs" /* ── Shared bits ──────────────────────────────────────────────────────────── */ const CLEARANCE_TINT: Record = { cleared: STATUS_BADGE_TONE_CLASS.success, "action-needed": STATUS_BADGE_TONE_CLASS.danger, "in-review": STATUS_BADGE_TONE_CLASS.warning, } const CLEARANCE_ICON: Record = { cleared: "fa-circle-check", "action-needed": "fa-triangle-exclamation", "in-review": "fa-clock", } const REQUEST_TINT: Record = { accepted: STATUS_BADGE_TONE_CLASS.success, pending: STATUS_BADGE_TONE_CLASS.warning, declined: STATUS_BADGE_TONE_CLASS.danger, draft: STATUS_BADGE_TONE_CLASS.neutral, } const REQUEST_ICON: Record = { accepted: "fa-circle-check", pending: "fa-clock", declined: "fa-circle-xmark", draft: "fa-pen-line", } function MonoCode({ children }: { children: React.ReactNode }) { return ( {children} ) } function TitleCell({ title, code }: { title: string; code: string }) { return (
{title} {code}
) } /** * Row actions here are illustrative: the hubs read from mock data and none of * these mutations exist yet, so the menu shows the shape of the work without * pretending to perform it. */ const NOT_WIRED = () => {} function selectOptions(values: string[]) { return [...new Set(values.filter(Boolean))].toSorted().map(value => ({ value, label: value })) } /** US short date. Dates are data, not IDs — no mono. */ function formatDay(iso: string) { return new Date(`${iso}T00:00:00`).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric", }) } function DateCell({ iso }: { iso: string }) { return ( {formatDay(iso)} ) } /* ── One — Sites · Locations ──────────────────────────────────────────────── */ export function buildOneLocationColumns(rows: OneLocation[]): ColumnDef[] { return [ { key: "name", label: "Location", width: 220, minWidth: 175, sortable: true, sortKey: "name", defaultPin: "left", cellKind: "text", filter: { type: "text", icon: "fa-font", operators: ["contains", "not_contains"] }, cell: row => , }, { key: "brand", label: "Brand", width: 200, minWidth: 160, sortable: true, sortKey: "brand", cellKind: "pill", filter: { options: selectOptions(rows.map(r => r.brand)) }, cell: row => , }, { key: "specialty", label: "Specialty", width: 145, minWidth: 125, sortable: true, sortKey: "specialty", cellKind: "pill", filter: { options: selectOptions(rows.map(r => r.specialty)) }, cell: row => , }, { key: "city", label: "City", width: 135, minWidth: 118, sortable: true, sortKey: "city", cellKind: "text", filter: { options: selectOptions(rows.map(r => r.city)) }, cell: row => {row.city}, }, { key: "placed", label: "Seats filled", width: 150, minWidth: 130, sortable: true, sortKey: "placed", cellKind: "progress", cell: row => ( ), }, { key: "clearance", label: "Clearance", width: 160, minWidth: 145, sortable: true, sortKey: "clearance", cellKind: "status", filter: { options: (Object.keys(ONE_CLEARANCE_LABEL) as OneClearanceState[]).map(state => ({ value: state, label: ONE_CLEARANCE_LABEL[state], })), }, cell: row => ( ), }, { key: "coordinator", label: "Site coordinator", width: 225, minWidth: 190, sortable: true, sortKey: "coordinator", cellKind: "person", cell: row => ( ), }, { key: "actions", label: "Action", width: 72, minWidth: 72, defaultPin: "right", lockPin: true, cell: row => ( ), }, ] } /* ── One — Sites · Slot requests ──────────────────────────────────────────── */ export function buildOneSlotRequestColumns( rows: OneSlotRequest[], ): ColumnDef[] { return [ { key: "school", label: "School", width: 230, minWidth: 180, sortable: true, sortKey: "school", defaultPin: "left", cellKind: "text", filter: { options: selectOptions(rows.map(r => r.school)) }, cell: row => , }, { key: "program", label: "Program", width: 225, minWidth: 170, sortable: true, sortKey: "program", cellKind: "pill", filter: { options: selectOptions(rows.map(r => r.program)) }, cell: row => , }, { key: "location", label: "Location", width: 235, minWidth: 180, sortable: true, sortKey: "location", cellKind: "text", filter: { options: selectOptions(rows.map(r => r.location)) }, cell: row => {row.location}, }, { key: "rotation", label: "Rotation", width: 170, minWidth: 140, sortable: true, sortKey: "rotation", cellKind: "pill", filter: { options: selectOptions(rows.map(r => r.rotation)) }, cell: row => , }, { key: "seats", label: "Seats", width: 85, minWidth: 70, sortable: true, sortKey: "seats", cellKind: "numeric", cell: row => , }, { key: "startsOn", label: "Starts", width: 130, minWidth: 115, sortable: true, sortKey: "startsOn", cellKind: "date", cell: row => , }, { key: "state", label: "Status", width: 145, minWidth: 125, sortable: true, sortKey: "state", cellKind: "status", filter: { options: (Object.keys(ONE_REQUEST_LABEL) as OneRequestState[]).map(state => ({ value: state, label: ONE_REQUEST_LABEL[state], })), }, cell: row => ( ), }, { key: "actions", label: "Action", width: 72, minWidth: 72, defaultPin: "right", lockPin: true, cell: row => ( ), }, ] } /* ── One — Schools · Explore & apply ──────────────────────────────────────── */