Passo 12 completo. Build limpo, AC server-side totalmente verificado. O que foi implementado: lib/queue/ — camada de persistência offline: db.ts — Dexie 4 com tabelas pending e deadLetters broadcast.ts — BroadcastChannel helper (mai-call-sync) para comunicar entre tabs sync.ts — loop de sync com retry/backoff: signPhotoUpload → PUT MinIO → create; 409 = sucesso; 4xx = dead-letter; erros de rede = paragem + retry na próxima volta SyncProvider — React Context que: Arranca sync ao reconectar (online event + visibilitychange) Polling de 10s como fallback Regista Background Sync API quando disponível Expõe pendingCount / deadLetterCount via useSyncState() Formulário (/maintenance/new) — refatorado: ao submeter, escreve em IndexedDB e navega imediatamente para /sent sem esperar pelo servidor. O SyncProvider processa a fila em background. Feedback visual: SyncChip na home: "Tudo sincronizado" / "N pedidos por enviar" / erro dead-letter /maintenance/sent: mostra "Em fila" (Clock) ou "Enviado" (CheckCircle2) reactivamente via BroadcastChannel Workbox (@ducanh2912/next-pwa) — app shell precaching ativo, para que o app carregue mesmo sem rede depois da primeira visita.
41 lines
898 B
TypeScript
41 lines
898 B
TypeScript
import Dexie, { type Table } from 'dexie';
|
|
|
|
export interface PendingRequest {
|
|
clientRequestId: string; // primary key
|
|
workstationId: string;
|
|
description: string;
|
|
photoBlob?: Blob;
|
|
queuedAt: number;
|
|
retries: number;
|
|
lastError?: string;
|
|
}
|
|
|
|
export interface DeadLetter {
|
|
clientRequestId: string;
|
|
error: string;
|
|
failedAt: number;
|
|
}
|
|
|
|
class MaiCallDb extends Dexie {
|
|
pending!: Table<PendingRequest, string>;
|
|
deadLetters!: Table<DeadLetter, string>;
|
|
|
|
constructor() {
|
|
super('mai-call-v1');
|
|
this.version(1).stores({
|
|
pending: 'clientRequestId, queuedAt',
|
|
deadLetters: 'clientRequestId',
|
|
});
|
|
}
|
|
}
|
|
|
|
// Single browser-side instance. Guards against SSR import.
|
|
function makeDb(): MaiCallDb {
|
|
if (typeof window === 'undefined') {
|
|
return null as unknown as MaiCallDb; // never called server-side
|
|
}
|
|
return new MaiCallDb();
|
|
}
|
|
|
|
export const db = makeDb();
|