Fixed other two components
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user