Suspensão temporária do agendamento via feature flag
TLDR: uma flag
scheduling_suspensionque, ligada, exibe oTopBannercom o aviso “O agendamento em nosso sistema precisará ser suspenso por 24h.” e faz todo agendador do front se comportar como se o terapeuta não tivesse horários disponíveis — para clientes, terapeutas e visitantes deslogados.
Contexto
O agendamento precisará ser suspenso por 24h. Durante esse período os usuários devem ser avisados e não podem conseguir marcar nem reagendar sessões, e a suspensão precisa ser ligada e desligada sem deploy.
O TopBanner (#505, #510) já existe, mas saiu do Header no #547 junto com a flag COST_REDUCTION_PHASE_ONE, e o MobileMenu perdeu o ajuste de posição do popover quando o banner está visível.
Todo fluxo de escolha de horário passa pelo hook useScheduler (src/hooks/useScheduler.ts), via UseSchedulerProvider:
- perfil público do terapeuta (
PublicProfile→ProfileScheduler) - modal de agendamento do card de terapeuta (
TerapeutaCard→withScheduler) - “Agendar Nova Sessão” em Meus Terapeutas (
pages/app/meus-terapeutas/[id].tsx) - “Reagendar Sessão” no painel, Minhas Sessões e Minha Agenda (
ReschedulingSession) - “Agendar sessão” do voucher na carteira (
ReschedulingVoucher)
Quando availableDays está vazio, esses agendadores já têm uma variante própria: exibem “Sem horários disponíveis para estas datas.” (NO_AVAILABLE_DAYS) e o botão “Confirmar Agendamento” fica desabilitado. A suspensão reaproveita essa variante em vez de esconder telas.
Os flags vêm do trgclub-api (GET /features/:user_id, sem autenticação), que lista as features do Flipper. Hoje FeatureFlag::FlipperAdapter#user_features retorna [] quando não há usuário, e o useFeature do front só busca flags com usuário logado — então visitantes deslogados nunca recebem flag ligada.
Objetivos
- Uma única flag
scheduling_suspension(Flag.SCHEDULING_SUSPENSION) controla o banner e a suspensão. - Banner — com a flag ligada, o
Headerrenderiza oTopBanner:description: “O agendamento em nosso sistema precisará ser suspenso por 24h.”- sem link (
labelehrefpassam a ser opcionais noTopBanner) - chave de persistência muda de
top-banner-storageparatop-banner-storage-v2, para reaparecer a quem dispensou o banner antigo - regras atuais do
useTopBannermantidas (só autenticado, fora das rotas excluídas, dismiss persistido)
- Responsividade — com a flag ligada e o banner visível, o popover do
MobileMenuvolta a usarmarginTop: 1.5rem; abaixo de391px,marginTop: 2rememaxHeight: calc(100vh - 10rem). Caso contrário,1rem. - Agendamento — com a flag ligada,
useSchedulernão busca disponibilidade e retornaavailableDaysvazio. Todos os agendadores listados no contexto passam a exibir a variante “Sem horários disponíveis para estas datas.” e não permitem confirmar. - Deslogados — a flag também vale para visitantes sem login (perfil público
/terapeuta/[slug]):- front:
useFeaturepassa a buscar/features/anonymousquando não há usuário, comqueryKey["features", userId || "anonymous"] - backend (
trgclub-api, PR separado):FlipperAdapter#user_featuresretorna as features habilitadas globalmente (Flipper.enabled?(feature)) quando não há usuário - a flag precisa estar ligada globalmente (“Fully enabled”) no Flipper para valer para deslogados
- front:
Fora de escopo
- Criar/ligar a flag no Flipper — feito manualmente em
/features_uidotrgclub-api. - Esconder menus, botões ou páginas (Minha Agenda, Minha Disponibilidade, carteira, checkout): continuam visíveis; só os horários aparecem como indisponíveis.
- Tela de disponibilidade do terapeuta (
/app/minha-disponibilidade) e onboarding de disponibilidade: não mudam — o terapeuta segue podendo configurar, só ninguém consegue marcar. - Bloqueio no backend (criação de meeting, reagendamento, voucher, checkout por URL já montada) — a suspensão é apenas de interface.
- Alterar estilos do banner, rotas excluídas ou a página
/novidades.
Mudanças
trgclub-web
src/services/feature/types.ts—SCHEDULING_SUSPENSION = "scheduling_suspension"no enumFlag.src/infra/FeatureManager/useFeature.ts— buscar/features/anonymoussem usuário;queryKey["features", userId || "anonymous"]; removerenabled: !!userId.src/hooks/useScheduler.ts— leruseFeature().isEnabled(Flag.SCHEDULING_SUSPENSION); quando ligada, query de disponibilidade comenabled: falseeavailableDaysretornado como[].src/components/TopBanner/index.tsx—labelehrefopcionais;S.Linksó renderiza quando ambos existem.src/stores/topBanner/topBanner.ts—name: 'top-banner-storage-v2'.src/components/Header/Header.tsx—TopBannerdentro de<Feature flag={Flag.SCHEDULING_SUSPENSION}><Feature.On>, logo após{withFlags && <TRGFlags />}.src/components/MobileMenu/MobileMenu.tsx—MobileMenuWithPopovervolta a calcularisEnabled(Flag.SCHEDULING_SUSPENSION) && shouldShowe monta oPopoverComponentcomReact.useMemo, aplicando os offsets acima.- Testes:
useScheduler(flag ligada → sem fetch eavailableDaysvazio; desligada → comportamento atual),useFeature(anônimo busca/features/anonymous),TopBanner(com e sem link),topBanner.test.tsse referenciar a chave.
trgclub-api (PR separado)
app/services/feature_flag/flipper_adapter.rb— sem usuário,user_featuresretornaall_features.select { |f| Flipper.enabled?(f) }.- Spec cobrindo usuário ausente com feature ligada globalmente e com feature ligada só por ator.
Como verificar
- Flag desligada ou inexistente: nenhum banner; agendadores mostram horários normalmente;
MobileMenucommarginTop: 1rem. - Flag ligada globalmente:
- logado (cliente ou terapeuta) em
/app/...: banner aparece sem link; ao fechar, some e não volta após reload; quem tinha fechado o banner antigo vê o novo - perfil público
/terapeuta/[slug]logado e deslogado: “Sem horários disponíveis para estas datas.” e “Confirmar Agendamento” desabilitado - modal do card de terapeuta, “Agendar Nova Sessão”, “Reagendar Sessão” (painel, Minhas Sessões, Minha Agenda) e voucher da carteira: mesma variante sem horários
MobileMenucom banner visível: popover abaixo do banner sem sobreposição em desktop e em viewport < 391px
- logado (cliente ou terapeuta) em
- Desligar a flag restaura os horários sem deploy (após o
staleTimede flags ou reload). - Testes,
lintetypechecksem erros nos dois repos.
Documentação
- Criar
.project/docs/rules/scheduling/scheduling_suspension.mddescrevendo a regra da flagscheduling_suspension(o que muda para cliente, terapeuta e deslogado) e registrar no índice.project/docs/README.md.