feat: implement offline queue, background sync and AI robustness
This commit is contained in:
@@ -12,9 +12,10 @@ export const geminiModel = genAI.getGenerativeModel({
|
||||
});
|
||||
|
||||
export const SYSTEM_INSTRUCTION = `
|
||||
You are a sommelier and database clerk. Analyze the whisky bottle image. Extract precise metadata.
|
||||
If a value is not visible, use null.
|
||||
Infer the 'Category' (e.g., Islay Single Malt) based on the Distillery if possible.
|
||||
You are a sommelier and database clerk. Analyze the whisky bottle image. Extract precise metadata.
|
||||
If the image is NOT a whisky bottle or if you are very unsure, set "is_whisky" to false and provide a low "confidence" score.
|
||||
If a value is not visible, use null.
|
||||
Infer the 'Category' (e.g., Islay Single Malt) based on the Distillery if possible.
|
||||
Search specifically for a "Whiskybase ID" or "WB ID" on the label.
|
||||
Output raw JSON matching the following schema:
|
||||
{
|
||||
@@ -25,6 +26,8 @@ Output raw JSON matching the following schema:
|
||||
"age": number | null,
|
||||
"vintage": string | null,
|
||||
"bottleCode": string | null,
|
||||
"whiskybaseId": string | null
|
||||
"whiskybaseId": string | null,
|
||||
"is_whisky": boolean,
|
||||
"confidence": number (0-100)
|
||||
}
|
||||
`;
|
||||
|
||||
66
src/lib/offline-db.ts
Normal file
66
src/lib/offline-db.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
export interface PendingBottle {
|
||||
id: string;
|
||||
imageBase64: string;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
const DB_NAME = 'WhiskyVaultOffline';
|
||||
const STORE_NAME = 'pendingCaptures';
|
||||
const DB_VERSION = 1;
|
||||
|
||||
export const openDB = (): Promise<IDBDatabase> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
||||
|
||||
request.onupgradeneeded = (event) => {
|
||||
const db = (event.target as IDBOpenDBRequest).result;
|
||||
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
||||
db.createObjectStore(STORE_NAME, { keyPath: 'id' });
|
||||
}
|
||||
};
|
||||
|
||||
request.onsuccess = (event) => {
|
||||
resolve((event.target as IDBOpenDBRequest).result);
|
||||
};
|
||||
|
||||
request.onerror = (event) => {
|
||||
reject((event.target as IDBOpenDBRequest).error);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const savePendingBottle = async (bottle: PendingBottle): Promise<void> => {
|
||||
const db = await openDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction(STORE_NAME, 'readwrite');
|
||||
const store = transaction.objectStore(STORE_NAME);
|
||||
const request = store.put(bottle);
|
||||
|
||||
request.onsuccess = () => resolve();
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
};
|
||||
|
||||
export const getAllPendingBottles = async (): Promise<PendingBottle[]> => {
|
||||
const db = await openDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction(STORE_NAME, 'readonly');
|
||||
const store = transaction.objectStore(STORE_NAME);
|
||||
const request = store.getAll();
|
||||
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
};
|
||||
|
||||
export const deletePendingBottle = async (id: string): Promise<void> => {
|
||||
const db = await openDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction(STORE_NAME, 'readwrite');
|
||||
const store = transaction.objectStore(STORE_NAME);
|
||||
const request = store.delete(id);
|
||||
|
||||
request.onsuccess = () => resolve();
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user