Move product line fetching out of ValidationContainer, clean up some unused files
This commit is contained in:
41
.VSCodeCounter/2025-03-17_16-24-17/details.md
Normal file
41
.VSCodeCounter/2025-03-17_16-24-17/details.md
Normal file
@@ -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)
|
||||||
20
.VSCodeCounter/2025-03-17_16-24-17/diff-details.md
Normal file
20
.VSCodeCounter/2025-03-17_16-24-17/diff-details.md
Normal file
@@ -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
|
||||||
23
.VSCodeCounter/2025-03-17_16-24-17/diff.md
Normal file
23
.VSCodeCounter/2025-03-17_16-24-17/diff.md
Normal file
@@ -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)
|
||||||
31
.VSCodeCounter/2025-03-17_16-24-17/diff.txt
Normal file
31
.VSCodeCounter/2025-03-17_16-24-17/diff.txt
Normal file
@@ -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 |
|
||||||
|
+-------------------------------------------------------------------------------------------------------------------------------------+----------------+------------+------------+------------+------------+
|
||||||
1
.VSCodeCounter/2025-03-17_16-24-17/results.json
Normal file
1
.VSCodeCounter/2025-03-17_16-24-17/results.json
Normal file
File diff suppressed because one or more lines are too long
31
.VSCodeCounter/2025-03-17_16-24-17/results.md
Normal file
31
.VSCodeCounter/2025-03-17_16-24-17/results.md
Normal file
@@ -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)
|
||||||
60
.VSCodeCounter/2025-03-17_16-24-17/results.txt
Normal file
60
.VSCodeCounter/2025-03-17_16-24-17/results.txt
Normal file
@@ -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 |
|
||||||
|
+------------------------------------------------------------------------------------------------------------------------------------------+----------------+------------+------------+------------+------------+
|
||||||
@@ -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<SaveTemplateDialogProps> = ({
|
|
||||||
isOpen,
|
|
||||||
onClose,
|
|
||||||
onSave,
|
|
||||||
}) => {
|
|
||||||
const [company, setCompany] = useState("")
|
|
||||||
const [productType, setProductType] = useState("")
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog open={isOpen} onOpenChange={(open) => {
|
|
||||||
if (!open) {
|
|
||||||
onClose()
|
|
||||||
setCompany("")
|
|
||||||
setProductType("")
|
|
||||||
}
|
|
||||||
}}>
|
|
||||||
<DialogContent className="sm:max-w-md">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>Save as Template</DialogTitle>
|
|
||||||
<DialogDescription>
|
|
||||||
Enter the company and product type for this template.
|
|
||||||
</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
<div className="space-y-4 py-4">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label htmlFor="company" className="text-sm font-medium">
|
|
||||||
Company
|
|
||||||
</label>
|
|
||||||
<Input
|
|
||||||
id="company"
|
|
||||||
value={company}
|
|
||||||
onChange={(e) => setCompany(e.target.value)}
|
|
||||||
placeholder="Enter company name"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label htmlFor="productType" className="text-sm font-medium">
|
|
||||||
Product Type
|
|
||||||
</label>
|
|
||||||
<Input
|
|
||||||
id="productType"
|
|
||||||
value={productType}
|
|
||||||
onChange={(e) => setProductType(e.target.value)}
|
|
||||||
placeholder="Enter product type"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<DialogFooter>
|
|
||||||
<Button variant="outline" onClick={onClose}>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
onSave(company, productType)
|
|
||||||
onClose()
|
|
||||||
setCompany("")
|
|
||||||
setProductType("")
|
|
||||||
}}
|
|
||||||
disabled={!company || !productType}
|
|
||||||
>
|
|
||||||
Save Template
|
|
||||||
</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SaveTemplateDialog
|
|
||||||
@@ -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<TemplateManagerProps> = ({
|
|
||||||
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 (
|
|
||||||
<>
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="flex flex-col space-y-2">
|
|
||||||
<label className="text-sm font-medium">Template</label>
|
|
||||||
<Popover open={open} onOpenChange={setOpen}>
|
|
||||||
<PopoverTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
role="combobox"
|
|
||||||
aria-expanded={open}
|
|
||||||
className="justify-between w-full"
|
|
||||||
>
|
|
||||||
{selectedTemplate
|
|
||||||
? getTemplateDisplayText(selectedTemplate)
|
|
||||||
: "Select a template"}
|
|
||||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
|
||||||
</Button>
|
|
||||||
</PopoverTrigger>
|
|
||||||
<PopoverContent className="p-0 w-[350px]">
|
|
||||||
<Command>
|
|
||||||
<CommandInput
|
|
||||||
placeholder="Search templates..."
|
|
||||||
value={searchQuery}
|
|
||||||
onValueChange={setSearchQuery}
|
|
||||||
/>
|
|
||||||
<CommandList>
|
|
||||||
<CommandEmpty>No templates found.</CommandEmpty>
|
|
||||||
<CommandGroup>
|
|
||||||
{filteredTemplates.map((template) => (
|
|
||||||
<CommandItem
|
|
||||||
key={template.id}
|
|
||||||
value={template.id.toString()}
|
|
||||||
onSelect={(value) => {
|
|
||||||
onSelectTemplate(value)
|
|
||||||
setOpen(false)
|
|
||||||
setSearchQuery('')
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Check
|
|
||||||
className={cn(
|
|
||||||
"mr-2 h-4 w-4",
|
|
||||||
selectedTemplateId === template.id.toString()
|
|
||||||
? "opacity-100"
|
|
||||||
: "opacity-0"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
{getTemplateDisplayText(template)}
|
|
||||||
</CommandItem>
|
|
||||||
))}
|
|
||||||
</CommandGroup>
|
|
||||||
</CommandList>
|
|
||||||
</Command>
|
|
||||||
</PopoverContent>
|
|
||||||
</Popover>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex space-x-2">
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => selectedTemplateId && onApplyTemplate(selectedTemplateId)}
|
|
||||||
disabled={!selectedTemplateId || selectedCount === 0}
|
|
||||||
className="flex-1"
|
|
||||||
>
|
|
||||||
Apply Template
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
onClick={onCloseDialog}
|
|
||||||
disabled={selectedCount === 0}
|
|
||||||
className="flex-1"
|
|
||||||
>
|
|
||||||
Save as Template
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Template Save Dialog */}
|
|
||||||
<Dialog open={showDialog} onOpenChange={onCloseDialog}>
|
|
||||||
<DialogContent>
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle>Save Template</DialogTitle>
|
|
||||||
<DialogDescription>
|
|
||||||
Create a template from the selected row.
|
|
||||||
</DialogDescription>
|
|
||||||
</DialogHeader>
|
|
||||||
|
|
||||||
<div className="space-y-4 py-4">
|
|
||||||
<div className="flex flex-col space-y-2">
|
|
||||||
<label className="text-sm font-medium">Company</label>
|
|
||||||
<Input
|
|
||||||
value={templateName}
|
|
||||||
onChange={(e) => setTemplateName(e.target.value)}
|
|
||||||
placeholder="Enter company name"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-col space-y-2">
|
|
||||||
<label className="text-sm font-medium">Product Type</label>
|
|
||||||
<Input
|
|
||||||
value={templateType}
|
|
||||||
onChange={(e) => setTemplateType(e.target.value)}
|
|
||||||
placeholder="Enter product type"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<DialogFooter>
|
|
||||||
<Button variant="outline" onClick={onCloseDialog}>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={handleSaveTemplate}
|
|
||||||
disabled={!templateName.trim() || !templateType.trim()}
|
|
||||||
>
|
|
||||||
Save Template
|
|
||||||
</Button>
|
|
||||||
</DialogFooter>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TemplateManager
|
|
||||||
@@ -16,6 +16,7 @@ import { TemplateForm } from '@/components/templates/TemplateForm'
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { RowSelectionState } from '@tanstack/react-table'
|
import { RowSelectionState } from '@tanstack/react-table'
|
||||||
import { useUpcValidation } from '../hooks/useUpcValidation'
|
import { useUpcValidation } from '../hooks/useUpcValidation'
|
||||||
|
import { useProductLinesFetching } from '../hooks/useProductLinesFetching'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ValidationContainer component - the main wrapper for the validation step
|
* ValidationContainer component - the main wrapper for the validation step
|
||||||
@@ -60,18 +61,15 @@ const ValidationContainer = <T extends string>({
|
|||||||
fields,
|
fields,
|
||||||
isLoadingTemplates } = validationState
|
isLoadingTemplates } = validationState
|
||||||
|
|
||||||
// Add state for tracking product lines and sublines per row
|
// Use product lines fetching hook
|
||||||
const [rowProductLines, setRowProductLines] = useState<Record<string, any[]>>({});
|
const {
|
||||||
const [rowSublines, setRowSublines] = useState<Record<string, any[]>>({});
|
rowProductLines,
|
||||||
// These variables are used in the fetchProductLines and fetchSublines functions
|
rowSublines,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
isLoadingLines,
|
||||||
const [isLoadingLines, setIsLoadingLines] = useState<Record<string, boolean>>({});
|
isLoadingSublines,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
fetchProductLines,
|
||||||
const [isLoadingSublines, setIsLoadingSublines] = useState<Record<string, boolean>>({});
|
fetchSublines
|
||||||
|
} = useProductLinesFetching(data);
|
||||||
// Add caches for product lines and sublines by company/line ID
|
|
||||||
const [companyLinesCache, setCompanyLinesCache] = useState<Record<string, any[]>>({});
|
|
||||||
const [lineSublineCache, setLineSublineCache] = useState<Record<string, any[]>>({});
|
|
||||||
|
|
||||||
// Add state for tracking cells in loading state
|
// Add state for tracking cells in loading state
|
||||||
const [validatingCells, setValidatingCells] = useState<Set<string>>(new Set());
|
const [validatingCells, setValidatingCells] = useState<Set<string>>(new Set());
|
||||||
@@ -85,556 +83,6 @@ const ValidationContainer = <T extends string>({
|
|||||||
// Apply all pending updates to the data state
|
// Apply all pending updates to the data state
|
||||||
const applyItemNumbersToData = upcValidation.applyItemNumbersToData;
|
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<string, any>;
|
|
||||||
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<string, any>;
|
|
||||||
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<string, string[]>(); // company ID -> row IDs
|
|
||||||
const linesNeeded = new Map<string, string[]>(); // 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<void>[] = [];
|
|
||||||
|
|
||||||
// Set initial loading states for all affected rows
|
|
||||||
const lineLoadingUpdates: Record<string, boolean> = {};
|
|
||||||
const sublineLoadingUpdates: Record<string, boolean> = {};
|
|
||||||
|
|
||||||
// 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<string, any[]> = {};
|
|
||||||
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<string, boolean> = {};
|
|
||||||
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<string, any[]> = {};
|
|
||||||
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<string, any[]> = {};
|
|
||||||
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<string, any[]> = {};
|
|
||||||
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<string, boolean> = {};
|
|
||||||
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<string, any[]> = {};
|
|
||||||
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<string, boolean> = {};
|
|
||||||
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<string, any[]> = {};
|
|
||||||
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<string, any[]> = {};
|
|
||||||
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<string, any[]> = {};
|
|
||||||
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<string, boolean> = {};
|
|
||||||
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
|
// Use AI validation hook
|
||||||
const aiValidation = useAiValidation<T>(
|
const aiValidation = useAiValidation<T>(
|
||||||
data,
|
data,
|
||||||
@@ -885,11 +333,171 @@ const ValidationContainer = <T extends string>({
|
|||||||
setRowSelection(newSelection);
|
setRowSelection(newSelection);
|
||||||
}, [setRowSelection]);
|
}, [setRowSelection]);
|
||||||
|
|
||||||
const handleUpdateRow = useCallback((rowIndex: number, key: T, value: any) => {
|
const handleUpdateRow = useCallback(async (rowIndex: number, key: T, value: any) => {
|
||||||
enhancedUpdateRow(rowIndex, key, value);
|
// Process value before updating data
|
||||||
}, [enhancedUpdateRow]);
|
console.log(`enhancedUpdateRow called: rowIndex=${rowIndex}, fieldKey=${key}, value=`, value);
|
||||||
|
let processedValue = value;
|
||||||
// Enhanced copy down that uses enhancedUpdateRow instead of regular updateRow
|
|
||||||
|
// 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<string, any>;
|
||||||
|
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<string, any>;
|
||||||
|
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) => {
|
const handleCopyDown = useCallback((rowIndex: number, fieldKey: string, endRowIndex?: number) => {
|
||||||
// Get the value to copy from the source row
|
// Get the value to copy from the source row
|
||||||
const sourceRow = data[rowIndex];
|
const sourceRow = data[rowIndex];
|
||||||
@@ -923,7 +531,7 @@ const ValidationContainer = <T extends string>({
|
|||||||
const targetRowIndex = rowIndex + 1 + i;
|
const targetRowIndex = rowIndex + 1 + i;
|
||||||
|
|
||||||
// Update the row with the copied value
|
// Update the row with the copied value
|
||||||
enhancedUpdateRow(targetRowIndex, fieldKey as T, valueCopy);
|
handleUpdateRow(targetRowIndex, fieldKey as T, valueCopy);
|
||||||
|
|
||||||
// Remove loading state
|
// Remove loading state
|
||||||
setValidatingCells(prev => {
|
setValidatingCells(prev => {
|
||||||
@@ -932,8 +540,19 @@ const ValidationContainer = <T extends string>({
|
|||||||
return newSet;
|
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
|
// Memoize the enhanced validation table component
|
||||||
const EnhancedValidationTable = useMemo(() => React.memo((props: React.ComponentProps<typeof ValidationTable>) => {
|
const EnhancedValidationTable = useMemo(() => React.memo((props: React.ComponentProps<typeof ValidationTable>) => {
|
||||||
// Create validatingCells set from validating rows, but only for item_number fields
|
// Create validatingCells set from validating rows, but only for item_number fields
|
||||||
@@ -993,7 +612,7 @@ const ValidationContainer = <T extends string>({
|
|||||||
fields={fields as unknown as Fields<string>}
|
fields={fields as unknown as Fields<string>}
|
||||||
rowSelection={rowSelection}
|
rowSelection={rowSelection}
|
||||||
setRowSelection={handleRowSelectionChange as React.Dispatch<React.SetStateAction<RowSelectionState>>}
|
setRowSelection={handleRowSelectionChange as React.Dispatch<React.SetStateAction<RowSelectionState>>}
|
||||||
updateRow={handleUpdateRow as (rowIndex: number, key: string, value: any) => void}
|
updateRow={handleUpdateRow as unknown as (rowIndex: number, key: string, value: any) => void}
|
||||||
validationErrors={validationErrors}
|
validationErrors={validationErrors}
|
||||||
isValidatingUpc={isRowValidatingUpc}
|
isValidatingUpc={isRowValidatingUpc}
|
||||||
validatingUpcRows={Array.from(upcValidation.validatingRows)}
|
validatingUpcRows={Array.from(upcValidation.validatingRows)}
|
||||||
|
|||||||
@@ -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 = <T extends string>(
|
|
||||||
data: RowData<T>[],
|
|
||||||
fields: Fields<T>,
|
|
||||||
validationErrors: Map<number, Record<string, any>>
|
|
||||||
) => {
|
|
||||||
// Filter state
|
|
||||||
const [filters, setFilters] = useState<FilterState>({
|
|
||||||
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<string>()
|
|
||||||
|
|
||||||
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<FilterState>) => {
|
|
||||||
setFilters(prev => ({
|
|
||||||
...prev,
|
|
||||||
...newFilters
|
|
||||||
}))
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
// Apply filters to data
|
|
||||||
const applyFilters = useCallback((dataToFilter: RowData<T>[]) => {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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<string, any>[]) => {
|
||||||
|
// State for tracking product lines and sublines per row
|
||||||
|
const [rowProductLines, setRowProductLines] = useState<Record<string, any[]>>({});
|
||||||
|
const [rowSublines, setRowSublines] = useState<Record<string, any[]>>({});
|
||||||
|
|
||||||
|
// State for tracking loading states
|
||||||
|
const [isLoadingLines, setIsLoadingLines] = useState<Record<string, boolean>>({});
|
||||||
|
const [isLoadingSublines, setIsLoadingSublines] = useState<Record<string, boolean>>({});
|
||||||
|
|
||||||
|
// Add caches for product lines and sublines by company/line ID
|
||||||
|
const [companyLinesCache, setCompanyLinesCache] = useState<Record<string, any[]>>({});
|
||||||
|
const [lineSublineCache, setLineSublineCache] = useState<Record<string, any[]>>({});
|
||||||
|
|
||||||
|
// 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<string, string[]>(); // company ID -> row IDs
|
||||||
|
const linesNeeded = new Map<string, string[]>(); // 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<void>[] = [];
|
||||||
|
|
||||||
|
// Set initial loading states for all affected rows
|
||||||
|
const lineLoadingUpdates: Record<string, boolean> = {};
|
||||||
|
const sublineLoadingUpdates: Record<string, boolean> = {};
|
||||||
|
|
||||||
|
// 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<string, any[]> = {};
|
||||||
|
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<string, boolean> = {};
|
||||||
|
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<string, any[]> = {};
|
||||||
|
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<string, any[]> = {};
|
||||||
|
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<string, any[]> = {};
|
||||||
|
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<string, boolean> = {};
|
||||||
|
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<string, any[]> = {};
|
||||||
|
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<string, boolean> = {};
|
||||||
|
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<string, any[]> = {};
|
||||||
|
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<string, any[]> = {};
|
||||||
|
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<string, any[]> = {};
|
||||||
|
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<string, boolean> = {};
|
||||||
|
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
|
||||||
|
};
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user