From ffa427d42c809bbb607329482955a273ce8135f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roman=20Kr=C4=8Dek?= Date: Thu, 17 Jul 2025 20:41:09 +0200 Subject: [PATCH] Fixed first two steps --- .github/copilot-instructions.md | 4 +- src/lib/components/Wizard.svelte | 19 +- .../components/wizard/StepColumnMap.svelte | 636 ++++++++++++------ .../components/wizard/StepSheetSearch.svelte | 75 ++- src/lib/stores.ts | 2 +- 5 files changed, 476 insertions(+), 260 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 0e8ba3a..6519c4d 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -3,8 +3,10 @@ - This code is written in Svelte 5 - It's important to only use modern Svelte 5 syntax, runes, and features. - Do not use $:, do not use eventDispatching as they are both deprecated - - User $effect, $state, $derived + - Use $effect, $state, $derived, eg. let { value } = $state(initialValue); - Pass fucntions as props instead od dispatching events + - Mixing old (on:click) and new syntaxes for event handling is not allowed. Use only the onclick syntax + - when setting state entity, simply od variable = newValue, do not use setState or similar methods like $state. - Use styling from ".github/styling.md" for any UI components. - Refer to the ".github/core-instructions.md" for the overall structure of the application. - Generate ".github/done.md" file to see what is done and what is not. Check it when you start and finish a task. diff --git a/src/lib/components/Wizard.svelte b/src/lib/components/Wizard.svelte index 13af3c4..bab8fcc 100644 --- a/src/lib/components/Wizard.svelte +++ b/src/lib/components/Wizard.svelte @@ -22,12 +22,6 @@ 'Filter Rows', 'Review Photos' ]; - - function goToPreviousStep() { - if ($currentStep > 1) { - currentStep.update(n => n - 1); - } - }
@@ -55,17 +49,6 @@
-
- - -
- -
+
diff --git a/src/lib/components/wizard/StepColumnMap.svelte b/src/lib/components/wizard/StepColumnMap.svelte index 3795593..5ec41fe 100644 --- a/src/lib/components/wizard/StepColumnMap.svelte +++ b/src/lib/components/wizard/StepColumnMap.svelte @@ -2,15 +2,48 @@ import { selectedSheet, columnMapping, rawSheetData, currentStep } from '$lib/stores'; import { getSheetNames, getSheetData } from '$lib/google'; import { onMount } from 'svelte'; + + // Type definitions for better TypeScript support + interface ColumnMappingType { + name: number; + surname: number; + nationality: number; + birthday: number; + pictureUrl: number; + alreadyPrinted: number; + [key: string]: number; // Index signature to allow string indexing + } + + interface SheetInfoType { + id?: string; + spreadsheetId?: string; + name: string; + sheetName?: string; + sheetMapping?: string; + columnMapping?: ColumnMappingType; + lastUsed?: string; + } - let isLoadingSheets = false; - let isLoadingData = false; - let availableSheets: string[] = []; - let selectedSheetName = ''; - let error = ''; - let sheetHeaders: string[] = []; - let previewData: string[][] = []; - let mappingComplete = false; + let isLoadingSheets = $state(false); + let isLoadingData = $state(false); + let availableSheets = $state([]); + let selectedSheetName = $state(''); + let error = $state(''); + let sheetHeaders = $state([]); + let previewData = $state([]); + let mappingComplete = $state(false); + let hasSavedMapping = $state(false); + let showMappingEditor = $state(false); + let savedSheetInfo = $state(null); + + let mappedIndices = $state({ + name: -1, + surname: -1, + nationality: -1, + birthday: -1, + pictureUrl: -1, + alreadyPrinted: -1 + }); const requiredFields = [ { key: 'name', label: 'First Name', required: true }, @@ -21,30 +54,123 @@ { key: 'alreadyPrinted', label: 'Already Printed', required: false } ]; - let mappedIndices = { - name: -1, - surname: -1, - nationality: -1, - birthday: -1, - pictureUrl: -1, - alreadyPrinted: -1 - }; - - // Load available sheets when component mounts + // Load available sheets when component mounts onMount(async () => { if ($selectedSheet) { + console.log('Selected sheet on mount:', $selectedSheet); + + // Check if we already have saved mapping data + const recentSheetsData = localStorage.getItem('esn-recent-sheets'); + + if (recentSheetsData) { + try { + const recentSheets = JSON.parse(recentSheetsData); + if (recentSheets && recentSheets.length > 0) { + // Find a sheet that matches the current spreadsheet + const savedSheet = recentSheets.find((sheet: SheetInfoType) => + (sheet.id === $selectedSheet.spreadsheetId || sheet.spreadsheetId === $selectedSheet.spreadsheetId) + ); + + if (savedSheet) { + console.log('Found saved sheet configuration:', savedSheet); + // We have a saved sheet for this spreadsheet + selectedSheetName = savedSheet.sheetName || savedSheet.sheetMapping || ''; + savedSheetInfo = savedSheet; + + if (savedSheet.columnMapping) { + // Set the mapped indices from saved data + mappedIndices = { + name: savedSheet.columnMapping.name ?? -1, + surname: savedSheet.columnMapping.surname ?? -1, + nationality: savedSheet.columnMapping.nationality ?? -1, + birthday: savedSheet.columnMapping.birthday ?? -1, + pictureUrl: savedSheet.columnMapping.pictureUrl ?? -1, + alreadyPrinted: savedSheet.columnMapping.alreadyPrinted ?? -1 + }; + + hasSavedMapping = true; + updateMappingStatus(); + columnMapping.set(mappedIndices); + + // Don't load sheet data immediately for better performance + // We'll load it when needed (when editing or continuing) + + return; // Skip loading available sheets since we're using saved data + } + } + } + } catch (err) { + console.error('Error parsing saved sheets data:', err); + } + } + + // If no saved data was found or it couldn't be used, load sheets as usual await loadAvailableSheets(); + } else { + console.error('No spreadsheet selected on mount'); } }); - async function loadAvailableSheets() { - if (!$selectedSheet) return; + // Load sheet data quietly (for previously saved sheets) + async function loadSheetDataQuietly(sheetName: string) { + if (!$selectedSheet || !sheetName) { + console.error('Cannot load sheet data: missing selectedSheet or sheetName', { + selectedSheet: $selectedSheet, + sheetName: sheetName + }); + return; + } + try { + console.log('Loading sheet data quietly for spreadsheet:', $selectedSheet.spreadsheetId, 'sheet:', sheetName); + + // Make sure we verify the sheet exists before trying to load it + if (availableSheets.length === 0) { + // We need to load available sheets first + await loadAvailableSheets(); + + // If after loading sheets, we still don't have the sheet, show the editor + if (!availableSheets.includes(sheetName)) { + console.warn(`Sheet "${sheetName}" not found in spreadsheet, showing editor`); + showMappingEditor = true; + return; + } + } + + // Fetch first 10 rows for headers only + const range = `${sheetName}!A1:Z10`; + const data = await getSheetData($selectedSheet.spreadsheetId, range); + + if (data && data.length > 0) { + console.log('Loaded sheet data with', data.length, 'rows'); + sheetHeaders = data[0]; + previewData = data.slice(1, Math.min(4, data.length)); // Get up to 3 rows for preview + // Don't set the rawSheetData here as that will be loaded in the next step + } else { + console.warn(`No data returned for sheet "${sheetName}", showing editor`); + showMappingEditor = true; + } + } catch (err) { + console.error('Error loading sheet data quietly:', err, 'for sheet:', sheetName); + // If there's an error, show the full editor so the user can select a sheet + showMappingEditor = true; + } + } + + async function loadAvailableSheets() { + if (!$selectedSheet) { + console.error('Cannot load available sheets: no sheet selected'); + return; + } + + console.log('Loading available sheets for spreadsheet:', $selectedSheet.spreadsheetId); isLoadingSheets = true; error = ''; try { - availableSheets = await getSheetNames($selectedSheet.id); + const sheetNames = await getSheetNames($selectedSheet.spreadsheetId); + console.log('Loaded sheet names:', sheetNames); + availableSheets = sheetNames; // Don't auto-select any sheet - let user choose } catch (err) { console.error('Error loading sheet names:', err); @@ -55,7 +181,9 @@ } function handleSheetSelect(sheetName: string) { + console.log('Sheet selected:', sheetName); selectedSheetName = sheetName; + // Clear any previous data when selecting a new sheet rawSheetData.set([]); sheetHeaders = []; @@ -69,6 +197,8 @@ alreadyPrinted: -1 }; mappingComplete = false; + hasSavedMapping = false; + showMappingEditor = true; // Load sheet data if (sheetName) { @@ -77,20 +207,25 @@ } async function loadSheetData(sheetName: string) { - if (!$selectedSheet) return; + if (!$selectedSheet) { + console.error('Cannot load sheet data: no sheet selected'); + return; + } + console.log('Loading sheet data for spreadsheet:', $selectedSheet.spreadsheetId, 'sheet:', sheetName); isLoadingData = true; error = ''; try { // Fetch first 10 rows for headers and preview const range = `${sheetName}!A1:Z10`; - const data = await getSheetData($selectedSheet.id, range); + const data = await getSheetData($selectedSheet.spreadsheetId, range); if (data && data.length > 0) { + console.log('Loaded sheet data with', data.length, 'rows'); sheetHeaders = data[0]; previewData = data.slice(1, Math.min(4, data.length)); // Get up to 3 rows for preview - rawSheetData.set(data); + // We don't need to set all the raw data here // Try to auto-map columns autoMapColumns(); @@ -99,6 +234,7 @@ loadSavedColumnMapping(); } else { error = 'The selected sheet appears to be empty.'; + console.warn('Sheet is empty'); } } catch (err) { console.error('Error loading sheet data:', err); @@ -120,7 +256,7 @@ }; // Auto-mapping patterns - const patterns = { + const patterns: Record = { name: /first[\s_-]*name|name|given[\s_-]*name|vorname/i, surname: /last[\s_-]*name|surname|family[\s_-]*name|nachname/i, nationality: /nationality|country|nation/i, @@ -159,11 +295,15 @@ } } + console.log('Auto-mapped columns:', mappedIndices); updateMappingStatus(); } function loadSavedColumnMapping() { - if (!$selectedSheet || !selectedSheetName) return; + if (!$selectedSheet || !selectedSheetName) { + console.log('Cannot load saved column mapping: missing selectedSheet or selectedSheetName'); + return; + } try { const recentSheetsKey = 'esn-recent-sheets'; @@ -171,11 +311,14 @@ if (existingData) { const recentSheets = JSON.parse(existingData); - const savedSheet = recentSheets.find(sheet => - sheet.id === $selectedSheet.id && sheet.sheetName === selectedSheetName + const savedSheet = recentSheets.find((sheet: SheetInfoType) => + (sheet.id === $selectedSheet.spreadsheetId || sheet.spreadsheetId === $selectedSheet.spreadsheetId) && + (sheet.sheetName === selectedSheetName || sheet.sheetMapping === selectedSheetName) ); if (savedSheet && savedSheet.columnMapping) { + console.log('Found saved column mapping for current sheet:', savedSheet.columnMapping); + // Override auto-mapping with saved mapping mappedIndices = { name: savedSheet.columnMapping.name ?? -1, @@ -186,7 +329,11 @@ alreadyPrinted: savedSheet.columnMapping.alreadyPrinted ?? -1 }; + hasSavedMapping = true; + savedSheetInfo = savedSheet; updateMappingStatus(); + } else { + console.log('No saved column mapping found for the current sheet'); } } } catch (err) { @@ -194,7 +341,7 @@ } } - function handleColumnMapping(field: string, index: number) { + function handleColumnMapping(field: keyof ColumnMappingType, index: number) { mappedIndices[field] = index; updateMappingStatus(); } @@ -210,6 +357,7 @@ }; mappingComplete = Object.values(requiredIndices).every(index => index !== -1); + console.log('Mapping complete:', mappingComplete); // Update the column mapping store columnMapping.set({ @@ -232,8 +380,9 @@ let recentSheets = existingData ? JSON.parse(existingData) : []; // Find the current sheet in recent sheets and update its column mapping - const sheetIndex = recentSheets.findIndex(sheet => - sheet.id === $selectedSheet.id && sheet.sheetName === selectedSheetName + const sheetIndex = recentSheets.findIndex((sheet: SheetInfoType) => + (sheet.id === $selectedSheet.spreadsheetId || sheet.spreadsheetId === $selectedSheet.spreadsheetId) && + (sheet.sheetName === selectedSheetName || sheet.sheetMapping === selectedSheetName) ); const columnMappingData = { @@ -249,12 +398,16 @@ // Update existing entry recentSheets[sheetIndex].columnMapping = columnMappingData; recentSheets[sheetIndex].lastUsed = new Date().toISOString(); + + // Ensure we have consistent property names + recentSheets[sheetIndex].spreadsheetId = recentSheets[sheetIndex].spreadsheetId || recentSheets[sheetIndex].id; + recentSheets[sheetIndex].sheetMapping = recentSheets[sheetIndex].sheetMapping || recentSheets[sheetIndex].sheetName; } else { // Add new entry const newEntry = { - id: $selectedSheet.id, + spreadsheetId: $selectedSheet.spreadsheetId, name: $selectedSheet.name, - sheetName: selectedSheetName, + sheetMapping: selectedSheetName, columnMapping: columnMappingData, lastUsed: new Date().toISOString() }; @@ -274,6 +427,34 @@ currentStep.set(4); // Move to next step } + + async function handleShowEditor() { + showMappingEditor = true; + + // Load available sheets if they haven't been loaded yet + if (availableSheets.length === 0) { + await loadAvailableSheets(); + } + + // Ensure we have sheet data if a sheet is already selected + if (selectedSheetName && sheetHeaders.length === 0) { + // Load the sheet data but keep mappings intact + try { + isLoadingData = true; + const range = `${selectedSheetName}!A1:Z10`; + const data = await getSheetData($selectedSheet.spreadsheetId, range); + + if (data && data.length > 0) { + sheetHeaders = data[0]; + previewData = data.slice(1, Math.min(4, data.length)); + } + } catch (err) { + console.error('Error loading sheet data for editor:', err); + } finally { + isLoadingData = false; + } + } + }
@@ -287,201 +468,236 @@ First, select which sheet contains your member data, then map the columns to the required fields.

- - -
-

- Step 1: Select Sheet -

- - {#if isLoadingSheets} -
-
- Loading sheets... -
- {:else if error} -
-

{error}

-
- {:else if availableSheets.length === 0} -
- - - -

No sheets found

-

- This spreadsheet doesn't appear to contain any sheets. -

-
- {:else} -
-

- Spreadsheet: {$selectedSheet?.name} -

- -
- +
+ {:else} + +
+

+ Step 1: Select Sheet +

+ + {#if isLoadingSheets} +
+
+ Loading sheets... +
+ {:else if error} +
+

{error}

+ +
+ {:else if availableSheets.length === 0} +
+ + + +

No sheets found

+

+ This spreadsheet doesn't appear to contain any sheets. +

+
+ {:else} +
+

+ Spreadsheet: {$selectedSheet?.name} +

-
- {#each availableSheets as sheetName} -
handleSheetSelect(sheetName)} - > -
-
-
- {#if selectedSheetName === sheetName} -
- {/if} -
-
- -
-

{sheetName}

-
- - {#if selectedSheetName === sheetName} +
+

+ Choose sheet: +

+ +
+ {#each availableSheets as sheetName} +
handleSheetSelect(sheetName)} + onkeydown={(e) => e.key === 'Enter' && handleSheetSelect(sheetName)} + > +
- - - +
+ {#if selectedSheetName === sheetName} +
+ {/if} +
- {/if} + +
+

{sheetName}

+
+ + {#if selectedSheetName === sheetName} +
+ + + +
+ {/if} +
+
+ {/each} +
+
+
+ {/if} +
+ + +
+

+ Step 2: Map Columns +

+ + {#if isLoadingData} +
+
+ Loading sheet data... +
+ {:else if sheetHeaders.length === 0} +
+

Select a sheet above to map columns

+
+ {:else} +
+

+ Map the columns from your sheet to the required fields: +

+ + +
+ {#each requiredFields as field} +
+
+ +
+ +
+
{/each}
-
-
- {/if} -
- - -
-

- Step 2: Map Columns -

- - {#if isLoadingData} -
-
- Loading sheet data... -
- {:else if sheetHeaders.length === 0} -
-

Select a sheet above to map columns

-
- {:else} -
-

- Map the columns from your sheet to the required fields: -

- - -
- {#each requiredFields as field} -
-
- -
- -
- -
-
- {/each} -
- - - {#if previewData.length > 0} -
-

Data Preview:

-
- - - - {#each Array.from({length: Math.max(sheetHeaders.length, previewData[0]?.length || 0, 26)}, (_, i) => i) as index} - - {/each} - - - - {#each previewData as row} + + + {#if previewData.length > 0} +
+

Data Preview:

+
+
- {sheetHeaders[index] || `Column ${String.fromCharCode(65 + index)}`} - {#if Object.values(mappedIndices).includes(index)} -
- {requiredFields.find(f => mappedIndices[f.key] === index)?.label} -
- {/if} -
+ - {#each Array.from({length: Math.max(sheetHeaders.length, row.length, 26)}, (_, i) => i) as index} - + {#each Array.from({length: Math.min(Math.max(sheetHeaders.length, previewData[0]?.length || 0), 26)}, (_, i) => i) as index} + {/each} - {/each} - -
- {row[index] || ''} - + {sheetHeaders[index] || `Column ${String.fromCharCode(65 + index)}`} + {#if Object.values(mappedIndices).includes(index)} +
+ {requiredFields.find(f => mappedIndices[f.key] === index)?.label} +
+ {/if} +
+ + + {#each previewData as row} + + {#each Array.from({length: Math.min(Math.max(sheetHeaders.length, row.length), 26)}, (_, i) => i) as index} + + {row[index] || ''} + + {/each} + + {/each} + + +
-
- {/if} - - - {#if mappingComplete} -
-

- ✓ All required fields are mapped! You can continue to the next step. -

-
- {:else} -
-

- Please map all required fields to continue. -

-
- {/if} -
- {/if} -
+ {/if} + + + {#if mappingComplete} +
+

+ ✓ All required fields are mapped! You can continue to the next step. +

+
+ {:else} +
+

+ Please map all required fields to continue. +

+
+ {/if} +
+ {/if} +
+ {/if}
-
- {/if} + +
+ + + +
diff --git a/src/lib/stores.ts b/src/lib/stores.ts index 4bebbea..d5d5d43 100644 --- a/src/lib/stores.ts +++ b/src/lib/stores.ts @@ -97,7 +97,7 @@ export const progress = writable({ // Google Sheets list for search export interface SheetInfo { - id: string; + spreadsheetId: string; name: string; url: string; }