From 5de8b2bf7f23a386da2ad2f2c81e54c8b060a553 Mon Sep 17 00:00:00 2001 From: Esdras Renan Date: Sat, 18 Oct 2025 21:57:13 -0300 Subject: [PATCH] fix: align company data with machines --- convex/migrations.ts | 65 +++++++++++++++++++ .../companies/admin-companies-manager.tsx | 65 +++++++++++++++---- .../tickets/recent-tickets-panel.tsx | 16 ++--- 3 files changed, 124 insertions(+), 22 deletions(-) diff --git a/convex/migrations.ts b/convex/migrations.ts index 48eac85..f6cc55f 100644 --- a/convex/migrations.ts +++ b/convex/migrations.ts @@ -641,3 +641,68 @@ export const importPrismaSnapshot = mutation({ } }, }) + +export const syncMachineCompanyReferences = mutation({ + args: { + tenantId: v.optional(v.string()), + dryRun: v.optional(v.boolean()), + }, + handler: async (ctx, { tenantId, dryRun }) => { + const effectiveDryRun = Boolean(dryRun) + + const machines = tenantId && tenantId.trim().length > 0 + ? await ctx.db + .query("machines") + .withIndex("by_tenant", (q) => q.eq("tenantId", tenantId)) + .collect() + : await ctx.db.query("machines").collect() + + const slugCache = new Map | null>() + const summary = { + total: machines.length, + updated: 0, + skippedMissingSlug: 0, + skippedMissingCompany: 0, + alreadyLinked: 0, + } + + for (const machine of machines) { + const slug = machine.companySlug ?? null + if (!slug) { + summary.skippedMissingSlug += 1 + continue + } + + const cacheKey = `${machine.tenantId}::${slug}` + let companyId = slugCache.get(cacheKey) + if (companyId === undefined) { + const company = await ctx.db + .query("companies") + .withIndex("by_tenant_slug", (q) => q.eq("tenantId", machine.tenantId).eq("slug", slug)) + .unique() + companyId = company?._id ?? null + slugCache.set(cacheKey, companyId) + } + + if (!companyId) { + summary.skippedMissingCompany += 1 + continue + } + + if (machine.companyId === companyId) { + summary.alreadyLinked += 1 + continue + } + + if (!effectiveDryRun) { + await ctx.db.patch(machine._id, { companyId }) + } + summary.updated += 1 + } + + return { + dryRun: effectiveDryRun, + ...summary, + } + }, +}) diff --git a/src/components/admin/companies/admin-companies-manager.tsx b/src/components/admin/companies/admin-companies-manager.tsx index 64f76a1..aaa4b57 100644 --- a/src/components/admin/companies/admin-companies-manager.tsx +++ b/src/components/admin/companies/admin-companies-manager.tsx @@ -119,22 +119,59 @@ export function AdminCompaniesManager({ initialCompanies }: { initialCompanies: const machinesByCompanyId = useMemo(() => { const map = new Map() ;(machinesQuery ?? []).forEach((machine) => { - if (!machine.companyId) return - const list = map.get(machine.companyId) ?? [] - list.push(machine) - map.set(machine.companyId, list) + const keys = [ + machine.companyId ?? undefined, + machine.companySlug ?? undefined, + ].filter((key): key is string => Boolean(key)) + if (keys.length === 0) { + return + } + keys.forEach((key) => { + const list = map.get(key) + if (list) { + list.push(machine) + } else { + map.set(key, [machine]) + } + }) }) return map }, [machinesQuery]) - const editingCompanyMachines = useMemo(() => { - if (!editingId) return [] - return machinesByCompanyId.get(editingId) ?? [] - }, [machinesByCompanyId, editingId]) - const machinesDialogList = useMemo(() => { - if (!machinesDialog) return [] - return machinesByCompanyId.get(machinesDialog.companyId) ?? [] - }, [machinesByCompanyId, machinesDialog]) + const getMachinesForCompany = useCallback( + (company: Company | null | undefined) => { + if (!company) return [] + const keys = [company.id, company.slug].filter(Boolean) + for (const key of keys) { + const list = machinesByCompanyId.get(key) + if (list && list.length > 0) { + return list + } + } + return [] + }, + [machinesByCompanyId] + ) + + const editingCompany = useMemo( + () => (editingId ? companies.find((company) => company.id === editingId) ?? null : null), + [companies, editingId] + ) + + const editingCompanyMachines = useMemo( + () => getMachinesForCompany(editingCompany), + [getMachinesForCompany, editingCompany] + ) + + const machinesDialogCompany = useMemo( + () => (machinesDialog ? companies.find((company) => company.id === machinesDialog.companyId) ?? null : null), + [companies, machinesDialog] + ) + + const machinesDialogList = useMemo( + () => getMachinesForCompany(machinesDialogCompany), + [getMachinesForCompany, machinesDialogCompany] + ) const resetForm = () => setForm({}) @@ -525,7 +562,7 @@ export function AdminCompaniesManager({ initialCompanies }: { initialCompanies:
{hasCompanies ? ( filteredCompanies.map((company) => { - const companyMachines = machinesByCompanyId.get(company.id) ?? [] + const companyMachines = getMachinesForCompany(company) const formattedPhone = formatPhoneDisplay(company.phone) const alertInfo = lastAlerts[company.slug] ?? null const usagePct = alertInfo?.usagePct ?? 0 @@ -714,7 +751,7 @@ export function AdminCompaniesManager({ initialCompanies }: { initialCompanies: ? formatDistanceToNow(alertInfo.createdAt, { addSuffix: true, locale: ptBR }) : null const formattedPhone = formatPhoneDisplay(company.phone) - const companyMachines = machinesByCompanyId.get(company.id) ?? [] + const companyMachines = getMachinesForCompany(company) const machineCount = companyMachines.length return ( +
+ + +
-
-
+
+
#{ticket.reference} {queueLabel}
-
- - -
-
+
{ticket.subject}