feat: agenda polish, SLA sync, filters

This commit is contained in:
Esdras Renan 2025-11-08 02:34:43 -03:00
parent 7fb6c65d9a
commit 6ab8a6ce89
40 changed files with 2771 additions and 154 deletions

View file

@ -71,6 +71,37 @@ function normalizeStatus(status) {
return STATUS_MAP[key] ?? "PENDING"
}
function serializeConvexSlaSnapshot(snapshot) {
if (!snapshot || typeof snapshot !== "object") return null
const categoryIdRaw = snapshot.categoryId
let categoryId
if (typeof categoryIdRaw === "string") {
categoryId = categoryIdRaw
} else if (categoryIdRaw && typeof categoryIdRaw === "object" && "_id" in categoryIdRaw) {
categoryId = categoryIdRaw._id
}
const pauseStatuses =
Array.isArray(snapshot.pauseStatuses) && snapshot.pauseStatuses.length > 0
? snapshot.pauseStatuses.filter((value) => typeof value === "string")
: undefined
const normalized = {
categoryId,
categoryName: typeof snapshot.categoryName === "string" ? snapshot.categoryName : undefined,
priority: typeof snapshot.priority === "string" ? snapshot.priority : undefined,
responseTargetMinutes: typeof snapshot.responseTargetMinutes === "number" ? snapshot.responseTargetMinutes : undefined,
responseMode: typeof snapshot.responseMode === "string" ? snapshot.responseMode : undefined,
solutionTargetMinutes: typeof snapshot.solutionTargetMinutes === "number" ? snapshot.solutionTargetMinutes : undefined,
solutionMode: typeof snapshot.solutionMode === "string" ? snapshot.solutionMode : undefined,
alertThreshold: typeof snapshot.alertThreshold === "number" ? snapshot.alertThreshold : undefined,
pauseStatuses,
}
const hasValues = Object.values(normalized).some((value) => value !== undefined)
return hasValues ? normalized : null
}
async function upsertCompanies(snapshotCompanies) {
const map = new Map()
@ -260,6 +291,8 @@ async function upsertTickets(snapshotTickets, userMap, queueMap, companyMap) {
const desiredAssigneeEmail = defaultAssigneeEmail || normalizeEmail(ticket.assigneeEmail)
const assigneeId = desiredAssigneeEmail ? userMap.get(desiredAssigneeEmail) || fallbackAssigneeId || null : fallbackAssigneeId || null
const slaSnapshot = serializeConvexSlaSnapshot(ticket.slaSnapshot)
const existing = await prisma.ticket.findFirst({
where: {
tenantId,
@ -283,6 +316,14 @@ async function upsertTickets(snapshotTickets, userMap, queueMap, companyMap) {
createdAt: toDate(ticket.createdAt) ?? new Date(),
updatedAt: toDate(ticket.updatedAt) ?? new Date(),
companyId,
slaSnapshot: slaSnapshot ?? null,
slaResponseDueAt: toDate(ticket.slaResponseDueAt),
slaSolutionDueAt: toDate(ticket.slaSolutionDueAt),
slaResponseStatus: typeof ticket.slaResponseStatus === "string" ? ticket.slaResponseStatus : null,
slaSolutionStatus: typeof ticket.slaSolutionStatus === "string" ? ticket.slaSolutionStatus : null,
slaPausedAt: toDate(ticket.slaPausedAt),
slaPausedBy: typeof ticket.slaPausedBy === "string" ? ticket.slaPausedBy : null,
slaPausedMs: typeof ticket.slaPausedMs === "number" ? ticket.slaPausedMs : null,
}
let ticketRecord

View file

@ -21,6 +21,29 @@ function slugify(value) {
.replace(/-+/g, "-") || undefined
}
function normalizePrismaSlaSnapshot(snapshot) {
if (!snapshot || typeof snapshot !== "object") return undefined
const record = snapshot
const pauseStatuses =
Array.isArray(record.pauseStatuses) && record.pauseStatuses.length > 0
? record.pauseStatuses.filter((value) => typeof value === "string")
: undefined
const normalized = {
categoryId: typeof record.categoryId === "string" ? record.categoryId : undefined,
categoryName: typeof record.categoryName === "string" ? record.categoryName : undefined,
priority: typeof record.priority === "string" ? record.priority : undefined,
responseTargetMinutes: typeof record.responseTargetMinutes === "number" ? record.responseTargetMinutes : undefined,
responseMode: typeof record.responseMode === "string" ? record.responseMode : undefined,
solutionTargetMinutes: typeof record.solutionTargetMinutes === "number" ? record.solutionTargetMinutes : undefined,
solutionMode: typeof record.solutionMode === "string" ? record.solutionMode : undefined,
alertThreshold: typeof record.alertThreshold === "number" ? record.alertThreshold : undefined,
pauseStatuses,
}
return Object.values(normalized).some((value) => value !== undefined) ? normalized : undefined
}
async function main() {
const tenantId = process.env.SYNC_TENANT_ID || "tenant-atlas"
const convexUrl = process.env.NEXT_PUBLIC_CONVEX_URL || "http://127.0.0.1:3210"
@ -103,6 +126,14 @@ async function main() {
createdAt: toMillis(ticket.createdAt) ?? Date.now(),
updatedAt: toMillis(ticket.updatedAt) ?? Date.now(),
tags: Array.isArray(ticket.tags) ? ticket.tags : undefined,
slaSnapshot: normalizePrismaSlaSnapshot(ticket.slaSnapshot),
slaResponseDueAt: toMillis(ticket.slaResponseDueAt),
slaSolutionDueAt: toMillis(ticket.slaSolutionDueAt),
slaResponseStatus: ticket.slaResponseStatus ?? undefined,
slaSolutionStatus: ticket.slaSolutionStatus ?? undefined,
slaPausedAt: toMillis(ticket.slaPausedAt),
slaPausedBy: ticket.slaPausedBy ?? undefined,
slaPausedMs: typeof ticket.slaPausedMs === "number" ? ticket.slaPausedMs : undefined,
comments: ticket.comments.map((comment) => ({
authorEmail: comment.author?.email ?? requesterEmail,
visibility: comment.visibility,