⚠️ Achado principal: o spec original parte de uma premissa errada
O spec pasted assume que hoje existe só o widget de Agenda, e que o card de contato precisa ser construído do zero. Isso não é verdade.
Os dois componentes já existem e já convivem no mesmo container:
TerapistContact.tsx(src/containers/PublicProfile/components/) — card “Informações de Contato” completo: telefone, localização, e-mail, botão WhatsApp (wa.me/:number).ProfileScheduler.tsx(mesma pasta) — o widget “Agenda” (calendário), título literal"Agenda", usaTRGScheduleCalendar.
A escolha entre os dois não é por tipo de profissional/plano (o spec já suspeitava disso e pediu verificação — a suspeita tinha fundamento, só que a variável real é outra). É por quem está vendo a página:
```ruby # trgclub-api/app/models/therapist_sections_manager.rb SECTIONS = { therapist: [“personal_contact”], patient: [“scheduling”] }.freeze
def sections return SECTIONS[:therapist] if user_logged? && user.is_therapist? SECTIONS[:patient] end ```
Chamado em Api::V1::TherapistsController com current_user = o visitante, não o dono do perfil. Ou seja: hoje, um terapeuta logado vendo qualquer perfil público vê o card de contato; um visitante anônimo ou paciente vê a Agenda — independente de quem seja o profissional.
No frontend isso é consumido em PublicProfile.tsx (showTerapistContact / showSchedulling), com um fallback controlado pela feature flag Flag.COST_REDUCTION_PHASE_ONE: se a flag estiver desligada, sempre mostra Agenda, ignorando a lógica de seções.
| As duas telas de referência (Vilson e Maria) muito provavelmente diferem pela sessão de quem tirou o print (terapeuta logado vs. não), não por serem profissionais de tiers diferentes. O badge “Master | CITRG” é outra lógica, completamente desacoplada (user_has_valid_master_certificate? em user_profile.rb, só afeta o selo no header, não a sidebar). |
Consequência prática: esta não é uma história de “criar componente novo”. É uma história de generalizar uma migração que já está em andamento — terminar de ligar a flag pra 100% dos usuários e simplificar TherapistSectionsManager pra sempre retornar contato, removendo a Agenda desta tela pra qualquer visitante.
Situação das outras 3 histórias do spec original
| História | Spec original assumia | Realidade no código |
|---|---|---|
| 2 — Vídeo | Feature nova | Já implementada por completo: campo video_url em UserProfile, upload via UserVideo.tsx, renderização com link “Assista meu vídeo” + modal em TerapeutaCardHeader.tsx. Provavelmente não precisa de código novo — só confirmar que está ativa pra todos os perfis. |
| 3 — Truncamento de bio | Componente novo | Componente reutilizável já existe: src/components/ui/TrucatedText/index.tsx (Typography com line-clamp + prop lines). O trabalho real é aplicar esse componente no campo de bio do perfil público, não construir um novo. |
| 4 — Bandeiras de idioma | Componente novo | Confirmado como novo de fato. Não existe mapa idioma→bandeira em nenhum lugar do frontend. Hoje idiomas são só texto (languages: string[] em TerapeutaCard.tsx). |
Outros pontos de atenção encontrados
- Nomenclatura: o use case sugerido no spec,
exibir_informacoes_contato.rb, viola a convenção do projeto (CLAUDE.md exige inglês; módulos seguem padrãoNamespace::Action, ex.:Invitations::AcceptFlow,UserProfiles::Update). Nome correto seria algo comoTherapistContacts::Show. - Agenda é reaproveitada em outro lugar (confirma a hipótese do próprio spec):
Availability,AvailabilityTool::*,Availabilities::FindByUser/DestroyByUsereTherapistAvailableTimes::UpdateByProfilesão usados no fluxo de agendamento de reuniões e na configuração de disponibilidade do terapeuta (onboarding). Não dá pra depreciar o backend — a história é só “esconder/substituir componente nessa view”, exatamente como o spec previu como plano B. - Não existe campo
whatsappdedicado — o link é derivado do campophone(+phone_country). Vale confirmar que todophonesalvo tem o formato certo (DDI + DDD + número, sem formatação) antes de virar linkwa.me, senão o botão pode gerar link quebrado pra números antigos sem DDI. - Não existe utilitário compartilhado pra montar link do WhatsApp — está duplicado em pelo menos 3 lugares (
TerapistContact.tsx,Menu/constants.ts,Footer/constants.ts). Boa oportunidade de extrair, mas fora do escopo se quiser manter o PR pequeno.
User Story (revisada): Card de contato sempre visível no perfil do profissional
Como visitante da página de perfil de qualquer profissional (seja terapeuta logado, paciente logado ou anônimo) Quero ver sempre o card “Informações de contato” (telefone, localização, e-mail, WhatsApp) na sidebar Para conseguir avaliar e falar com o profissional diretamente, sem depender do widget de agendamento automático, independente de quem estou logado como
Necessidade
Hoje o card de contato só aparece para um subconjunto de visitantes (terapeutas logados); todo o resto vê a Agenda. Isso limita o alcance da necessidade original: permitir que qualquer visitante avalie e contate o profissional sem depender do agendamento automático.
Output (definição de pronto)
TherapistSectionsManager#sectionsdeixa de depender decurrent_user(visitante) — sempre retorna["personal_contact"], para qualquer tipo de visitante (anônimo, paciente, terapeuta).- Renomear/ajustar o método e a classe se o nome
TherapistSectionsManager/SECTIONSdeixar de fazer sentido sem a branch de paciente (evitar código morto — remover a chave:patiente a lógica deschedulingse não for usada em mais nenhum lugar). - Frontend: remover o fallback controlado por
Flag.COST_REDUCTION_PHASE_ONEemPublicProfile.tsx— a flag deixa de ser condição para mostrar contato; vira comportamento único. ProfileScheduler.tsxdeixa de ser importado/renderizado emPublicProfile.tsx(mas o componente e toda a stack deAvailability/AvailabilityTool::*permanecem intactos — são usados no fluxo de agendamento de reuniões e na configuração de disponibilidade do terapeuta).- Testes: request spec cobrindo os 3 papéis de visitante (anônimo, paciente, terapeuta) recebendo
personal_contactem todos os casos. - QA manual: validar formato do link
wa.mepara telefones salvos sem DDI (dado legado). - Sem use case novo necessário do lado de “montar dados do card” —
TerapistContact.tsxjá consome os dados existentes deUserProfile/User/UserAddress. O trabalho é majoritariamente remoção/simplificação de condicional, não criação.
Cenários Gherkin
```gherkin Cenário: Visitante anônimo vê o card de contato Dado que não estou autenticado Quando acesso a página de perfil de qualquer profissional Então devo ver o card “Informações de contato” na lateral direita E não devo ver o widget de “Agenda” com calendário
Cenário: Paciente logado vê o card de contato Dado que estou autenticado como paciente Quando acesso a página de perfil de qualquer profissional Então devo ver o card “Informações de contato” na lateral direita E não devo ver o widget de “Agenda” com calendário
Cenário: Terapeuta logado vê o card de contato (comportamento já existente, mantido) Dado que estou autenticado como terapeuta Quando acesso a página de perfil de qualquer profissional Então devo ver o card “Informações de contato” na lateral direita
Cenário: Botão de WhatsApp Dado que estou no card de informações de contato Quando clico no botão “WhatsApp” Então devo ser redirecionado para o WhatsApp com o número do profissional já preenchido, incluindo o DDI
Cenário: Feature flag de rollout não altera mais o resultado Dado que a flag “COST_REDUCTION_PHASE_ONE” está desligada para o meu usuário Quando acesso a página de perfil de qualquer profissional Então devo ver o card “Informações de contato” da mesma forma que veria com a flag ligada ```
Histórias 2, 3 e 4 — status revisado (sem necessidade de reescrever o spec original)
Os cenários Gherkin das Histórias 2, 3 e 4 do spec original continuam válidos como critério de aceite — a diferença é o tamanho do trabalho, não o comportamento esperado:
- História 2 (vídeo): tratar como verificação, não implementação. Rodar os cenários já escritos no spec original contra o ambiente atual antes de abrir qualquer PR; é provável que já passem.
- História 3 (truncamento de bio): o “Output” muda de “criar
<TruncatedText />” para “importarTrucatedTextdesrc/components/ui/TrucatedTexte aplicá-lo ao campo de bio emPublicProfile”. Vale corrigir o typo da pasta (Trucated→Truncated) no mesmo PR ou abrir um ticket separado de cleanup. - História 4 (bandeiras): único item do spec original que é 100% trabalho novo, como descrito. Mantém o use case/componente sugerido (
<LanguageTag idioma="pt-BR" />+ mapa centralizado idioma→bandeira).