Fixed other two components

This commit is contained in:
Roman Krček
2025-07-17 21:00:47 +02:00
parent ffa427d42c
commit 4f119dc121
2 changed files with 135 additions and 87 deletions

View File

@@ -695,13 +695,23 @@
{/if}
<!-- Navigation -->
<div class="flex justify-end">
<!-- Navigation -->
<div class="flex justify-between">
<button
onclick={() => currentStep.set(2)}
class="px-4 py-2 bg-gray-200 text-gray-700 rounded-lg font-medium hover:bg-gray-300"
>
← Back to Sheet Selection
</button>
<button
onclick={handleContinue}
disabled={!mappingComplete}
class="px-4 py-2 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed"
>
{mappingComplete ? 'Continue →' : 'Complete mapping to continue'}
{mappingComplete
? 'Continue →'
: 'Select a column mapping'}
</button>
</div>
</div>

View File

@@ -2,6 +2,7 @@
import { selectedSheet, columnMapping, rawSheetData, filteredSheetData, currentStep, sheetData } from '$lib/stores';
import type { RowData } from '$lib/stores';
import { onMount } from 'svelte';
import { getSheetNames, getSheetData } from '$lib/google';
let searchTerm = '';
let sortColumn = '';
@@ -11,6 +12,7 @@
let processedData: any[] = [];
let filteredData: any[] = [];
let headers: string[] = [];
let isLoading = false;
$: {
// Filter data based on search term
@@ -46,68 +48,87 @@
processSheetData();
});
function processSheetData() {
if (!$rawSheetData || $rawSheetData.length === 0 || !$columnMapping) {
return;
// Fetch raw sheet data from Google Sheets if not already loaded
async function fetchRawSheetData() {
if (!$rawSheetData || $rawSheetData.length === 0) {
if (!$selectedSheet) return;
const sheetNames = await getSheetNames($selectedSheet.spreadsheetId);
if (sheetNames.length === 0) return;
const sheetName = sheetNames[0];
const range = `${sheetName}!A:Z`;
const data = await getSheetData($selectedSheet.spreadsheetId, range);
rawSheetData.set(data);
}
}
// Get headers from the mapping
headers = Object.keys($columnMapping);
// Process the data starting from row 2 (skip header row)
processedData = $rawSheetData.slice(1).map((row, index) => {
const processedRow: any = {
_rowIndex: index + 1, // Store original row index
_isValid: true
};
async function processSheetData() {
isLoading = true;
try {
await fetchRawSheetData();
if (!$rawSheetData || $rawSheetData.length === 0 || !$columnMapping) {
return;
}
// Map each column according to the column mapping
for (const [field, columnIndex] of Object.entries($columnMapping)) {
if (columnIndex !== -1 && columnIndex !== undefined && columnIndex < row.length) {
processedRow[field] = row[columnIndex] || '';
} else {
processedRow[field] = '';
// Only mark as invalid if it's a required field
if (field !== 'alreadyPrinted') {
processedRow._isValid = false;
// Get headers from the mapping
headers = Object.keys($columnMapping);
// Process the data starting from row 2 (skip header row)
processedData = $rawSheetData.slice(1).map((row, index) => {
const processedRow: any = {
_rowIndex: index + 1, // Store original row index
_isValid: true
};
// Map each column according to the column mapping
for (const [field, columnIndex] of Object.entries($columnMapping)) {
if (columnIndex !== -1 && columnIndex !== undefined && columnIndex < row.length) {
processedRow[field] = row[columnIndex] || '';
} else {
processedRow[field] = '';
// Only mark as invalid if it's a required field
if (field !== 'alreadyPrinted') {
processedRow._isValid = false;
}
}
}
}
// Check if all required fields have values (excluding alreadyPrinted)
const requiredFields = ['name', 'surname', 'nationality', 'birthday', 'pictureUrl'];
const hasAllRequiredFields = requiredFields.every(field =>
processedRow[field] && String(processedRow[field]).trim() !== ''
// Check if all required fields have values (excluding alreadyPrinted)
const requiredFields = ['name', 'surname', 'nationality', 'birthday', 'pictureUrl'];
const hasAllRequiredFields = requiredFields.every(field =>
processedRow[field] && String(processedRow[field]).trim() !== ''
);
if (!hasAllRequiredFields) {
processedRow._isValid = false;
}
return processedRow;
});
// Initially select rows based on validity and "Already Printed" status
selectedRows = new Set(
processedData
.filter(row => {
if (!row._isValid) return false;
// Check "Already Printed" column value
const alreadyPrinted = row.alreadyPrinted;
if (alreadyPrinted) {
const value = String(alreadyPrinted).toLowerCase().trim();
// If the value is "true", "yes", "1", or any truthy value, don't select
return !(value === 'true' || value === 'yes' || value === '1' || value === 'x');
}
// If empty or falsy, select the row
return true;
})
.map(row => row._rowIndex)
);
if (!hasAllRequiredFields) {
processedRow._isValid = false;
}
return processedRow;
});
// Initially select rows based on validity and "Already Printed" status
selectedRows = new Set(
processedData
.filter(row => {
if (!row._isValid) return false;
// Check "Already Printed" column value
const alreadyPrinted = row.alreadyPrinted;
if (alreadyPrinted) {
const value = String(alreadyPrinted).toLowerCase().trim();
// If the value is "true", "yes", "1", or any truthy value, don't select
return !(value === 'true' || value === 'yes' || value === '1' || value === 'x');
}
// If empty or falsy, select the row
return true;
})
.map(row => row._rowIndex)
);
updateSelectAllState();
updateSelectAllState();
} finally {
isLoading = false;
}
}
function toggleRowSelection(rowIndex: number) {
@@ -193,6 +214,8 @@
const row = processedData.find(r => r._rowIndex === rowIndex);
return row && row._isValid;
}).length;
// Allow proceeding only if at least one valid row is selected
$: canProceed = selectedValidCount > 0;
</script>
<div class="p-6">
@@ -244,17 +267,40 @@
</div>
<!-- Stats -->
<div class="mt-4 flex flex-wrap gap-4 text-sm text-gray-600">
<div class="mt-4 flex items-center flex-wrap gap-4 text-sm text-gray-600">
<span>Total rows: {processedData.length}</span>
<span>Valid rows: {processedData.filter(row => row._isValid).length}</span>
<span class="text-orange-600">Already printed: {processedData.filter(row => isRowAlreadyPrinted(row)).length}</span>
<span class="text-orange-600">Printed: {processedData.filter(row => isRowAlreadyPrinted(row)).length}</span>
<span>Filtered rows: {filteredData.length}</span>
<span class="font-medium text-blue-600">Selected: {selectedValidCount}</span>
<button
onclick={processSheetData}
disabled={isLoading}
class="ml-auto inline-flex items-center px-3 py-1 bg-blue-600 text-white rounded-md text-sm font-medium hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-wait"
>
{#if isLoading}
<svg class="h-4 w-4 mr-2 animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z" />
</svg>
Refreshing...
{:else}
Refresh Data
{/if}
</button>
</div>
</div>
<!-- Data Table -->
<div class="bg-white border border-gray-200 rounded-lg overflow-hidden mb-6">
<div class="bg-white border border-gray-200 rounded-lg overflow-hidden mb-6 relative">
{#if isLoading}
<div class="absolute inset-0 flex items-center justify-center bg-white bg-opacity-75 z-10">
<svg class="h-10 w-10 text-blue-600 animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z" />
</svg>
</div>
{/if}
{#if filteredData.length === 0}
<div class="text-center py-12">
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
@@ -275,16 +321,16 @@
<input
type="checkbox"
bind:checked={selectAll}
on:change={toggleSelectAll}
onchange={toggleSelectAll}
class="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
/>
</th>
<!-- Column Headers -->
{#each headers as header}
{#each headers.filter(h => h !== 'alreadyPrinted') as header}
<th
class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100"
on:click={() => handleSort(header)}
onclick={() => handleSort(header)}
>
<div class="flex items-center space-x-1">
<span>{getFieldLabel(header)}</span>
@@ -300,7 +346,7 @@
</div>
</th>
{/each}
<!-- Status Column -->
<th class="px-3 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Status
@@ -316,37 +362,21 @@
<input
type="checkbox"
checked={selectedRows.has(row._rowIndex)}
on:change={() => toggleRowSelection(row._rowIndex)}
onchange={() => 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)}
<div class="text-xs text-orange-600 mt-1">Already printed</div>
{/if}
{:else}
<div class="w-4 h-4 bg-gray-200 rounded"></div>
{/if}
</td>
<!-- Data Columns -->
{#each headers as header}
{#each headers.filter(h => h !== 'alreadyPrinted') as header}
<td class="px-3 py-4 text-sm text-gray-900 max-w-xs truncate">
{#if header === 'alreadyPrinted'}
{#if isRowAlreadyPrinted(row)}
<span class="inline-flex px-2 py-1 text-xs font-medium bg-orange-100 text-orange-800 rounded-full">
Already Printed
</span>
{:else}
<span class="inline-flex px-2 py-1 text-xs font-medium bg-gray-100 text-gray-600 rounded-full">
Not Printed
</span>
{/if}
{:else}
{row[header] || ''}
{/if}
{row[header] || ''}
</td>
{/each}
<!-- Status Column -->
<td class="px-3 py-4 text-sm">
<div class="flex flex-col space-y-1">
@@ -390,13 +420,21 @@
{/if}
<!-- Navigation -->
<div class="flex justify-end">
<div class="flex justify-between">
<button
on:click={handleContinue}
disabled={selectedValidCount === 0}
onclick={() => currentStep.set(3)}
class="px-4 py-2 bg-gray-200 text-gray-700 rounded-lg font-medium hover:bg-gray-300"
>
← Back to Colum Selection
</button>
<button
onclick={handleContinue}
disabled={!canProceed}
class="px-4 py-2 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed"
>
{selectedValidCount > 0 ? `Continue with ${selectedValidCount} ${selectedValidCount === 1 ? 'row' : 'rows'} ` : 'Select rows to continue'}
{canProceed
? `Continue with ${selectedValidCount} ${selectedValidCount === 1 ? 'row' : 'rows'} `
: 'Select rows to continue'}
</button>
</div>
</div>