eletrotupi / tcc/ commit / 0e69b39

frontend: linting hooks/api's

Pedro Lucas Porcellis porcellis@eletrotupi.com · 4 months ago 0e69b3981ab74bef567c19220d0b6dc95b9e909f Browse files
Parents 2d01897
Changed files
6
Additions
+74
Deletions
-56
frontend/hooks/useMoodEntries.queries.ts
1 1
import {
2 2
  useQuery,
3 3
  useMutation,
4 4
  useQueryClient,
5 5
  useInfiniteQuery,
6 -
} from '@tanstack/react-query';
6 +
} from "@tanstack/react-query";
7 7

8 -
import { apiClient, MoodEntry, CreateMoodEntryPayload } from '@/lib/api';
8 +
import { apiClient, MoodEntry, CreateMoodEntryPayload } from "@/lib/api";
9 9

10 10
export const moodKeys = {
11 -
  all:    () => ['mood-entries'] as const,
12 -
  lists:  () => [...moodKeys.all(), 'list'] as const,
13 -
  list:   (filters?: MoodEntryFilters) => [...moodKeys.lists(), filters] as const,
14 -
  detail: (id: string) => [...moodKeys.all(), 'detail', id] as const,
11 +
  all: () => ["mood-entries"] as const,
12 +
  lists: () => [...moodKeys.all(), "list"] as const,
13 +
  list: (filters?: MoodEntryFilters) => [...moodKeys.lists(), filters] as const,
14 +
  detail: (id: string) => [...moodKeys.all(), "detail", id] as const,
15 15
};
16 16

17 17
interface MoodEntryFilters {
18 -
  from?: string;   // ISO date
19 -
  to?: string;     // ISO date
18 +
  from?: string; // ISO date
19 +
  to?: string; // ISO date
20 20
  limit?: number;
21 21
}
22 22

23 23
// Queries
24 24

27 27
export const useMoodEntries = (filters?: MoodEntryFilters) => {
28 28
  return useQuery({
29 29
    queryKey: moodKeys.list(filters),
30 30
    queryFn: () => apiClient.getMoodEntries(filters),
31 31
  });
32 -
}
32 +
};
33 33

34 34
// Infinite scroll version
35 35
export const useMoodEntriesInfinite = (limit = 20) => {
36 36
  return useInfiniteQuery({
37 -
    queryKey: [...moodKeys.lists(), 'infinite'],
37 +
    queryKey: [...moodKeys.lists(), "infinite"],
38 38
    queryFn: ({ pageParam = 1 }) =>
39 39
      apiClient.getMoodEntries({ page: pageParam, limit }),
40 40
    getNextPageParam: (lastPage) => lastPage.nextPage ?? undefined,
41 41
    initialPageParam: 1,
42 42
  });
43 -
}
43 +
};
44 44

45 45
// Single entry detail
46 46
export const useMoodEntry = (id: string) => {
47 47
  return useQuery({
48 48
    queryKey: moodKeys.detail(id),
49 49
    queryFn: () => apiClient.getMoodEntry(id),
50 50
    enabled: !!id,
51 51
  });
52 -
}
52 +
};
53 53

54 54
// MUTATIONS
55 55

56 56
// Create a new mood entry
57 57
// Optimistically adds to cache, rolls back on failure
95 95
    // On success, replace optimistic record with real one from server
96 96
    onSuccess: () => {
97 97
      queryClient.invalidateQueries({ queryKey: moodKeys.lists() });
98 98
    },
99 99
  });
100 -
}
100 +
};
101 101

102 102
// Delete a mood entry with optimistic removal
103 103
export const useDeleteMoodEntry = () => {
104 104
  const queryClient = useQueryClient();
105 105

109 109
    onMutate: async (id) => {
110 110
      await queryClient.cancelQueries({ queryKey: moodKeys.lists() });
111 111
      const previous = queryClient.getQueryData(moodKeys.list());
112 112

113 113
      queryClient.setQueryData(moodKeys.list(), (old: MoodEntry[] = []) =>
114 -
        old.filter((e) => e.id !== id)
114 +
        old.filter((e) => e.id !== Number(id)),
115 115
      );
116 116

117 117
      return { previous };
118 118
    },
119 119

125 125

126 126
    onSuccess: () => {
127 127
      queryClient.invalidateQueries({ queryKey: moodKeys.lists() });
128 128
    },
129 129
  });
130 -
}
130 +
};
frontend/hooks/useSleepRecord.queries.ts
1 -
import {
2 -
  useQuery,
3 -
  useMutation,
4 -
  useQueryClient
5 -
} from '@tanstack/react-query';
1 +
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
6 2

7 -
import { apiClient, SleepRecord, SleepRecordPayload } from '@/lib/api';
3 +
import { apiClient, SleepRecord, SleepRecordPayload } from "@/lib/api";
8 4

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

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

22 19
export const useSleepRecords = (filters?: SleepRecordFilters) => {
23 20
  return useQuery({
37 34
      await queryClient.cancelQueries({ queryKey: sleepRecordKeys.lists() });
38 35

39 36
      const previous = queryClient.getQueryData(sleepRecordKeys.list());
40 37

41 38
      // TODO: might as well create a shared attrs that we're keeping it
42 -
      queryClient.setQueryData(sleepRecordKeys, (old: SleepRecord[] = []) => [
43 -
        {
44 -
          ...payload,
45 -
          id: `temp-${Date.now()}`,
46 -
          createdAt: new Date().toISOString(),
47 -
          _optimistic: true,
48 -
        },
49 -
        ...old,
50 -
      ]);
39 +
      queryClient.setQueryData(
40 +
        sleepRecordKeys.list(),
41 +
        (old: SleepRecord[] = []) => [
42 +
          {
43 +
            ...payload,
44 +
            id: `temp-${Date.now()}`,
45 +
            createdAt: new Date().toISOString(),
46 +
            _optimistic: true,
47 +
          },
48 +
          ...old,
49 +
        ],
50 +
      );
51 51

52 52
      return { previous };
53 53
    },
54 54

55 55
    onError: (_err, _payload, context) => {
60 60

61 61
    onSuccess: () => {
62 62
      queryClient.invalidateQueries({ queryKey: sleepRecordKeys.lists() });
63 63
    },
64 64
  });
65 -
}
65 +
};
frontend/hooks/useTrigger.queries.ts
1 -
import {
2 -
  useQuery,
3 -
  useMutation,
4 -
  useQueryClient,
5 -
  useInfiniteQuery,
6 -
} from "@tanstack/react-query";
1 +
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
7 2

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

10 5
export const triggerKeys = {
11 6
  all: () => ["triggers"] as const,
99 94
    onMutate: async (id) => {
100 95
      await queryClient.cancelQueries({ queryKey: triggerKeys.lists() });
101 96
      const previous = queryClient.getQueryData(triggerKeys.list());
102 97

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

107 102
      return { previous };
108 103
    },
109 104

116 111
    onSuccess: () => {
117 112
      queryClient.invalidateQueries({ queryKey: triggerKeys.lists() });
118 113
    },
119 114
  });
120 115
};
frontend/lib/api/client.ts
1 1
import * as SecureStore from "expo-secure-store";
2 2
import {
3 3
  User,
4 4
  AuthResponse,
5 5
  VerifyTokenResponse,
6 6
  SignUpPayload,
7 7
  ResetPasswordRequestResponse,
8 8
  ResetPasswordResponse,
9 9
  UserUpdatePayload,
10 -
  UserUpdateResponse,
11 -
  MoodComponentPayload,
12 -
  MoodEntryPayload,
10 +
  CreateMoodEntryPayload,
13 11
  SleepRecordPayload,
12 +
  MoodEntry,
13 +
  SleepRecord,
14 +
  SleepRecordResponse,
15 +
  MoodEntryResponse,
16 +
  Trigger,
17 +
  TriggerResponse,
14 18
  CreateTriggerPayload,
19 +
  ActivateResponse,
15 20
  PaginatedResponse,
16 21
} from "@/lib/api/types";
17 22

18 23
const API_BASE_URL = process.env.EXPO_PUBLIC_API_BASE_URL;
19 24
const TOKEN_KEY = process.env.EXPO_PUBLIC_TOKEN_KEY;
20 25

21 26
class ApiClient {
22 27
  private async getStoredToken(): Promise<string | null> {
23 28
    try {
24 -
      return await SecureStore.getItemAsync(TOKEN_KEY);
29 +
      return await SecureStore.getItemAsync(TOKEN_KEY!);
25 30
    } catch (error) {
26 31
      console.error("Failed to get stored token:", error);
27 32
      return null;
28 33
    }
29 34
  }
30 35

31 36
  private async storeToken(token: string): Promise<void> {
32 37
    try {
33 -
      await SecureStore.setItemAsync(TOKEN_KEY, token);
38 +
      await SecureStore.setItemAsync(TOKEN_KEY!, token);
34 39
    } catch (error) {
35 40
      console.error("Failed to store token:", error);
36 41
      throw error;
37 42
    }
38 43
  }
39 44

40 45
  private async removeToken(): Promise<void> {
41 46
    try {
42 -
      await SecureStore.deleteItemAsync(TOKEN_KEY);
47 +
      await SecureStore.deleteItemAsync(TOKEN_KEY!);
43 48
    } catch (error) {
44 49
      console.error("Failed to remove token:", error);
45 50
    }
46 51
  }
47 52

210 215
    return this.request(`/moods/${id}`, { method: "DELETE" });
211 216
  }
212 217

213 218
  // Sleep Record
214 219
  async createSleepRecord(
215 -
    sleepRecord: CreateSleepRecordPayload,
220 +
    sleepRecord: SleepRecordPayload,
216 221
  ): Promise<SleepRecordResponse> {
217 222
    return await this.request(`/sleep_records`, {
218 223
      method: "POST",
219 224
      body: JSON.stringify({ sleepRecord }),
220 225
    });
268 273
    return this.request(`/triggers/${id}`, { method: "DELETE" });
269 274
  }
270 275
}
271 276

272 277
export const apiClient = new ApiClient();
frontend/lib/api/index.ts
1 -
export {
2 -
  apiClient
3 -
} from '@/lib/api/client';
1 +
export { apiClient } from "@/lib/api/client";
4 2

5 3
export type {
6 4
  User,
7 5
  AuthResponse,
6 +
  PaginatedResponse,
7 +
  // Mood
8 +
  MoodEntry,
8 9
  CreateMoodEntryPayload,
9 10
  MoodComponentPayload,
10 -
  PaginatedResponse,
11 +
  // User
11 12
  UserUpdateResponse,
13 +
  // Sleep
12 14
  SleepRecordPayload,
13 -
} from '@/lib/api/types';
15 +
  SleepRecord,
16 +
  // Trigger
17 +
  Trigger,
18 +
  CreateTriggerPayload,
19 +
} from "@/lib/api/types";
frontend/lib/api/types.ts
1 1
export interface User {
2 2
  id: number;
3 3
  email: string;
4 4
  firstName: string;
5 5
  lastName?: string;
127 127
  entries: T[];
128 128
  total: number;
129 129
  page: number;
130 130
  nextPage: number | null;
131 131
}
132 +

133 +
export interface MoodEntryResponse {
134 +
  moodEntry: MoodEntry;
135 +
}
136 +

137 +
export interface SleepRecordResponse {
138 +
  sleepRecord: SleepRecord;
139 +
}
140 +

141 +
export interface TriggerResponse {
142 +
  trigger: Trigger;
143 +
}