eletrotupi / tcc/ frontend/hooks/useSleepRecord.queries.ts9cd93e6ab48e317d51b0bdf9066dc5852cda738f
5.0 KB | 161 lines Raw
1
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
2

3
import { apiClient, SleepRecord, SleepRecordPayload, UpdateSleepRecordPayload } from "@/lib/api";
4
import { insightKeys } from "./useInsights.queries";
5

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

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

20
export const useSleepRecords = (filters?: SleepRecordFilters) => {
21
  return useQuery({
22
    queryKey: sleepRecordKeys.list(filters),
23
    queryFn: () => apiClient.getSleepRecords(filters),
24
  });
25
};
26

27
// Single entry detail
28
export const useSleepRecord = (id: string) => {
29
  return useQuery({
30
    queryKey: sleepRecordKeys.detail(id),
31
    queryFn: () => apiClient.getSleepRecord(id),
32
    enabled: !!id,
33
  });
34
};
35

36
export const useCreateSleepRecord = () => {
37
  const queryClient = useQueryClient();
38

39
  return useMutation({
40
    mutationFn: (payload: SleepRecordPayload) =>
41
      apiClient.createSleepRecord(payload),
42

43
    onMutate: async (payload) => {
44
      await queryClient.cancelQueries({ queryKey: sleepRecordKeys.lists() });
45

46
      const previous = queryClient.getQueryData(sleepRecordKeys.list());
47

48
      // TODO: might as well create a shared attrs that we're keeping it
49
      queryClient.setQueryData(
50
        sleepRecordKeys.list(),
51
        (old: SleepRecord[] = []) => [
52
          {
53
            ...payload,
54
            id: `temp-${Date.now()}`,
55
            createdAt: new Date().toISOString(),
56
            _optimistic: true,
57
          },
58
          ...old,
59
        ],
60
      );
61

62
      return { previous };
63
    },
64

65
    onError: (_err, _payload, context) => {
66
      if (context?.previous) {
67
        queryClient.setQueryData(sleepRecordKeys.list(), context.previous);
68
      }
69
    },
70

71
    onSuccess: () => {
72
      queryClient.invalidateQueries({ queryKey: sleepRecordKeys.lists() });
73

74
      // Invalidate insights, so we can force refresh
75
      setTimeout(() => {
76
        queryClient.invalidateQueries({ queryKey: insightKeys.byType("ENERGY_SLEEP_CORRELATION") });
77
        queryClient.invalidateQueries({ queryKey: insightKeys.byType("DAILY_SLEEP") });
78
      }, 2000);
79
    },
80
  });
81
};
82

83
// Update a sleep record
84
export const useUpdateSleepRecord = () => {
85
  const queryClient = useQueryClient();
86

87
  return useMutation({
88
    mutationFn: ({ id, payload }: { id: string; payload: UpdateSleepRecordPayload }) =>
89
      apiClient.updateSleepRecord(id, payload),
90

91
    onMutate: async ({ id, payload }) => {
92
      await queryClient.cancelQueries({ queryKey: sleepRecordKeys.detail(id) });
93
      await queryClient.cancelQueries({ queryKey: sleepRecordKeys.lists() });
94

95
      const previousDetail = queryClient.getQueryData(sleepRecordKeys.detail(id));
96
      const previousList = queryClient.getQueryData(sleepRecordKeys.list());
97

98
      if (previousDetail) {
99
        queryClient.setQueryData(
100
          sleepRecordKeys.detail(id),
101
          (old: SleepRecord) => ({ ...old, ...payload }),
102
        );
103
      }
104

105
      return { previousDetail, previousList };
106
    },
107

108
    onError: (_err, { id }, context) => {
109
      if (context?.previousDetail) {
110
        queryClient.setQueryData(sleepRecordKeys.detail(id), context.previousDetail);
111
      }
112
      if (context?.previousList) {
113
        queryClient.setQueryData(sleepRecordKeys.list(), context.previousList);
114
      }
115
    },
116

117
    onSuccess: (_data, { id }) => {
118
      queryClient.invalidateQueries({ queryKey: sleepRecordKeys.detail(id) });
119
      queryClient.invalidateQueries({ queryKey: sleepRecordKeys.lists() });
120
      setTimeout(() => {
121
        queryClient.invalidateQueries({ queryKey: insightKeys.byType("ENERGY_SLEEP_CORRELATION") });
122
        queryClient.invalidateQueries({ queryKey: insightKeys.byType("DAILY_SLEEP") });
123
      }, 2000);
124
    },
125
  });
126
};
127

128
// Delete a sleep record with optimistic removal
129
export const useDeleteSleepRecord = () => {
130
  const queryClient = useQueryClient();
131

132
  return useMutation({
133
    mutationFn: (id: string) => apiClient.deleteSleepRecord(id),
134

135
    onMutate: async (id) => {
136
      await queryClient.cancelQueries({ queryKey: sleepRecordKeys.lists() });
137
      const previous = queryClient.getQueryData(sleepRecordKeys.list());
138

139
      queryClient.setQueryData(
140
        sleepRecordKeys.list(),
141
        (old: SleepRecord[] = []) => old.filter((e) => e.id !== Number(id)),
142
      );
143

144
      return { previous };
145
    },
146

147
    onError: (_err, _id, context) => {
148
      if (context?.previous) {
149
        queryClient.setQueryData(sleepRecordKeys.list(), context.previous);
150
      }
151
    },
152

153
    onSuccess: () => {
154
      queryClient.invalidateQueries({ queryKey: sleepRecordKeys.lists() });
155
      setTimeout(() => {
156
        queryClient.invalidateQueries({ queryKey: insightKeys.byType("ENERGY_SLEEP_CORRELATION") });
157
        queryClient.invalidateQueries({ queryKey: insightKeys.byType("DAILY_SLEEP") });
158
      }, 2000);
159
    },
160
  });
161
};