eletrotupi / tcc/ frontend/hooks/useTrigger.queries.tsd3c5b58dc393528a72928debc31a06def83abf7f
3.3 KB | 115 lines Raw
1
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
2

3
import { apiClient, Trigger, CreateTriggerPayload } from "@/lib/api";
4

5
export const triggerKeys = {
6
  all: () => ["triggers"] as const,
7
  lists: () => [...triggerKeys.all(), "list"] as const,
8
  list: (filters?: TriggerFilters) =>
9
    [...triggerKeys.lists(), filters] as const,
10
  detail: (id: string) => [...triggerKeys.all(), "detail", id] as const,
11
};
12

13
interface TriggerFilters {
14
  from?: string; // ISO date
15
  to?: string; // ISO date
16
  limit?: number;
17
}
18

19
// Queries
20

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

30
// Single entry detail
31
export const useTrigger = (id: string) => {
32
  return useQuery({
33
    queryKey: triggerKeys.detail(id),
34
    queryFn: () => apiClient.getTrigger(id),
35
    enabled: !!id,
36
  });
37
};
38

39
// MUTATIONS
40

41
// Create a new mood entry
42
// Optimistically adds to cache, rolls back on failure
43
export const useCreateTrigger = () => {
44
  const queryClient = useQueryClient();
45

46
  return useMutation({
47
    mutationFn: (payload: CreateTriggerPayload) =>
48
      apiClient.createTrigger(payload),
49

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

55
      // Snapshot current cache so we can roll back
56
      const previous = queryClient.getQueryData(triggerKeys.list());
57

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

70
      return { previous };
71
    },
72

73
    // On error, roll back to the snapshot
74
    onError: (_err, _payload, context) => {
75
      if (context?.previous) {
76
        queryClient.setQueryData(triggerKeys.list(), context.previous);
77
      }
78
    },
79

80
    // On success, replace optimistic record with real one from server
81
    onSuccess: () => {
82
      queryClient.invalidateQueries({ queryKey: triggerKeys.lists() });
83
    },
84
  });
85
};
86

87
// Delete a mood entry with optimistic removal
88
export const useDeleteTrigger = () => {
89
  const queryClient = useQueryClient();
90

91
  return useMutation({
92
    mutationFn: (id: string) => apiClient.deleteTrigger(id),
93

94
    onMutate: async (id) => {
95
      await queryClient.cancelQueries({ queryKey: triggerKeys.lists() });
96
      const previous = queryClient.getQueryData(triggerKeys.list());
97

98
      queryClient.setQueryData(triggerKeys.list(), (old: Trigger[] = []) =>
99
        old.filter((e) => e.id !== Number(id)),
100
      );
101

102
      return { previous };
103
    },
104

105
    onError: (_err, _id, context) => {
106
      if (context?.previous) {
107
        queryClient.setQueryData(triggerKeys.list(), context.previous);
108
      }
109
    },
110

111
    onSuccess: () => {
112
      queryClient.invalidateQueries({ queryKey: triggerKeys.lists() });
113
    },
114
  });
115
};