11import { useCallback , useEffect , useMemo , useRef , useState } from "react" ;
2- import { useMutation , useQuery , useQueryClient } from "@tanstack/react-query" ;
2+ import { useMutation , useQueryClient } from "@tanstack/react-query" ;
33import { Popover , PopoverButton , PopoverPanel } from "@headlessui/react" ;
44import { Avatar , Button , Input , Text } from "@libretexts/davis-react" ;
55import { IconCheck , IconPlus , IconSearch , IconX } from "@tabler/icons-react" ;
66import api from "../../api" ;
7- import { User } from "../../types" ;
7+ import { SupportTicket } from "../../types" ;
88import { useNotifications } from "../../context/NotificationContext" ;
99import { useTypedSelector } from "../../state/hooks" ;
1010import useDebounce from "../../hooks/useDebounce" ;
11+ import useAssignableUsers , {
12+ AssignableUser ,
13+ } from "../../hooks/useAssignableUsers" ;
1114
1215interface TicketAssigneePickerProps {
1316 ticketId : string ;
@@ -23,11 +26,6 @@ interface TicketAssigneePickerProps {
2326 triggerId ?: string ;
2427}
2528
26- type AssignableUser = Pick <
27- User ,
28- "uuid" | "firstName" | "lastName" | "email" | "avatar"
29- > ;
30-
3129const fullName = ( u : AssignableUser ) => `${ u . firstName } ${ u . lastName } ` ;
3230
3331const TicketAssigneePicker : React . FC < TicketAssigneePickerProps > = ( {
@@ -50,12 +48,7 @@ const TicketAssigneePicker: React.FC<TicketAssigneePickerProps> = ({
5048 setSelected ( assignedUUIDs ?? [ ] ) ;
5149 } , [ assignedUUIDs ] ) ;
5250
53- const { data : assignableUsers } = useQuery < AssignableUser [ ] > ( {
54- queryKey : [ "assignableUsers" ] ,
55- queryFn : async ( ) => {
56- const res = await api . getSupportAssignableUsers ( ) ;
57- return res . data . users ;
58- } ,
51+ const { data : assignableUsers } = useAssignableUsers ( {
5952 enabled : ! ! ticketId && ! disabled ,
6053 } ) ;
6154
@@ -67,8 +60,31 @@ const TicketAssigneePicker: React.FC<TicketAssigneePickerProps> = ({
6760 throw new Error ( res . data . errMsg ) ;
6861 }
6962 } ,
70- onSuccess : async ( ) => {
71- await queryClient . invalidateQueries ( [ "ticket" , ticketId ] ) ;
63+ onSuccess : ( _data , assignees ) => {
64+ // Patch the cached ticket so the UI settles immediately; the background
65+ // invalidation below reconciles with the server without blocking the user.
66+ // Additions resolve against the cached staff roster (falling back to the
67+ // records already on the ticket) so both chips and any assignedUsers-driven
68+ // UI stay correct even if the refetch is slow or fails.
69+ queryClient . setQueryData < SupportTicket | undefined > (
70+ [ "ticket" , ticketId ] ,
71+ ( prev ) => {
72+ if ( ! prev ) return prev ;
73+ const roster = assignableUsers ?? [ ] ;
74+ return {
75+ ...prev ,
76+ assignedUUIDs : assignees ,
77+ assignedUsers : assignees
78+ . map (
79+ ( uuid ) =>
80+ prev . assignedUsers ?. find ( ( u ) => u . uuid === uuid ) ??
81+ roster . find ( ( u ) => u . uuid === uuid )
82+ )
83+ . filter ( ( u ) : u is AssignableUser => ! ! u ) ,
84+ } ;
85+ }
86+ ) ;
87+ queryClient . invalidateQueries ( [ "ticket" , ticketId ] ) ;
7288 addNotification ( {
7389 type : "success" ,
7490 message : "Successfully updated assignees." ,
@@ -92,7 +108,7 @@ const TicketAssigneePicker: React.FC<TicketAssigneePickerProps> = ({
92108 mutateRef . current = updateAssignedMutation . mutate ;
93109
94110 const debouncedSave = useMemo (
95- ( ) => debounce ( ( assignees : string [ ] ) => mutateRef . current ( assignees ) , 500 ) ,
111+ ( ) => debounce ( ( assignees : string [ ] ) => mutateRef . current ( assignees ) , 250 ) ,
96112 [ ]
97113 ) ;
98114
0 commit comments