{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"c-trace-15","type":"registry:block","title":"CSV import","description":"A CSV import that counts up while it runs, with rejected records, their problems and a download link.","dependencies":[],"registryDependencies":["button","card","table","trace"],"files":[{"path":"c-trace-15.tsx","type":"registry:block","content":"\"use client\"\n\nimport * as React from \"react\"\n\nimport { Button } from \"@/components/ui/button\"\nimport { Card, CardContent } from \"@/components/ui/card\"\nimport {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from \"@/components/ui/table\"\nimport {\n  Trace,\n  TraceCollapsible,\n  TraceCollapsibleContent,\n  TraceCollapsibleTrigger,\n  TraceDescription,\n  TraceIcon,\n  TraceLine,\n  TraceTitle,\n} from \"@/components/ui/trace\"\nimport { IconPlaceholder } from \"@/app/(create)/components/icon-placeholder\"\n\nconst ERRORS = [\n  { row: 12, field: \"email\", problem: \"Missing\" },\n  { row: 87, field: \"phone\", problem: \"Not a phone number\" },\n  { row: 233, field: \"email\", problem: \"Invalid domain\" },\n]\n\nconst TOTAL = 2404\n\n// The last step counts up in its description, and rejected rows are a Table\n// because they are scanned by row number.\nexport function Pattern() {\n  const [imported, setImported] = React.useState(0)\n  const done = imported >= TOTAL\n\n  React.useEffect(() => {\n    if (done) return\n    const timer = window.setTimeout(\n      () => setImported((n) => Math.min(n + 143, TOTAL)),\n      120\n    )\n    return () => window.clearTimeout(timer)\n  }, [imported, done])\n\n  return (\n    <div className=\"flex w-full max-w-md flex-col gap-4 text-sm\">\n      <Trace>\n        <TraceLine>\n          <TraceIcon>\n            <IconPlaceholder\n              lucide=\"FileSpreadsheetIcon\"\n              tabler=\"IconFileSpreadsheet\"\n              hugeicons=\"FileSpreadsheetIcon\"\n              phosphor=\"FileXlsIcon\"\n              remixicon=\"RiFileExcel2Line\"\n            />\n          </TraceIcon>\n          <TraceTitle>Parsed contacts-october.csv</TraceTitle>\n          <TraceDescription>2,418 rows</TraceDescription>\n        </TraceLine>\n        <TraceLine>\n          <TraceIcon>\n            <IconPlaceholder\n              lucide=\"ArrowLeftRightIcon\"\n              tabler=\"IconArrowsLeftRight\"\n              hugeicons=\"ArrowDataTransferHorizontalIcon\"\n              phosphor=\"ArrowsLeftRightIcon\"\n              remixicon=\"RiArrowLeftRightLine\"\n            />\n          </TraceIcon>\n          <TraceTitle>Mapped 8 of 9 columns</TraceTitle>\n          <TraceDescription>1 ignored</TraceDescription>\n        </TraceLine>\n        <TraceCollapsible defaultOpen>\n          <TraceCollapsibleTrigger>\n            <TraceLine status=\"error\">\n              <TraceIcon>\n                <IconPlaceholder\n                  lucide=\"CircleAlertIcon\"\n                  tabler=\"IconAlertCircle\"\n                  hugeicons=\"AlertCircleIcon\"\n                  phosphor=\"WarningCircleIcon\"\n                  remixicon=\"RiErrorWarningLine\"\n                />\n              </TraceIcon>\n              <TraceTitle>Rejected 14 invalid rows</TraceTitle>\n            </TraceLine>\n          </TraceCollapsibleTrigger>\n          <TraceCollapsibleContent>\n            <div className=\"flex flex-col gap-2\">\n              {/* A borderless Card is the outer frame; the table sits flush in it. */}\n              <Card className=\"p-0\">\n                <CardContent className=\"p-0\">\n                  <Table>\n                    <TableHeader>\n                      <TableRow>\n                        <TableHead className=\"h-8 w-14\">Row</TableHead>\n                        <TableHead className=\"h-8\">Field</TableHead>\n                        <TableHead className=\"h-8\">Problem</TableHead>\n                      </TableRow>\n                    </TableHeader>\n                    <TableBody>\n                      {ERRORS.map((error) => (\n                        <TableRow key={error.row}>\n                          <TableCell className=\"py-1.5\">{error.row}</TableCell>\n                          <TableCell className=\"py-1.5\">\n                            <code className=\"text-xs\">{error.field}</code>\n                          </TableCell>\n                          <TableCell className=\"py-1.5\">\n                            {error.problem}\n                          </TableCell>\n                        </TableRow>\n                      ))}\n                    </TableBody>\n                  </Table>\n                </CardContent>\n              </Card>\n              <Button\n                variant=\"link\"\n                size=\"xs\"\n                render={<a href=\"#\" />}\n                nativeButton={false}\n                className=\"self-start\"\n              >\n                Download all 14 rows as CSV\n              </Button>\n            </div>\n          </TraceCollapsibleContent>\n        </TraceCollapsible>\n        <TraceLine status={done ? undefined : \"running\"}>\n          <TraceIcon>\n            <IconPlaceholder\n              lucide=\"UsersIcon\"\n              tabler=\"IconUsers\"\n              hugeicons=\"UserMultiple02Icon\"\n              phosphor=\"UsersIcon\"\n              remixicon=\"RiGroupLine\"\n            />\n          </TraceIcon>\n          <TraceTitle>\n            {done ? \"Imported\" : \"Importing\"} {TOTAL.toLocaleString(\"en-US\")}{\" \"}\n            contacts\n          </TraceTitle>\n          {/* Only the end result is announced; every tick would flood a\n                screen reader with numbers. */}\n          <TraceDescription aria-hidden={!done}>\n            {done\n              ? \"in 2.1s\"\n              : `${imported.toLocaleString(\"en-US\")} of ${TOTAL.toLocaleString(\"en-US\")}`}\n          </TraceDescription>\n        </TraceLine>\n      </Trace>\n      <p role=\"status\" className=\"sr-only\">\n        {done ? `Imported ${TOTAL} contacts` : \"\"}\n      </p>\n    </div>\n  )\n}","target":"components/examples/c-trace-15.tsx"}],"meta":{"order":15,"gridSize":2,"label":"CSV Import"}}