@growsober/sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +276 -0
- package/dist/__tests__/e2e.test.d.ts +7 -0
- package/dist/__tests__/e2e.test.js +472 -0
- package/dist/api/client.d.ts +11 -0
- package/dist/api/client.js +61 -0
- package/dist/api/mutations/admin.d.ts +167 -0
- package/dist/api/mutations/admin.js +326 -0
- package/dist/api/mutations/ambassadors.d.ts +52 -0
- package/dist/api/mutations/ambassadors.js +148 -0
- package/dist/api/mutations/auth.d.ts +267 -0
- package/dist/api/mutations/auth.js +332 -0
- package/dist/api/mutations/bookings.d.ts +59 -0
- package/dist/api/mutations/bookings.js +143 -0
- package/dist/api/mutations/event-chat.d.ts +35 -0
- package/dist/api/mutations/event-chat.js +147 -0
- package/dist/api/mutations/events.d.ts +87 -0
- package/dist/api/mutations/events.js +205 -0
- package/dist/api/mutations/grow90.d.ts +36 -0
- package/dist/api/mutations/grow90.js +132 -0
- package/dist/api/mutations/hubs.d.ts +111 -0
- package/dist/api/mutations/hubs.js +240 -0
- package/dist/api/mutations/index.d.ts +22 -0
- package/dist/api/mutations/index.js +39 -0
- package/dist/api/mutations/jack.d.ts +61 -0
- package/dist/api/mutations/jack.js +104 -0
- package/dist/api/mutations/library.d.ts +67 -0
- package/dist/api/mutations/library.js +168 -0
- package/dist/api/mutations/map.d.ts +153 -0
- package/dist/api/mutations/map.js +181 -0
- package/dist/api/mutations/matching.d.ts +130 -0
- package/dist/api/mutations/matching.js +204 -0
- package/dist/api/mutations/notifications.d.ts +63 -0
- package/dist/api/mutations/notifications.js +106 -0
- package/dist/api/mutations/offers.d.ts +26 -0
- package/dist/api/mutations/offers.js +47 -0
- package/dist/api/mutations/subscriptions.d.ts +127 -0
- package/dist/api/mutations/subscriptions.js +140 -0
- package/dist/api/mutations/support.d.ts +165 -0
- package/dist/api/mutations/support.js +307 -0
- package/dist/api/mutations/users.d.ts +211 -0
- package/dist/api/mutations/users.js +261 -0
- package/dist/api/queries/admin.d.ts +257 -0
- package/dist/api/queries/admin.js +320 -0
- package/dist/api/queries/ambassadors.d.ts +53 -0
- package/dist/api/queries/ambassadors.js +98 -0
- package/dist/api/queries/auth.d.ts +16 -0
- package/dist/api/queries/auth.js +25 -0
- package/dist/api/queries/bookings.d.ts +91 -0
- package/dist/api/queries/bookings.js +102 -0
- package/dist/api/queries/businesses.d.ts +212 -0
- package/dist/api/queries/businesses.js +154 -0
- package/dist/api/queries/event-chat.d.ts +19 -0
- package/dist/api/queries/event-chat.js +75 -0
- package/dist/api/queries/events.d.ts +322 -0
- package/dist/api/queries/events.js +221 -0
- package/dist/api/queries/grow90.d.ts +26 -0
- package/dist/api/queries/grow90.js +85 -0
- package/dist/api/queries/hubs.d.ts +165 -0
- package/dist/api/queries/hubs.js +143 -0
- package/dist/api/queries/index.d.ts +23 -0
- package/dist/api/queries/index.js +40 -0
- package/dist/api/queries/jack.d.ts +63 -0
- package/dist/api/queries/jack.js +92 -0
- package/dist/api/queries/library.d.ts +132 -0
- package/dist/api/queries/library.js +120 -0
- package/dist/api/queries/map.d.ts +216 -0
- package/dist/api/queries/map.js +278 -0
- package/dist/api/queries/matching.d.ts +136 -0
- package/dist/api/queries/matching.js +161 -0
- package/dist/api/queries/notifications.d.ts +78 -0
- package/dist/api/queries/notifications.js +88 -0
- package/dist/api/queries/offers.d.ts +91 -0
- package/dist/api/queries/offers.js +103 -0
- package/dist/api/queries/subscriptions.d.ts +56 -0
- package/dist/api/queries/subscriptions.js +73 -0
- package/dist/api/queries/support.d.ts +106 -0
- package/dist/api/queries/support.js +202 -0
- package/dist/api/queries/users.d.ts +293 -0
- package/dist/api/queries/users.js +370 -0
- package/dist/api/types.d.ts +464 -0
- package/dist/api/types.js +9 -0
- package/dist/hooks/useAuth.d.ts +5 -0
- package/dist/hooks/useAuth.js +39 -0
- package/dist/hooks/useUser.d.ts +43 -0
- package/dist/hooks/useUser.js +44 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +67 -0
- package/package.json +62 -0
- package/src/__tests__/e2e.test.ts +502 -0
- package/src/api/client.ts +71 -0
- package/src/api/mutations/admin.ts +531 -0
- package/src/api/mutations/ambassadors.ts +185 -0
- package/src/api/mutations/auth.ts +350 -0
- package/src/api/mutations/bookings.ts +190 -0
- package/src/api/mutations/event-chat.ts +177 -0
- package/src/api/mutations/events.ts +273 -0
- package/src/api/mutations/grow90.ts +169 -0
- package/src/api/mutations/hubs.ts +385 -0
- package/src/api/mutations/index.ts +23 -0
- package/src/api/mutations/jack.ts +130 -0
- package/src/api/mutations/library.ts +212 -0
- package/src/api/mutations/map.ts +230 -0
- package/src/api/mutations/matching.ts +271 -0
- package/src/api/mutations/notifications.ts +114 -0
- package/src/api/mutations/offers.ts +73 -0
- package/src/api/mutations/subscriptions.ts +162 -0
- package/src/api/mutations/support.ts +390 -0
- package/src/api/mutations/users.ts +271 -0
- package/src/api/queries/admin.ts +480 -0
- package/src/api/queries/ambassadors.ts +139 -0
- package/src/api/queries/auth.ts +24 -0
- package/src/api/queries/bookings.ts +135 -0
- package/src/api/queries/businesses.ts +203 -0
- package/src/api/queries/event-chat.ts +78 -0
- package/src/api/queries/events.ts +272 -0
- package/src/api/queries/grow90.ts +98 -0
- package/src/api/queries/hubs.ts +211 -0
- package/src/api/queries/index.ts +24 -0
- package/src/api/queries/jack.ts +127 -0
- package/src/api/queries/library.ts +166 -0
- package/src/api/queries/map.ts +331 -0
- package/src/api/queries/matching.ts +238 -0
- package/src/api/queries/notifications.ts +103 -0
- package/src/api/queries/offers.ts +136 -0
- package/src/api/queries/subscriptions.ts +91 -0
- package/src/api/queries/support.ts +235 -0
- package/src/api/queries/users.ts +393 -0
- package/src/api/types.ts +596 -0
- package/src/index.ts +57 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
|
2
|
+
import { getApiClient } from '../client';
|
|
3
|
+
import type {
|
|
4
|
+
Grow90EnrollmentResponse,
|
|
5
|
+
Grow90ProgressResponse,
|
|
6
|
+
Grow90TodayResponse,
|
|
7
|
+
Grow90StatsResponse,
|
|
8
|
+
} from '../types';
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// QUERY KEYS
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
export const grow90Keys = {
|
|
15
|
+
all: ['grow90'] as const,
|
|
16
|
+
enrollment: () => [...grow90Keys.all, 'enrollment'] as const,
|
|
17
|
+
today: () => [...grow90Keys.all, 'today'] as const,
|
|
18
|
+
progress: () => [...grow90Keys.all, 'progress'] as const,
|
|
19
|
+
progressDay: (day: number) => [...grow90Keys.progress(), day] as const,
|
|
20
|
+
stats: () => [...grow90Keys.all, 'stats'] as const,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// ============================================================================
|
|
24
|
+
// QUERY HOOKS
|
|
25
|
+
// ============================================================================
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get current user's GROW90 enrollment
|
|
29
|
+
*/
|
|
30
|
+
export function useGrow90Enrollment(
|
|
31
|
+
options?: Omit<UseQueryOptions<Grow90EnrollmentResponse | null>, 'queryKey' | 'queryFn'>
|
|
32
|
+
) {
|
|
33
|
+
return useQuery({
|
|
34
|
+
queryKey: grow90Keys.enrollment(),
|
|
35
|
+
queryFn: async (): Promise<Grow90EnrollmentResponse | null> => {
|
|
36
|
+
const client = getApiClient();
|
|
37
|
+
const response = await client.get('/api/v1/grow90/me');
|
|
38
|
+
// API returns { data: {...}, meta: {...} }, extract the data
|
|
39
|
+
const enrollment = 'data' in response.data ? response.data.data : response.data;
|
|
40
|
+
return enrollment;
|
|
41
|
+
},
|
|
42
|
+
...options,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Get today's GROW90 content and progress
|
|
48
|
+
*/
|
|
49
|
+
export function useGrow90Today(
|
|
50
|
+
options?: Omit<UseQueryOptions<Grow90TodayResponse>, 'queryKey' | 'queryFn'>
|
|
51
|
+
) {
|
|
52
|
+
return useQuery({
|
|
53
|
+
queryKey: grow90Keys.today(),
|
|
54
|
+
queryFn: async (): Promise<Grow90TodayResponse> => {
|
|
55
|
+
const client = getApiClient();
|
|
56
|
+
const response = await client.get('/api/v1/grow90/today');
|
|
57
|
+
// API returns { data: {...}, meta: {...} }, extract the data
|
|
58
|
+
return response.data?.data || response.data;
|
|
59
|
+
},
|
|
60
|
+
...options,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get all GROW90 progress entries
|
|
66
|
+
*/
|
|
67
|
+
export function useGrow90Progress(
|
|
68
|
+
options?: Omit<UseQueryOptions<Grow90ProgressResponse[]>, 'queryKey' | 'queryFn'>
|
|
69
|
+
) {
|
|
70
|
+
return useQuery({
|
|
71
|
+
queryKey: grow90Keys.progress(),
|
|
72
|
+
queryFn: async (): Promise<Grow90ProgressResponse[]> => {
|
|
73
|
+
const client = getApiClient();
|
|
74
|
+
const response = await client.get('/api/v1/grow90/progress');
|
|
75
|
+
// API returns { data: [...], meta: {...} }, extract the data
|
|
76
|
+
return response.data?.data || response.data;
|
|
77
|
+
},
|
|
78
|
+
...options,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get GROW90 statistics
|
|
84
|
+
*/
|
|
85
|
+
export function useGrow90Stats(
|
|
86
|
+
options?: Omit<UseQueryOptions<Grow90StatsResponse>, 'queryKey' | 'queryFn'>
|
|
87
|
+
) {
|
|
88
|
+
return useQuery({
|
|
89
|
+
queryKey: grow90Keys.stats(),
|
|
90
|
+
queryFn: async (): Promise<Grow90StatsResponse> => {
|
|
91
|
+
const client = getApiClient();
|
|
92
|
+
const response = await client.get('/api/v1/grow90/stats');
|
|
93
|
+
// API returns { data: {...}, meta: {...} }, extract the data
|
|
94
|
+
return response.data?.data || response.data;
|
|
95
|
+
},
|
|
96
|
+
...options,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
|
2
|
+
import { getApiClient } from '../client';
|
|
3
|
+
import type { HubResponse, HubMemberResponse } from '../types';
|
|
4
|
+
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// QUERY KEYS
|
|
7
|
+
// ============================================================================
|
|
8
|
+
|
|
9
|
+
export const hubKeys = {
|
|
10
|
+
all: ['hubs'] as const,
|
|
11
|
+
lists: () => [...hubKeys.all, 'list'] as const,
|
|
12
|
+
list: (filters?: HubListFilters) => [...hubKeys.lists(), filters] as const,
|
|
13
|
+
details: () => [...hubKeys.all, 'detail'] as const,
|
|
14
|
+
detail: (id: string) => [...hubKeys.details(), id] as const,
|
|
15
|
+
bySlug: (slug: string) => [...hubKeys.all, 'slug', slug] as const,
|
|
16
|
+
myHubs: () => [...hubKeys.all, 'my-hubs'] as const,
|
|
17
|
+
members: (id: string, filters?: MemberFilters) =>
|
|
18
|
+
[...hubKeys.detail(id), 'members', filters] as const,
|
|
19
|
+
membership: (id: string) => [...hubKeys.detail(id), 'membership'] as const,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// TYPES
|
|
24
|
+
// ============================================================================
|
|
25
|
+
|
|
26
|
+
export interface HubListFilters {
|
|
27
|
+
page?: number;
|
|
28
|
+
limit?: number;
|
|
29
|
+
cityId?: string;
|
|
30
|
+
search?: string;
|
|
31
|
+
isFeatured?: boolean;
|
|
32
|
+
visibility?: 'PUBLIC' | 'PRIVATE' | 'INVITE_ONLY';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface MemberFilters {
|
|
36
|
+
page?: number;
|
|
37
|
+
limit?: number;
|
|
38
|
+
role?: string;
|
|
39
|
+
status?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface PaginatedHubsResponse {
|
|
43
|
+
data: HubResponse[];
|
|
44
|
+
meta: {
|
|
45
|
+
total: number;
|
|
46
|
+
page: number;
|
|
47
|
+
limit: number;
|
|
48
|
+
totalPages: number;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// HubMemberResponse is imported from '../types'
|
|
53
|
+
|
|
54
|
+
export interface PaginatedMembersResponse {
|
|
55
|
+
data: HubMemberResponse[];
|
|
56
|
+
meta: {
|
|
57
|
+
total: number;
|
|
58
|
+
page: number;
|
|
59
|
+
limit: number;
|
|
60
|
+
totalPages: number;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface MembershipResponse {
|
|
65
|
+
isMember: boolean;
|
|
66
|
+
role?: 'OWNER' | 'ADMIN' | 'MEMBER';
|
|
67
|
+
status?: 'ACTIVE' | 'PENDING' | 'BANNED';
|
|
68
|
+
joinedAt?: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ============================================================================
|
|
72
|
+
// QUERY HOOKS
|
|
73
|
+
// ============================================================================
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get all hubs with pagination and filters
|
|
77
|
+
*
|
|
78
|
+
* @param filters - Optional filters for hubs list
|
|
79
|
+
* @param options - React Query options
|
|
80
|
+
* @returns Query result with paginated hubs
|
|
81
|
+
*/
|
|
82
|
+
export function useHubs(
|
|
83
|
+
filters?: HubListFilters,
|
|
84
|
+
options?: Omit<UseQueryOptions<PaginatedHubsResponse>, 'queryKey' | 'queryFn'>
|
|
85
|
+
) {
|
|
86
|
+
return useQuery({
|
|
87
|
+
queryKey: hubKeys.list(filters),
|
|
88
|
+
queryFn: async (): Promise<PaginatedHubsResponse> => {
|
|
89
|
+
const client = getApiClient();
|
|
90
|
+
const response = await client.get('/api/v1/hubs', { params: filters });
|
|
91
|
+
return response.data;
|
|
92
|
+
},
|
|
93
|
+
...options,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Get hub by ID
|
|
99
|
+
*
|
|
100
|
+
* @param id - Hub ID
|
|
101
|
+
* @param options - React Query options
|
|
102
|
+
* @returns Query result with hub details
|
|
103
|
+
*/
|
|
104
|
+
export function useHub(
|
|
105
|
+
id: string,
|
|
106
|
+
options?: Omit<UseQueryOptions<HubResponse>, 'queryKey' | 'queryFn'>
|
|
107
|
+
) {
|
|
108
|
+
return useQuery({
|
|
109
|
+
queryKey: hubKeys.detail(id),
|
|
110
|
+
queryFn: async (): Promise<HubResponse> => {
|
|
111
|
+
const client = getApiClient();
|
|
112
|
+
const response = await client.get(`/api/v1/hubs/${id}`);
|
|
113
|
+
return response.data;
|
|
114
|
+
},
|
|
115
|
+
enabled: !!id && (options?.enabled !== false),
|
|
116
|
+
...options,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Get hub by slug
|
|
122
|
+
*
|
|
123
|
+
* @param slug - Hub slug
|
|
124
|
+
* @param options - React Query options
|
|
125
|
+
* @returns Query result with hub details
|
|
126
|
+
*/
|
|
127
|
+
export function useHubBySlug(
|
|
128
|
+
slug: string,
|
|
129
|
+
options?: Omit<UseQueryOptions<HubResponse>, 'queryKey' | 'queryFn'>
|
|
130
|
+
) {
|
|
131
|
+
return useQuery({
|
|
132
|
+
queryKey: hubKeys.bySlug(slug),
|
|
133
|
+
queryFn: async (): Promise<HubResponse> => {
|
|
134
|
+
const client = getApiClient();
|
|
135
|
+
const response = await client.get(`/api/v1/hubs/slug/${slug}`);
|
|
136
|
+
return response.data;
|
|
137
|
+
},
|
|
138
|
+
enabled: !!slug && (options?.enabled !== false),
|
|
139
|
+
...options,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Get hubs the current user is a member of
|
|
145
|
+
*
|
|
146
|
+
* @param options - React Query options
|
|
147
|
+
* @returns Query result with user's hubs
|
|
148
|
+
*/
|
|
149
|
+
export function useMyHubs(
|
|
150
|
+
options?: Omit<UseQueryOptions<HubResponse[]>, 'queryKey' | 'queryFn'>
|
|
151
|
+
) {
|
|
152
|
+
return useQuery({
|
|
153
|
+
queryKey: hubKeys.myHubs(),
|
|
154
|
+
queryFn: async (): Promise<HubResponse[]> => {
|
|
155
|
+
const client = getApiClient();
|
|
156
|
+
const response = await client.get('/api/v1/hubs/my-hubs');
|
|
157
|
+
return response.data;
|
|
158
|
+
},
|
|
159
|
+
...options,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Get hub members with pagination and filters
|
|
165
|
+
*
|
|
166
|
+
* @param id - Hub ID
|
|
167
|
+
* @param filters - Optional filters for members list
|
|
168
|
+
* @param options - React Query options
|
|
169
|
+
* @returns Query result with paginated members
|
|
170
|
+
*/
|
|
171
|
+
export function useHubMembers(
|
|
172
|
+
id: string,
|
|
173
|
+
filters?: MemberFilters,
|
|
174
|
+
options?: Omit<UseQueryOptions<PaginatedMembersResponse>, 'queryKey' | 'queryFn'>
|
|
175
|
+
) {
|
|
176
|
+
return useQuery({
|
|
177
|
+
queryKey: hubKeys.members(id, filters),
|
|
178
|
+
queryFn: async (): Promise<PaginatedMembersResponse> => {
|
|
179
|
+
const client = getApiClient();
|
|
180
|
+
const response = await client.get(`/api/v1/hubs/${id}/members`, {
|
|
181
|
+
params: filters
|
|
182
|
+
});
|
|
183
|
+
return response.data;
|
|
184
|
+
},
|
|
185
|
+
enabled: !!id && (options?.enabled !== false),
|
|
186
|
+
...options,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Get current user's membership status for a hub
|
|
192
|
+
*
|
|
193
|
+
* @param id - Hub ID
|
|
194
|
+
* @param options - React Query options
|
|
195
|
+
* @returns Query result with membership status
|
|
196
|
+
*/
|
|
197
|
+
export function useHubMembership(
|
|
198
|
+
id: string,
|
|
199
|
+
options?: Omit<UseQueryOptions<MembershipResponse>, 'queryKey' | 'queryFn'>
|
|
200
|
+
) {
|
|
201
|
+
return useQuery({
|
|
202
|
+
queryKey: hubKeys.membership(id),
|
|
203
|
+
queryFn: async (): Promise<MembershipResponse> => {
|
|
204
|
+
const client = getApiClient();
|
|
205
|
+
const response = await client.get(`/api/v1/hubs/${id}/membership`);
|
|
206
|
+
return response.data;
|
|
207
|
+
},
|
|
208
|
+
enabled: !!id && (options?.enabled !== false),
|
|
209
|
+
...options,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query Hooks
|
|
3
|
+
*
|
|
4
|
+
* Re-exports all query hooks for API endpoints.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export * from './admin';
|
|
8
|
+
export * from './auth';
|
|
9
|
+
export * from './bookings';
|
|
10
|
+
export * from './businesses';
|
|
11
|
+
export * from './events';
|
|
12
|
+
export * from './hubs';
|
|
13
|
+
export * from './library';
|
|
14
|
+
export * from './map';
|
|
15
|
+
export * from './notifications';
|
|
16
|
+
export * from './offers';
|
|
17
|
+
export * from './subscriptions';
|
|
18
|
+
export * from './support';
|
|
19
|
+
export * from './users';
|
|
20
|
+
export * from './jack';
|
|
21
|
+
export * from './ambassadors';
|
|
22
|
+
export * from './grow90';
|
|
23
|
+
export * from './matching';
|
|
24
|
+
export * from './event-chat';
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
|
2
|
+
import { getApiClient } from '../client';
|
|
3
|
+
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// QUERY KEY FACTORY
|
|
6
|
+
// ============================================================================
|
|
7
|
+
|
|
8
|
+
export const jackKeys = {
|
|
9
|
+
all: ['jack'] as const,
|
|
10
|
+
conversations: () => [...jackKeys.all, 'conversations'] as const,
|
|
11
|
+
conversation: (id: string) => [...jackKeys.all, 'conversation', id] as const,
|
|
12
|
+
history: () => [...jackKeys.all, 'history'] as const,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// TYPES
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
export interface JackConversation {
|
|
20
|
+
id: string;
|
|
21
|
+
title: string;
|
|
22
|
+
messageCount: number;
|
|
23
|
+
lastMessageAt: string | null;
|
|
24
|
+
createdAt: string;
|
|
25
|
+
isArchived: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface JackMessage {
|
|
29
|
+
id: string;
|
|
30
|
+
conversationId: string;
|
|
31
|
+
role: 'USER' | 'ASSISTANT' | 'SYSTEM';
|
|
32
|
+
content: string;
|
|
33
|
+
model?: string;
|
|
34
|
+
promptTokens?: number;
|
|
35
|
+
completionTokens?: number;
|
|
36
|
+
createdAt: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface JackConversationWithMessages extends JackConversation {
|
|
40
|
+
messages: JackMessage[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ============================================================================
|
|
44
|
+
// QUERY HOOKS
|
|
45
|
+
// ============================================================================
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get all conversations with Jack
|
|
49
|
+
*
|
|
50
|
+
* @param limit - Maximum conversations to return (default: 20)
|
|
51
|
+
* @param options - TanStack Query options
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```tsx
|
|
55
|
+
* const { data: conversations } = useJackConversations();
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export function useJackConversations(
|
|
59
|
+
limit = 20,
|
|
60
|
+
options?: Omit<UseQueryOptions<JackConversation[]>, 'queryKey' | 'queryFn'>
|
|
61
|
+
) {
|
|
62
|
+
return useQuery({
|
|
63
|
+
queryKey: jackKeys.conversations(),
|
|
64
|
+
queryFn: async (): Promise<JackConversation[]> => {
|
|
65
|
+
const client = getApiClient();
|
|
66
|
+
const response = await client.get('/api/v1/support/jack/conversations', {
|
|
67
|
+
params: { limit },
|
|
68
|
+
});
|
|
69
|
+
// API wraps response in { data: {...}, meta: {...} }
|
|
70
|
+
return response.data?.data || response.data;
|
|
71
|
+
},
|
|
72
|
+
...options,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get a specific conversation with messages
|
|
78
|
+
*
|
|
79
|
+
* @param conversationId - The conversation ID
|
|
80
|
+
* @param options - TanStack Query options
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```tsx
|
|
84
|
+
* const { data: conversation } = useJackConversation('conv-123');
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export function useJackConversation(
|
|
88
|
+
conversationId: string,
|
|
89
|
+
options?: Omit<UseQueryOptions<JackConversationWithMessages | null>, 'queryKey' | 'queryFn'>
|
|
90
|
+
) {
|
|
91
|
+
return useQuery({
|
|
92
|
+
queryKey: jackKeys.conversation(conversationId),
|
|
93
|
+
queryFn: async (): Promise<JackConversationWithMessages | null> => {
|
|
94
|
+
const client = getApiClient();
|
|
95
|
+
const response = await client.get(`/api/v1/support/jack/conversations/${conversationId}`);
|
|
96
|
+
// API wraps response in { data: {...}, meta: {...} }
|
|
97
|
+
return response.data?.data || response.data;
|
|
98
|
+
},
|
|
99
|
+
enabled: !!conversationId,
|
|
100
|
+
...options,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get the most recent active conversation with Jack
|
|
106
|
+
*
|
|
107
|
+
* @param options - TanStack Query options
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```tsx
|
|
111
|
+
* const { data: history } = useJackHistory();
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
export function useJackHistory(
|
|
115
|
+
options?: Omit<UseQueryOptions<JackConversationWithMessages | null>, 'queryKey' | 'queryFn'>
|
|
116
|
+
) {
|
|
117
|
+
return useQuery({
|
|
118
|
+
queryKey: jackKeys.history(),
|
|
119
|
+
queryFn: async (): Promise<JackConversationWithMessages | null> => {
|
|
120
|
+
const client = getApiClient();
|
|
121
|
+
const response = await client.get('/api/v1/support/jack/history');
|
|
122
|
+
// API wraps response in { data: {...}, meta: {...} }
|
|
123
|
+
return response.data?.data || response.data;
|
|
124
|
+
},
|
|
125
|
+
...options,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
|
|
2
|
+
import { getApiClient } from '../client';
|
|
3
|
+
import type {
|
|
4
|
+
LibraryContentResponse,
|
|
5
|
+
LibraryContentDetailResponse,
|
|
6
|
+
} from '../types';
|
|
7
|
+
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// QUERY KEY FACTORY
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
export const libraryKeys = {
|
|
13
|
+
all: ['library'] as const,
|
|
14
|
+
lists: () => [...libraryKeys.all, 'list'] as const,
|
|
15
|
+
list: (filters?: LibraryFilters) => [...libraryKeys.lists(), filters] as const,
|
|
16
|
+
featured: () => [...libraryKeys.all, 'featured'] as const,
|
|
17
|
+
categories: () => [...libraryKeys.all, 'categories'] as const,
|
|
18
|
+
details: () => [...libraryKeys.all, 'detail'] as const,
|
|
19
|
+
detail: (id: string) => [...libraryKeys.details(), id] as const,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// TYPES
|
|
24
|
+
// ============================================================================
|
|
25
|
+
|
|
26
|
+
export interface LibraryFilters {
|
|
27
|
+
page?: number;
|
|
28
|
+
limit?: number;
|
|
29
|
+
categoryId?: string;
|
|
30
|
+
type?: 'ARTICLE' | 'VIDEO' | 'PODCAST' | 'MEDITATION' | 'WORKSHEET';
|
|
31
|
+
search?: string;
|
|
32
|
+
isFeatured?: boolean;
|
|
33
|
+
drinkingIdentity?: 'SOBER' | 'SOBER_CURIOUS' | 'MINDFUL';
|
|
34
|
+
sortBy?: 'createdAt' | 'title' | 'viewCount';
|
|
35
|
+
sortOrder?: 'asc' | 'desc';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface PaginatedLibraryResponse {
|
|
39
|
+
content: LibraryContentResponse[];
|
|
40
|
+
total: number;
|
|
41
|
+
page: number;
|
|
42
|
+
limit: number;
|
|
43
|
+
totalPages: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface LibraryCategoryResponse {
|
|
47
|
+
id: string;
|
|
48
|
+
name: string;
|
|
49
|
+
slug: string;
|
|
50
|
+
description?: string;
|
|
51
|
+
icon?: string;
|
|
52
|
+
contentCount?: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// QUERY HOOKS
|
|
57
|
+
// ============================================================================
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get paginated list of library content with optional filters
|
|
61
|
+
*
|
|
62
|
+
* @param filters - Query parameters for filtering and pagination
|
|
63
|
+
* @param options - TanStack Query options
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```tsx
|
|
67
|
+
* const { data, isLoading } = useLibraryContent({
|
|
68
|
+
* page: 1,
|
|
69
|
+
* limit: 20,
|
|
70
|
+
* categoryId: 'cat-123',
|
|
71
|
+
* type: 'ARTICLE',
|
|
72
|
+
* isFeatured: true
|
|
73
|
+
* });
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export function useLibraryContent(
|
|
77
|
+
filters?: LibraryFilters,
|
|
78
|
+
options?: Omit<UseQueryOptions<PaginatedLibraryResponse>, 'queryKey' | 'queryFn'>
|
|
79
|
+
) {
|
|
80
|
+
return useQuery({
|
|
81
|
+
queryKey: libraryKeys.list(filters),
|
|
82
|
+
queryFn: async (): Promise<PaginatedLibraryResponse> => {
|
|
83
|
+
const client = getApiClient();
|
|
84
|
+
const response = await client.get('/api/v1/library', {
|
|
85
|
+
params: filters,
|
|
86
|
+
});
|
|
87
|
+
return response.data;
|
|
88
|
+
},
|
|
89
|
+
...options,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get featured library content
|
|
95
|
+
*
|
|
96
|
+
* @param options - TanStack Query options
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```tsx
|
|
100
|
+
* const { data, isLoading } = useFeaturedContent();
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
export function useFeaturedContent(
|
|
104
|
+
options?: Omit<UseQueryOptions<LibraryContentResponse[]>, 'queryKey' | 'queryFn'>
|
|
105
|
+
) {
|
|
106
|
+
return useQuery({
|
|
107
|
+
queryKey: libraryKeys.featured(),
|
|
108
|
+
queryFn: async (): Promise<LibraryContentResponse[]> => {
|
|
109
|
+
const client = getApiClient();
|
|
110
|
+
const response = await client.get('/api/v1/library/featured');
|
|
111
|
+
return response.data;
|
|
112
|
+
},
|
|
113
|
+
...options,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get all library categories
|
|
119
|
+
*
|
|
120
|
+
* @param options - TanStack Query options
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```tsx
|
|
124
|
+
* const { data, isLoading } = useLibraryCategories();
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
export function useLibraryCategories(
|
|
128
|
+
options?: Omit<UseQueryOptions<LibraryCategoryResponse[]>, 'queryKey' | 'queryFn'>
|
|
129
|
+
) {
|
|
130
|
+
return useQuery({
|
|
131
|
+
queryKey: libraryKeys.categories(),
|
|
132
|
+
queryFn: async (): Promise<LibraryCategoryResponse[]> => {
|
|
133
|
+
const client = getApiClient();
|
|
134
|
+
const response = await client.get('/api/v1/library/categories');
|
|
135
|
+
return response.data;
|
|
136
|
+
},
|
|
137
|
+
...options,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Get a single library item by ID
|
|
143
|
+
*
|
|
144
|
+
* @param id - Library content ID
|
|
145
|
+
* @param options - TanStack Query options
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```tsx
|
|
149
|
+
* const { data, isLoading } = useLibraryItem('lib-123');
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
export function useLibraryItem(
|
|
153
|
+
id: string,
|
|
154
|
+
options?: Omit<UseQueryOptions<LibraryContentDetailResponse>, 'queryKey' | 'queryFn'>
|
|
155
|
+
) {
|
|
156
|
+
return useQuery({
|
|
157
|
+
queryKey: libraryKeys.detail(id),
|
|
158
|
+
queryFn: async (): Promise<LibraryContentDetailResponse> => {
|
|
159
|
+
const client = getApiClient();
|
|
160
|
+
const response = await client.get(`/api/v1/library/${id}`);
|
|
161
|
+
return response.data;
|
|
162
|
+
},
|
|
163
|
+
enabled: !!id,
|
|
164
|
+
...options,
|
|
165
|
+
});
|
|
166
|
+
}
|