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

3
import { apiClient, Trigger, CreateTriggerPayload } from "@/lib/api";
4
import { insightKeys } from "@/hooks/useInsights.queries";
5

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

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

20
// Queries
21

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

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

40
// MUTATIONS
41

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

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

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

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

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

71
      return { previous };
72
    },
73

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

81
    // On success, replace optimistic record with real one from server
82
    onSuccess: () => {
83
      queryClient.invalidateQueries({ queryKey: triggerKeys.lists() });
84
      // Delay to allow the async queue job to finish writing the new insight row
85
      setTimeout(() => {
86
        queryClient.invalidateQueries({ queryKey: insightKeys.byType("TRIGGER_PATTERN") });
87
      }, 2000);
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 !== Number(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
      setTimeout(() => {
119
        queryClient.invalidateQueries({ queryKey: insightKeys.byType("TRIGGER_PATTERN") });
120
      }, 2000);
121
    },
122
  });
123
};