From 11efad0312ea9cb1510be5f8526b30242c642f57 Mon Sep 17 00:00:00 2001 From: Esdras Renan Date: Mon, 13 Oct 2025 14:18:57 -0300 Subject: [PATCH] Fix company search filters and build regressions --- src/app/api/machines/companies/route.ts | 36 +++++++++----- src/components/admin/admin-users-manager.tsx | 51 ++++++++++++-------- src/server/pdf/ticket-pdf-template.tsx | 16 +++++- 3 files changed, 71 insertions(+), 32 deletions(-) diff --git a/src/app/api/machines/companies/route.ts b/src/app/api/machines/companies/route.ts index efb7799..6826a24 100644 --- a/src/app/api/machines/companies/route.ts +++ b/src/app/api/machines/companies/route.ts @@ -1,5 +1,6 @@ import { ConvexHttpClient } from "convex/browser" +import { Prisma } from "@prisma/client" import { api } from "@/convex/_generated/api" import { DEFAULT_TENANT_ID } from "@/lib/constants" import { env } from "@/lib/env" @@ -59,18 +60,31 @@ export async function GET(request: Request) { const search = url.searchParams.get("search")?.trim() ?? "" try { + const slugSearch = search ? normalizeSlug(search) ?? slugify(search) : null + const orFilters: Prisma.CompanyWhereInput[] = [] + if (search) { + orFilters.push({ + name: { + contains: search, + mode: Prisma.QueryMode.insensitive, + } as unknown as Prisma.StringFilter<"Company">, + }) + if (slugSearch) { + orFilters.push({ + slug: { + contains: slugSearch, + mode: Prisma.QueryMode.insensitive, + } as unknown as Prisma.StringFilter<"Company">, + }) + } + } + const where: Prisma.CompanyWhereInput = { + tenantId, + ...(orFilters.length > 0 ? { OR: orFilters } : {}), + } + const companies = await prisma.company.findMany({ - where: { - tenantId, - ...(search - ? { - OR: [ - { name: { contains: search, mode: "insensitive" } }, - { slug: { contains: normalizeSlug(search) ?? slugify(search), mode: "insensitive" } }, - ], - } - : {}), - }, + where, orderBy: { name: "asc" }, take: 20, }) diff --git a/src/components/admin/admin-users-manager.tsx b/src/components/admin/admin-users-manager.tsx index 910514c..df1d0d2 100644 --- a/src/components/admin/admin-users-manager.tsx +++ b/src/components/admin/admin-users-manager.tsx @@ -21,11 +21,13 @@ import { Sheet, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetT import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { ROLE_OPTIONS, type RoleOption } from "@/lib/authz" +type AdminRole = RoleOption | "machine" + type AdminUser = { id: string email: string name: string - role: RoleOption + role: AdminRole tenantId: string createdAt: string updatedAt: string | null @@ -61,7 +63,7 @@ type CompanyOption = { type Props = { initialUsers: AdminUser[] initialInvites: AdminInvite[] - roleOptions: readonly RoleOption[] + roleOptions: readonly AdminRole[] defaultTenantId: string } @@ -113,6 +115,11 @@ function sanitizeInvite(invite: AdminInvite & { events?: unknown }): AdminInvite return rest } +function coerceRole(role: AdminRole | string | null | undefined): RoleOption { + const candidate = (role ?? "agent").toLowerCase() + return (ROLE_OPTIONS as readonly string[]).includes(candidate) ? (candidate as RoleOption) : "agent" +} + export function AdminUsersManager({ initialUsers, initialInvites, roleOptions, defaultTenantId }: Props) { const [users, setUsers] = useState(initialUsers) const [invites, setInvites] = useState(initialInvites) @@ -144,7 +151,17 @@ export function AdminUsersManager({ initialUsers, initialInvites, roleOptions, d const [isResettingPassword, setIsResettingPassword] = useState(false) const [passwordPreview, setPasswordPreview] = useState(null) - const normalizedRoles = useMemo(() => roleOptions ?? ROLE_OPTIONS, [roleOptions]) + const normalizedRoles = useMemo(() => { + return (roleOptions && roleOptions.length > 0 ? roleOptions : ROLE_OPTIONS) as readonly AdminRole[] + }, [roleOptions]) + const selectableRoles = useMemo(() => { + const unique = new Set() + normalizedRoles.forEach((roleOption) => { + const coerced = coerceRole(roleOption) + unique.add(coerced) + }) + return Array.from(unique) + }, [normalizedRoles]) const teamUsers = useMemo(() => users.filter((user) => user.role !== "machine"), [users]) const machineUsers = useMemo(() => users.filter((user) => user.role === "machine"), [users]) @@ -175,7 +192,7 @@ export function AdminUsersManager({ initialUsers, initialInvites, roleOptions, d setEditForm({ name: editUser.name || "", email: editUser.email, - role: editUser.role, + role: coerceRole(editUser.role), tenantId: editUser.tenantId || defaultTenantId, companyId: editUser.companyId ?? "", }) @@ -567,13 +584,11 @@ export function AdminUsersManager({ initialUsers, initialInvites, roleOptions, d - {normalizedRoles - .filter((option) => option !== "machine") - .map((option) => ( - - {formatRole(option)} - - ))} + {selectableRoles.map((option) => ( + + {formatRole(option)} + + ))} @@ -693,7 +708,7 @@ export function AdminUsersManager({ initialUsers, initialInvites, roleOptions, d (!open ? setEditUserId(null) : null)}> - + Editar usuário Atualize os dados cadastrais, papel e vínculo do colaborador. @@ -733,13 +748,11 @@ export function AdminUsersManager({ initialUsers, initialInvites, roleOptions, d - {normalizedRoles - .filter((option) => option !== "machine") - .map((option) => ( - - {formatRole(option)} - - ))} + {selectableRoles.map((option) => ( + + {formatRole(option)} + + ))} diff --git a/src/server/pdf/ticket-pdf-template.tsx b/src/server/pdf/ticket-pdf-template.tsx index a6ae8bc..d2942c5 100644 --- a/src/server/pdf/ticket-pdf-template.tsx +++ b/src/server/pdf/ticket-pdf-template.tsx @@ -472,7 +472,11 @@ function TicketPdfDocument({ ticket, logoDataUrl }: { ticket: TicketWithDetails; {comments.map((comment, index) => ( - 0 ? styles.cardSpacing : null]} wrap={false}> + 0 ? [styles.card, styles.cardSpacing] : [styles.card]} + wrap={false} + > {comment.author.name} @@ -505,7 +509,15 @@ function TicketPdfDocument({ ticket, logoDataUrl }: { ticket: TicketWithDetails; {timeline.map((event, index) => ( - 0 ? styles.cardSpacing : null]} wrap={false}> + 0 + ? [styles.card, styles.timelineCard, styles.cardSpacing] + : [styles.card, styles.timelineCard] + } + wrap={false} + > {event.label} {formatDateTime(event.createdAt)} {event.description ? (