feat(frontend): Platform > Clientes > Assinaturas/Quotas + achado real no dashboard
GET /billing/subscriptions e POST /billing/plan-versions já existiam desde a fase Billing (PHASE 22) sem tela nenhuma — Assinaturas agora deixa escolher um tenant, ver o histórico de versões de preço assinadas, e criar uma nova assinatura reaproveitando uma versão existente ou versionando um preço novo na mesma ação (preço nunca é sobrescrito, sempre uma linha nova). GET /platform/quotas (novo) agrega uso vs. limite do plano em todos os tenants de uma vez (ramais/agentes/troncos/filas/campanhas/chamadas-mês/armazenamento), com a tela destacando quem está em 80%+ (amarelo) ou 100%+ (vermelho) do limite. Achado real ao revisar o dashboard "Visão Geral" antes de escrever a agregação cross-tenant de Quotas: aiUsageThisMonth/recordingStorageBytes sempre devolviam zero/vazio, porque a query rodava direto no Prisma sem nenhum app.current_tenant_id setado — ai_usage_records/recordings têm FORCE RLS, então a policy nega a leitura silenciosamente (0 linhas, sem erro), não importa quanto uso real existisse. Mesma classe de bug já corrigida 2x antes nesta sessão; corrigido com o mesmo padrão (loop withTenantContext por tenant). Confirmado inserindo um AIUsageRecord de teste no Postgres, vendo o número aparecer, e removendo o teste depois. Testado ponta a ponta: fluxo completo de criar assinatura via UI pro tenant Beta Corp (nova versão de preço + assinatura, confirmado na tela e no banco), Quotas mostrando os números reais dos dois tenants de teste. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFaBaBSQGhyXGEgtTYZGV8
This commit is contained in:
@@ -92,3 +92,33 @@ export interface BillingStatement {
|
||||
billingPeriod: BillingPeriod;
|
||||
items?: BillingStatementItem[];
|
||||
}
|
||||
|
||||
export interface PlanVersion {
|
||||
id: string;
|
||||
planId: string;
|
||||
version: number;
|
||||
basePrice: number;
|
||||
currency: string;
|
||||
effectiveFrom: string;
|
||||
effectiveUntil: string | null;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export const TENANT_SUBSCRIPTION_STATUS_LABELS: Record<string, string> = {
|
||||
TRIALING: "Em trial",
|
||||
ACTIVE: "Ativa",
|
||||
PAST_DUE: "Inadimplente",
|
||||
CANCELED: "Cancelada",
|
||||
};
|
||||
|
||||
export interface TenantSubscription {
|
||||
id: string;
|
||||
tenantId: string;
|
||||
planVersionId: string;
|
||||
status: string;
|
||||
startedAt: string;
|
||||
endsAt: string | null;
|
||||
billingCycleAnchor: number;
|
||||
currency: string;
|
||||
planVersion: PlanVersion;
|
||||
}
|
||||
|
||||
@@ -83,6 +83,22 @@ export interface PlatformHealth {
|
||||
checkedAt: string;
|
||||
}
|
||||
|
||||
export interface QuotaItem {
|
||||
key: string;
|
||||
label: string;
|
||||
used: number;
|
||||
max: number | null;
|
||||
ratio: number | null;
|
||||
}
|
||||
|
||||
export interface TenantQuotas {
|
||||
tenantId: string;
|
||||
legalName: string;
|
||||
planName: string;
|
||||
status: string;
|
||||
items: QuotaItem[];
|
||||
}
|
||||
|
||||
export const PLAN_LIMIT_FIELDS: { key: keyof Plan; label: string }[] = [
|
||||
{ key: "maxExtensions", label: "Ramais" },
|
||||
{ key: "maxAgents", label: "Agentes" },
|
||||
|
||||
Reference in New Issue
Block a user