fix: [BLOCKER] status RESOLVED race condition - use setQueryData from PATCH response, no refetch race
This commit is contained in:
@@ -85,8 +85,11 @@ export default function TicketDetailScreen() {
|
||||
|
||||
const updateStatus = useMutation({
|
||||
mutationFn: async ({ status, activationTicket, subId }: { status: string; activationTicket: boolean; subId?: string }) => {
|
||||
await api.patch(`/api/v1/tickets/${id}`, { status });
|
||||
// If this is an activation ticket being RESOLVED → activate subscription
|
||||
// Return the patched ticket so onSuccess can set cache directly
|
||||
const res = await api.patch(`/api/v1/tickets/${id}`, { status });
|
||||
const updatedTicket = res.data;
|
||||
|
||||
// Activate subscription when resolving an activation ticket
|
||||
if ((status === 'RESOLVED' || status === 'CLOSED') && activationTicket && subId) {
|
||||
await api.patch(`/api/v1/subscriptions/${subId}`, { status: 'ACTIVE' }).catch(() => {});
|
||||
}
|
||||
@@ -94,15 +97,19 @@ export default function TicketDetailScreen() {
|
||||
await api.post(`/api/v1/tickets/${id}/messages`, {
|
||||
body: `Status changed to ${status.replace('_', ' ')} by ${who}`,
|
||||
}).catch(() => {});
|
||||
|
||||
return updatedTicket;
|
||||
},
|
||||
onSuccess: async () => {
|
||||
onSuccess: (updatedTicket) => {
|
||||
// Directly inject fresh data into cache — avoids race condition with refetch
|
||||
if (updatedTicket) {
|
||||
qc.setQueryData(['task', id], (old: any) => ({ ...(old ?? {}), ...updatedTicket }));
|
||||
}
|
||||
setShowStatusPicker(false);
|
||||
await qc.invalidateQueries({ queryKey: ['task', id] });
|
||||
await qc.invalidateQueries({ queryKey: ['tasks'] });
|
||||
await qc.invalidateQueries({ queryKey: ['clients'] });
|
||||
await refetch();
|
||||
await refetchClient().catch(() => {});
|
||||
await refetchInvoices().catch(() => {});
|
||||
// Invalidate list queries in background (non-blocking)
|
||||
qc.invalidateQueries({ queryKey: ['tasks'] });
|
||||
qc.invalidateQueries({ queryKey: ['clients'] });
|
||||
qc.invalidateQueries({ queryKey: ['task-client', updatedTicket?.clientId] });
|
||||
},
|
||||
onError: (e: any) => {
|
||||
const msg = e?.response?.data?.message ?? 'Could not update status.';
|
||||
@@ -130,8 +137,11 @@ export default function TicketDetailScreen() {
|
||||
const confirmInstallation = async () => {
|
||||
setInstConfirming(true);
|
||||
try {
|
||||
// 1. Resolve the ticket
|
||||
await api.patch(`/api/v1/tickets/${id}`, { status: 'RESOLVED' });
|
||||
// 1. Resolve the ticket — use response to directly update cache
|
||||
const patchRes = await api.patch(`/api/v1/tickets/${id}`, { status: 'RESOLVED' });
|
||||
if (patchRes.data) {
|
||||
qc.setQueryData(['task', id], (old: any) => ({ ...(old ?? {}), ...patchRes.data }));
|
||||
}
|
||||
|
||||
// 2. Update client location with recorded coordinates (skip if GPS unavailable)
|
||||
if (ticket?.clientId && coords) {
|
||||
@@ -163,12 +173,9 @@ export default function TicketDetailScreen() {
|
||||
|
||||
setInstNotes('');
|
||||
setCoords(null);
|
||||
// Invalidate + force refetch so the status shows RESOLVED immediately
|
||||
await qc.invalidateQueries({ queryKey: ['tasks'] });
|
||||
await qc.invalidateQueries({ queryKey: ['task', id] });
|
||||
await refetch();
|
||||
await qc.invalidateQueries({ queryKey: ['client', ticket?.clientId] });
|
||||
await qc.invalidateQueries({ queryKey: ['client-tickets', ticket?.clientId] });
|
||||
// Invalidate list queries in background (cache already updated above)
|
||||
qc.invalidateQueries({ queryKey: ['tasks'] });
|
||||
qc.invalidateQueries({ queryKey: ['clients'] });
|
||||
Alert.alert(
|
||||
'Installation Complete! ✓',
|
||||
'Ticket resolved, location recorded, and activation ticket created.',
|
||||
|
||||
Reference in New Issue
Block a user