eletrotupi / tcc/ frontend/hooks/useTrigger.queries.ts2d0189798624bc5481535444134dca1bb0fb1ee1
3.3 KB | 120 lines Raw
1
import {
2
  useQuery,
3
  useMutation,
4
  useQueryClient,
5
  useInfiniteQuery,
6
} from "@tanstack/react-query";
7

8
import { apiClient, Trigger, CreateTriggerPayload } from "@/lib/api";
9

10
export const triggerKeys = {
11
  all: () => ["triggers"] as const,
12
  lists: () => [...triggerKeys.all(), "list"] as const,
13
  list: (filters?: TriggerFilters) =>
14
    [...triggerKeys.lists(), filters] as const,
15
  detail: (id: string) => [...triggerKeys.all(), "detail", id] as const,
16
};
17

18
interface TriggerFilters {
19
  from?: string; // ISO date
20
  to?: string; // ISO date
21
  limit?: number;
22
}
23

24
// Queries
25

26
// Fetch a paginated list of mood entries.
27
// On cold start, returns cached data instantly then revalidates.
28
export const useTriggers = (filters?: TriggerFilters) => {
29
  return useQuery({
30
    queryKey: triggerKeys.list(filters),
31
    queryFn: () => apiClient.getTriggers(filters),
32
  });
33
};
34

35
// Single entry detail
36
export const useTrigger = (id: string) => {
37
  return useQuery({
38
    queryKey: triggerKeys.detail(id),
39
    queryFn: () => apiClient.getTrigger(id),
40
    enabled: !!id,
41
  });
42
};
43

44
// MUTATIONS
45

46
// Create a new mood entry
47
// Optimistically adds to cache, rolls back on failure
48
export const useCreateTrigger = () => {
49
  const queryClient = useQueryClient();
50

51
  return useMutation({
52
    mutationFn: (payload: CreateTriggerPayload) =>
53
      apiClient.createTrigger(payload),
54

55
    // Optimistic update so that the UI reflects the new entry immediately
56
    onMutate: async (payload) => {
57
      // Cancel any in-flight refetches so they don't overwrite our optimistic update
58
      await queryClient.cancelQueries({ queryKey: triggerKeys.lists() });
59

60
      // Snapshot current cache so we can roll back
61
      const previous = queryClient.getQueryData(triggerKeys.list());
62

63
      // Inject a temporary optimistic record
64
      // TODO: might as well create a shared attrs that we're keeping it
65
      queryClient.setQueryData(triggerKeys.list(), (old: Trigger[] = []) => [
66
        {
67
          ...payload,
68
          id: `temp-${Date.now()}`,
69
          createdAt: new Date().toISOString(),
70
          _optimistic: true,
71
        },
72
        ...old,
73
      ]);
74

75
      return { previous };
76
    },
77

78
    // On error, roll back to the snapshot
79
    onError: (_err, _payload, context) => {
80
      if (context?.previous) {
81
        queryClient.setQueryData(triggerKeys.list(), context.previous);
82
      }
83
    },
84

85
    // On success, replace optimistic record with real one from server
86
    onSuccess: () => {
87
      queryClient.invalidateQueries({ queryKey: triggerKeys.lists() });
88
    },
89
  });
90
};
91

92
// Delete a mood entry with optimistic removal
93
export const useDeleteTrigger = () => {
94
  const queryClient = useQueryClient();
95

96
  return useMutation({
97
    mutationFn: (id: string) => apiClient.deleteTrigger(id),
98

99
    onMutate: async (id) => {
100
      await queryClient.cancelQueries({ queryKey: triggerKeys.lists() });
101
      const previous = queryClient.getQueryData(triggerKeys.list());
102

103
      queryClient.setQueryData(triggerKeys.list(), (old: Trigger[] = []) =>
104
        old.filter((e) => e.id !== id),
105
      );
106

107
      return { previous };
108
    },
109

110
    onError: (_err, _id, context) => {
111
      if (context?.previous) {
112
        queryClient.setQueryData(triggerKeys.list(), context.previous);
113
      }
114
    },
115

116
    onSuccess: () => {
117
      queryClient.invalidateQueries({ queryKey: triggerKeys.lists() });
118
    },
119
  });
120
};