feat: add full project - backend, frontend, docker, specs and configs

This commit is contained in:
MatheusAlves96 2026-04-20 23:59:45 -03:00
parent b77c7d5a01
commit e6cb06255b
24489 changed files with 61341 additions and 36 deletions

View file

@ -0,0 +1,171 @@
# Tasks: Feature 018 — Homepage Imersiva com Scroll (Hero + Destaques)
**Feature Branch**: `018-homepage-scroll-hero`
**Input**: `.specify/features/018-homepage-scroll-hero/plan.md`, `.specify/features/018-homepage-scroll-hero/spec.md`
**Prerequisites**: plan.md ✅, spec.md ✅
## Format: `[ID] [P?] [Story?] Description — caminho`
- **[P]**: Pode rodar em paralelo (arquivos diferentes, sem dependências de tarefas incompletas)
- **[Story]**: User story correspondente (US1US5, mapeado ao spec.md)
---
## Phase 1: Foundational — Backend + Frontend Types (Pré-requisitos bloqueantes)
**Purpose**: Expor `hero_image_url` no banco de dados, no backend e no frontend. Todas as fases de user story dependem desta fase.
**⚠️ CRÍTICO**: Nenhuma user story pode ser iniciada até esta fase estar completa.
- [ ] T001 Criar migration Alembic com `revision = "d1e2f3a4b5c6"`, `down_revision = "c8d9e0f1a2b3"`: `upgrade()` executa `op.add_column("homepage_config", sa.Column("hero_image_url", sa.String(512), nullable=True))`, `downgrade()` executa `op.drop_column``backend/migrations/versions/d1e2f3a4b5c6_add_hero_image_url_to_homepage_config.py`
- [ ] T002 [P] Adicionar campo `hero_image_url = db.Column(db.String(512), nullable=True)` ao modelo `HomepageConfig` após `featured_properties_limit``backend/app/models/homepage.py`
- [ ] T003 [P] Adicionar `hero_image_url: str | None = None` a `HomepageConfigOut` e `HomepageConfigIn`; em `HomepageConfigIn` incluir `@field_validator("hero_image_url", mode="before")` que converte string vazia ou de apenas espaços para `None``backend/app/schemas/homepage.py`
- [ ] T004 [P] Adicionar campo `hero_image_url?: string | null` à interface `HomepageConfig`; adicionar `hero_image_url: null` ao objeto `FALLBACK_CONFIG``frontend/src/types/homepage.ts` e `frontend/src/pages/HomePage.tsx`
**Checkpoint**: `GET /api/v1/homepage-config` retorna `hero_image_url`; interface TypeScript corretamente tipada.
---
## Phase 2: User Story 1 — Visitante visualiza a hero fullscreen (P1) 🎯 MVP
**Goal**: Hero ocupa 100vh com imagem de fundo opcional, overlay escuro semitransparente e headline/subheadline/CTA centralizados; fallback para gradiente CSS quando sem imagem.
**Independent Test**: Acessar homepage com `hero_image_url` configurado e verificar imagem fullscreen com overlay escuro e texto branco legível. Acessar sem configuração e verificar exibição do gradiente padrão com texto igualmente legível.
- [ ] T005 [P] [US1] Adicionar prop `backgroundImage?: string | null` à interface `HeroSectionProps`; quando `backgroundImage` presente, aplicar `style={{ backgroundImage: \`url(\${backgroundImage})\`, backgroundSize: 'cover', backgroundPosition: 'center' }}` ao `<section>` (substituindo o gradiente); adicionar `<div className="absolute inset-0 pointer-events-none" style={{ background: 'rgba(0,0,0,0.52)' }} aria-hidden="true" />` dentro da section; garantir conteúdo com `position: relative; z-index: 1` — `frontend/src/components/HeroSection.tsx`
- [ ] T006 [P] [US1] Atualizar `HomeScrollScene.tsx`: (a) expandir interface `HomeScrollSceneProps` para incluir `headline: string`, `subheadline?: string | null`, `ctaLabel?: string`, `ctaUrl?: string`, `backgroundImage?: string | null`, `isLoading?: boolean`; (b) renomear prop `imageUrl``backgroundImage` em toda a implementação; (c) adicionar `<div className="absolute inset-0 pointer-events-none" style={{ background: 'rgba(0,0,0,0.50)' }} aria-hidden="true" />` após o bloco `{backgroundImage ? <img> : <div gradiente>}` e antes do overlay de gradiente existente — apenas quando `backgroundImage` presente; (d) adicionar bloco de hero text com skeleton de loading e conteúdo real (h1, p, `<a>`) dentro do container sticky, posicionado com `className="absolute inset-0 flex flex-col items-center justify-center px-6 text-center z-10 pt-14"``frontend/src/components/HomeScrollScene.tsx`
- [ ] T007 [US1] Remover import e uso de `<HeroSection>` do JSX de `HomePage`; passar ao `<HomeScrollScene>` as props: `headline={config.hero_headline}`, `subheadline={config.hero_subheadline}`, `ctaLabel={config.hero_cta_label}`, `ctaUrl={config.hero_cta_url}`, `backgroundImage={config.hero_image_url ?? null}`, `isLoading={isLoading}``frontend/src/pages/HomePage.tsx`
**Checkpoint**: Hero fullscreen com imagem de fundo (ou gradiente como fallback), overlay escuro e texto centralizado visível e legível.
---
## Phase 3: User Story 2 — Cards de imóveis emergem durante o scroll (P1)
**Goal**: Imagem hero permanece sticky enquanto cards sobem por cima com animação `opacity 0→1` + `translateY 48px→0`; stagger 60ms entre cards; usuários com `prefers-reduced-motion` não recebem animações de movimento.
**Independent Test**: Rolar a homepage com imóveis em destaque cadastrados; verificar sticky hero + animação de entrada por card com stagger. Ativar `prefers-reduced-motion: reduce` no DevTools e verificar ausência de `translateY`.
> **Nota**: Animação `RiseCard` (IntersectionObserver `threshold: 0.05`, stagger via `transitionDelay: ${index * 60}ms`, container sticky `h-screen z-0`) já está implementada. Esta fase adiciona apenas suporte a `prefers-reduced-motion`.
- [ ] T008 [US2] Adicionar classes `motion-reduce:transition-none motion-reduce:translate-y-0` ao `className` do `<div>` animado dentro de `RiseCard` para suprimir a transição CSS e o `translateY` quando `prefers-reduced-motion` estiver ativo — `frontend/src/components/HomeScrollScene.tsx`
**Checkpoint**: Cards animam com opacity + translateY normalmente; `prefers-reduced-motion` suprime o movimento mantendo a visibilidade.
---
## Phase 4: User Story 3 — Indicador visual guia o visitante a rolar (P2)
**Goal**: `ScrollHint` exibe "Role para ver os destaques" com 3 chevrons animados em cascata enquanto o hero é visível; animação suprimida em `prefers-reduced-motion`.
**Independent Test**: Verificar label "Role para ver os destaques" e 3 chevrons animados enquanto hero visível; ativar `prefers-reduced-motion: reduce` e verificar que chevrons param de animar.
> **Nota**: `ScrollHint` com 3 chevrons animados em cascata (keyframe `fadeDown`, `animation-delay: ${i * 0.2}s`) já existe. Esta fase atualiza o label e adiciona suporte a `prefers-reduced-motion`.
- [ ] T009 [US3] Em `HomeScrollScene.tsx`: alterar o label fixo passado ao `<ScrollHint>` de `"Imóveis em destaque"` para `"Role para ver os destaques"`; adicionar `className="scroll-chevron"` em cada `<svg>` dos chevrons dentro de `ScrollHint`; acrescentar ao bloco `<style>` inline a regra `@media (prefers-reduced-motion: reduce) { .scroll-chevron { animation: none !important; } }``frontend/src/components/HomeScrollScene.tsx`
**Checkpoint**: Indicador exibe texto correto; chevrons animam normalmente e têm animação suprimida em `prefers-reduced-motion`.
---
## Phase 5: User Story 4 — Redirecionamento automático ao fim dos destaques (P2)
**Goal**: Após 800ms com marcador final 100% visível, redirecionar para `/imoveis` com overlay blur + spinner.
**Independent Test**: Rolar até o fim dos cards de destaque; aguardar 800ms; verificar overlay com blur e spinner e redirect para `/imoveis`.
> **Nota**: Implementação completa já existe em `HomeScrollScene.tsx`: `sentinelRef` + `IntersectionObserver threshold: 1.0` + `setTimeout 800ms` + estado `navigating` + overlay com `backdropFilter: blur(8px)` + spinner animado + `navigate('/imoveis')`. Esta fase apenas melhora a acessibilidade do overlay.
- [ ] T010 [US4] Adicionar `role="status"` e `aria-live="polite"` ao `<div>` do overlay de transição (bloco `{navigating && (...)}`), e `aria-label="Redirecionando para a listagem de imóveis"` ao spinner, para acessibilidade de screen readers — `frontend/src/components/HomeScrollScene.tsx`
**Checkpoint**: Redirect automático funcionando em 800ms com overlay blur+spinner e overlay acessível via `aria-live`.
---
## Phase 6: User Story 5 — Administrador configura a imagem de fundo do hero (P3)
**Goal**: Admin persiste URL da imagem via painel; API retorna o valor; homepage exibe a imagem configurada.
**Independent Test**: Salvar URL válida via admin panel; consultar `GET /api/v1/homepage-config` e verificar `hero_image_url` na resposta; confirmar exibição na homepage. Salvar string vazia e verificar que API retorna `null` e fallback de gradiente é usado.
> **Nota**: A spec assume que o painel admin para `HomepageConfig` já existe, mas nenhum endpoint `PUT` nem página admin foram encontrados no codebase. Esta fase cria ambos. O schema `HomepageConfigIn` (atualizado em T003) já valida o campo.
- [ ] T011 [P] [US5] Adicionar endpoint `PUT /api/v1/admin/homepage-config` ao blueprint `homepage_bp`: requer JWT de admin (usar decorator de auth já existente em `backend/app/utils/auth.py`), valida payload com `HomepageConfigIn`, atualiza registro via SQLAlchemy (`.query.first()` + atribuição de atributos + `db.session.commit()`), retorna `HomepageConfigOut.model_validate(config).model_dump()``backend/app/routes/homepage.py`
- [ ] T012 [US5] Criar `AdminHomepagePage.tsx` com formulário controlado (campos: `hero_headline`, `hero_subheadline`, `hero_cta_label`, `hero_cta_url`, `featured_properties_limit`, `hero_image_url`); buscar config atual com `GET /api/v1/homepage-config` ao montar; salvar com `PUT /api/v1/admin/homepage-config`; seguir padrão visual e de autenticação das demais páginas admin existentes — `frontend/src/pages/admin/AdminHomepagePage.tsx`
**Checkpoint**: Admin salva `hero_image_url` via painel; valor retornado pela API; homepage exibe imagem configurada.
---
## Phase 7: Polish & Cross-Cutting Concerns
**Purpose**: Comportamentos de borda e qualidade transversal.
- [ ] T013 [P] Verificar fallback de imagem padrão: quando `backgroundImage` é `null`, `undefined` ou URL que resulta em erro (add `onError` handler no `<img>` de `HomeScrollScene.tsx` para setar `backgroundImage` state como `null` em caso de falha de carregamento) — `frontend/src/components/HomeScrollScene.tsx`
- [ ] T014 [P] Verificar comportamento mobile: testar hero em viewports < 640px no Chrome DevTools garantindo `backgroundSize: cover` sem distorção; confirmar que hero text e CTA são legíveis em telas pequenas `frontend/src/components/HomeScrollScene.tsx`
---
## Dependencies & Execution Order
### Phase Dependencies
- **Foundational (Phase 1)**: Sem dependências — pode começar imediatamente
- **User Stories (Phases 26)**: Todas dependem da conclusão da Phase 1
- US1 (Phase 2) e US2 (Phase 3): US2 depende de T006 estar completo; demais são independentes entre si
- US3 (Phase 4), US4 (Phase 5), US5 (Phase 6): podem rodar em paralelo entre si após Phase 1
- **Polish (Phase 7)**: Após todas as user stories desejadas estarem completas
### User Story Dependencies
- **US1 (Phase 2, P1)**: Depende da Foundational (T001T004)
- **US2 (Phase 3, P1)**: Depende de T006 (props interface de HomeScrollScene já atualizada)
- **US3 (Phase 4, P2)**: Depende apenas de Phase 1 — independente de US1/US2
- **US4 (Phase 5, P2)**: Já implementado — apenas acessibilidade; depende de Phase 1
- **US5 (Phase 6, P3)**: Depende de T002 e T003 (model e schema já prontos)
### Parallel Execution
```
Phase 1 (Foundational):
T001 → [T002 ∥ T003 ∥ T004]
Phase 2 (US1):
[T005 ∥ T006] → T007
Após Phase 2 (US2US5 em paralelo):
T008 (US2) ∥ T009 (US3) ∥ T010 (US4) ∥ [T011 → T012] (US5)
Phase 7 (Polish):
T013 ∥ T014
```
---
## Implementation Strategy
**MVP (entrega mínima — 7 tarefas)**: Phases 1 + 2 (T001T007)
- Homepage já exibe hero imersivo fullscreen com imagem de fundo opcional, overlay escuro e hero text centralizado
- Sticky scroll + cards animados continuam funcionando (já implementados)
**Incremental após MVP**:
1. T008 (US2) — reduced-motion support para cards
2. T009 (US3) — label correto + reduced-motion no ScrollHint
3. T010 (US4) — acessibilidade no overlay de redirect (núcleo já funcional)
4. T011T012 (US5) — admin panel para configurar `hero_image_url`
5. T013T014 (Polish) — fallback de imagem + validação mobile
---
## Summary
| Fase | User Story | Tarefas | Prioridade |
|------|-----------|---------|-----------|
| Foundational | — | T001T004 | Bloqueante |
| Phase 2 | US1 — Hero Fullscreen | T005T007 | P1 (MVP) |
| Phase 3 | US2 — Cards emergem | T008 | P1 |
| Phase 4 | US3 — Indicador scroll | T009 | P2 |
| Phase 5 | US4 — Auto-redirect | T010 | P2 |
| Phase 6 | US5 — Admin config | T011T012 | P3 |
| Phase 7 | Polish | T013T014 | — |
| **Total** | | **14 tarefas** | |