11import Dexie , { type Table } from 'dexie' ;
22import { version as appVersion } from '../../package.json' ;
33import i18n from '../i18n/i18n' ;
4- import { getLocale } from './utils' ;
4+ import { getLocale , lookupBarcode , fetchAndCompressImage } from './utils' ;
55import type {
66 Product ,
77 StorageLocation ,
@@ -200,7 +200,14 @@ export async function exportCSV(): Promise<string> {
200200 return BOM + [ headers . join ( ';' ) , ...rows . map ( ( r ) => r . join ( ';' ) ) ] . join ( '\r\n' ) ;
201201}
202202
203- export async function importData ( jsonString : string ) : Promise < number > {
203+ export interface ImportDataResult {
204+ imported : number ;
205+ skipped : number ;
206+ /** IDs von importierten Produkten die einen Barcode aber kein Foto haben */
207+ productsNeedingImages : number [ ] ;
208+ }
209+
210+ export async function importData ( jsonString : string ) : Promise < ImportDataResult > {
204211 const t = i18n . t . bind ( i18n ) ;
205212 let data : Record < string , unknown > ;
206213 try {
@@ -219,6 +226,7 @@ export async function importData(jsonString: string): Promise<number> {
219226
220227 let imported = 0 ;
221228 let skipped = 0 ;
229+ const productsNeedingImages : number [ ] = [ ] ;
222230
223231 await db . transaction (
224232 'rw' ,
@@ -273,12 +281,13 @@ export async function importData(jsonString: string): Promise<number> {
273281 // Clean up photo field - don't import placeholder markers
274282 const rawPhoto = product . photo ;
275283 const photo = rawPhoto && rawPhoto !== '[FOTO]' && typeof rawPhoto === 'string' ? rawPhoto : undefined ;
284+ const barcode = typeof product . barcode === 'string' ? product . barcode : undefined ;
276285 const now = new Date ( ) . toISOString ( ) ;
277286
278287 // Only import known fields to prevent injection of unexpected data
279- await db . products . add ( {
288+ const newId = await db . products . add ( {
280289 name : String ( product . name ) ,
281- barcode : typeof product . barcode === 'string' ? product . barcode : undefined ,
290+ barcode,
282291 category : typeof product . category === 'string' ? product . category as Product [ 'category' ] : 'sonstiges' ,
283292 storageLocation : typeof product . storageLocation === 'string' ? product . storageLocation : 'Keller' ,
284293 quantity : typeof product . quantity === 'number' ? product . quantity : 1 ,
@@ -293,6 +302,11 @@ export async function importData(jsonString: string): Promise<number> {
293302 updatedAt : typeof product . updatedAt === 'string' ? product . updatedAt : now ,
294303 } ) ;
295304 imported ++ ;
305+
306+ // Produkt hat Barcode aber kein Foto → Bild nachladen
307+ if ( barcode && ! photo ) {
308+ productsNeedingImages . push ( newId ) ;
309+ }
296310 }
297311
298312 // Import consumption logs
@@ -305,23 +319,65 @@ export async function importData(jsonString: string): Promise<number> {
305319 ) ;
306320
307321 if ( skipped > 0 ) {
308- throw new ImportResult ( imported , skipped ) ;
322+ throw new ImportResult ( imported , skipped , productsNeedingImages ) ;
323+ }
324+
325+ return { imported, skipped, productsNeedingImages } ;
326+ }
327+
328+ /**
329+ * Lädt Produktbilder im Hintergrund per Barcode von Open Food Facts.
330+ * Ruft für jedes Produkt lookupBarcode auf, holt das Bild und speichert es.
331+ * @param productIds - IDs der Produkte die ein Bild brauchen
332+ * @param onProgress - Callback für Fortschritt (geladen, gesamt)
333+ */
334+ export async function loadImportedImages (
335+ productIds : number [ ] ,
336+ onProgress ?: ( loaded : number , total : number ) => void
337+ ) : Promise < number > {
338+ let loaded = 0 ;
339+ const total = productIds . length ;
340+
341+ for ( const id of productIds ) {
342+ try {
343+ const product = await db . products . get ( id ) ;
344+ if ( ! product ?. barcode || product . photo ) {
345+ onProgress ?.( ++ loaded , total ) ;
346+ continue ;
347+ }
348+
349+ const result = await lookupBarcode ( product . barcode ) ;
350+ if ( result ?. imageUrl ) {
351+ const photo = await fetchAndCompressImage ( result . imageUrl ) ;
352+ if ( photo ) {
353+ await db . products . update ( id , {
354+ photo,
355+ updatedAt : new Date ( ) . toISOString ( ) ,
356+ } ) ;
357+ }
358+ }
359+ } catch {
360+ // Einzelnes Bild fehlgeschlagen — weiter mit dem nächsten
361+ }
362+ onProgress ?.( ++ loaded , total ) ;
309363 }
310364
311- return imported ;
365+ return loaded ;
312366}
313367
314368// Custom class to pass both imported and skipped counts
315369export class ImportResult extends Error {
316370 imported : number ;
317371 skipped : number ;
372+ productsNeedingImages : number [ ] ;
318373
319- constructor ( imported : number , skipped : number ) {
374+ constructor ( imported : number , skipped : number , productsNeedingImages : number [ ] = [ ] ) {
320375 const t = i18n . t . bind ( i18n ) ;
321376 const msg = t ( 'dbErrors.importResult' , { imported, skipped } ) ;
322377 super ( msg ) ;
323378 this . name = 'ImportResult' ;
324379 this . imported = imported ;
325380 this . skipped = skipped ;
381+ this . productsNeedingImages = productsNeedingImages ;
326382 }
327383}
0 commit comments