@instockng/api-client 1.0.7 → 1.0.9
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/apps/backend/src/http-app.js +28 -0
- package/dist/apps/backend/src/lib/brand-response.d.ts +1 -1
- package/dist/apps/backend/src/lib/cart-helpers.d.ts +13 -11
- package/dist/apps/backend/src/lib/cart-helpers.js +28 -0
- package/dist/apps/backend/src/lib/cart-response.d.ts +5 -5
- package/dist/apps/backend/src/lib/clerk.js +23 -0
- package/dist/apps/backend/src/lib/delivery-zone-response.d.ts +6 -4
- package/dist/apps/backend/src/lib/discount-code-response.d.ts +2 -2
- package/dist/apps/backend/src/lib/meta-capi.d.ts +5 -0
- package/dist/apps/backend/src/lib/meta-capi.js +33 -2
- package/dist/apps/backend/src/lib/order-recovery.d.ts +30 -24
- package/dist/apps/backend/src/lib/order-response.d.ts +9 -7
- package/dist/apps/backend/src/lib/product-response.d.ts +4 -4
- package/dist/apps/backend/src/lib/sentry.d.ts +48 -0
- package/dist/apps/backend/src/lib/sentry.js +180 -0
- package/dist/apps/backend/src/lib/utils.d.ts +1 -1
- package/dist/apps/backend/src/lib/utils.js +1 -1
- package/dist/apps/backend/src/notifications/producers/meta-capi-producer.js +18 -4
- package/dist/apps/backend/src/routes/admin/abandoned-carts.d.ts +18 -10
- package/dist/apps/backend/src/routes/admin/brands.d.ts +4 -4
- package/dist/apps/backend/src/routes/admin/customers.d.ts +13 -7
- package/dist/apps/backend/src/routes/admin/delivery-zones.d.ts +23 -15
- package/dist/apps/backend/src/routes/admin/discount-codes.d.ts +10 -10
- package/dist/apps/backend/src/routes/admin/inventory.d.ts +4 -0
- package/dist/apps/backend/src/routes/admin/inventory.js +10 -0
- package/dist/apps/backend/src/routes/admin/orders.d.ts +78 -42
- package/dist/apps/backend/src/routes/admin/orders.js +25 -0
- package/dist/apps/backend/src/routes/admin/products.d.ts +28 -12
- package/dist/apps/backend/src/routes/admin/products.js +34 -15
- package/dist/apps/backend/src/routes/admin/stats.d.ts +13 -7
- package/dist/apps/backend/src/routes/admin/variants.d.ts +5 -5
- package/dist/apps/backend/src/routes/admin/variants.js +42 -18
- package/dist/apps/backend/src/routes/admin/warehouses.d.ts +11 -7
- package/dist/apps/backend/src/routes/public/brands.d.ts +1 -1
- package/dist/apps/backend/src/routes/public/carts.d.ts +85 -47
- package/dist/apps/backend/src/routes/public/carts.js +148 -1
- package/dist/apps/backend/src/routes/public/delivery-zones.d.ts +2 -0
- package/dist/apps/backend/src/routes/public/delivery-zones.js +2 -0
- package/dist/apps/backend/src/routes/public/orders.d.ts +26 -14
- package/dist/apps/backend/src/routes/public/orders.js +24 -0
- package/dist/apps/backend/src/routes/public/products.d.ts +14 -6
- package/dist/apps/backend/src/types/index.d.ts +1 -0
- package/dist/apps/backend/src/validators/delivery-zone.d.ts +4 -0
- package/dist/apps/backend/src/validators/delivery-zone.js +4 -0
- package/dist/apps/backend/src/validators/discount-code.d.ts +3 -3
- package/dist/apps/backend/src/validators/order.d.ts +3 -1
- package/dist/apps/backend/src/validators/order.js +3 -1
- package/dist/packages/api-client/src/fetchers/brands.d.ts +1 -1
- package/dist/packages/api-client/src/fetchers/carts.d.ts +85 -47
- package/dist/packages/api-client/src/fetchers/delivery-zones.d.ts +2 -0
- package/dist/packages/api-client/src/fetchers/orders.d.ts +26 -14
- package/dist/packages/api-client/src/fetchers/products.d.ts +14 -6
- package/dist/packages/api-client/src/hooks/admin/abandoned-carts.d.ts +18 -10
- package/dist/packages/api-client/src/hooks/admin/brands.d.ts +4 -4
- package/dist/packages/api-client/src/hooks/admin/customers.d.ts +13 -7
- package/dist/packages/api-client/src/hooks/admin/delivery-zones.d.ts +23 -15
- package/dist/packages/api-client/src/hooks/admin/discount-codes.d.ts +10 -10
- package/dist/packages/api-client/src/hooks/admin/inventory.d.ts +4 -0
- package/dist/packages/api-client/src/hooks/admin/orders.d.ts +78 -42
- package/dist/packages/api-client/src/hooks/admin/products.d.ts +14 -6
- package/dist/packages/api-client/src/hooks/admin/stats.d.ts +13 -7
- package/dist/packages/api-client/src/hooks/admin/variants.d.ts +5 -5
- package/dist/packages/api-client/src/hooks/admin/warehouses.d.ts +11 -7
- package/dist/packages/api-client/src/hooks/public/brands.d.ts +1 -1
- package/dist/packages/api-client/src/hooks/public/carts.d.ts +85 -47
- package/dist/packages/api-client/src/hooks/public/delivery-zones.d.ts +2 -0
- package/dist/packages/api-client/src/hooks/public/orders.d.ts +26 -14
- package/dist/packages/api-client/src/hooks/public/products.d.ts +14 -6
- package/dist/packages/api-client/src/rpc-client.d.ts +335 -187
- package/package.json +1 -1
|
@@ -8,19 +8,32 @@ import { toPricingItems } from '../../lib/cart-response';
|
|
|
8
8
|
import { validateCart, buildCartResponse, etagFrom, paymentFromZone, CART_INCLUDE_FULL, CART_ITEM_WITH_CART_INCLUDE, ORDER_INCLUDE_FULL, } from '../../lib/cart-helpers';
|
|
9
9
|
import { round, toNumber } from '../../lib/utils';
|
|
10
10
|
import { formatOrderResponse } from '../../lib/order-response';
|
|
11
|
+
import { captureMessage, captureException, getSampleRate } from '../../lib/sentry';
|
|
11
12
|
const app = new Hono()
|
|
12
13
|
// CREATE CART
|
|
13
14
|
.post('/', zValidator('json', z.object({
|
|
14
15
|
brandSlug: z.string(),
|
|
15
16
|
})), async (c) => {
|
|
17
|
+
const input = c.req.valid('json');
|
|
16
18
|
try {
|
|
17
|
-
const input = c.req.valid('json');
|
|
18
19
|
const prisma = getPrismaClient(c.env.DATABASE_URL);
|
|
19
20
|
// Look up brand by slug
|
|
20
21
|
const brand = await prisma.brand.findFirst({
|
|
21
22
|
where: { slug: input.brandSlug, deletedAt: null },
|
|
22
23
|
});
|
|
23
24
|
if (!brand) {
|
|
25
|
+
// Track brand not found with sampling - could indicate broken links
|
|
26
|
+
captureMessage('Brand not found during cart creation', {
|
|
27
|
+
level: 'info',
|
|
28
|
+
tags: {
|
|
29
|
+
error_code: 'BRAND_NOT_FOUND',
|
|
30
|
+
endpoint: 'POST /carts',
|
|
31
|
+
},
|
|
32
|
+
extra: {
|
|
33
|
+
brandSlug: input.brandSlug,
|
|
34
|
+
},
|
|
35
|
+
sampleRate: 0.1, // 10% sampling
|
|
36
|
+
});
|
|
24
37
|
return c.json({ error: { code: 'BRAND_NOT_FOUND', message: 'Brand not found' } }, 404);
|
|
25
38
|
}
|
|
26
39
|
// Create cart that expires in 7 days
|
|
@@ -37,6 +50,19 @@ const app = new Hono()
|
|
|
37
50
|
}
|
|
38
51
|
catch (error) {
|
|
39
52
|
console.error('Error creating cart:', error);
|
|
53
|
+
// Capture cart creation errors - could be database issues
|
|
54
|
+
captureException(error instanceof Error ? error : new Error(String(error)), {
|
|
55
|
+
level: 'error',
|
|
56
|
+
tags: {
|
|
57
|
+
error_code: 'CART_CREATE_ERROR',
|
|
58
|
+
endpoint: 'POST /carts',
|
|
59
|
+
},
|
|
60
|
+
extra: {
|
|
61
|
+
brandSlug: input.brandSlug,
|
|
62
|
+
errorMessage: error?.message,
|
|
63
|
+
},
|
|
64
|
+
honoContext: c,
|
|
65
|
+
});
|
|
40
66
|
return c.json({ error: { code: 'INTERNAL_ERROR', message: error.message } }, 500);
|
|
41
67
|
}
|
|
42
68
|
})
|
|
@@ -56,6 +82,17 @@ const app = new Hono()
|
|
|
56
82
|
}
|
|
57
83
|
catch (error) {
|
|
58
84
|
console.error('Error fetching cart:', error);
|
|
85
|
+
captureException(error instanceof Error ? error : new Error(String(error)), {
|
|
86
|
+
level: 'error',
|
|
87
|
+
tags: {
|
|
88
|
+
error_code: 'CART_FETCH_ERROR',
|
|
89
|
+
endpoint: 'GET /carts/:id',
|
|
90
|
+
},
|
|
91
|
+
extra: {
|
|
92
|
+
cartId: c.req.param('id'),
|
|
93
|
+
},
|
|
94
|
+
honoContext: c,
|
|
95
|
+
});
|
|
59
96
|
return c.json({ error: { code: 'INTERNAL_ERROR', message: error.message } }, 500);
|
|
60
97
|
}
|
|
61
98
|
})
|
|
@@ -125,6 +162,17 @@ const app = new Hono()
|
|
|
125
162
|
}
|
|
126
163
|
catch (error) {
|
|
127
164
|
console.error('Error updating cart:', error);
|
|
165
|
+
captureException(error instanceof Error ? error : new Error(String(error)), {
|
|
166
|
+
level: 'error',
|
|
167
|
+
tags: {
|
|
168
|
+
error_code: 'CART_UPDATE_ERROR',
|
|
169
|
+
endpoint: 'PATCH /carts/:id',
|
|
170
|
+
},
|
|
171
|
+
extra: {
|
|
172
|
+
cartId: c.req.param('id'),
|
|
173
|
+
},
|
|
174
|
+
honoContext: c,
|
|
175
|
+
});
|
|
128
176
|
return c.json({ error: { code: 'INTERNAL_ERROR', message: error.message } }, 500);
|
|
129
177
|
}
|
|
130
178
|
})
|
|
@@ -153,6 +201,20 @@ const app = new Hono()
|
|
|
153
201
|
},
|
|
154
202
|
});
|
|
155
203
|
if (!discount) {
|
|
204
|
+
// Track invalid discount code attempts
|
|
205
|
+
captureMessage('Invalid or expired discount code', {
|
|
206
|
+
level: 'info',
|
|
207
|
+
tags: {
|
|
208
|
+
error_code: 'INVALID_DISCOUNT_CODE',
|
|
209
|
+
brand: cart.brand?.slug || 'unknown',
|
|
210
|
+
},
|
|
211
|
+
extra: {
|
|
212
|
+
code,
|
|
213
|
+
cartId,
|
|
214
|
+
brandId: cart.brandId,
|
|
215
|
+
},
|
|
216
|
+
sampleRate: getSampleRate('INVALID_DISCOUNT_CODE'), // 20% sampling
|
|
217
|
+
});
|
|
156
218
|
return c.json({ error: { code: 'INVALID_DISCOUNT_CODE', message: 'Invalid or expired discount code' } }, 400);
|
|
157
219
|
}
|
|
158
220
|
// Check minimum purchase
|
|
@@ -160,6 +222,22 @@ const app = new Hono()
|
|
|
160
222
|
if (discount.minPurchase) {
|
|
161
223
|
const minPurchase = toNumber(discount.minPurchase);
|
|
162
224
|
if (pricing.subtotal < minPurchase) {
|
|
225
|
+
// Track minimum purchase not met
|
|
226
|
+
captureMessage('Minimum purchase not met for discount code', {
|
|
227
|
+
level: 'info',
|
|
228
|
+
tags: {
|
|
229
|
+
error_code: 'MIN_PURCHASE_NOT_MET',
|
|
230
|
+
brand: cart.brand?.slug || 'unknown',
|
|
231
|
+
},
|
|
232
|
+
extra: {
|
|
233
|
+
code,
|
|
234
|
+
cartId,
|
|
235
|
+
minPurchase,
|
|
236
|
+
currentSubtotal: pricing.subtotal,
|
|
237
|
+
shortfall: minPurchase - pricing.subtotal,
|
|
238
|
+
},
|
|
239
|
+
sampleRate: getSampleRate('MIN_PURCHASE_NOT_MET'), // 15% sampling
|
|
240
|
+
});
|
|
163
241
|
return c.json({ error: { code: 'MIN_PURCHASE_NOT_MET', message: `Minimum purchase of ₦${minPurchase.toLocaleString('en-NG', { minimumFractionDigits: 2 })} required` } }, 400);
|
|
164
242
|
}
|
|
165
243
|
}
|
|
@@ -174,6 +252,17 @@ const app = new Hono()
|
|
|
174
252
|
}
|
|
175
253
|
catch (error) {
|
|
176
254
|
console.error('Error applying discount to cart:', error);
|
|
255
|
+
captureException(error instanceof Error ? error : new Error(String(error)), {
|
|
256
|
+
level: 'error',
|
|
257
|
+
tags: {
|
|
258
|
+
error_code: 'DISCOUNT_APPLY_ERROR',
|
|
259
|
+
endpoint: 'POST /carts/:id/apply-discount',
|
|
260
|
+
},
|
|
261
|
+
extra: {
|
|
262
|
+
cartId: c.req.param('id'),
|
|
263
|
+
},
|
|
264
|
+
honoContext: c,
|
|
265
|
+
});
|
|
177
266
|
return c.json({ error: { code: 'INTERNAL_ERROR', message: error.message } }, 500);
|
|
178
267
|
}
|
|
179
268
|
})
|
|
@@ -197,6 +286,17 @@ const app = new Hono()
|
|
|
197
286
|
}
|
|
198
287
|
catch (error) {
|
|
199
288
|
console.error('Error removing discount from cart:', error);
|
|
289
|
+
captureException(error instanceof Error ? error : new Error(String(error)), {
|
|
290
|
+
level: 'error',
|
|
291
|
+
tags: {
|
|
292
|
+
error_code: 'DISCOUNT_REMOVE_ERROR',
|
|
293
|
+
endpoint: 'POST /carts/:id/remove-discount',
|
|
294
|
+
},
|
|
295
|
+
extra: {
|
|
296
|
+
cartId: c.req.param('id'),
|
|
297
|
+
},
|
|
298
|
+
honoContext: c,
|
|
299
|
+
});
|
|
200
300
|
return c.json({ error: { code: 'INTERNAL_ERROR', message: error.message } }, 500);
|
|
201
301
|
}
|
|
202
302
|
})
|
|
@@ -307,6 +407,17 @@ const app = new Hono()
|
|
|
307
407
|
}
|
|
308
408
|
catch (error) {
|
|
309
409
|
console.error('Error adding cart item:', error);
|
|
410
|
+
captureException(error instanceof Error ? error : new Error(String(error)), {
|
|
411
|
+
level: 'error',
|
|
412
|
+
tags: {
|
|
413
|
+
error_code: 'CART_ITEM_ADD_ERROR',
|
|
414
|
+
endpoint: 'POST /carts/:id/items',
|
|
415
|
+
},
|
|
416
|
+
extra: {
|
|
417
|
+
cartId: c.req.param('id'),
|
|
418
|
+
},
|
|
419
|
+
honoContext: c,
|
|
420
|
+
});
|
|
310
421
|
return c.json({ error: { code: 'INTERNAL_ERROR', message: error.message } }, 500);
|
|
311
422
|
}
|
|
312
423
|
})
|
|
@@ -346,6 +457,18 @@ const app = new Hono()
|
|
|
346
457
|
}
|
|
347
458
|
catch (error) {
|
|
348
459
|
console.error('Error updating cart item:', error);
|
|
460
|
+
captureException(error instanceof Error ? error : new Error(String(error)), {
|
|
461
|
+
level: 'error',
|
|
462
|
+
tags: {
|
|
463
|
+
error_code: 'CART_ITEM_UPDATE_ERROR',
|
|
464
|
+
endpoint: 'PATCH /carts/:id/items/:itemId',
|
|
465
|
+
},
|
|
466
|
+
extra: {
|
|
467
|
+
cartId: c.req.param('id'),
|
|
468
|
+
itemId: c.req.param('itemId'),
|
|
469
|
+
},
|
|
470
|
+
honoContext: c,
|
|
471
|
+
});
|
|
349
472
|
return c.json({ error: { code: 'INTERNAL_ERROR', message: error.message } }, 500);
|
|
350
473
|
}
|
|
351
474
|
})
|
|
@@ -384,6 +507,18 @@ const app = new Hono()
|
|
|
384
507
|
}
|
|
385
508
|
catch (error) {
|
|
386
509
|
console.error('Error removing cart item:', error);
|
|
510
|
+
captureException(error instanceof Error ? error : new Error(String(error)), {
|
|
511
|
+
level: 'error',
|
|
512
|
+
tags: {
|
|
513
|
+
error_code: 'CART_ITEM_DELETE_ERROR',
|
|
514
|
+
endpoint: 'DELETE /carts/:id/items/:itemId',
|
|
515
|
+
},
|
|
516
|
+
extra: {
|
|
517
|
+
cartId: c.req.param('id'),
|
|
518
|
+
itemId: c.req.param('itemId'),
|
|
519
|
+
},
|
|
520
|
+
honoContext: c,
|
|
521
|
+
});
|
|
387
522
|
return c.json({ error: { code: 'INTERNAL_ERROR', message: error.message } }, 500);
|
|
388
523
|
}
|
|
389
524
|
})
|
|
@@ -625,6 +760,18 @@ const app = new Hono()
|
|
|
625
760
|
}
|
|
626
761
|
catch (error) {
|
|
627
762
|
console.error('Error checking out cart:', error);
|
|
763
|
+
// CRITICAL: Checkout failures directly impact revenue
|
|
764
|
+
captureException(error instanceof Error ? error : new Error(String(error)), {
|
|
765
|
+
level: 'fatal', // Highest severity - revenue impacting
|
|
766
|
+
tags: {
|
|
767
|
+
error_code: 'CHECKOUT_ERROR',
|
|
768
|
+
endpoint: 'POST /carts/:id/checkout',
|
|
769
|
+
},
|
|
770
|
+
extra: {
|
|
771
|
+
cartId: c.req.param('id'),
|
|
772
|
+
},
|
|
773
|
+
honoContext: c,
|
|
774
|
+
});
|
|
628
775
|
return c.json({ error: { code: 'INTERNAL_ERROR', message: error.message } }, 500);
|
|
629
776
|
}
|
|
630
777
|
});
|
|
@@ -41,8 +41,8 @@ declare const app: import("hono/hono-base").HonoBase<AppContext, {
|
|
|
41
41
|
createdAt: string;
|
|
42
42
|
updatedAt: string;
|
|
43
43
|
deletedAt: string;
|
|
44
|
-
name: string;
|
|
45
44
|
id: string;
|
|
45
|
+
name: string;
|
|
46
46
|
slug: string;
|
|
47
47
|
logoUrl: string | null;
|
|
48
48
|
siteUrl: string;
|
|
@@ -59,18 +59,20 @@ declare const app: import("hono/hono-base").HonoBase<AppContext, {
|
|
|
59
59
|
createdAt: string;
|
|
60
60
|
updatedAt: string;
|
|
61
61
|
deletedAt: string;
|
|
62
|
-
name: string;
|
|
63
62
|
id: string;
|
|
63
|
+
name: string;
|
|
64
64
|
isActive: boolean;
|
|
65
65
|
};
|
|
66
|
-
name: string;
|
|
67
66
|
id: string;
|
|
67
|
+
name: string;
|
|
68
68
|
brandId: string | null;
|
|
69
69
|
stateId: string;
|
|
70
70
|
allowCOD: boolean;
|
|
71
71
|
allowOnline: boolean;
|
|
72
72
|
waybillOnly: boolean;
|
|
73
73
|
estimatedDays: number | null;
|
|
74
|
+
noteTitle: string | null;
|
|
75
|
+
noteContent: string | null;
|
|
74
76
|
isActive: boolean;
|
|
75
77
|
};
|
|
76
78
|
items: {
|
|
@@ -84,8 +86,8 @@ declare const app: import("hono/hono-base").HonoBase<AppContext, {
|
|
|
84
86
|
createdAt: string;
|
|
85
87
|
updatedAt: string;
|
|
86
88
|
deletedAt: string;
|
|
87
|
-
name: string;
|
|
88
89
|
id: string;
|
|
90
|
+
name: string;
|
|
89
91
|
slug: string;
|
|
90
92
|
brandId: string;
|
|
91
93
|
isActive: boolean;
|
|
@@ -161,7 +163,9 @@ declare const app: import("hono/hono-base").HonoBase<AppContext, {
|
|
|
161
163
|
includes?: boolean;
|
|
162
164
|
flatMap?: boolean;
|
|
163
165
|
flat?: boolean;
|
|
166
|
+
at?: boolean;
|
|
164
167
|
};
|
|
168
|
+
at: never;
|
|
165
169
|
};
|
|
166
170
|
} | {
|
|
167
171
|
[x: number]: string | number | boolean | {
|
|
@@ -234,11 +238,13 @@ declare const app: import("hono/hono-base").HonoBase<AppContext, {
|
|
|
234
238
|
includes?: boolean;
|
|
235
239
|
flatMap?: boolean;
|
|
236
240
|
flat?: boolean;
|
|
241
|
+
at?: boolean;
|
|
237
242
|
};
|
|
243
|
+
at: never;
|
|
238
244
|
};
|
|
239
245
|
};
|
|
240
|
-
name: string | null;
|
|
241
246
|
id: string;
|
|
247
|
+
name: string | null;
|
|
242
248
|
isActive: boolean;
|
|
243
249
|
thumbnailUrl: string | null;
|
|
244
250
|
productId: string;
|
|
@@ -250,8 +256,8 @@ declare const app: import("hono/hono-base").HonoBase<AppContext, {
|
|
|
250
256
|
createdAt: string;
|
|
251
257
|
updatedAt: string;
|
|
252
258
|
deletedAt: string;
|
|
253
|
-
name: string;
|
|
254
259
|
id: string;
|
|
260
|
+
name: string;
|
|
255
261
|
isActive: boolean;
|
|
256
262
|
address: string | null;
|
|
257
263
|
city: string | null;
|
|
@@ -263,8 +269,8 @@ declare const app: import("hono/hono-base").HonoBase<AppContext, {
|
|
|
263
269
|
warehouseId: string | null;
|
|
264
270
|
quantity: number;
|
|
265
271
|
}[];
|
|
266
|
-
id: string;
|
|
267
272
|
email: string | null;
|
|
273
|
+
id: string;
|
|
268
274
|
brandId: string;
|
|
269
275
|
deliveryZoneId: string;
|
|
270
276
|
recoveryAttempts: number;
|
|
@@ -343,8 +349,8 @@ declare const app: import("hono/hono-base").HonoBase<AppContext, {
|
|
|
343
349
|
createdAt: string;
|
|
344
350
|
updatedAt: string;
|
|
345
351
|
deletedAt: string;
|
|
346
|
-
name: string;
|
|
347
352
|
id: string;
|
|
353
|
+
name: string;
|
|
348
354
|
slug: string;
|
|
349
355
|
logoUrl: string | null;
|
|
350
356
|
siteUrl: string;
|
|
@@ -361,18 +367,20 @@ declare const app: import("hono/hono-base").HonoBase<AppContext, {
|
|
|
361
367
|
createdAt: string;
|
|
362
368
|
updatedAt: string;
|
|
363
369
|
deletedAt: string;
|
|
364
|
-
name: string;
|
|
365
370
|
id: string;
|
|
371
|
+
name: string;
|
|
366
372
|
isActive: boolean;
|
|
367
373
|
};
|
|
368
|
-
name: string;
|
|
369
374
|
id: string;
|
|
375
|
+
name: string;
|
|
370
376
|
brandId: string | null;
|
|
371
377
|
stateId: string;
|
|
372
378
|
allowCOD: boolean;
|
|
373
379
|
allowOnline: boolean;
|
|
374
380
|
waybillOnly: boolean;
|
|
375
381
|
estimatedDays: number | null;
|
|
382
|
+
noteTitle: string | null;
|
|
383
|
+
noteContent: string | null;
|
|
376
384
|
isActive: boolean;
|
|
377
385
|
};
|
|
378
386
|
items: {
|
|
@@ -386,8 +394,8 @@ declare const app: import("hono/hono-base").HonoBase<AppContext, {
|
|
|
386
394
|
createdAt: string;
|
|
387
395
|
updatedAt: string;
|
|
388
396
|
deletedAt: string;
|
|
389
|
-
name: string;
|
|
390
397
|
id: string;
|
|
398
|
+
name: string;
|
|
391
399
|
slug: string;
|
|
392
400
|
brandId: string;
|
|
393
401
|
isActive: boolean;
|
|
@@ -463,7 +471,9 @@ declare const app: import("hono/hono-base").HonoBase<AppContext, {
|
|
|
463
471
|
includes?: boolean;
|
|
464
472
|
flatMap?: boolean;
|
|
465
473
|
flat?: boolean;
|
|
474
|
+
at?: boolean;
|
|
466
475
|
};
|
|
476
|
+
at: never;
|
|
467
477
|
};
|
|
468
478
|
} | {
|
|
469
479
|
[x: number]: string | number | boolean | {
|
|
@@ -536,11 +546,13 @@ declare const app: import("hono/hono-base").HonoBase<AppContext, {
|
|
|
536
546
|
includes?: boolean;
|
|
537
547
|
flatMap?: boolean;
|
|
538
548
|
flat?: boolean;
|
|
549
|
+
at?: boolean;
|
|
539
550
|
};
|
|
551
|
+
at: never;
|
|
540
552
|
};
|
|
541
553
|
};
|
|
542
|
-
name: string | null;
|
|
543
554
|
id: string;
|
|
555
|
+
name: string | null;
|
|
544
556
|
isActive: boolean;
|
|
545
557
|
thumbnailUrl: string | null;
|
|
546
558
|
productId: string;
|
|
@@ -552,8 +564,8 @@ declare const app: import("hono/hono-base").HonoBase<AppContext, {
|
|
|
552
564
|
createdAt: string;
|
|
553
565
|
updatedAt: string;
|
|
554
566
|
deletedAt: string;
|
|
555
|
-
name: string;
|
|
556
567
|
id: string;
|
|
568
|
+
name: string;
|
|
557
569
|
isActive: boolean;
|
|
558
570
|
address: string | null;
|
|
559
571
|
city: string | null;
|
|
@@ -565,8 +577,8 @@ declare const app: import("hono/hono-base").HonoBase<AppContext, {
|
|
|
565
577
|
warehouseId: string | null;
|
|
566
578
|
quantity: number;
|
|
567
579
|
}[];
|
|
568
|
-
id: string;
|
|
569
580
|
email: string | null;
|
|
581
|
+
id: string;
|
|
570
582
|
brandId: string;
|
|
571
583
|
deliveryZoneId: string;
|
|
572
584
|
recoveryAttempts: number;
|
|
@@ -6,6 +6,7 @@ import { confirmProspectOrder } from '../../lib/order-recovery';
|
|
|
6
6
|
import { formatOrderResponse } from '../../lib/order-response';
|
|
7
7
|
import { enqueueOrderNotification } from '../../notifications/producers/order-notification';
|
|
8
8
|
import { OrderStatus } from '@prisma/client';
|
|
9
|
+
import { captureException } from '../../lib/sentry';
|
|
9
10
|
const app = new Hono()
|
|
10
11
|
// GET order by ID and token (for order tracking and confirmation)
|
|
11
12
|
.get('/:id/:token', zValidator('param', z.object({
|
|
@@ -66,6 +67,17 @@ const app = new Hono()
|
|
|
66
67
|
return c.json({ ...formattedOrder, canConfirm, confirmationMessage }, 200);
|
|
67
68
|
}
|
|
68
69
|
catch (error) {
|
|
70
|
+
captureException(error instanceof Error ? error : new Error(String(error)), {
|
|
71
|
+
level: 'error',
|
|
72
|
+
tags: {
|
|
73
|
+
error_code: 'ORDER_FETCH_ERROR',
|
|
74
|
+
endpoint: 'GET /orders/:id/:token',
|
|
75
|
+
},
|
|
76
|
+
extra: {
|
|
77
|
+
orderId: c.req.param('id'),
|
|
78
|
+
},
|
|
79
|
+
honoContext: c,
|
|
80
|
+
});
|
|
69
81
|
return c.json({ error: { code: 'INTERNAL_ERROR', message: 'Failed to retrieve order' } }, 500);
|
|
70
82
|
}
|
|
71
83
|
})
|
|
@@ -149,6 +161,18 @@ const app = new Hono()
|
|
|
149
161
|
}
|
|
150
162
|
}, 400);
|
|
151
163
|
}
|
|
164
|
+
// CRITICAL: Order confirmation failures affect customer experience
|
|
165
|
+
captureException(error instanceof Error ? error : new Error(String(error)), {
|
|
166
|
+
level: 'fatal', // High severity - customer waiting for order
|
|
167
|
+
tags: {
|
|
168
|
+
error_code: 'ORDER_CONFIRM_ERROR',
|
|
169
|
+
endpoint: 'POST /orders/confirm',
|
|
170
|
+
},
|
|
171
|
+
extra: {
|
|
172
|
+
orderId: c.req.valid('json').orderId,
|
|
173
|
+
},
|
|
174
|
+
honoContext: c,
|
|
175
|
+
});
|
|
152
176
|
return c.json({
|
|
153
177
|
error: {
|
|
154
178
|
code: 'INTERNAL_ERROR',
|
|
@@ -26,8 +26,8 @@ declare const app: import("hono/hono-base").HonoBase<AppContext, {
|
|
|
26
26
|
createdAt: string;
|
|
27
27
|
updatedAt: string;
|
|
28
28
|
deletedAt: string;
|
|
29
|
-
name: string;
|
|
30
29
|
id: string;
|
|
30
|
+
name: string;
|
|
31
31
|
slug: string;
|
|
32
32
|
logoUrl: string | null;
|
|
33
33
|
siteUrl: string;
|
|
@@ -39,8 +39,8 @@ declare const app: import("hono/hono-base").HonoBase<AppContext, {
|
|
|
39
39
|
updatedAt: string;
|
|
40
40
|
price: number;
|
|
41
41
|
deletedAt: string;
|
|
42
|
-
name: string | null;
|
|
43
42
|
id: string;
|
|
43
|
+
name: string | null;
|
|
44
44
|
isActive: boolean;
|
|
45
45
|
thumbnailUrl: string | null;
|
|
46
46
|
productId: string;
|
|
@@ -48,8 +48,8 @@ declare const app: import("hono/hono-base").HonoBase<AppContext, {
|
|
|
48
48
|
trackInventory: boolean;
|
|
49
49
|
lowStockThreshold: number | null;
|
|
50
50
|
}[];
|
|
51
|
-
name: string;
|
|
52
51
|
id: string;
|
|
52
|
+
name: string;
|
|
53
53
|
slug: string;
|
|
54
54
|
createdAt: string;
|
|
55
55
|
updatedAt: string;
|
|
@@ -128,7 +128,9 @@ declare const app: import("hono/hono-base").HonoBase<AppContext, {
|
|
|
128
128
|
includes?: boolean;
|
|
129
129
|
flatMap?: boolean;
|
|
130
130
|
flat?: boolean;
|
|
131
|
+
at?: boolean;
|
|
131
132
|
};
|
|
133
|
+
at: never;
|
|
132
134
|
};
|
|
133
135
|
} | {
|
|
134
136
|
[x: number]: string | number | boolean | {
|
|
@@ -201,7 +203,9 @@ declare const app: import("hono/hono-base").HonoBase<AppContext, {
|
|
|
201
203
|
includes?: boolean;
|
|
202
204
|
flatMap?: boolean;
|
|
203
205
|
flat?: boolean;
|
|
206
|
+
at?: boolean;
|
|
204
207
|
};
|
|
208
|
+
at: never;
|
|
205
209
|
};
|
|
206
210
|
};
|
|
207
211
|
outputFormat: "json";
|
|
@@ -249,8 +253,8 @@ declare const app: import("hono/hono-base").HonoBase<AppContext, {
|
|
|
249
253
|
createdAt: string;
|
|
250
254
|
updatedAt: string;
|
|
251
255
|
deletedAt: string;
|
|
252
|
-
name: string;
|
|
253
256
|
id: string;
|
|
257
|
+
name: string;
|
|
254
258
|
slug: string;
|
|
255
259
|
logoUrl: string | null;
|
|
256
260
|
siteUrl: string;
|
|
@@ -262,8 +266,8 @@ declare const app: import("hono/hono-base").HonoBase<AppContext, {
|
|
|
262
266
|
updatedAt: string;
|
|
263
267
|
price: number;
|
|
264
268
|
deletedAt: string;
|
|
265
|
-
name: string | null;
|
|
266
269
|
id: string;
|
|
270
|
+
name: string | null;
|
|
267
271
|
isActive: boolean;
|
|
268
272
|
thumbnailUrl: string | null;
|
|
269
273
|
productId: string;
|
|
@@ -271,8 +275,8 @@ declare const app: import("hono/hono-base").HonoBase<AppContext, {
|
|
|
271
275
|
trackInventory: boolean;
|
|
272
276
|
lowStockThreshold: number | null;
|
|
273
277
|
}[];
|
|
274
|
-
name: string;
|
|
275
278
|
id: string;
|
|
279
|
+
name: string;
|
|
276
280
|
slug: string;
|
|
277
281
|
createdAt: string;
|
|
278
282
|
updatedAt: string;
|
|
@@ -351,7 +355,9 @@ declare const app: import("hono/hono-base").HonoBase<AppContext, {
|
|
|
351
355
|
includes?: boolean;
|
|
352
356
|
flatMap?: boolean;
|
|
353
357
|
flat?: boolean;
|
|
358
|
+
at?: boolean;
|
|
354
359
|
};
|
|
360
|
+
at: never;
|
|
355
361
|
};
|
|
356
362
|
} | {
|
|
357
363
|
[x: number]: string | number | boolean | {
|
|
@@ -424,7 +430,9 @@ declare const app: import("hono/hono-base").HonoBase<AppContext, {
|
|
|
424
430
|
includes?: boolean;
|
|
425
431
|
flatMap?: boolean;
|
|
426
432
|
flat?: boolean;
|
|
433
|
+
at?: boolean;
|
|
427
434
|
};
|
|
435
|
+
at: never;
|
|
428
436
|
};
|
|
429
437
|
}[];
|
|
430
438
|
outputFormat: "json";
|
|
@@ -17,6 +17,8 @@ export declare const createDeliveryZoneSchema: z.ZodObject<{
|
|
|
17
17
|
allowOnline: z.ZodOptional<z.ZodBoolean>;
|
|
18
18
|
waybillOnly: z.ZodOptional<z.ZodBoolean>;
|
|
19
19
|
estimatedDays: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
20
|
+
noteTitle: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
21
|
+
noteContent: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
20
22
|
isActive: z.ZodOptional<z.ZodBoolean>;
|
|
21
23
|
}, z.core.$strip>;
|
|
22
24
|
export declare const updateDeliveryZoneSchema: z.ZodObject<{
|
|
@@ -27,5 +29,7 @@ export declare const updateDeliveryZoneSchema: z.ZodObject<{
|
|
|
27
29
|
allowOnline: z.ZodOptional<z.ZodBoolean>;
|
|
28
30
|
waybillOnly: z.ZodOptional<z.ZodBoolean>;
|
|
29
31
|
estimatedDays: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
32
|
+
noteTitle: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
33
|
+
noteContent: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
30
34
|
isActive: z.ZodOptional<z.ZodBoolean>;
|
|
31
35
|
}, z.core.$strip>;
|
|
@@ -18,6 +18,8 @@ export const createDeliveryZoneSchema = z
|
|
|
18
18
|
allowOnline: z.boolean().optional(),
|
|
19
19
|
waybillOnly: z.boolean().optional(),
|
|
20
20
|
estimatedDays: z.number().int().positive().nullable().optional(),
|
|
21
|
+
noteTitle: z.string().nullable().optional(),
|
|
22
|
+
noteContent: z.string().nullable().optional(),
|
|
21
23
|
isActive: z.boolean().optional(),
|
|
22
24
|
})
|
|
23
25
|
.refine((data) => {
|
|
@@ -38,6 +40,8 @@ export const updateDeliveryZoneSchema = z
|
|
|
38
40
|
allowOnline: z.boolean().optional(),
|
|
39
41
|
waybillOnly: z.boolean().optional(),
|
|
40
42
|
estimatedDays: z.number().int().positive().nullable().optional(),
|
|
43
|
+
noteTitle: z.string().nullable().optional(),
|
|
44
|
+
noteContent: z.string().nullable().optional(),
|
|
41
45
|
isActive: z.boolean().optional(),
|
|
42
46
|
})
|
|
43
47
|
.refine((data) => {
|
|
@@ -9,8 +9,8 @@ export declare const createDiscountCodeSchema: z.ZodObject<{
|
|
|
9
9
|
value: z.ZodNumber;
|
|
10
10
|
description: z.ZodOptional<z.ZodString>;
|
|
11
11
|
category: z.ZodDefault<z.ZodEnum<{
|
|
12
|
-
recovery: "recovery";
|
|
13
12
|
manual: "manual";
|
|
13
|
+
recovery: "recovery";
|
|
14
14
|
influencer: "influencer";
|
|
15
15
|
seasonal: "seasonal";
|
|
16
16
|
referral: "referral";
|
|
@@ -33,8 +33,8 @@ export declare const updateDiscountCodeSchema: z.ZodObject<{
|
|
|
33
33
|
value: z.ZodOptional<z.ZodNumber>;
|
|
34
34
|
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
35
35
|
category: z.ZodOptional<z.ZodEnum<{
|
|
36
|
-
recovery: "recovery";
|
|
37
36
|
manual: "manual";
|
|
37
|
+
recovery: "recovery";
|
|
38
38
|
influencer: "influencer";
|
|
39
39
|
seasonal: "seasonal";
|
|
40
40
|
referral: "referral";
|
|
@@ -59,8 +59,8 @@ export declare const bulkGenerateCodesSchema: z.ZodObject<{
|
|
|
59
59
|
value: z.ZodNumber;
|
|
60
60
|
description: z.ZodOptional<z.ZodString>;
|
|
61
61
|
category: z.ZodDefault<z.ZodEnum<{
|
|
62
|
-
recovery: "recovery";
|
|
63
62
|
manual: "manual";
|
|
63
|
+
recovery: "recovery";
|
|
64
64
|
influencer: "influencer";
|
|
65
65
|
seasonal: "seasonal";
|
|
66
66
|
referral: "referral";
|
|
@@ -3,6 +3,7 @@ export declare const createOrderSchema: z.ZodObject<{
|
|
|
3
3
|
brandSlug: z.ZodString;
|
|
4
4
|
firstName: z.ZodString;
|
|
5
5
|
lastName: z.ZodString;
|
|
6
|
+
email: z.ZodOptional<z.ZodString>;
|
|
6
7
|
phone: z.ZodString;
|
|
7
8
|
address: z.ZodString;
|
|
8
9
|
city: z.ZodString;
|
|
@@ -13,13 +14,14 @@ export declare const createOrderSchema: z.ZodObject<{
|
|
|
13
14
|
quantity: z.ZodNumber;
|
|
14
15
|
}, z.core.$strip>>;
|
|
15
16
|
discount: z.ZodOptional<z.ZodNumber>;
|
|
16
|
-
paymentMethod:
|
|
17
|
+
paymentMethod: z.ZodOptional<z.ZodString>;
|
|
17
18
|
paystackReference: z.ZodOptional<z.ZodString>;
|
|
18
19
|
}, z.core.$strip>;
|
|
19
20
|
export declare const updateOrderSchema: z.ZodObject<{
|
|
20
21
|
brandSlug: z.ZodOptional<z.ZodString>;
|
|
21
22
|
firstName: z.ZodOptional<z.ZodString>;
|
|
22
23
|
lastName: z.ZodOptional<z.ZodString>;
|
|
24
|
+
email: z.ZodOptional<z.ZodString>;
|
|
23
25
|
phone: z.ZodOptional<z.ZodString>;
|
|
24
26
|
address: z.ZodOptional<z.ZodString>;
|
|
25
27
|
city: z.ZodOptional<z.ZodString>;
|