Correcao Sidebar Classico e Rail, Correcao Layout, Ajuste de Breakpoint para Tailwind, Ajuste AppTopbar, Ajuste Menu PopOver, Recriado Paleta de Cores, Inserido algumas animações leves, Reajuste Cor items NOVOS da tabela, Drawer Ajuda Corrigido no Logout, Whatsapp, sms, email, recursos extras

This commit is contained in:
Leonardo
2026-03-24 21:26:58 -03:00
parent a89d1f5560
commit 53a4980396
453 changed files with 121427 additions and 174407 deletions

View File

@@ -0,0 +1,206 @@
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Agência PSI — Billing & Subscriptions v1.2</title>
<style>
body{
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial;
margin:0;
padding:40px;
background:#f6f8fc;
color:#0f172a;
}
h1{font-size:28px;margin-bottom:8px;}
h2{margin-top:40px;font-size:20px;}
h3{margin-top:24px;font-size:16px;}
p{line-height:1.6;color:#334155;}
pre{
background:#0f172a;
color:#e2e8f0;
padding:16px;
border-radius:12px;
overflow:auto;
}
code{font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;}
.section{margin-bottom:40px;}
.badge{
display:inline-block;
padding:4px 10px;
border-radius:999px;
background:#e2e8f0;
font-size:12px;
margin-right:6px;
}
.rule{
background:#e0f2fe;
padding:14px;
border-left:4px solid #0284c7;
border-radius:10px;
margin-top:12px;
}
.warn{
background:#fef3c7;
padding:14px;
border-left:4px solid #d97706;
border-radius:10px;
margin-top:12px;
}
.danger{
background:#fee2e2;
padding:14px;
border-left:4px solid #dc2626;
border-radius:10px;
margin-top:12px;
}
.ok{
background:#dcfce7;
padding:14px;
border-left:4px solid #16a34a;
border-radius:10px;
margin-top:12px;
}
footer{
margin-top:60px;
font-size:12px;
color:#64748b;
}
</style>
</head>
<body>
<h1>Billing & Subscriptions — v1.2</h1>
<p><strong>Agência PSI</strong> — Documento consolidado da sessão técnica sobre Subscriptions, Guardrails e Seeder.</p>
<div class="section">
<h2>1. Escopo desta versão</h2>
<p>Este documento consolida tudo o que foi tratado nesta sessão:</p>
<ul>
<li>Modelagem real da tabela <code>subscriptions</code></li>
<li>Histórico via <code>subscription_events</code></li>
<li>Triggers (guardrails) de proteção</li>
<li>Views oficiais</li>
<li>Seeder completo (planos + preços + metadata pública)</li>
<li>Erros reais encontrados e solução aplicada</li>
</ul>
</div>
<div class="section">
<h2>2. Estrutura confirmada — subscriptions</h2>
<pre><code>id uuid PK
tenant_id uuid NULL
user_id uuid NULL
plan_id uuid NOT NULL
plan_key text NULL
interval text CHECK ('month','year')
status text DEFAULT 'active'
current_period_start timestamptz
current_period_end timestamptz
provider text DEFAULT 'manual'
cancel_at_period_end boolean DEFAULT false
created_at timestamptz DEFAULT now()
updated_at timestamptz DEFAULT now()</code></pre>
<div class="rule">
Assinatura de clínica exige <strong>tenant_id</strong>.
Assinatura de terapeuta pode usar <strong>user_id</strong>.
</div>
</div>
<div class="section">
<h2>3. Guardrails (Proteções de Integridade)</h2>
<h3>3.1 Impedir deletar planos core</h3>
<pre><code>create or replace function guard_no_delete_core_plans()
returns trigger language plpgsql as $$
begin
if old.key in ('clinic_free','clinic_pro','therapist_free','therapist_pro') then
raise exception 'Plano padrão (%) não pode ser removido.', old.key;
end if;
return old;
end $$;</code></pre>
<h3>3.2 Impedir alterar target</h3>
<pre><code>create or replace function guard_no_change_plan_target()
returns trigger language plpgsql as $$
begin
if new.target is distinct from old.target then
raise exception 'Não é permitido alterar target do plano.';
end if;
return new;
end $$;</code></pre>
<h3>3.3 Impedir alterar key core</h3>
<pre><code>create or replace function guard_no_change_core_plan_key()
returns trigger language plpgsql as $$
begin
if old.key in ('clinic_free','clinic_pro','therapist_free','therapist_pro')
and new.key is distinct from old.key then
raise exception 'Não é permitido alterar a key do plano padrão.';
end if;
return new;
end $$;</code></pre>
<div class="warn">
Esses guardrails impediram alterações indevidas quando tentamos renomear planos core.
</div>
</div>
<div class="section">
<h2>4. Views Oficiais</h2>
<p><strong>v_public_pricing</strong> — Tela pública de preços.</p>
<p><strong>v_tenant_active_subscription</strong> — Plano vigente do tenant.</p>
<p><strong>v_subscription_health</strong> — Diagnóstico de inconsistências.</p>
</div>
<div class="section">
<h2>5. Seeder Oficial (MVP)</h2>
<pre><code>insert into plans (key,name,target,is_active)
values
('clinic_free','Clinic Free','clinic',true),
('clinic_pro','Clinic Pro','clinic',true),
('therapist_free','Therapist Free','therapist',true),
('therapist_pro','Therapist Pro','therapist',true)
on conflict (key) do update set
name=excluded.name,
target=excluded.target,
is_active=excluded.is_active;</code></pre>
<div class="ok">
Seeder é idempotente. Pode rodar múltiplas vezes sem duplicar.
</div>
</div>
<div class="section">
<h2>6. Incidentes reais resolvidos</h2>
<h3>6.1 Pricing retornando null</h3>
<p>Causa: não havia preço vigente (is_active=true e active_to is null).</p>
<h3>6.2 Erro ao alterar plano padrão</h3>
<p>Causa: trigger guard_no_change_core_plan_key bloqueando alteração.</p>
<h3>6.3 Assinatura sem tenant_id</h3>
<p>Causa: regra de negócio no banco impedindo clinic sem tenant.</p>
</div>
<div class="section">
<h2>7. Diretrizes finais</h2>
<ul>
<li>Plano nunca deve ser inferido do role.</li>
<li>UI deve consumir apenas views oficiais.</li>
<li>Plano core nunca deve ser renomeado.</li>
<li>Preço sempre deve ter vigência ativa.</li>
</ul>
</div>
<footer>
Agência PSI — Billing & Subscriptions v1.2<br>
Documento gerado automaticamente.
</footer>
</body>
</html>