From 909fcbb38c65a3680cacd2a0fb951098cafaf33b Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 30 Jun 2026 11:15:24 +0000 Subject: [PATCH 1/2] feat(app): botao 'Entrar com Google' na tela de login - Adiciona divisor 'ou' + OutlinedButton 'Entrar com Google' na etapa de e-mail - Handler usa authStateProvider.loginWithGoogle (Google Sign-In + Firebase + auth/social) - Cancelamento do seletor nao navega; erro mostra snackbar - Teste de widget: botao aparece na etapa de e-mail - Observacao: E2E requer GOOGLE_SIGN_IN_CLIENT_ID + config Firebase Co-authored-by: Rapha --- .../presentation/screens/login_screen.dart | 42 ++++++++++++++++++- .../auth/presentation/login_screen_test.dart | 22 ++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/frontend/arah.app/lib/features/auth/presentation/screens/login_screen.dart b/frontend/arah.app/lib/features/auth/presentation/screens/login_screen.dart index 672c2891..0b95c0a1 100644 --- a/frontend/arah.app/lib/features/auth/presentation/screens/login_screen.dart +++ b/frontend/arah.app/lib/features/auth/presentation/screens/login_screen.dart @@ -112,6 +112,21 @@ class _LoginScreenState extends ConsumerState { } } + Future _loginWithGoogle() async { + await ref.read(authStateProvider.notifier).loginWithGoogle(); + if (!mounted) return; + final auth = ref.read(authStateProvider); + if (auth.hasError) { + final msg = auth.error is ApiException + ? (auth.error! as ApiException).userMessage + : auth.error.toString(); + showErrorSnackBar(context, msg); + } else if (auth.valueOrNull?.accessToken.isNotEmpty ?? false) { + context.go('/onboarding'); + } + // valueOrNull == null → usuário cancelou o seletor do Google: nenhuma ação. + } + Future _submitSignUp() async { if (!(_formKey.currentState?.validate() ?? false)) return; final displayName = _displayNameController.text.trim(); @@ -167,7 +182,7 @@ class _LoginScreenState extends ConsumerState { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - if (_step == LoginStep.email) ..._buildEmailStep(l10n), + if (_step == LoginStep.email) ..._buildEmailStep(l10n, auth.isLoading), if (_step == LoginStep.password) ..._buildPasswordStep(l10n, auth.isLoading), if (_step == LoginStep.signup) ..._buildSignUpStep(l10n, auth.isLoading), ], @@ -182,7 +197,8 @@ class _LoginScreenState extends ConsumerState { ); } - List _buildEmailStep(AppLocalizations l10n) { + List _buildEmailStep(AppLocalizations l10n, bool authLoading) { + final busy = _checkEmailLoading || authLoading; return [ TextFormField( controller: _emailController, @@ -205,6 +221,28 @@ class _LoginScreenState extends ConsumerState { loading: _checkEmailLoading, expand: true, ), + const SizedBox(height: AppConstants.spacingMd), + Row( + children: [ + const Expanded(child: Divider()), + Padding( + padding: const EdgeInsets.symmetric(horizontal: AppConstants.spacingSm), + child: Text( + l10n.loginOr, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + ), + const Expanded(child: Divider()), + ], + ), + const SizedBox(height: AppConstants.spacingMd), + OutlinedButton.icon( + onPressed: busy ? null : _loginWithGoogle, + icon: const Icon(Icons.g_mobiledata, size: 28), + label: Text(l10n.loginWithGoogle), + ), ]; } diff --git a/frontend/arah.app/test/features/auth/presentation/login_screen_test.dart b/frontend/arah.app/test/features/auth/presentation/login_screen_test.dart index e8cab775..8bc849fd 100644 --- a/frontend/arah.app/test/features/auth/presentation/login_screen_test.dart +++ b/frontend/arah.app/test/features/auth/presentation/login_screen_test.dart @@ -70,6 +70,28 @@ void main() { expect(find.byType(TextFormField), findsWidgets); }); + testWidgets('LoginScreen shows "Entrar com Google" on email step', (WidgetTester tester) async { + await tester.pumpWidget( + ProviderScope( + overrides: [ + authStateProvider.overrideWith(() => _FakeAuthStateNotifier()), + ], + child: MaterialApp( + theme: AppTheme.dark, + locale: const Locale('pt'), + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + home: const LoginScreen(bffBaseUrl: 'http://test'), + ), + ), + ); + await tester.pumpAndSettle(); + + expect(find.text('Entrar com Google'), findsOneWidget); + // O botão fica na etapa de e-mail, ao lado do fluxo e-mail-first ("ou"). + expect(find.text('ou'), findsOneWidget); + }); + testWidgets('LoginScreen shows signup form when email does not exist', (WidgetTester tester) async { final fakeRepo = FakeAuthRepository( config: const AppConfig(bffBaseUrl: 'http://test'), From 33a5045005feaeabdea8a8398110a5d8b1f9bdd5 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 30 Jun 2026 11:15:24 +0000 Subject: [PATCH 2/2] docs: sincronizar README, changelog, matriz e status do app com entregas recentes - README: secao 'App (Flutter) - Entregas Recentes' (login Google, eventos, governanca, deep-links) + proximos passos - FEATURE_MATRIX: linha Governance; eventos (create-event) e mapa (deep-links) atualizados - STABLE_RELEASE_APP_ONBOARDING: implementado + tabela de proximos passos atualizada - CHANGELOG: entradas das entregas recentes do app - AGENTS.md: notas (l10n gen, flutter_map onTap, registrar jornadas BFF) + manter docs em sincronia Co-authored-by: Rapha --- AGENTS.md | 16 ++++++++++++++++ README.md | 17 +++++++++++++++-- docs/CHANGELOG.md | 9 +++++++++ docs/FEATURE_MATRIX_API_BFF_APP.md | 7 ++++--- docs/STABLE_RELEASE_APP_ONBOARDING.md | 24 +++++++++++++----------- 5 files changed, 57 insertions(+), 16 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 3dd96aaa..01e207b2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -46,3 +46,19 @@ startup update script refreshes dependencies. Standard build/test/run commands l ### Flutter app — `frontend/arah.app` - `flutter pub get`, then `flutter analyze --no-fatal-infos` (CI tolerates info-level lints) and `flutter test`. It talks only to the **BFF**, not the API directly (`--dart-define=BFF_BASE_URL=...`). +- l10n: arb files in `lib/l10n/app_pt.arb` (template) + `app_en.arb`. `flutter gen-l10n` writes to the + synthetic package (`.dart_tool/flutter_gen/gen_l10n/`), but the app imports the **committed** + `lib/l10n/app_localizations*.dart`. After editing arb files, run `flutter gen-l10n` then copy the 3 + generated files from `.dart_tool/flutter_gen/gen_l10n/` over `lib/l10n/`. +- `flutter_map` 8.x markers: a `GestureDetector` inside a `Marker` child does **not** fire reliably on + web. Handle taps via `MapOptions.onTap` + nearest-pin matching (see `map_screen.dart`). +- New app journeys must be registered in the BFF `BffJourneyRegistry` (constant + `JourneyToApiPathBase` + + `AllEndpoints` + `CacheableGetEndpoints` + `AllPathPrefixes`), or BFF tests/`/bff/journeys` break. + +### Keep docs in sync with every delivery (important) +When shipping a feature (app/BFF/API), update the relevant docs **in the same PR**: +- `README.md` (phase/status + "App (Flutter) — Entregas Recentes"), `docs/CHANGELOG.md`, + `docs/STABLE_RELEASE_APP_ONBOARDING.md` (app implemented + próximos passos), + `docs/FEATURE_MATRIX_API_BFF_APP.md` (API/BFF/App columns), and the phase docs under + `docs/backlog-api/` + `docs/STATUS_FASES.md` when a backlog phase status changes. +Treat "documentação desatualizada" as a bug (see `.cursorrules`). diff --git a/README.md b/README.md index 1098eb56..5383cf8f 100644 --- a/README.md +++ b/README.md @@ -334,6 +334,19 @@ O Arah está em **desenvolvimento ativo** com **16 fases completas** (Fases 1-16 --- +### 📱 App (Flutter) — Entregas Recentes + +O app consome o backend via **BFF** (jornadas `/api/v2/journeys/*`). Entregas recentes que expõem capacidades já existentes no backend: + +- ✅ **Login com Google na UI** — botão "Entrar com Google" no fluxo de login (requer `GOOGLE_SIGN_IN_CLIENT_ID` + config Firebase para E2E). +- ✅ **Criação de eventos** — formulário com data/hora e local (jornada `events/create-event`). +- ✅ **Governança/Votações** — listar, votar, ver resultados e criar votações (jornada `governance`, expõe a Fase 14). +- ✅ **Deep-links no mapa** — tocar num pin abre detalhes e navega para evento/asset/alerta/feed. + +Detalhes e o que falta no app: [Release estável – App e Onboarding](./docs/STABLE_RELEASE_APP_ONBOARDING.md) · [Matriz API/BFF/App](./docs/FEATURE_MATRIX_API_BFF_APP.md). + +--- + ### 🔒 Segurança e Confiabilidade (Cross-Phase) - ✅ JWT com secret de 32+ caracteres via variáveis de ambiente @@ -471,9 +484,9 @@ Ver documentação completa: [`docs/ENTERPRISE_COVERAGE_PHASES_7_8_9_STATUS.md`] 1. **Fase 17**: Compra Coletiva (P0 Crítico) - Organização de compras coletivas, agrupamento de pedidos 2. **Fase 18**: Hospedagem Territorial (P0 Crítico) - Sistema de hospedagem, agenda, aprovação 3. **Fase 19**: Demandas e Ofertas (P0 Crítico) - Moradores cadastram demandas, outros fazem ofertas -4. **Frontend**: Começar desenvolvimento da interface (Vue/React) +4. **App (Flutter)**: provisionar config do Google Sign-In (OAuth/Firebase); push/FCM; tela de detalhe de post; upload de avatar 5. **Testes**: Validar cobertura de 90%+ (2171+ testes passando) -6. **Documentação**: Manter wiki sincronizado com código +6. **Documentação**: Manter wiki, README e docs de fases sincronizados com cada entrega (app/BFF/API) 7. **Admin Dashboard**: Ferramentas de observabilidade para moderadores 8. **Escalabilidade**: Preparar para múltiplos territórios/usuários em produção diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index c628338a..181a6b00 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -12,6 +12,15 @@ e este projeto adere ao [Semantic Versioning](https://semver.org/lang/pt-BR/). - Reorganização federal da documentação (estrutura por domínios, unificação de duplicados, archive de PRs). - Changelog unificado (conteúdo de 40_CHANGELOG incorporado). +### Adicionado - App (Flutter): entregas recentes (2026-06) + +- ✅ **Documentação de Arquitetura C4** publicada no DevPortal (`/architecture/`) e referenciada no Wiki (`docs/14_C4_ARCHITECTURE.md`). +- ✅ **Governança/Votações no app**: nova jornada `governance` no BFF (proxy para `api/v1/territories/{id}/votings`) e tela de votações (listar com filtro de status, votar inline, ver resultados, criar votação). +- ✅ **Criação de eventos no app**: formulário com início/término (date/time pickers) e local, via jornada `events/create-event`. +- ✅ **Deep-links no mapa**: toque no pin abre detalhes e navega para evento/asset/alerta/feed (tratamento de toque no nível do mapa). +- ✅ **Login com Google na UI**: botão "Entrar com Google" no fluxo de login (requer `GOOGLE_SIGN_IN_CLIENT_ID` + config Firebase para E2E). +- ✅ Documentação sincronizada: `README.md`, `docs/STABLE_RELEASE_APP_ONBOARDING.md`, `docs/FEATURE_MATRIX_API_BFF_APP.md`. + ### Alterado - Fase 12 Encerrada (2026-01-25) - ✅ **Fase 12 declarada 100% encerrada.** Todas as funcionalidades críticas entregues; melhorias contínuas (cobertura >90%, P95 < 200ms) fora do escopo de fechamento. diff --git a/docs/FEATURE_MATRIX_API_BFF_APP.md b/docs/FEATURE_MATRIX_API_BFF_APP.md index 99ccd548..d7df8f49 100644 --- a/docs/FEATURE_MATRIX_API_BFF_APP.md +++ b/docs/FEATURE_MATRIX_API_BFF_APP.md @@ -42,8 +42,8 @@ O **BFF** expõe tudo sob `/api/v2/journeys//` e faz proxy para a | **Onboarding** (suggested-territories, complete) | ✅ | ✅ | ✅ | App: lista sugeridos, completa com território selecionado. | | **Territories** (listar, paged, detalhe, enter, chat/channels) | ✅ | ✅ | ✅ | App: paged (explorar), get by id (mapa), enter, canais de chat. | | **Feed** (territory-feed, create-post, interact, post-comments, delete-post) | ✅ | ✅ | ✅ | App: feed paginado, like/comment/share, thread, mídia, excluir, filtros. | -| **Events** (territory-events, participate) | ✅ | ✅ | ✅ | App: lista eventos, participar (interesse/confirmado). | -| **Map** (pins, entities) | ✅ | ✅ | ✅ | App: GET map/pins para exibir pins no mapa. | +| **Events** (territory-events, create-event, participate) | ✅ | ✅ | ✅ | App: lista eventos, **criar evento** (título, descrição, início/término, local), participar (interesse/confirmado). | +| **Map** (pins, entities) | ✅ | ✅ | ✅ | App: GET map/pins para exibir pins no mapa; **toque no pin abre detalhes (deep-link)** para eventos/assets/alertas/feed. | | **Me** (profile, preferences, interests, devices) | ✅ | ✅ | ✅ | App: profile, interests, preferences, registro automático de device. | | **Notifications** (listar paginado, marcar lida) | ✅ | ✅ | ✅ | App: notifications/paged, notifications/{id}/read. | | **Membership** (me, become-resident, verify-residency) | ✅ | ✅ | ✅ | App: tela Membership com status, solicitar residência e verificação geo. | @@ -54,6 +54,7 @@ O **BFF** expõe tudo sob `/api/v2/journeys//` e faz proxy para a | **Marketplace V1** (cart, stores, items) | ✅ | ✅ | ✅ | App: cart v1, busca/checkout v2 e gestão de loja própria (`stores/me`, criar/atualizar). | | **Subscription plans / Subscriptions** | ✅ | ✅ | ✅ | App: planos, minha assinatura, assinar e cancelar. | | **Moderation** (work-items, cases, evidences) | ✅ | ✅ | ✅ | App: fila, casos (decidir), evidências (download + decidir residência). | +| **Governance** (votings: listar, criar, votar, fechar, resultados) | ✅ | ✅ | ✅ | App: tela Governança (jornada `governance`) — lista votações com filtro de status, votar inline, ver resultados e criar votação. | | **Chat** (conversations, messages, participants) | ✅ | ✅ | ✅ | App: canais, grupos (criar), mensagens e envio. | | **Alerts** (listar, criar) | ✅ | ✅ | ✅ | App: listagem e criação de alertas. | | **Admin** (seed, cache-metrics, configs) | ✅ | ✅ | ➖ | Uso administrativo; não no app usuário. | @@ -89,7 +90,7 @@ Prioridade sugerida para **evoluir** com novas fases do backlog (API → BFF → ## Documentos a revisar (app já existe) -> **App Flutter**: Existe uma versão estável do app (auth, onboarding, feed, mapa, eventos, perfil, notificações, publicar, marketplace, chat, membership, alertas). Ver [Release estável – App e Onboarding](./STABLE_RELEASE_APP_ONBOARDING.md) e esta matriz. +> **App Flutter**: Existe uma versão estável do app (auth + **login com Google na UI**, onboarding, feed, mapa com **deep-links nos pins**, eventos com **criação**, **governança/votações**, perfil, notificações, publicar, marketplace, chat, membership, alertas). Ver [Release estável – App e Onboarding](./STABLE_RELEASE_APP_ONBOARDING.md) e esta matriz. --- diff --git a/docs/STABLE_RELEASE_APP_ONBOARDING.md b/docs/STABLE_RELEASE_APP_ONBOARDING.md index 554dd408..8f4dad33 100644 --- a/docs/STABLE_RELEASE_APP_ONBOARDING.md +++ b/docs/STABLE_RELEASE_APP_ONBOARDING.md @@ -18,7 +18,7 @@ Este documento descreve a **versão estável** atual do app (Flutter), do fluxo ### App (Flutter – `frontend/Arah.app`) -- **Autenticação**: login com e-mail (check-email → senha ou criar conta), signup com nome e senha. +- **Autenticação**: login com e-mail (check-email → senha ou criar conta), signup com nome e senha; **login com Google na UI** (botão "Entrar com Google" no fluxo de login — requer `GOOGLE_SIGN_IN_CLIENT_ID` e config Firebase para funcionar end-to-end). - **Onboarding**: tela de seleção de território após login/cadastro quando não há território salvo. - Lista “Próximos a você” (sugeridos por lat/lng). - Seleção na lista **só altera o mapa e o destaque**; o botão **Continuar** é o único que conclui o onboarding e leva ao feed. @@ -27,7 +27,9 @@ Este documento descreve a **versão estável** atual do app (Flutter), do fluxo - **Feed**: listagem do feed do território selecionado, com paginação e pull-to-refresh. - **Explorar**: troca de território (lista paginada); ao “entrar” em outro território, o feed e o mapa refletem o escolhido. - **Mapa**: pins do território, contorno (polígono/círculo) em verde floresta, marcador do usuário. -- **Eventos**: lista de eventos do território, interesse e confirmação de presença. +- **Eventos**: lista de eventos do território, **criação de evento** (título, descrição, início/término com seletores de data/hora, local), interesse e confirmação de presença. +- **Governança**: tela de votações do território (jornada `governance`) — lista com filtro de status (Todas/Abertas/Fechadas), votar inline, ver resultados (barras) e criar votação (tipo, visibilidade, opções). +- **Mapa (deep-links)**: tocar num pin abre uma folha com "Ver detalhes" que navega para o conteúdo correspondente (evento → Eventos, asset → Assets, alerta → Alertas, post → feed). - **Perfil**: exibição e edição de nome e bio; preferências de notificação (estrutura). - **Publicar**: criação de post (título, conteúdo, tipo, visibilidade) no território ativo. @@ -123,15 +125,15 @@ Evolução planejada, em ordem de prioridade sugerida: | Área | O que falta | |------|-------------| -| **Mídia / imagens** | Fotos e mídia nos posts (upload, exibição). | -| **Interações no feed** | Curtir (like), comentar, compartilhar. | -| **Gestão de posts** | Excluir o próprio post. | -| **Filtros** | Opções para filtrar o feed (por tipo, tags, etc.) e preferência de o que filtrar. | -| **Preferências no perfil** | Definir interesses/preferências do usuário para uso em filtros e sugestões. | -| **Tipo de post** | Escolher tipo ao publicar (ex.: geral, alerta, evento) de forma explícita na UI. | -| **Marketplace** | Lojas, listagens, carrinho, checkout no app. | - -O backend já cobre parte dessas capacidades (feed, eventos, perfil, notificações); o app será evoluído aos poucos para expor essas funcionalidades. +| **Login Google (config)** | UI já existe; falta provisionar `GOOGLE_SIGN_IN_CLIENT_ID` (OAuth Web) e config Firebase para o fluxo funcionar end-to-end. | +| **Push / FCM** | Recepção de notificações push e deep-link a partir da notificação (hoje só o registro de device token). | +| **Mapa → post detalhe** | Deep-link de pin do tipo `post` hoje leva ao feed; falta tela de detalhe de post dedicada. | +| **Perfil** | Upload de avatar; ligar o histórico de governança (`me/profile/governance`) na UI. | +| **Marketplace (aprofundamento)** | Tela de carrinho dedicada, gestão de itens do vendedor e fluxo de pagamento (avaliar frente aos valores do produto). | + +> **Entregue recentemente no app**: login com Google na UI, criação de eventos, governança/votações (listar, votar, resultados, criar) e deep-links nos pins do mapa. Feed já cobre mídia, curtir/comentar/compartilhar, exclusão e filtros. + +O backend cobre amplamente essas capacidades; o app é evoluído de forma incremental (API → BFF → App) para expô-las. ---