@instockng/api-client 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 +263 -0
- package/package.json +45 -0
- package/src/client.ts +57 -0
- package/src/fetchers/carts.ts +202 -0
- package/src/fetchers/delivery-zones.ts +29 -0
- package/src/fetchers/index.ts +22 -0
- package/src/fetchers/orders.ts +48 -0
- package/src/fetchers/products.ts +46 -0
- package/src/hooks/admin/abandoned-carts.ts +102 -0
- package/src/hooks/admin/brands.ts +134 -0
- package/src/hooks/admin/customers.ts +31 -0
- package/src/hooks/admin/delivery-zones.ts +236 -0
- package/src/hooks/admin/discount-codes.ts +222 -0
- package/src/hooks/admin/index.ts +17 -0
- package/src/hooks/admin/inventory.ts +137 -0
- package/src/hooks/admin/orders.ts +229 -0
- package/src/hooks/admin/products.ts +116 -0
- package/src/hooks/admin/stats.ts +30 -0
- package/src/hooks/admin/variants.ts +173 -0
- package/src/hooks/admin/warehouses.ts +143 -0
- package/src/hooks/public/carts.ts +298 -0
- package/src/hooks/public/delivery-zones.ts +34 -0
- package/src/hooks/public/index.ts +10 -0
- package/src/hooks/public/orders.ts +66 -0
- package/src/hooks/public/products.ts +57 -0
- package/src/hooks/use-query-unwrapped.ts +30 -0
- package/src/hooks/useApiConfig.ts +22 -0
- package/src/index.ts +42 -0
- package/src/provider.tsx +89 -0
- package/src/rpc-client.ts +106 -0
- package/src/rpc-types.ts +121 -0
- package/src/types.ts +39 -0
- package/src/utils/query-keys.ts +121 -0
package/README.md
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# @oms/api-client
|
|
2
|
+
|
|
3
|
+
React Query hooks for the OMS API. This package provides type-safe API client with hooks for both public (e-commerce) and admin (internal) endpoints.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @oms/api-client
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @oms/api-client
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
### 1. Setup Provider
|
|
16
|
+
|
|
17
|
+
Wrap your app with `ApiClientProvider`:
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { ApiClientProvider } from '@oms/api-client';
|
|
21
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
22
|
+
|
|
23
|
+
const queryClient = new QueryClient();
|
|
24
|
+
|
|
25
|
+
function App() {
|
|
26
|
+
return (
|
|
27
|
+
<QueryClientProvider client={queryClient}>
|
|
28
|
+
<ApiClientProvider baseURL="https://api.yoursite.com">
|
|
29
|
+
<YourApp />
|
|
30
|
+
</ApiClientProvider>
|
|
31
|
+
</QueryClientProvider>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 2. Use Hooks
|
|
37
|
+
|
|
38
|
+
#### Orders Example
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import { useGetOrder, useConfirmOrder } from '@oms/api-client';
|
|
42
|
+
|
|
43
|
+
function OrderConfirmation() {
|
|
44
|
+
const { orderId, token } = useParams();
|
|
45
|
+
|
|
46
|
+
// Get order details
|
|
47
|
+
const { data, isLoading } = useGetOrder(orderId, token);
|
|
48
|
+
|
|
49
|
+
// Confirm order
|
|
50
|
+
const confirmMutation = useConfirmOrder({
|
|
51
|
+
onSuccess: () => {
|
|
52
|
+
console.log('Order confirmed!');
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<div>
|
|
58
|
+
<h1>Order #{data?.order.orderNumber}</h1>
|
|
59
|
+
<button onClick={() => confirmMutation.mutate({ orderId, token })}>
|
|
60
|
+
Confirm Order
|
|
61
|
+
</button>
|
|
62
|
+
</div>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
#### Products Example
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import { useGetProducts } from '@oms/api-client';
|
|
71
|
+
|
|
72
|
+
function ProductList({ brandId }) {
|
|
73
|
+
const { data, isLoading } = useGetProducts(brandId);
|
|
74
|
+
|
|
75
|
+
if (isLoading) return <div>Loading...</div>;
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<div>
|
|
79
|
+
{data?.products.map(product => (
|
|
80
|
+
<div key={product.id}>
|
|
81
|
+
<h3>{product.name}</h3>
|
|
82
|
+
<p>{product.description}</p>
|
|
83
|
+
</div>
|
|
84
|
+
))}
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
#### Cart & Checkout Example
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
import {
|
|
94
|
+
useGetCart,
|
|
95
|
+
useCreateCart,
|
|
96
|
+
useAddToCart,
|
|
97
|
+
useRemoveCartItem,
|
|
98
|
+
useCheckout
|
|
99
|
+
} from '@oms/api-client';
|
|
100
|
+
|
|
101
|
+
function ShoppingCart() {
|
|
102
|
+
const [cartId, setCartId] = useState<string | null>(null);
|
|
103
|
+
const { data: cart } = useGetCart(cartId!, { enabled: !!cartId });
|
|
104
|
+
|
|
105
|
+
const createCart = useCreateCart({
|
|
106
|
+
onSuccess: (data) => setCartId(data.cart.id)
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const addToCart = useAddToCart(cartId!, {
|
|
110
|
+
onSuccess: () => {
|
|
111
|
+
// Cart is automatically updated via React Query
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const removeItem = useRemoveCartItem(cartId!, '');
|
|
116
|
+
|
|
117
|
+
const checkout = useCheckout(cartId!, {
|
|
118
|
+
onSuccess: (data) => {
|
|
119
|
+
console.log('Order created:', data.order.id);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const handleAddToCart = (sku: string, quantity: number) => {
|
|
124
|
+
if (!cartId) {
|
|
125
|
+
createCart.mutate({
|
|
126
|
+
brandId: 'your-brand-id',
|
|
127
|
+
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString()
|
|
128
|
+
});
|
|
129
|
+
} else {
|
|
130
|
+
addToCart.mutate({ sku, quantity });
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const handleCheckout = (customerInfo) => {
|
|
135
|
+
checkout.mutate({
|
|
136
|
+
...customerInfo,
|
|
137
|
+
paymentMethod: 'cod'
|
|
138
|
+
});
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<div>
|
|
143
|
+
{cart?.items.map(item => (
|
|
144
|
+
<div key={item.id}>
|
|
145
|
+
<span>{item.variant.product.name} x {item.quantity}</span>
|
|
146
|
+
<button onClick={() => removeItem.mutate()}>Remove</button>
|
|
147
|
+
</div>
|
|
148
|
+
))}
|
|
149
|
+
<button onClick={() => handleCheckout(formData)}>
|
|
150
|
+
Checkout
|
|
151
|
+
</button>
|
|
152
|
+
</div>
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
#### Admin Hooks (Internal OMS)
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
// Coming soon - admin hooks will be added here
|
|
161
|
+
// import { useGetOrders, useUpdateOrder } from '@oms/api-client/admin';
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 3. Custom Error Handling
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
<ApiClientProvider
|
|
168
|
+
baseURL="https://api.yoursite.com"
|
|
169
|
+
onError={(error) => {
|
|
170
|
+
console.error('API Error:', error.message);
|
|
171
|
+
// Show toast notification, etc.
|
|
172
|
+
}}
|
|
173
|
+
>
|
|
174
|
+
<App />
|
|
175
|
+
</ApiClientProvider>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Available Hooks
|
|
179
|
+
|
|
180
|
+
### Public Hooks
|
|
181
|
+
|
|
182
|
+
#### Orders
|
|
183
|
+
- `useGetOrder(orderId, token, options?)` - Get order details by ID and token
|
|
184
|
+
- `useConfirmOrder(options?)` - Confirm a prospect order
|
|
185
|
+
|
|
186
|
+
#### Products
|
|
187
|
+
- `useGetProducts(brandId, options?)` - Get all products for a brand
|
|
188
|
+
|
|
189
|
+
#### Carts
|
|
190
|
+
- `useGetCart(cartId, options?)` - Get cart details by ID
|
|
191
|
+
- `useCreateCart(options?)` - Create a new cart
|
|
192
|
+
- `useAddToCart(cartId, options?)` - Add item to cart by SKU
|
|
193
|
+
- `useRemoveCartItem(cartId, itemId, options?)` - Remove item from cart
|
|
194
|
+
- `useApplyDiscount(cartId, options?)` - Apply discount code to cart
|
|
195
|
+
- `useRemoveDiscount(cartId, options?)` - Remove discount from cart
|
|
196
|
+
- `useCheckout(cartId, options?)` - Checkout cart and create order
|
|
197
|
+
|
|
198
|
+
#### Delivery Zones
|
|
199
|
+
- `useGetDeliveryZones(params?, options?)` - Get delivery zones with optional filters
|
|
200
|
+
|
|
201
|
+
### Admin Hooks
|
|
202
|
+
|
|
203
|
+
Coming soon! Will include hooks for:
|
|
204
|
+
- Orders management
|
|
205
|
+
- Inventory management
|
|
206
|
+
- Products management
|
|
207
|
+
- Brands management
|
|
208
|
+
|
|
209
|
+
## Type Safety
|
|
210
|
+
|
|
211
|
+
All hooks are fully typed using OpenAPI-generated types from `@oms/types`. TypeScript will autocomplete and validate all API requests and responses.
|
|
212
|
+
|
|
213
|
+
## Advanced Usage
|
|
214
|
+
|
|
215
|
+
### Query Keys
|
|
216
|
+
|
|
217
|
+
Access query keys for manual cache invalidation:
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
import { queryKeys } from '@oms/api-client';
|
|
221
|
+
import { useQueryClient } from '@tanstack/react-query';
|
|
222
|
+
|
|
223
|
+
function MyComponent() {
|
|
224
|
+
const queryClient = useQueryClient();
|
|
225
|
+
|
|
226
|
+
// Invalidate specific order
|
|
227
|
+
queryClient.invalidateQueries({
|
|
228
|
+
queryKey: queryKeys.public.orders.detail(orderId, token)
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// Invalidate all orders
|
|
232
|
+
queryClient.invalidateQueries({
|
|
233
|
+
queryKey: queryKeys.public.orders.all
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Direct API Client Access
|
|
239
|
+
|
|
240
|
+
For advanced use cases, access the axios client directly:
|
|
241
|
+
|
|
242
|
+
```tsx
|
|
243
|
+
import { getApiClient } from '@oms/api-client';
|
|
244
|
+
|
|
245
|
+
const client = getApiClient();
|
|
246
|
+
const response = await client.get('/custom-endpoint');
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Development
|
|
250
|
+
|
|
251
|
+
### Updating Types
|
|
252
|
+
|
|
253
|
+
When the backend API changes, regenerate types:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
# From the monorepo root
|
|
257
|
+
cd packages/types
|
|
258
|
+
pnpm run generate
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## License
|
|
262
|
+
|
|
263
|
+
ISC
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@instockng/api-client",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "React Query hooks for OMS API",
|
|
5
|
+
"main": "src/index.ts",
|
|
6
|
+
"types": "src/index.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.ts",
|
|
9
|
+
"./fetchers": "./src/fetchers/index.ts",
|
|
10
|
+
"./public": "./src/hooks/public/index.ts",
|
|
11
|
+
"./admin": "./src/hooks/admin/index.ts"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"src",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"keywords": [
|
|
18
|
+
"api",
|
|
19
|
+
"react-query",
|
|
20
|
+
"hooks",
|
|
21
|
+
"oms"
|
|
22
|
+
],
|
|
23
|
+
"author": "",
|
|
24
|
+
"license": "ISC",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@trpc/client": "^11.6.0",
|
|
27
|
+
"hono": "^4.9.10"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"@tanstack/react-query": "^5.0.0",
|
|
31
|
+
"axios": "^1.6.0",
|
|
32
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@tanstack/react-query": "^5.17.19",
|
|
36
|
+
"@types/react": "npm:types-react@^19.0.0-rc.1",
|
|
37
|
+
"axios": "^1.6.5",
|
|
38
|
+
"react": "^19.0.0",
|
|
39
|
+
"typescript": "^5.3.3"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"type-check": "tsc --noEmit",
|
|
43
|
+
"build": "tsc --build"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Axios client configuration for OMS API
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import axios, { AxiosInstance, AxiosError } from 'axios';
|
|
6
|
+
|
|
7
|
+
export interface ApiClientConfig {
|
|
8
|
+
baseURL: string;
|
|
9
|
+
onError?: (error: AxiosError) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let apiClient: AxiosInstance | null = null;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Initialize the API client with configuration
|
|
16
|
+
*/
|
|
17
|
+
export function initializeApiClient(config: ApiClientConfig): AxiosInstance {
|
|
18
|
+
apiClient = axios.create({
|
|
19
|
+
baseURL: config.baseURL,
|
|
20
|
+
headers: {
|
|
21
|
+
'Content-Type': 'application/json',
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Response interceptor for error handling
|
|
26
|
+
apiClient.interceptors.response.use(
|
|
27
|
+
(response) => response,
|
|
28
|
+
(error: AxiosError) => {
|
|
29
|
+
// Call custom error handler if provided
|
|
30
|
+
if (config.onError) {
|
|
31
|
+
config.onError(error);
|
|
32
|
+
}
|
|
33
|
+
return Promise.reject(error);
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return apiClient;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get the current API client instance
|
|
42
|
+
*/
|
|
43
|
+
export function getApiClient(): AxiosInstance {
|
|
44
|
+
if (!apiClient) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
'API client not initialized. Make sure to wrap your app with ApiClientProvider.'
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
return apiClient;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Reset the API client (mainly for testing)
|
|
54
|
+
*/
|
|
55
|
+
export function resetApiClient(): void {
|
|
56
|
+
apiClient = null;
|
|
57
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cart fetcher functions
|
|
3
|
+
*
|
|
4
|
+
* These are the actual data-fetching functions used by hooks.
|
|
5
|
+
* They can also be imported directly in Server Components.
|
|
6
|
+
*
|
|
7
|
+
* API URL is hardcoded to https://oms-api.instock.ng
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { createRpcClients } from '../rpc-client';
|
|
11
|
+
|
|
12
|
+
const API_URL = 'https://oms-api.instock.ng';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Fetch a cart by ID
|
|
16
|
+
*
|
|
17
|
+
* @param cartId - Cart UUID
|
|
18
|
+
* @returns Cart with items, brand, and delivery zone
|
|
19
|
+
*/
|
|
20
|
+
export async function fetchCart(cartId: string) {
|
|
21
|
+
const clients = createRpcClients(API_URL);
|
|
22
|
+
const res = await clients.carts[':id'].$get({
|
|
23
|
+
param: { id: cartId },
|
|
24
|
+
});
|
|
25
|
+
if (!res.ok) {
|
|
26
|
+
throw new Error(`Failed to fetch cart: ${res.statusText}`);
|
|
27
|
+
}
|
|
28
|
+
return res.json();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Create a new cart
|
|
33
|
+
*
|
|
34
|
+
* @param brandSlug - Brand slug
|
|
35
|
+
* @returns Newly created cart
|
|
36
|
+
*/
|
|
37
|
+
export async function createCart(brandSlug: string) {
|
|
38
|
+
const clients = createRpcClients(API_URL);
|
|
39
|
+
const res = await clients.carts.index.$post({
|
|
40
|
+
json: { brandSlug },
|
|
41
|
+
});
|
|
42
|
+
if (!res.ok) {
|
|
43
|
+
throw new Error(`Failed to create cart: ${res.statusText}`);
|
|
44
|
+
}
|
|
45
|
+
return res.json();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Update a cart
|
|
50
|
+
*
|
|
51
|
+
* @param cartId - Cart UUID
|
|
52
|
+
* @param data - Cart update data (customer info, deliveryZoneId, etc.)
|
|
53
|
+
* @returns Updated cart
|
|
54
|
+
*/
|
|
55
|
+
export async function updateCart(
|
|
56
|
+
cartId: string,
|
|
57
|
+
data: {
|
|
58
|
+
customerPhone?: string | null;
|
|
59
|
+
customerEmail?: string | null;
|
|
60
|
+
customerFirstName?: string | null;
|
|
61
|
+
customerLastName?: string | null;
|
|
62
|
+
deliveryZoneId?: string | null;
|
|
63
|
+
ifUnmodifiedSince?: string;
|
|
64
|
+
}
|
|
65
|
+
) {
|
|
66
|
+
const clients = createRpcClients(API_URL);
|
|
67
|
+
const res = await clients.carts[':id'].$patch({
|
|
68
|
+
param: { id: cartId },
|
|
69
|
+
json: data,
|
|
70
|
+
});
|
|
71
|
+
if (!res.ok) {
|
|
72
|
+
throw new Error(`Failed to update cart: ${res.statusText}`);
|
|
73
|
+
}
|
|
74
|
+
return res.json();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Add an item to cart
|
|
79
|
+
*
|
|
80
|
+
* @param cartId - Cart UUID
|
|
81
|
+
* @param sku - Product variant SKU
|
|
82
|
+
* @param quantity - Quantity to add
|
|
83
|
+
* @returns Updated cart
|
|
84
|
+
*/
|
|
85
|
+
export async function addCartItem(cartId: string, sku: string, quantity: number) {
|
|
86
|
+
const clients = createRpcClients(API_URL);
|
|
87
|
+
const res = await clients.carts[':id'].items.$post({
|
|
88
|
+
param: { id: cartId },
|
|
89
|
+
json: { sku, quantity },
|
|
90
|
+
});
|
|
91
|
+
if (!res.ok) {
|
|
92
|
+
throw new Error(`Failed to add item to cart: ${res.statusText}`);
|
|
93
|
+
}
|
|
94
|
+
return res.json();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Update a cart item quantity
|
|
99
|
+
*
|
|
100
|
+
* @param cartId - Cart UUID
|
|
101
|
+
* @param itemId - Cart item UUID
|
|
102
|
+
* @param quantity - New quantity
|
|
103
|
+
* @returns Updated cart
|
|
104
|
+
*/
|
|
105
|
+
export async function updateCartItem(cartId: string, itemId: string, quantity: number) {
|
|
106
|
+
const clients = createRpcClients(API_URL);
|
|
107
|
+
const res = await clients.carts[':id'].items[':itemId'].$patch({
|
|
108
|
+
param: { id: cartId, itemId },
|
|
109
|
+
json: { quantity },
|
|
110
|
+
});
|
|
111
|
+
if (!res.ok) {
|
|
112
|
+
throw new Error(`Failed to update cart item: ${res.statusText}`);
|
|
113
|
+
}
|
|
114
|
+
return res.json();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Remove an item from cart
|
|
119
|
+
*
|
|
120
|
+
* @param cartId - Cart UUID
|
|
121
|
+
* @param itemId - Cart item UUID
|
|
122
|
+
* @returns Updated cart
|
|
123
|
+
*/
|
|
124
|
+
export async function removeCartItem(cartId: string, itemId: string) {
|
|
125
|
+
const clients = createRpcClients(API_URL);
|
|
126
|
+
const res = await clients.carts[':id'].items[':itemId'].$delete({
|
|
127
|
+
param: { id: cartId, itemId },
|
|
128
|
+
});
|
|
129
|
+
if (!res.ok) {
|
|
130
|
+
throw new Error(`Failed to remove cart item: ${res.statusText}`);
|
|
131
|
+
}
|
|
132
|
+
return res.json();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Apply a discount code to cart
|
|
137
|
+
*
|
|
138
|
+
* @param cartId - Cart UUID
|
|
139
|
+
* @param code - Discount code
|
|
140
|
+
* @returns Updated cart
|
|
141
|
+
*/
|
|
142
|
+
export async function applyDiscount(cartId: string, code: string) {
|
|
143
|
+
const clients = createRpcClients(API_URL);
|
|
144
|
+
const res = await clients.carts[':id']['apply-discount'].$post({
|
|
145
|
+
param: { id: cartId },
|
|
146
|
+
json: { code },
|
|
147
|
+
});
|
|
148
|
+
if (!res.ok) {
|
|
149
|
+
throw new Error(`Failed to apply discount: ${res.statusText}`);
|
|
150
|
+
}
|
|
151
|
+
return res.json();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Remove discount from cart
|
|
156
|
+
*
|
|
157
|
+
* @param cartId - Cart UUID
|
|
158
|
+
* @returns Updated cart
|
|
159
|
+
*/
|
|
160
|
+
export async function removeDiscount(cartId: string) {
|
|
161
|
+
const clients = createRpcClients(API_URL);
|
|
162
|
+
const res = await clients.carts[':id']['remove-discount'].$post({
|
|
163
|
+
param: { id: cartId },
|
|
164
|
+
});
|
|
165
|
+
if (!res.ok) {
|
|
166
|
+
throw new Error(`Failed to remove discount: ${res.statusText}`);
|
|
167
|
+
}
|
|
168
|
+
return res.json();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Checkout a cart
|
|
173
|
+
*
|
|
174
|
+
* @param cartId - Cart UUID
|
|
175
|
+
* @param checkoutData - Checkout information (customer details, delivery, payment)
|
|
176
|
+
* @returns Created order
|
|
177
|
+
*/
|
|
178
|
+
export async function checkoutCart(
|
|
179
|
+
cartId: string,
|
|
180
|
+
checkoutData: {
|
|
181
|
+
firstName: string;
|
|
182
|
+
lastName: string;
|
|
183
|
+
phone?: string;
|
|
184
|
+
email?: string;
|
|
185
|
+
address: string;
|
|
186
|
+
city: string;
|
|
187
|
+
deliveryZoneId: string;
|
|
188
|
+
paymentMethod: 'cod' | 'online';
|
|
189
|
+
paystackReference?: string;
|
|
190
|
+
ifUnmodifiedSince?: string;
|
|
191
|
+
}
|
|
192
|
+
) {
|
|
193
|
+
const clients = createRpcClients(API_URL);
|
|
194
|
+
const res = await clients.carts[':id'].checkout.$post({
|
|
195
|
+
param: { id: cartId },
|
|
196
|
+
json: checkoutData,
|
|
197
|
+
});
|
|
198
|
+
if (!res.ok) {
|
|
199
|
+
throw new Error(`Failed to checkout cart: ${res.statusText}`);
|
|
200
|
+
}
|
|
201
|
+
return res.json();
|
|
202
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Delivery zone fetcher functions
|
|
3
|
+
*
|
|
4
|
+
* These are the actual data-fetching functions used by hooks.
|
|
5
|
+
* They can also be imported directly in Server Components.
|
|
6
|
+
*
|
|
7
|
+
* API URL is hardcoded to https://oms-api.instock.ng
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { createRpcClients } from '../rpc-client';
|
|
11
|
+
|
|
12
|
+
const API_URL = 'https://oms-api.instock.ng';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Fetch delivery zones
|
|
16
|
+
*
|
|
17
|
+
* @param brandId - Optional brand UUID to filter brand-specific zones
|
|
18
|
+
* @returns List of delivery zones with states
|
|
19
|
+
*/
|
|
20
|
+
export async function fetchDeliveryZones(brandId?: string) {
|
|
21
|
+
const clients = createRpcClients(API_URL);
|
|
22
|
+
const res = await clients.deliveryZones.index.$get({
|
|
23
|
+
query: brandId ? { brandId } : {},
|
|
24
|
+
});
|
|
25
|
+
if (!res.ok) {
|
|
26
|
+
throw new Error(`Failed to fetch delivery zones: ${res.statusText}`);
|
|
27
|
+
}
|
|
28
|
+
return res.json();
|
|
29
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data fetcher functions for Server Components
|
|
3
|
+
*
|
|
4
|
+
* These functions can be used directly in Next.js Server Components
|
|
5
|
+
* without needing React hooks. They use the same underlying logic
|
|
6
|
+
* as the client-side hooks, ensuring no code duplication.
|
|
7
|
+
*
|
|
8
|
+
* @example Server Component
|
|
9
|
+
* ```tsx
|
|
10
|
+
* import { fetchProductBySlug, fetchCart } from '@oms/api-client/fetchers';
|
|
11
|
+
*
|
|
12
|
+
* export default async function ProductPage({ params }) {
|
|
13
|
+
* const product = await fetchProductBySlug(params.slug);
|
|
14
|
+
* return <div>{product.name}</div>;
|
|
15
|
+
* }
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export * from './products';
|
|
20
|
+
export * from './carts';
|
|
21
|
+
export * from './orders';
|
|
22
|
+
export * from './delivery-zones';
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Order fetcher functions
|
|
3
|
+
*
|
|
4
|
+
* These are the actual data-fetching functions used by hooks.
|
|
5
|
+
* They can also be imported directly in Server Components.
|
|
6
|
+
*
|
|
7
|
+
* API URL is hardcoded to https://oms-api.instock.ng
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { createRpcClients } from '../rpc-client';
|
|
11
|
+
|
|
12
|
+
const API_URL = 'https://oms-api.instock.ng';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Fetch an order by ID and token
|
|
16
|
+
*
|
|
17
|
+
* @param orderId - Order UUID
|
|
18
|
+
* @param token - User action token
|
|
19
|
+
* @returns Order with items, delivery zone, and brand
|
|
20
|
+
*/
|
|
21
|
+
export async function fetchOrder(orderId: string, token: string) {
|
|
22
|
+
const clients = createRpcClients(API_URL);
|
|
23
|
+
const res = await clients.orders[':id'][':token'].$get({
|
|
24
|
+
param: { id: orderId, token },
|
|
25
|
+
});
|
|
26
|
+
if (!res.ok) {
|
|
27
|
+
throw new Error(`Failed to fetch order: ${res.statusText}`);
|
|
28
|
+
}
|
|
29
|
+
return res.json();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Confirm a prospect order
|
|
34
|
+
*
|
|
35
|
+
* @param orderId - Order UUID
|
|
36
|
+
* @param token - User action token
|
|
37
|
+
* @returns Confirmed order
|
|
38
|
+
*/
|
|
39
|
+
export async function confirmOrder(orderId: string, token: string) {
|
|
40
|
+
const clients = createRpcClients(API_URL);
|
|
41
|
+
const res = await clients.orders.confirm.$post({
|
|
42
|
+
json: { orderId, token },
|
|
43
|
+
});
|
|
44
|
+
if (!res.ok) {
|
|
45
|
+
throw new Error(`Failed to confirm order: ${res.statusText}`);
|
|
46
|
+
}
|
|
47
|
+
return res.json();
|
|
48
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Product fetcher functions
|
|
3
|
+
*
|
|
4
|
+
* These are the actual data-fetching functions used by hooks.
|
|
5
|
+
* They can also be imported directly in Server Components.
|
|
6
|
+
*
|
|
7
|
+
* API URL is hardcoded to https://oms-api.instock.ng
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { createRpcClients } from '../rpc-client';
|
|
11
|
+
|
|
12
|
+
const API_URL = 'https://oms-api.instock.ng';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Fetch products by brand ID
|
|
16
|
+
*
|
|
17
|
+
* @param brandId - Brand UUID
|
|
18
|
+
* @returns Products for the brand with variants and availability
|
|
19
|
+
*/
|
|
20
|
+
export async function fetchProductsByBrand(brandId: string) {
|
|
21
|
+
const clients = createRpcClients(API_URL);
|
|
22
|
+
const res = await clients.products[':brandId'].$get({
|
|
23
|
+
param: { brandId },
|
|
24
|
+
});
|
|
25
|
+
if (!res.ok) {
|
|
26
|
+
throw new Error(`Failed to fetch products: ${res.statusText}`);
|
|
27
|
+
}
|
|
28
|
+
return res.json();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Fetch a single product by slug
|
|
33
|
+
*
|
|
34
|
+
* @param slug - Product slug (e.g., 'cotton-t-shirt')
|
|
35
|
+
* @returns Product with variants and availability
|
|
36
|
+
*/
|
|
37
|
+
export async function fetchProductBySlug(slug: string) {
|
|
38
|
+
const clients = createRpcClients(API_URL);
|
|
39
|
+
const res = await clients.products.product[':slug'].$get({
|
|
40
|
+
param: { slug },
|
|
41
|
+
});
|
|
42
|
+
if (!res.ok) {
|
|
43
|
+
throw new Error(`Failed to fetch product: ${res.statusText}`);
|
|
44
|
+
}
|
|
45
|
+
return res.json();
|
|
46
|
+
}
|