Style tweaks, fix section hiding in map columns step

This commit is contained in:
2025-03-07 22:23:42 -05:00
parent 875d0b8f55
commit 6a5e6d2bfb
6 changed files with 43 additions and 215 deletions

View File

@@ -143,7 +143,10 @@ const ColumnActions = memo(({
className="h-7 px-2" className="h-7 px-2"
> >
{isExpanded ? {isExpanded ?
"Hide values" : <>
<EyeOffIcon className="h-3.5 w-3.5 mr-1" />
Hide values
</> :
<> <>
<LinkIcon className="h-3.5 w-3.5 mr-1" /> <LinkIcon className="h-3.5 w-3.5 mr-1" />
Map values Map values
@@ -630,27 +633,21 @@ export const MatchColumnsStep = React.memo(<T extends string>({
) )
const [globalSelections, setGlobalSelections] = useState<GlobalSelections>(initialGlobalSelections || {}) const [globalSelections, setGlobalSelections] = useState<GlobalSelections>(initialGlobalSelections || {})
const [showAllColumns, setShowAllColumns] = useState(true) const [showAllColumns, setShowAllColumns] = useState(true)
const [expandedValueMappings, setExpandedValueMappings] = useState<number[]>([]) const [expandedValues, setExpandedValues] = useState<number[]>([])
const [userCollapsedColumns, setUserCollapsedColumns] = useState<number[]>([])
// Use debounce for expensive operations // Toggle with immediate visual feedback
const [expandedValues, setExpandedValues] = useState<number[]>([]);
useEffect(() => {
const timeoutId = setTimeout(() => {
setExpandedValueMappings(expandedValues);
}, 50);
return () => clearTimeout(timeoutId);
}, [expandedValues]);
// Toggle with immediate visual feedback but debounced actual state change
const toggleValueMappingOptimized = useCallback((columnIndex: number) => { const toggleValueMappingOptimized = useCallback((columnIndex: number) => {
setExpandedValues(prev => if (expandedValues.includes(columnIndex)) {
prev.includes(columnIndex) // User is collapsing this column - add to userCollapsedColumns
? prev.filter(idx => idx !== columnIndex) setUserCollapsedColumns(prev => [...prev, columnIndex]);
: [...prev, columnIndex] setExpandedValues(prev => prev.filter(idx => idx !== columnIndex));
); } else {
}, []); // User is expanding this column - remove from userCollapsedColumns
setUserCollapsedColumns(prev => prev.filter(idx => idx !== columnIndex));
setExpandedValues(prev => [...prev, columnIndex]);
}
}, [expandedValues]);
// Check if column is expandable (has value mappings) // Check if column is expandable (has value mappings)
const isExpandable = useCallback((column: Column<T>) => { const isExpandable = useCallback((column: Column<T>) => {
@@ -798,9 +795,6 @@ export const MatchColumnsStep = React.memo(<T extends string>({
const stableMappedProductLines = useMemo(() => mappedProductLines || [], [mappedProductLines]); const stableMappedProductLines = useMemo(() => mappedProductLines || [], [mappedProductLines]);
const stableMappedSublines = useMemo(() => mappedSublines || [], [mappedSublines]); const stableMappedSublines = useMemo(() => mappedSublines || [], [mappedSublines]);
// Type guard for suppliers and companies
// Check if a field is covered by global selections // Check if a field is covered by global selections
const isFieldCoveredByGlobalSelections = useCallback((key: string) => { const isFieldCoveredByGlobalSelections = useCallback((key: string) => {
return (key === 'supplier' && !!globalSelections.supplier) || return (key === 'supplier' && !!globalSelections.supplier) ||
@@ -1268,15 +1262,18 @@ export const MatchColumnsStep = React.memo(<T extends string>({
// Automatically expand columns with unmapped values - use layoutEffect to avoid flashing // Automatically expand columns with unmapped values - use layoutEffect to avoid flashing
useLayoutEffect(() => { useLayoutEffect(() => {
// Only add new columns that need to be expanded without removing existing ones // Track the current unmapped column indexes
const columnsToExpand = columnsWithUnmappedValues const currentUnmappedIndexes = columnsWithUnmappedValues.map(col => col.index);
.filter(column => !expandedValueMappings.includes(column.index))
.map(column => column.index);
if (columnsToExpand.length > 0) { // Find columns that are newly unmapped (weren't in expandedValues or userCollapsedColumns)
setExpandedValueMappings(prev => [...prev, ...columnsToExpand]); const newlyUnmappedColumns = currentUnmappedIndexes.filter(index =>
!expandedValues.includes(index) && !userCollapsedColumns.includes(index)
);
if (newlyUnmappedColumns.length > 0) {
setExpandedValues(prev => [...prev, ...newlyUnmappedColumns]);
} }
}, [columnsWithUnmappedValues, expandedValueMappings]); }, [columnsWithUnmappedValues, expandedValues, userCollapsedColumns]);
// Create a stable mapping of column index to change handlers // Create a stable mapping of column index to change handlers
const columnChangeHandlers = useMemo(() => { const columnChangeHandlers = useMemo(() => {
@@ -1351,7 +1348,7 @@ export const MatchColumnsStep = React.memo(<T extends string>({
<Table> <Table>
<TableHeader className="sticky top-0 bg-muted z-10"> <TableHeader className="sticky top-0 bg-muted z-10">
<TableRow> <TableRow>
<TableHead className="w-1/4">Imported Spreadsheet Column</TableHead> <TableHead className="w-1/4">Imported Column</TableHead>
<TableHead className="w-15 text-center">Sample Data</TableHead> <TableHead className="w-15 text-center">Sample Data</TableHead>
<TableHead className="w-12"></TableHead> <TableHead className="w-12"></TableHead>
<TableHead>Map To Field</TableHead> <TableHead>Map To Field</TableHead>
@@ -1361,7 +1358,7 @@ export const MatchColumnsStep = React.memo(<T extends string>({
<TableBody> <TableBody>
{/* Always show columns with unmapped values */} {/* Always show columns with unmapped values */}
{columnsWithUnmappedValues.map((column) => { {columnsWithUnmappedValues.map((column) => {
const isExpanded = expandedValueMappings.includes(column.index); const isExpanded = expandedValues.includes(column.index);
return ( return (
<React.Fragment key={`unmapped-values-${column.index}`}> <React.Fragment key={`unmapped-values-${column.index}`}>
@@ -1381,7 +1378,7 @@ export const MatchColumnsStep = React.memo(<T extends string>({
column={column} column={column}
onIgnore={onIgnore} onIgnore={onIgnore}
toggleValueMapping={toggleValueMappingOptimized} toggleValueMapping={toggleValueMappingOptimized}
isExpanded={expandedValueMappings.includes(column.index)} isExpanded={isExpanded}
canExpandValues={isExpandable(column)} canExpandValues={isExpandable(column)}
/> />
</TableCell> </TableCell>
@@ -1401,7 +1398,7 @@ export const MatchColumnsStep = React.memo(<T extends string>({
{/* Always show unmapped columns */} {/* Always show unmapped columns */}
{unmatchedColumns.map((column) => ( {unmatchedColumns.map((column) => (
<TableRow key={`unmatched-${column.index}`}> <TableRow key={`unmatched-${column.index}`} className="bg-amber-50 hover:bg-amber-100">
<TableCell className="font-medium">{column.header}</TableCell> <TableCell className="font-medium">{column.header}</TableCell>
<TableCell className="text-center"> <TableCell className="text-center">
{renderSamplePreview(column.index)} {renderSamplePreview(column.index)}
@@ -1417,7 +1414,7 @@ export const MatchColumnsStep = React.memo(<T extends string>({
column={column} column={column}
onIgnore={onIgnore} onIgnore={onIgnore}
toggleValueMapping={toggleValueMappingOptimized} toggleValueMapping={toggleValueMappingOptimized}
isExpanded={expandedValueMappings.includes(column.index)} isExpanded={expandedValues.includes(column.index)}
canExpandValues={isExpandable(column)} canExpandValues={isExpandable(column)}
/> />
</TableCell> </TableCell>
@@ -1426,12 +1423,12 @@ export const MatchColumnsStep = React.memo(<T extends string>({
{/* Show matched columns if showAllColumns is true */} {/* Show matched columns if showAllColumns is true */}
{showAllColumns && matchedColumns.map((column) => { {showAllColumns && matchedColumns.map((column) => {
const isExpanded = expandedValueMappings.includes(column.index); const isExpanded = expandedValues.includes(column.index);
const canExpandValues = isExpandable(column); const canExpandValues = isExpandable(column);
return ( return (
<React.Fragment key={`matched-${column.index}`}> <React.Fragment key={`matched-${column.index}`}>
<TableRow className="group"> <TableRow className="group bg-green-50 hover:bg-green-100">
<TableCell className="font-medium">{column.header}</TableCell> <TableCell className="font-medium">{column.header}</TableCell>
<TableCell className="text-center"> <TableCell className="text-center">
{renderSamplePreview(column.index)} {renderSamplePreview(column.index)}
@@ -1447,7 +1444,7 @@ export const MatchColumnsStep = React.memo(<T extends string>({
column={column} column={column}
onIgnore={onIgnore} onIgnore={onIgnore}
toggleValueMapping={toggleValueMappingOptimized} toggleValueMapping={toggleValueMappingOptimized}
isExpanded={expandedValueMappings.includes(column.index)} isExpanded={isExpanded}
canExpandValues={canExpandValues} canExpandValues={canExpandValues}
/> />
</TableCell> </TableCell>
@@ -1467,7 +1464,7 @@ export const MatchColumnsStep = React.memo(<T extends string>({
{/* Show ignored columns if showAllColumns is true */} {/* Show ignored columns if showAllColumns is true */}
{showAllColumns && ignoredColumns.map((column) => ( {showAllColumns && ignoredColumns.map((column) => (
<TableRow key={`ignored-${column.index}`} className="text-muted-foreground"> <TableRow key={`ignored-${column.index}`} className="text-muted-foreground bg-red-50 hover:bg-red-100">
<TableCell className="font-medium">{column.header}</TableCell> <TableCell className="font-medium">{column.header}</TableCell>
<TableCell className="text-center"> <TableCell className="text-center">
{renderSamplePreview(column.index)} {renderSamplePreview(column.index)}
@@ -1476,7 +1473,7 @@ export const MatchColumnsStep = React.memo(<T extends string>({
<ArrowRightIcon className="h-4 w-4 mx-auto" /> <ArrowRightIcon className="h-4 w-4 mx-auto" />
</TableCell> </TableCell>
<TableCell> <TableCell>
<Badge variant="outline">Ignored</Badge> <Badge variant="outline" className="bg-card">Ignored</Badge>
</TableCell> </TableCell>
<TableCell className="text-right"> <TableCell className="text-right">
<Button <Button
@@ -1517,7 +1514,7 @@ export const MatchColumnsStep = React.memo(<T extends string>({
matchedColumns, matchedColumns,
ignoredColumns, ignoredColumns,
showAllColumns, showAllColumns,
expandedValueMappings, expandedValues,
renderSamplePreview, renderSamplePreview,
renderFieldSelector, renderFieldSelector,
renderValueMappings, renderValueMappings,
@@ -1535,7 +1532,7 @@ export const MatchColumnsStep = React.memo(<T extends string>({
<div className="grid grid-cols-1 lg:grid-cols-4 gap-6 h-full"> <div className="grid grid-cols-1 lg:grid-cols-4 gap-6 h-full">
{/* Left panel - Global selections & Required fields */} {/* Left panel - Global selections & Required fields */}
<div className="lg:col-span-1 space-y-4"> <div className="lg:col-span-1 space-y-4">
<div> <div className="bg-muted border rounded-md p-4 shadow-md">
<h3 className="text-lg font-medium mb-0">Global Settings</h3> <h3 className="text-lg font-medium mb-0">Global Settings</h3>
<p className="text-sm text-muted-foreground mb-2"> <p className="text-sm text-muted-foreground mb-2">
These values will be applied to all imported items These values will be applied to all imported items
@@ -1594,8 +1591,6 @@ export const MatchColumnsStep = React.memo(<T extends string>({
</div> </div>
</div> </div>
<Separator className="my-8" />
{/* Required Fields Section - Updated to show source column */} {/* Required Fields Section - Updated to show source column */}
<div> <div>
<div className="flex items-center gap-2 mb-4"> <div className="flex items-center gap-2 mb-4">
@@ -1645,9 +1640,6 @@ export const MatchColumnsStep = React.memo(<T extends string>({
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<h3 className="text-lg font-medium">Map Spreadsheet Columns</h3> <h3 className="text-lg font-medium">Map Spreadsheet Columns</h3>
<Badge variant={unmatchedRequiredFields.length > 0 ? "destructive" : "outline"}>
{unmatchedRequiredFields.length} required missing
</Badge>
{columnsWithUnmappedValues.length > 0 && ( {columnsWithUnmappedValues.length > 0 && (
<Badge variant="outline" className="bg-amber-100 text-amber-800 hover:bg-amber-100 border-amber-300"> <Badge variant="outline" className="bg-amber-100 text-amber-800 hover:bg-amber-100 border-amber-300">
{columnsWithUnmappedValues.length} with unmapped values {columnsWithUnmappedValues.length} with unmapped values

View File

@@ -1,88 +0,0 @@
import type React from "react"
import type { Column, Columns } from "../MatchColumnsStep"
import { ColumnType } from "../MatchColumnsStep"
import { useRsi } from "../../../hooks/useRsi"
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"
type ColumnGridProps<T extends string> = {
columns: Columns<T>
userColumn: (column: Column<T>) => React.ReactNode
templateColumn: (column: Column<T>) => React.ReactNode
onContinue: (val: Record<string, string>[]) => void
onBack?: () => void
isLoading: boolean
}
export const ColumnGrid = <T extends string>({
columns,
userColumn,
templateColumn,
}: ColumnGridProps<T>) => {
const { translations } = useRsi()
const normalColumnWidth = 250
const ignoredColumnWidth = 48 // 12 units = 3rem = 48px
const gap = 16
const totalWidth = columns.reduce((acc, col) =>
acc + (col.type === ColumnType.ignored ? ignoredColumnWidth : normalColumnWidth) + gap,
-gap // Subtract one gap since we need gaps between columns only
)
return (
<div className="h-full">
<div className="mb-8">
<h2 className="text-3xl font-semibold text-foreground">
{translations.matchColumnsStep.title}
</h2>
</div>
<ScrollArea className="relative" type="hover">
<div className="space-y-8" style={{ width: totalWidth }}>
{/* Your table section */}
<div>
<h3 className="mb-4 text-lg font-medium text-foreground">
{translations.matchColumnsStep.userTableTitle}
</h3>
<div className="relative">
<div
className="grid auto-cols-fr gap-4"
style={{
gridTemplateColumns: columns.map(col =>
`${col.type === ColumnType.ignored ? ignoredColumnWidth : normalColumnWidth}px`
).join(" "),
}}
>
{columns.map((column, index) => (
<div key={column.header + index}>
{userColumn(column)}
</div>
))}
</div>
<div className="pointer-events-none absolute bottom-0 left-0 right-0 h-16 bg-gradient-to-b from-transparent via-background/50 to-background" />
</div>
</div>
{/* Will become section */}
<div>
<h3 className="mb-4 text-lg font-medium text-foreground">
{translations.matchColumnsStep.templateTitle}
</h3>
<div
className="grid auto-cols-fr gap-4"
style={{
gridTemplateColumns: columns.map(col =>
`${col.type === ColumnType.ignored ? ignoredColumnWidth : normalColumnWidth}px`
).join(" "),
}}
>
{columns.map((column, index) => (
<div key={column.header + index}>
{templateColumn(column)}
</div>
))}
</div>
</div>
</div>
<ScrollBar orientation="horizontal" />
</ScrollArea>
</div>
)
}

View File

@@ -1,74 +0,0 @@
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader } from "@/components/ui/card"
import { X, RotateCcw } from "lucide-react"
import type { Column } from "../MatchColumnsStep"
import { ColumnType } from "../MatchColumnsStep"
import type { RawData } from "../../../types"
type UserTableColumnProps<T extends string> = {
column: Column<T>
entries: RawData
onIgnore: (index: number) => void
onRevertIgnore: (index: number) => void
}
export const UserTableColumn = <T extends string>(props: UserTableColumnProps<T>) => {
const {
column: { header, index, type },
entries,
onIgnore,
onRevertIgnore,
} = props
const isIgnored = type === ColumnType.ignored
if (isIgnored) {
return (
<Card className="h-full w-12 bg-muted/50">
<CardHeader className="flex flex-col items-center space-y-4 p-2">
<Button
variant="ghost"
size="icon"
onClick={() => onRevertIgnore(index)}
className="h-8 w-8"
>
<RotateCcw className="h-4 w-4" />
</Button>
<div
className="vertical-text font-medium text-muted-foreground"
style={{ writingMode: 'vertical-rl', textOrientation: 'mixed', transform: 'rotate(0deg)' }}
>
{header}
</div>
</CardHeader>
</Card>
)
}
return (
<Card className="h-full">
<CardHeader className="flex flex-row items-center justify-between space-x-2 p-4">
<p className="font-medium">
{header}
</p>
<Button
variant="ghost"
size="icon"
onClick={() => onIgnore(index)}
className="h-8 w-8"
>
<X className="h-4 w-4" />
</Button>
</CardHeader>
<CardContent className="space-y-2 p-4">
{entries.map((entry, i) => (
<p
key={`${entry || ""}-${i}`}
className="truncate text-sm text-muted-foreground"
>
{entry}
</p>
))}
</CardContent>
</Card>
)
}

View File

@@ -159,7 +159,7 @@ export const SelectHeaderStep = ({ data, onContinue, onBack }: SelectHeaderProps
</p> </p>
</div> </div>
<Button <Button
variant="default" variant="secondary"
size="sm" size="sm"
onClick={discardEmptyAndDuplicateRows} onClick={discardEmptyAndDuplicateRows}
> >

View File

@@ -70,7 +70,7 @@ export const SelectHeaderTable = ({ data, selectedRows, setSelectedRows }: Props
key={rowIndex} key={rowIndex}
className={cn( className={cn(
"grid", "grid",
selectedRowIndex === rowIndex && "bg-muted", selectedRowIndex === rowIndex && "bg-muted font-bold",
"group hover:bg-muted/50" "group hover:bg-muted/50"
)} )}
style={{ gridTemplateColumns }} style={{ gridTemplateColumns }}

View File

@@ -54,9 +54,7 @@ const ValidationContainer = <T extends string>({
loadTemplates, loadTemplates,
setData, setData,
fields, fields,
isLoadingTemplates, isLoadingTemplates } = validationState
rowValidationStatus
} = validationState
// Add state for tracking product lines and sublines per row // Add state for tracking product lines and sublines per row
const [rowProductLines, setRowProductLines] = useState<Record<string, any[]>>({}); const [rowProductLines, setRowProductLines] = useState<Record<string, any[]>>({});
@@ -133,7 +131,7 @@ const ValidationContainer = <T extends string>({
// Clear loading state // Clear loading state
setIsLoadingLines(prev => ({ ...prev, [rowIndex]: false })); setIsLoadingLines(prev => ({ ...prev, [rowIndex]: false }));
} }
}, []); }, []);
// Function to fetch sublines for a specific line - memoized // Function to fetch sublines for a specific line - memoized
const fetchSublines = useCallback(async (rowIndex: string | number, lineId: string) => { const fetchSublines = useCallback(async (rowIndex: string | number, lineId: string) => {