# Guia de Implementação - Sincronização Offline ## Visão Geral Este guia fornece instruções passo a passo para implementar a sincronização offline no aplicativo de entregas, baseado na análise completa da arquitetura existente. ## Fase 1: Preparação e Configuração ### 1.1 Instalação de Dependências ```bash # Dependências para sincronização npm install lz-string crypto-js npm install @react-native-async-storage/async-storage npm install expo-background-fetch expo-task-manager # Dependências para desenvolvimento npm install --save-dev @types/lz-string @types/crypto-js ``` ### 1.2 Configuração de Variáveis de Ambiente **Arquivo**: `.env` ```env # Configurações de sincronização SYNC_INTERVAL=900000 SYNC_RETRY_ATTEMPTS=3 SYNC_TIMEOUT=30000 MAX_CACHE_SIZE=100 ENCRYPTION_KEY=your-secret-encryption-key # Configurações de API API_BASE_URL=https://api.example.com API_TIMEOUT=10000 ``` ### 1.3 Atualização do Banco de Dados **Arquivo**: `src/services/database.ts` ```typescript // Adicionar ao setupDatabase export const setupDatabase = async (): Promise => { if (usingSQLite) { return new Promise((resolve, reject) => { db.transaction( (tx: any) => { // Tabelas existentes... // Nova tabela de controle de sincronização tx.executeSql( `CREATE TABLE IF NOT EXISTS sync_control ( id INTEGER PRIMARY KEY AUTOINCREMENT, table_name TEXT NOT NULL, last_sync_timestamp INTEGER, sync_status TEXT DEFAULT 'pending', created_at INTEGER DEFAULT (strftime('%s', 'now')), updated_at INTEGER DEFAULT (strftime('%s', 'now')) );` ); // Nova tabela de conflitos tx.executeSql( `CREATE TABLE IF NOT EXISTS sync_conflicts ( id TEXT PRIMARY KEY, table_name TEXT NOT NULL, record_id TEXT NOT NULL, local_data TEXT, server_data TEXT, conflict_fields TEXT, resolution TEXT, resolved_at INTEGER, created_at INTEGER DEFAULT (strftime('%s', 'now')) );` ); // Nova tabela de log de sincronização tx.executeSql( `CREATE TABLE IF NOT EXISTS sync_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, sync_type TEXT NOT NULL, table_name TEXT, record_id TEXT, action TEXT, success INTEGER DEFAULT 1, error_message TEXT, duration INTEGER, timestamp INTEGER DEFAULT (strftime('%s', 'now')) );` ); // Adicionar campos de controle às tabelas existentes tx.executeSql( `ALTER TABLE deliveries ADD COLUMN version INTEGER DEFAULT 1;` ); tx.executeSql( `ALTER TABLE deliveries ADD COLUMN last_modified INTEGER DEFAULT (strftime('%s', 'now'));` ); tx.executeSql( `ALTER TABLE deliveries ADD COLUMN sync_timestamp INTEGER;` ); // Inserir configurações de sincronização tx.executeSql( `INSERT OR IGNORE INTO settings (key, value) VALUES ('initial_sync_complete', 'false'), ('last_sync_time', '0'), ('sync_interval', '900000'), ('auto_sync_enabled', 'true');` ); }, (error: any) => reject(error), () => resolve() ); }); } }; ``` ## Fase 2: Implementação dos Serviços Base ### 2.1 Serviço de Sincronização Principal **Arquivo**: `src/services/syncService.ts` ```typescript import { api } from './api'; import { executeQuery, saveSetting, getSetting } from './database'; import { offlineStorage } from './offlineStorage'; export enum SyncStatus { SYNCED = 'synced', PENDING = 'pending', CONFLICT = 'conflict', ERROR = 'error', OFFLINE = 'offline' } export enum SyncType { FULL = 'full', INCREMENTAL = 'incremental', SELECTIVE = 'selective' } interface SyncResult { success: boolean; totalRecords: number; syncedRecords: number; errors: Array<{ recordId: string; error: string }>; duration: number; } class SyncService { private isInitialized = false; constructor() { this.initializeSyncService(); } private async initializeSyncService(): Promise { try { // Verificar se já foi feita sincronização inicial const initialSyncComplete = await getSetting('initial_sync_complete'); if (initialSyncComplete === 'false') { console.log('Sincronização inicial necessária'); // Não iniciar automaticamente - aguardar ação do usuário } else { console.log('Sincronização inicial já foi realizada'); this.isInitialized = true; } } catch (error) { console.error('Erro ao inicializar serviço de sincronização:', error); } } // Sincronização inicial completa async performInitialSync(): Promise { try { console.log('=== INICIANDO SINCRONIZAÇÃO INICIAL ==='); const syncResult: SyncResult = { success: true, totalRecords: 0, syncedRecords: 0, errors: [], duration: 0 }; const startTime = Date.now(); // 1. Verificar conectividade const isOnline = await this.checkConnectivity(); if (!isOnline) { throw new Error('Sem conexão com a internet'); } // 2. Sincronizar dados de usuário await this.syncUserData(); syncResult.syncedRecords += 1; // 3. Sincronizar entregas const deliveriesResult = await this.syncDeliveries(); syncResult.syncedRecords += deliveriesResult.count; // 4. Sincronizar configurações await this.syncSettings(); syncResult.syncedRecords += 1; // 5. Marcar sincronização como completa await saveSetting('initial_sync_complete', 'true'); await saveSetting('last_sync_time', Date.now().toString()); syncResult.duration = Date.now() - startTime; syncResult.totalRecords = syncResult.syncedRecords; this.isInitialized = true; console.log('=== SINCRONIZAÇÃO INICIAL CONCLUÍDA ==='); return syncResult; } catch (error) { console.error('Erro na sincronização inicial:', error); throw new Error(`Falha na sincronização inicial: ${error.message}`); } } // Sincronização incremental async performIncrementalSync(): Promise { try { console.log('=== INICIANDO SINCRONIZAÇÃO INCREMENTAL ==='); const lastSyncTime = await getSetting('last_sync_time'); const syncResult: SyncResult = { success: true, totalRecords: 0, syncedRecords: 0, errors: [], duration: 0 }; const startTime = Date.now(); // 1. Verificar conectividade const isOnline = await this.checkConnectivity(); if (!isOnline) { throw new Error('Sem conexão com a internet'); } // 2. Buscar mudanças do servidor const serverChanges = await this.getServerChangesSince(parseInt(lastSyncTime || '0')); // 3. Buscar mudanças locais não sincronizadas const localChanges = await this.getLocalUnsyncedChanges(); // 4. Aplicar mudanças do servidor await this.applyServerChanges(serverChanges); syncResult.syncedRecords += serverChanges.length; // 5. Enviar mudanças locais await this.sendLocalChanges(localChanges); syncResult.syncedRecords += localChanges.length; // 6. Atualizar timestamp de sincronização await saveSetting('last_sync_time', Date.now().toString()); syncResult.duration = Date.now() - startTime; syncResult.totalRecords = syncResult.syncedRecords; console.log('=== SINCRONIZAÇÃO INCREMENTAL CONCLUÍDA ==='); return syncResult; } catch (error) { console.error('Erro na sincronização incremental:', error); throw new Error(`Falha na sincronização incremental: ${error.message}`); } } // Métodos auxiliares private async checkConnectivity(): Promise { try { // Implementar verificação de conectividade const response = await fetch('https://www.google.com', { method: 'HEAD', timeout: 5000 }); return response.ok; } catch { return false; } } private async syncUserData(): Promise { // Implementar sincronização de dados do usuário console.log('Sincronizando dados do usuário...'); } private async syncDeliveries(): Promise<{ count: number }> { try { const deliveries = await api.getDeliveries(); let count = 0; for (const delivery of deliveries) { await this.saveDeliveryLocally(delivery); count += 1; } return { count }; } catch (error) { console.error('Erro ao sincronizar entregas:', error); throw error; } } private async syncSettings(): Promise { // Implementar sincronização de configurações console.log('Sincronizando configurações...'); } private async saveDeliveryLocally(delivery: any): Promise { try { await executeQuery( `INSERT OR REPLACE INTO deliveries ( id, outId, customerName, street, streetNumber, neighborhood, city, state, zipCode, customerPhone, lat, lng, deliverySeq, routing, status, outDate, notes, version, last_modified, sync_timestamp ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [ delivery.id, delivery.outId, delivery.customerName, delivery.street, delivery.streetNumber, delivery.neighborhood, delivery.city, delivery.state, delivery.zipCode, delivery.customerPhone, delivery.lat, delivery.lng, delivery.deliverySeq, delivery.routing, delivery.status, delivery.outDate, delivery.notes, 1, Date.now(), Date.now() ] ); } catch (error) { console.error('Erro ao salvar entrega localmente:', error); throw error; } } private async getServerChangesSince(timestamp: number): Promise { // Implementar busca de mudanças do servidor // Por enquanto, retornar array vazio return []; } private async getLocalUnsyncedChanges(): Promise { try { const result = await executeQuery( "SELECT * FROM deliveries WHERE sync_timestamp IS NULL OR sync_timestamp < last_modified" ); return result.rows._array; } catch (error) { console.error('Erro ao buscar mudanças locais:', error); return []; } } private async applyServerChanges(changes: any[]): Promise { for (const change of changes) { await this.saveDeliveryLocally(change); } } private async sendLocalChanges(changes: any[]): Promise { for (const change of changes) { try { await api.createDelivery(change); // Marcar como sincronizado await executeQuery( "UPDATE deliveries SET sync_timestamp = ? WHERE id = ?", [Date.now(), change.id] ); } catch (error) { console.error('Erro ao enviar mudança local:', error); } } } // Verificar se sincronização inicial foi realizada async isInitialSyncComplete(): Promise { try { const result = await getSetting('initial_sync_complete'); return result === 'true'; } catch (error) { console.error('Erro ao verificar sincronização inicial:', error); return false; } } // Obter estatísticas de sincronização async getSyncStats(): Promise { try { const totalResult = await executeQuery("SELECT COUNT(*) as count FROM deliveries"); const syncedResult = await executeQuery( "SELECT COUNT(*) as count FROM deliveries WHERE sync_timestamp IS NOT NULL" ); const pendingResult = await executeQuery( "SELECT COUNT(*) as count FROM deliveries WHERE sync_timestamp IS NULL" ); return { totalRecords: totalResult.rows._array[0].count, syncedRecords: syncedResult.rows._array[0].count, pendingRecords: pendingResult.rows._array[0].count, lastSyncTime: await getSetting('last_sync_time') }; } catch (error) { console.error('Erro ao obter estatísticas de sincronização:', error); return null; } } } export const syncService = new SyncService(); ``` ### 2.2 Contexto de Sincronização Inicial **Arquivo**: `src/contexts/InitialSyncContext.tsx` ```typescript import React, { createContext, useContext, useState, useEffect } from 'react'; import { syncService, SyncResult } from '../services/syncService'; interface InitialSyncContextData { // Estados isInitialSyncComplete: boolean; syncProgress: number; syncStatus: string; lastSyncTime: number | null; pendingChanges: number; isLoading: boolean; error: string | null; // Métodos startInitialSync: () => Promise; retryInitialSync: () => Promise; performIncrementalSync: () => Promise; getSyncStats: () => Promise; } const InitialSyncContext = createContext({} as InitialSyncContextData); export const useInitialSync = () => { const context = useContext(InitialSyncContext); if (!context) { throw new Error('useInitialSync deve ser usado dentro de um InitialSyncProvider'); } return context; }; export const InitialSyncProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { const [isInitialSyncComplete, setIsInitialSyncComplete] = useState(false); const [syncProgress, setSyncProgress] = useState(0); const [syncStatus, setSyncStatus] = useState('idle'); const [lastSyncTime, setLastSyncTime] = useState(null); const [pendingChanges, setPendingChanges] = useState(0); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); // Verificar status inicial useEffect(() => { checkInitialSyncStatus(); }, []); const checkInitialSyncStatus = async () => { try { const isComplete = await syncService.isInitialSyncComplete(); setIsInitialSyncComplete(isComplete); if (isComplete) { const stats = await syncService.getSyncStats(); if (stats) { setLastSyncTime(parseInt(stats.lastSyncTime || '0')); setPendingChanges(stats.pendingRecords); } } } catch (error) { console.error('Erro ao verificar status de sincronização:', error); } }; const startInitialSync = async () => { setIsLoading(true); setError(null); setSyncStatus('starting'); setSyncProgress(0); try { // Simular progresso const progressInterval = setInterval(() => { setSyncProgress(prev => { if (prev >= 90) { clearInterval(progressInterval); return prev; } return prev + 10; }); }, 200); const result = await syncService.performInitialSync(); clearInterval(progressInterval); setSyncProgress(100); setSyncStatus('completed'); setIsInitialSyncComplete(true); setLastSyncTime(Date.now()); setPendingChanges(0); console.log('Sincronização inicial concluída:', result); } catch (error) { console.error('Erro na sincronização inicial:', error); setError(error.message); setSyncStatus('error'); } finally { setIsLoading(false); } }; const retryInitialSync = async () => { setError(null); await startInitialSync(); }; const performIncrementalSync = async () => { setIsLoading(true); setError(null); setSyncStatus('syncing'); try { const result = await syncService.performIncrementalSync(); setSyncStatus('completed'); setLastSyncTime(Date.now()); setPendingChanges(0); console.log('Sincronização incremental concluída:', result); } catch (error) { console.error('Erro na sincronização incremental:', error); setError(error.message); setSyncStatus('error'); } finally { setIsLoading(false); } }; const getSyncStats = async () => { try { return await syncService.getSyncStats(); } catch (error) { console.error('Erro ao obter estatísticas:', error); return null; } }; return ( {children} ); }; ``` ## Fase 3: Implementação das Telas ### 3.1 Tela de Sincronização Inicial **Arquivo**: `src/screens/sync/InitialSyncScreen.tsx` ```typescript import React, { useState } from 'react'; import { View, Text, StyleSheet, TouchableOpacity, Alert, ActivityIndicator, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { LinearGradient } from 'expo-linear-gradient'; import { Ionicons } from '@expo/vector-icons'; import { useInitialSync } from '../../contexts/InitialSyncContext'; import { COLORS, SHADOWS } from '../../constants/theme'; const InitialSyncScreen: React.FC = () => { const { syncProgress, syncStatus, isLoading, error, startInitialSync, retryInitialSync, } = useInitialSync(); const [hasStarted, setHasStarted] = useState(false); const handleStartSync = async () => { setHasStarted(true); try { await startInitialSync(); // Navegação será feita pelo contexto pai } catch (error) { Alert.alert('Erro', 'Falha na sincronização inicial'); } }; const handleRetry = async () => { try { await retryInitialSync(); } catch (error) { Alert.alert('Erro', 'Falha ao tentar novamente'); } }; const getStatusText = (status: string) => { switch (status) { case 'starting': return 'Iniciando sincronização...'; case 'syncing': return 'Sincronizando dados...'; case 'completed': return 'Sincronização concluída!'; case 'error': return 'Erro na sincronização'; default: return 'Pronto para sincronizar'; } }; const getStatusColor = (status: string) => { switch (status) { case 'completed': return COLORS.success; case 'error': return COLORS.danger; default: return COLORS.primary; } }; return ( Sincronização Inicial Baixando dados necessários para funcionamento offline {Math.round(syncProgress)}% concluído {getStatusText(syncStatus)} {error && ( {error} )} {!hasStarted || syncStatus === 'error' ? ( {isLoading ? ( ) : ( )} {syncStatus === 'error' ? 'Tentar Novamente' : 'Iniciar Sincronização'} ) : ( Sincronização concluída com sucesso! )} • Dados de entregas serão baixados • Configurações serão sincronizadas • Aplicativo funcionará offline após sincronização ); }; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: COLORS.background, }, header: { paddingVertical: 40, paddingHorizontal: 20, alignItems: 'center', }, headerContent: { alignItems: 'center', }, title: { fontSize: 24, fontWeight: 'bold', color: 'white', marginTop: 16, marginBottom: 8, }, subtitle: { fontSize: 16, color: 'rgba(255, 255, 255, 0.8)', textAlign: 'center', }, content: { flex: 1, padding: 20, }, progressContainer: { marginBottom: 30, }, progressBar: { height: 8, backgroundColor: COLORS.border, borderRadius: 4, overflow: 'hidden', marginBottom: 8, }, progressFill: { height: '100%', backgroundColor: COLORS.primary, borderRadius: 4, }, progressText: { fontSize: 16, fontWeight: '600', color: COLORS.text, textAlign: 'center', }, statusContainer: { alignItems: 'center', marginBottom: 20, }, statusText: { fontSize: 18, fontWeight: '600', }, errorContainer: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#FEF2F2', padding: 16, borderRadius: 12, marginBottom: 20, }, errorText: { fontSize: 14, color: COLORS.danger, marginLeft: 8, flex: 1, }, buttonContainer: { marginBottom: 30, }, syncButton: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', backgroundColor: COLORS.primary, paddingVertical: 16, paddingHorizontal: 24, borderRadius: 12, ...SHADOWS.medium, }, syncButtonDisabled: { backgroundColor: COLORS.textLight, }, syncButtonText: { fontSize: 16, fontWeight: '600', color: 'white', marginLeft: 8, }, completedContainer: { alignItems: 'center', padding: 20, }, completedText: { fontSize: 18, fontWeight: '600', color: COLORS.success, marginTop: 16, textAlign: 'center', }, infoContainer: { backgroundColor: COLORS.secondary, padding: 20, borderRadius: 12, }, infoText: { fontSize: 14, color: COLORS.textLight, marginBottom: 8, }, }); export default InitialSyncScreen; ``` ### 3.2 Atualização da Navegação **Arquivo**: `src/navigation/index.tsx` ```typescript // Adicionar import import InitialSyncScreen from '../screens/sync/InitialSyncScreen'; // Adicionar ao AuthNavigator const AuthNavigator = () => { return ( ); }; // Atualizar Navigation component const Navigation = () => { const { user, isLoading } = useAuth(); const { isInitialSyncComplete } = useInitialSync(); useEffect(() => { console.log('=== DEBUG: NAVIGATION STATE ==='); console.log('isLoading:', isLoading); console.log('user:', user ? 'Logado' : 'Não logado'); console.log('isInitialSyncComplete:', isInitialSyncComplete); if (!isLoading && !user) { console.log('Redirecionando para Auth...'); navigationRef.current?.reset({ index: 0, routes: [{ name: 'Auth' }] }); } else if (!isLoading && user && !isInitialSyncComplete) { console.log('Redirecionando para InitialSync...'); navigationRef.current?.reset({ index: 0, routes: [{ name: 'InitialSync' }] }); } }, [user, isLoading, isInitialSyncComplete]); if (isLoading) { return ( Carregando... ); } return ( {user ? ( <> {!isInitialSyncComplete ? ( ) : ( <> )} ) : ( )} ); }; ``` ### 3.3 Atualização do App.tsx **Arquivo**: `App.tsx` ```typescript // Adicionar import import { InitialSyncProvider } from "./src/contexts/InitialSyncContext"; // Atualizar estrutura de providers return ( {Platform.OS === 'android' ? ( ) : ( )} ); ``` ## Fase 4: Implementação de Sincronização Incremental ### 4.1 Hook para Sincronização Automática **Arquivo**: `src/hooks/useAutoSync.ts` ```typescript import { useEffect, useRef } from 'react'; import { AppState, AppStateStatus } from 'react-native'; import { useInitialSync } from '../contexts/InitialSyncContext'; import { useSync } from '../contexts/SyncContext'; export const useAutoSync = () => { const { isInitialSyncComplete, performIncrementalSync } = useInitialSync(); const { isOnline } = useSync(); const appState = useRef(AppState.currentState); const syncTimeoutRef = useRef(); useEffect(() => { const handleAppStateChange = (nextAppState: AppStateStatus) => { if ( appState.current.match(/inactive|background/) && nextAppState === 'active' && isInitialSyncComplete && isOnline ) { // App voltou ao foreground e está online console.log('App ativo - verificando sincronização'); scheduleSync(); } appState.current = nextAppState; }; const subscription = AppState.addEventListener('change', handleAppStateChange); return () => { subscription?.remove(); if (syncTimeoutRef.current) { clearTimeout(syncTimeoutRef.current); } }; }, [isInitialSyncComplete, isOnline]); const scheduleSync = () => { // Aguardar 5 segundos antes de sincronizar syncTimeoutRef.current = setTimeout(async () => { try { console.log('Executando sincronização automática'); await performIncrementalSync(); } catch (error) { console.error('Erro na sincronização automática:', error); } }, 5000); }; return { scheduleSync, }; }; ``` ### 4.2 Atualização do HomeScreen **Arquivo**: `src/screens/main/HomeScreen.tsx` ```typescript // Adicionar imports import { useInitialSync } from '../../contexts/InitialSyncContext'; import { useAutoSync } from '../../hooks/useAutoSync'; // Adicionar ao componente const HomeScreen = () => { // ... código existente ... const { isInitialSyncComplete, pendingChanges, performIncrementalSync, getSyncStats } = useInitialSync(); const { scheduleSync } = useAutoSync(); // Usar auto sync useEffect(() => { if (isInitialSyncComplete) { scheduleSync(); } }, [isInitialSyncComplete, scheduleSync]); // ... resto do código ... }; ``` ## Fase 5: Testes e Validação ### 5.1 Script de Teste **Arquivo**: `scripts/test-sync.js` ```javascript const { execSync } = require('child_process'); console.log('=== TESTANDO SINCRONIZAÇÃO OFFLINE ==='); // Teste 1: Verificar se banco de dados foi criado console.log('1. Verificando estrutura do banco...'); try { execSync('npx react-native run-android --variant=debug', { stdio: 'inherit' }); console.log('✅ Banco de dados criado com sucesso'); } catch (error) { console.error('❌ Erro ao criar banco de dados:', error.message); } // Teste 2: Verificar sincronização inicial console.log('2. Testando sincronização inicial...'); // Implementar testes específicos // Teste 3: Verificar modo offline console.log('3. Testando modo offline...'); // Implementar testes específicos console.log('=== TESTES CONCLUÍDOS ==='); ``` ### 5.2 Checklist de Implementação ```markdown ## Checklist de Implementação ### Fase 1: Preparação - [ ] Instalar dependências necessárias - [ ] Configurar variáveis de ambiente - [ ] Atualizar estrutura do banco de dados - [ ] Criar tabelas de sincronização ### Fase 2: Serviços Base - [ ] Implementar SyncService - [ ] Criar InitialSyncContext - [ ] Implementar métodos de sincronização - [ ] Adicionar tratamento de erros ### Fase 3: Interface - [ ] Criar InitialSyncScreen - [ ] Atualizar navegação - [ ] Integrar com App.tsx - [ ] Testar fluxo de navegação ### Fase 4: Sincronização Automática - [ ] Implementar useAutoSync hook - [ ] Adicionar sincronização incremental - [ ] Atualizar HomeScreen - [ ] Testar sincronização automática ### Fase 5: Testes - [ ] Testar sincronização inicial - [ ] Testar modo offline - [ ] Testar sincronização incremental - [ ] Validar performance - [ ] Testar tratamento de erros ### Fase 6: Otimizações - [ ] Implementar compressão de dados - [ ] Adicionar cache inteligente - [ ] Otimizar queries do banco - [ ] Implementar logs de debug ``` ## Fase 6: Deploy e Monitoramento ### 6.1 Configuração de Build **Arquivo**: `eas.json` ```json { "cli": { "version": ">= 5.9.0" }, "build": { "development": { "developmentClient": true, "distribution": "internal" }, "preview": { "distribution": "internal", "env": { "API_BASE_URL": "https://api-staging.example.com" } }, "production": { "env": { "API_BASE_URL": "https://api.example.com" } } }, "submit": { "production": {} } } ``` ### 6.2 Monitoramento de Sincronização **Arquivo**: `src/services/syncMonitor.ts` ```typescript class SyncMonitor { private metrics: Map = new Map(); recordSyncAttempt(type: string, success: boolean, duration: number) { const key = `${type}_${new Date().toISOString().split('T')[0]}`; const current = this.metrics.get(key) || { attempts: 0, successes: 0, failures: 0, totalDuration: 0, }; current.attempts += 1; current.totalDuration += duration; if (success) { current.successes += 1; } else { current.failures += 1; } this.metrics.set(key, current); } getMetrics(): any { return Object.fromEntries(this.metrics); } exportMetrics(): string { return JSON.stringify(this.getMetrics(), null, 2); } } export const syncMonitor = new SyncMonitor(); ``` ## Conclusão Este guia fornece uma implementação completa e passo a passo da sincronização offline no aplicativo de entregas. A implementação é modular e pode ser feita incrementalmente, permitindo testes e validação em cada fase. **Próximos Passos**: 1. Implementar Fase 1 (Preparação) 2. Testar estrutura do banco de dados 3. Implementar Fase 2 (Serviços Base) 4. Testar sincronização inicial 5. Continuar com as demais fases **Considerações Importantes**: - Sempre testar em ambiente de desenvolvimento primeiro - Implementar logs detalhados para debugging - Considerar performance e uso de bateria - Implementar fallbacks para cenários de erro - Monitorar uso de dados e armazenamento