Reworked row filter
This commit is contained in:
@@ -4,8 +4,7 @@
|
||||
columnMapping,
|
||||
rawSheetData,
|
||||
filteredSheetData,
|
||||
currentStep,
|
||||
sheetData
|
||||
currentStep
|
||||
} from '$lib/stores';
|
||||
import Navigator from './subcomponents/Navigator.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
@@ -16,30 +15,52 @@
|
||||
let sortDirection: 'asc' | 'desc' = 'asc';
|
||||
let selectedRows = new Set<number>();
|
||||
let selectAll = false;
|
||||
let processedData: any[] = [];
|
||||
let filteredData: any[] = [];
|
||||
let processedData = $state<any[]>([]);
|
||||
let headers: string[] = [];
|
||||
let isLoading = false;
|
||||
let isLoading = $state(false);
|
||||
let showAlreadyPrinted = $state(false);
|
||||
|
||||
$: {
|
||||
// Filter data based on search term
|
||||
// Use $state for displayData instead of $derived to avoid TypeScript errors
|
||||
let displayData = $state<any[]>([]);
|
||||
|
||||
// Update displayData whenever relevant values change
|
||||
$effect(() => {
|
||||
// Debug log at the start
|
||||
console.log('Updating displayData from processedData:', processedData);
|
||||
|
||||
// If processedData is empty, return empty array
|
||||
if (!processedData || !processedData.length) {
|
||||
displayData = [];
|
||||
return;
|
||||
}
|
||||
|
||||
// Clone array to avoid mutations
|
||||
let data = [...processedData];
|
||||
console.log('Initial data length:', data.length);
|
||||
|
||||
// 1. Filter by search term
|
||||
if (searchTerm.trim()) {
|
||||
filteredData = processedData.filter((row) =>
|
||||
data = data.filter((row) =>
|
||||
Object.values(row).some((value) =>
|
||||
String(value).toLowerCase().includes(searchTerm.toLowerCase())
|
||||
String(value)
|
||||
.toLowerCase()
|
||||
.includes(searchTerm.toLowerCase())
|
||||
)
|
||||
);
|
||||
} else {
|
||||
filteredData = processedData;
|
||||
console.log('After search term filter:', data.length);
|
||||
}
|
||||
}
|
||||
|
||||
$: {
|
||||
// Sort data if sort column is selected
|
||||
if (sortColumn && filteredData.length > 0) {
|
||||
filteredData = [...filteredData].sort((a, b) => {
|
||||
const aVal = String(a[sortColumn]).toLowerCase();
|
||||
const bVal = String(b[sortColumn]).toLowerCase();
|
||||
// 2. Filter out already printed rows if collapsed
|
||||
if (!showAlreadyPrinted) {
|
||||
data = data.filter((row) => !isRowAlreadyPrinted(row));
|
||||
console.log('After already printed filter:', data.length);
|
||||
}
|
||||
|
||||
// 3. Sort the data
|
||||
if (sortColumn) {
|
||||
data = [...data].sort((a, b) => {
|
||||
const aVal = String(a[sortColumn] || '').toLowerCase();
|
||||
const bVal = String(b[sortColumn] || '').toLowerCase();
|
||||
|
||||
if (sortDirection === 'asc') {
|
||||
return aVal.localeCompare(bVal);
|
||||
@@ -48,7 +69,10 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Final filtered data:', data);
|
||||
displayData = data;
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
console.log('StepRowFilter mounted');
|
||||
@@ -57,13 +81,25 @@
|
||||
|
||||
// Fetch raw sheet data from Google Sheets if not already loaded
|
||||
async function fetchRawSheetData() {
|
||||
console.log("Fetching raw sheet data...");
|
||||
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);
|
||||
console.log('Fetching raw sheet data...');
|
||||
try {
|
||||
if (!$selectedSheet) {
|
||||
console.error('No sheet selected');
|
||||
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);
|
||||
console.log('Fetched data:', data);
|
||||
rawSheetData.set(data);
|
||||
} catch (e) {
|
||||
console.error('Error fetching raw sheet data:', e);
|
||||
// Re-throw the error to be caught by the caller
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async function processSheetData() {
|
||||
@@ -74,6 +110,13 @@
|
||||
|
||||
await fetchRawSheetData();
|
||||
|
||||
// Debug logging
|
||||
console.log('Raw sheet data:', $rawSheetData);
|
||||
console.log('Column mapping:', $columnMapping);
|
||||
|
||||
// Clear existing state before processing new data
|
||||
selectedRows = new Set();
|
||||
|
||||
// Process the data starting from row 2 (skip header row)
|
||||
processedData = $rawSheetData.slice(1).map((row, index) => {
|
||||
const processedRow: any = {
|
||||
@@ -119,9 +162,20 @@
|
||||
});
|
||||
|
||||
const initialSelection = rowsToConsider.map((row) => row._rowIndex);
|
||||
selectedRows = new Set(initialSelection);
|
||||
// Make sure we create a completely new Set for reactivity
|
||||
selectedRows = new Set([...initialSelection]);
|
||||
|
||||
// Update UI state
|
||||
updateSelectAllState();
|
||||
updateSelectedValidCount();
|
||||
|
||||
// Debug logging
|
||||
console.log('Processed data:', processedData);
|
||||
console.log('Display data:', displayData);
|
||||
console.log('Selected rows:', selectedRows);
|
||||
console.log('Selected valid count after initialization:', selectedValidCount);
|
||||
} catch (e) {
|
||||
console.error('Error processing sheet data:', e);
|
||||
} finally {
|
||||
isLoading = false;
|
||||
}
|
||||
@@ -133,43 +187,62 @@
|
||||
} else {
|
||||
selectedRows.add(rowIndex);
|
||||
}
|
||||
selectedRows = new Set(selectedRows); // Trigger reactivity
|
||||
// Force reactivity with new Set
|
||||
selectedRows = new Set([...selectedRows]);
|
||||
console.log('Toggled row selection, new selectedRows size:', selectedRows.size);
|
||||
updateSelectAllState();
|
||||
updateSelectedValidCount();
|
||||
console.log('After toggle - canProceed:', canProceed);
|
||||
console.log('After toggle - selectedValidCount > 0:', selectedValidCount > 0);
|
||||
console.log('After toggle - selectedValidCount:', selectedValidCount);
|
||||
}
|
||||
|
||||
function toggleSelectAll() {
|
||||
if (selectAll) {
|
||||
// Deselect all visible valid rows
|
||||
filteredData.forEach((row) => {
|
||||
// Toggle selectAll state first
|
||||
selectAll = !selectAll;
|
||||
console.log('Toggle select all clicked, new state:', selectAll);
|
||||
|
||||
if (!selectAll) {
|
||||
// If now unchecked, deselect all visible valid rows
|
||||
displayData.forEach((row) => {
|
||||
if (row._isValid) {
|
||||
selectedRows.delete(row._rowIndex);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Select all visible valid rows that aren't already printed
|
||||
const rowsToSelect = filteredData.filter(
|
||||
(row) => row._isValid && !isRowAlreadyPrinted(row) && !selectedRows.has(row._rowIndex)
|
||||
);
|
||||
|
||||
for (const row of rowsToSelect) {
|
||||
selectedRows.add(row._rowIndex);
|
||||
}
|
||||
// If now checked, select all visible valid rows
|
||||
displayData.forEach((row) => {
|
||||
if (row._isValid) {
|
||||
selectedRows.add(row._rowIndex);
|
||||
}
|
||||
});
|
||||
}
|
||||
selectedRows = new Set(selectedRows);
|
||||
updateSelectAllState();
|
||||
|
||||
// Force reactivity with new Set
|
||||
selectedRows = new Set([...selectedRows]);
|
||||
console.log('Toggled select all, new selectedRows size:', selectedRows.size);
|
||||
updateSelectedValidCount();
|
||||
console.log('Selected valid count after toggleSelectAll:', selectedValidCount);
|
||||
}
|
||||
|
||||
function updateSelectAllState() {
|
||||
const visibleValidUnprintedRows = filteredData.filter(
|
||||
(row) => row._isValid && !isRowAlreadyPrinted(row)
|
||||
);
|
||||
const selectedVisibleValidUnprintedRows = visibleValidUnprintedRows.filter((row) =>
|
||||
selectedRows.has(row._rowIndex)
|
||||
);
|
||||
// Find all valid rows that are currently visible
|
||||
const visibleValidRows = displayData.filter(row => row._isValid);
|
||||
|
||||
selectAll =
|
||||
visibleValidUnprintedRows.length > 0 &&
|
||||
selectedVisibleValidUnprintedRows.length === visibleValidUnprintedRows.length;
|
||||
if (visibleValidRows.length === 0) {
|
||||
// No valid rows to select
|
||||
selectAll = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if all visible valid rows are selected
|
||||
const allSelected = visibleValidRows.every(row => selectedRows.has(row._rowIndex));
|
||||
|
||||
// Update selectAll state
|
||||
selectAll = allSelected;
|
||||
console.log('updateSelectAllState: selectAll =', selectAll,
|
||||
', visibleValidRows =', visibleValidRows.length,
|
||||
', selectedRows size =', selectedRows.size);
|
||||
}
|
||||
|
||||
function handleSort(column: string) {
|
||||
@@ -211,12 +284,41 @@
|
||||
filteredSheetData.set(selectedData);
|
||||
}
|
||||
|
||||
$: selectedValidCount = Array.from(selectedRows).filter((rowIndex) => {
|
||||
const row = processedData.find((r) => r._rowIndex === rowIndex);
|
||||
return row && row._isValid;
|
||||
}).length;
|
||||
// Use $state for selectedValidCount
|
||||
let selectedValidCount = $state(0);
|
||||
|
||||
// Create a dedicated function to recalculate selectedValidCount
|
||||
function updateSelectedValidCount() {
|
||||
// Get array of row indices
|
||||
const rowIndices = Array.from(selectedRows);
|
||||
console.log('Selected row indices:', rowIndices);
|
||||
|
||||
// Count valid rows
|
||||
let count = 0;
|
||||
for (const rowIndex of rowIndices) {
|
||||
// Find the row in processedData
|
||||
const row = processedData.find(r => r._rowIndex === rowIndex);
|
||||
if (row && row._isValid) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Setting selectedValidCount to:', count, 'from', selectedValidCount);
|
||||
selectedValidCount = count;
|
||||
}
|
||||
|
||||
// Update count when selectedRows or processedData changes
|
||||
$effect(() => {
|
||||
// Track dependencies explicitly
|
||||
const rowsSize = selectedRows.size;
|
||||
const dataSize = processedData.length;
|
||||
|
||||
console.log('Effect triggered - selectedRows size:', rowsSize, 'processedData size:', dataSize);
|
||||
updateSelectedValidCount();
|
||||
});
|
||||
|
||||
// Allow proceeding only if at least one valid row is selected
|
||||
$: canProceed = selectedValidCount > 0;
|
||||
let canProceed = $derived(selectedValidCount > 0);
|
||||
</script>
|
||||
|
||||
<div class="p-6">
|
||||
@@ -269,41 +371,57 @@
|
||||
<span class="text-orange-600"
|
||||
>Printed: {processedData.filter((row) => isRowAlreadyPrinted(row)).length}</span
|
||||
>
|
||||
<span>Filtered rows: {filteredData.length}</span>
|
||||
<span>Filtered rows: {displayData.length}</span>
|
||||
<span class="font-medium text-blue-600">Selected: {selectedValidCount}</span>
|
||||
<button
|
||||
onclick={processSheetData}
|
||||
disabled={isLoading}
|
||||
class="ml-auto inline-flex items-center rounded-md bg-blue-600 px-3 py-1 text-sm font-medium text-white hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 focus:outline-none disabled:cursor-wait disabled:opacity-50"
|
||||
>
|
||||
{#if isLoading}
|
||||
<svg
|
||||
class="mr-2 h-4 w-4 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 class="ml-auto flex items-center gap-4">
|
||||
<button
|
||||
class="text-sm text-gray-600 hover:text-gray-900"
|
||||
onclick={() => (showAlreadyPrinted = !showAlreadyPrinted)}
|
||||
>
|
||||
{showAlreadyPrinted ? 'Hide' : 'Show'} Printed ({processedData.filter((row) =>
|
||||
isRowAlreadyPrinted(row)
|
||||
).length})
|
||||
</button>
|
||||
|
||||
<button
|
||||
onclick={processSheetData}
|
||||
disabled={isLoading}
|
||||
class="inline-flex items-center rounded-md bg-blue-600 px-3 py-1 text-sm font-medium text-white hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 focus:outline-none disabled:cursor-wait disabled:opacity-50"
|
||||
>
|
||||
{#if isLoading}
|
||||
<svg
|
||||
class="mr-2 h-4 w-4 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>
|
||||
</div>
|
||||
|
||||
<!-- Data Table -->
|
||||
<div class="relative mb-6 overflow-hidden rounded-lg border border-gray-200 bg-white">
|
||||
{#if filteredData.length === 0 && !isLoading}
|
||||
{#if displayData.length === 0 && !isLoading}
|
||||
<div class="py-12 text-center">
|
||||
<svg
|
||||
class="mx-auto h-12 w-12 text-gray-400"
|
||||
@@ -332,8 +450,12 @@
|
||||
<th class="px-3 py-3 text-left">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={selectAll}
|
||||
onchange={toggleSelectAll}
|
||||
checked={selectAll}
|
||||
onclick={(e) => {
|
||||
// Use event.preventDefault() to avoid default checkbox behavior
|
||||
e.preventDefault();
|
||||
toggleSelectAll();
|
||||
}}
|
||||
class="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||
disabled={isLoading}
|
||||
/>
|
||||
@@ -406,7 +528,7 @@
|
||||
{/each}
|
||||
{:else}
|
||||
<!-- Actual data rows -->
|
||||
{#each filteredData as row}
|
||||
{#each displayData as row}
|
||||
<tr
|
||||
class="hover:bg-gray-50 {!row._isValid ? 'opacity-50' : ''} {isRowAlreadyPrinted(
|
||||
row
|
||||
@@ -420,7 +542,11 @@
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selectedRows.has(row._rowIndex)}
|
||||
onchange={() => toggleRowSelection(row._rowIndex)}
|
||||
onclick={(e) => {
|
||||
// Use event.preventDefault() to avoid default checkbox behavior
|
||||
e.preventDefault();
|
||||
toggleRowSelection(row._rowIndex);
|
||||
}}
|
||||
class="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
|
||||
/>
|
||||
{:else}
|
||||
|
||||
Reference in New Issue
Block a user