@spotsdev/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/dist/api/client.d.ts +12 -0
- package/dist/api/client.js +68 -0
- package/dist/api/mutations/clubs.d.ts +47 -0
- package/dist/api/mutations/clubs.js +95 -0
- package/dist/api/mutations/conversations.d.ts +45 -0
- package/dist/api/mutations/conversations.js +110 -0
- package/dist/api/mutations/index.d.ts +13 -0
- package/dist/api/mutations/index.js +38 -0
- package/dist/api/mutations/notifications.d.ts +38 -0
- package/dist/api/mutations/notifications.js +64 -0
- package/dist/api/mutations/orders.d.ts +73 -0
- package/dist/api/mutations/orders.js +116 -0
- package/dist/api/mutations/posts.d.ts +123 -0
- package/dist/api/mutations/posts.js +229 -0
- package/dist/api/mutations/products.d.ts +81 -0
- package/dist/api/mutations/products.js +102 -0
- package/dist/api/mutations/spots.d.ts +59 -0
- package/dist/api/mutations/spots.js +129 -0
- package/dist/api/mutations/users.d.ts +71 -0
- package/dist/api/mutations/users.js +173 -0
- package/dist/api/queries/auth.d.ts +37 -0
- package/dist/api/queries/auth.js +61 -0
- package/dist/api/queries/clubs.d.ts +52 -0
- package/dist/api/queries/clubs.js +116 -0
- package/dist/api/queries/conversations.d.ts +52 -0
- package/dist/api/queries/conversations.js +83 -0
- package/dist/api/queries/index.d.ts +26 -0
- package/dist/api/queries/index.js +65 -0
- package/dist/api/queries/misc.d.ts +54 -0
- package/dist/api/queries/misc.js +129 -0
- package/dist/api/queries/notifications.d.ts +34 -0
- package/dist/api/queries/notifications.js +62 -0
- package/dist/api/queries/orders.d.ts +45 -0
- package/dist/api/queries/orders.js +93 -0
- package/dist/api/queries/posts.d.ts +55 -0
- package/dist/api/queries/posts.js +130 -0
- package/dist/api/queries/products.d.ts +52 -0
- package/dist/api/queries/products.js +89 -0
- package/dist/api/queries/spots.d.ts +78 -0
- package/dist/api/queries/spots.js +168 -0
- package/dist/api/queries/templates.d.ts +42 -0
- package/dist/api/queries/templates.js +86 -0
- package/dist/api/queries/users.d.ts +90 -0
- package/dist/api/queries/users.js +187 -0
- package/dist/api/services/index.d.ts +2 -0
- package/dist/api/services/index.js +8 -0
- package/dist/api/services/marketplace.d.ts +129 -0
- package/dist/api/services/marketplace.js +168 -0
- package/dist/api/types.d.ts +54 -0
- package/dist/api/types.js +34 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +73 -0
- package/package.json +57 -0
- package/src/api/client.ts +78 -0
- package/src/api/mutations/clubs.ts +107 -0
- package/src/api/mutations/conversations.ts +124 -0
- package/src/api/mutations/index.ts +29 -0
- package/src/api/mutations/notifications.ts +70 -0
- package/src/api/mutations/orders.ts +174 -0
- package/src/api/mutations/posts.ts +278 -0
- package/src/api/mutations/products.ts +160 -0
- package/src/api/mutations/spots.ts +146 -0
- package/src/api/mutations/users.ts +197 -0
- package/src/api/queries/auth.ts +67 -0
- package/src/api/queries/clubs.ts +135 -0
- package/src/api/queries/conversations.ts +94 -0
- package/src/api/queries/index.ts +48 -0
- package/src/api/queries/misc.ts +140 -0
- package/src/api/queries/notifications.ts +66 -0
- package/src/api/queries/orders.ts +119 -0
- package/src/api/queries/posts.ts +142 -0
- package/src/api/queries/products.ts +123 -0
- package/src/api/queries/spots.ts +201 -0
- package/src/api/queries/templates.ts +95 -0
- package/src/api/queries/users.ts +206 -0
- package/src/api/services/index.ts +6 -0
- package/src/api/services/marketplace.ts +265 -0
- package/src/api/types.ts +144 -0
- package/src/index.ts +63 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Misc Query Hooks
|
|
3
|
+
*
|
|
4
|
+
* TanStack Query hooks for cities, vibes, and other reference data.
|
|
5
|
+
* All types come from @prisma/client (via ../types re-export).
|
|
6
|
+
*
|
|
7
|
+
* API Response Patterns:
|
|
8
|
+
* - Reference data (vibes, templates, interests): { success, data: T[], timestamp }
|
|
9
|
+
* - Large datasets (cities, spots, posts): { success, data: { data: T[], meta }, timestamp }
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { useQuery, UseQueryOptions, UseQueryResult } from '@tanstack/react-query';
|
|
13
|
+
import { getApiClient } from '../client';
|
|
14
|
+
import type { City, Vibe, LifeSituation, Interest, Intention, ApiResponse, PaginatedResponse } from '../types';
|
|
15
|
+
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// QUERY KEYS
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
export const miscKeys = {
|
|
21
|
+
cities: () => ['cities'] as const,
|
|
22
|
+
vibes: () => ['vibes'] as const,
|
|
23
|
+
lifeSituations: () => ['life-situations'] as const,
|
|
24
|
+
interests: () => ['interests'] as const,
|
|
25
|
+
intentions: () => ['intentions'] as const,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// QUERY HOOKS
|
|
30
|
+
// ============================================================================
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Get all cities (paginated endpoint)
|
|
34
|
+
*
|
|
35
|
+
* @endpoint GET /api/v1/cities
|
|
36
|
+
* @returns PaginatedResponse with cities array
|
|
37
|
+
*/
|
|
38
|
+
export function useCities(
|
|
39
|
+
options?: Omit<UseQueryOptions<City[]>, 'queryKey' | 'queryFn'>
|
|
40
|
+
): UseQueryResult<City[]> {
|
|
41
|
+
return useQuery({
|
|
42
|
+
queryKey: miscKeys.cities(),
|
|
43
|
+
queryFn: async (): Promise<City[]> => {
|
|
44
|
+
const client = getApiClient();
|
|
45
|
+
const response = await client.get<ApiResponse<PaginatedResponse<City>>>('/api/v1/cities');
|
|
46
|
+
// Cities endpoint returns paginated format: { data: { data: [], meta: {} } }
|
|
47
|
+
return response.data.data.data;
|
|
48
|
+
},
|
|
49
|
+
staleTime: 1000 * 60 * 60,
|
|
50
|
+
...options,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Get all vibes (flat array - reference data)
|
|
56
|
+
*
|
|
57
|
+
* @endpoint GET /api/v1/vibes
|
|
58
|
+
* @returns Flat array of vibes (not paginated)
|
|
59
|
+
*/
|
|
60
|
+
export function useVibes(
|
|
61
|
+
options?: Omit<UseQueryOptions<Vibe[]>, 'queryKey' | 'queryFn'>
|
|
62
|
+
): UseQueryResult<Vibe[]> {
|
|
63
|
+
return useQuery({
|
|
64
|
+
queryKey: miscKeys.vibes(),
|
|
65
|
+
queryFn: async (): Promise<Vibe[]> => {
|
|
66
|
+
const client = getApiClient();
|
|
67
|
+
const response = await client.get<ApiResponse<Vibe[]>>('/api/v1/vibes');
|
|
68
|
+
// Vibes endpoint returns flat array: { data: [] }
|
|
69
|
+
return response.data.data;
|
|
70
|
+
},
|
|
71
|
+
staleTime: 1000 * 60 * 60,
|
|
72
|
+
...options,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get all life situations (flat array - reference data for onboarding)
|
|
78
|
+
*
|
|
79
|
+
* @endpoint GET /api/v1/life-situations
|
|
80
|
+
* @returns Flat array of life situations (not paginated)
|
|
81
|
+
*/
|
|
82
|
+
export function useLifeSituations(
|
|
83
|
+
options?: Omit<UseQueryOptions<LifeSituation[]>, 'queryKey' | 'queryFn'>
|
|
84
|
+
): UseQueryResult<LifeSituation[]> {
|
|
85
|
+
return useQuery({
|
|
86
|
+
queryKey: miscKeys.lifeSituations(),
|
|
87
|
+
queryFn: async (): Promise<LifeSituation[]> => {
|
|
88
|
+
const client = getApiClient();
|
|
89
|
+
const response = await client.get<ApiResponse<LifeSituation[]>>('/api/v1/life-situations');
|
|
90
|
+
// Life situations endpoint returns flat array: { data: [] }
|
|
91
|
+
return response.data.data;
|
|
92
|
+
},
|
|
93
|
+
staleTime: 1000 * 60 * 60,
|
|
94
|
+
...options,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Get all interests (flat array - reference data)
|
|
100
|
+
*
|
|
101
|
+
* @endpoint GET /api/v1/interests
|
|
102
|
+
* @returns Flat array of interests (not paginated)
|
|
103
|
+
*/
|
|
104
|
+
export function useInterests(
|
|
105
|
+
options?: Omit<UseQueryOptions<Interest[]>, 'queryKey' | 'queryFn'>
|
|
106
|
+
): UseQueryResult<Interest[]> {
|
|
107
|
+
return useQuery({
|
|
108
|
+
queryKey: miscKeys.interests(),
|
|
109
|
+
queryFn: async (): Promise<Interest[]> => {
|
|
110
|
+
const client = getApiClient();
|
|
111
|
+
const response = await client.get<ApiResponse<Interest[]>>('/api/v1/interests');
|
|
112
|
+
// Interests endpoint returns flat array: { data: [] }
|
|
113
|
+
return response.data.data;
|
|
114
|
+
},
|
|
115
|
+
staleTime: 1000 * 60 * 60,
|
|
116
|
+
...options,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Get all intentions (flat array - reference data)
|
|
122
|
+
*
|
|
123
|
+
* @endpoint GET /api/v1/intentions
|
|
124
|
+
* @returns Flat array of intentions (not paginated)
|
|
125
|
+
*/
|
|
126
|
+
export function useIntentions(
|
|
127
|
+
options?: Omit<UseQueryOptions<Intention[]>, 'queryKey' | 'queryFn'>
|
|
128
|
+
): UseQueryResult<Intention[]> {
|
|
129
|
+
return useQuery({
|
|
130
|
+
queryKey: miscKeys.intentions(),
|
|
131
|
+
queryFn: async (): Promise<Intention[]> => {
|
|
132
|
+
const client = getApiClient();
|
|
133
|
+
const response = await client.get<ApiResponse<Intention[]>>('/api/v1/intentions');
|
|
134
|
+
// Intentions endpoint returns flat array: { data: [] }
|
|
135
|
+
return response.data.data;
|
|
136
|
+
},
|
|
137
|
+
staleTime: 1000 * 60 * 60,
|
|
138
|
+
...options,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notifications Query Hooks
|
|
3
|
+
*
|
|
4
|
+
* TanStack Query hooks for notification operations.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { useQuery, UseQueryOptions, UseQueryResult } from '@tanstack/react-query';
|
|
8
|
+
import { getApiClient } from '../client';
|
|
9
|
+
import type { Notification, ApiResponse } from '../types';
|
|
10
|
+
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// QUERY KEYS
|
|
13
|
+
// ============================================================================
|
|
14
|
+
|
|
15
|
+
export const notificationKeys = {
|
|
16
|
+
all: ['notifications'] as const,
|
|
17
|
+
lists: () => [...notificationKeys.all, 'list'] as const,
|
|
18
|
+
list: (params?: { limit?: number; unreadOnly?: boolean }) => [...notificationKeys.lists(), params] as const,
|
|
19
|
+
unreadCount: () => [...notificationKeys.all, 'unreadCount'] as const,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// QUERY HOOKS
|
|
24
|
+
// ============================================================================
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get notifications for current user
|
|
28
|
+
*
|
|
29
|
+
* @endpoint GET /api/v1/notifications
|
|
30
|
+
*/
|
|
31
|
+
export function useNotifications(
|
|
32
|
+
params?: { limit?: number; unreadOnly?: boolean },
|
|
33
|
+
options?: Omit<UseQueryOptions<Notification[]>, 'queryKey' | 'queryFn'>
|
|
34
|
+
): UseQueryResult<Notification[]> {
|
|
35
|
+
return useQuery({
|
|
36
|
+
queryKey: notificationKeys.list(params),
|
|
37
|
+
queryFn: async (): Promise<Notification[]> => {
|
|
38
|
+
const client = getApiClient();
|
|
39
|
+
const queryParams = new URLSearchParams();
|
|
40
|
+
if (params?.limit) queryParams.set('limit', String(params.limit));
|
|
41
|
+
if (params?.unreadOnly) queryParams.set('unreadOnly', String(params.unreadOnly));
|
|
42
|
+
const response = await client.get<ApiResponse<Notification[]>>(`/api/v1/notifications?${queryParams}`);
|
|
43
|
+
return response.data.data;
|
|
44
|
+
},
|
|
45
|
+
...options,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get unread notification count
|
|
51
|
+
*
|
|
52
|
+
* @endpoint GET /api/v1/notifications/unread-count
|
|
53
|
+
*/
|
|
54
|
+
export function useUnreadNotificationCount(
|
|
55
|
+
options?: Omit<UseQueryOptions<number>, 'queryKey' | 'queryFn'>
|
|
56
|
+
): UseQueryResult<number> {
|
|
57
|
+
return useQuery({
|
|
58
|
+
queryKey: notificationKeys.unreadCount(),
|
|
59
|
+
queryFn: async (): Promise<number> => {
|
|
60
|
+
const client = getApiClient();
|
|
61
|
+
const response = await client.get<ApiResponse<{ count: number }>>('/api/v1/notifications/unread-count');
|
|
62
|
+
return response.data.data.count;
|
|
63
|
+
},
|
|
64
|
+
...options,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orders Query Hooks
|
|
3
|
+
*
|
|
4
|
+
* TanStack Query hooks for order-related operations.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { useQuery, UseQueryOptions, UseQueryResult } from '@tanstack/react-query';
|
|
8
|
+
import { getApiClient } from '../client';
|
|
9
|
+
import type { Order, OrderItem, Product, Spot, OrderStatus, ApiResponse, PaginatedResponse } from '../types';
|
|
10
|
+
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// QUERY KEYS
|
|
13
|
+
// ============================================================================
|
|
14
|
+
|
|
15
|
+
export const orderKeys = {
|
|
16
|
+
all: ['orders'] as const,
|
|
17
|
+
lists: () => [...orderKeys.all, 'list'] as const,
|
|
18
|
+
list: (filters?: Record<string, unknown>) => [...orderKeys.lists(), filters] as const,
|
|
19
|
+
details: () => [...orderKeys.all, 'detail'] as const,
|
|
20
|
+
detail: (id: string) => [...orderKeys.details(), id] as const,
|
|
21
|
+
myOrders: () => [...orderKeys.all, 'my'] as const,
|
|
22
|
+
spotOrders: (spotId: string) => [...orderKeys.all, 'spot', spotId] as const,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// TYPES
|
|
27
|
+
// ============================================================================
|
|
28
|
+
|
|
29
|
+
export interface OrderWithDetails extends Order {
|
|
30
|
+
items: (OrderItem & {
|
|
31
|
+
product: Pick<Product, 'id' | 'name' | 'slug' | 'type' | 'imageUrl'>;
|
|
32
|
+
})[];
|
|
33
|
+
spot: Pick<Spot, 'id' | 'name' | 'slug'>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface OrderFilters {
|
|
37
|
+
status?: OrderStatus;
|
|
38
|
+
limit?: number;
|
|
39
|
+
page?: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ============================================================================
|
|
43
|
+
// QUERY HOOKS
|
|
44
|
+
// ============================================================================
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Get current user's orders (purchases)
|
|
48
|
+
*
|
|
49
|
+
* @endpoint GET /api/v1/users/me/orders
|
|
50
|
+
*/
|
|
51
|
+
export function useMyOrders(
|
|
52
|
+
params?: OrderFilters,
|
|
53
|
+
options?: Omit<UseQueryOptions<PaginatedResponse<OrderWithDetails>>, 'queryKey' | 'queryFn'>
|
|
54
|
+
): UseQueryResult<PaginatedResponse<OrderWithDetails>> {
|
|
55
|
+
return useQuery({
|
|
56
|
+
queryKey: orderKeys.list({ ...params, my: true }),
|
|
57
|
+
queryFn: async (): Promise<PaginatedResponse<OrderWithDetails>> => {
|
|
58
|
+
const client = getApiClient();
|
|
59
|
+
const queryParams = new URLSearchParams();
|
|
60
|
+
if (params?.status) queryParams.set('status', params.status);
|
|
61
|
+
if (params?.limit) queryParams.set('limit', String(params.limit));
|
|
62
|
+
if (params?.page) queryParams.set('page', String(params.page));
|
|
63
|
+
const response = await client.get<ApiResponse<PaginatedResponse<OrderWithDetails>>>(
|
|
64
|
+
`/api/v1/users/me/orders?${queryParams}`
|
|
65
|
+
);
|
|
66
|
+
return response.data.data;
|
|
67
|
+
},
|
|
68
|
+
...options,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get a single order by ID
|
|
74
|
+
*
|
|
75
|
+
* @endpoint GET /api/v1/orders/{orderId}
|
|
76
|
+
*/
|
|
77
|
+
export function useOrder(
|
|
78
|
+
orderId: string,
|
|
79
|
+
options?: Omit<UseQueryOptions<OrderWithDetails>, 'queryKey' | 'queryFn'>
|
|
80
|
+
): UseQueryResult<OrderWithDetails> {
|
|
81
|
+
return useQuery({
|
|
82
|
+
queryKey: orderKeys.detail(orderId),
|
|
83
|
+
queryFn: async (): Promise<OrderWithDetails> => {
|
|
84
|
+
const client = getApiClient();
|
|
85
|
+
const response = await client.get<ApiResponse<OrderWithDetails>>(`/api/v1/orders/${orderId}`);
|
|
86
|
+
return response.data.data;
|
|
87
|
+
},
|
|
88
|
+
enabled: !!orderId,
|
|
89
|
+
...options,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get orders for a spot (seller view)
|
|
95
|
+
*
|
|
96
|
+
* @endpoint GET /api/v1/seller/spots/{spotId}/orders
|
|
97
|
+
*/
|
|
98
|
+
export function useSpotOrders(
|
|
99
|
+
spotId: string,
|
|
100
|
+
params?: OrderFilters,
|
|
101
|
+
options?: Omit<UseQueryOptions<PaginatedResponse<OrderWithDetails>>, 'queryKey' | 'queryFn'>
|
|
102
|
+
): UseQueryResult<PaginatedResponse<OrderWithDetails>> {
|
|
103
|
+
return useQuery({
|
|
104
|
+
queryKey: orderKeys.spotOrders(spotId),
|
|
105
|
+
queryFn: async (): Promise<PaginatedResponse<OrderWithDetails>> => {
|
|
106
|
+
const client = getApiClient();
|
|
107
|
+
const queryParams = new URLSearchParams();
|
|
108
|
+
if (params?.status) queryParams.set('status', params.status);
|
|
109
|
+
if (params?.limit) queryParams.set('limit', String(params.limit));
|
|
110
|
+
if (params?.page) queryParams.set('page', String(params.page));
|
|
111
|
+
const response = await client.get<ApiResponse<PaginatedResponse<OrderWithDetails>>>(
|
|
112
|
+
`/api/v1/seller/spots/${spotId}/orders?${queryParams}`
|
|
113
|
+
);
|
|
114
|
+
return response.data.data;
|
|
115
|
+
},
|
|
116
|
+
enabled: !!spotId,
|
|
117
|
+
...options,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Posts Query Hooks
|
|
3
|
+
*
|
|
4
|
+
* TanStack Query hooks for post/board operations.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { useQuery, UseQueryOptions, UseQueryResult } from '@tanstack/react-query';
|
|
8
|
+
import { getApiClient } from '../client';
|
|
9
|
+
import type { Post, PostResponse, ApiResponse, PostStatusDto } from '../types';
|
|
10
|
+
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// QUERY KEYS
|
|
13
|
+
// ============================================================================
|
|
14
|
+
|
|
15
|
+
export const postKeys = {
|
|
16
|
+
all: ['posts'] as const,
|
|
17
|
+
lists: () => [...postKeys.all, 'list'] as const,
|
|
18
|
+
list: (filters?: Record<string, unknown>) => [...postKeys.lists(), filters] as const,
|
|
19
|
+
bySpot: (spotId: string, filters?: Record<string, unknown>) => [...postKeys.all, 'spot', spotId, filters] as const,
|
|
20
|
+
details: () => [...postKeys.all, 'detail'] as const,
|
|
21
|
+
detail: (id: string) => [...postKeys.details(), id] as const,
|
|
22
|
+
responses: (postId: string) => [...postKeys.detail(postId), 'responses'] as const,
|
|
23
|
+
status: (postId: string) => [...postKeys.detail(postId), 'status'] as const,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// QUERY HOOKS
|
|
28
|
+
// ============================================================================
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get posts for a spot
|
|
32
|
+
*
|
|
33
|
+
* @endpoint GET /api/v1/spots/{spotId}/posts
|
|
34
|
+
*/
|
|
35
|
+
export function useSpotPosts(
|
|
36
|
+
spotId: string,
|
|
37
|
+
params?: { postType?: string; status?: string; page?: number; limit?: number },
|
|
38
|
+
options?: Omit<UseQueryOptions<Post[]>, 'queryKey' | 'queryFn'>
|
|
39
|
+
): UseQueryResult<Post[]> {
|
|
40
|
+
return useQuery({
|
|
41
|
+
queryKey: postKeys.bySpot(spotId, params),
|
|
42
|
+
queryFn: async (): Promise<Post[]> => {
|
|
43
|
+
const client = getApiClient();
|
|
44
|
+
const queryParams = new URLSearchParams();
|
|
45
|
+
if (params?.postType) queryParams.set('postType', params.postType);
|
|
46
|
+
if (params?.status) queryParams.set('status', params.status);
|
|
47
|
+
if (params?.page) queryParams.set('page', String(params.page));
|
|
48
|
+
if (params?.limit) queryParams.set('limit', String(params.limit));
|
|
49
|
+
const response = await client.get<ApiResponse<Post[]>>(`/api/v1/spots/${spotId}/posts?${queryParams}`);
|
|
50
|
+
return response.data.data;
|
|
51
|
+
},
|
|
52
|
+
enabled: !!spotId,
|
|
53
|
+
...options,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get a single post by ID
|
|
59
|
+
*
|
|
60
|
+
* @endpoint GET /api/v1/posts/{postId}
|
|
61
|
+
*/
|
|
62
|
+
export function usePost(
|
|
63
|
+
postId: string,
|
|
64
|
+
options?: Omit<UseQueryOptions<Post>, 'queryKey' | 'queryFn'>
|
|
65
|
+
): UseQueryResult<Post> {
|
|
66
|
+
return useQuery({
|
|
67
|
+
queryKey: postKeys.detail(postId),
|
|
68
|
+
queryFn: async (): Promise<Post> => {
|
|
69
|
+
const client = getApiClient();
|
|
70
|
+
const response = await client.get<ApiResponse<Post>>(`/api/v1/posts/${postId}`);
|
|
71
|
+
return response.data.data;
|
|
72
|
+
},
|
|
73
|
+
enabled: !!postId,
|
|
74
|
+
...options,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Get responses for a post
|
|
80
|
+
*
|
|
81
|
+
* @endpoint GET /api/v1/posts/{postId}/responses
|
|
82
|
+
*/
|
|
83
|
+
export function usePostResponses(
|
|
84
|
+
postId: string,
|
|
85
|
+
options?: Omit<UseQueryOptions<PostResponse[]>, 'queryKey' | 'queryFn'>
|
|
86
|
+
): UseQueryResult<PostResponse[]> {
|
|
87
|
+
return useQuery({
|
|
88
|
+
queryKey: postKeys.responses(postId),
|
|
89
|
+
queryFn: async (): Promise<PostResponse[]> => {
|
|
90
|
+
const client = getApiClient();
|
|
91
|
+
const response = await client.get<ApiResponse<PostResponse[]>>(`/api/v1/posts/${postId}/responses`);
|
|
92
|
+
return response.data.data;
|
|
93
|
+
},
|
|
94
|
+
enabled: !!postId,
|
|
95
|
+
...options,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Get all posts (with filters)
|
|
101
|
+
*
|
|
102
|
+
* @endpoint GET /api/v1/posts
|
|
103
|
+
*/
|
|
104
|
+
export function usePosts(
|
|
105
|
+
params?: { postType?: string; limit?: number },
|
|
106
|
+
options?: Omit<UseQueryOptions<Post[]>, 'queryKey' | 'queryFn'>
|
|
107
|
+
): UseQueryResult<Post[]> {
|
|
108
|
+
return useQuery({
|
|
109
|
+
queryKey: postKeys.list(params),
|
|
110
|
+
queryFn: async (): Promise<Post[]> => {
|
|
111
|
+
const client = getApiClient();
|
|
112
|
+
const queryParams = new URLSearchParams();
|
|
113
|
+
if (params?.postType) queryParams.set('postType', params.postType);
|
|
114
|
+
if (params?.limit) queryParams.set('limit', String(params.limit));
|
|
115
|
+
const response = await client.get<ApiResponse<Post[]>>(`/api/v1/posts?${queryParams}`);
|
|
116
|
+
return response.data.data;
|
|
117
|
+
},
|
|
118
|
+
...options,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Get user's status for a post (read/hidden/pinned)
|
|
125
|
+
*
|
|
126
|
+
* @endpoint GET /api/v1/posts/{postId}/status
|
|
127
|
+
*/
|
|
128
|
+
export function usePostStatus(
|
|
129
|
+
postId: string,
|
|
130
|
+
options?: Omit<UseQueryOptions<PostStatusDto>, 'queryKey' | 'queryFn'>
|
|
131
|
+
): UseQueryResult<PostStatusDto> {
|
|
132
|
+
return useQuery({
|
|
133
|
+
queryKey: postKeys.status(postId),
|
|
134
|
+
queryFn: async (): Promise<PostStatusDto> => {
|
|
135
|
+
const client = getApiClient();
|
|
136
|
+
const response = await client.get<ApiResponse<PostStatusDto>>(`/api/v1/posts/${postId}/status`);
|
|
137
|
+
return response.data.data;
|
|
138
|
+
},
|
|
139
|
+
enabled: !!postId,
|
|
140
|
+
...options,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Products Query Hooks
|
|
3
|
+
*
|
|
4
|
+
* TanStack Query hooks for product-related operations.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { useQuery, UseQueryOptions, UseQueryResult } from '@tanstack/react-query';
|
|
8
|
+
import { getApiClient } from '../client';
|
|
9
|
+
import type { Product, ApiResponse, PaginatedResponse, ProductType, ProductStatus } from '../types';
|
|
10
|
+
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// QUERY KEYS
|
|
13
|
+
// ============================================================================
|
|
14
|
+
|
|
15
|
+
export const productKeys = {
|
|
16
|
+
all: ['products'] as const,
|
|
17
|
+
lists: () => [...productKeys.all, 'list'] as const,
|
|
18
|
+
list: (filters?: Record<string, unknown>) => [...productKeys.lists(), filters] as const,
|
|
19
|
+
details: () => [...productKeys.all, 'detail'] as const,
|
|
20
|
+
detail: (id: string) => [...productKeys.details(), id] as const,
|
|
21
|
+
bySlug: (spotId: string, slug: string) => [...productKeys.all, 'slug', spotId, slug] as const,
|
|
22
|
+
bySpot: (spotId: string) => [...productKeys.all, 'spot', spotId] as const,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// TYPES
|
|
27
|
+
// ============================================================================
|
|
28
|
+
|
|
29
|
+
export interface ProductFilters {
|
|
30
|
+
spotId?: string;
|
|
31
|
+
type?: ProductType;
|
|
32
|
+
status?: ProductStatus;
|
|
33
|
+
limit?: number;
|
|
34
|
+
page?: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ProductWithSpot extends Product {
|
|
38
|
+
spot: {
|
|
39
|
+
id: string;
|
|
40
|
+
name: string;
|
|
41
|
+
slug: string;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ============================================================================
|
|
46
|
+
// QUERY HOOKS
|
|
47
|
+
// ============================================================================
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get products for a spot (public browse)
|
|
51
|
+
*
|
|
52
|
+
* @endpoint GET /api/v1/spots/{spotId}/products
|
|
53
|
+
*/
|
|
54
|
+
export function useSpotProducts(
|
|
55
|
+
spotId: string,
|
|
56
|
+
params?: { type?: ProductType; limit?: number; page?: number },
|
|
57
|
+
options?: Omit<UseQueryOptions<PaginatedResponse<Product>>, 'queryKey' | 'queryFn'>
|
|
58
|
+
): UseQueryResult<PaginatedResponse<Product>> {
|
|
59
|
+
return useQuery({
|
|
60
|
+
queryKey: productKeys.bySpot(spotId),
|
|
61
|
+
queryFn: async (): Promise<PaginatedResponse<Product>> => {
|
|
62
|
+
const client = getApiClient();
|
|
63
|
+
const queryParams = new URLSearchParams();
|
|
64
|
+
if (params?.limit) queryParams.set('limit', String(params.limit));
|
|
65
|
+
if (params?.page) queryParams.set('page', String(params.page));
|
|
66
|
+
if (params?.type) queryParams.set('type', params.type);
|
|
67
|
+
const response = await client.get<ApiResponse<PaginatedResponse<Product>>>(
|
|
68
|
+
`/api/v1/spots/${spotId}/products?${queryParams}`
|
|
69
|
+
);
|
|
70
|
+
return response.data.data;
|
|
71
|
+
},
|
|
72
|
+
enabled: !!spotId,
|
|
73
|
+
...options,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get a product by ID
|
|
79
|
+
*
|
|
80
|
+
* @endpoint GET /api/v1/products/{productId}
|
|
81
|
+
*/
|
|
82
|
+
export function useProduct(
|
|
83
|
+
productId: string,
|
|
84
|
+
options?: Omit<UseQueryOptions<ProductWithSpot>, 'queryKey' | 'queryFn'>
|
|
85
|
+
): UseQueryResult<ProductWithSpot> {
|
|
86
|
+
return useQuery({
|
|
87
|
+
queryKey: productKeys.detail(productId),
|
|
88
|
+
queryFn: async (): Promise<ProductWithSpot> => {
|
|
89
|
+
const client = getApiClient();
|
|
90
|
+
const response = await client.get<ApiResponse<ProductWithSpot>>(`/api/v1/products/${productId}`);
|
|
91
|
+
return response.data.data;
|
|
92
|
+
},
|
|
93
|
+
enabled: !!productId,
|
|
94
|
+
...options,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Get a product by slug (within a spot)
|
|
100
|
+
*
|
|
101
|
+
* @endpoint GET /api/v1/spots/{spotId}/products/slug/{slug}
|
|
102
|
+
*/
|
|
103
|
+
export function useProductBySlug(
|
|
104
|
+
spotId: string,
|
|
105
|
+
slug: string,
|
|
106
|
+
options?: Omit<UseQueryOptions<ProductWithSpot>, 'queryKey' | 'queryFn'>
|
|
107
|
+
): UseQueryResult<ProductWithSpot> {
|
|
108
|
+
return useQuery({
|
|
109
|
+
queryKey: productKeys.bySlug(spotId, slug),
|
|
110
|
+
queryFn: async (): Promise<ProductWithSpot> => {
|
|
111
|
+
const client = getApiClient();
|
|
112
|
+
const response = await client.get<ApiResponse<ProductWithSpot>>(
|
|
113
|
+
`/api/v1/spots/${spotId}/products/slug/${slug}`
|
|
114
|
+
);
|
|
115
|
+
return response.data.data;
|
|
116
|
+
},
|
|
117
|
+
enabled: !!spotId && !!slug,
|
|
118
|
+
...options,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Note: To list products as a seller, use useSpotProducts with your spot ID.
|
|
123
|
+
// There is no cross-spot product listing endpoint currently.
|