sass-imobiliaria/specs/028-trabalhe-conosco/tasks.md
MatheusAlves96 cf5603243c
Some checks failed
CI/CD → Deploy via SSH / Build & Push Docker Images (push) Successful in 1m0s
CI/CD → Deploy via SSH / Deploy via SSH (push) Successful in 4m35s
CI/CD → Deploy via SSH / Validate HTTPS & Endpoints (push) Failing after 46s
feat: features 025-032 - favoritos, contatos, trabalhe-conosco, area-cliente, navbar, hero-light-dark, performance-homepage
- feat(025): favoritos locais com FavoritesContext, HeartButton, PublicFavoritesPage
- feat(026): central de contatos admin (leads/contatos unificados)
- feat(027): configuração da página de contato via admin
- feat(028): trabalhe conosco - candidaturas com upload e admin
- feat(029): UX área do cliente - visitas, comparação, perfil
- feat(030): navbar UX - menu mobile, ThemeToggle, useFavorites
- feat(031): hero light/dark - imagens separadas por tema, upload, preview, seed
- feat(032): performance homepage - Promise.all parallel fetches, sessionStorage cache,
  preload hero image, loading=lazy nos cards, useInView hook, will-change carrossel,
  keyframes em index.css, AgentsCarousel e HomeScrollScene via props
- fix: light mode HomeScrollScene - gradiente, cores de texto, scroll hint

migrations: g1h2i3j4k5l6 (source em leads), h1i2j3k4l5m6 (contact_config),
            i1j2k3l4m5n6 (job_applications), j2k3l4m5n6o7 (hero theme images)
2026-04-22 22:35:17 -03:00

14 KiB
Raw Blame History

description
Task list para a feature 028 - Trabalhe Conosco

Tasks: Trabalhe Conosco (028)

Input: Design documents de specs/028-trabalhe-conosco/ Prerequisites: plan.md · spec.md · data-model.md · contracts/jobs-api.md

Format: [ID] [P?] [Story?] Description — arquivo

  • [P]: Pode executar em paralelo (arquivo diferente, sem bloqueadores incompletos)
  • [Story]: User story correspondente (US1, US2, US3, US4)
  • Arquivo exato indicado em cada task

Phase 1: Foundational — Backend (Bloqueador de tudo)

Purpose: Migration, model, schemas e rotas Flask precisam existir antes que qualquer integração frontend possa ser testada contra o servidor real.

⚠️ CRÍTICO: Nenhuma fase de user story pode começar até esta fase estar completa.

  • T001 Criar migration Alembic i1j2k3l4m5n6 em backend/migrations/versions/i1j2k3l4m5n6_add_job_applications.py com down_revision = "h1i2j3k4l5m6" — implementar upgrade() criando a tabela job_applications (9 colunas conforme data-model.md: id SERIAL PK, name VARCHAR(150) NOT NULL, email VARCHAR(254) NOT NULL, phone VARCHAR(30) NULL, role_interest VARCHAR(100) NOT NULL, message TEXT NOT NULL, file_name VARCHAR(255) NULL, status VARCHAR(50) NOT NULL server_default 'pending', created_at TIMESTAMP NOT NULL server_default now()) + 2 índices (ix_job_applications_created_at em created_at, ix_job_applications_status em status); downgrade() remove índices e tabela na ordem inversa

  • T002 Criar modelo SQLAlchemy JobApplication em backend/app/models/job_application.py — classe com __tablename__ = "job_applications", 9 colunas mapeando o schema da tabela (status com default="pending", created_at com server_default=db.func.now()), constante ROLE_INTEREST_OPTIONS = ["Corretor(a)", "Assistente Administrativo", "Estagiário(a)", "Outro"] e __repr__ com id + email

  • T003 [P] Criar schemas Pydantic em backend/app/schemas/job.py — definir 3 classes:

    • JobApplicationIn(BaseModel): campos name: str (strip, não vazio), email: EmailStr, phone: str | None = None, role_interest: str (validado contra ROLE_INTEREST_OPTIONS via @field_validator), message: str (max_length=5000, não vazio), file_name: str | None = None
    • JobApplicationOut(BaseModel): todos os campos de JobApplicationIn + id: int, status: str, created_at: datetime; model_config = ConfigDict(from_attributes=True)
    • PaginatedJobApplications(BaseModel): items: list[JobApplicationOut], total: int, page: int, per_page: int, pages: int
  • T004 Criar rotas em backend/app/routes/jobs.py com dois blueprints:

    • jobs_public_bp = Blueprint("jobs_public", __name__): endpoint POST /jobs/apply público — valida body via JobApplicationIn (retorna 422 com {"error": "Dados inválidos", "details": ...} em ValidationError), cria e salva JobApplication via db.session, retorna {"message": "Candidatura recebida com sucesso"} com status 201
    • jobs_admin_bp = Blueprint("jobs_admin", __name__): endpoint GET /jobs decorado com @require_admin — lê query params page (default 1, ≥ 1) e per_page (default 20, clamp 1100), consulta JobApplication.query.order_by(JobApplication.created_at.desc()).paginate(...), serializa via PaginatedJobApplications e retorna JSON 200
  • T005 Registrar model e blueprints em backend/app/__init__.py:

    • Na seção de imports de models, adicionar from app.models import job_application as _job_application_models
    • Registrar jobs_public_bp com url_prefix="/api/v1" e jobs_admin_bp com url_prefix="/api/v1/admin" na função create_app()
  • T006 Aplicar migration no container e verificar schema: docker-compose exec backend flask db upgrade → confirmar tabela com docker-compose exec db psql -U postgres -d saas_imobiliaria -c "\d job_applications"

Checkpoint: curl -X POST http://localhost:5000/api/v1/jobs/apply com body válido retorna 201. GET /api/v1/admin/jobs sem token retorna 401.


Phase 2: User Story 1 — Candidato Envia Formulário (Priority: P1) 🎯 MVP

Goal: Página /trabalhe-conosco com formulário funcional que submete via POST /api/v1/jobs/apply, exibe confirmação de sucesso e mantém dados em caso de erro de rede.

Independent Test: Acessar /trabalhe-conosco sem autenticação, preencher todos os campos obrigatórios (nome, e-mail, cargo de interesse, mensagem) e clicar em "Enviar Candidatura". Verificar que uma mensagem de confirmação é exibida e que GET /api/v1/admin/jobs (com token admin) lista a candidatura recebida.

  • T007 [P] [US1] Criar interface TypeScript em frontend/src/types/jobApplication.ts:

    export interface JobApplicationPayload {
      name: string;
      email: string;
      phone?: string;
      role_interest: string;
      message: string;
      file_name?: string;
    }
    
    export const ROLE_INTEREST_OPTIONS = [
      "Corretor(a)",
      "Assistente Administrativo",
      "Estagiário(a)",
      "Outro",
    ] as const;
    
  • T008 [P] [US1] Criar frontend/src/services/jobsService.ts com função submitApplication(data: JobApplicationPayload): Promise<void> — chama api.post("/api/v1/jobs/apply", data) via instância Axios do projeto e relança o erro para tratamento no componente

  • T009 [US1] Criar frontend/src/pages/JobsPage.tsx com formulário de candidatura:

    • Campos controlados com useState: name, email, phone, role_interest (select com ROLE_INTEREST_OPTIONS), message (textarea, contador de caracteres até 5000), file_name (input file decorativo — apenas registra e.target.files?.[0]?.name)
    • Validação frontend antes do submit: e-mail formato válido, campos obrigatórios não vazios, message ≤ 5000 chars, arquivo (se presente) deve ser PDF e ≤ 2 MB
    • Estado submitting: boolean para desabilitar o botão durante o envio
    • Submit: chama submitApplication(), em sucesso exibe mensagem de confirmação e limpa o formulário; em erro de rede exibe mensagem genérica sem apagar os dados preenchidos
    • Estilo: dark theme do projeto (bg-panel, border-borderSubtle, accent #5e6ad2, tipografia Inter, Tailwind CSS)
  • T010 [US1] Adicionar rota /trabalhe-conosco em frontend/src/App.tsx: importar JobsPage e inserir <Route path="/trabalhe-conosco" element={<JobsPage />} /> entre as rotas públicas

Checkpoint: Formulário em /trabalhe-conosco envia candidatura, recebe 201 e exibe confirmação. Erro de rede exibe mensagem sem apagar campos.


Phase 3: User Story 2 — Visitante Descobre a Oportunidade (Priority: P1)

Goal: Links "Trabalhe Conosco" no footer (coluna "A Imobiliária") e na página /corretores tornam a página de candidatura descobrível organicamente.

Independent Test: Acessar qualquer página e verificar que o footer contém o link "Trabalhe Conosco" na coluna "A Imobiliária". Acessar /corretores e verificar que existe elemento com texto "Trabalhe Conosco" que navega para /trabalhe-conosco.

  • T011 [P] [US2] Adicionar link "Trabalhe Conosco" em frontend/src/components/Footer.tsx — localizar a coluna "A Imobiliária" e inserir <Link to="/trabalhe-conosco">Trabalhe Conosco</Link> seguindo o mesmo padrão visual dos demais links da coluna

  • T012 [P] [US2] Adicionar link/botão "Trabalhe Conosco" em frontend/src/pages/AgentsPage.tsx — inserir elemento (link <Link> ou botão secundário) com texto "Trabalhe Conosco" e href/to="/trabalhe-conosco" em posição visível na página (ex.: ao final da seção de equipe ou como chamada à ação após o grid de corretores)

Checkpoint: Footer exibe link em todas as páginas. /corretores exibe elemento que navega para /trabalhe-conosco.


Phase 4: User Story 3 — Administrador Visualiza Candidaturas (Priority: P2)

Goal: Endpoint GET /api/v1/admin/jobs (implementado na Phase 1) retorna listagem paginada e corretamente serializada. Serviço Axios disponível para consumo futuro no painel admin.

Independent Test: Autenticar como admin e consultar GET /api/v1/admin/jobs?page=1&per_page=20. Verificar: resposta 200 com campos items, total, page, per_page, pages; cada item contém id, name, email, phone, role_interest, message, file_name, status, created_at. Sem token: 401. Token não-admin: 403.

  • T013 [US3] Adicionar função listApplications(page?: number, perPage?: number): Promise<PaginatedJobApplications> em frontend/src/services/jobsService.ts — chama api.get("/api/v1/admin/jobs", { params: { page, per_page: perPage } }) com header Authorization via instância autenticada do Axios; adicionar tipo PaginatedJobApplications em frontend/src/types/jobApplication.ts espelhando o schema do contrato (items: JobApplicationItem[], total, page, per_page, pages)

Checkpoint: listApplications() pode ser chamado do console do browser (após login admin) e retorna dados paginados com a estrutura correta.


Phase 5: User Story 4 — Conteúdo Institucional (Priority: P3)

Goal: Página /trabalhe-conosco enriquecida com hero section e seção "Por que trabalhar conosco?" com 3 cards de benefícios, posicionados acima do formulário.

Independent Test: Acessar /trabalhe-conosco e verificar: hero section com título principal e subtítulo no topo; seção "Por que trabalhar conosco?" com exatamente 3 cards de benefícios antes do formulário; layout responsivo sem sobreposição em mobile.

  • T014 [US4] Adicionar hero section no topo de frontend/src/pages/JobsPage.tsx — bloco com título principal (ex.: "Faça parte da nossa equipe") e subtítulo descritivo; seguir design tokens dark (text-primary, text-secondary, fundo com gradiente sutil ou bg-surface); posicionar acima dos cards de benefícios e do formulário

  • T015 [US4] Adicionar seção "Por que trabalhar conosco?" em frontend/src/pages/JobsPage.tsx com 3 cards de benefícios estáticos — cada card tem ícone SVG, título e descrição; layout em grid responsivo (grid-cols-1 md:grid-cols-3); estilo bg-panel border border-borderSubtle rounded-xl; posicionar entre o hero e o formulário de candidatura. Sugestão de conteúdo dos cards: "Crescimento Profissional" / "Ambiente Colaborativo" / "Comissões Competitivas"

Checkpoint: /trabalhe-conosco exibe hero → 3 benefit cards → formulário nessa ordem. Em mobile (375 px) os cards empilham verticalmente sem overflow horizontal.


Phase 6: Polish & Verificação Final

  • T016 Executar verificação end-to-end manualmente:
    1. GET /api/v1/admin/jobs sem token → 401
    2. POST /api/v1/jobs/apply com body válido → 201, candidatura registrada
    3. POST /api/v1/jobs/apply com e-mail inválido → 422 com details
    4. GET /api/v1/admin/jobs?page=1 com token admin → 200 com a candidatura enviada
    5. Browser: /trabalhe-conosco renderiza hero + 3 cards + formulário
    6. Browser: footer → link "Trabalhe Conosco" → navega para /trabalhe-conosco
    7. Browser: /corretores → link "Trabalhe Conosco" → navega para /trabalhe-conosco

Dependencies & Execution Order

Dependências entre fases

Phase 1 (Foundational Backend)
        │
        ├──→ Phase 2 (US1 — Formulário) ──→ Phase 4 (US3 — Admin service)
        │                │
        │                └──→ Phase 3 (US2 — Links de entrada)
        │
        └──→ Phase 5 (US4 — Conteúdo institucional, extensão da Phase 2)
                                │
                                └──→ Phase 6 (Polish)
  • Phase 1: Sem dependências — começa imediatamente
  • Phase 2: T007 e T008 podem começar em paralelo com Phase 1 (sem necessidade do backend para criar os arquivos TS); T009 depende de T007 + T008; T010 depende de T009
  • Phase 3: T011 e T012 são paralelos entre si e independentes do backend; dependem apenas de T010 (rota já existir no App.tsx)
  • Phase 4: T013 depende de T007/T008 (padrão do serviço) e do endpoint já implementado em T004
  • Phase 5: T014 e T015 são modificações em JobsPage.tsx criado em T009 — devem ser feitas sequencialmente em relação a T009
  • Phase 6: Depende de todas as fases anteriores

Dependências por task

Task Depende de Pode ir em paralelo com
T001 T003
T002 T001 T003
T003 T001, T002
T004 T002, T003
T005 T002, T004
T006 T005
T007 T001T006, T008, T011, T012
T008 T007 T011, T012
T009 T007, T008 T011, T012
T010 T009 T011, T012
T011 T010 T012
T012 T010 T011
T013 T007, T008 T011, T012
T014 T009 T013
T015 T014 T013
T016 T006, T015

Parallel Execution Examples

Fluxo MVP (US1 apenas — Phase 1 + Phase 2)

Stream A (Backend):   T001 → T002 → T004 → T005 → T006
Stream B (Schemas):         T003 (paralelo a T001-T002)
Stream C (Frontend):  T007 → T008 → T009 → T010

Fluxo completo

Stream A (Backend):   T001 → T002 → T004 → T005 → T006
Stream B (Schemas):         T003
Stream C (Frontend):  T007 → T008 → T009 → T010 → T014 → T015
Stream D (Links):           T011 (paralelo após T010)
Stream E (Links):           T012 (paralelo após T010)
Stream F (Admin svc):       T013 (paralelo após T008)

Implementation Strategy

MVP Scope (Phase 1 + Phase 2): Formulário público funcional com persistência — entrega o núcleo da feature (US1 P1).

Incremento 2 (Phase 3): Links de descoberta — sem novos arquivos backend, apenas modificações pontuais em Footer e AgentsPage (US2 P1).

Incremento 3 (Phase 4): Serviço admin no frontend — prepara consumo da listagem (US3 P2); página admin React adiada para iteração futura.

Incremento 4 (Phase 5): Conteúdo institucional (hero + cards) sobre a base já existente de JobsPage (US4 P3).