Fixed the rest
This commit is contained in:
@@ -5,15 +5,17 @@
|
||||
import StepColumnMap from './wizard/StepColumnMap.svelte';
|
||||
import StepRowFilter from './wizard/StepRowFilter.svelte';
|
||||
import StepCardDetails from './wizard/StepCardDetails.svelte';
|
||||
// import StepGallery from './wizard/StepGallery.svelte';
|
||||
// import StepGenerate from './wizard/StepGenerate.svelte';
|
||||
import StepGallery from './wizard/StepGallery.svelte';
|
||||
import StepGenerate from './wizard/StepGenerate.svelte';
|
||||
|
||||
const steps = [
|
||||
StepAuth,
|
||||
StepSheetSearch,
|
||||
StepColumnMap,
|
||||
StepRowFilter,
|
||||
StepCardDetails
|
||||
StepCardDetails,
|
||||
StepGallery,
|
||||
StepGenerate
|
||||
];
|
||||
|
||||
const stepTitles = [
|
||||
@@ -21,7 +23,9 @@
|
||||
'Select Sheet',
|
||||
'Map Columns',
|
||||
'Filter Rows',
|
||||
'Enter Card Details'
|
||||
'Enter Card Details',
|
||||
'Preview Gallery',
|
||||
'Generate Cards'
|
||||
];
|
||||
</script>
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
console.error('Failed to save to localStorage:', error);
|
||||
}
|
||||
$cardDetails = { homeSection, validityStart };
|
||||
$currentStep++;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { env } from '$env/dynamic/public';
|
||||
import { columnMapping, filteredSheetData, currentStep, pictures, cropRects } from '$lib/stores';
|
||||
import { columnMapping, sheetData, currentStep, pictures, cropRects } from '$lib/stores';
|
||||
import { downloadDriveImage, isGoogleDriveUrl, createImageObjectUrl } from '$lib/google';
|
||||
import Navigator from './subcomponents/Navigator.svelte';
|
||||
import PhotoCard from './subcomponents/PhotoCard.svelte';
|
||||
@@ -61,8 +61,6 @@
|
||||
}
|
||||
|
||||
async function processPhotosInParallel() {
|
||||
if (isProcessing) return;
|
||||
|
||||
console.log('Starting processPhotos with queues...');
|
||||
isProcessing = true;
|
||||
processedCount = 0;
|
||||
@@ -73,6 +71,7 @@
|
||||
console.log('Cleared IndexedDB.');
|
||||
} catch (e) {
|
||||
console.error('Could not clear IndexedDB:', e);
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize queues with more conservative concurrency
|
||||
@@ -95,7 +94,7 @@
|
||||
}
|
||||
});
|
||||
|
||||
const validRows = $filteredSheetData.filter((row) => row._isValid);
|
||||
const validRows = $sheetData.filter((row) => row._valid);
|
||||
const photoUrls = new Set<string>();
|
||||
const photoMap = new Map<string, any[]>();
|
||||
|
||||
@@ -130,16 +129,12 @@
|
||||
|
||||
// Initialize detector and process photos
|
||||
onMount(() => {
|
||||
console.log('StepGallery mounted');
|
||||
initializeDetector(); // Start loading model
|
||||
if ($filteredSheetData.length > 0 && $columnMapping.pictureUrl !== undefined) {
|
||||
if ($sheetData.length > 0 && $columnMapping.pictureUrl !== undefined) {
|
||||
console.log('Processing photos for gallery step');
|
||||
processPhotosInParallel();
|
||||
} else {
|
||||
console.log('No data to process:', {
|
||||
dataLength: $filteredSheetData.length,
|
||||
pictureUrlMapping: $columnMapping.pictureUrl
|
||||
});
|
||||
console.log('No data to process: !');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -148,7 +143,6 @@
|
||||
|
||||
if (!isRetry) {
|
||||
photo.status = 'loading';
|
||||
// No need to reassign photos array with $state reactivity
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -285,14 +279,6 @@
|
||||
imageTensor = tf.browser.fromPixels(img);
|
||||
const predictions = await detector.estimateFaces(imageTensor, false);
|
||||
|
||||
// Log memory usage for debugging
|
||||
const memInfo = tf.memory();
|
||||
console.log(`TensorFlow.js memory after face detection for ${photo.name}:`, {
|
||||
numTensors: memInfo.numTensors,
|
||||
numDataBuffers: memInfo.numDataBuffers,
|
||||
numBytes: memInfo.numBytes
|
||||
});
|
||||
|
||||
if (predictions.length > 0) {
|
||||
const getProbability = (p: number | tf.Tensor) =>
|
||||
typeof p === 'number' ? p : p.dataSync()[0];
|
||||
@@ -518,23 +504,6 @@
|
||||
<div class="text-gray-600">Failed</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if photos.filter((p) => p.status === 'error').length > 0}
|
||||
<div class="mt-4 rounded border border-yellow-200 bg-yellow-50 p-3">
|
||||
<p class="text-sm text-yellow-800">
|
||||
<strong>Note:</strong> Cards will only be generated for photos that load successfully.
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !canProceed() && photos.filter((p) => p.status === 'success').length > 0}
|
||||
<div class="mt-4 rounded border border-blue-200 bg-blue-50 p-3">
|
||||
<p class="text-sm text-blue-800">
|
||||
<strong>Tip:</strong> All photos need to be cropped before proceeding. Face detection runs
|
||||
automatically.
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { filteredSheetData, currentStep, pictures, cropRects } from '$lib/stores';
|
||||
import { sheetData, currentStep, pictures, cropRects } from '$lib/stores';
|
||||
import { PDFDocument, StandardFonts, rgb } from 'pdf-lib';
|
||||
import * as fontkit from 'fontkit';
|
||||
import { clear } from 'idb-keyval';
|
||||
@@ -30,11 +30,12 @@
|
||||
url: string | null;
|
||||
size: number | null;
|
||||
error: string | null;
|
||||
downloadName?: string;
|
||||
};
|
||||
|
||||
const initialFiles: GeneratedFile[] = [
|
||||
{
|
||||
name: 'people_data.pdf',
|
||||
name: 'esncards_text.pdf',
|
||||
displayName: 'Text PDF',
|
||||
state: 'idle',
|
||||
url: null,
|
||||
@@ -42,7 +43,7 @@
|
||||
error: null
|
||||
},
|
||||
{
|
||||
name: 'people_photos.pdf',
|
||||
name: 'esncards_photos.pdf',
|
||||
displayName: 'Photos PDF',
|
||||
state: 'idle',
|
||||
url: null,
|
||||
@@ -71,11 +72,11 @@
|
||||
onMount(() => {
|
||||
// Add event listener for page unload
|
||||
window.addEventListener('beforeunload', handleBeforeUnload);
|
||||
|
||||
|
||||
// Start generation automatically when the component mounts
|
||||
handleGenerate('people_data.pdf');
|
||||
handleGenerate('people_photos.pdf');
|
||||
|
||||
handleGenerate('esncards_text.pdf');
|
||||
handleGenerate('esncards_photos.pdf');
|
||||
|
||||
// Cleanup function when component unmounts
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', handleBeforeUnload);
|
||||
@@ -97,6 +98,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Create a formatted timestamp string
|
||||
function getTimestamp(): string {
|
||||
const d = new Date();
|
||||
const year = d.getFullYear();
|
||||
const month = (d.getMonth() + 1).toString().padStart(2, '0');
|
||||
const day = d.getDate().toString().padStart(2, '0');
|
||||
const hours = d.getHours().toString().padStart(2, '0');
|
||||
const minutes = d.getMinutes().toString().padStart(2, '0');
|
||||
return `${year}-${month}-${day}-${hours}-${minutes}`;
|
||||
}
|
||||
|
||||
// Crop image using canvas
|
||||
async function cropImage(
|
||||
imageBlob: Blob,
|
||||
@@ -163,7 +175,7 @@
|
||||
|
||||
try {
|
||||
const pdfBytes =
|
||||
fileName === 'people_data.pdf' ? await generateTextPDF() : await generatePhotoPDF();
|
||||
fileName === 'esncards_text.pdf' ? await generateTextPDF() : await generatePhotoPDF();
|
||||
|
||||
const blob = new Blob([pdfBytes], { type: 'application/pdf' });
|
||||
|
||||
@@ -176,6 +188,10 @@
|
||||
fileToUpdate.size = pdfBytes.length;
|
||||
fileToUpdate.state = 'done';
|
||||
|
||||
const timestamp = getTimestamp();
|
||||
const baseName = fileName.replace('.pdf', '');
|
||||
fileToUpdate.downloadName = `${baseName}_${timestamp}.pdf`;
|
||||
|
||||
// Check if both PDFs are done, then clear sensitive data
|
||||
const allDone = files.every((f) => f.state === 'done' || f.state === 'error');
|
||||
if (allDone) {
|
||||
@@ -214,7 +230,7 @@
|
||||
let currentRow = 0;
|
||||
let currentCol = 0;
|
||||
|
||||
const validRows = $filteredSheetData.filter((row) => row._isValid);
|
||||
const validRows = $sheetData.filter((row) => row._valid);
|
||||
|
||||
for (let i = 0; i < validRows.length; i++) {
|
||||
const row = validRows[i];
|
||||
@@ -224,11 +240,10 @@
|
||||
const cellY_mm = PAGE_SETTINGS.margin + currentRow * gridLayout.cellHeight;
|
||||
|
||||
// Get field values
|
||||
const name = row.name || row.Name || '';
|
||||
const surname = row.surname || row.Surname || row.lastname || row.LastName || '';
|
||||
const nationality = row.nationality || row.Nationality || row.country || row.Country || '';
|
||||
const birthday =
|
||||
row.birthday || row.Birthday || row.birthdate || row.Birthdate || row.birth_date || '';
|
||||
const name = row.name;
|
||||
const surname = row.surname;
|
||||
const nationality = row.nationality;
|
||||
const birthday = row.birthday;
|
||||
|
||||
// Draw name
|
||||
const namePos = getAbsolutePositionPt(
|
||||
@@ -319,7 +334,7 @@
|
||||
let currentRow = 0;
|
||||
let currentCol = 0;
|
||||
|
||||
const validRows = $filteredSheetData.filter((row) => row._isValid);
|
||||
const validRows = $sheetData.filter((row) => row._valid);
|
||||
|
||||
for (let i = 0; i < validRows.length; i++) {
|
||||
const row = validRows[i];
|
||||
@@ -336,7 +351,7 @@
|
||||
PHOTO_FIELD_LAYOUT.photo
|
||||
);
|
||||
|
||||
const pictureUrl = row.pictureUrl || row.picture_url || row.Picture || row.PictureUrl;
|
||||
const pictureUrl = row.pictureUrl;
|
||||
const pictureInfo = $pictures[pictureUrl];
|
||||
const cropData = $cropRects[pictureUrl];
|
||||
|
||||
@@ -397,8 +412,8 @@
|
||||
}
|
||||
|
||||
// Draw name
|
||||
const name = row.name || row.Name || '';
|
||||
const surname = row.surname || row.Surname || row.lastname || row.LastName || '';
|
||||
const name = row.name;
|
||||
const surname = row.surname;
|
||||
const namePos = getAbsolutePositionPt(
|
||||
cellX_mm,
|
||||
cellY_mm,
|
||||
@@ -430,7 +445,7 @@
|
||||
if (!file.url) return;
|
||||
const link = document.createElement('a');
|
||||
link.href = file.url;
|
||||
link.download = file.name;
|
||||
link.download = file.downloadName || file.name;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
@@ -443,10 +458,10 @@
|
||||
}
|
||||
});
|
||||
files = JSON.parse(JSON.stringify(initialFiles));
|
||||
|
||||
|
||||
// Clear sensitive data when starting over
|
||||
clearSensitiveData();
|
||||
|
||||
|
||||
currentStep.set(0);
|
||||
}
|
||||
|
||||
@@ -478,7 +493,7 @@
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm">
|
||||
<div class="text-center">
|
||||
<div class="text-2xl font-bold text-gray-900">
|
||||
{$filteredSheetData.filter((row) => row._isValid).length}
|
||||
{$sheetData.filter((row) => row._valid).length}
|
||||
</div>
|
||||
<div class="text-gray-600">Records to Process</div>
|
||||
</div>
|
||||
@@ -590,7 +605,7 @@
|
||||
<!-- Navigation -->
|
||||
<div class="flex justify-between">
|
||||
<button
|
||||
onclick={() => currentStep.set(5)}
|
||||
onclick={() => currentStep.set(6)}
|
||||
class="px-4 py-2 bg-gray-200 text-gray-700 rounded-lg font-medium hover:bg-gray-300"
|
||||
>
|
||||
← Back to Gallery
|
||||
|
||||
@@ -38,7 +38,6 @@ export interface RowData {
|
||||
// Picture storage and metadata
|
||||
export interface PictureBlobInfoType {
|
||||
id: string;
|
||||
blob: Blob;
|
||||
url: string;
|
||||
downloaded: boolean;
|
||||
faceDetected: boolean;
|
||||
@@ -87,7 +86,7 @@ export const sheetData = writable<RowData[]>([]);
|
||||
export const pictures = writable<Record<string, PictureBlobInfoType>>({});
|
||||
|
||||
// Store and hold the crop rectangles from face detection
|
||||
export const CropRects = writable<Record<string, CropType>>({});
|
||||
export const cropRects = writable<Record<string, CropType>>({});
|
||||
|
||||
// Store and hold the selected sheet
|
||||
export const selectedSheet = writable<SheetInfoType>({ id: '', name: '', webViewLink: '' });
|
||||
|
||||
Reference in New Issue
Block a user