sass-imobiliaria/.specify/features/001-homepage/tasks.md

33 KiB
Raw Permalink Blame History

Tasks: Homepage (Página Inicial)

Feature: 001-homepage Branch: 001-homepage Input: spec.md, plan.md, DESIGN.md, .specify/memory/constitution.md Generated: 2026-04-13 Status: Ready for implementation


Format

- [ ] T[NNN] [P?] [USN?] Description — path/to/file.ext
  • [P] — Paralelizável (arquivo diferente, sem dependência de tarefa incompleta)
  • [USN] — User Story associada (US1US4)
  • IDs sequenciais na ordem de execução recomendada

Phase 1: Setup — Scaffolding do Projeto

Objetivo: Criar a estrutura de pastas, dependências e arquivos de configuração. Nenhuma lógica de negócio ainda.

ID Complexidade Deps spec_ref
T001 S plan.md §1.1
T002 S T001 plan.md §1.1
T003 S T001 plan.md §1.1
T004 S T001 plan.md §1.1
T005 S T001 plan.md §1.1
T006 M plan.md §1.3
T007 S T006 plan.md §1.3
T008 M T007 plan.md §3.1, DESIGN.md
T009 S T007 plan.md §3.1
T010 S T006 plan.md §1.3
  • T001 Criar estrutura de diretórios do backend — backend/app/models/, backend/app/schemas/, backend/app/routes/, backend/seeds/, backend/tests/, backend/migrations/

    • Done when: Todos os diretórios existem conforme plan.md project structure; __init__.py vazio em cada subpacote Python.
  • T002 Criar backend/pyproject.toml com dependências Flask, SQLAlchemy, Flask-Migrate, Flask-CORS, Pydantic v2, psycopg2-binary, python-dotenv, pytest, pytest-flask — backend/pyproject.toml

    • Done when: uv sync executa sem erro; uv run python -c "import flask; import pydantic" passa.
  • T003 Criar backend/app/config.py com DevelopmentConfig, ProductionConfig, TestingConfig lendo DATABASE_URL, SECRET_KEY, CORS_ORIGINS de variáveis de ambiente — backend/app/config.py

    • Done when: from app.config import config importa sem erro; chave ausente levanta KeyError explícito.
  • T004 Criar backend/app/extensions.py com instâncias únicas db = SQLAlchemy(), migrate = Migrate(), cors = CORS()backend/app/extensions.py

    • Done when: from app.extensions import db, migrate, cors importa sem erro; nenhuma extensão é inicializada neste arquivo (apenas instanciada).
  • T005 Criar backend/.env.example com DATABASE_URL, SECRET_KEY, CORS_ORIGINS, FLASK_ENVbackend/.env.example

    • Done when: Arquivo presente sem valores reais; todos os campos obrigatórios do config.py cobertos.
  • T006 [P] Criar projeto frontend com npm create vite@latest frontend -- --template react-tsfrontend/

    • Done when: cd frontend && npm run dev sobe em localhost:5173 sem erros.
  • T007 Instalar dependências do frontend: tailwindcss, postcss, autoprefixer, axios, react-router-dom, @types/react-router-domfrontend/package.json

    • Done when: npm install completa; node_modules/tailwindcss e node_modules/axios existem; npm run build passa.
  • T008 Criar frontend/tailwind.config.ts com todos os tokens de DESIGN.md: cores mkt-black, panel-dark, surface-elevated, brand-indigo, accent-violet, accent-hover, pesos medium: 510, semibold: 590, letter-spacing para display sizes, fontFamily Inter Variable — frontend/tailwind.config.ts

    • Done when: npx tailwindcss --input src/index.css --output /dev/null compila sem aviso; classe bg-mkt-black e text-brand-indigo existem no output CSS gerado.
  • T009 Configurar frontend/src/index.css: importar Tailwind (@tailwind base/components/utilities), @import Inter Variable via Google Fonts, @layer base com font-feature-settings: "cv01", "ss03" e body { @apply bg-mkt-black text-text-primary font-sans antialiased }frontend/src/index.css

    • Done when: Página abre com fundo #08090a e fonte Inter Variable sem inline style.
  • T010 [P] Configurar frontend/vite.config.ts com proxy /apihttp://localhost:5000 e changeOrigin: truefrontend/vite.config.ts

    • Done when: npm run build passa sem erros TypeScript; proxy configurado no bloco server.proxy.

Checkpoint Phase 1: uv sync e npm run dev funcionam; estrutura de pastas completa conforme plan.md.


Phase 2: Foundational — Infraestrutura Flask + PostgreSQL

Objetivo: Flask app factory funcional, banco de dados conectado, Flask-Migrate inicializado. Bloqueia todas as fases de user story.

⚠️ CRÍTICO: Nenhuma User Story pode ser implementada antes desta fase estar completa.

ID Complexidade Deps spec_ref
T011 S T005 plan.md §1.2
T012 M T002, T003, T004 plan.md §1.1
T013 S T011, T012 plan.md §1.2
T014 S T013 plan.md §1.2
  • T011 Iniciar container PostgreSQL local: docker run -d --name saas_imob_db -e POSTGRES_USER=imob -e POSTGRES_PASSWORD=imob_dev -e POSTGRES_DB=saas_imobiliaria -p 5432:5432 postgres:16-alpine; copiar backend/.env.example para backend/.env e preencher com valores locais — backend/.env

    • Done when: docker ps mostra container saas_imob_db Running; psql postgresql://imob:imob_dev@localhost:5432/saas_imobiliaria -c "\l" lista o banco.
  • T012 Criar backend/app/__init__.py com create_app(config_name="default") que inicializa db, migrate, cors e registra blueprints de app.routesbackend/app/__init__.py

    • Done when: uv run flask --app app shell abre sem traceback; db.engine.connect() no shell retorna sem erro.
  • T013 Inicializar Flask-Migrate: uv run flask --app app db init dentro de backend/backend/migrations/

    • Done when: Pasta backend/migrations/ criada com env.py e versions/ pelo Alembic.
  • T014 Confirmar conexão DB no shell Flask: db.engine.connect() — nenhum arquivo criado

    • Done when: Comando retorna Connection sem exceção; PostgreSQL aceita a conexão com DATABASE_URL do .env.

Checkpoint Phase 2: uv run flask --app app db init ok; uv run pytest passa (0 testes, setup ok).


Phase 3: User Story 4 — Admin Configura Conteúdo da Homepage (Priority: P1)

Goal: API backend completamente funcional: GET /api/v1/homepage-config e GET /api/v1/properties?featured=true retornando JSON válido com dados do seeder.

Independent Test: curl http://localhost:5000/api/v1/homepage-config retorna 200 com JSON contendo hero_headline; curl "http://localhost:5000/api/v1/properties?featured=true" retorna array de até 6 imóveis; uv run pytest passa nos dois arquivos de teste.

ID Complexidade Deps spec_ref
T015 M T012 plan.md §2.1, spec.md US4, FR-006, FR-008
T016 S T012 plan.md §2.1, spec.md US4, FR-005
T017 M T015 plan.md §2.2, spec.md FR-007
T018 M T016 plan.md §2.2, spec.md FR-005, FR-016
T019 S T015, T016 plan.md §2.4
T020 S T019 plan.md §2.4
T021 M T018, T020 plan.md §2.3, spec.md US4 scenario 1
T022 M T017, T020 plan.md §2.3, spec.md US2, FR-006009
T023 S T021, T022 plan.md §1.1
T024 M T023 plan.md §2.5, spec.md US4 scenario 2
T025 S T012 plan.md §2
T026 M T025, T021 plan.md §2, spec.md US4 scenarios 13
T027 M T025, T022 plan.md §2, spec.md US2 scenarios 12, FR-010
  • T015 [P] [US4] Criar backend/app/models/property.py com classes Property (UUID pk, title, slug, address, price Numeric(12,2), type enum venda|aluguel, bedrooms, bathrooms, area_m2, is_featured, is_active, created_at) e PropertyPhoto (id, property_id FK CASCADE, url, alt_text, display_order) com relationship order_by display_order — backend/app/models/property.py

    • Done when: from app.models.property import Property, PropertyPhoto importa sem erro; campos declarados exatamente conforme plan.md §2.1; price usa Numeric(12,2), nunca Float.
  • T016 [P] [US4] Criar backend/app/models/homepage.py com classe HomepageConfig (id Integer PK, hero_headline String(120) NOT NULL, hero_subheadline String(240) nullable, hero_cta_label String(40) default "Ver Imóveis", hero_cta_url String(200) default "/imoveis", featured_properties_limit Integer default 6, updated_at DateTime server_default+onupdate) — backend/app/models/homepage.py

    • Done when: from app.models.homepage import HomepageConfig importa sem erro; hero_headline é NOT NULL no modelo; featured_properties_limit tem default 6.
  • T017 [US4] Criar backend/app/schemas/property.py com PropertyPhotoOut e PropertyOut (Pydantic v2, model_config = ConfigDict(from_attributes=True), price: Decimal, type: Literal["venda", "aluguel"], photos: list[PropertyPhotoOut]) — backend/app/schemas/property.py

    • Done when: PropertyOut.model_validate(property_instance) funciona em teste manual; price serializa como Decimal (não float).
  • T018 [US4] Criar backend/app/schemas/homepage.py com HomepageConfigOut e HomepageConfigIn (Pydantic v2); HomepageConfigIn com @field_validator("hero_headline") rejeitando string vazia e @field_validator("featured_properties_limit") rejeitando valores fora de 112 — backend/app/schemas/homepage.py

    • Done when: HomepageConfigIn(hero_headline="") levanta ValidationError; HomepageConfigIn(featured_properties_limit=13) levanta ValidationError; instância válida passa.
  • T019 [US4] Gerar migração inicial com Flask-Migrate: uv run flask --app app db migrate -m "initial schema: properties, property_photos, homepage_config"backend/migrations/versions/

    • Done when: Arquivo de migração criado em backend/migrations/versions/; revisão manual confirma tabelas properties, property_photos, homepage_config e enum property_type presentes.
  • T020 [US4] Aplicar migração e testar ciclo upgrade/downgrade: flask db upgrade, flask db downgrade base, flask db upgrade — banco de dados

    • Done when: Ambos os comandos executam sem erro; tabelas existem no banco após upgrade final; \dt no psql lista as três tabelas.
  • T021 [US4] Criar backend/app/routes/homepage.py com Blueprint homepage_bp e rota GET /api/v1/homepage-config retornando HomepageConfigOut.model_validate(config).model_dump() ou 404 se nenhum registro — backend/app/routes/homepage.py

    • Done when: curl http://localhost:5000/api/v1/homepage-config retorna 200 com JSON contendo hero_headline após seeder; retorna 404 se tabela vazia.
  • T022 [US4] Criar backend/app/routes/properties.py com Blueprint properties_bp e rota GET /api/v1/properties que filtra por is_active=True; quando featured=true, filtra por is_featured=True, ordena por created_at DESC, aplica limit de HomepageConfig.featured_properties_limit (fallback 6) — backend/app/routes/properties.py

    • Done when: curl "http://localhost:5000/api/v1/properties?featured=true" retorna array JSON; quando nenhum imóvel featured, retorna [] (não 500); limite máximo respeitado conforme config.
  • T023 [US4] Registrar homepage_bp e properties_bp no create_app() de backend/app/__init__.py; importar models de property e homepage para garantir que Flask-Migrate os detecte — backend/app/__init__.py

    • Done when: flask routes lista /api/v1/homepage-config e /api/v1/properties; CORS permite http://localhost:5173.
  • T024 [US4] Criar backend/seeds/seed.py que apaga e recria: 1 HomepageConfig com headline/subheadline/cta configurados e 6 Property com is_featured=True, tipos variados (venda/aluguel), e pelo menos 1 PropertyPhoto por imóvel usando URLs do picsum.photosbackend/seeds/seed.py

    • Done when: uv run python seeds/seed.py executa sem erro; GET /api/v1/properties?featured=true retorna exatamente 6 imóveis; cada imóvel tem photos com pelo menos 1 entrada.
  • T025 [US4] Criar backend/tests/conftest.py com fixture app (usando TestingConfig com SQLite em memória ou PostgreSQL de teste) e fixture client (app.test_client()) — backend/tests/conftest.py

    • Done when: uv run pytest --collect-only descobre conftest sem error; fixture client disponível nos testes.
  • T026 [P] [US4] Criar backend/tests/test_homepage.py com testes: (1) GET /api/v1/homepage-config200 com campos obrigatórios; (2) GET /api/v1/homepage-config sem registro → 404; (3) HomepageConfigIn(hero_headline="")ValidationErrorbackend/tests/test_homepage.py

    • Done when: uv run pytest tests/test_homepage.py -v passa com 3 testes verdes.
  • T027 [P] [US4] Criar backend/tests/test_properties.py com testes: (1) GET /api/v1/properties?featured=true200 com array; (2) resultado contém campos id, title, slug, price, type, bedrooms, bathrooms, area_m2, photos; (3) sem imóveis featured → 200 com [] (não 500) — backend/tests/test_properties.py

    • Done when: uv run pytest tests/test_properties.py -v passa com 3 testes verdes.

Checkpoint Phase 3 (US4): uv run pytest passa; ambos os endpoints retornam JSON válido; seeder popula 6 imóveis.


Phase 4: User Story 1 — Visitante Experimenta o Hero e a Navegação (Priority: P1) 🎯 MVP

Goal: Navbar sticky e HeroSection renderizando com conteúdo real da API; fallback silencioso quando API falha; responsivo em todos os breakpoints.

Independent Test: Abrir http://localhost:5173 com backend rodando — Navbar exibe logo + links; Hero exibe headline da API; CTA redireciona para /imoveis; sem backend, FALLBACK_CONFIG é exibido silenciosamente.

ID Complexidade Deps spec_ref
T028 S T010 plan.md §3.2, spec.md US1
T029 S T010 plan.md §3.2, spec.md US2
T030 S T028 plan.md §3.3
T031 S T030 plan.md §3.3
T032 S T030 plan.md §3.3
T033 M T008, T009 plan.md §3.4, spec.md FR-001003, NFR-004
T034 L T031, T033 plan.md §3.4, spec.md FR-004, US1 scenarios 15, NFR-006
T035 M T031, T034 plan.md §3.5, spec.md US1 scenario 4, edge-cases
T036 S T035 plan.md §3.6
T037 S T036 plan.md §3.6
T038 S T007 plan.md §3.4, spec.md FR-011
  • T028 [P] [US1] Criar frontend/src/types/homepage.ts com interface HomepageConfig (hero_headline, hero_subheadline string | null, hero_cta_label, hero_cta_url, featured_properties_limit) — frontend/src/types/homepage.ts

    • Done when: Arquivo exporta interface sem erro TypeScript; todos os campos opcionais/nullable corretamente tipados.
  • T029 [P] [US1] Criar frontend/src/types/property.ts com interfaces PropertyPhoto (url, alt_text, display_order) e Property (id, title, slug, price string, type 'venda' | 'aluguel', bedrooms, bathrooms, area_m2, is_featured, photos) — frontend/src/types/property.ts

    • Done when: Arquivo exporta ambas as interfaces sem erro TypeScript; price é string (Decimal serializado do backend).
  • T030 [US1] Criar frontend/src/services/api.ts com instância Axios (baseURL: '/api/v1', timeout: 8000, header Content-Type: application/json) — frontend/src/services/api.ts

    • Done when: import { api } from './api' compila sem erro; baseURL aponta para /api/v1.
  • T031 [US1] Criar frontend/src/services/homepage.ts com getHomepageConfig(): Promise<HomepageConfig> chamando api.get<HomepageConfig>('/homepage-config')frontend/src/services/homepage.ts

    • Done when: Função exportada compila sem erro TypeScript; retorna tipo Promise<HomepageConfig>.
  • T032 [US1] Criar frontend/src/services/properties.ts com getFeaturedProperties(): Promise<Property[]> chamando api.get<Property[]>('/properties', { params: { featured: 'true' } })frontend/src/services/properties.ts

    • Done when: Função exportada compila sem erro TypeScript; retorna tipo Promise<Property[]>.
  • T033 [US1] Criar frontend/src/components/Navbar.tsx: header semântico (<header role="banner"><nav aria-label="Navegação principal">), fundo rgba(8,9,10,0.85) + backdrop-blur-navbar (classe Tailwind), sticky z-50, border-bottom border-white/5, logo à esquerda, links "Imóveis"/"Sobre"/"Contato" à direita (text-sm text-text-secondary hover:text-text-primary), hamburger menu em mobile (<768px) com toggle de estado — frontend/src/components/Navbar.tsx

    • Done when: Navbar visível sticky ao scroll; hamburger aparece em <768px; links navegáveis por teclado com foco visível (NFR-008); fundo com blur confirmado visualmente.
  • T034 [US1] Criar frontend/src/components/HeroSection.tsx com props { headline, subheadline, ctaLabel, ctaUrl }: fundo #08090a + gradiente radial rgba(94,106,210,0.08), headline com text-[72px] tracking-display-xl font-medium em desktop / text-[48px] tablet / text-[40px] mobile, subheadline text-xl text-text-secondary font-light (não renderizado quando null), botão CTA bg-brand-indigo hover:bg-accent-hover rounded text-white font-semibold transition-colors duration-200 com focus ring, skeleton de 3 linhas animadas quando isLoading=truefrontend/src/components/HeroSection.tsx

    • Done when: Hero renderiza headline + subheadline da API; subheadline=null não insere elemento vazio; tipografia escala nos 3 breakpoints; CTA navega para ctaUrl; foco visível no botão (NFR-008); skeleton exibido quando isLoading.
  • T035 [US1] Criar frontend/src/pages/HomePage.tsx com FALLBACK_CONFIG estático, useState<HomepageConfig>(FALLBACK_CONFIG), useEffect(() => getHomepageConfig().then(setConfig).catch(() => {}), []), e renderização de <Navbar> + <HeroSection> com props do config — frontend/src/pages/HomePage.tsx

    • Done when: Página carrega com conteúdo da API após 1 request; com API indisponível, exibe FALLBACK_CONFIG silenciosamente (sem erro visível); isLoading passado para HeroSection durante fetch.
  • T036 [US1] Criar frontend/src/App.tsx com <BrowserRouter><Routes><Route path="/" element={<HomePage />} /></Routes></BrowserRouter>frontend/src/App.tsx

    • Done when: npm run build compila sem erro; http://localhost:5173/ renderiza HomePage.
  • T037 [US1] Atualizar frontend/src/main.tsx para usar <React.StrictMode><App /></React.StrictMode> com importação de ./index.cssfrontend/src/main.tsx

    • Done when: npm run dev não lança erro de StrictMode; CSS global carregado.
  • T038 [US1] Adicionar imagem placeholder frontend/public/placeholder-property.jpg (imagem minimalista ≤ 100 KB, 16:9, tema imobiliário ou cinza neutro) — frontend/public/placeholder-property.jpg

    • Done when: Arquivo presente em public/; acessível em http://localhost:5173/placeholder-property.jpg; ≤ 100 KB.

Checkpoint Phase 4 (US1): Homepage abre com Navbar + Hero; hero exibe dados reais; fallback silencioso funciona; responsivo mobile/tablet/desktop.


Phase 5: User Story 2 — Visitante Explora Imóveis em Destaque (Priority: P1)

Goal: Grade de PropertyCards responsiva (1→2→3 colunas) renderizando dados reais da API, com skeleton durante loading, fallback para grade vazia e placeholder para imóveis sem foto.

Independent Test: Abrir homepage com seeder rodado — grade exibe 6 cards com foto, título, preço formatado em BRL, badge de tipo, quartos/banheiros/área; durante load exibe 3 skeletons; com array vazio exibe mensagem; imóvel sem foto exibe placeholder.

ID Complexidade Deps spec_ref
T039 L T029, T008 plan.md §3.4, spec.md FR-007, US2 scenarios 14, NFR-007
T040 S T039 plan.md §3.4, spec.md edge-cases
T041 M T032, T039, T040 plan.md §3.4, spec.md FR-006011, US2, NFR-005
T042 S T041, T035 plan.md §3.5
  • T039 [US2] Criar frontend/src/components/PropertyCard.tsx com props { property: Property }: container rounded-xl border border-white/5 bg-surface-elevated overflow-hidden cursor-pointer hover:border-white/[0.08] transition-all duration-200, foto aspect-[16/9] w-full object-cover rounded-t-xl (usa placeholder-property.jpg quando photos.length === 0, alt={property.title} NFR-007), badge de tipo pill (rounded-full text-xs font-medium px-2.5 py-1, Venda: bg-brand-indigo/20 text-accent-violet, Aluguel: bg-white/5 text-text-secondary border border-white/10), preço formatado com Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }) em text-lg font-medium text-text-primary, stats (quartos/banheiros/área) com ícone SVG + text-sm text-text-secondary, clique navega para /imoveis/{property.slug}frontend/src/components/PropertyCard.tsx

    • Done when: Card renderiza todos os campos obrigatórios (FR-007); placeholder exibido quando sem foto (FR-011); preço formatado como R$ 750.000,00; navegação para /imoveis/slug funciona; alt text presente em todas as imagens (NFR-007).
  • T040 [US2] Criar frontend/src/components/PropertyCardSkeleton.tsx com estrutura idêntica ao PropertyCard usando animate-pulse, blocos bg-surface-secondary rounded no lugar de foto, badge, preço e stats — frontend/src/components/PropertyCardSkeleton.tsx

    • Done when: Componente renderiza sem props; altura/largura compatíveis com PropertyCard; animação pulso visível.
  • T041 [US2] Criar frontend/src/components/FeaturedProperties.tsx com estados loading | success | error | empty gerenciados via useEffect(() => getFeaturedProperties()...): loading → 3 <PropertyCardSkeleton>, success com dados → grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 de <PropertyCard>, success sem dados ([]) → mensagem "Nenhum imóvel em destaque no momento" centralizada, error → mensagem de fallback sem stack trace — frontend/src/components/FeaturedProperties.tsx

    • Done when: 3 skeletons exibidos durante fetch; grid 1→2→3 colunas conforme breakpoints (NFR-005); estado vazio não quebra layout; estado error mostra mensagem amigável (spec edge-case: API indisponível).
  • T042 [US2] Integrar <FeaturedProperties /> em frontend/src/pages/HomePage.tsx após <HeroSection>frontend/src/pages/HomePage.tsx

    • Done when: Seção de imóveis em destaque visível abaixo do hero; título da seção "Imóveis em Destaque" presente; dados reais exibidos após fetch.

Checkpoint Phase 5 (US2): Grade responsiva funcionando com dados reais; skeleton durante loading; placeholder para fotos ausentes; vazio sem erro.


Phase 6: User Story 3 — Visitante Descobre a Agência e Inicia Contato (Priority: P2)

Goal: Seções About, CTA e Footer implementadas com conteúdo estático, visualmente alinhadas ao DESIGN.md.

Independent Test: Rolar a homepage além da grade de imóveis — AboutSection exibe nome + descrição da agência; CTASection exibe convite ao contato com elemento acionável; Footer exibe informações de contato e links de navegação.

ID Complexidade Deps spec_ref
T043 S T008 plan.md §3.4, spec.md FR-012, US3 scenario 1
T044 S T008 plan.md §3.4, spec.md FR-013, US3 scenario 2
T045 S T008 plan.md §3.4, spec.md FR-014, US3 scenario 3, NFR-010
T046 S T042 plan.md §3.5
  • T043 [US3] Criar frontend/src/components/AboutSection.tsx com fundo bg-panel-dark, título "Sobre Nós" text-3xl font-medium tracking-h1 text-text-primary, parágrafo de descrição da agência text-base text-text-secondary leading-relaxed, padding vertical py-20 md:py-[80px], max-width 1200px centralizado — frontend/src/components/AboutSection.tsx

    • Done when: Seção visível ao rolar; nome e descrição presentes; background #0f1011 confirmado; padding conforme DESIGN.md.
  • T044 [US3] Criar frontend/src/components/CTASection.tsx com fundo bg-surface-elevated border-t border-white/5, título de convite ao contato, telefone ou e-mail como elemento acionável (<a href="tel:..." ou <a href="mailto:...") estilizado como botão outline ou link proeminente, padding vertical py-20frontend/src/components/CTASection.tsx

    • Done when: Elemento acionável presente e clicável; navegável por teclado com foco visível (NFR-008); seção visível antes do rodapé.
  • T045 [US3] Criar frontend/src/components/Footer.tsx com <footer role="contentinfo"> (NFR-010), fundo bg-panel-dark border-t border-white/5, informações de contato (e-mail e/ou telefone), links de navegação "Imóveis"/"Sobre"/"Contato" text-xs text-text-tertiary hover:text-text-secondary, copyright com ano atual — frontend/src/components/Footer.tsx

    • Done when: Tag semântica <footer role="contentinfo"> presente; informações de contato exibidas; links navegáveis por teclado; copyright visível.
  • T046 [US3] Integrar <AboutSection />, <CTASection /> e <Footer /> em frontend/src/pages/HomePage.tsx após <FeaturedProperties>frontend/src/pages/HomePage.tsx

    • Done when: Todas as seções visíveis ao rolar a página completa; ordem: Navbar → Hero → FeaturedProperties → AboutSection → CTASection → Footer.

Checkpoint Phase 6 (US3): Homepage completa visualmente; todas as seções visíveis; estrutura semântica HTML5 correta em toda a página.


Phase 7: Polish & Cross-Cutting Concerns

Objetivo: Integração E2E verificada, edge cases cobertos, conformidade visual com DESIGN.md, acessibilidade WCAG 2.1 AA, performance.

ID Complexidade Deps spec_ref
T047 M T046 plan.md §4.1, spec.md US4 scenarios 12
T048 S T047 plan.md §4.2, spec.md edge-cases
T049 M T047 plan.md §4.2, spec.md edge-cases
T050 M T046 plan.md §4.3, spec.md NFR-004006
T051 M T046 plan.md §4.3, spec.md NFR-007010
T052 S T046 plan.md §4.3, spec.md NFR-009
T053 M T047 plan.md §4.4, spec.md NFR-001003
  • T047 Verificar integração E2E completa com backend e frontend rodando: hero exibe headline real da API, grade exibe 6 imóveis do seeder, atualizar headline via PATCH /api/v1/homepage-config no shell e confirmar que reload da homepage reflete novo texto (FR-005, US4 scenario 1); confirmar que CORS aceita apenas http://localhost:5173 — nenhum arquivo criado

    • Done when: Todos os dados dinâmicos vêm da API; headline atualizado via API reflete no próximo reload; CORS não aceita origens não configuradas.
  • T048 [P] Verificar fallback e estados de erro: (1) parar backend e confirmar que FALLBACK_CONFIG é exibido silenciosamente; (2) FeaturedProperties com API indisponível exibe mensagem de fallback (não 500); (3) nenhum stack trace visível ao usuário — nenhum arquivo criado

    • Done when: Página renderiza versão degradada sem crash visível; console do browser não lança erros não tratados.
  • T049 [P] Verificar edge cases: (a) subheadline null → HeroSection não renderiza elemento vazio; (b) headline com 120 caracteres → sem overflow no hero; (c) imóvel sem photo → placeholder exibido; (d) featured vazio ([]) → mensagem "Nenhum imóvel em destaque" sem colapso de seção — nenhum arquivo criado

    • Done when: Todos os 4 casos verificados visualmente; nenhum layout quebrado.
  • T050 [P] Verificar responsividade nos breakpoints 320px, 375px, 768px, 1024px, 1280px, 1440px via DevTools: tipografia do hero escala (40px/48px/72px conforme NFR-006), grade 1→2→3 colunas (NFR-005), Navbar hamburger em <768px, nenhum overflow horizontal — nenhum arquivo criado

    • Done when: Todos os 6 breakpoints verificados sem overflow; tipografia e grade conforme NFR-004006.
  • T051 [P] Verificar acessibilidade: (a) todas as imagens têm alt descritivos (NFR-007); (b) tab order lógico por toda a página; (c) botões/links com foco visível (NFR-008); (d) elementos semânticos <header>, <nav>, <main>, <footer> presentes (NFR-010); (e) roles ARIA corretos — nenhum arquivo criado

    • Done when: Axe DevTools (ou similar) sem violações críticas; tab navigation cobre todos os elementos interativos.
  • T052 Verificar contraste WCAG 2.1 AA: (a) #f7f8f8 sobre #08090a ≥ 4,5:1 ; (b) #d0d6e0 sobre #08090a ≥ 4,5:1 ; (c) branco sobre #5e6ad2 (botão CTA) — verificar com tool; (d) cor do badge Aluguel sobre fundo do card — verificar com tool; corrigir para #828fff se necessário (plan.md §4.3) — possível ajuste em frontend/src/components/PropertyCard.tsx

    • Done when: Todos os pares de cor críticos ≥ 4,5:1 para texto de corpo; ≥ 3:1 para componentes UI (NFR-009).
  • T053 Executar Lighthouse no modo "Mobile" com throttling 4G: LCP < 2,5s (NFR-001); verificar no DevTools Network que GET /api/v1/properties?featured=true < 500ms (NFR-002); confirmar nenhuma imagem do seeder > 300 KB (NFR-003) — possível ajuste em backend/seeds/seed.py para usar URLs de imagens otimizadas

    • Done when: Lighthouse LCP < 2,5s; API < 500ms em máquina local; imagens do seeder de picsum.photos com dimensões adequadas (max 300 KB cada).

Checkpoint Phase 7: Integração E2E completa; edge cases tratados; WCAG 2.1 AA ok; responsividade verificada; performance dentro das metas.


Dependencies & Execution Order

Phase Dependencies

Phase 1 (Setup)
    └── Phase 2 (Foundational — BLOQUEIA tudo)
             ├── Phase 3 (US4 — Backend API)
             │       └── Phase 4 (US1 — Hero/Nav) *requires T031
             │       └── Phase 5 (US2 — Featured Properties) *requires T032
             └── Phase 4 pode iniciar em paralelo com Phase 3
                 (frontend não depende do backend, só de tipos e services)

Phase 4 ── completa ──┐
Phase 5 ── completa ──┼── Phase 6 (US3 — About/CTA/Footer)
                       └── Phase 7 (Polish — requer Phases 36)

User Story Dependencies

  • US4 (Phase 3): Pode iniciar após Phase 2. Independente dos outros US.
  • US1 (Phase 4): Pode iniciar após Phase 2 (frontend não precisa do backend rodando para ser codado). Requer backend para teste E2E.
  • US2 (Phase 5): Pode iniciar após Phase 4 (depende de PropertyCard e FeaturedProperties). Independente de US3.
  • US3 (Phase 6): Pode iniciar após Phase 5 ou em paralelo (componentes independentes). Independente de US1/US2 no código.
  • Polish (Phase 7): Requer todas as fases anteriores completas.

Within Each User Story

  • Types/interfaces → Services → Components → Page integration
  • Models → Schemas → Routes → seed/tests (backend)

Parallel Execution Examples

Paralelo possível na Phase 1

# Terminal 1 — Backend
cd backend
uv sync
uv run flask --app app shell

# Terminal 2 — Frontend (independente)
npm create vite@latest frontend -- --template react-ts
cd frontend && npm install && npm run dev

Paralelo possível na Phase 3 (US4)

# T015 e T016 podem ser escritos por pessoas diferentes simultaneamente
# (arquivos diferentes, sem dependência entre si)
# T017 depende de T015; T018 depende de T016
# T026 e T027 podem ser escritos em paralelo após T025

Paralelo possível nas Phases 46

# Após Phase 2 concluída:
# Phase 4 (US1) e Phase 3 (US4) podem rodar em paralelo
# (frontend e backend são totalmente desacoplados até o teste E2E)

# Dentro da Phase 4:
# T028 (types/homepage) e T029 (types/property) são paralelos
# T033 (Navbar) e T034 (HeroSection) são paralelos após T030/T031

# Dentro da Phase 6:
# T043, T044, T045 são paralelos (arquivos independentes)

Implementation Strategy

MVP Scope (entregável mínimo para validação)

Completar apenas as fases de P1 para ter uma homepage funcional:

  1. Phase 1 (Setup)
  2. Phase 2 (Foundational)
  3. Phase 3 (US4 — API backend)
  4. Phase 4 (US1 — Hero + Nav)
  5. Phase 5 (US2 — Featured Properties)
  6. ⏭️ Phase 6 (US3 — P2, pode ser adicionado depois)
  7. ⏭️ Phase 7 (Polish — verificação final antes de deploy)

O MVP entrega: homepage com hero dinâmico, grade de imóveis em destaque, responsividade, fallback silencioso.

Incremental Delivery Order

Sprint 1: T001T014 (Setup + Foundational)
Sprint 2: T015T027 (Backend completo com testes)
Sprint 3: T028T038 (Frontend: tipos, services, Navbar, Hero)
Sprint 4: T039T042 (Frontend: PropertyCard, FeaturedProperties)
Sprint 5: T043T046 (Frontend: About, CTA, Footer)
Sprint 6: T047T053 (Polish, integração, QA)

Summary

Métrica Valor
Total de tarefas 53
US1 (Hero/Nav) 11 tarefas (T028T038)
US2 (Featured Properties) 4 tarefas (T039T042)
US3 (About/CTA/Footer) 4 tarefas (T043T046)
US4 (Backend API) 13 tarefas (T015T027)
Setup + Foundational 14 tarefas (T001T014)
Polish 7 tarefas (T047T053)
Tarefas paralelizáveis [P] 22
Complexidade S 26
Complexidade M 19
Complexidade L 2 (T034, T039)

Format Validation

Todas as 53 tarefas seguem o formato obrigatório:

  • Checkbox - [ ]
  • Task ID sequencial (T001T053)
  • Marker [P] onde aplicável
  • Label [USN] em todas as tarefas de user story
  • Caminhos de arquivo explícitos em cada tarefa
  • Critério "Done when" em cada tarefa