From aa9c09c30e2b324662437a7de074626ae9342c19 Mon Sep 17 00:00:00 2001 From: rever-tecnologia Date: Wed, 17 Dec 2025 09:35:51 -0300 Subject: [PATCH] refactor(tickets): corrige avatar desincronizado e otimiza performance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Corrige avatar desincronizado na listagem de tickets usando sessao do usuario logado - Otimiza timer da tickets-table de 1s para 15s (reduz re-renders) - Remove useEffect desnecessario em status-select (usa prop diretamente) - Remove useEffects desnecessarios em ticket-custom-fields (usa ticket.customFields diretamente) - Adiciona React.memo no AssigneeCell para evitar re-renders 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/components/tickets/status-select.tsx | 12 +++------ .../tickets/ticket-custom-fields.tsx | 26 ++++++++----------- src/components/tickets/tickets-table.tsx | 19 +++++++++----- 3 files changed, 27 insertions(+), 30 deletions(-) diff --git a/src/components/tickets/status-select.tsx b/src/components/tickets/status-select.tsx index 4f16aef..11056b1 100644 --- a/src/components/tickets/status-select.tsx +++ b/src/components/tickets/status-select.tsx @@ -1,6 +1,6 @@ "use client" -import { useEffect, useState } from "react" +import { useState } from "react" import type { Id } from "@/convex/_generated/dataModel" import type { TicketStatus } from "@/lib/schemas/ticket" import { useAuth } from "@/lib/auth-client" @@ -54,18 +54,13 @@ export function StatusSelect({ : machineAssignedName && machineAssignedName.length > 0 ? machineAssignedName : null - const [status, setStatus] = useState(value) const [closeDialogOpen, setCloseDialogOpen] = useState(false) - useEffect(() => { - setStatus(value) - }, [value]) - return ( <>
- - {statusStyles[status]?.label ?? statusStyles.PENDING.label} + + {statusStyles[value]?.label ?? statusStyles.PENDING.label} {showCloseButton ? ( @@ -100,7 +95,6 @@ export function StatusSelect({ requesterName={requesterName} agentName={agentName} onSuccess={() => { - setStatus("RESOLVED") setCloseDialogOpen(false) onStatusChange?.("RESOLVED") }} diff --git a/src/components/tickets/ticket-custom-fields.tsx b/src/components/tickets/ticket-custom-fields.tsx index c3e6803..337abdf 100644 --- a/src/components/tickets/ticket-custom-fields.tsx +++ b/src/components/tickets/ticket-custom-fields.tsx @@ -274,22 +274,19 @@ export function TicketCustomFieldsSection({ ticket, variant = "card", className const [openCalendarField, setOpenCalendarField] = useState(null) const [isSaving, setIsSaving] = useState(false) const [validationError, setValidationError] = useState(null) - const [currentFields, setCurrentFields] = useState(ticket.customFields) - - useEffect(() => { - setCurrentFields(ticket.customFields) - }, [ticket.customFields]) + // Usa ticket.customFields diretamente (Convex atualiza automaticamente) const initialValues = useMemo( - () => buildInitialValues(selectedForm.fields, currentFields), - [selectedForm.fields, currentFields] + () => buildInitialValues(selectedForm.fields, ticket.customFields), + [selectedForm.fields, ticket.customFields] ) - useEffect(() => { - if (!editorOpen) return + // Funcao para abrir o editor e resetar os valores + const openEditor = () => { setCustomFieldValues(initialValues) setValidationError(null) - }, [editorOpen, initialValues]) + setEditorOpen(true) + } const updateCustomFields = useMutation(api.tickets.updateCustomFields) @@ -333,12 +330,11 @@ export function TicketCustomFieldsSection({ ticket, variant = "card", className setIsSaving(true) setValidationError(null) try { - const result = await updateCustomFields({ + await updateCustomFields({ ticketId: ticket.id as Id<"tickets">, actorId: viewerId, fields: payload, }) - setCurrentFields(result?.customFields ?? currentFields) toast.success("Campos personalizados atualizados!", { id: "ticket-custom-fields" }) setEditorOpen(false) } catch (error) { @@ -355,7 +351,7 @@ export function TicketCustomFieldsSection({ ticket, variant = "card", className canEdit && hasConfiguredFields ? (
diff --git a/src/components/tickets/tickets-table.tsx b/src/components/tickets/tickets-table.tsx index c8d7310..5cb659f 100644 --- a/src/components/tickets/tickets-table.tsx +++ b/src/components/tickets/tickets-table.tsx @@ -1,12 +1,13 @@ "use client" -import { useEffect, useRef, useState } from "react" +import { memo, useEffect, useRef, useState } from "react" import { useRouter } from "next/navigation" import { format, formatDistanceToNowStrict } from "date-fns" import { ptBR } from "date-fns/locale" import { IconBuildingOff, IconCategory, IconHierarchyOff, IconUserOff } from "@tabler/icons-react" import type { Ticket } from "@/lib/schemas/ticket" +import { useAuth } from "@/lib/auth-client" import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { Badge } from "@/components/ui/badge" import { Card, CardContent } from "@/components/ui/card" @@ -60,7 +61,7 @@ function formatQueueLabel(queue?: string | null) { return { label: queue, title: queue } } -function AssigneeCell({ ticket }: { ticket: Ticket }) { +const AssigneeCell = memo(function AssigneeCell({ ticket, sessionUserId, sessionAvatarUrl }: { ticket: Ticket; sessionUserId?: string; sessionAvatarUrl?: string | null }) { if (!ticket.assignee) { return (
@@ -79,10 +80,14 @@ function AssigneeCell({ ticket }: { ticket: Ticket }) { .map((part) => part[0]?.toUpperCase()) .join("") + // Usa o avatar da sessao se o assignee for o usuario logado (garante sincronizacao) + const isCurrentUser = sessionUserId && ticket.assignee.id === sessionUserId + const avatarUrl = isCurrentUser ? sessionAvatarUrl : ticket.assignee.avatarUrl + return (
- + {initials}
@@ -95,7 +100,7 @@ function AssigneeCell({ ticket }: { ticket: Ticket }) {
) -} +}) export type TicketsTableProps = { tickets?: Ticket[] @@ -107,11 +112,13 @@ export function TicketsTable({ tickets, enteringIds }: TicketsTableProps) { const [now, setNow] = useState(() => Date.now()) const serverOffsetRef = useRef(0) const router = useRouter() + const { session } = useAuth() + // Atualiza o timer a cada 15 segundos (otimizado de 1s) useEffect(() => { const interval = setInterval(() => { setNow(Date.now()) - }, 1000) + }, 15000) return () => clearInterval(interval) }, []) @@ -325,7 +332,7 @@ export function TicketsTable({ tickets, enteringIds }: TicketsTableProps) {