feat: allow company deletion by detaching dependents

This commit is contained in:
Esdras Renan 2025-10-16 22:28:12 -03:00
parent 2980885bf8
commit 7951bc25a3
3 changed files with 32 additions and 21 deletions

View file

@ -23,6 +23,7 @@ Resultado: front/back sobem com o novo código sem editar o stack a cada release
- Mount fixo: `/home/renan/apps/sistema.current:/app` (não interpolar APP_DIR).
- Comando inline (sem script), com migrations na subida:
- `command: ["bash","-lc","corepack enable && corepack prepare pnpm@9 --activate && pnpm exec prisma migrate deploy && pnpm start -p 3000"]`
- **Se você optar por usar `/app/scripts/start-web.sh`** (como no workflow atual), ele já garante `pnpm@9` via Corepack/NPM antes de rodar Prisma/Next. Certifique-se de copiar esse arquivo para o build publicado; sem ele, a task cai com `pnpm: command not found`.
- Env obrigatórias (URLs válidas):
- `DATABASE_URL=file:/app/data/db.sqlite`
- `NEXT_PUBLIC_CONVEX_URL=http://sistema_convex_backend:3210`
@ -121,4 +122,3 @@ docker service scale sistema_web=1
- Como o stack monta `/home/renan/apps/sistema.current`, um novo release exige apenas atualizar o symlink e forçar a task. O `stack.yml` só precisa ser redeployado quando você altera labels/envs/serviços.
- Se a UI parecer não mudar, valide o mount/args via inspect, confira logs da task atual e force hardreload no navegador.

View file

@ -77,23 +77,20 @@ export async function DELETE(_: Request, { params }: { params: Promise<{ id: str
}
try {
// Préchecagem para evitar 500 por FK: conta vínculos antes
const [usersCount, ticketsCount] = await Promise.all([
prisma.user.count({ where: { companyId: company.id } }),
prisma.ticket.count({ where: { companyId: company.id } }),
])
if (usersCount > 0 || ticketsCount > 0) {
return NextResponse.json(
{
error: "Não é possível remover esta empresa pois existem registros vinculados.",
details: { users: usersCount, tickets: ticketsCount },
},
{ status: 409 }
)
}
const result = await prisma.$transaction(async (tx) => {
const users = await tx.user.updateMany({
where: { companyId: company.id, tenantId: company.tenantId },
data: { companyId: null },
})
const tickets = await tx.ticket.updateMany({
where: { companyId: company.id, tenantId: company.tenantId },
data: { companyId: null },
})
await tx.company.delete({ where: { id: company.id } })
return { detachedUsers: users.count, detachedTickets: tickets.count }
})
await prisma.company.delete({ where: { id: company.id } })
return NextResponse.json({ ok: true })
return NextResponse.json({ ok: true, ...result })
} catch (error) {
if (error instanceof PrismaClientKnownRequestError && error.code === "P2003") {
return NextResponse.json(

View file

@ -184,11 +184,25 @@ export function AdminCompaniesManager({ initialCompanies }: { initialCompanies:
method: "DELETE",
credentials: "include",
})
if (!response.ok) {
const data = await response.json().catch(() => ({}))
throw new Error(data.error ?? "Falha ao excluir empresa")
const data = (await response.json().catch(() => ({}))) as {
error?: string
detachedUsers?: number
detachedTickets?: number
}
toast.success("Empresa removida")
if (!response.ok) {
throw new Error(data?.error ?? "Falha ao excluir empresa")
}
const detachedUsers = data?.detachedUsers ?? 0
const detachedTickets = data?.detachedTickets ?? 0
const details: string[] = []
if (detachedUsers > 0) {
details.push(`${detachedUsers} usuário${detachedUsers > 1 ? "s" : ""} desvinculado${detachedUsers > 1 ? "s" : ""}`)
}
if (detachedTickets > 0) {
details.push(`${detachedTickets} ticket${detachedTickets > 1 ? "s" : ""} atualizado${detachedTickets > 1 ? "s" : ""}`)
}
const successMessage = details.length > 0 ? `Empresa removida (${details.join(", ")})` : "Empresa removida"
toast.success(successMessage)
if (editingId === deleteId) {
resetForm()
setEditingId(null)