Victor Mesquita

Uai Blocks Import Workflow

Component

About

File selection, column mapping, validation, preview, and completion.

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Uai Blocks Import Workflow to your project and fits it to your theme.

Code

How Uai Blocks Import Workflow is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

"use client";
import {ThemeProvider as LibraryDemoThemeProvider} from "next-themes";
"use client";

import { useEffect, useState } from "react";
import {
  ImportWorkflow,
  ImportWorkflowAction,
  ImportWorkflowBody,
  ImportWorkflowCell,
  ImportWorkflowDescription,
  ImportWorkflowFooter,
  ImportWorkflowHeader,
  ImportWorkflowHeaderCell,
  ImportWorkflowHeading,
  ImportWorkflowIssues,
  ImportWorkflowMapping,
  ImportWorkflowMappingOption,
  ImportWorkflowMappingRow,
  ImportWorkflowMappingSample,
  ImportWorkflowMappingSource,
  ImportWorkflowMappingTarget,
  ImportWorkflowPanel,
  ImportWorkflowPanelTitle,
  ImportWorkflowProgress,
  ImportWorkflowStep,
  ImportWorkflowSteps,
  ImportWorkflowTable,
  ImportWorkflowTableRow,
  ImportWorkflowTitle,
  ImportWorkflowUpload,
  type ImportWorkflowVariant,
} from "@/components/ui/uai-import-workflow";
import {
  FileUploadDropzone,
  FileUploadInput,
  FileUploadItem,
  FileUploadList,
  FileUploadRemove,
  FileUploadTrigger,
} from "@/components/ui/uai-import-workflow-utils/src-components-ui-uai-file-upload";
import {
  ProgressSummaryBar,
  ProgressSummaryHeader,
  ProgressSummaryStat,
  ProgressSummaryStatLabel,
  ProgressSummaryStats,
  ProgressSummaryStatusText,
  ProgressSummaryStatValue,
  ProgressSummaryTitle,
  ProgressSummaryValue,
} from "@/components/ui/uai-import-workflow-utils/src-components-ui-uai-progress-summary";
import {
  StatusBannerContent,
  StatusBannerDescription,
  StatusBannerIcon,
  StatusBannerTitle,
} from "@/components/ui/uai-import-workflow-utils/src-components-ui-uai-status-banner";
import { StepIndicatorDescription, StepIndicatorTitle } from "@/components/ui/uai-import-workflow-utils/src-components-ui-uai-step-indicator";

const steps = [
  { value: "file", title: "Choose file", description: "CSV up to 10 MB" },
  { value: "mapping", title: "Map columns", description: "Match CRM fields" },
  { value: "review", title: "Review", description: "Fix flagged rows" },
  { value: "import", title: "Import", description: "Write contacts" },
];
const columns = [
  { source: "email_address", sample: "ana@fieldnote.io", target: "email" },
  { source: "full_name", sample: "Ana Duarte", target: "name" },
  { source: "company", sample: "Fieldnote Labs", target: "account" },
  { source: "fax", sample: "+351 21 555 0100", target: "" },
];
const total = 2418;

export function ImportWorkflowPreview({ variant = "wizard" }: { variant?: ImportWorkflowVariant }) {
  const [step, setStep] = useState("file");
  const [file, setFile] = useState<string | null>("contacts-september.csv");
  const [mapping, setMapping] = useState(columns.map((column) => column.target));
  const [imported, setImported] = useState(0);
  const index = steps.findIndex((item) => item.value === step);
  const go = (offset: number) => setStep(steps[index + offset]?.value ?? "file");

  useEffect(() => {
    if (step !== "import") return;
    setImported(0);
    const timer = window.setInterval(() => {
      setImported((value) => Math.min(value + 403, total - 3));
    }, 400);
    return () => window.clearInterval(timer);
  }, [step]);
  const done = imported === total - 3;

  return (
    <ImportWorkflow variant={variant} value={step} onValueChange={setStep}>
      <ImportWorkflowHeader>
        <ImportWorkflowHeading>
          <ImportWorkflowTitle>Import contacts</ImportWorkflowTitle>
          <ImportWorkflowDescription>
            Add people to the Sales workspace from a spreadsheet export.
          </ImportWorkflowDescription>
        </ImportWorkflowHeading>
      </ImportWorkflowHeader>
      <ImportWorkflowBody>
        <ImportWorkflowSteps>
          {steps.map((item, position) => (
            <ImportWorkflowStep
              key={item.value}
              value={item.value}
              status={position < index ? "complete" : "upcoming"}
            >
              <StepIndicatorTitle>{item.title}</StepIndicatorTitle>
              {variant === "compact" ? null : (
                <StepIndicatorDescription>{item.description}</StepIndicatorDescription>
              )}
            </ImportWorkflowStep>
          ))}
        </ImportWorkflowSteps>
        <ImportWorkflowPanel value="file">
          <ImportWorkflowPanelTitle>Choose a CSV file</ImportWorkflowPanelTitle>
          <ImportWorkflowUpload
            accept=".csv"
            maxSize={10_000_000}
            maxFiles={1}
            multiple={false}
            fileCount={file ? 1 : 0}
            onFilesAccepted={(files) => setFile(files[0]?.name ?? null)}
          >
            <FileUploadDropzone>
              <span>Drop a .csv file here, or</span>
              <FileUploadTrigger>Choose file</FileUploadTrigger>
              <FileUploadInput />
            </FileUploadDropzone>
            {file ? (
              <FileUploadList>
                <FileUploadItem status="complete">
                  <strong>{file}</strong>
                  <FileUploadRemove onClick={() => setFile(null)} />
                </FileUploadItem>
              </FileUploadList>
            ) : null}
          </ImportWorkflowUpload>
        </ImportWorkflowPanel>
        <ImportWorkflowPanel value="mapping">
          <ImportWorkflowPanelTitle>Map columns to contact fields</ImportWorkflowPanelTitle>
          <ImportWorkflowMapping>
            {columns.map((column, position) => (
              <ImportWorkflowMappingRow key={column.source}>
                <ImportWorkflowMappingSource>
                  {column.source}
                  <ImportWorkflowMappingSample>{column.sample}</ImportWorkflowMappingSample>
                </ImportWorkflowMappingSource>
                <ImportWorkflowMappingTarget
                  value={mapping[position]}
                  onValueChange={(next) =>
                    setMapping((current) =>
                      current.map((target, item) => (item === position ? next : target)),
                    )
                  }
                >
                  <ImportWorkflowMappingOption value="">
                    Skip this column
                  </ImportWorkflowMappingOption>
                  <ImportWorkflowMappingOption value="email">Email</ImportWorkflowMappingOption>
                  <ImportWorkflowMappingOption value="name">Name</ImportWorkflowMappingOption>
                  <ImportWorkflowMappingOption value="account">Account</ImportWorkflowMappingOption>
                  <ImportWorkflowMappingOption value="phone">Phone</ImportWorkflowMappingOption>
                </ImportWorkflowMappingTarget>
              </ImportWorkflowMappingRow>
            ))}
          </ImportWorkflowMapping>
        </ImportWorkflowPanel>
        <ImportWorkflowPanel value="review">
          <ImportWorkflowPanelTitle>Review before importing</ImportWorkflowPanelTitle>
          <ImportWorkflowIssues tone="warning">
            <StatusBannerIcon />
            <StatusBannerContent>
              <StatusBannerTitle>
                3 of {total.toLocaleString("en-US")} rows will be skipped
              </StatusBannerTitle>
              <StatusBannerDescription>
                Their email addresses are missing or malformed. Everything else is ready.
              </StatusBannerDescription>
            </StatusBannerContent>
          </ImportWorkflowIssues>
          <ImportWorkflowTable aria-label="First rows of contacts-september.csv">
            <thead>
              <tr>
                <ImportWorkflowHeaderCell>Email</ImportWorkflowHeaderCell>
                <ImportWorkflowHeaderCell>Name</ImportWorkflowHeaderCell>
                <ImportWorkflowHeaderCell>Account</ImportWorkflowHeaderCell>
              </tr>
            </thead>
            <tbody>
              <ImportWorkflowTableRow>
                <ImportWorkflowCell>ana@fieldnote.io</ImportWorkflowCell>
                <ImportWorkflowCell>Ana Duarte</ImportWorkflowCell>
                <ImportWorkflowCell>Fieldnote Labs</ImportWorkflowCell>
              </ImportWorkflowTableRow>
              <ImportWorkflowTableRow>
                <ImportWorkflowCell tone="error">
                  jonas.orbit-dental.com · Missing @
                </ImportWorkflowCell>
                <ImportWorkflowCell>Jonas Berg</ImportWorkflowCell>
                <ImportWorkflowCell>Orbit Dental</ImportWorkflowCell>
              </ImportWorkflowTableRow>
              <ImportWorkflowTableRow>
                <ImportWorkflowCell>mei@kestrel.studio</ImportWorkflowCell>
                <ImportWorkflowCell>Mei Tanaka</ImportWorkflowCell>
                <ImportWorkflowCell>Kestrel Studio</ImportWorkflowCell>
              </ImportWorkflowTableRow>
            </tbody>
          </ImportWorkflowTable>
        </ImportWorkflowPanel>
        <ImportWorkflowPanel value="import">
          <ImportWorkflowPanelTitle>
            {done ? "Import complete" : "Importing contacts"}
          </ImportWorkflowPanelTitle>
          <ImportWorkflowProgress
            value={imported}
            max={total - 3}
            status={done ? "complete" : "running"}
          >
            <ProgressSummaryHeader>
              <ProgressSummaryTitle>contacts-september.csv</ProgressSummaryTitle>
              <ProgressSummaryStatusText />
            </ProgressSummaryHeader>
            <ProgressSummaryValue />
            <ProgressSummaryBar />
            <ProgressSummaryStats>
              <ProgressSummaryStat>
                <ProgressSummaryStatLabel>Imported</ProgressSummaryStatLabel>
                <ProgressSummaryStatValue>
                  {imported.toLocaleString("en-US")}
                </ProgressSummaryStatValue>
              </ProgressSummaryStat>
              <ProgressSummaryStat>
                <ProgressSummaryStatLabel>Skipped</ProgressSummaryStatLabel>
                <ProgressSummaryStatValue>3</ProgressSummaryStatValue>
              </ProgressSummaryStat>
            </ProgressSummaryStats>
          </ImportWorkflowProgress>
        </ImportWorkflowPanel>
      </ImportWorkflowBody>
      <ImportWorkflowFooter>
        {index > 0 && step !== "import" ? (
          <ImportWorkflowAction onClick={() => go(-1)}>Back</ImportWorkflowAction>
        ) : null}
        {step === "import" ? (
          <ImportWorkflowAction emphasis="primary" disabled={!done} onClick={() => go(-3)}>
            Import another file
          </ImportWorkflowAction>
        ) : (
          <ImportWorkflowAction emphasis="primary" disabled={!file} onClick={() => go(1)}>
            {step === "review" ? "Import 2,415 contacts" : "Continue"}
          </ImportWorkflowAction>
        )}
      </ImportWorkflowFooter>
    </ImportWorkflow>
  );
}

function SourceSpecimen(){return <ImportWorkflowPreview/>}
export default function LibraryRegistryPreview(){return <LibraryDemoThemeProvider defaultTheme="dark" enableSystem={false}><div data-component-demo data-demo-theme="dark" data-theme="dark" className="dark" style={{background:"var(--background)",color:"var(--foreground)"}}><div className="relative w-full min-w-0 max-w-5xl min-h-[400px] flex items-center justify-center"><SourceSpecimen/></div></div></LibraryDemoThemeProvider>}

Dependencies

More by Victor Mesquita

View all
  • Data Table Toolbar

    Data Table Toolbar

    2
  • Uai Blocks Resource Manager

    Uai Blocks Resource Manager

  • Uai Blocks Response Status

    Uai Blocks Response Status

  • Uai Blocks Data Explorer

    Uai Blocks Data Explorer

  • Uai Blocks Order Status

    Uai Blocks Order Status

  • Uai Blocks CEP Field

    Uai Blocks CEP Field

  • Uai Blocks Community Feed

    Uai Blocks Community Feed

  • Dashboard Shell

    Dashboard Shell