16 KiB
Product Import Module - Enhancement & Issues Outline
This document outlines the investigation and implementation requirements for each requested enhancement to the product import module.
1. UPC Import - Strip Quotes and Spaces ✅ IMPLEMENTED
Issue: When importing UPCs, strip ', " characters and any spaces, leaving only numbers.
Implementation (Completed):
- Modified
normalizeUpcValue()in Import.tsx:661-667 - Strips single quotes, double quotes, smart quotes (
'"), and whitespace before processing - Then handles scientific notation and extracts only digits
Files Modified:
inventory/src/pages/Import.tsx-normalizeUpcValue()function
2. AI Context Columns in Validation Payloads ✅ IMPLEMENTED
Issue: The match columns step has a setting to use a field only for AI context (isAiSupplemental). Update AI description validation to include any columns selected with this option in the payload. Also include in sanity check payload. Not needed for names.
Current Implementation:
- AI Supplemental toggle: MatchColumnsStep.tsx:102-118
- AI supplemental data stored in
__aiSupplementalfield on each row - Description payload builder: inlineAiPayload.ts:183-195
Implementation:
-
Update
buildDescriptionValidationPayload()ininlineAiPayload.tsto include AI supplemental data:export const buildDescriptionValidationPayload = ( row: Data<string>, fieldOptions: FieldOptionsMap, productLinesCache: Map<string, SelectOption[]>, sublinesCache: Map<string, SelectOption[]> ) => { const payload: Record<string, unknown> = { name: row.name, description: row.description, company_name: getFieldOptionLabel(row.company, fieldOptions, 'company'), company_id: row.company, categories: getFieldOptionLabel(row.category, fieldOptions, 'category'), }; // Add AI supplemental context if present if (row.__aiSupplemental && typeof row.__aiSupplemental === 'object') { payload.additional_context = row.__aiSupplemental; } return payload; }; -
Update sanity check payload - Locate sanity check submission logic and include
__aiSupplementaldata -
Verify
__aiSupplementalis properly populated from MatchColumnsStep when columns are marked as AI context only
Files to Modify:
inventory/src/components/product-import/steps/ValidationStep/utils/inlineAiPayload.ts- Backend sanity check endpoint (if separate from description validation)
- Verify data flow in
MatchColumnsStep.tsx→ValidationStep
3. Fresh Taxonomy Data Per Session ✅ IMPLEMENTED
Issue: Ensure taxonomy data is brought in fresh with each session - cache should be invalidated if we exit the import flow and start again.
Current Implementation:
- Field options cached 5 minutes: ValidationStep/index.tsx:128-133
- Product lines cache:
productLinesCachein Zustand store - Sublines cache:
sublinesCachein Zustand store - Caches set to 10-minute stale time
Implementation:
-
Add cache invalidation on import flow mount/unmount in
UploadFlow.tsx:useEffect(() => { // On mount - invalidate import-related query cache queryClient.invalidateQueries({ queryKey: ['import-field-options'] }); return () => { // On unmount - clear caches queryClient.removeQueries({ queryKey: ['import-field-options'] }); queryClient.removeQueries({ queryKey: ['product-lines'] }); queryClient.removeQueries({ queryKey: ['sublines'] }); }; }, []); -
Clear Zustand store caches when exiting import flow:
- Add action to
validationStore.tsto clearproductLinesCacheandsublinesCache - Call this action on unmount of
UploadFlowor when navigating away
- Add action to
-
Consider adding a
sessionIdthat changes on each import flow start, used as part of cache keys
Files to Modify:
inventory/src/components/product-import/steps/UploadFlow.tsx- Add cleanup effectinventory/src/components/product-import/steps/ValidationStep/store/validationStore.ts- Add cache clear action- Potentially
inventory/src/components/product-import/steps/ValidationStep/index.tsx- Query key updates
4. Save Template from Confirmation Page ✅ IMPLEMENTED
Issue: Add option to save rows of submitted data as a new template on the confirmation page after completing the import flow. Verify this works with new validation step changes.
Current Implementation:
- Import Results section already exists inline in Import.tsx:968-1150
- Shows created products (lines 1021-1097) with image, name, UPC, item number
- Shows errored products (lines 1100-1138) with error details
- "Fix products with errors" button resumes validation flow for failed items
- Template saving logic in ValidationStep: useTemplateManagement.ts:204-266
- Saves via
POST /api/templates importOutcome.submittedProductscontains the full product data for each row
Implementation:
-
Add "Save as Template" button to each created product row in the results section (around line 1087-1092 in Import.tsx):
// Add button after the item number display <Button variant="ghost" size="sm" onClick={() => handleSaveAsTemplate(index)} > <BookmarkPlus className="h-4 w-4" /> </Button> -
Add state and dialog for template saving in Import.tsx:
const [templateSaveDialogOpen, setTemplateSaveDialogOpen] = useState(false); const [selectedProductForTemplate, setSelectedProductForTemplate] = useState<NormalizedProduct | null>(null); -
Extract/reuse template save logic from
useTemplateManagement.ts:- The
saveNewTemplate()function (lines 204-266) can be extracted into a shared utility - Or create a
SaveTemplateDialogcomponent that can be used in both places - Key fields needed:
company(for template name),product_type, and all product field values
- The
-
Data mapping consideration:
importOutcome.submittedProductsusesNormalizedProducttype- Templates expect raw field values - may need to map back from normalized format
- Exclude metadata fields:
['id', '__index', '__meta', '__template', '__original', '__corrected', '__changes', '__aiSupplemental']
Files to Modify:
inventory/src/pages/Import.tsx- Add save template button, state, and dialog- Consider creating
inventory/src/components/product-import/SaveTemplateDialog.tsxfor reusability - Potentially extract core save logic from
useTemplateManagement.tsinto shared utility
5. Sheet Preview on Select Sheet Step ✅ IMPLEMENTED
Issue: On the select sheet step, show a preview of the first 10 lines or so of each sheet underneath the options.
Implementation (Completed):
- Added
workbookprop toSelectSheetStepcomponent - Added
sheetPreviewsmemoized computation usingXLSXLib.utils.sheet_to_json() - Shows first 10 rows, 8 columns max per sheet
- Added
truncateCell()helper to limit cell content to 30 characters with ellipsis - Each sheet option is now a clickable card with:
- Radio button and sheet name
- Row count indicator
- Scrollable preview table with horizontal scroll
- Selected state highlighted with primary border
- Updated
UploadFlow.tsxto pass workbook prop
Files Modified:
inventory/src/components/product-import/steps/SelectSheetStep/SelectSheetStep.tsxinventory/src/components/product-import/steps/UploadFlow.tsx
6. Empty Row Removal ✅ IMPLEMENTED
Issue: When importing a sheet, automatically remove completely empty rows.
Current Implementation:
- Empty columns are filtered: MatchColumnsStep.tsx:616-634
- A "Remove empty/duplicates" button exists that removes empty rows, single-value rows, AND duplicates
- The automatic removal should ONLY remove completely empty rows, not duplicates or single-value rows
Implementation (Completed):
- Added
isRowCompletelyEmpty()helper function to SelectHeaderStep.tsx - Added
useMemoto filter empty rows on initial data load - Uses
Object.values(row)to check all cell values (matches existing button logic) - Only removes rows where ALL values are undefined, null, or whitespace-only strings
- Manual "Remove Empty/Duplicates" button still available for additional cleanup (duplicates, single-value rows)
Files Modified:
inventory/src/components/product-import/steps/SelectHeaderStep/SelectHeaderStep.tsx
7. Unit Conversion for Weight/Dimensions ✅ IMPLEMENTED
Issue: Add unit conversion feature for weight and dimensions columns - similar to calculator button on cost/msrp, add button that opens popover with options to convert grams → oz, lbs → oz for the whole column at once.
Current Implementation:
- Calculator button on price columns: ValidationTable.tsx:1491-1627
PriceColumnHeadercomponent shows calculator icon on hover- Weight field defined in config with validation
Implementation:
-
Create
UnitConversionColumnHeadercomponent (similar toPriceColumnHeader):const UnitConversionColumnHeader = ({ field, table }) => { const [showPopover, setShowPopover] = useState(false); const conversions = { weight: [ { label: 'Grams → Ounces', factor: 0.035274 }, { label: 'Pounds → Ounces', factor: 16 }, { label: 'Kilograms → Ounces', factor: 35.274 }, ], dimensions: [ { label: 'Centimeters → Inches', factor: 0.393701 }, { label: 'Millimeters → Inches', factor: 0.0393701 }, ] }; const applyConversion = (factor: number) => { // Batch update all cells in column table.rows.forEach((row, index) => { const currentValue = parseFloat(row[field.key]); if (!isNaN(currentValue)) { updateCell(index, field.key, (currentValue * factor).toFixed(2)); } }); }; return ( <Popover open={showPopover} onOpenChange={setShowPopover}> <PopoverTrigger> <Scale className="h-4 w-4" /> {/* or similar icon */} </PopoverTrigger> <PopoverContent> {conversions[fieldType].map(conv => ( <Button key={conv.label} onClick={() => applyConversion(conv.factor)}> {conv.label} </Button> ))} </PopoverContent> </Popover> ); }; -
Identify weight/dimension fields in config:
weight_oz,length_in,width_in,height_in(check actual field keys)
-
Add to column header render logic in ValidationTable
Files to Modify:
inventory/src/components/product-import/steps/ValidationStep/components/ValidationTable.tsx- Potentially create new component file for
UnitConversionColumnHeader - Update column header rendering to use new component for weight/dimension fields
8. Expanded MSRP Auto-Fill from Cost ✅ IMPLEMENTED
Issue: Expand auto-fill functionality for MSRP from cost - open small popover with options for 2x, 2.1x, 2.2x, 2.3x, 2.4x, 2.5x multipliers, plus checkbox to round up to nearest 9.
Current Implementation:
- Calculator on MSRP column: ValidationTable.tsx:1540-1584
- Currently only does
Cost × 2then subtracts 0.01 if whole number
Implementation:
-
Replace simple click with popover in
PriceColumnHeader:const [selectedMultiplier, setSelectedMultiplier] = useState(2.0); const [roundToNine, setRoundToNine] = useState(false); const multipliers = [2.0, 2.1, 2.2, 2.3, 2.4, 2.5]; const roundUpToNine = (value: number): number => { // 1.41 → 1.49, 2.78 → 2.79, 12.32 → 12.39 const wholePart = Math.floor(value); const decimal = value - wholePart; if (decimal <= 0.09) return wholePart + 0.09; if (decimal <= 0.19) return wholePart + 0.19; // ... continue pattern, or: const lastDigit = Math.floor(decimal * 10); return wholePart + (lastDigit / 10) + 0.09; }; const calculateMsrp = (cost: number): number => { let result = cost * selectedMultiplier; if (roundToNine) { result = roundUpToNine(result); } return result; }; -
Create popover UI:
<Popover> <PopoverTrigger><Calculator className="h-4 w-4" /></PopoverTrigger> <PopoverContent className="w-48"> <div className="space-y-2"> <Label>Multiplier</Label> <div className="grid grid-cols-3 gap-1"> {multipliers.map(m => ( <Button key={m} variant={selectedMultiplier === m ? 'default' : 'outline'} size="sm" onClick={() => setSelectedMultiplier(m)} > {m}x </Button> ))} </div> <div className="flex items-center gap-2"> <Checkbox checked={roundToNine} onCheckedChange={setRoundToNine} /> <Label>Round to .X9</Label> </div> <Button onClick={applyCalculation} className="w-full"> Apply </Button> </div> </PopoverContent> </Popover>
Files to Modify:
inventory/src/components/product-import/steps/ValidationStep/components/ValidationTable.tsx-PriceColumnHeadercomponent
9. Debug Mode - Skip API Submission ✅ IMPLEMENTED
Issue: Add a third switch in the footer of image upload step (visible only to users with admin:debug permission) that will not submit data to any API, only complete the process and show results page as if it had worked.
Implementation (Completed):
- Added
skipApiSubmissionstate toImageUploadStep.tsx - Added amber-colored "Skip API (Debug)" switch (visible only with
admin:debugpermission) - When skip is active, "Use Test API" and "Use Test Database" switches are hidden
- Added
skipApiSubmission?: booleantoSubmitOptionstype intypes.ts - In
Import.tsx, whenskipApiSubmissionis true:- Skips the actual API call entirely
- Generates mock success response with mock PIDs
- Shows
[DEBUG]prefix in toast and result message - Displays results page as if submission succeeded
Files Modified:
inventory/src/components/product-import/types.ts- AddedskipApiSubmissiontoSubmitOptionsinventory/src/components/product-import/steps/ImageUploadStep/ImageUploadStep.tsx- Added switch UIinventory/src/pages/Import.tsx- Added skip logic inhandleData()
Summary
| # | Enhancement | Complexity | Status |
|---|---|---|---|
| 1 | Strip UPC quotes/spaces | Low | ✅ Implemented |
| 2 | AI context in validation | Medium | ✅ Implemented |
| 3 | Fresh taxonomy per session | Medium | ✅ Implemented |
| 4 | Save template from confirmation | Medium-High | ✅ Implemented |
| 5 | Sheet preview | Low-Medium | ✅ Implemented |
| 6 | Remove empty rows | Low | ✅ Implemented |
| 7 | Unit conversion | Medium | ✅ Implemented |
| 8 | MSRP multiplier options | Medium | ✅ Implemented |
| 9 | Debug skip API | Low-Medium | ✅ Implemented |
Implemented: 9 of 9 items - All enhancements complete!
Document generated: 2026-01-25