@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.
Files changed (129) hide show
  1. package/README.md +276 -0
  2. package/dist/__tests__/e2e.test.d.ts +7 -0
  3. package/dist/__tests__/e2e.test.js +472 -0
  4. package/dist/api/client.d.ts +11 -0
  5. package/dist/api/client.js +61 -0
  6. package/dist/api/mutations/admin.d.ts +167 -0
  7. package/dist/api/mutations/admin.js +326 -0
  8. package/dist/api/mutations/ambassadors.d.ts +52 -0
  9. package/dist/api/mutations/ambassadors.js +148 -0
  10. package/dist/api/mutations/auth.d.ts +267 -0
  11. package/dist/api/mutations/auth.js +332 -0
  12. package/dist/api/mutations/bookings.d.ts +59 -0
  13. package/dist/api/mutations/bookings.js +143 -0
  14. package/dist/api/mutations/event-chat.d.ts +35 -0
  15. package/dist/api/mutations/event-chat.js +147 -0
  16. package/dist/api/mutations/events.d.ts +87 -0
  17. package/dist/api/mutations/events.js +205 -0
  18. package/dist/api/mutations/grow90.d.ts +36 -0
  19. package/dist/api/mutations/grow90.js +132 -0
  20. package/dist/api/mutations/hubs.d.ts +111 -0
  21. package/dist/api/mutations/hubs.js +240 -0
  22. package/dist/api/mutations/index.d.ts +22 -0
  23. package/dist/api/mutations/index.js +39 -0
  24. package/dist/api/mutations/jack.d.ts +61 -0
  25. package/dist/api/mutations/jack.js +104 -0
  26. package/dist/api/mutations/library.d.ts +67 -0
  27. package/dist/api/mutations/library.js +168 -0
  28. package/dist/api/mutations/map.d.ts +153 -0
  29. package/dist/api/mutations/map.js +181 -0
  30. package/dist/api/mutations/matching.d.ts +130 -0
  31. package/dist/api/mutations/matching.js +204 -0
  32. package/dist/api/mutations/notifications.d.ts +63 -0
  33. package/dist/api/mutations/notifications.js +106 -0
  34. package/dist/api/mutations/offers.d.ts +26 -0
  35. package/dist/api/mutations/offers.js +47 -0
  36. package/dist/api/mutations/subscriptions.d.ts +127 -0
  37. package/dist/api/mutations/subscriptions.js +140 -0
  38. package/dist/api/mutations/support.d.ts +165 -0
  39. package/dist/api/mutations/support.js +307 -0
  40. package/dist/api/mutations/users.d.ts +211 -0
  41. package/dist/api/mutations/users.js +261 -0
  42. package/dist/api/queries/admin.d.ts +257 -0
  43. package/dist/api/queries/admin.js +320 -0
  44. package/dist/api/queries/ambassadors.d.ts +53 -0
  45. package/dist/api/queries/ambassadors.js +98 -0
  46. package/dist/api/queries/auth.d.ts +16 -0
  47. package/dist/api/queries/auth.js +25 -0
  48. package/dist/api/queries/bookings.d.ts +91 -0
  49. package/dist/api/queries/bookings.js +102 -0
  50. package/dist/api/queries/businesses.d.ts +212 -0
  51. package/dist/api/queries/businesses.js +154 -0
  52. package/dist/api/queries/event-chat.d.ts +19 -0
  53. package/dist/api/queries/event-chat.js +75 -0
  54. package/dist/api/queries/events.d.ts +322 -0
  55. package/dist/api/queries/events.js +221 -0
  56. package/dist/api/queries/grow90.d.ts +26 -0
  57. package/dist/api/queries/grow90.js +85 -0
  58. package/dist/api/queries/hubs.d.ts +165 -0
  59. package/dist/api/queries/hubs.js +143 -0
  60. package/dist/api/queries/index.d.ts +23 -0
  61. package/dist/api/queries/index.js +40 -0
  62. package/dist/api/queries/jack.d.ts +63 -0
  63. package/dist/api/queries/jack.js +92 -0
  64. package/dist/api/queries/library.d.ts +132 -0
  65. package/dist/api/queries/library.js +120 -0
  66. package/dist/api/queries/map.d.ts +216 -0
  67. package/dist/api/queries/map.js +278 -0
  68. package/dist/api/queries/matching.d.ts +136 -0
  69. package/dist/api/queries/matching.js +161 -0
  70. package/dist/api/queries/notifications.d.ts +78 -0
  71. package/dist/api/queries/notifications.js +88 -0
  72. package/dist/api/queries/offers.d.ts +91 -0
  73. package/dist/api/queries/offers.js +103 -0
  74. package/dist/api/queries/subscriptions.d.ts +56 -0
  75. package/dist/api/queries/subscriptions.js +73 -0
  76. package/dist/api/queries/support.d.ts +106 -0
  77. package/dist/api/queries/support.js +202 -0
  78. package/dist/api/queries/users.d.ts +293 -0
  79. package/dist/api/queries/users.js +370 -0
  80. package/dist/api/types.d.ts +464 -0
  81. package/dist/api/types.js +9 -0
  82. package/dist/hooks/useAuth.d.ts +5 -0
  83. package/dist/hooks/useAuth.js +39 -0
  84. package/dist/hooks/useUser.d.ts +43 -0
  85. package/dist/hooks/useUser.js +44 -0
  86. package/dist/index.d.ts +36 -0
  87. package/dist/index.js +67 -0
  88. package/package.json +62 -0
  89. package/src/__tests__/e2e.test.ts +502 -0
  90. package/src/api/client.ts +71 -0
  91. package/src/api/mutations/admin.ts +531 -0
  92. package/src/api/mutations/ambassadors.ts +185 -0
  93. package/src/api/mutations/auth.ts +350 -0
  94. package/src/api/mutations/bookings.ts +190 -0
  95. package/src/api/mutations/event-chat.ts +177 -0
  96. package/src/api/mutations/events.ts +273 -0
  97. package/src/api/mutations/grow90.ts +169 -0
  98. package/src/api/mutations/hubs.ts +385 -0
  99. package/src/api/mutations/index.ts +23 -0
  100. package/src/api/mutations/jack.ts +130 -0
  101. package/src/api/mutations/library.ts +212 -0
  102. package/src/api/mutations/map.ts +230 -0
  103. package/src/api/mutations/matching.ts +271 -0
  104. package/src/api/mutations/notifications.ts +114 -0
  105. package/src/api/mutations/offers.ts +73 -0
  106. package/src/api/mutations/subscriptions.ts +162 -0
  107. package/src/api/mutations/support.ts +390 -0
  108. package/src/api/mutations/users.ts +271 -0
  109. package/src/api/queries/admin.ts +480 -0
  110. package/src/api/queries/ambassadors.ts +139 -0
  111. package/src/api/queries/auth.ts +24 -0
  112. package/src/api/queries/bookings.ts +135 -0
  113. package/src/api/queries/businesses.ts +203 -0
  114. package/src/api/queries/event-chat.ts +78 -0
  115. package/src/api/queries/events.ts +272 -0
  116. package/src/api/queries/grow90.ts +98 -0
  117. package/src/api/queries/hubs.ts +211 -0
  118. package/src/api/queries/index.ts +24 -0
  119. package/src/api/queries/jack.ts +127 -0
  120. package/src/api/queries/library.ts +166 -0
  121. package/src/api/queries/map.ts +331 -0
  122. package/src/api/queries/matching.ts +238 -0
  123. package/src/api/queries/notifications.ts +103 -0
  124. package/src/api/queries/offers.ts +136 -0
  125. package/src/api/queries/subscriptions.ts +91 -0
  126. package/src/api/queries/support.ts +235 -0
  127. package/src/api/queries/users.ts +393 -0
  128. package/src/api/types.ts +596 -0
  129. 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
+ }