mirror of
https://github.com/melgarafael/DeskcommCRM.git
synced 2026-10-02 01:28:34 +08:00
fix(admin): criação de tenant violava check constraint de status (#28)
POST /api/v1/admin/tenants inseria status 'onboarding', valor que a check constraint de organizations.status (active|suspended|redacted|archived) nunca aceitou — a rota 500ava em qualquer criação. O marcador real de onboarding no produto é onboarded_at null (mesmo modelo do signup self-service e do gate de /onboarding), então: - insert passa a usar status 'active' (onboarded_at null = em onboarding) - filtro GET status=onboarding vira estado derivado: active + onboarded_at is null (o dropdown "Onboarding" do admin, que nunca casava nada, agora funciona) - badge da listagem deriva "Onboarding" de active + onboarded_at null - tipos de linha (useTenantDetail, TenantActions) deixam de aceitar um status que o banco não produz; o tipo de FILTRO mantém 'onboarding' Sem migration: nenhuma linha com status inválido pode existir (a constraint sempre bloqueou o INSERT). Claude-Session: https://claude.ai/code/session_01454SGvQ7wBAvah8ZPnnWXv Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
fd1e291186
commit
73a23750b2
@@ -100,7 +100,10 @@ export async function GET(req: NextRequest) {
|
||||
.order("id", { ascending: false })
|
||||
.limit(limit + 1);
|
||||
|
||||
if (status) {
|
||||
if (status === "onboarding") {
|
||||
// Estado derivado: ativo no banco, onboarding ainda não concluído.
|
||||
query = query.eq("status", "active").is("onboarded_at", null);
|
||||
} else if (status) {
|
||||
query = query.eq("status", status);
|
||||
}
|
||||
|
||||
@@ -195,7 +198,9 @@ export async function POST(req: NextRequest) {
|
||||
slug,
|
||||
legal_name: legal_name ?? null,
|
||||
cnpj: cnpj ?? null,
|
||||
status: "onboarding",
|
||||
// A check constraint de organizations.status não tem 'onboarding' — o
|
||||
// marcador de onboarding é onboarded_at null (mesmo modelo do signup).
|
||||
status: "active",
|
||||
settings: { plan },
|
||||
created_by: adminCtx.user.id,
|
||||
})
|
||||
|
||||
@@ -11,7 +11,7 @@ import { ImpersonateButton } from "@/components/admin/ImpersonateButton";
|
||||
|
||||
interface TenantActionsProps {
|
||||
organizationId: string;
|
||||
status: "active" | "suspended" | "onboarding" | "redacted";
|
||||
status: "active" | "suspended" | "redacted";
|
||||
displayName: string;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ export function TenantActions({
|
||||
const [suspendOpen, setSuspendOpen] = useState(false);
|
||||
const [reactivateOpen, setReactivateOpen] = useState(false);
|
||||
|
||||
const canSuspend = status === "active" || status === "onboarding";
|
||||
const canSuspend = status === "active";
|
||||
const isSuspended = status === "suspended";
|
||||
const isRedacted = status === "redacted";
|
||||
|
||||
|
||||
@@ -35,10 +35,18 @@ const STATUS_LABELS: Record<string, string> = {
|
||||
redacted: "Redigido",
|
||||
};
|
||||
|
||||
function StatusBadge({ status }: { status: string }) {
|
||||
function StatusBadge({
|
||||
status,
|
||||
onboardedAt,
|
||||
}: {
|
||||
status: string;
|
||||
onboardedAt: string | null;
|
||||
}) {
|
||||
// 'onboarding' não existe no banco — é derivado: ativo sem onboarding concluído.
|
||||
const effective = status === "active" && !onboardedAt ? "onboarding" : status;
|
||||
return (
|
||||
<Badge variant={STATUS_VARIANTS[status] ?? "neutral"}>
|
||||
{STATUS_LABELS[status] ?? status}
|
||||
<Badge variant={STATUS_VARIANTS[effective] ?? "neutral"}>
|
||||
{STATUS_LABELS[effective] ?? effective}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
@@ -158,7 +166,7 @@ export function TenantsTable({
|
||||
{shortCnpj(row.cnpj)}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<StatusBadge status={row.status} />
|
||||
<StatusBadge status={row.status} onboardedAt={row.onboarded_at} />
|
||||
</TableCell>
|
||||
<TableCell className="text-right tabular-nums">
|
||||
{extractCount(row.user_count)}
|
||||
|
||||
@@ -12,7 +12,9 @@ export interface TenantOrganization {
|
||||
display_name: string;
|
||||
legal_name: string | null;
|
||||
cnpj: string | null;
|
||||
status: "active" | "suspended" | "onboarding" | "redacted";
|
||||
// 'onboarding' não existe na check constraint do banco — é estado derivado
|
||||
// (active + onboarded_at null), nunca vem numa linha real.
|
||||
status: "active" | "suspended" | "redacted";
|
||||
onboarded_at: string | null;
|
||||
suspended_at: string | null;
|
||||
created_at: string;
|
||||
|
||||
Reference in New Issue
Block a user