Match columns tweaks
This commit is contained in:
@@ -19,13 +19,21 @@ import {
|
||||
import { useQuery } from "@tanstack/react-query"
|
||||
import config from "@/config"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { CheckCircle2, AlertCircle, EyeIcon, EyeOffIcon, ArrowRightIcon, XIcon, FileSpreadsheetIcon, LinkIcon, FileIcon } from "lucide-react"
|
||||
import { CheckCircle2, AlertCircle, EyeIcon, EyeOffIcon, ArrowRightIcon, XIcon, FileSpreadsheetIcon, LinkIcon, FileIcon, CheckIcon, ChevronsUpDown } from "lucide-react"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { ScrollArea } from "@/components/ui/scroll-area"
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
|
||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from "@/components/ui/command"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
export type MatchColumnsProps<T extends string> = {
|
||||
data: RawData[]
|
||||
@@ -166,15 +174,12 @@ const MemoizedColumnSamplePreview = React.memo(({ samples }: { samples: any[] })
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent side="right" align="start" className="w-[250px] p-0">
|
||||
<div className="p-3 border-b">
|
||||
<h4 className="text-sm font-medium">Sample Data</h4>
|
||||
</div>
|
||||
<ScrollArea className="h-[200px] overflow-auto">
|
||||
<ScrollArea className="h-[200px] overflow-y-auto">
|
||||
<div className="p-3 space-y-2">
|
||||
{samples.map((sample, i) => (
|
||||
<div key={i} className="text-sm">
|
||||
<span className="text-muted-foreground text-xs mr-2">{i + 1}:</span>
|
||||
<span className="font-medium">{String(sample || '(empty)')}</span>
|
||||
{i < samples.length - 1 && <Separator className="w-full my-2" />}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -264,6 +269,353 @@ const ValueMappings = memo(({
|
||||
);
|
||||
});
|
||||
|
||||
// Add these new components before the MatchColumnsStep component
|
||||
const SupplierSelector = React.memo(({
|
||||
value,
|
||||
onChange,
|
||||
suppliers
|
||||
}: {
|
||||
value?: string;
|
||||
onChange: (value: string) => void;
|
||||
suppliers: any[]
|
||||
}) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const handleCommandListWheel = (e: React.WheelEvent) => {
|
||||
e.currentTarget.scrollTop += e.deltaY;
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
className="w-full justify-between"
|
||||
>
|
||||
{value
|
||||
? suppliers?.find((supplier: any) =>
|
||||
supplier.value === value)?.label || "Select supplier..."
|
||||
: "Select supplier..."}
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[var(--radix-popover-trigger-width)] p-0">
|
||||
<Command>
|
||||
<CommandInput placeholder="Search suppliers..." />
|
||||
<CommandList className="max-h-[200px] overflow-y-auto" onWheel={handleCommandListWheel}>
|
||||
<CommandEmpty>No suppliers found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{suppliers?.map((supplier: any) => (
|
||||
<CommandItem
|
||||
key={supplier.value}
|
||||
value={supplier.label}
|
||||
onSelect={() => {
|
||||
onChange(supplier.value);
|
||||
setOpen(false); // Close popover after selection
|
||||
}}
|
||||
>
|
||||
{supplier.label}
|
||||
<CheckIcon
|
||||
className={cn(
|
||||
"ml-auto h-4 w-4",
|
||||
value === supplier.value
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
)}
|
||||
/>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
});
|
||||
|
||||
const CompanySelector = React.memo(({
|
||||
value,
|
||||
onChange,
|
||||
companies
|
||||
}: {
|
||||
value?: string;
|
||||
onChange: (value: string) => void;
|
||||
companies: any[]
|
||||
}) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const handleCommandListWheel = (e: React.WheelEvent) => {
|
||||
e.currentTarget.scrollTop += e.deltaY;
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
className="w-full justify-between"
|
||||
>
|
||||
{value
|
||||
? companies?.find((company: any) =>
|
||||
company.value === value)?.label || "Select company..."
|
||||
: "Select company..."}
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[var(--radix-popover-trigger-width)] p-0">
|
||||
<Command>
|
||||
<CommandInput placeholder="Search companies..." />
|
||||
<CommandList className="max-h-[200px] overflow-y-auto" onWheel={handleCommandListWheel}>
|
||||
<CommandEmpty>No companies found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{companies?.map((company: any) => (
|
||||
<CommandItem
|
||||
key={company.value}
|
||||
value={company.label}
|
||||
onSelect={() => {
|
||||
onChange(company.value);
|
||||
setOpen(false); // Close popover after selection
|
||||
}}
|
||||
>
|
||||
{company.label}
|
||||
<CheckIcon
|
||||
className={cn(
|
||||
"ml-auto h-4 w-4",
|
||||
value === company.value
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
)}
|
||||
/>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
});
|
||||
|
||||
const LineSelector = React.memo(({
|
||||
value,
|
||||
onChange,
|
||||
lines,
|
||||
disabled
|
||||
}: {
|
||||
value?: string;
|
||||
onChange: (value: string) => void;
|
||||
lines: any[];
|
||||
disabled: boolean;
|
||||
}) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const handleCommandListWheel = (e: React.WheelEvent) => {
|
||||
e.currentTarget.scrollTop += e.deltaY;
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
className="w-full justify-between"
|
||||
disabled={disabled}
|
||||
>
|
||||
{value
|
||||
? lines?.find((line: any) =>
|
||||
line.value === value)?.label || "Select line..."
|
||||
: "Select line..."}
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[var(--radix-popover-trigger-width)] p-0">
|
||||
<Command>
|
||||
<CommandInput placeholder="Search lines..." />
|
||||
<CommandList className="max-h-[200px] overflow-y-auto" onWheel={handleCommandListWheel}>
|
||||
<CommandEmpty>No lines found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{lines?.map((line: any) => (
|
||||
<CommandItem
|
||||
key={line.value}
|
||||
value={line.label}
|
||||
onSelect={() => {
|
||||
onChange(line.value);
|
||||
setOpen(false); // Close popover after selection
|
||||
}}
|
||||
>
|
||||
{line.label}
|
||||
<CheckIcon
|
||||
className={cn(
|
||||
"ml-auto h-4 w-4",
|
||||
value === line.value
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
)}
|
||||
/>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
});
|
||||
|
||||
const SubLineSelector = React.memo(({
|
||||
value,
|
||||
onChange,
|
||||
sublines,
|
||||
disabled
|
||||
}: {
|
||||
value?: string;
|
||||
onChange: (value: string) => void;
|
||||
sublines: any[];
|
||||
disabled: boolean;
|
||||
}) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const handleCommandListWheel = (e: React.WheelEvent) => {
|
||||
e.currentTarget.scrollTop += e.deltaY;
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
className="w-full justify-between"
|
||||
disabled={disabled}
|
||||
>
|
||||
{value
|
||||
? sublines?.find((subline: any) =>
|
||||
subline.value === value)?.label || "Select sub line..."
|
||||
: "Select sub line..."}
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[var(--radix-popover-trigger-width)] p-0">
|
||||
<Command>
|
||||
<CommandInput placeholder="Search sub lines..." />
|
||||
<CommandList className="max-h-[200px] overflow-y-auto" onWheel={handleCommandListWheel}>
|
||||
<CommandEmpty>No sub lines found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{sublines?.map((subline: any) => (
|
||||
<CommandItem
|
||||
key={subline.value}
|
||||
value={subline.label}
|
||||
onSelect={() => {
|
||||
onChange(subline.value);
|
||||
setOpen(false); // Close popover after selection
|
||||
}}
|
||||
>
|
||||
{subline.label}
|
||||
<CheckIcon
|
||||
className={cn(
|
||||
"ml-auto h-4 w-4",
|
||||
value === subline.value
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
)}
|
||||
/>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
});
|
||||
|
||||
// Add this new component before the MatchColumnsStep component
|
||||
const FieldSelector = React.memo(({
|
||||
column,
|
||||
isUnmapped = false,
|
||||
fieldCategories,
|
||||
allFields,
|
||||
onChange,
|
||||
isFieldMappedToOtherColumn,
|
||||
handleCommandListWheel
|
||||
}: {
|
||||
column: any;
|
||||
isUnmapped?: boolean;
|
||||
fieldCategories: any[];
|
||||
allFields: any[];
|
||||
onChange: (value: string) => void;
|
||||
isFieldMappedToOtherColumn: (fieldKey: string, currentColumnIndex: number) => { isMapped: boolean, columnHeader?: string };
|
||||
handleCommandListWheel: (e: React.WheelEvent) => void;
|
||||
}) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
// For ignored columns, show a badge
|
||||
if (column.type === ColumnType.ignored) {
|
||||
return <Badge variant="outline">Ignored</Badge>;
|
||||
}
|
||||
|
||||
// Get the current value if this is a mapped column
|
||||
const currentValue = "value" in column ? column.value as string : undefined;
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
className="w-full justify-between"
|
||||
>
|
||||
{currentValue
|
||||
? allFields.find(f => f.key === currentValue)?.label || "Select field..."
|
||||
: "Select field..."}
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[var(--radix-popover-trigger-width)] p-0">
|
||||
<Command>
|
||||
<CommandInput placeholder="Search fields..." />
|
||||
<CommandList className="max-h-[300px] overflow-y-auto" onWheel={handleCommandListWheel}>
|
||||
<CommandEmpty>No fields found.</CommandEmpty>
|
||||
{fieldCategories.map(category => (
|
||||
<CommandGroup key={category.name} heading={category.name}>
|
||||
{category.fields.map((field: { key: string; label: string }) => {
|
||||
const { isMapped, columnHeader } = isFieldMappedToOtherColumn(field.key as string, column.index);
|
||||
return (
|
||||
<CommandItem
|
||||
key={field.key as string}
|
||||
value={field.key as string}
|
||||
onSelect={(value: string) => {
|
||||
onChange(value);
|
||||
setOpen(false); // Close the popover after selection
|
||||
}}
|
||||
className={isMapped ? "opacity-70" : ""}
|
||||
>
|
||||
<div className="flex-1 flex items-center justify-between">
|
||||
<span>{field.label}</span>
|
||||
{isMapped ? (
|
||||
<span className="text-xs text-muted-foreground ml-2">
|
||||
(mapped to "{columnHeader}")
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
{currentValue === field.key && (
|
||||
<CheckIcon className="ml-2 h-4 w-4" />
|
||||
)}
|
||||
</CommandItem>
|
||||
);
|
||||
})}
|
||||
</CommandGroup>
|
||||
))}
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
});
|
||||
|
||||
export const MatchColumnsStep = React.memo(<T extends string>({
|
||||
data,
|
||||
headerValues,
|
||||
@@ -278,7 +630,7 @@ export const MatchColumnsStep = React.memo(<T extends string>({
|
||||
([...headerValues] as string[]).map((value, index) => ({ type: ColumnType.empty, index, header: value ?? "" })),
|
||||
)
|
||||
const [globalSelections, setGlobalSelections] = useState<GlobalSelections>(initialGlobalSelections || {})
|
||||
const [showAllColumns, setShowAllColumns] = useState(false)
|
||||
const [showAllColumns, setShowAllColumns] = useState(true)
|
||||
const [expandedValueMappings, setExpandedValueMappings] = useState<number[]>([])
|
||||
|
||||
// Use debounce for expensive operations
|
||||
@@ -601,18 +953,14 @@ export const MatchColumnsStep = React.memo(<T extends string>({
|
||||
return mappings;
|
||||
}, [columns, fields, isFieldCoveredByGlobalSelections]);
|
||||
|
||||
// Available fields for mapping (excluding already mapped fields)
|
||||
// Available fields for mapping (including already mapped fields)
|
||||
const availableFields = useMemo(() => {
|
||||
const fieldsArray = Array.isArray(fields) ? fields : [fields];
|
||||
const mappedFieldKeys = matchedColumns
|
||||
.filter(col => "value" in col)
|
||||
.map(col => (col as any).value);
|
||||
|
||||
// Don't filter out mapped fields, only filter global selections
|
||||
return fieldsArray.filter(field =>
|
||||
!mappedFieldKeys.includes(field.key) &&
|
||||
!isFieldCoveredByGlobalSelections(field.key)
|
||||
);
|
||||
}, [fields, matchedColumns, isFieldCoveredByGlobalSelections]);
|
||||
}, [fields, isFieldCoveredByGlobalSelections]);
|
||||
|
||||
// All available fields including already mapped ones (for editing mapped columns)
|
||||
const allFields = useMemo(() => {
|
||||
@@ -963,48 +1311,48 @@ export const MatchColumnsStep = React.memo(<T extends string>({
|
||||
return handlers;
|
||||
}, [columns, onChange]);
|
||||
|
||||
// Render the field selector for a column
|
||||
// Add a function to check if a field is already mapped to another column
|
||||
const isFieldMappedToOtherColumn = useCallback((fieldKey: string, currentColumnIndex: number) => {
|
||||
const matchedColumnForField = columns.find(col =>
|
||||
col.type !== ColumnType.empty &&
|
||||
col.type !== ColumnType.ignored &&
|
||||
"value" in col &&
|
||||
col.value === fieldKey &&
|
||||
col.index !== currentColumnIndex
|
||||
);
|
||||
return matchedColumnForField ? { isMapped: true, columnHeader: matchedColumnForField.header } : { isMapped: false };
|
||||
}, [columns]);
|
||||
|
||||
// Add a wheel handler function for command lists
|
||||
const handleCommandListWheel = useCallback((e: React.WheelEvent) => {
|
||||
e.currentTarget.scrollTop += e.deltaY;
|
||||
e.stopPropagation();
|
||||
}, []);
|
||||
|
||||
// Replace the renderFieldSelector function with a more stable version
|
||||
const renderFieldSelector = useCallback((column: Column<T>, isUnmapped: boolean = false) => {
|
||||
// For ignored columns, show a badge
|
||||
if (column.type === ColumnType.ignored) {
|
||||
return <Badge variant="outline">Ignored</Badge>;
|
||||
}
|
||||
|
||||
// Get the current value if this is a mapped column
|
||||
const currentValue = "value" in column ? column.value as string : undefined;
|
||||
|
||||
// Use all fields for mapped columns, and only available fields for unmapped columns
|
||||
const fieldCategoriesForSelector = isUnmapped ? availableFieldCategories : allFieldCategories;
|
||||
|
||||
// Get the pre-created onChange handler for this column
|
||||
const handleChange = columnChangeHandlers.get(column.index);
|
||||
|
||||
return (
|
||||
<Select
|
||||
value={currentValue}
|
||||
onValueChange={handleChange}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select field..." />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{fieldCategoriesForSelector.map(category => (
|
||||
<div key={category.name}>
|
||||
<div className="px-2 py-1.5 text-sm font-semibold text-muted-foreground">
|
||||
{category.name}
|
||||
</div>
|
||||
{category.fields.map(field => (
|
||||
<SelectItem key={field.key as string} value={field.key as string}>
|
||||
{field.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
<Separator className="my-1" />
|
||||
</div>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FieldSelector
|
||||
column={column}
|
||||
isUnmapped={isUnmapped}
|
||||
fieldCategories={availableFieldCategories}
|
||||
allFields={allFields}
|
||||
onChange={(value: string) => {
|
||||
if (handleChange) handleChange(value);
|
||||
}}
|
||||
isFieldMappedToOtherColumn={isFieldMappedToOtherColumn}
|
||||
handleCommandListWheel={handleCommandListWheel}
|
||||
/>
|
||||
);
|
||||
}, [availableFieldCategories, allFieldCategories, columnChangeHandlers]);
|
||||
}, [availableFieldCategories, allFields, columnChangeHandlers, isFieldMappedToOtherColumn, handleCommandListWheel]);
|
||||
|
||||
// Replace the renderValueMappings function with a memoized version
|
||||
const renderValueMappings = useCallback((column: Column<T>) => {
|
||||
@@ -1023,8 +1371,8 @@ export const MatchColumnsStep = React.memo(<T extends string>({
|
||||
<Table>
|
||||
<TableHeader className="sticky top-0 bg-muted z-10">
|
||||
<TableRow>
|
||||
<TableHead className="w-1/3">Spreadsheet Column</TableHead>
|
||||
<TableHead className="w-12 text-center">Data</TableHead>
|
||||
<TableHead className="w-1/4">Imported Spreadsheet Column</TableHead>
|
||||
<TableHead className="w-15 text-center">Sample Data</TableHead>
|
||||
<TableHead className="w-12"></TableHead>
|
||||
<TableHead>Map To Field</TableHead>
|
||||
<TableHead className="w-24 text-right">Ignore</TableHead>
|
||||
@@ -1216,97 +1564,52 @@ export const MatchColumnsStep = React.memo(<T extends string>({
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-1">
|
||||
<label className="text-sm font-medium">Supplier</label>
|
||||
<Select
|
||||
<SupplierSelector
|
||||
value={globalSelections.supplier}
|
||||
onValueChange={(value) => setGlobalSelections(prev => ({ ...prev, supplier: value }))}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select supplier..." />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{hasSuppliers(stableFieldOptions) ?
|
||||
stableFieldOptions.suppliers.map((supplier: any) => (
|
||||
<SelectItem key={supplier.value} value={supplier.value}>
|
||||
{supplier.label}
|
||||
</SelectItem>
|
||||
)) : null}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
onChange={(value) => setGlobalSelections(prev => ({ ...prev, supplier: value }))}
|
||||
suppliers={fieldOptions?.suppliers || []}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<label className="text-sm font-medium">Company</label>
|
||||
<Select
|
||||
<CompanySelector
|
||||
value={globalSelections.company}
|
||||
onValueChange={(value) => {
|
||||
setGlobalSelections(prev => ({
|
||||
onChange={(value) => setGlobalSelections(prev => ({
|
||||
...prev,
|
||||
company: value,
|
||||
line: undefined,
|
||||
subline: undefined
|
||||
}))
|
||||
}}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select company..." />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{hasCompanies(stableFieldOptions) ?
|
||||
stableFieldOptions.companies.map((company: any) => (
|
||||
<SelectItem key={company.value} value={company.value}>
|
||||
{company.label}
|
||||
</SelectItem>
|
||||
)) : null}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
}))}
|
||||
companies={fieldOptions?.companies || []}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<label className="text-sm font-medium">Line</label>
|
||||
<Select
|
||||
<LineSelector
|
||||
value={globalSelections.line}
|
||||
onValueChange={(value) => {
|
||||
setGlobalSelections(prev => ({
|
||||
onChange={(value) => setGlobalSelections(prev => ({
|
||||
...prev,
|
||||
line: value,
|
||||
subline: undefined
|
||||
}))
|
||||
}}
|
||||
}))}
|
||||
lines={productLines || []}
|
||||
disabled={!globalSelections.company}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select line..." />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{Array.isArray(stableProductLines) ?
|
||||
stableProductLines.map((line: any) => (
|
||||
<SelectItem key={line.value} value={line.value}>
|
||||
{line.label}
|
||||
</SelectItem>
|
||||
)) : null}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<label className="text-sm font-medium">Sub Line</label>
|
||||
<Select
|
||||
<SubLineSelector
|
||||
value={globalSelections.subline}
|
||||
onValueChange={(value) => setGlobalSelections(prev => ({ ...prev, subline: value }))}
|
||||
onChange={(value) => setGlobalSelections(prev => ({
|
||||
...prev,
|
||||
subline: value
|
||||
}))}
|
||||
sublines={sublines || []}
|
||||
disabled={!globalSelections.line}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select sub line..." />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{Array.isArray(stableSublines) ?
|
||||
stableSublines.map((subline: any) => (
|
||||
<SelectItem key={subline.value} value={subline.value}>
|
||||
{subline.label}
|
||||
</SelectItem>
|
||||
)) : null}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1378,8 +1681,8 @@ export const MatchColumnsStep = React.memo(<T extends string>({
|
||||
size="sm"
|
||||
onClick={() => setShowAllColumns(!showAllColumns)}
|
||||
>
|
||||
{showAllColumns ? <EyeOffIcon className="h-3.5 w-3.5 mr-1" /> : <EyeIcon className="h-3.5 w-3.5 mr-1" />}
|
||||
{showAllColumns ? "Hide mapped" : "Show all"}
|
||||
{!showAllColumns ? <EyeIcon className="h-3.5 w-3.5 mr-1" /> : <EyeOffIcon className="h-3.5 w-3.5 mr-1" />}
|
||||
{!showAllColumns ? "Show all" : "Hide mapped"}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -48,7 +48,7 @@ export const translations = {
|
||||
filterSwitchTitle: "Show only rows with errors",
|
||||
},
|
||||
imageUploadStep: {
|
||||
title: "Add Product Images",
|
||||
title: "Add Images",
|
||||
nextButtonTitle: "Submit",
|
||||
backButtonTitle: "Back",
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user