sass-imobiliaria/.specify/features/018-homepage-scroll-hero/tasks.md

171 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 atualizada)
- **US3 (Phase 4, P2)**: Depende apenas de Phase 1 independente de US1/US2
- **US4 (Phase 5, P2)**: implementado apenas acessibilidade; depende de Phase 1
- **US5 (Phase 6, P3)**: Depende de T002 e T003 (model e schema 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 exibe hero imersivo fullscreen com imagem de fundo opcional, overlay escuro e hero text centralizado
- Sticky scroll + cards animados continuam funcionando ( 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 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** | |