"use client" /** * Import roster — the primary action on every Admin hub. * * A sheet rather than a route because the hub behind it is the context: the * super admin is looking at what is already there while deciding what to bring * in, and closing the panel has to leave that untouched * (`exxat-drawer-vs-dialog`). It is also reversible, which rules out a dialog. * * Scope: this queues an import and reports what will happen. The column mapping * editor and the row level exception review are the next two screens, and both * need a real feed behind them to be worth building. */ import * as React from "react" import { devLog } from "@/lib/dev-log" import { FloatingSheetPanel, FloatingSheetPanelBody, FloatingSheetPanelContent, FloatingSheetPanelHeader, FloatingSheetPanelToolbar, FloatingSheetPanelWorkflowFooter, } from "@/lib/floating-sheet-panel" import { cn } from "@/lib/utils" import { Field, FieldDescription, FieldLabel } from "@/components/ui/field" type ImportSource = "file" | "feed" const SOURCES: { id: ImportSource; label: string; description: string; icon: string }[] = [ { id: "file", label: "Upload a file", description: "A CSV or Excel export from your student or HR system.", icon: "fa-file-arrow-up", }, { id: "feed", label: "Run the connected feed now", description: "Pulls the same data the nightly sync collects, ahead of schedule.", icon: "fa-database", }, ] export interface AdminImportSheetProps { open: boolean onOpenChange: (open: boolean) => void /** Plural object name in the hub's own words, e.g. "people". */ objectLabel: string /** Called with a one sentence summary the hub renders as a banner. */ onQueued: (summary: string) => void } export function AdminImportSheet({ open, onOpenChange, objectLabel, onQueued, }: AdminImportSheetProps) { const [source, setSource] = React.useState("file") const [fileName, setFileName] = React.useState(null) const ready = source === "feed" || fileName !== null function reset() { setSource("file") setFileName(null) } function handleOpenChange(next: boolean) { if (!next) reset() onOpenChange(next) } function handleQueue() { if (!ready) return devLog("Queue admin import:", { objectLabel, source, fileName }) onQueued( source === "feed" ? `The connected feed is running now. New and changed ${objectLabel} will appear here within a few minutes.` : `${fileName} is queued. Rows that cannot be matched to a program will be held for your review rather than skipped.`, ) handleOpenChange(false) } return (
Where from {SOURCES.map(option => { const selected = source === option.id return (