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 { 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 = <T extends string>({
|
||||
fields,
|
||||
isLoadingTemplates } = validationState
|
||||
|
||||
// Add state for tracking product lines and sublines per row
|
||||
const [rowProductLines, setRowProductLines] = useState<Record<string, any[]>>({});
|
||||
const [rowSublines, setRowSublines] = useState<Record<string, any[]>>({});
|
||||
// These variables are used in the fetchProductLines and fetchSublines functions
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [isLoadingLines, setIsLoadingLines] = useState<Record<string, boolean>>({});
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
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[]>>({});
|
||||
// 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<Set<string>>(new Set());
|
||||
@@ -85,556 +83,6 @@ const ValidationContainer = <T extends string>({
|
||||
// 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<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
|
||||
const aiValidation = useAiValidation<T>(
|
||||
data,
|
||||
@@ -885,11 +333,171 @@ const ValidationContainer = <T extends string>({
|
||||
setRowSelection(newSelection);
|
||||
}, [setRowSelection]);
|
||||
|
||||
const handleUpdateRow = useCallback((rowIndex: number, key: T, value: any) => {
|
||||
enhancedUpdateRow(rowIndex, key, value);
|
||||
}, [enhancedUpdateRow]);
|
||||
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;
|
||||
|
||||
// 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) => {
|
||||
// Get the value to copy from the source row
|
||||
const sourceRow = data[rowIndex];
|
||||
@@ -923,7 +531,7 @@ const ValidationContainer = <T extends string>({
|
||||
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,7 +540,18 @@ const ValidationContainer = <T extends string>({
|
||||
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<typeof ValidationTable>) => {
|
||||
@@ -993,7 +612,7 @@ const ValidationContainer = <T extends string>({
|
||||
fields={fields as unknown as Fields<string>}
|
||||
rowSelection={rowSelection}
|
||||
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}
|
||||
isValidatingUpc={isRowValidatingUpc}
|
||||
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