diff --git a/.VSCodeCounter/2025-03-17_16-24-17/details.md b/.VSCodeCounter/2025-03-17_16-24-17/details.md new file mode 100644 index 0000000..06d6b8c --- /dev/null +++ b/.VSCodeCounter/2025-03-17_16-24-17/details.md @@ -0,0 +1,41 @@ +# Details + +Date : 2025-03-17 16:24:17 + +Directory /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew + +Total : 26 files, 6193 codes, 1008 comments, 1017 blanks, all 8218 lines + +[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md) + +## Files +| filename | language | code | comment | blank | total | +| :--- | :--- | ---: | ---: | ---: | ---: | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/README.md](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/README.md) | Markdown | 39 | 0 | 19 | 58 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/AiValidationDialogs.tsx](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/AiValidationDialogs.tsx) | TypeScript JSX | 230 | 10 | 8 | 248 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/BaseCellContent.tsx](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/BaseCellContent.tsx) | TypeScript JSX | 18 | 0 | 3 | 21 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/SearchableTemplateSelect.tsx](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/SearchableTemplateSelect.tsx) | TypeScript JSX | 273 | 19 | 37 | 329 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationCell.tsx](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationCell.tsx) | TypeScript JSX | 374 | 42 | 44 | 460 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationContainer.tsx](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationContainer.tsx) | TypeScript JSX | 730 | 126 | 106 | 962 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationTable.tsx](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationTable.tsx) | TypeScript JSX | 499 | 48 | 54 | 601 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/cells/CheckboxCell.tsx](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/cells/CheckboxCell.tsx) | TypeScript JSX | 112 | 12 | 21 | 145 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/cells/InputCell.tsx](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/cells/InputCell.tsx) | TypeScript JSX | 232 | 31 | 32 | 295 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/cells/MultiSelectCell.tsx](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/cells/MultiSelectCell.tsx) | TypeScript JSX | 407 | 56 | 52 | 515 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/cells/MultilineInput.tsx](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/cells/MultilineInput.tsx) | TypeScript JSX | 193 | 23 | 22 | 238 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/cells/SelectCell.tsx](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/cells/SelectCell.tsx) | TypeScript JSX | 289 | 36 | 31 | 356 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useAiValidation.tsx](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useAiValidation.tsx) | TypeScript JSX | 500 | 75 | 89 | 664 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useProductLinesFetching.tsx](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useProductLinesFetching.tsx) | TypeScript JSX | 248 | 69 | 74 | 391 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useTemplates.tsx](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useTemplates.tsx) | TypeScript JSX | 204 | 26 | 33 | 263 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useUpcValidation.tsx](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useUpcValidation.tsx) | TypeScript JSX | 209 | 49 | 50 | 308 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useValidation.tsx](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useValidation.tsx) | TypeScript JSX | 219 | 39 | 47 | 305 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useValidationState.tsx](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useValidationState.tsx) | TypeScript JSX | 1,060 | 228 | 229 | 1,517 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/index.tsx](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/index.tsx) | TypeScript JSX | 20 | 6 | 2 | 28 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/types.ts](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/types.ts) | TypeScript | 4 | 0 | 1 | 5 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/types/index.ts](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/types/index.ts) | TypeScript | 16 | 4 | 4 | 24 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/utils/dataMutations.ts](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/utils/dataMutations.ts) | TypeScript | 124 | 4 | 14 | 142 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/utils/errorUtils.ts](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/utils/errorUtils.ts) | TypeScript | 21 | 15 | 5 | 41 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/utils/upcValidation.ts](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/utils/upcValidation.ts) | TypeScript | 43 | 24 | 7 | 74 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/utils/validation-helper.js](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/utils/validation-helper.js) | JavaScript | 28 | 7 | 9 | 44 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/utils/validationUtils.ts](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/utils/validationUtils.ts) | TypeScript | 101 | 59 | 24 | 184 | + +[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2025-03-17_16-24-17/diff-details.md b/.VSCodeCounter/2025-03-17_16-24-17/diff-details.md new file mode 100644 index 0000000..e2452d9 --- /dev/null +++ b/.VSCodeCounter/2025-03-17_16-24-17/diff-details.md @@ -0,0 +1,20 @@ +# Diff Details + +Date : 2025-03-17 16:24:17 + +Directory /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew + +Total : 5 files, -358 codes, -15 comments, -33 blanks, all -406 lines + +[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details + +## Files +| filename | language | code | comment | blank | total | +| :--- | :--- | ---: | ---: | ---: | ---: | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/SaveTemplateDialog.tsx](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/SaveTemplateDialog.tsx) | TypeScript JSX | -83 | 0 | -4 | -87 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/TemplateManager.tsx](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/TemplateManager.tsx) | TypeScript JSX | -193 | -4 | -15 | -212 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationContainer.tsx](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationContainer.tsx) | TypeScript JSX | -241 | -68 | -72 | -381 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useFilters.tsx](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useFilters.tsx) | TypeScript JSX | -89 | -12 | -16 | -117 | +| [inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useProductLinesFetching.tsx](/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useProductLinesFetching.tsx) | TypeScript JSX | 248 | 69 | 74 | 391 | + +[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details \ No newline at end of file diff --git a/.VSCodeCounter/2025-03-17_16-24-17/diff.md b/.VSCodeCounter/2025-03-17_16-24-17/diff.md new file mode 100644 index 0000000..8cdc16b --- /dev/null +++ b/.VSCodeCounter/2025-03-17_16-24-17/diff.md @@ -0,0 +1,23 @@ +# Diff Summary + +Date : 2025-03-17 16:24:17 + +Directory /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew + +Total : 5 files, -358 codes, -15 comments, -33 blanks, all -406 lines + +[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md) + +## Languages +| language | files | code | comment | blank | total | +| :--- | ---: | ---: | ---: | ---: | ---: | +| TypeScript JSX | 5 | -358 | -15 | -33 | -406 | + +## Directories +| path | files | code | comment | blank | total | +| :--- | ---: | ---: | ---: | ---: | ---: | +| . | 5 | -358 | -15 | -33 | -406 | +| components | 3 | -517 | -72 | -91 | -680 | +| hooks | 2 | 159 | 57 | 58 | 274 | + +[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2025-03-17_16-24-17/diff.txt b/.VSCodeCounter/2025-03-17_16-24-17/diff.txt new file mode 100644 index 0000000..b359f38 --- /dev/null +++ b/.VSCodeCounter/2025-03-17_16-24-17/diff.txt @@ -0,0 +1,31 @@ +Date : 2025-03-17 16:24:17 +Directory : /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew +Total : 5 files, -358 codes, -15 comments, -33 blanks, all -406 lines + +Languages ++----------------+------------+------------+------------+------------+------------+ +| language | files | code | comment | blank | total | ++----------------+------------+------------+------------+------------+------------+ +| TypeScript JSX | 5 | -358 | -15 | -33 | -406 | ++----------------+------------+------------+------------+------------+------------+ + +Directories ++-------------------------------------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ +| path | files | code | comment | blank | total | ++-------------------------------------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ +| . | 5 | -358 | -15 | -33 | -406 | +| components | 3 | -517 | -72 | -91 | -680 | +| hooks | 2 | 159 | 57 | 58 | 274 | ++-------------------------------------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ + +Files ++-------------------------------------------------------------------------------------------------------------------------------------+----------------+------------+------------+------------+------------+ +| filename | language | code | comment | blank | total | ++-------------------------------------------------------------------------------------------------------------------------------------+----------------+------------+------------+------------+------------+ +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/SaveTemplateDialog.tsx | TypeScript JSX | -83 | 0 | -4 | -87 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/TemplateManager.tsx | TypeScript JSX | -193 | -4 | -15 | -212 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationContainer.tsx | TypeScript JSX | -241 | -68 | -72 | -381 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useFilters.tsx | TypeScript JSX | -89 | -12 | -16 | -117 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useProductLinesFetching.tsx | TypeScript JSX | 248 | 69 | 74 | 391 | +| Total | | -358 | -15 | -33 | -406 | ++-------------------------------------------------------------------------------------------------------------------------------------+----------------+------------+------------+------------+------------+ \ No newline at end of file diff --git a/.VSCodeCounter/2025-03-17_16-24-17/results.json b/.VSCodeCounter/2025-03-17_16-24-17/results.json new file mode 100644 index 0000000..f39e682 --- /dev/null +++ b/.VSCodeCounter/2025-03-17_16-24-17/results.json @@ -0,0 +1 @@ +{"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useValidation.tsx":{"language":"TypeScript JSX","code":219,"comment":39,"blank":47},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useTemplates.tsx":{"language":"TypeScript JSX","code":204,"comment":26,"blank":33},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useAiValidation.tsx":{"language":"TypeScript JSX","code":500,"comment":75,"blank":89},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useUpcValidation.tsx":{"language":"TypeScript JSX","code":209,"comment":49,"blank":50},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useValidationState.tsx":{"language":"TypeScript JSX","code":1060,"comment":228,"blank":229},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationCell.tsx":{"language":"TypeScript JSX","code":374,"comment":42,"blank":44},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationTable.tsx":{"language":"TypeScript JSX","code":499,"comment":48,"blank":54},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useProductLinesFetching.tsx":{"language":"TypeScript JSX","code":248,"comment":69,"blank":74},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/SearchableTemplateSelect.tsx":{"language":"TypeScript JSX","code":273,"comment":19,"blank":37},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationContainer.tsx":{"language":"TypeScript JSX","code":730,"comment":126,"blank":106},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/BaseCellContent.tsx":{"language":"TypeScript JSX","code":18,"comment":0,"blank":3},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/cells/InputCell.tsx":{"language":"TypeScript JSX","code":232,"comment":31,"blank":32},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/cells/MultilineInput.tsx":{"language":"TypeScript JSX","code":193,"comment":23,"blank":22},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/cells/CheckboxCell.tsx":{"language":"TypeScript JSX","code":112,"comment":12,"blank":21},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/cells/MultiSelectCell.tsx":{"language":"TypeScript JSX","code":407,"comment":56,"blank":52},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/cells/SelectCell.tsx":{"language":"TypeScript JSX","code":289,"comment":36,"blank":31},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/types.ts":{"language":"TypeScript","code":4,"comment":0,"blank":1},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/AiValidationDialogs.tsx":{"language":"TypeScript JSX","code":230,"comment":10,"blank":8},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/README.md":{"language":"Markdown","code":39,"comment":0,"blank":19},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/utils/upcValidation.ts":{"language":"TypeScript","code":43,"comment":24,"blank":7},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/utils/validation-helper.js":{"language":"JavaScript","code":28,"comment":7,"blank":9},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/utils/validationUtils.ts":{"language":"TypeScript","code":101,"comment":59,"blank":24},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/utils/dataMutations.ts":{"language":"TypeScript","code":124,"comment":4,"blank":14},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/utils/errorUtils.ts":{"language":"TypeScript","code":21,"comment":15,"blank":5},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/index.tsx":{"language":"TypeScript JSX","code":20,"comment":6,"blank":2},"file:///Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/types/index.ts":{"language":"TypeScript","code":16,"comment":4,"blank":4}} \ No newline at end of file diff --git a/.VSCodeCounter/2025-03-17_16-24-17/results.md b/.VSCodeCounter/2025-03-17_16-24-17/results.md new file mode 100644 index 0000000..31b4059 --- /dev/null +++ b/.VSCodeCounter/2025-03-17_16-24-17/results.md @@ -0,0 +1,31 @@ +# Summary + +Date : 2025-03-17 16:24:17 + +Directory /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew + +Total : 26 files, 6193 codes, 1008 comments, 1017 blanks, all 8218 lines + +Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md) + +## Languages +| language | files | code | comment | blank | total | +| :--- | ---: | ---: | ---: | ---: | ---: | +| TypeScript JSX | 18 | 5,817 | 895 | 934 | 7,646 | +| TypeScript | 6 | 309 | 106 | 55 | 470 | +| Markdown | 1 | 39 | 0 | 19 | 58 | +| JavaScript | 1 | 28 | 7 | 9 | 44 | + +## Directories +| path | files | code | comment | blank | total | +| :--- | ---: | ---: | ---: | ---: | ---: | +| . | 26 | 6,193 | 1,008 | 1,017 | 8,218 | +| . (Files) | 3 | 63 | 6 | 22 | 91 | +| components | 11 | 3,357 | 403 | 410 | 4,170 | +| components (Files) | 6 | 2,124 | 245 | 252 | 2,621 | +| components/cells | 5 | 1,233 | 158 | 158 | 1,549 | +| hooks | 6 | 2,440 | 486 | 522 | 3,448 | +| types | 1 | 16 | 4 | 4 | 24 | +| utils | 5 | 317 | 109 | 59 | 485 | + +Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2025-03-17_16-24-17/results.txt b/.VSCodeCounter/2025-03-17_16-24-17/results.txt new file mode 100644 index 0000000..b75c52c --- /dev/null +++ b/.VSCodeCounter/2025-03-17_16-24-17/results.txt @@ -0,0 +1,60 @@ +Date : 2025-03-17 16:24:17 +Directory : /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew +Total : 26 files, 6193 codes, 1008 comments, 1017 blanks, all 8218 lines + +Languages ++----------------+------------+------------+------------+------------+------------+ +| language | files | code | comment | blank | total | ++----------------+------------+------------+------------+------------+------------+ +| TypeScript JSX | 18 | 5,817 | 895 | 934 | 7,646 | +| TypeScript | 6 | 309 | 106 | 55 | 470 | +| Markdown | 1 | 39 | 0 | 19 | 58 | +| JavaScript | 1 | 28 | 7 | 9 | 44 | ++----------------+------------+------------+------------+------------+------------+ + +Directories ++------------------------------------------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ +| path | files | code | comment | blank | total | ++------------------------------------------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ +| . | 26 | 6,193 | 1,008 | 1,017 | 8,218 | +| . (Files) | 3 | 63 | 6 | 22 | 91 | +| components | 11 | 3,357 | 403 | 410 | 4,170 | +| components (Files) | 6 | 2,124 | 245 | 252 | 2,621 | +| components/cells | 5 | 1,233 | 158 | 158 | 1,549 | +| hooks | 6 | 2,440 | 486 | 522 | 3,448 | +| types | 1 | 16 | 4 | 4 | 24 | +| utils | 5 | 317 | 109 | 59 | 485 | ++------------------------------------------------------------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ + +Files ++------------------------------------------------------------------------------------------------------------------------------------------+----------------+------------+------------+------------+------------+ +| filename | language | code | comment | blank | total | ++------------------------------------------------------------------------------------------------------------------------------------------+----------------+------------+------------+------------+------------+ +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/README.md | Markdown | 39 | 0 | 19 | 58 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/AiValidationDialogs.tsx | TypeScript JSX | 230 | 10 | 8 | 248 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/BaseCellContent.tsx | TypeScript JSX | 18 | 0 | 3 | 21 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/SearchableTemplateSelect.tsx | TypeScript JSX | 273 | 19 | 37 | 329 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationCell.tsx | TypeScript JSX | 374 | 42 | 44 | 460 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationContainer.tsx | TypeScript JSX | 730 | 126 | 106 | 962 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationTable.tsx | TypeScript JSX | 499 | 48 | 54 | 601 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/cells/CheckboxCell.tsx | TypeScript JSX | 112 | 12 | 21 | 145 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/cells/InputCell.tsx | TypeScript JSX | 232 | 31 | 32 | 295 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/cells/MultiSelectCell.tsx | TypeScript JSX | 407 | 56 | 52 | 515 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/cells/MultilineInput.tsx | TypeScript JSX | 193 | 23 | 22 | 238 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/cells/SelectCell.tsx | TypeScript JSX | 289 | 36 | 31 | 356 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useAiValidation.tsx | TypeScript JSX | 500 | 75 | 89 | 664 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useProductLinesFetching.tsx | TypeScript JSX | 248 | 69 | 74 | 391 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useTemplates.tsx | TypeScript JSX | 204 | 26 | 33 | 263 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useUpcValidation.tsx | TypeScript JSX | 209 | 49 | 50 | 308 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useValidation.tsx | TypeScript JSX | 219 | 39 | 47 | 305 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useValidationState.tsx | TypeScript JSX | 1,060 | 228 | 229 | 1,517 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/index.tsx | TypeScript JSX | 20 | 6 | 2 | 28 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/types.ts | TypeScript | 4 | 0 | 1 | 5 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/types/index.ts | TypeScript | 16 | 4 | 4 | 24 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/utils/dataMutations.ts | TypeScript | 124 | 4 | 14 | 142 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/utils/errorUtils.ts | TypeScript | 21 | 15 | 5 | 41 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/utils/upcValidation.ts | TypeScript | 43 | 24 | 7 | 74 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/utils/validation-helper.js | JavaScript | 28 | 7 | 9 | 44 | +| /Users/matt/Dev/inventory/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/utils/validationUtils.ts | TypeScript | 101 | 59 | 24 | 184 | +| Total | | 6,193 | 1,008 | 1,017 | 8,218 | ++------------------------------------------------------------------------------------------------------------------------------------------+----------------+------------+------------+------------+------------+ \ No newline at end of file diff --git a/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/SaveTemplateDialog.tsx b/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/SaveTemplateDialog.tsx deleted file mode 100644 index dbb2b3d..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/SaveTemplateDialog.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import React, { useState } from 'react' -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog" -import { Button } from '@/components/ui/button' -import { Input } from '@/components/ui/input' - -interface SaveTemplateDialogProps { - isOpen: boolean - onClose: () => void - onSave: (company: string, productType: string) => void -} - -const SaveTemplateDialog: React.FC = ({ - isOpen, - onClose, - onSave, -}) => { - const [company, setCompany] = useState("") - const [productType, setProductType] = useState("") - - return ( - { - if (!open) { - onClose() - setCompany("") - setProductType("") - } - }}> - - - Save as Template - - Enter the company and product type for this template. - - -
-
- - setCompany(e.target.value)} - placeholder="Enter company name" - /> -
-
- - setProductType(e.target.value)} - placeholder="Enter product type" - /> -
-
- - - - -
-
- ) -} - -export default SaveTemplateDialog \ No newline at end of file diff --git a/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/TemplateManager.tsx b/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/TemplateManager.tsx deleted file mode 100644 index 6d80b97..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/TemplateManager.tsx +++ /dev/null @@ -1,212 +0,0 @@ -import React, { useState } from 'react' -import { Button } from '@/components/ui/button' -import { Input } from '@/components/ui/input' -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from '@/components/ui/dialog' -import { - Popover, - PopoverContent, - PopoverTrigger -} from '@/components/ui/popover' -import { - Command, - CommandEmpty, - CommandGroup, - CommandInput, - CommandItem, - CommandList -} from '@/components/ui/command' -import { ChevronsUpDown, Check } from 'lucide-react' -import { cn } from '@/lib/utils' -import { Template } from '../hooks/useValidationState' -import { toast } from 'sonner' - -interface TemplateManagerProps { - templates: Template[] - selectedTemplateId: string | null - onSelectTemplate: (templateId: string | null) => void - onSaveTemplate: (name: string, type: string) => void - onApplyTemplate: (templateId: string) => void - showDialog: boolean - onCloseDialog: () => void - selectedCount: number -} - -const TemplateManager: React.FC = ({ - templates, - selectedTemplateId, - onSelectTemplate, - onSaveTemplate, - onApplyTemplate, - showDialog, - onCloseDialog, - selectedCount, -}) => { - const [templateName, setTemplateName] = useState('') - const [templateType, setTemplateType] = useState('') - const [open, setOpen] = useState(false) - const [searchQuery, setSearchQuery] = useState('') - - // Filter templates based on search - const filteredTemplates = searchQuery - ? templates.filter(template => - template.product_type.toLowerCase().includes(searchQuery.toLowerCase()) || - template.company.toLowerCase().includes(searchQuery.toLowerCase()) - ) - : templates - - const handleSaveTemplate = () => { - if (!templateName.trim()) { - toast.error('Please enter a template name') - return - } - - if (!templateType.trim()) { - toast.error('Please select a template type') - return - } - - onSaveTemplate(templateName, templateType) - setTemplateName('') - setTemplateType('') - } - - // Get display text for template - const getTemplateDisplayText = (template: Template) => { - return `${template.product_type} - ${template.company}` - } - - // Find the currently selected template - const selectedTemplate = templates.find(t => t.id.toString() === selectedTemplateId) - - return ( - <> -
-
- - - - - - - - - - No templates found. - - {filteredTemplates.map((template) => ( - { - onSelectTemplate(value) - setOpen(false) - setSearchQuery('') - }} - > - - {getTemplateDisplayText(template)} - - ))} - - - - - -
- -
- - -
-
- - {/* Template Save Dialog */} - - - - Save Template - - Create a template from the selected row. - - - -
-
- - setTemplateName(e.target.value)} - placeholder="Enter company name" - /> -
- -
- - setTemplateType(e.target.value)} - placeholder="Enter product type" - /> -
-
- - - - - -
-
- - ) -} - -export default TemplateManager \ No newline at end of file diff --git a/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationContainer.tsx b/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationContainer.tsx index d28fc78..34955ac 100644 --- a/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationContainer.tsx +++ b/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/components/ValidationContainer.tsx @@ -16,6 +16,7 @@ import { TemplateForm } from '@/components/templates/TemplateForm' import axios from 'axios' import { RowSelectionState } from '@tanstack/react-table' import { useUpcValidation } from '../hooks/useUpcValidation' +import { useProductLinesFetching } from '../hooks/useProductLinesFetching' /** * ValidationContainer component - the main wrapper for the validation step @@ -60,18 +61,15 @@ const ValidationContainer = ({ fields, isLoadingTemplates } = validationState - // Add state for tracking product lines and sublines per row - const [rowProductLines, setRowProductLines] = useState>({}); - const [rowSublines, setRowSublines] = useState>({}); - // These variables are used in the fetchProductLines and fetchSublines functions - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [isLoadingLines, setIsLoadingLines] = useState>({}); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [isLoadingSublines, setIsLoadingSublines] = useState>({}); - - // Add caches for product lines and sublines by company/line ID - const [companyLinesCache, setCompanyLinesCache] = useState>({}); - const [lineSublineCache, setLineSublineCache] = useState>({}); + // Use product lines fetching hook + const { + rowProductLines, + rowSublines, + isLoadingLines, + isLoadingSublines, + fetchProductLines, + fetchSublines + } = useProductLinesFetching(data); // Add state for tracking cells in loading state const [validatingCells, setValidatingCells] = useState>(new Set()); @@ -85,556 +83,6 @@ const ValidationContainer = ({ // Apply all pending updates to the data state const applyItemNumbersToData = upcValidation.applyItemNumbersToData; - // Function to fetch product lines for a specific company - memoized - const fetchProductLines = useCallback(async (rowIndex: string | number, companyId: string) => { - try { - // Only fetch if we have a valid company ID - if (!companyId) return; - - console.log(`Fetching product lines for row ${rowIndex}, company ${companyId}`); - - // Check if we already have this company's lines in the cache - if (companyLinesCache[companyId]) { - console.log(`Using cached product lines for company ${companyId}`); - // Use cached data - setRowProductLines(prev => ({ ...prev, [rowIndex]: companyLinesCache[companyId] })); - return companyLinesCache[companyId]; - } - - // Set loading state for this row - setIsLoadingLines(prev => ({ ...prev, [rowIndex]: true })); - - // Fetch product lines from API - const productLinesUrl = `/api/import/product-lines/${companyId}`; - console.log(`Fetching from URL: ${productLinesUrl}`); - const response = await axios.get(productLinesUrl); - if (response.status !== 200) { - throw new Error(`Failed to fetch product lines: ${response.status}`); - } - - const productLines = response.data; - console.log(`Received ${productLines.length} product lines for company ${companyId}`); - - // Store in company cache - setCompanyLinesCache(prev => ({ ...prev, [companyId]: productLines })); - - // Store for this specific row - setRowProductLines(prev => ({ ...prev, [rowIndex]: productLines })); - - return productLines; - } catch (error) { - console.error(`Error fetching product lines for company ${companyId}:`, error); - - // Set empty array for this company to prevent repeated failed requests - setCompanyLinesCache(prev => ({ ...prev, [companyId]: [] })); - - // Store empty array for this specific row - setRowProductLines(prev => ({ ...prev, [rowIndex]: [] })); - - return []; - } finally { - // Clear loading state - setIsLoadingLines(prev => ({ ...prev, [rowIndex]: false })); - } - }, [companyLinesCache]); - - // Function to fetch sublines for a specific line - memoized - const fetchSublines = useCallback(async (rowIndex: string | number, lineId: string) => { - try { - // Only fetch if we have a valid line ID - if (!lineId) return; - - console.log(`Fetching sublines for row ${rowIndex}, line ${lineId}`); - - // Check if we already have this line's sublines in the cache - if (lineSublineCache[lineId]) { - console.log(`Using cached sublines for line ${lineId}`); - // Use cached data - setRowSublines(prev => ({ ...prev, [rowIndex]: lineSublineCache[lineId] })); - return lineSublineCache[lineId]; - } - - // Set loading state for this row - setIsLoadingSublines(prev => ({ ...prev, [rowIndex]: true })); - - // Fetch sublines from API - const sublinesUrl = `/api/import/sublines/${lineId}`; - console.log(`Fetching from URL: ${sublinesUrl}`); - const response = await axios.get(sublinesUrl); - if (response.status !== 200) { - throw new Error(`Failed to fetch sublines: ${response.status}`); - } - - const sublines = response.data; - console.log(`Received ${sublines.length} sublines for line ${lineId}`); - - // Store in line cache - setLineSublineCache(prev => ({ ...prev, [lineId]: sublines })); - - // Store for this specific row - setRowSublines(prev => ({ ...prev, [rowIndex]: sublines })); - - return sublines; - } catch (error) { - console.error(`Error fetching sublines for line ${lineId}:`, error); - - // Set empty array for this line to prevent repeated failed requests - setLineSublineCache(prev => ({ ...prev, [lineId]: [] })); - - // Store empty array for this specific row - setRowSublines(prev => ({ ...prev, [rowIndex]: [] })); - - return []; - } finally { - // Clear loading state - setIsLoadingSublines(prev => ({ ...prev, [rowIndex]: false })); - } - }, [lineSublineCache]); - - // Enhanced updateRow function - memoized - const enhancedUpdateRow = useCallback(async (rowIndex: number, fieldKey: T, value: any) => { - // Process value before updating data - console.log(`enhancedUpdateRow called: rowIndex=${rowIndex}, fieldKey=${fieldKey}, value=`, value); - let processedValue = value; - - // Strip dollar signs from price fields - if ((fieldKey === 'msrp' || fieldKey === 'cost_each') && typeof value === 'string') { - processedValue = value.replace(/[$,]/g, ''); - - // Also ensure it's a valid number - const numValue = parseFloat(processedValue); - if (!isNaN(numValue)) { - processedValue = numValue.toFixed(2); - } - } - - // Save current scroll position - const scrollPosition = { - left: window.scrollX, - top: window.scrollY - }; - - // Find the original index in the data array - const rowData = filteredData[rowIndex]; - const originalIndex = data.findIndex(item => item.__index === rowData?.__index); - - if (originalIndex === -1) { - // If we can't find the original row, just do a simple update - updateRow(rowIndex, fieldKey, processedValue); - } else { - // Update the data directly - setData(prevData => { - const newData = [...prevData]; - const updatedRow = { - ...newData[originalIndex], - [fieldKey]: processedValue - }; - - newData[originalIndex] = updatedRow; - return newData; - }); - } - - // Restore scroll position after update - setTimeout(() => { - window.scrollTo(scrollPosition.left, scrollPosition.top); - }, 0); - - // Now handle any additional logic for specific fields - if (fieldKey === 'company' && value) { - // Clear any existing line/subline values for this row if company changes - if (originalIndex !== -1) { - setData(prevData => { - const newData = [...prevData]; - newData[originalIndex] = { - ...newData[originalIndex], - line: undefined, - subline: undefined - }; - return newData; - }); - } - - // Use cached product lines if available, otherwise fetch - if (rowData && rowData.__index) { - const companyId = value.toString(); - if (companyLinesCache[companyId]) { - // Use cached data - console.log(`Using cached product lines for company ${companyId}`); - setRowProductLines(prev => ({ - ...prev, - [rowData.__index as string]: companyLinesCache[companyId] - })); - } else { - // Fetch product lines for the new company - setTimeout(async () => { - if (value !== undefined) { - await fetchProductLines(rowData.__index as string, companyId); - } - }, 0); - } - } - } - - // If updating supplier field AND there's a UPC value, validate UPC - if (fieldKey === 'supplier' && value && rowData) { - const rowDataAny = rowData as Record; - if (rowDataAny.upc || rowDataAny.barcode) { - const upcValue = rowDataAny.upc || rowDataAny.barcode; - - try { - // Mark the item_number cell as being validated - setValidatingCells(prev => { - const newSet = new Set(prev); - newSet.add(`${rowIndex}-item_number`); - return newSet; - }); - - // Use supplier ID (the value being set) to validate UPC - await upcValidation.validateUpc(rowIndex, value.toString(), upcValue.toString()); - } catch (error) { - console.error('Error validating UPC:', error); - } finally { - // Clear validation state for the item_number cell - setValidatingCells(prev => { - const newSet = new Set(prev); - newSet.delete(`${rowIndex}-item_number`); - return newSet; - }); - } - } - } - - // If updating line field, fetch sublines - if (fieldKey === 'line' && value) { - // Clear any existing subline value for this row - if (originalIndex !== -1) { - setData(prevData => { - const newData = [...prevData]; - newData[originalIndex] = { - ...newData[originalIndex], - subline: undefined - }; - return newData; - }); - } - - // Use cached sublines if available, otherwise fetch - if (rowData && rowData.__index) { - const lineId = value.toString(); - if (lineSublineCache[lineId]) { - // Use cached data - console.log(`Using cached sublines for line ${lineId}`); - setRowSublines(prev => ({ - ...prev, - [rowData.__index as string]: lineSublineCache[lineId] - })); - } else { - // Fetch sublines for the new line - setTimeout(async () => { - if (value !== undefined) { - await fetchSublines(rowData.__index as string, lineId); - } - }, 0); - } - } - } - - // If updating UPC/barcode field AND there's a supplier value, validate UPC - if ((fieldKey === 'upc' || fieldKey === 'barcode') && value && rowData) { - const rowDataAny = rowData as Record; - if (rowDataAny.supplier) { - try { - // Mark the item_number cell as being validated - setValidatingCells(prev => { - const newSet = new Set(prev); - newSet.add(`${rowIndex}-item_number`); - return newSet; - }); - - // Use supplier ID from the row data to validate UPC - await upcValidation.validateUpc(rowIndex, rowDataAny.supplier.toString(), value.toString()); - } catch (error) { - console.error('Error validating UPC:', error); - } finally { - // Clear validation state for the item_number cell - setValidatingCells(prev => { - const newSet = new Set(prev); - newSet.delete(`${rowIndex}-item_number`); - return newSet; - }); - } - } - } - }, [data, filteredData, updateRow, fetchProductLines, fetchSublines, setData, companyLinesCache, lineSublineCache, upcValidation]); - - // When data changes, fetch product lines and sublines for rows that have company/line values - useEffect(() => { - // Skip if there's no data - if (!data.length) return; - - console.log("Starting to fetch product lines and sublines"); - - // Group rows by company and line to minimize API calls - const companiesNeeded = new Map(); // company ID -> row IDs - const linesNeeded = new Map(); // line ID -> row IDs - - data.forEach(row => { - const rowId = row.__index; - if (!rowId) return; // Skip rows without an index - - // If row has company but no product lines fetched yet - if (row.company && !rowProductLines[rowId]) { - const companyId = row.company.toString(); - if (!companiesNeeded.has(companyId)) { - companiesNeeded.set(companyId, []); - } - companiesNeeded.get(companyId)?.push(rowId); - } - - // If row has line but no sublines fetched yet - if (row.line && !rowSublines[rowId]) { - const lineId = row.line.toString(); - if (!linesNeeded.has(lineId)) { - linesNeeded.set(lineId, []); - } - linesNeeded.get(lineId)?.push(rowId); - } - }); - - console.log(`Need to fetch product lines for ${companiesNeeded.size} companies and sublines for ${linesNeeded.size} lines`); - - // Create arrays to hold all fetch promises - const fetchPromises: Promise[] = []; - - // Set initial loading states for all affected rows - const lineLoadingUpdates: Record = {}; - const sublineLoadingUpdates: Record = {}; - - // Process companies that need product lines - companiesNeeded.forEach((rowIds, companyId) => { - // Skip if already in cache - if (companyLinesCache[companyId]) { - console.log(`Using cached product lines for company ${companyId}`); - // Use cached data for all rows with this company - const lines = companyLinesCache[companyId]; - const updates: Record = {}; - rowIds.forEach(rowId => { - updates[rowId] = lines; - }); - setRowProductLines(prev => ({ ...prev, ...updates })); - return; - } - - // Set loading state for all affected rows - rowIds.forEach(rowId => { - lineLoadingUpdates[rowId] = true; - }); - - // Create fetch promise - const fetchPromise = (async () => { - // Safety timeout to ensure loading state is cleared after 10 seconds - const timeoutId = setTimeout(() => { - console.log(`Safety timeout triggered for company ${companyId}`); - const clearLoadingUpdates: Record = {}; - rowIds.forEach(rowId => { - clearLoadingUpdates[rowId] = false; - }); - setIsLoadingLines(prev => ({ ...prev, ...clearLoadingUpdates })); - - // Set empty cache to prevent repeated requests - setCompanyLinesCache(prev => ({ ...prev, [companyId]: [] })); - - // Update rows with empty array - const updates: Record = {}; - rowIds.forEach(rowId => { - updates[rowId] = []; - }); - setRowProductLines(prev => ({ ...prev, ...updates })); - - toast.error(`Timeout loading product lines for company ${companyId}`); - }, 10000); - - try { - console.log(`Fetching product lines for company ${companyId} (affecting ${rowIds.length} rows)`); - - // Fetch product lines from API - const productLinesUrl = `/api/import/product-lines/${companyId}`; - console.log(`Fetching from URL: ${productLinesUrl}`); - - const response = await axios.get(productLinesUrl); - console.log(`Product lines API response status for company ${companyId}:`, response.status); - - const productLines = response.data; - console.log(`Received ${productLines.length} product lines for company ${companyId}`); - - // Store in company cache - setCompanyLinesCache(prev => ({ ...prev, [companyId]: productLines })); - - // Update all rows with this company - const updates: Record = {}; - rowIds.forEach(rowId => { - updates[rowId] = productLines; - }); - setRowProductLines(prev => ({ ...prev, ...updates })); - } catch (error) { - console.error(`Error fetching product lines for company ${companyId}:`, error); - - // Set empty array for this company to prevent repeated failed requests - setCompanyLinesCache(prev => ({ ...prev, [companyId]: [] })); - - // Update rows with empty array - const updates: Record = {}; - rowIds.forEach(rowId => { - updates[rowId] = []; - }); - setRowProductLines(prev => ({ ...prev, ...updates })); - - // Show error toast - toast.error(`Failed to load product lines for company ${companyId}`); - } finally { - // Clear the safety timeout - clearTimeout(timeoutId); - - // Clear loading state for all affected rows - const clearLoadingUpdates: Record = {}; - rowIds.forEach(rowId => { - clearLoadingUpdates[rowId] = false; - }); - setIsLoadingLines(prev => ({ ...prev, ...clearLoadingUpdates })); - } - })(); - - fetchPromises.push(fetchPromise); - }); - - // Process lines that need sublines - linesNeeded.forEach((rowIds, lineId) => { - // Skip if already in cache - if (lineSublineCache[lineId]) { - console.log(`Using cached sublines for line ${lineId}`); - // Use cached data for all rows with this line - const sublines = lineSublineCache[lineId]; - const updates: Record = {}; - rowIds.forEach(rowId => { - updates[rowId] = sublines; - }); - setRowSublines(prev => ({ ...prev, ...updates })); - return; - } - - // Set loading state for all affected rows - rowIds.forEach(rowId => { - sublineLoadingUpdates[rowId] = true; - }); - - // Create fetch promise - const fetchPromise = (async () => { - // Safety timeout to ensure loading state is cleared after 10 seconds - const timeoutId = setTimeout(() => { - console.log(`Safety timeout triggered for line ${lineId}`); - const clearLoadingUpdates: Record = {}; - rowIds.forEach(rowId => { - clearLoadingUpdates[rowId] = false; - }); - setIsLoadingSublines(prev => ({ ...prev, ...clearLoadingUpdates })); - - // Set empty cache to prevent repeated requests - setLineSublineCache(prev => ({ ...prev, [lineId]: [] })); - - // Update rows with empty array - const updates: Record = {}; - rowIds.forEach(rowId => { - updates[rowId] = []; - }); - setRowSublines(prev => ({ ...prev, ...updates })); - - toast.error(`Timeout loading sublines for line ${lineId}`); - }, 10000); - - try { - console.log(`Fetching sublines for line ${lineId} (affecting ${rowIds.length} rows)`); - - // Fetch sublines from API - const sublinesUrl = `/api/import/sublines/${lineId}`; - console.log(`Fetching from URL: ${sublinesUrl}`); - - const response = await axios.get(sublinesUrl); - console.log(`Sublines API response status for line ${lineId}:`, response.status); - - const sublines = response.data; - console.log(`Received ${sublines.length} sublines for line ${lineId}`); - - // Store in line cache - setLineSublineCache(prev => ({ ...prev, [lineId]: sublines })); - - // Update all rows with this line - const updates: Record = {}; - rowIds.forEach(rowId => { - updates[rowId] = sublines; - }); - setRowSublines(prev => ({ ...prev, ...updates })); - } catch (error) { - console.error(`Error fetching sublines for line ${lineId}:`, error); - - // Set empty array for this line to prevent repeated failed requests - setLineSublineCache(prev => ({ ...prev, [lineId]: [] })); - - // Update rows with empty array - const updates: Record = {}; - rowIds.forEach(rowId => { - updates[rowId] = []; - }); - setRowSublines(prev => ({ ...prev, ...updates })); - - // Show error toast - toast.error(`Failed to load sublines for line ${lineId}`); - } finally { - // Clear the safety timeout - clearTimeout(timeoutId); - - // Clear loading state for all affected rows - const clearLoadingUpdates: Record = {}; - rowIds.forEach(rowId => { - clearLoadingUpdates[rowId] = false; - }); - setIsLoadingSublines(prev => ({ ...prev, ...clearLoadingUpdates })); - } - })(); - - fetchPromises.push(fetchPromise); - }); - - // Set initial loading states - if (Object.keys(lineLoadingUpdates).length > 0) { - console.log(`Setting loading state for ${Object.keys(lineLoadingUpdates).length} rows (product lines)`); - setIsLoadingLines(prev => ({ ...prev, ...lineLoadingUpdates })); - } - if (Object.keys(sublineLoadingUpdates).length > 0) { - console.log(`Setting loading state for ${Object.keys(sublineLoadingUpdates).length} rows (sublines)`); - setIsLoadingSublines(prev => ({ ...prev, ...sublineLoadingUpdates })); - } - - // Run all fetch operations in parallel - Promise.all(fetchPromises).then(() => { - console.log("All product lines and sublines fetch operations completed"); - }).catch(error => { - console.error('Error in fetch operations:', error); - }); - - }, [data, rowProductLines, rowSublines, companyLinesCache, lineSublineCache]); - - // Use UPC validation when data changes - useEffect(() => { - // Skip if there's no data or already validated - if (data.length === 0 || upcValidation.initialValidationDone) return; - - // Run validation immediately without timeout - upcValidation.validateAllUPCs(); - - // No cleanup needed since we're not using a timer - }, [data, upcValidation]); - // Use AI validation hook const aiValidation = useAiValidation( data, @@ -885,11 +333,171 @@ const ValidationContainer = ({ setRowSelection(newSelection); }, [setRowSelection]); - const handleUpdateRow = useCallback((rowIndex: number, key: T, value: any) => { - enhancedUpdateRow(rowIndex, key, value); - }, [enhancedUpdateRow]); - - // Enhanced copy down that uses enhancedUpdateRow instead of regular updateRow + const handleUpdateRow = useCallback(async (rowIndex: number, key: T, value: any) => { + // Process value before updating data + console.log(`enhancedUpdateRow called: rowIndex=${rowIndex}, fieldKey=${key}, value=`, value); + let processedValue = value; + + // Strip dollar signs from price fields + if ((key === 'msrp' || key === 'cost_each') && typeof value === 'string') { + processedValue = value.replace(/[$,]/g, ''); + + // Also ensure it's a valid number + const numValue = parseFloat(processedValue); + if (!isNaN(numValue)) { + processedValue = numValue.toFixed(2); + } + } + + // Save current scroll position + const scrollPosition = { + left: window.scrollX, + top: window.scrollY + }; + + // Find the original index in the data array + const rowData = filteredData[rowIndex]; + const originalIndex = data.findIndex(item => item.__index === rowData?.__index); + + if (originalIndex === -1) { + // If we can't find the original row, just do a simple update + updateRow(rowIndex, key, processedValue); + } else { + // Update the data directly + setData(prevData => { + const newData = [...prevData]; + const updatedRow = { + ...newData[originalIndex], + [key]: processedValue + }; + + newData[originalIndex] = updatedRow; + return newData; + }); + } + + // Restore scroll position after update + setTimeout(() => { + window.scrollTo(scrollPosition.left, scrollPosition.top); + }, 0); + + // Now handle any additional logic for specific fields + if (key === 'company' && value) { + // Clear any existing line/subline values for this row if company changes + if (originalIndex !== -1) { + setData(prevData => { + const newData = [...prevData]; + newData[originalIndex] = { + ...newData[originalIndex], + line: undefined, + subline: undefined + }; + return newData; + }); + } + + // Use cached product lines if available, otherwise fetch + if (rowData && rowData.__index) { + const companyId = value.toString(); + if (rowProductLines[companyId]) { + // Use cached data + console.log(`Using cached product lines for company ${companyId}`); + } else { + // Fetch product lines for the new company + if (value !== undefined) { + await fetchProductLines(rowData.__index as string, companyId); + } + } + } + } + + // If updating supplier field AND there's a UPC value, validate UPC + if (key === 'supplier' && value && rowData) { + const rowDataAny = rowData as Record; + if (rowDataAny.upc || rowDataAny.barcode) { + const upcValue = rowDataAny.upc || rowDataAny.barcode; + + try { + // Mark the item_number cell as being validated + setValidatingCells(prev => { + const newSet = new Set(prev); + newSet.add(`${rowIndex}-item_number`); + return newSet; + }); + + // Use supplier ID (the value being set) to validate UPC + await upcValidation.validateUpc(rowIndex, value.toString(), upcValue.toString()); + } catch (error) { + console.error('Error validating UPC:', error); + } finally { + // Clear validation state for the item_number cell + setValidatingCells(prev => { + const newSet = new Set(prev); + newSet.delete(`${rowIndex}-item_number`); + return newSet; + }); + } + } + } + + // If updating line field, fetch sublines + if (key === 'line' && value) { + // Clear any existing subline value for this row + if (originalIndex !== -1) { + setData(prevData => { + const newData = [...prevData]; + newData[originalIndex] = { + ...newData[originalIndex], + subline: undefined + }; + return newData; + }); + } + + // Use cached sublines if available, otherwise fetch + if (rowData && rowData.__index) { + const lineId = value.toString(); + if (rowSublines[lineId]) { + // Use cached data + console.log(`Using cached sublines for line ${lineId}`); + } else { + // Fetch sublines for the new line + if (value !== undefined) { + await fetchSublines(rowData.__index as string, lineId); + } + } + } + } + + // If updating UPC/barcode field AND there's a supplier value, validate UPC + if ((key === 'upc' || key === 'barcode') && value && rowData) { + const rowDataAny = rowData as Record; + if (rowDataAny.supplier) { + try { + // Mark the item_number cell as being validated + setValidatingCells(prev => { + const newSet = new Set(prev); + newSet.add(`${rowIndex}-item_number`); + return newSet; + }); + + // Use supplier ID from the row data to validate UPC + await upcValidation.validateUpc(rowIndex, rowDataAny.supplier.toString(), value.toString()); + } catch (error) { + console.error('Error validating UPC:', error); + } finally { + // Clear validation state for the item_number cell + setValidatingCells(prev => { + const newSet = new Set(prev); + newSet.delete(`${rowIndex}-item_number`); + return newSet; + }); + } + } + } + }, [data, filteredData, updateRow, fetchProductLines, fetchSublines, setData, rowProductLines, rowSublines, upcValidation]); + + // Create a separate copyDown function that uses handleUpdateRow const handleCopyDown = useCallback((rowIndex: number, fieldKey: string, endRowIndex?: number) => { // Get the value to copy from the source row const sourceRow = data[rowIndex]; @@ -923,7 +531,7 @@ const ValidationContainer = ({ const targetRowIndex = rowIndex + 1 + i; // Update the row with the copied value - enhancedUpdateRow(targetRowIndex, fieldKey as T, valueCopy); + handleUpdateRow(targetRowIndex, fieldKey as T, valueCopy); // Remove loading state setValidatingCells(prev => { @@ -932,8 +540,19 @@ const ValidationContainer = ({ return newSet; }); }); - }, [data, enhancedUpdateRow, setValidatingCells]); + }, [data, handleUpdateRow, setValidatingCells]); + // Use UPC validation when data changes + useEffect(() => { + // Skip if there's no data or already validated + if (data.length === 0 || upcValidation.initialValidationDone) return; + + // Run validation immediately without timeout + upcValidation.validateAllUPCs(); + + // No cleanup needed since we're not using a timer + }, [data, upcValidation]); + // Memoize the enhanced validation table component const EnhancedValidationTable = useMemo(() => React.memo((props: React.ComponentProps) => { // Create validatingCells set from validating rows, but only for item_number fields @@ -993,7 +612,7 @@ const ValidationContainer = ({ fields={fields as unknown as Fields} rowSelection={rowSelection} setRowSelection={handleRowSelectionChange as React.Dispatch>} - updateRow={handleUpdateRow as (rowIndex: number, key: string, value: any) => void} + updateRow={handleUpdateRow as unknown as (rowIndex: number, key: string, value: any) => void} validationErrors={validationErrors} isValidatingUpc={isRowValidatingUpc} validatingUpcRows={Array.from(upcValidation.validatingRows)} diff --git a/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useFilters.tsx b/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useFilters.tsx deleted file mode 100644 index 01c7717..0000000 --- a/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useFilters.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import { useState, useCallback, useMemo } from 'react' -import type { Fields } from '../../../types' -import { RowData } from './useValidationState' - -export interface FilterState { - searchText: string - showErrorsOnly: boolean - filterField: string | null - filterValue: string | null -} - -export const useFilters = ( - data: RowData[], - fields: Fields, - validationErrors: Map> -) => { - // Filter state - const [filters, setFilters] = useState({ - searchText: '', - showErrorsOnly: false, - filterField: null, - filterValue: null - }) - - // Get available filter fields - const filterFields = useMemo(() => { - return fields.map(field => ({ - key: field.key, - label: field.label - })) - }, [fields]) - - // Get available filter values for the selected field - const filterValues = useMemo(() => { - if (!filters.filterField) return [] - - // Get unique values for the selected field - const uniqueValues = new Set() - - data.forEach(row => { - const value = row[filters.filterField as keyof typeof row] - if (value !== undefined && value !== null) { - uniqueValues.add(String(value)) - } - }) - - return Array.from(uniqueValues).map(value => ({ - value, - label: value - })) - }, [data, filters.filterField]) - - // Update filters - const updateFilters = useCallback((newFilters: Partial) => { - setFilters(prev => ({ - ...prev, - ...newFilters - })) - }, []) - - // Apply filters to data - const applyFilters = useCallback((dataToFilter: RowData[]) => { - return dataToFilter.filter((row, index) => { - // Filter by search text - if (filters.searchText) { - const lowerSearchText = filters.searchText.toLowerCase() - const matchesSearch = Object.entries(row).some(([key, value]) => { - // Skip metadata fields - if (key.startsWith('__')) return false - - // Check if the value contains the search text - return value !== undefined && - value !== null && - String(value).toLowerCase().includes(lowerSearchText) - }) - - if (!matchesSearch) return false - } - - // Filter by errors - if (filters.showErrorsOnly) { - const hasErrors = validationErrors.has(index) && - Object.keys(validationErrors.get(index) || {}).length > 0 - if (!hasErrors) return false - } - - // Filter by field value - if (filters.filterField && filters.filterValue) { - const fieldValue = row[filters.filterField as keyof typeof row] - return fieldValue !== undefined && - fieldValue !== null && - String(fieldValue) === filters.filterValue - } - - return true - }) - }, [filters, validationErrors]) - - // Reset all filters - const resetFilters = useCallback(() => { - setFilters({ - searchText: '', - showErrorsOnly: false, - filterField: null, - filterValue: null - }) - }, []) - - return { - filters, - filterFields, - filterValues, - updateFilters, - applyFilters, - resetFilters - } -} \ No newline at end of file diff --git a/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useProductLinesFetching.tsx b/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useProductLinesFetching.tsx new file mode 100644 index 0000000..b4b8b96 --- /dev/null +++ b/inventory/src/lib/react-spreadsheet-import/src/steps/ValidationStepNew/hooks/useProductLinesFetching.tsx @@ -0,0 +1,391 @@ +import { useState, useCallback, useEffect } from 'react' +import axios from 'axios' +import { toast } from 'sonner' + +/** + * Custom hook for managing product lines and sublines fetching with caching + */ +export const useProductLinesFetching = (data: Record[]) => { + // State for tracking product lines and sublines per row + const [rowProductLines, setRowProductLines] = useState>({}); + const [rowSublines, setRowSublines] = useState>({}); + + // State for tracking loading states + const [isLoadingLines, setIsLoadingLines] = useState>({}); + const [isLoadingSublines, setIsLoadingSublines] = useState>({}); + + // Add caches for product lines and sublines by company/line ID + const [companyLinesCache, setCompanyLinesCache] = useState>({}); + const [lineSublineCache, setLineSublineCache] = useState>({}); + + // Function to fetch product lines for a specific company - memoized + const fetchProductLines = useCallback(async (rowIndex: string | number, companyId: string) => { + try { + // Only fetch if we have a valid company ID + if (!companyId) return; + + console.log(`Fetching product lines for row ${rowIndex}, company ${companyId}`); + + // Check if we already have this company's lines in the cache + if (companyLinesCache[companyId]) { + console.log(`Using cached product lines for company ${companyId}`); + // Use cached data + setRowProductLines(prev => ({ ...prev, [rowIndex]: companyLinesCache[companyId] })); + return companyLinesCache[companyId]; + } + + // Set loading state for this row + setIsLoadingLines(prev => ({ ...prev, [rowIndex]: true })); + + // Fetch product lines from API + const productLinesUrl = `/api/import/product-lines/${companyId}`; + console.log(`Fetching from URL: ${productLinesUrl}`); + const response = await axios.get(productLinesUrl); + if (response.status !== 200) { + throw new Error(`Failed to fetch product lines: ${response.status}`); + } + + const productLines = response.data; + console.log(`Received ${productLines.length} product lines for company ${companyId}`); + + // Store in company cache + setCompanyLinesCache(prev => ({ ...prev, [companyId]: productLines })); + + // Store for this specific row + setRowProductLines(prev => ({ ...prev, [rowIndex]: productLines })); + + return productLines; + } catch (error) { + console.error(`Error fetching product lines for company ${companyId}:`, error); + + // Set empty array for this company to prevent repeated failed requests + setCompanyLinesCache(prev => ({ ...prev, [companyId]: [] })); + + // Store empty array for this specific row + setRowProductLines(prev => ({ ...prev, [rowIndex]: [] })); + + return []; + } finally { + // Clear loading state + setIsLoadingLines(prev => ({ ...prev, [rowIndex]: false })); + } + }, [companyLinesCache]); + + // Function to fetch sublines for a specific line - memoized + const fetchSublines = useCallback(async (rowIndex: string | number, lineId: string) => { + try { + // Only fetch if we have a valid line ID + if (!lineId) return; + + console.log(`Fetching sublines for row ${rowIndex}, line ${lineId}`); + + // Check if we already have this line's sublines in the cache + if (lineSublineCache[lineId]) { + console.log(`Using cached sublines for line ${lineId}`); + // Use cached data + setRowSublines(prev => ({ ...prev, [rowIndex]: lineSublineCache[lineId] })); + return lineSublineCache[lineId]; + } + + // Set loading state for this row + setIsLoadingSublines(prev => ({ ...prev, [rowIndex]: true })); + + // Fetch sublines from API + const sublinesUrl = `/api/import/sublines/${lineId}`; + console.log(`Fetching from URL: ${sublinesUrl}`); + const response = await axios.get(sublinesUrl); + if (response.status !== 200) { + throw new Error(`Failed to fetch sublines: ${response.status}`); + } + + const sublines = response.data; + console.log(`Received ${sublines.length} sublines for line ${lineId}`); + + // Store in line cache + setLineSublineCache(prev => ({ ...prev, [lineId]: sublines })); + + // Store for this specific row + setRowSublines(prev => ({ ...prev, [rowIndex]: sublines })); + + return sublines; + } catch (error) { + console.error(`Error fetching sublines for line ${lineId}:`, error); + + // Set empty array for this line to prevent repeated failed requests + setLineSublineCache(prev => ({ ...prev, [lineId]: [] })); + + // Store empty array for this specific row + setRowSublines(prev => ({ ...prev, [rowIndex]: [] })); + + return []; + } finally { + // Clear loading state + setIsLoadingSublines(prev => ({ ...prev, [rowIndex]: false })); + } + }, [lineSublineCache]); + + // When data changes, fetch product lines and sublines for rows that have company/line values + useEffect(() => { + // Skip if there's no data + if (!data.length) return; + + console.log("Starting to fetch product lines and sublines"); + + // Group rows by company and line to minimize API calls + const companiesNeeded = new Map(); // company ID -> row IDs + const linesNeeded = new Map(); // line ID -> row IDs + + data.forEach(row => { + const rowId = row.__index; + if (!rowId) return; // Skip rows without an index + + // If row has company but no product lines fetched yet + if (row.company && !rowProductLines[rowId]) { + const companyId = row.company.toString(); + if (!companiesNeeded.has(companyId)) { + companiesNeeded.set(companyId, []); + } + companiesNeeded.get(companyId)?.push(rowId); + } + + // If row has line but no sublines fetched yet + if (row.line && !rowSublines[rowId]) { + const lineId = row.line.toString(); + if (!linesNeeded.has(lineId)) { + linesNeeded.set(lineId, []); + } + linesNeeded.get(lineId)?.push(rowId); + } + }); + + console.log(`Need to fetch product lines for ${companiesNeeded.size} companies and sublines for ${linesNeeded.size} lines`); + + // Create arrays to hold all fetch promises + const fetchPromises: Promise[] = []; + + // Set initial loading states for all affected rows + const lineLoadingUpdates: Record = {}; + const sublineLoadingUpdates: Record = {}; + + // Process companies that need product lines + companiesNeeded.forEach((rowIds, companyId) => { + // Skip if already in cache + if (companyLinesCache[companyId]) { + console.log(`Using cached product lines for company ${companyId}`); + // Use cached data for all rows with this company + const lines = companyLinesCache[companyId]; + const updates: Record = {}; + rowIds.forEach(rowId => { + updates[rowId] = lines; + }); + setRowProductLines(prev => ({ ...prev, ...updates })); + return; + } + + // Set loading state for all affected rows + rowIds.forEach(rowId => { + lineLoadingUpdates[rowId] = true; + }); + + // Create fetch promise + const fetchPromise = (async () => { + // Safety timeout to ensure loading state is cleared after 10 seconds + const timeoutId = setTimeout(() => { + console.log(`Safety timeout triggered for company ${companyId}`); + const clearLoadingUpdates: Record = {}; + rowIds.forEach(rowId => { + clearLoadingUpdates[rowId] = false; + }); + setIsLoadingLines(prev => ({ ...prev, ...clearLoadingUpdates })); + + // Set empty cache to prevent repeated requests + setCompanyLinesCache(prev => ({ ...prev, [companyId]: [] })); + + // Update rows with empty array + const updates: Record = {}; + rowIds.forEach(rowId => { + updates[rowId] = []; + }); + setRowProductLines(prev => ({ ...prev, ...updates })); + + toast.error(`Timeout loading product lines for company ${companyId}`); + }, 10000); + + try { + console.log(`Fetching product lines for company ${companyId} (affecting ${rowIds.length} rows)`); + + // Fetch product lines from API + const productLinesUrl = `/api/import/product-lines/${companyId}`; + console.log(`Fetching from URL: ${productLinesUrl}`); + + const response = await axios.get(productLinesUrl); + console.log(`Product lines API response status for company ${companyId}:`, response.status); + + const productLines = response.data; + console.log(`Received ${productLines.length} product lines for company ${companyId}`); + + // Store in company cache + setCompanyLinesCache(prev => ({ ...prev, [companyId]: productLines })); + + // Update all rows with this company + const updates: Record = {}; + rowIds.forEach(rowId => { + updates[rowId] = productLines; + }); + setRowProductLines(prev => ({ ...prev, ...updates })); + } catch (error) { + console.error(`Error fetching product lines for company ${companyId}:`, error); + + // Set empty array for this company to prevent repeated failed requests + setCompanyLinesCache(prev => ({ ...prev, [companyId]: [] })); + + // Update rows with empty array + const updates: Record = {}; + rowIds.forEach(rowId => { + updates[rowId] = []; + }); + setRowProductLines(prev => ({ ...prev, ...updates })); + + // Show error toast + toast.error(`Failed to load product lines for company ${companyId}`); + } finally { + // Clear the safety timeout + clearTimeout(timeoutId); + + // Clear loading state for all affected rows + const clearLoadingUpdates: Record = {}; + rowIds.forEach(rowId => { + clearLoadingUpdates[rowId] = false; + }); + setIsLoadingLines(prev => ({ ...prev, ...clearLoadingUpdates })); + } + })(); + + fetchPromises.push(fetchPromise); + }); + + // Process lines that need sublines + linesNeeded.forEach((rowIds, lineId) => { + // Skip if already in cache + if (lineSublineCache[lineId]) { + console.log(`Using cached sublines for line ${lineId}`); + // Use cached data for all rows with this line + const sublines = lineSublineCache[lineId]; + const updates: Record = {}; + rowIds.forEach(rowId => { + updates[rowId] = sublines; + }); + setRowSublines(prev => ({ ...prev, ...updates })); + return; + } + + // Set loading state for all affected rows + rowIds.forEach(rowId => { + sublineLoadingUpdates[rowId] = true; + }); + + // Create fetch promise + const fetchPromise = (async () => { + // Safety timeout to ensure loading state is cleared after 10 seconds + const timeoutId = setTimeout(() => { + console.log(`Safety timeout triggered for line ${lineId}`); + const clearLoadingUpdates: Record = {}; + rowIds.forEach(rowId => { + clearLoadingUpdates[rowId] = false; + }); + setIsLoadingSublines(prev => ({ ...prev, ...clearLoadingUpdates })); + + // Set empty cache to prevent repeated requests + setLineSublineCache(prev => ({ ...prev, [lineId]: [] })); + + // Update rows with empty array + const updates: Record = {}; + rowIds.forEach(rowId => { + updates[rowId] = []; + }); + setRowSublines(prev => ({ ...prev, ...updates })); + + toast.error(`Timeout loading sublines for line ${lineId}`); + }, 10000); + + try { + console.log(`Fetching sublines for line ${lineId} (affecting ${rowIds.length} rows)`); + + // Fetch sublines from API + const sublinesUrl = `/api/import/sublines/${lineId}`; + console.log(`Fetching from URL: ${sublinesUrl}`); + + const response = await axios.get(sublinesUrl); + console.log(`Sublines API response status for line ${lineId}:`, response.status); + + const sublines = response.data; + console.log(`Received ${sublines.length} sublines for line ${lineId}`); + + // Store in line cache + setLineSublineCache(prev => ({ ...prev, [lineId]: sublines })); + + // Update all rows with this line + const updates: Record = {}; + rowIds.forEach(rowId => { + updates[rowId] = sublines; + }); + setRowSublines(prev => ({ ...prev, ...updates })); + } catch (error) { + console.error(`Error fetching sublines for line ${lineId}:`, error); + + // Set empty array for this line to prevent repeated failed requests + setLineSublineCache(prev => ({ ...prev, [lineId]: [] })); + + // Update rows with empty array + const updates: Record = {}; + rowIds.forEach(rowId => { + updates[rowId] = []; + }); + setRowSublines(prev => ({ ...prev, ...updates })); + + // Show error toast + toast.error(`Failed to load sublines for line ${lineId}`); + } finally { + // Clear the safety timeout + clearTimeout(timeoutId); + + // Clear loading state for all affected rows + const clearLoadingUpdates: Record = {}; + rowIds.forEach(rowId => { + clearLoadingUpdates[rowId] = false; + }); + setIsLoadingSublines(prev => ({ ...prev, ...clearLoadingUpdates })); + } + })(); + + fetchPromises.push(fetchPromise); + }); + + // Set initial loading states + if (Object.keys(lineLoadingUpdates).length > 0) { + console.log(`Setting loading state for ${Object.keys(lineLoadingUpdates).length} rows (product lines)`); + setIsLoadingLines(prev => ({ ...prev, ...lineLoadingUpdates })); + } + if (Object.keys(sublineLoadingUpdates).length > 0) { + console.log(`Setting loading state for ${Object.keys(sublineLoadingUpdates).length} rows (sublines)`); + setIsLoadingSublines(prev => ({ ...prev, ...sublineLoadingUpdates })); + } + + // Run all fetch operations in parallel + Promise.all(fetchPromises).then(() => { + console.log("All product lines and sublines fetch operations completed"); + }).catch(error => { + console.error('Error in fetch operations:', error); + }); + + }, [data, rowProductLines, rowSublines, companyLinesCache, lineSublineCache]); + + return { + rowProductLines, + rowSublines, + isLoadingLines, + isLoadingSublines, + fetchProductLines, + fetchSublines + }; +}; \ No newline at end of file