Fix form template labels and guard admin auth tables

This commit is contained in:
Esdras Renan 2025-11-08 00:40:32 -03:00
parent 003d068c56
commit 7fb6c65d9a
2 changed files with 113 additions and 65 deletions

View file

@ -80,6 +80,21 @@ function plainTextLength(html: string): number {
} }
} }
function resolveFormTemplateLabel(
templateKey: string | null | undefined,
storedLabel: string | null | undefined
): string | null {
if (storedLabel && storedLabel.trim().length > 0) {
return storedLabel.trim();
}
const normalizedKey = templateKey?.trim();
if (!normalizedKey) {
return null;
}
const fallback = TICKET_FORM_CONFIG.find((entry) => entry.key === normalizedKey);
return fallback ? fallback.label : null;
}
function escapeHtml(input: string): string { function escapeHtml(input: string): string {
return input return input
.replace(/&/g, "&") .replace(/&/g, "&")
@ -1281,8 +1296,8 @@ export const list = query({
csatComment: typeof t.csatComment === "string" && t.csatComment.trim().length > 0 ? t.csatComment.trim() : null, csatComment: typeof t.csatComment === "string" && t.csatComment.trim().length > 0 ? t.csatComment.trim() : null,
csatRatedAt: t.csatRatedAt ?? null, csatRatedAt: t.csatRatedAt ?? null,
csatRatedBy: t.csatRatedBy ? String(t.csatRatedBy) : null, csatRatedBy: t.csatRatedBy ? String(t.csatRatedBy) : null,
formTemplate: t.formTemplate ?? null, formTemplate: t.formTemplate ?? null,
formTemplateLabel: t.formTemplateLabel ?? null, formTemplateLabel: resolveFormTemplateLabel(t.formTemplate ?? null, t.formTemplateLabel ?? null),
company: company company: company
? { id: company._id, name: company.name, isAvulso: company.isAvulso ?? false } ? { id: company._id, name: company.name, isAvulso: company.isAvulso ?? false }
: t.companyId || t.companySnapshot : t.companyId || t.companySnapshot
@ -1592,7 +1607,7 @@ export const getById = query({
})), })),
}, },
formTemplate: t.formTemplate ?? null, formTemplate: t.formTemplate ?? null,
formTemplateLabel: t.formTemplateLabel ?? null, formTemplateLabel: resolveFormTemplateLabel(t.formTemplate ?? null, t.formTemplateLabel ?? null),
chatEnabled: Boolean(t.chatEnabled), chatEnabled: Boolean(t.chatEnabled),
relatedTicketIds: Array.isArray(t.relatedTicketIds) ? t.relatedTicketIds.map((id) => String(id)) : [], relatedTicketIds: Array.isArray(t.relatedTicketIds) ? t.relatedTicketIds.map((id) => String(id)) : [],
resolvedWithTicketId: t.resolvedWithTicketId ? String(t.resolvedWithTicketId) : null, resolvedWithTicketId: t.resolvedWithTicketId ? String(t.resolvedWithTicketId) : null,

View file

@ -1,3 +1,5 @@
import { Prisma } from "@prisma/client"
import { AdminUsersManager } from "@/components/admin/admin-users-manager" import { AdminUsersManager } from "@/components/admin/admin-users-manager"
import { AppShell } from "@/components/app-shell" import { AppShell } from "@/components/app-shell"
import { SiteHeader } from "@/components/site-header" import { SiteHeader } from "@/components/site-header"
@ -10,80 +12,111 @@ import { getServerSession } from "@/lib/auth-server"
export const runtime = "nodejs" export const runtime = "nodejs"
export const dynamic = "force-dynamic" export const dynamic = "force-dynamic"
async function loadUsers() { function isMissingAuthTableError(error: unknown, table: string): boolean {
const users = await prisma.authUser.findMany({ if (!(error instanceof Prisma.PrismaClientKnownRequestError)) {
orderBy: { createdAt: "desc" }, return false
select: { }
id: true, if (error.code !== "P2021" && error.code !== "P2023") {
email: true, return false
name: true, }
role: true, const target = typeof error.meta?.table === "string" ? error.meta.table.toLowerCase() : ""
tenantId: true, return target.includes(table.toLowerCase())
machinePersona: true, }
createdAt: true,
updatedAt: true,
},
})
const domainUsers = await prisma.user.findMany({ async function loadUsers() {
select: { try {
email: true, const users = await prisma.authUser.findMany({
companyId: true, orderBy: { createdAt: "desc" },
company: { select: {
select: { id: true,
id: true, email: true,
name: true, name: true,
role: true,
tenantId: true,
machinePersona: true,
createdAt: true,
updatedAt: true,
},
})
if (users.length === 0) {
return []
}
const domainUsers = await prisma.user.findMany({
select: {
email: true,
companyId: true,
company: {
select: {
id: true,
name: true,
},
}, },
}, },
}, })
})
const domainByEmail = new Map<string, (typeof domainUsers)[number]>( const domainByEmail = new Map<string, (typeof domainUsers)[number]>(
domainUsers.map( domainUsers.map(
(user: (typeof domainUsers)[number]): [string, (typeof domainUsers)[number]] => [ (user: (typeof domainUsers)[number]): [string, (typeof domainUsers)[number]] => [
user.email.toLowerCase(), user.email.toLowerCase(),
user, user,
] ]
)
) )
)
return users.map((user: (typeof users)[number]) => { return users.map((user: (typeof users)[number]) => {
const domain = domainByEmail.get(user.email.toLowerCase()) const domain = domainByEmail.get(user.email.toLowerCase())
const normalizedRole = (normalizeRole(user.role) ?? "agent") as RoleOption const normalizedRole = (normalizeRole(user.role) ?? "agent") as RoleOption
return { return {
id: user.id, id: user.id,
email: user.email, email: user.email,
name: user.name ?? "", name: user.name ?? "",
role: normalizedRole, role: normalizedRole,
tenantId: user.tenantId ?? DEFAULT_TENANT_ID, tenantId: user.tenantId ?? DEFAULT_TENANT_ID,
createdAt: user.createdAt.toISOString(), createdAt: user.createdAt.toISOString(),
updatedAt: user.updatedAt?.toISOString() ?? null, updatedAt: user.updatedAt?.toISOString() ?? null,
companyId: domain?.companyId ?? null, companyId: domain?.companyId ?? null,
companyName: domain?.company?.name ?? null, companyName: domain?.company?.name ?? null,
machinePersona: user.machinePersona ?? null, machinePersona: user.machinePersona ?? null,
}
})
} catch (error) {
if (isMissingAuthTableError(error, "AuthUser")) {
console.warn("[admin] auth tables missing; returning empty user list")
return []
} }
}) throw error
}
} }
async function loadInvites(): Promise<NormalizedInvite[]> { async function loadInvites(): Promise<NormalizedInvite[]> {
const invites = await prisma.authInvite.findMany({ try {
orderBy: { createdAt: "desc" }, const invites = await prisma.authInvite.findMany({
include: { orderBy: { createdAt: "desc" },
events: { include: {
orderBy: { createdAt: "asc" }, events: {
orderBy: { createdAt: "asc" },
},
}, },
},
})
const now = new Date()
const cutoff = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)
return invites
.map((invite: (typeof invites)[number]) => normalizeInvite(invite, now))
.filter((invite: NormalizedInvite) => {
if (invite.status !== "revoked") return true
if (!invite.revokedAt) return true
return new Date(invite.revokedAt) > cutoff
}) })
const now = new Date()
const cutoff = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)
return invites
.map((invite) => normalizeInvite(invite, now))
.filter((invite: NormalizedInvite) => {
if (invite.status !== "revoked") return true
if (!invite.revokedAt) return true
return new Date(invite.revokedAt) > cutoff
})
} catch (error) {
if (isMissingAuthTableError(error, "AuthInvite")) {
console.warn("[admin] auth invite tables missing; returning empty invite list")
return []
}
throw error
}
} }
export default async function AdminPage() { export default async function AdminPage() {