Column mapper

Pro

Match file columns to the object's fields. Exact name matches, the model's suggestions (with how sure it is) and your own choices are marked apart, so a guess never passes for a match. Required fields left unmapped, and two columns sent to one field, are flagged before anything loads.

Markdown

Preview

Install

With the Candor CLI

First time? Get started sets up the registry and theme. Or with shadcn directly: npx shadcn@latest add @candor/column-mapper

Brings in: data, locale, select, utils · npm: lucide-react@^1.67.0

Usage

TSX · The example above
"use client"

import * as React from "react"
import { ColumnMapper } from "@/components/column-mapper"
import type { ColumnMapping, ImportColumn, TargetField } from "@/lib/data"

const columns: ImportColumn[] = [
  { header: "Flag", samples: ["new-billing-page", "dark-mode", "fast-search"] },
  { header: "Repo", samples: ["acme/web", "acme/web", "acme/api"] },
  { header: "Owner Email", samples: ["dana@acme.example", "jonas@acme", "mei@acme.example"] },
  { header: "Rollout %", samples: ["25", "100", "n/a"] },
  { header: "Notes", samples: ["Remove after Q4", "", "Behind a beta"] },
]

const fields: TargetField[] = [
  { name: "key", label: "Flag key", type: "text", required: true },
  { name: "repository", label: "Repository", type: "choice" },
  { name: "owner_id", label: "Owner", type: "id", required: true },
  { name: "rollout", label: "Rollout", type: "number" },
  { name: "description", label: "Description", type: "text" },
]

// Keel's first pass: one exact match, its own suggestions with confidence, and a column it couldn't place.
const suggested: ColumnMapping[] = [
  { header: "Flag", field: "key", by: "model", confidence: 0.91 },
  { header: "Repo", field: "repository", by: "match" },
  { header: "Owner Email", field: null, by: "model", confidence: 0 },
  { header: "Rollout %", field: "rollout", by: "model", confidence: 0.82 },
  { header: "Notes", field: "description", by: "model", confidence: 0.58 },
]

export default function Example() {
  const [mapping, setMapping] = React.useState(suggested)
  return <ColumnMapper className="max-w-2xl" columns={columns} fields={fields} mapping={mapping} onChange={setMapping} />
}

<ColumnMapper>

PropTypeDefaultDescription
columnsrequiredImportColumn[]—The file's columns, with sample values.
fieldsrequiredTargetField[]—The fields a column can map to; required ones are marked and checked.
mappingrequiredColumnMapping[]—The current match for each column, and who made it: exact name, the model (with confidence) or you.
onChangerequired(mapping: ColumnMapping[]) => void—Called with the whole new mapping when a person changes a column's field.
classNamestring | undefined—Extra classes for the outer element.

Strings

The fixed words, in English by default. Change any of them under columnMapper in <LocaleProvider strings={{ … }}>; the table is exported as columnMapperStrings.

KeyEnglish
mapColumnsMap columns
mapped{mapped} of {total} mapped
suggestedCount{count} suggested
requiredMissingRequired and not mapped: {fields}
noValuesNo values
fieldForField for {column}
dontImportDon’t import
clashAnother column also maps here
suggestedSuggested · {percent}% sure
nameMatchesName matches
yourChoiceYour choice
skippedSkipped