diff --git a/src/app/tickets/new/page.tsx b/src/app/tickets/new/page.tsx index eeec6d4..1efa7fa 100644 --- a/src/app/tickets/new/page.tsx +++ b/src/app/tickets/new/page.tsx @@ -48,12 +48,12 @@ export default function NewTicketPage() { const queuesEnabled = Boolean(isStaff && convexUserId) const queueArgs = queuesEnabled ? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> } - : "skip" - const queuesRaw = useQuery( - queuesEnabled ? api.queues.summary : "skip", - queueArgs - ) as TicketQueueSummary[] | undefined - const queues = useMemo(() => queuesRaw ?? [], [queuesRaw]) + : undefined + const queuesRemote = useQuery(queuesEnabled ? api.queues.summary : "skip", queueArgs) + const queues = useMemo( + () => (Array.isArray(queuesRemote) ? (queuesRemote as TicketQueueSummary[]) : []), + [queuesRemote] + ) const create = useMutation(api.tickets.create) const addComment = useMutation(api.tickets.addComment) const staffRaw = useQuery(api.users.listAgents, { tenantId: DEFAULT_TENANT_ID }) as Doc<"users">[] | undefined @@ -63,29 +63,34 @@ export default function NewTicketPage() { ) const directoryQueryEnabled = queuesEnabled && Boolean(convexUserId) - const companiesRaw = useQuery( + const companiesArgs = directoryQueryEnabled + ? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> } + : undefined + const companiesRemote = useQuery( directoryQueryEnabled ? api.companies.list : "skip", - directoryQueryEnabled - ? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> } - : "skip" - ) as Array<{ id: string; name: string; slug?: string | null }> | undefined + companiesArgs + ) const companies = useMemo( () => - (companiesRaw ?? []).map((company) => ({ + (Array.isArray(companiesRemote) ? companiesRemote : []).map((company) => ({ id: String(company.id), name: company.name, slug: company.slug ?? null, })), - [companiesRaw] + [companiesRemote] ) - const customersRaw = useQuery( + const customersArgs = directoryQueryEnabled + ? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> } + : undefined + const customersRemote = useQuery( directoryQueryEnabled ? api.users.listCustomers : "skip", - directoryQueryEnabled - ? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> } - : "skip" - ) as CustomerOption[] | undefined - const customers = useMemo(() => customersRaw ?? [], [customersRaw]) + customersArgs + ) + const customers = useMemo( + () => (Array.isArray(customersRemote) ? (customersRemote as CustomerOption[]) : []), + [customersRemote] + ) const [subject, setSubject] = useState("") const [summary, setSummary] = useState("") diff --git a/src/components/tickets/close-ticket-dialog.tsx b/src/components/tickets/close-ticket-dialog.tsx index 59d2183..2b7003a 100644 --- a/src/components/tickets/close-ticket-dialog.tsx +++ b/src/components/tickets/close-ticket-dialog.tsx @@ -132,12 +132,17 @@ export function CloseTicketDialog({ const addComment = useMutation(api.tickets.addComment) const adjustWorkSummary = useMutation(api.tickets.adjustWorkSummary) - const closingTemplates = useQuery( + const closingTemplateArgs = + actorId && open ? { tenantId, viewerId: actorId, kind: "closing" as const } : undefined + const closingTemplatesRemote = useQuery( actorId && open ? api.commentTemplates.list : "skip", - actorId && open ? { tenantId, viewerId: actorId, kind: "closing" as const } : "skip" - ) as { id: string; title: string; body: string }[] | undefined + closingTemplateArgs + ) + const closingTemplates = Array.isArray(closingTemplatesRemote) + ? (closingTemplatesRemote as { id: string; title: string; body: string }[]) + : undefined - const templatesLoading = Boolean(actorId && open && closingTemplates === undefined) + const templatesLoading = Boolean(actorId && open && !Array.isArray(closingTemplatesRemote)) const templates = useMemo(() => { if (closingTemplates && closingTemplates.length > 0) { return closingTemplates.map((t) => ({ id: t.id, title: t.title, body: t.body })) diff --git a/src/components/tickets/new-ticket-dialog.tsx b/src/components/tickets/new-ticket-dialog.tsx index 857bafe..c9a7495 100644 --- a/src/components/tickets/new-ticket-dialog.tsx +++ b/src/components/tickets/new-ticket-dialog.tsx @@ -128,14 +128,14 @@ export function NewTicketDialog({ triggerClassName }: { triggerClassName?: strin const queuesEnabled = Boolean(isStaff && convexUserId) const queueArgs = queuesEnabled ? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> } - : "skip" + : undefined useDefaultQueues(DEFAULT_TENANT_ID) - const queuesRaw = useQuery( - queuesEnabled ? api.queues.summary : "skip", - queueArgs - ) as TicketQueueSummary[] | undefined - const queues = useMemo(() => queuesRaw ?? [], [queuesRaw]) + const queuesRemote = useQuery(queuesEnabled ? api.queues.summary : "skip", queueArgs) + const queues = useMemo( + () => (Array.isArray(queuesRemote) ? (queuesRemote as TicketQueueSummary[]) : []), + [queuesRemote] + ) const create = useMutation(api.tickets.create) const addComment = useMutation(api.tickets.addComment) const staffRaw = useQuery(api.users.listAgents, { tenantId: DEFAULT_TENANT_ID }) as Doc<"users">[] | undefined @@ -145,29 +145,34 @@ export function NewTicketDialog({ triggerClassName }: { triggerClassName?: strin ) const directoryQueryEnabled = queuesEnabled && Boolean(convexUserId) - const companiesRaw = useQuery( + const companiesArgs = directoryQueryEnabled + ? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> } + : undefined + const companiesRemote = useQuery( directoryQueryEnabled ? api.companies.list : "skip", - directoryQueryEnabled - ? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> } - : "skip" - ) as Array<{ id: string; name: string; slug?: string | null }> | undefined + companiesArgs + ) const companies = useMemo( () => - (companiesRaw ?? []).map((company) => ({ + (Array.isArray(companiesRemote) ? companiesRemote : []).map((company) => ({ id: String(company.id), name: company.name, slug: company.slug ?? null, })), - [companiesRaw] + [companiesRemote] ) - const customersRaw = useQuery( + const customersArgs = directoryQueryEnabled + ? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> } + : undefined + const customersRemote = useQuery( directoryQueryEnabled ? api.users.listCustomers : "skip", - directoryQueryEnabled - ? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> } - : "skip" - ) as CustomerOption[] | undefined - const customers = useMemo(() => customersRaw ?? [], [customersRaw]) + customersArgs + ) + const customers = useMemo( + () => (Array.isArray(customersRemote) ? (customersRemote as CustomerOption[]) : []), + [customersRemote] + ) const [attachments, setAttachments] = useState>([]) const [customersInitialized, setCustomersInitialized] = useState(false) const attachmentsTotalBytes = useMemo( diff --git a/src/components/tickets/play-next-ticket-card.tsx b/src/components/tickets/play-next-ticket-card.tsx index 5f46453..105c317 100644 --- a/src/components/tickets/play-next-ticket-card.tsx +++ b/src/components/tickets/play-next-ticket-card.tsx @@ -32,12 +32,9 @@ export function PlayNextTicketCard({ context }: PlayNextTicketCardProps) { const router = useRouter() const { convexUserId, isStaff } = useAuth() const queuesEnabled = Boolean(isStaff && convexUserId) - const queueArgs = queuesEnabled - ? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> } - : "skip" - const queueSummary = ( - useQuery(queuesEnabled ? api.queues.summary : "skip", queueArgs) as TicketQueueSummary[] | undefined - ) ?? [] + const queueArgs = queuesEnabled ? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> } : undefined + const queueSummaryResult = useQuery(queuesEnabled ? api.queues.summary : "skip", queueArgs) + const queueSummary: TicketQueueSummary[] = Array.isArray(queueSummaryResult) ? queueSummaryResult : [] const playNext = useMutation(api.tickets.playNext) const [selectedQueueId, setSelectedQueueId] = useState(undefined) diff --git a/src/components/tickets/recent-tickets-panel.tsx b/src/components/tickets/recent-tickets-panel.tsx index 3c8bb48..33a1b47 100644 --- a/src/components/tickets/recent-tickets-panel.tsx +++ b/src/components/tickets/recent-tickets-panel.tsx @@ -74,15 +74,16 @@ function TicketRow({ ticket, entering }: { ticket: Ticket; entering: boolean }) export function RecentTicketsPanel() { const { convexUserId } = useAuth() - const ticketsRaw = useQuery( - api.tickets.list, - convexUserId ? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users">, limit: 12 } : "skip" - ) + const ticketsArgs = convexUserId + ? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users">, limit: 12 } + : undefined + const ticketsResult = useQuery(convexUserId ? api.tickets.list : "skip", ticketsArgs) const [enteringId, setEnteringId] = useState(null) const previousIdsRef = useRef([]) const tickets = useMemo(() => { - const all = mapTicketsFromServerList((ticketsRaw ?? []) as unknown[]).filter((t) => t.status !== "RESOLVED") + if (!Array.isArray(ticketsResult)) return [] + const all = mapTicketsFromServerList(ticketsResult as unknown[]).filter((t) => t.status !== "RESOLVED") // Unassigned first (no assignee), oldest first among unassigned; then the rest by updatedAt desc const unassigned = all .filter((t) => !t.assignee) @@ -91,10 +92,10 @@ export function RecentTicketsPanel() { .filter((t) => !!t.assignee) .sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()) return [...unassigned, ...assigned].slice(0, 6) - }, [ticketsRaw]) + }, [ticketsResult]) useEffect(() => { - if (ticketsRaw === undefined) { + if (!Array.isArray(ticketsResult)) { previousIdsRef.current = [] return } @@ -113,7 +114,7 @@ export function RecentTicketsPanel() { setEnteringId(topId) } previousIdsRef.current = ids - }, [tickets, ticketsRaw]) + }, [tickets, ticketsResult]) useEffect(() => { if (!enteringId) return @@ -121,7 +122,7 @@ export function RecentTicketsPanel() { return () => window.clearTimeout(timer) }, [enteringId]) - if (ticketsRaw === undefined) { + if (convexUserId && !Array.isArray(ticketsResult)) { return ( diff --git a/src/components/tickets/ticket-comments.rich.tsx b/src/components/tickets/ticket-comments.rich.tsx index 8743340..2c257e5 100644 --- a/src/components/tickets/ticket-comments.rich.tsx +++ b/src/components/tickets/ticket-comments.rich.tsx @@ -70,14 +70,15 @@ export function TicketComments({ ticket }: TicketCommentsProps) { const [localBodies, setLocalBodies] = useState>({}) const [commentsOrder, setCommentsOrder] = useState("descending") - const templateArgs = convexUserId && isStaff - ? { tenantId: ticket.tenantId, viewerId: convexUserId as Id<"users">, kind: "comment" as const } - : "skip" - const templatesResult = useQuery(convexUserId && isStaff ? api.commentTemplates.list : "skip", templateArgs) as - | { id: string; title: string; body: string }[] - | undefined - const templates = templatesResult ?? [] - const templatesLoading = Boolean(convexUserId && isStaff) && templatesResult === undefined + const templateArgs = + convexUserId && isStaff + ? { tenantId: ticket.tenantId, viewerId: convexUserId as Id<"users">, kind: "comment" as const } + : undefined + const templatesResult = useQuery(convexUserId && isStaff ? api.commentTemplates.list : "skip", templateArgs) + const templates = Array.isArray(templatesResult) + ? (templatesResult as { id: string; title: string; body: string }[]) + : [] + const templatesLoading = Boolean(convexUserId && isStaff) && !Array.isArray(templatesResult) const canUseTemplates = Boolean(convexUserId && isStaff) const insertTemplateIntoBody = (html: string) => { diff --git a/src/components/tickets/ticket-queue-summary.tsx b/src/components/tickets/ticket-queue-summary.tsx index e6fec11..ef3b8c5 100644 --- a/src/components/tickets/ticket-queue-summary.tsx +++ b/src/components/tickets/ticket-queue-summary.tsx @@ -16,11 +16,10 @@ interface TicketQueueSummaryProps { export function TicketQueueSummaryCards({ queues }: TicketQueueSummaryProps) { const { convexUserId, isStaff } = useAuth() const enabled = Boolean(isStaff && convexUserId) - const queueArgs = enabled - ? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> } - : "skip" + const queueArgs = enabled ? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> } : undefined const fromServer = useQuery(enabled ? api.queues.summary : "skip", queueArgs) - const data: TicketQueueSummary[] = (queues ?? (fromServer as TicketQueueSummary[] | undefined) ?? []) + const serverData = Array.isArray(fromServer) ? fromServer : undefined + const data: TicketQueueSummary[] = queues ?? serverData ?? [] if (!queues && fromServer === undefined) { return ( diff --git a/src/components/tickets/ticket-summary-header.tsx b/src/components/tickets/ticket-summary-header.tsx index b1044ab..fe8aab1 100644 --- a/src/components/tickets/ticket-summary-header.tsx +++ b/src/components/tickets/ticket-summary-header.tsx @@ -167,42 +167,47 @@ export function TicketSummaryHeader({ ticket }: TicketHeaderProps) { const updateCategories = useMutation(api.tickets.updateCategories) const agents = (useQuery(api.users.listAgents, { tenantId: ticket.tenantId }) as Doc<"users">[] | undefined) ?? [] const queuesEnabled = Boolean(isStaff && convexUserId) - const companiesRaw = useQuery( + const companiesArgs = convexUserId + ? { tenantId: ticket.tenantId, viewerId: convexUserId as Id<"users"> } + : undefined + const companiesRemote = useQuery( convexUserId ? api.companies.list : "skip", - convexUserId - ? { tenantId: ticket.tenantId, viewerId: convexUserId as Id<"users"> } - : "skip" - ) as Array<{ id: string; name: string; slug?: string | null }> | undefined + companiesArgs + ) const companies = useMemo( () => - (companiesRaw ?? []).map((company) => ({ + (Array.isArray(companiesRemote) ? companiesRemote : []).map((company) => ({ id: String(company.id), name: company.name, slug: company.slug ?? null, })), - [companiesRaw] + [companiesRemote] ) - const customersRaw = useQuery( + const customersArgs = convexUserId + ? { tenantId: ticket.tenantId, viewerId: convexUserId as Id<"users"> } + : undefined + const customersRemote = useQuery( convexUserId ? api.users.listCustomers : "skip", - convexUserId - ? { tenantId: ticket.tenantId, viewerId: convexUserId as Id<"users"> } - : "skip" - ) as CustomerOption[] | undefined - const customers = useMemo(() => customersRaw ?? [], [customersRaw]) + customersArgs + ) + const customers = useMemo( + () => (Array.isArray(customersRemote) ? (customersRemote as CustomerOption[]) : []), + [customersRemote] + ) const queueArgs = queuesEnabled ? { tenantId: ticket.tenantId, viewerId: convexUserId as Id<"users"> } - : "skip" - const queues = ( - useQuery(queuesEnabled ? api.queues.summary : "skip", queueArgs) as TicketQueueSummary[] | undefined - ) ?? [] + : undefined + const queuesResult = useQuery(queuesEnabled ? api.queues.summary : "skip", queueArgs) + const queues: TicketQueueSummary[] = Array.isArray(queuesResult) ? queuesResult : [] const { categories, isLoading: categoriesLoading } = useTicketCategories(ticket.tenantId) + const workSummaryArgs = convexUserId + ? { ticketId: ticket.id as Id<"tickets">, viewerId: convexUserId as Id<"users"> } + : undefined const workSummaryRemote = useQuery( - api.tickets.workSummary, - convexUserId - ? { ticketId: ticket.id as Id<"tickets">, viewerId: convexUserId as Id<"users"> } - : "skip" + convexUserId ? api.tickets.workSummary : "skip", + workSummaryArgs ) as | { ticketId: Id<"tickets"> diff --git a/src/components/tickets/tickets-view.tsx b/src/components/tickets/tickets-view.tsx index 63604a9..01830b0 100644 --- a/src/components/tickets/tickets-view.tsx +++ b/src/components/tickets/tickets-view.tsx @@ -65,28 +65,28 @@ export function TicketsView({ initialFilters }: TicketsViewProps = {}) { useDefaultQueues(tenantId) const queuesEnabled = Boolean(isStaff && convexUserId) - const queues = useQuery( - queuesEnabled ? api.queues.summary : "skip", - queuesEnabled ? { tenantId, viewerId: convexUserId as Id<"users"> } : "skip" - ) as TicketQueueSummary[] | undefined + const queueArgs = queuesEnabled ? { tenantId, viewerId: convexUserId as Id<"users"> } : undefined + const queuesResult = useQuery(queuesEnabled ? api.queues.summary : "skip", queueArgs) + const queues: TicketQueueSummary[] = Array.isArray(queuesResult) ? queuesResult : [] const agents = useQuery(api.users.listAgents, { tenantId }) as { _id: string; name: string }[] | undefined - const ticketsRaw = useQuery( - api.tickets.list, - convexUserId - ? { - tenantId, - viewerId: convexUserId as Id<"users">, - status: filters.status ?? undefined, - priority: filters.priority ?? undefined, - channel: filters.channel ?? undefined, - queueId: undefined, // simplified: filter by queue name on client - assigneeId: filters.assigneeId ? (filters.assigneeId as unknown as Id<"users">) : undefined, - search: filters.search || undefined, - } - : "skip" - ) + const ticketsArgs = convexUserId + ? { + tenantId, + viewerId: convexUserId as Id<"users">, + status: filters.status ?? undefined, + priority: filters.priority ?? undefined, + channel: filters.channel ?? undefined, + queueId: undefined, // simplified: filter by queue name on client + assigneeId: filters.assigneeId ? (filters.assigneeId as unknown as Id<"users">) : undefined, + search: filters.search || undefined, + } + : undefined + const ticketsRaw = useQuery(convexUserId ? api.tickets.list : "skip", ticketsArgs) - const tickets = useMemo(() => mapTicketsFromServerList((ticketsRaw ?? []) as unknown[]), [ticketsRaw]) + const tickets = useMemo( + () => mapTicketsFromServerList(Array.isArray(ticketsRaw) ? (ticketsRaw as unknown[]) : []), + [ticketsRaw] + ) const [companies, setCompanies] = useState([]) useEffect(() => { let aborted = false @@ -190,7 +190,7 @@ export function TicketsView({ initialFilters }: TicketsViewProps = {}) {
q.name)} + queues={queues.map((q) => q.name)} companies={companies} assignees={(agents ?? []).map((a) => ({ id: a._id, name: a.name }))} initialState={mergedInitialFilters}