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

12 KiB
Raw Permalink Blame History

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_columnbackend/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_limitbackend/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 Nonebackend/app/schemas/homepage.py
  • T004 [P] Adicionar campo hero_image_url?: string | null à interface HomepageConfig; adicionar hero_image_url: null ao objeto FALLBACK_CONFIGfrontend/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
    (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 composition: relative; z-index: 1frontend/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 imageUrlbackgroundImage 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