Skip to content

offline-first: salva cadastros localmente e sincroniza quando a rede volta - #3

Merged
wellington1993 merged 6 commits into
mainfrom
feat/offline-resilience
Mar 13, 2026
Merged

offline-first: salva cadastros localmente e sincroniza quando a rede volta#3
wellington1993 merged 6 commits into
mainfrom
feat/offline-resilience

Conversation

@wellington1993

Copy link
Copy Markdown
Owner

O que muda

A tela de cadastro agora funciona sem internet. O colaborador e salvo localmente e enviado pro Firestore automaticamente quando a conexao volta, tudo em background, sem o usuario precisar fazer nada.

Por que fiz assim

Queria evitar aquela situacao chata de perder o formulario porque a internet caiu na hora errada. A solucao foi guardar no localStorage e sincronizar quando der.

Optei por nao mostrar indicador de pendente ou sincronizando pra nao deixar a interface confusa. O usuario ve o cadastro na lista normalmente.

O que foi feito

  • deteccao de rede em tempo real via navigator.onLine e eventos online/offline
  • salvamento no localStorage quando offline, com o e-mail como chave unica pra evitar duplicatas
  • sincronizacao automatica em background ao reconectar
  • remocao dos chips e alertas de offline que poluiam a tela
  • configuracao de PWA com manifest, icones e favicon
  • refresh funcionando no GitHub Pages
  • cobertura de testes para os fluxos offline

Testes

16/16 E2E passando, 76/76 unitarios.

Copilot AI review requested due to automatic review settings March 11, 2026 11:24
@vercel

vercel Bot commented Mar 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
flugo-employees Ready Ready Preview, Comment Mar 11, 2026 11:24am

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implementa o fluxo offline-first para cadastro/listagem de colaboradores, com persistência local e sincronização automática quando a rede volta, além de ajustes de PWA/GitHub Pages e atualização da suíte de testes para cobrir os novos fluxos.

Changes:

  • Persistência offline: grava cadastros pendentes no localStorage, sincroniza em background ao reconectar e ajusta merge entre Firestore + pendentes.
  • Infra offline/PWA: registro de Service Worker em produção, manifest/ícones e suporte a refresh em SPA no GitHub Pages.
  • Testes: novas specs/ajustes E2E + unitários para conectividade e limpeza de registros de teste.

Reviewed changes

Copilot reviewed 27 out of 32 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
vite.config.ts Ajusta configuração do VitePWA (assets, devOptions, manifest/icons).
index.html Adiciona script de fallback SPA para GitHub Pages e ícone alternativo.
playwright.config.ts Ajusta estratégia do webServer para E2E.
.gitignore Ignora artefatos de execução do Playwright.
public/favicon.ico Adiciona favicon.
public/apple-touch-icon.png Adiciona ícone para iOS.
public/maskable-icon.png Adiciona ícone maskable para PWA.
src/main.tsx Registra SW em prod e altera gate de Analytics/SpeedInsights.
src/vite-env.d.ts Adiciona tipos para virtual:pwa-register.
src/libs/firebase.ts Silencia warning em prod quando config Firebase está ausente/ inválida.
src/libs/tanstack-query.ts Ajusta cache/networkMode para suporte offline-first no React Query.
src/services/local-storage.ts Dedupe por e-mail e adiciona createdAt ao pendente.
src/services/storage-provider.ts Reestrutura provider para retornar staff no create, suportar offline/navigator, e sync de pendentes.
src/services/storage-provider.test.ts Atualiza testes unitários para nova lógica (inclui getDoc).
src/services/staffs.ts Ajusta assinatura/retorno de createStaff.
src/services/staffs.test.ts Atualiza mocks/asserts para o novo fluxo de create/list.
src/features/staff/hooks.ts Ajusta create mutation e implementa sync automático via conectividade.
src/features/staff/hooks.test.tsx Expande cobertura de useSyncPending (contagem e lock contra concorrência).
src/features/staff/use-staff-form.ts Ajusta UX: toast unificado de sucesso e feedback de erro na validação por step.
src/hooks/use-connectivity.ts Novo hook de conectividade baseado em useSyncExternalStore.
src/hooks/use-connectivity.test.ts Testa transições online/offline via eventos.
src/hooks/use-sort-table.ts Permite ordem padrão configurável (ex.: desc por createdAt).
src/helpers/table-sorting.ts Ajusta comparator para lidar com createdAt numérico.
src/components/staff-list.tsx Remove chips/indicadores offline, ordena por createdAt desc e dispara sync em background.
src/components/staff-form.tsx Ajuste visual mínimo (espaçamento).
src/App.tsx Ativa sync pendente em segundo plano no nível do App.
tests/e2e/utils/cleanup.ts Novo utilitário para remover registros de teste no Firestore.
tests/e2e/staff.spec.ts Ajusta seletores/fluxos e adiciona cleanup após testes.
tests/e2e/smoke.spec.ts Ajusta smoke tests, adiciona cleanup e logging de erros do browser/rede.
tests/e2e/offline-sync.spec.ts Atualiza teste offline/sync usando context.setOffline e eventos online/offline.
tests/e2e/full-flow.spec.ts Separa fluxo online/offline e adiciona cleanup.
src/test/cleanup.test.ts Adiciona testes unitários para cleanupTestRecords.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/main.tsx
Comment on lines +25 to +26
// Verifica se o ambiente é Vercel Produção para evitar erros de Analytics no localhost ou preview
const isVercelProduction = import.meta.env.PROD && !!import.meta.env.VITE_VERCEL_ENV

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isVercelProduction depende de import.meta.env.VITE_VERCEL_ENV, mas não existe nenhuma outra referência/definição dessa env no repo (logo, Analytics/SpeedInsights nunca vão renderizar). Ajuste a condição para usar uma env realmente exposta ao client (prefixo VITE_) ou volte a usar apenas import.meta.env.PROD/MODE conforme a intenção.

Suggested change
// Verifica se o ambiente é Vercel Produção para evitar erros de Analytics no localhost ou preview
const isVercelProduction = import.meta.env.PROD && !!import.meta.env.VITE_VERCEL_ENV
// Verifica se o ambiente é produção para evitar erros de Analytics no localhost ou preview
const isVercelProduction = import.meta.env.PROD

Copilot uses AI. Check for mistakes.
Comment on lines +45 to +48
const { order, orderBy, createSortHandler } = useSortTable('createdAt', setPage, 'desc')

// Sincronização automática em background
useSyncPending()

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useSyncPending() está sendo chamado aqui e também em src/App.tsx (o App envolve as rotas), o que cria múltiplas instâncias do sync rodando em paralelo quando a lista está montada. Isso pode gerar escrita duplicada/custo extra e corrida no removePendingByEmail. Centralize o sync em um único ponto (ex.: só no App) ou garanta singleton (ex.: provider/flag global).

Copilot uses AI. Check for mistakes.
Comment on lines +13 to +23
const firebaseConfig = {
apiKey: process.env.VITE_FIREBASE_API_KEY,
authDomain: process.env.VITE_FIREBASE_AUTH_DOMAIN,
projectId: process.env.VITE_FIREBASE_PROJECT_ID,
storageBucket: process.env.VITE_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.VITE_FIREBASE_APP_ID,
};

const app = initializeApp(firebaseConfig);
const db = getFirestore(app);

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Esse utilitário inicializa o Firebase com envs e executa deletes em uma coleção real. Do jeito que está, basta a pipeline apontar para um projeto não-isolado para apagar dados reais que coincidam com os padrões. Adicione um guard-rail obrigatório (ex.: exigir E2E_CLEANUP_ENABLED=true e validar projectId contra uma allowlist/regex de projeto de teste) e faça fallback para no-op quando a configuração estiver incompleta.

Copilot uses AI. Check for mistakes.
Comment thread tests/e2e/staff.spec.ts
import { test, expect } from '@playwright/test'
import { cleanupTestRecords } from './utils/cleanup'

test.afterEach(async () => {

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cleanupTestRecords() faz um getDocs da coleção inteira e roda após cada teste. Isso tende a deixar a suíte E2E bem mais lenta e aumentar consumo/custo no Firestore. Preferível mover a limpeza para afterAll (por spec ou global), ou rastrear/deletar apenas os IDs criados pelo teste atual.

Suggested change
test.afterEach(async () => {
test.afterAll(async () => {

Copilot uses AI. Check for mistakes.
Comment thread tests/e2e/smoke.spec.ts
import { test, expect } from '@playwright/test'
import { cleanupTestRecords } from './utils/cleanup'

test.afterEach(async () => {

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cleanupTestRecords() faz um getDocs da coleção inteira e roda após cada teste. Isso tende a deixar a suíte E2E bem mais lenta e aumentar consumo/custo no Firestore. Preferível mover a limpeza para afterAll (por spec ou global), ou rastrear/deletar apenas os IDs criados pelo teste atual.

Suggested change
test.afterEach(async () => {
test.afterAll(async () => {

Copilot uses AI. Check for mistakes.
import { test, expect } from '@playwright/test'
import { cleanupTestRecords } from './utils/cleanup'

test.afterEach(async () => {

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cleanupTestRecords() faz um getDocs da coleção inteira e roda após cada teste. Isso tende a deixar a suíte E2E bem mais lenta e aumentar consumo/custo no Firestore. Preferível mover a limpeza para afterAll (por spec ou global), ou rastrear/deletar apenas os IDs criados pelo teste atual.

Suggested change
test.afterEach(async () => {
test.afterAll(async () => {

Copilot uses AI. Check for mistakes.
import { cleanupTestRecords } from './utils/cleanup'

test.describe('Fluxo de Cadastro', () => {
test.afterEach(async () => {

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cleanupTestRecords() faz um getDocs da coleção inteira e roda após cada teste. Isso tende a deixar a suíte E2E bem mais lenta e aumentar consumo/custo no Firestore. Preferível mover a limpeza para afterAll (por spec ou global), ou rastrear/deletar apenas os IDs criados pelo teste atual.

Suggested change
test.afterEach(async () => {
test.afterAll(async () => {

Copilot uses AI. Check for mistakes.
Comment on lines +30 to 33
// Sincroniza o localStorage se algum item pendente já foi detectado no Firebase
if (stillPending.length !== pending.length) {
localStorage.setItem('flugo_pending_staffs', JSON.stringify(stillPending))
}

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aqui o código grava direto em localStorage usando a string literal 'flugo_pending_staffs', enquanto o módulo local-storage.ts já tem a constante LS_KEY. Para evitar divergência/regressão futura, prefira reutilizar a mesma constante (exportando-a) ou encapsular essa atualização em uma função no serviço de local-storage.

Copilot uses AI. Check for mistakes.
@wellington1993
wellington1993 merged commit 1b864cf into main Mar 13, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants