# Feature Specification: Configuração da Página de Contato (Admin) **Feature Branch**: `027-config-pagina-contato` **Created**: 2026-04-21 **Status**: Draft --- ## Contexto A página `/contato` do site exibe informações institucionais de contato — endereço, telefone, e-mail e horário de atendimento — atualmente fixadas no código-fonte do frontend. Qualquer alteração nessas informações exige um deploy de código, o que cria dependência técnica para uma tarefa puramente operacional. Esta spec cobre a criação de uma configuração persistida em banco de dados que o administrador pode editar pelo painel admin, tornando o conteúdo da página de contato dinâmico e gerenciável sem necessidade de deploy. O padrão adotado é o mesmo já utilizado para a `HomepageConfig`: tabela singleton (sempre `id = 1`), endpoint público de leitura e endpoint protegido de escrita acessível apenas por administradores autenticados. --- ## User Scenarios & Testing ### User Story 1 — Administrador Atualiza as Informações de Contato (Priority: P1) O administrador da imobiliária precisa atualizar o endereço, telefone, e-mail ou horário de atendimento sem depender de um desenvolvedor ou deploy de código. **Why this priority**: É o núcleo da feature. Sem a capacidade de edição pelo admin, todo o restante não tem valor. **Independent Test**: Autenticar como administrador, acessar a página de configuração de contato no painel admin (`/admin/contact-config`), alterar o campo de telefone para um novo valor e salvar. Verificar que a resposta da API pública `GET /api/v1/contact-config` retorna o novo valor e que a página `/contato` do site exibe o telefone atualizado após recarregar. **Acceptance Scenarios**: 1. **Given** um administrador autenticado no painel admin, **When** ele acessa `/admin/contact-config`, **Then** um formulário é exibido com os valores atuais dos campos de endereço, telefone, e-mail e horário de atendimento já preenchidos. 2. **Given** o formulário preenchido com os valores atuais, **When** o admin altera o campo de telefone e clica em "Salvar", **Then** as alterações são persistidas e uma mensagem de sucesso é exibida na tela. 3. **Given** que a configuração foi salva com sucesso, **When** a API pública de configuração de contato é consultada, **Then** ela retorna os novos valores imediatamente, sem necessidade de reiniciar o sistema. 4. **Given** um administrador autenticado, **When** ele tenta salvar o formulário com o campo de e-mail em branco, **Then** o campo é destacado com erro de validação e o envio é bloqueado. 5. **Given** um administrador autenticado, **When** ele tenta salvar com um endereço de e-mail em formato inválido, **Then** o campo é destacado com erro de validação antes do envio ser processado. --- ### User Story 2 — Página de Contato Exibe Informações Dinâmicas (Priority: P1) Um visitante do site acessa a página `/contato` e vê as informações de contato mais recentes cadastradas pelo administrador, sem nenhuma interação adicional necessária. **Why this priority**: É o consumidor final da configuração. Sem a integração com a API, a feature não entrega valor ao visitante nem à imobiliária. **Independent Test**: Com uma configuração de contato salva via painel admin, acessar `/contato` sem autenticação e verificar que o endereço, telefone, e-mail e horário de atendimento exibidos correspondem exatamente aos valores salvos — e não aos dados anteriormente fixados no código. **Acceptance Scenarios**: 1. **Given** uma configuração de contato salva no sistema, **When** qualquer visitante acessa `/contato`, **Then** a página exibe o endereço (rua, bairro/cidade e CEP), telefone, e-mail e horário de atendimento provenientes da API. 2. **Given** que o administrador atualizou o horário de atendimento, **When** um visitante recarrega `/contato`, **Then** o novo horário é exibido imediatamente. 3. **Given** que a API de configuração de contato está indisponível, **When** um visitante acessa `/contato`, **Then** a página exibe um estado de carregamento ou uma mensagem informativa, sem exibir dados desatualizados ou causar erro de renderização crítico. 4. **Given** um visitante não autenticado, **When** ele acessa a rota pública `GET /api/v1/contact-config` diretamente, **Then** a resposta retorna os dados de configuração sem exigir autenticação. --- ### User Story 3 — Proteção do Endpoint de Edição (Priority: P1) Apenas administradores autenticados podem alterar a configuração de contato. Tentativas não autorizadas são bloqueadas. **Why this priority**: Segurança é requisito não-negociável para qualquer endpoint de escrita no painel admin. O impacto de um acesso não autorizado incluiria exibição de informações falsas para todos os visitantes do site. **Independent Test**: Enviar uma requisição `PUT /admin/contact-config` sem token de autenticação (ou com token de usuário comum) e verificar que a resposta é HTTP 401 ou 403. Verificar também que os dados salvos no banco não foram alterados. **Acceptance Scenarios**: 1. **Given** uma requisição `PUT /admin/contact-config` sem token de autenticação, **When** a requisição é processada, **Then** o sistema responde com erro de acesso não autorizado (HTTP 401) e não altera nenhum dado. 2. **Given** uma requisição `PUT /admin/contact-config` com um token de usuário comum (não administrador), **When** a requisição é processada, **Then** o sistema responde com erro de permissão insuficiente (HTTP 403) e não altera nenhum dado. 3. **Given** uma requisição `PUT /admin/contact-config` com token de administrador válido, **When** os dados enviados são válidos, **Then** a configuração é atualizada e o sistema retorna os dados atualizados (HTTP 200). --- ### Edge Cases - O que acontece se a tabela `contact_config` estiver vazia (nenhuma configuração foi salva ainda)? O endpoint público deve retornar valores padrão pré-populados ou um erro? - Como a página `/contato` se comporta durante o carregamento inicial enquanto aguarda a resposta da API? - O que acontece se o campo `business_hours` for enviado com um texto excessivamente longo? - Como o formulário admin lida com falha de rede ao tentar salvar — o usuário perde as alterações não salvas? - O que acontece se dois administradores tentarem salvar a configuração simultaneamente? --- ## Requirements ### Functional Requirements #### Grupo 1 — Dados e Persistência - **FR-001**: O sistema DEVE armazenar as informações de configuração de contato em uma tabela singleton de forma que exista sempre exatamente um registro com `id = 1`, criado automaticamente na primeira leitura ou escrita caso ainda não exista. - **FR-002**: A configuração DEVE incluir os seguintes campos: logradouro do endereço, complemento de bairro/cidade, CEP, telefone, e-mail e horário de atendimento (texto livre multilinha). - **FR-003**: Todos os campos DEVEM ser obrigatórios — nenhum pode ser salvo como nulo ou vazio. - **FR-004**: O campo de e-mail DEVE ser validado quanto ao formato antes de ser persistido. - **FR-005**: O sistema DEVE registrar automaticamente a data e hora da última atualização da configuração. #### Grupo 2 — API Pública de Leitura - **FR-006**: O sistema DEVE disponibilizar um endpoint público de leitura de configuração de contato acessível sem autenticação. - **FR-007**: O endpoint público DEVE retornar todos os campos da configuração em formato estruturado (um objeto com os campos nomeados). - **FR-008**: O endpoint público DEVE retornar os valores padrão (correspondentes aos dados atualmente fixados no código) caso nenhuma configuração tenha sido salva ainda, em vez de retornar erro. #### Grupo 3 — API Protegida de Escrita - **FR-009**: O sistema DEVE disponibilizar um endpoint protegido de atualização de configuração de contato acessível apenas por administradores autenticados. - **FR-010**: O endpoint protegido DEVE rejeitar requisições sem token de autenticação válido com HTTP 401. - **FR-011**: O endpoint protegido DEVE rejeitar tokens de usuários com perfil diferente de administrador com HTTP 403. - **FR-012**: O endpoint protegido DEVE validar todos os campos recebidos antes de persistir e retornar erros de validação específicos por campo em caso de dados inválidos (HTTP 422). - **FR-013**: Após persistência bem-sucedida, o endpoint DEVE retornar os dados atualizados incluindo a data de última atualização. #### Grupo 4 — Interface Admin - **FR-014**: O painel admin DEVE disponibilizar uma página de edição de configuração de contato (`/admin/contact-config`) acessível apenas a administradores autenticados. - **FR-015**: A página admin DEVE carregar automaticamente os valores atuais da configuração ao ser aberta e pré-preencher o formulário. - **FR-016**: O formulário DEVE conter campos de texto para logradouro, bairro/cidade, CEP, telefone, e-mail e uma área de texto para horário de atendimento. - **FR-017**: O formulário DEVE exibir erros de validação inline por campo antes de tentar salvar no servidor, quando possível (ex.: e-mail com formato inválido, campo obrigatório vazio). - **FR-018**: O formulário DEVE exibir uma notificação de sucesso após salvar com êxito e uma notificação de erro em caso de falha na requisição ao servidor. - **FR-019**: O botão de salvar DEVE ser desabilitado enquanto a requisição de salvamento estiver em andamento, para evitar submissões duplicadas. #### Grupo 5 — Página Pública de Contato - **FR-020**: A página `/contato` DEVE buscar as informações de contato da API pública em vez de usar valores fixados no código. - **FR-021**: A página `/contato` DEVE exibir um indicador de carregamento enquanto aguarda a resposta da API. - **FR-022**: A página `/contato` DEVE continuar renderizando normalmente em caso de falha na API, exibindo uma mensagem informativa no lugar das informações de contato. - **FR-023**: A estrutura visual e o layout da página `/contato` NÃO DEVEM ser alterados por esta feature — apenas a origem dos dados muda. ### Key Entities - **ContactConfig**: Registro singleton de configuração de contato da imobiliária. Atributos: logradouro, bairro/cidade, CEP, telefone, e-mail, horário de atendimento (texto multilinha), data de última atualização. Relacionamentos: nenhum — é uma entidade independente de configuração. --- ## Success Criteria ### Measurable Outcomes - **SC-001**: O administrador consegue atualizar qualquer campo de contato em menos de 1 minuto, do acesso à página admin até a confirmação de salvamento. - **SC-002**: A página `/contato` reflete as alterações feitas pelo admin imediatamente após o recarregamento da página, sem necessidade de nenhuma intervenção técnica. - **SC-003**: 100% das tentativas de acesso não autenticado ao endpoint de escrita são bloqueadas com resposta de erro apropriada. - **SC-004**: A página `/contato` permanece funcional e renderizável mesmo quando a API de configuração retorna erro, sem quebrar a experiência do visitante. - **SC-005**: Nenhum campo de configuração de contato permanece fixado no código-fonte do frontend após a conclusão da feature. --- ## Assumptions - O padrão singleton (`get_or_create` com `id = 1`) já é conhecido e usado na codebase (`HomepageConfig`); esta feature segue exatamente o mesmo padrão. - Os valores padrão (usados como fallback quando nenhuma configuração existe) são os atualmente hardcoded na página `/contato`: endereço "Rua das Imobiliárias, 123 / Centro — São Paulo, SP / CEP 01000-000", telefone "(11) 99999-0000", e-mail "contato@imobiliariahub.com.br" e horário conforme texto atual. - O mecanismo de autenticação e autorização de admin (`require_admin`) já existe e será reutilizado sem modificações. - Não há necessidade de histórico de versões da configuração — apenas o valor atual importa. - O campo `business_hours` é texto livre; a formatação de exibição (ex.: quebras de linha) é responsabilidade do componente de apresentação, não da API. - Esta feature não altera o design, layout ou demais seções da página `/contato` — apenas substitui os dados hardcoded por dados dinâmicos. - A migração de banco de dados para criar a tabela `contact_config` será gerada via Alembic, seguindo o padrão já adotado no projeto. - A seed inicial que popula a tabela com os valores padrão é opcional — o comportamento de fallback no endpoint público é suficiente para o primeiro acesso.