From 3ea48272b2b3dc9f4822253535f301398c779f63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Kr=C4=8Dek?= Date: Thu, 17 Jul 2025 16:34:02 +0200 Subject: [PATCH] Step RowFiltering done --- src/lib/components/Wizard.svelte | 7 +- .../components/wizard/StepColumnMap.svelte | 73 +++- .../components/wizard/StepRowFilter.svelte | 401 +++++++++++++++++- src/lib/stores.ts | 4 + 4 files changed, 466 insertions(+), 19 deletions(-) diff --git a/src/lib/components/Wizard.svelte b/src/lib/components/Wizard.svelte index 5b7a100..d6bbc2f 100644 --- a/src/lib/components/Wizard.svelte +++ b/src/lib/components/Wizard.svelte @@ -3,18 +3,21 @@ import StepAuth from './wizard/StepAuth.svelte'; import StepSheetSearch from './wizard/StepSheetSearch.svelte'; import StepColumnMap from './wizard/StepColumnMap.svelte'; + import StepRowFilter from './wizard/StepRowFilter.svelte'; // Additional steps to be added as they are implemented const steps = [ StepAuth, StepSheetSearch, - StepColumnMap + StepColumnMap, + StepRowFilter ]; const stepTitles = [ 'Authenticate', 'Select Sheet', - 'Map Columns' + 'Map Columns', + 'Filter Rows' ]; function goToPreviousStep() { diff --git a/src/lib/components/wizard/StepColumnMap.svelte b/src/lib/components/wizard/StepColumnMap.svelte index 01f1412..3795593 100644 --- a/src/lib/components/wizard/StepColumnMap.svelte +++ b/src/lib/components/wizard/StepColumnMap.svelte @@ -17,7 +17,8 @@ { key: 'surname', label: 'Last Name', required: true }, { key: 'nationality', label: 'Nationality', required: true }, { key: 'birthday', label: 'Birthday', required: true }, - { key: 'pictureUrl', label: 'Photo URL', required: true } + { key: 'pictureUrl', label: 'Photo URL', required: true }, + { key: 'alreadyPrinted', label: 'Already Printed', required: false } ]; let mappedIndices = { @@ -25,7 +26,8 @@ surname: -1, nationality: -1, birthday: -1, - pictureUrl: -1 + pictureUrl: -1, + alreadyPrinted: -1 }; // Load available sheets when component mounts @@ -63,7 +65,8 @@ surname: -1, nationality: -1, birthday: -1, - pictureUrl: -1 + pictureUrl: -1, + alreadyPrinted: -1 }; mappingComplete = false; @@ -112,7 +115,8 @@ surname: -1, nationality: -1, birthday: -1, - pictureUrl: -1 + pictureUrl: -1, + alreadyPrinted: -1 }; // Auto-mapping patterns @@ -121,7 +125,8 @@ surname: /last[\s_-]*name|surname|family[\s_-]*name|nachname/i, nationality: /nationality|country|nation/i, birthday: /birth|date[\s_-]*of[\s_-]*birth|birthday|dob/i, - pictureUrl: /photo|picture|image|url|avatar/i + pictureUrl: /photo|picture|image|url|avatar/i, + alreadyPrinted: /already[\s_-]*printed|printed|status/i }; sheetHeaders.forEach((header, index) => { @@ -133,6 +138,27 @@ } }); + // If "Already Printed" column wasn't found, try to find the first empty column + if (mappedIndices.alreadyPrinted === -1 && previewData.length > 0) { + // Check up to 26 columns (A-Z) or the number of headers, whichever is larger + const maxColumns = Math.max(sheetHeaders.length, 26); + + for (let colIndex = 0; colIndex < maxColumns; colIndex++) { + // Check if this column is empty (all preview rows are empty for this column) + const isEmpty = previewData.every(row => !row[colIndex] || String(row[colIndex]).trim() === ''); + + // Also check if this column isn't already mapped to another field + const isAlreadyMapped = Object.entries(mappedIndices).some(([field, index]) => + field !== 'alreadyPrinted' && index === colIndex + ); + + if (isEmpty && !isAlreadyMapped) { + mappedIndices.alreadyPrinted = colIndex; + break; + } + } + } + updateMappingStatus(); } @@ -156,7 +182,8 @@ surname: savedSheet.columnMapping.surname ?? -1, nationality: savedSheet.columnMapping.nationality ?? -1, birthday: savedSheet.columnMapping.birthday ?? -1, - pictureUrl: savedSheet.columnMapping.pictureUrl ?? -1 + pictureUrl: savedSheet.columnMapping.pictureUrl ?? -1, + alreadyPrinted: savedSheet.columnMapping.alreadyPrinted ?? -1 }; updateMappingStatus(); @@ -173,7 +200,16 @@ } function updateMappingStatus() { - mappingComplete = Object.values(mappedIndices).every(index => index !== -1); + // Only check required fields for completion + const requiredIndices = { + name: mappedIndices.name, + surname: mappedIndices.surname, + nationality: mappedIndices.nationality, + birthday: mappedIndices.birthday, + pictureUrl: mappedIndices.pictureUrl + }; + + mappingComplete = Object.values(requiredIndices).every(index => index !== -1); // Update the column mapping store columnMapping.set({ @@ -181,7 +217,8 @@ surname: mappedIndices.surname, nationality: mappedIndices.nationality, birthday: mappedIndices.birthday, - pictureUrl: mappedIndices.pictureUrl + pictureUrl: mappedIndices.pictureUrl, + alreadyPrinted: mappedIndices.alreadyPrinted }); } @@ -204,7 +241,8 @@ surname: mappedIndices.surname, nationality: mappedIndices.nationality, birthday: mappedIndices.birthday, - pictureUrl: mappedIndices.pictureUrl + pictureUrl: mappedIndices.pictureUrl, + alreadyPrinted: mappedIndices.alreadyPrinted }; if (sheetIndex !== -1) { @@ -370,8 +408,13 @@ class="w-full px-3 py-2 border border-gray-300 rounded-md bg-white text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" > - {#each sheetHeaders as header, index} - + {#each Array.from({length: Math.max(sheetHeaders.length, 26)}, (_, i) => i) as index} + {/each} @@ -387,10 +430,10 @@ - {#each sheetHeaders as header, index} + {#each Array.from({length: Math.max(sheetHeaders.length, previewData[0]?.length || 0, 26)}, (_, i) => i) as index} {#each previewData as row} - {#each row as cell, index} + {#each Array.from({length: Math.max(sheetHeaders.length, row.length, 26)}, (_, i) => i) as index} {/each} diff --git a/src/lib/components/wizard/StepRowFilter.svelte b/src/lib/components/wizard/StepRowFilter.svelte index 79c4b4b..d3c0527 100644 --- a/src/lib/components/wizard/StepRowFilter.svelte +++ b/src/lib/components/wizard/StepRowFilter.svelte @@ -1,4 +1,401 @@ + +
-

Filter Rows

-

Row filtering functionality will be implemented here.

+
+
+

+ Filter and Select Rows +

+ +

+ Review your data and select which rows you want to include in the card generation. + Only rows with all required fields will be available for selection. +

+
+ + +
+
+ +
+ + +
+ + +
+ + +
+
+ + +
+ Total rows: {processedData.length} + Valid rows: {processedData.filter(row => row._isValid).length} + Already printed: {processedData.filter(row => isRowAlreadyPrinted(row)).length} + Filtered rows: {filteredData.length} + Selected: {selectedValidCount} +
+
+ + +
+ {#if filteredData.length === 0} +
+ + + +

No data found

+

+ {searchTerm ? 'No rows match your search criteria.' : 'No data available to display.'} +

+
+ {:else} +
+
- {header} + {sheetHeaders[index] || `Column ${String.fromCharCode(65 + index)}`} {#if Object.values(mappedIndices).includes(index)}
{requiredFields.find(f => mappedIndices[f.key] === index)?.label} @@ -403,10 +446,10 @@
- {cell} + {row[index] || ''}
+ + + + + + + {#each headers as header} + + {/each} + + + + + + + {#each filteredData as row} + + + + + + {#each headers as header} + + {/each} + + + + + {/each} + +
+ + handleSort(header)} + > +
+ {getFieldLabel(header)} + {#if sortColumn === header} + + {#if sortDirection === 'asc'} + + {:else} + + {/if} + + {/if} +
+
+ Status +
+ {#if row._isValid} + toggleRowSelection(row._rowIndex)} + class="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500" + disabled={isRowAlreadyPrinted(row)} + /> + {#if isRowAlreadyPrinted(row)} +
Already printed
+ {/if} + {:else} +
+ {/if} +
+ {#if header === 'alreadyPrinted'} + {#if isRowAlreadyPrinted(row)} + + Already Printed + + {:else} + + Not Printed + + {/if} + {:else} + {row[header] || ''} + {/if} + +
+ {#if row._isValid} + + Valid + + {:else} + + Missing data + + {/if} + + {#if isRowAlreadyPrinted(row)} + + Already Printed + + {/if} +
+
+ + {/if} + + + + {#if selectedValidCount > 0} +
+
+ + + + + {selectedValidCount} {selectedValidCount === 1 ? 'row' : 'rows'} selected for card generation + +
+
+ {/if} + + +
+ +
+ diff --git a/src/lib/stores.ts b/src/lib/stores.ts index 0c67e0a..4bebbea 100644 --- a/src/lib/stores.ts +++ b/src/lib/stores.ts @@ -9,6 +9,9 @@ export const session = writable<{ // Raw sheet data after import export const rawSheetData = writable([]); +// Filtered sheet data after row selection +export const filteredSheetData = writable([]); + // Column mapping configuration export const columnMapping = writable<{ name?: number; @@ -16,6 +19,7 @@ export const columnMapping = writable<{ nationality?: number; birthday?: number; pictureUrl?: number; + alreadyPrinted?: number; }>({}); // Processed row data after mapping and validation