Style tweaks, fix section hiding in map columns step
This commit is contained in:
@@ -143,7 +143,10 @@ const ColumnActions = memo(({
|
||||
className="h-7 px-2"
|
||||
>
|
||||
{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" />
|
||||
Map values
|
||||
@@ -630,27 +633,21 @@ export const MatchColumnsStep = React.memo(<T extends string>({
|
||||
)
|
||||
const [globalSelections, setGlobalSelections] = useState<GlobalSelections>(initialGlobalSelections || {})
|
||||
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
|
||||
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
|
||||
// Toggle with immediate visual feedback
|
||||
const toggleValueMappingOptimized = useCallback((columnIndex: number) => {
|
||||
setExpandedValues(prev =>
|
||||
prev.includes(columnIndex)
|
||||
? prev.filter(idx => idx !== columnIndex)
|
||||
: [...prev, columnIndex]
|
||||
);
|
||||
}, []);
|
||||
if (expandedValues.includes(columnIndex)) {
|
||||
// User is collapsing this column - add to userCollapsedColumns
|
||||
setUserCollapsedColumns(prev => [...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)
|
||||
const isExpandable = useCallback((column: Column<T>) => {
|
||||
@@ -798,9 +795,6 @@ export const MatchColumnsStep = React.memo(<T extends string>({
|
||||
const stableMappedProductLines = useMemo(() => mappedProductLines || [], [mappedProductLines]);
|
||||
const stableMappedSublines = useMemo(() => mappedSublines || [], [mappedSublines]);
|
||||
|
||||
// Type guard for suppliers and companies
|
||||
|
||||
|
||||
// Check if a field is covered by global selections
|
||||
const isFieldCoveredByGlobalSelections = useCallback((key: string) => {
|
||||
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
|
||||
useLayoutEffect(() => {
|
||||
// Only add new columns that need to be expanded without removing existing ones
|
||||
const columnsToExpand = columnsWithUnmappedValues
|
||||
.filter(column => !expandedValueMappings.includes(column.index))
|
||||
.map(column => column.index);
|
||||
// Track the current unmapped column indexes
|
||||
const currentUnmappedIndexes = columnsWithUnmappedValues.map(col => col.index);
|
||||
|
||||
if (columnsToExpand.length > 0) {
|
||||
setExpandedValueMappings(prev => [...prev, ...columnsToExpand]);
|
||||
// Find columns that are newly unmapped (weren't in expandedValues or userCollapsedColumns)
|
||||
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
|
||||
const columnChangeHandlers = useMemo(() => {
|
||||
@@ -1351,7 +1348,7 @@ export const MatchColumnsStep = React.memo(<T extends string>({
|
||||
<Table>
|
||||
<TableHeader className="sticky top-0 bg-muted z-10">
|
||||
<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-12"></TableHead>
|
||||
<TableHead>Map To Field</TableHead>
|
||||
@@ -1361,7 +1358,7 @@ export const MatchColumnsStep = React.memo(<T extends string>({
|
||||
<TableBody>
|
||||
{/* Always show columns with unmapped values */}
|
||||
{columnsWithUnmappedValues.map((column) => {
|
||||
const isExpanded = expandedValueMappings.includes(column.index);
|
||||
const isExpanded = expandedValues.includes(column.index);
|
||||
|
||||
return (
|
||||
<React.Fragment key={`unmapped-values-${column.index}`}>
|
||||
@@ -1381,7 +1378,7 @@ export const MatchColumnsStep = React.memo(<T extends string>({
|
||||
column={column}
|
||||
onIgnore={onIgnore}
|
||||
toggleValueMapping={toggleValueMappingOptimized}
|
||||
isExpanded={expandedValueMappings.includes(column.index)}
|
||||
isExpanded={isExpanded}
|
||||
canExpandValues={isExpandable(column)}
|
||||
/>
|
||||
</TableCell>
|
||||
@@ -1401,7 +1398,7 @@ export const MatchColumnsStep = React.memo(<T extends string>({
|
||||
|
||||
{/* Always show unmapped columns */}
|
||||
{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="text-center">
|
||||
{renderSamplePreview(column.index)}
|
||||
@@ -1417,7 +1414,7 @@ export const MatchColumnsStep = React.memo(<T extends string>({
|
||||
column={column}
|
||||
onIgnore={onIgnore}
|
||||
toggleValueMapping={toggleValueMappingOptimized}
|
||||
isExpanded={expandedValueMappings.includes(column.index)}
|
||||
isExpanded={expandedValues.includes(column.index)}
|
||||
canExpandValues={isExpandable(column)}
|
||||
/>
|
||||
</TableCell>
|
||||
@@ -1426,12 +1423,12 @@ export const MatchColumnsStep = React.memo(<T extends string>({
|
||||
|
||||
{/* Show matched columns if showAllColumns is true */}
|
||||
{showAllColumns && matchedColumns.map((column) => {
|
||||
const isExpanded = expandedValueMappings.includes(column.index);
|
||||
const isExpanded = expandedValues.includes(column.index);
|
||||
const canExpandValues = isExpandable(column);
|
||||
|
||||
return (
|
||||
<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="text-center">
|
||||
{renderSamplePreview(column.index)}
|
||||
@@ -1447,7 +1444,7 @@ export const MatchColumnsStep = React.memo(<T extends string>({
|
||||
column={column}
|
||||
onIgnore={onIgnore}
|
||||
toggleValueMapping={toggleValueMappingOptimized}
|
||||
isExpanded={expandedValueMappings.includes(column.index)}
|
||||
isExpanded={isExpanded}
|
||||
canExpandValues={canExpandValues}
|
||||
/>
|
||||
</TableCell>
|
||||
@@ -1467,7 +1464,7 @@ export const MatchColumnsStep = React.memo(<T extends string>({
|
||||
|
||||
{/* Show ignored columns if showAllColumns is true */}
|
||||
{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="text-center">
|
||||
{renderSamplePreview(column.index)}
|
||||
@@ -1476,7 +1473,7 @@ export const MatchColumnsStep = React.memo(<T extends string>({
|
||||
<ArrowRightIcon className="h-4 w-4 mx-auto" />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant="outline">Ignored</Badge>
|
||||
<Badge variant="outline" className="bg-card">Ignored</Badge>
|
||||
</TableCell>
|
||||
<TableCell className="text-right">
|
||||
<Button
|
||||
@@ -1517,7 +1514,7 @@ export const MatchColumnsStep = React.memo(<T extends string>({
|
||||
matchedColumns,
|
||||
ignoredColumns,
|
||||
showAllColumns,
|
||||
expandedValueMappings,
|
||||
expandedValues,
|
||||
renderSamplePreview,
|
||||
renderFieldSelector,
|
||||
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">
|
||||
{/* Left panel - Global selections & Required fields */}
|
||||
<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>
|
||||
<p className="text-sm text-muted-foreground mb-2">
|
||||
These values will be applied to all imported items
|
||||
@@ -1594,8 +1591,6 @@ export const MatchColumnsStep = React.memo(<T extends string>({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Separator className="my-8" />
|
||||
|
||||
{/* Required Fields Section - Updated to show source column */}
|
||||
<div>
|
||||
<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 gap-2">
|
||||
<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 && (
|
||||
<Badge variant="outline" className="bg-amber-100 text-amber-800 hover:bg-amber-100 border-amber-300">
|
||||
{columnsWithUnmappedValues.length} with unmapped values
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -159,7 +159,7 @@ export const SelectHeaderStep = ({ data, onContinue, onBack }: SelectHeaderProps
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
variant="default"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={discardEmptyAndDuplicateRows}
|
||||
>
|
||||
|
||||
@@ -70,7 +70,7 @@ export const SelectHeaderTable = ({ data, selectedRows, setSelectedRows }: Props
|
||||
key={rowIndex}
|
||||
className={cn(
|
||||
"grid",
|
||||
selectedRowIndex === rowIndex && "bg-muted",
|
||||
selectedRowIndex === rowIndex && "bg-muted font-bold",
|
||||
"group hover:bg-muted/50"
|
||||
)}
|
||||
style={{ gridTemplateColumns }}
|
||||
|
||||
@@ -54,9 +54,7 @@ const ValidationContainer = <T extends string>({
|
||||
loadTemplates,
|
||||
setData,
|
||||
fields,
|
||||
isLoadingTemplates,
|
||||
rowValidationStatus
|
||||
} = validationState
|
||||
isLoadingTemplates } = validationState
|
||||
|
||||
// Add state for tracking product lines and sublines per row
|
||||
const [rowProductLines, setRowProductLines] = useState<Record<string, any[]>>({});
|
||||
@@ -133,7 +131,7 @@ const ValidationContainer = <T extends string>({
|
||||
// Clear loading state
|
||||
setIsLoadingLines(prev => ({ ...prev, [rowIndex]: false }));
|
||||
}
|
||||
}, []);
|
||||
}, []);
|
||||
|
||||
// Function to fetch sublines for a specific line - memoized
|
||||
const fetchSublines = useCallback(async (rowIndex: string | number, lineId: string) => {
|
||||
|
||||
Reference in New Issue
Block a user