Adjust validation table, add custom fields

This commit is contained in:
2025-02-19 12:35:24 -05:00
parent fe70b56d24
commit 527dec4d49
2 changed files with 378 additions and 202 deletions

View File

@@ -411,8 +411,9 @@ export const ValidationStep = <T extends string>({ initialData, file, onBack }:
</AlertDialogPortal> </AlertDialogPortal>
</AlertDialog> </AlertDialog>
<div className="flex-1 overflow-hidden"> <div className="flex-1 overflow-hidden">
<div className="px-8 py-6"> <div className="h-full flex flex-col">
<div className="mb-8 flex flex-wrap items-center justify-between gap-2"> <div className="px-8 pt-6">
<div className="mb-6 flex flex-wrap items-center justify-between gap-2">
<h2 className="text-3xl font-semibold text-foreground"> <h2 className="text-3xl font-semibold text-foreground">
{translations.validationStep.title} {translations.validationStep.title}
</h2> </h2>
@@ -436,10 +437,12 @@ export const ValidationStep = <T extends string>({ initialData, file, onBack }:
</div> </div>
</div> </div>
</div> </div>
<div className="rounded-md border overflow-hidden"> </div>
<div className="overflow-x-auto"> <div className="px-8 pb-6 flex-1 min-h-0">
<div className="rounded-md border h-full flex flex-col overflow-hidden">
<div className="flex-1 overflow-auto">
<Table> <Table>
<TableHeader> <TableHeader className="sticky top-0 bg-muted z-10">
{table.getHeaderGroups().map((headerGroup) => ( {table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}> <TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => ( {headerGroup.headers.map((header) => (
@@ -497,7 +500,8 @@ export const ValidationStep = <T extends string>({ initialData, file, onBack }:
</div> </div>
</div> </div>
</div> </div>
<div className="border-t bg-muted px-8 py-4 mt-5"> </div>
<div className="border-t bg-muted px-8 py-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
{onBack && ( {onBack && (
<Button variant="outline" onClick={onBack}> <Button variant="outline" onClick={onBack}>

View File

@@ -8,173 +8,345 @@ import { motion } from "framer-motion";
const IMPORT_FIELDS = [ const IMPORT_FIELDS = [
{ {
label: "Name", label: "Supplier",
key: "name", key: "supplier",
alternateMatches: ["product", "product name", "item name", "title"], description: "Primary supplier/manufacturer of the product",
fieldType: { fieldType: {
type: "input", type: "select",
options: [
{ label: "Acme Corp", value: "acme" },
{ label: "Global Supplies", value: "global" },
{ label: "Best Manufacturers", value: "best" },
{ label: "Quality Goods", value: "quality" },
],
},
width: 200,
validations: [{ rule: "required", errorMessage: "Supplier is required", level: "error" }],
}, },
example: "Widget X",
description: "The name or title of the product",
width: 300,
validations: [
{ {
rule: "required", label: "UPC",
errorMessage: "Name is required", key: "upc",
level: "error", description: "Universal Product Code/Barcode",
}, alternateMatches: ["barcode", "bar code", "JAN", "EAN"],
fieldType: { type: "input" },
width: 150,
validations: [
{ rule: "required", errorMessage: "UPC is required", level: "error" },
{ rule: "unique", errorMessage: "UPC must be unique", level: "error" },
{ rule: "regex", value: "^[0-9]+$", errorMessage: "UPC must be a number", level: "error" },
], ],
}, },
{ {
label: "SKU", label: "Supplier #",
key: "sku", key: "supplier_no",
alternateMatches: ["item number", "product code", "product id", "item id"], description: "Supplier's product identifier",
fieldType: { alternateMatches: ["sku", "item#", "mfg item #", "item"],
type: "input", fieldType: { type: "input" },
},
example: "WX-123",
description: "Unique product identifier",
width: 120, width: 120,
validations: [ validations: [
{ { rule: "required", errorMessage: "Supplier # is required", level: "error" },
rule: "required", { rule: "unique", errorMessage: "Supplier # must be unique", level: "error" },
errorMessage: "SKU is required",
level: "error",
},
{
rule: "unique",
errorMessage: "SKU must be unique",
level: "error",
},
], ],
}, },
{ {
label: "Category", label: "Notions #",
key: "category", key: "notions_no",
alternateMatches: ["product category", "type", "product type"], description: "Internal notions number",
fieldType: { type: "input" },
width: 120,
validations: [
{ rule: "required", errorMessage: "Notions # is required", level: "error" },
{ rule: "unique", errorMessage: "Notions # must be unique", level: "error" },
{ rule: "regex", value: "^[0-9]+$", errorMessage: "Notions # must be a number", level: "error" },
],
},
{
label: "Name",
key: "name",
description: "Product name/title",
alternateMatches: ["sku description"],
fieldType: { type: "input" },
width: 300,
validations: [
{ rule: "required", errorMessage: "Name is required", level: "error" },
{ rule: "unique", errorMessage: "Name must be unique", level: "error" },
],
},
{
label: "Item Number",
key: "item_number",
description: "Internal item reference number",
fieldType: { type: "input" },
width: 120,
validations: [
{ rule: "required", errorMessage: "Item Number is required", level: "error" },
{ rule: "unique", errorMessage: "Item Number must be unique", level: "error" },
],
},
{
label: "Image URL",
key: "image_url",
description: "Product image URL(s)",
fieldType: { type: "input" },
width: 250,
},
{
label: "MSRP",
key: "msrp",
description: "Manufacturer's Suggested Retail Price",
alternateMatches: ["retail", "retail price", "sugg retail", "price", "sugg. Retail"],
fieldType: { type: "input" },
width: 100,
validations: [
{ rule: "required", errorMessage: "MSRP is required", level: "error" },
{ rule: "regex", value: "^[0-9]*.?[0-9]+$", errorMessage: "MSRP must be a number", level: "error" },
],
},
{
label: "Qty Per Unit",
key: "qty_per_unit",
description: "Quantity of items per individual unit",
alternateMatches: ["inner pack", "inner", "min qty", "unit qty", "min. order qty"],
fieldType: { type: "input" },
width: 100,
validations: [
{ rule: "required", errorMessage: "Qty Per Unit is required", level: "error" },
{ rule: "regex", value: "^[0-9]+$", errorMessage: "Qty Per Unit must be a number", level: "error" },
],
},
{
label: "Cost Each",
key: "cost_each",
description: "Wholesale cost per unit",
alternateMatches: ["wholesale", "wholesale price"],
fieldType: { type: "input" },
width: 100,
validations: [
{ rule: "required", errorMessage: "Cost Each is required", level: "error" },
{ rule: "regex", value: "^[0-9]*.?[0-9]+$", errorMessage: "Cost Each must be a number", level: "error" },
],
},
{
label: "Case Pack",
key: "case_qty",
description: "Number of units per case",
alternateMatches: ["mc qty"],
fieldType: { type: "input" },
width: 100,
validations: [
{ rule: "regex", value: "^[0-9]+$", errorMessage: "Case Pack must be a number", level: "error" },
],
},
{
label: "Tax Category",
key: "tax_cat",
description: "Product tax category",
fieldType: { fieldType: {
type: "select", type: "select",
options: [ options: [
{ label: "Electronics", value: "electronics" }, { label: "Standard", value: "standard" },
{ label: "Clothing", value: "clothing" }, { label: "Reduced", value: "reduced" },
{ label: "Food & Beverage", value: "food_beverage" }, { label: "Zero", value: "zero" },
{ label: "Office Supplies", value: "office_supplies" }, { label: "Exempt", value: "exempt" },
{ label: "Other", value: "other" },
], ],
}, },
width: 150, width: 150,
validations: [ validations: [{ rule: "required", errorMessage: "Tax Category is required", level: "error" }],
{
rule: "required",
errorMessage: "Category is required",
level: "error",
},
],
example: "Electronics",
description: "Product category",
}, },
{ {
label: "Quantity", label: "Company",
key: "quantity", key: "company",
alternateMatches: ["qty", "stock", "amount", "inventory", "stock level"], description: "Company/Brand name",
fieldType: { fieldType: { type: "input" },
type: "input", width: 200,
validations: [{ rule: "required", errorMessage: "Company is required", level: "error" }],
}, },
example: "100", {
description: "Current stock quantity", label: "Line",
key: "line",
description: "Product line",
fieldType: { type: "input" },
width: 150,
},
{
label: "Sub Line",
key: "subline",
description: "Product sub-line",
fieldType: { type: "input" },
width: 150,
},
{
label: "Artist",
key: "artist",
description: "Artist/Designer name",
fieldType: { type: "input" },
width: 200,
},
{
label: "ETA Date",
key: "eta",
description: "Estimated arrival date",
alternateMatches: ["shipping month"],
fieldType: { type: "input" },
width: 120,
},
{
label: "Weight",
key: "weight",
description: "Product weight (in lbs)",
fieldType: { type: "input" },
width: 100, width: 100,
validations: [ validations: [
{ { rule: "required", errorMessage: "Weight is required", level: "error" },
rule: "required", { rule: "regex", value: "^[0-9]*.?[0-9]+$", errorMessage: "Weight must be a number", level: "error" },
errorMessage: "Quantity is required",
level: "error",
},
{
rule: "regex",
value: "^[0-9]+$",
errorMessage: "Quantity must be a positive number",
level: "error",
},
], ],
}, },
{ {
label: "Price", label: "Length",
key: "price", key: "length",
alternateMatches: ["unit price", "cost", "selling price", "retail price"], description: "Product length (in inches)",
fieldType: { fieldType: { type: "input" },
type: "input",
},
example: "29.99",
description: "Selling price per unit",
width: 100, width: 100,
validations: [ validations: [
{ { rule: "required", errorMessage: "Length is required", level: "error" },
rule: "required", { rule: "regex", value: "^[0-9]*.?[0-9]+$", errorMessage: "Length must be a number", level: "error" },
errorMessage: "Price is required",
level: "error",
},
{
rule: "regex",
value: "^\\d*\\.?\\d+$",
errorMessage: "Price must be a valid number",
level: "error",
},
], ],
}, },
{ {
label: "In Stock", label: "Width",
key: "inStock", key: "width",
alternateMatches: ["available", "active", "status"], description: "Product width (in inches)",
fieldType: { fieldType: { type: "input" },
type: "checkbox",
booleanMatches: {
yes: true,
no: false,
"in stock": true,
"out of stock": false,
available: true,
unavailable: false,
},
},
width: 80,
example: "Yes",
description: "Whether the item is currently in stock",
},
{
label: "Minimum Stock",
key: "minStock",
alternateMatches: ["min qty", "reorder point", "low stock level"],
fieldType: {
type: "input",
},
example: "10",
description: "Minimum stock level before reorder",
width: 100, width: 100,
validations: [ validations: [
{ { rule: "required", errorMessage: "Width is required", level: "error" },
rule: "regex", { rule: "regex", value: "^[0-9]*.?[0-9]+$", errorMessage: "Width must be a number", level: "error" },
value: "^[0-9]+$",
errorMessage: "Minimum stock must be a positive number",
level: "error",
},
], ],
}, },
{ {
label: "Location", label: "Height",
key: "location", key: "height",
alternateMatches: ["storage location", "warehouse", "shelf", "bin"], description: "Product height (in inches)",
fieldType: { type: "input" },
width: 100,
validations: [
{ rule: "required", errorMessage: "Height is required", level: "error" },
{ rule: "regex", value: "^[0-9]*.?[0-9]+$", errorMessage: "Height must be a number", level: "error" },
],
},
{
label: "Shipping Restrictions",
key: "ship_restrictions",
description: "Product shipping restrictions",
fieldType: { fieldType: {
type: "select", type: "select",
options: [ options: [
{ label: "Warehouse A", value: "warehouse_a" }, { label: "None", value: "none" },
{ label: "Warehouse B", value: "warehouse_b" }, { label: "Hazmat", value: "hazmat" },
{ label: "Store Front", value: "store_front" }, { label: "Oversize", value: "oversize" },
{ label: "External Storage", value: "external" }, { label: "Restricted", value: "restricted" },
],
},
width: 150,
validations: [{ rule: "required", errorMessage: "Shipping Restrictions is required", level: "error" }],
},
{
label: "Country Of Origin",
key: "coo",
description: "2-letter country code (ISO)",
alternateMatches: ["coo"],
fieldType: { type: "input" },
width: 100,
validations: [
{ rule: "regex", value: "^[A-Z]{2}$", errorMessage: "Country code must be 2 letters", level: "error" },
],
},
{
label: "HTS Code",
key: "hts_code",
description: "Harmonized Tariff Schedule code",
alternateMatches: ["taric"],
fieldType: { type: "input" },
width: 120,
validations: [
{ rule: "regex", value: "^[0-9]+$", errorMessage: "HTS Code must be a number", level: "error" },
],
},
{
label: "Size Category",
key: "size_cat",
description: "Product size category",
fieldType: {
type: "select",
options: [
{ label: "Small", value: "small" },
{ label: "Medium", value: "medium" },
{ label: "Large", value: "large" },
{ label: "Extra Large", value: "xl" },
],
},
width: 150,
},
{
label: "Description",
key: "description",
description: "Detailed product description",
fieldType: { type: "input" },
width: 400,
validations: [{ rule: "required", errorMessage: "Description is required", level: "error" }],
},
{
label: "Private Notes",
key: "priv_notes",
description: "Internal notes about the product",
fieldType: { type: "input" },
width: 300,
},
{
label: "Categories",
key: "categories",
description: "Product categories",
fieldType: {
type: "select",
options: [
{ label: "Art Supplies", value: "art" },
{ label: "Crafts", value: "crafts" },
{ label: "Home Decor", value: "home" },
{ label: "Stationery", value: "stationery" },
],
},
width: 200,
validations: [{ rule: "required", errorMessage: "Categories is required", level: "error" }],
},
{
label: "Themes",
key: "themes",
description: "Product themes/styles",
fieldType: {
type: "select",
options: [
{ label: "Modern", value: "modern" },
{ label: "Vintage", value: "vintage" },
{ label: "Nature", value: "nature" },
{ label: "Abstract", value: "abstract" },
],
},
width: 200,
},
{
label: "Colors",
key: "colors",
description: "Product colors",
fieldType: {
type: "select",
options: [
{ label: "Red", value: "red" },
{ label: "Blue", value: "blue" },
{ label: "Green", value: "green" },
{ label: "Multi", value: "multi" },
], ],
}, },
width: 150, width: 150,
example: "Warehouse A",
description: "Storage location of the product",
}, },
]; ];