hey-pharmacist-ecommerce 1.1.30 → 1.1.31

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 (76) hide show
  1. package/dist/index.d.mts +1451 -1303
  2. package/dist/index.d.ts +1451 -1303
  3. package/dist/index.js +10502 -5728
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +7817 -3059
  6. package/dist/index.mjs.map +1 -1
  7. package/package.json +4 -3
  8. package/src/components/AccountReviewsTab.tsx +97 -0
  9. package/src/components/CouponCodeInput.tsx +190 -0
  10. package/src/components/Header.tsx +5 -1
  11. package/src/components/Notification.tsx +1 -1
  12. package/src/components/NotificationBell.tsx +33 -0
  13. package/src/components/NotificationCard.tsx +211 -0
  14. package/src/components/NotificationDrawer.tsx +195 -0
  15. package/src/components/OrderCard.tsx +164 -99
  16. package/src/components/ProductReviewsSection.tsx +30 -0
  17. package/src/components/RatingDistribution.tsx +86 -0
  18. package/src/components/ReviewCard.tsx +59 -0
  19. package/src/components/ReviewForm.tsx +207 -0
  20. package/src/components/ReviewPromptBanner.tsx +98 -0
  21. package/src/components/ReviewsList.tsx +151 -0
  22. package/src/components/StarRating.tsx +98 -0
  23. package/src/hooks/useDiscounts.ts +7 -0
  24. package/src/hooks/useOrders.ts +15 -0
  25. package/src/hooks/useReviews.ts +230 -0
  26. package/src/index.ts +25 -0
  27. package/src/lib/Apis/apis/discounts-api.ts +23 -72
  28. package/src/lib/Apis/apis/notifications-api.ts +196 -231
  29. package/src/lib/Apis/apis/products-api.ts +84 -0
  30. package/src/lib/Apis/apis/review-api.ts +283 -4
  31. package/src/lib/Apis/apis/stores-api.ts +180 -0
  32. package/src/lib/Apis/models/bulk-channel-toggle-dto.ts +52 -0
  33. package/src/lib/Apis/models/cart-body-populated.ts +3 -3
  34. package/src/lib/Apis/models/channel-settings-dto.ts +39 -0
  35. package/src/lib/Apis/models/{discount-paginated-response.ts → completed-order-dto.ts} +21 -16
  36. package/src/lib/Apis/models/create-discount-dto.ts +31 -92
  37. package/src/lib/Apis/models/create-review-dto.ts +4 -4
  38. package/src/lib/Apis/models/create-shippo-account-dto.ts +45 -0
  39. package/src/lib/Apis/models/create-store-dto.ts +6 -0
  40. package/src/lib/Apis/models/discount.ts +37 -98
  41. package/src/lib/Apis/models/discounts-insights-dto.ts +12 -0
  42. package/src/lib/Apis/models/index.ts +13 -7
  43. package/src/lib/Apis/models/{manual-discount.ts → manual-discount-dto.ts} +10 -10
  44. package/src/lib/Apis/models/manual-order-dto.ts +3 -3
  45. package/src/lib/Apis/models/populated-discount.ts +41 -101
  46. package/src/lib/Apis/models/preference-update-item.ts +59 -0
  47. package/src/lib/Apis/models/product-light-dto.ts +40 -0
  48. package/src/lib/Apis/models/{check-notifications-response-dto.ts → review-status-dto.ts} +8 -7
  49. package/src/lib/Apis/models/review.ts +9 -3
  50. package/src/lib/Apis/models/reviewable-order-dto.ts +58 -0
  51. package/src/lib/Apis/models/reviewable-product-dto.ts +81 -0
  52. package/src/lib/Apis/models/shippo-account-response-dto.ts +51 -0
  53. package/src/lib/Apis/models/store-entity.ts +6 -0
  54. package/src/lib/Apis/models/store.ts +6 -0
  55. package/src/lib/Apis/models/update-discount-dto.ts +31 -92
  56. package/src/lib/Apis/models/update-notification-settings-dto.ts +28 -0
  57. package/src/lib/Apis/models/update-review-dto.ts +4 -4
  58. package/src/lib/Apis/models/update-store-dto.ts +6 -0
  59. package/src/lib/Apis/models/{pick-type-class.ts → variant-light-dto.ts} +20 -14
  60. package/src/lib/utils/discount.ts +155 -0
  61. package/src/lib/validations/discount.ts +11 -0
  62. package/src/providers/CartProvider.tsx +2 -2
  63. package/src/providers/DiscountProvider.tsx +97 -0
  64. package/src/providers/EcommerceProvider.tsx +13 -5
  65. package/src/providers/NotificationCenterProvider.tsx +436 -0
  66. package/src/screens/CartScreen.tsx +1 -1
  67. package/src/screens/CheckoutScreen.tsx +39 -12
  68. package/src/screens/NotificationSettingsScreen.tsx +413 -0
  69. package/src/screens/OrderDetailScreen.tsx +283 -0
  70. package/src/screens/OrderReviewsScreen.tsx +308 -0
  71. package/src/screens/OrdersScreen.tsx +31 -7
  72. package/src/screens/ProductDetailScreen.tsx +24 -11
  73. package/src/screens/ProfileScreen.tsx +5 -0
  74. package/src/lib/Apis/models/create-notification-dto.ts +0 -75
  75. package/src/lib/Apis/models/notification.ts +0 -93
  76. package/src/lib/Apis/models/single-notification-dto.ts +0 -99
@@ -19,155 +19,112 @@
19
19
  export interface UpdateDiscountDto {
20
20
  _id?: string;
21
21
  /**
22
- *
22
+ * Display name for the discount
23
23
  * @type {string}
24
24
  * @memberof UpdateDiscountDto
25
25
  */
26
- discountName?: string;
26
+ name?: string;
27
27
  /**
28
- *
28
+ * Optional description for internal use
29
29
  * @type {string}
30
30
  * @memberof UpdateDiscountDto
31
31
  */
32
32
  description?: string;
33
33
  /**
34
- *
34
+ * ITEM_DISCOUNT: Shows on products automatically. COUPON_CODE: Requires code at checkout
35
35
  * @type {string}
36
36
  * @memberof UpdateDiscountDto
37
37
  */
38
- discountType?: UpdateDiscountDtoDiscountTypeEnum;
38
+ type?: UpdateDiscountDtoTypeEnum;
39
39
  /**
40
- *
40
+ * PERCENTAGE or FIXED_AMOUNT
41
41
  * @type {string}
42
42
  * @memberof UpdateDiscountDto
43
43
  */
44
- decreasePriceBy?: UpdateDiscountDtoDecreasePriceByEnum;
44
+ valueType?: UpdateDiscountDtoValueTypeEnum;
45
45
  /**
46
- *
47
- * @type {string}
48
- * @memberof UpdateDiscountDto
49
- */
50
- code?: string;
51
- /**
52
- *
53
- * @type {boolean}
46
+ * The discount value (percentage 1-100 or fixed dollar amount)
47
+ * @type {number}
54
48
  * @memberof UpdateDiscountDto
55
49
  */
56
- automatic?: boolean;
50
+ value?: number;
57
51
  /**
58
- *
52
+ * Maximum discount amount for percentage discounts (optional, mainly for COUPON_CODE)
59
53
  * @type {number}
60
54
  * @memberof UpdateDiscountDto
61
55
  */
62
56
  cappedAt?: number;
63
57
  /**
64
- *
65
- * @type {number}
58
+ * Coupon code (required for COUPON_CODE type)
59
+ * @type {string}
66
60
  * @memberof UpdateDiscountDto
67
61
  */
68
- amount?: number;
62
+ code?: string;
69
63
  /**
70
- *
64
+ * When the discount becomes active
71
65
  * @type {Date}
72
66
  * @memberof UpdateDiscountDto
73
67
  */
74
68
  startsAt?: Date;
75
69
  /**
76
- *
70
+ * When the discount expires
77
71
  * @type {Date}
78
72
  * @memberof UpdateDiscountDto
79
73
  */
80
74
  expiresAt?: Date;
81
75
  /**
82
- *
83
- * @type {number}
84
- * @memberof UpdateDiscountDto
85
- */
86
- maxUses?: number;
87
- /**
88
- *
76
+ * Current lifecycle state
89
77
  * @type {string}
90
78
  * @memberof UpdateDiscountDto
91
79
  */
92
80
  state?: UpdateDiscountDtoStateEnum;
93
81
  /**
94
- *
95
- * @type {Array<string>}
82
+ * Quick toggle to enable/disable the discount
83
+ * @type {boolean}
96
84
  * @memberof UpdateDiscountDto
97
85
  */
98
- categoriesEligible?: Array<string>;
86
+ isActive?: boolean;
99
87
  /**
100
- *
88
+ * Products to apply discount to (all their variants will be discounted)
101
89
  * @type {Array<string>}
102
90
  * @memberof UpdateDiscountDto
103
91
  */
104
92
  productsEligible?: Array<string>;
105
93
  /**
106
- *
94
+ * Specific variants to apply discount to
107
95
  * @type {Array<string>}
108
96
  * @memberof UpdateDiscountDto
109
97
  */
110
98
  variantsEligible?: Array<string>;
111
99
  /**
112
- *
113
- * @type {number}
114
- * @memberof UpdateDiscountDto
115
- */
116
- maxUsesPerUser?: number;
117
- /**
118
- *
119
- * @type {string}
120
- * @memberof UpdateDiscountDto
121
- */
122
- minPurchaseRequired?: UpdateDiscountDtoMinPurchaseRequiredEnum;
123
- /**
124
- *
100
+ * Maximum total times this discount can be used (optional)
125
101
  * @type {number}
126
102
  * @memberof UpdateDiscountDto
127
103
  */
128
- minPurchaseAmount?: number;
104
+ maxUses?: number;
129
105
  /**
130
- *
106
+ * Maximum times a single user can use this discount (optional)
131
107
  * @type {number}
132
108
  * @memberof UpdateDiscountDto
133
109
  */
134
- minPurchaseQuantity?: number;
135
- /**
136
- *
137
- * @type {string}
138
- * @memberof UpdateDiscountDto
139
- */
140
- customerEligibility?: UpdateDiscountDtoCustomerEligibilityEnum;
141
- /**
142
- *
143
- * @type {Array<string>}
144
- * @memberof UpdateDiscountDto
145
- */
146
- usersEligible?: Array<string>;
147
- /**
148
- *
149
- * @type {Array<string>}
150
- * @memberof UpdateDiscountDto
151
- */
152
- groupsEligible?: Array<string>;
110
+ maxUsesPerUser?: number;
153
111
  }
154
112
 
155
113
  /**
156
114
  * @export
157
115
  * @enum {string}
158
116
  */
159
- export enum UpdateDiscountDtoDiscountTypeEnum {
160
- PRODUCT = 'PRODUCT',
161
- CATEGORY = 'CATEGORY',
162
- VARIANT = 'VARIANT'
117
+ export enum UpdateDiscountDtoTypeEnum {
118
+ ITEMDISCOUNT = 'ITEM_DISCOUNT',
119
+ COUPONCODE = 'COUPON_CODE'
163
120
  }
164
121
  /**
165
122
  * @export
166
123
  * @enum {string}
167
124
  */
168
- export enum UpdateDiscountDtoDecreasePriceByEnum {
125
+ export enum UpdateDiscountDtoValueTypeEnum {
169
126
  PERCENTAGE = 'PERCENTAGE',
170
- AMOUNT = 'AMOUNT'
127
+ FIXEDAMOUNT = 'FIXED_AMOUNT'
171
128
  }
172
129
  /**
173
130
  * @export
@@ -179,22 +136,4 @@ export enum UpdateDiscountDtoStateEnum {
179
136
  EXPIRED = 'EXPIRED',
180
137
  SCHEDULED = 'SCHEDULED'
181
138
  }
182
- /**
183
- * @export
184
- * @enum {string}
185
- */
186
- export enum UpdateDiscountDtoMinPurchaseRequiredEnum {
187
- NOREQUIREMENT = 'NO_REQUIREMENT',
188
- MINIMUMAMOUNT = 'MINIMUM_AMOUNT',
189
- MINIMUMQUANTITY = 'MINIMUM_QUANTITY'
190
- }
191
- /**
192
- * @export
193
- * @enum {string}
194
- */
195
- export enum UpdateDiscountDtoCustomerEligibilityEnum {
196
- ALL = 'ALL',
197
- SPECIFICUSERS = 'SPECIFIC_USERS',
198
- SPECIFICGROUPS = 'SPECIFIC_GROUPS'
199
- }
200
139
 
@@ -0,0 +1,28 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+ /**
4
+ * Hey Pharamcist API
5
+ * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
6
+ *
7
+ * OpenAPI spec version: 1.0
8
+ *
9
+ *
10
+ * NOTE: This class is auto generated by the swagger code generator program.
11
+ * https://github.com/swagger-api/swagger-codegen.git
12
+ * Do not edit the class manually.
13
+ */
14
+ import { PreferenceUpdateItem } from './preference-update-item';
15
+ /**
16
+ *
17
+ * @export
18
+ * @interface UpdateNotificationSettingsDto
19
+ */
20
+ export interface UpdateNotificationSettingsDto {
21
+ _id?: string;
22
+ /**
23
+ * Array of preference updates to apply
24
+ * @type {Array<PreferenceUpdateItem>}
25
+ * @memberof UpdateNotificationSettingsDto
26
+ */
27
+ preferences: Array<PreferenceUpdateItem>;
28
+ }
@@ -23,13 +23,13 @@ export interface UpdateReviewDto {
23
23
  * @type {string}
24
24
  * @memberof UpdateReviewDto
25
25
  */
26
- userId?: string;
26
+ productId?: string;
27
27
  /**
28
28
  *
29
29
  * @type {string}
30
30
  * @memberof UpdateReviewDto
31
31
  */
32
- productId?: string;
32
+ productVariantId?: string;
33
33
  /**
34
34
  *
35
35
  * @type {string}
@@ -56,8 +56,8 @@ export interface UpdateReviewDto {
56
56
  reply?: string;
57
57
  /**
58
58
  *
59
- * @type {string}
59
+ * @type {Date}
60
60
  * @memberof UpdateReviewDto
61
61
  */
62
- replyDate?: string;
62
+ replyDate?: Date;
63
63
  }
@@ -49,6 +49,12 @@ export interface UpdateStoreDto {
49
49
  * @memberof UpdateStoreDto
50
50
  */
51
51
  adminUrl?: string;
52
+ /**
53
+ * Shippo Platform managed account ID
54
+ * @type {string}
55
+ * @memberof UpdateStoreDto
56
+ */
57
+ shippoAccountId?: string;
52
58
  /**
53
59
  *
54
60
  * @type {string}
@@ -14,38 +14,44 @@
14
14
  /**
15
15
  *
16
16
  * @export
17
- * @interface PickTypeClass
17
+ * @interface VariantLightDto
18
18
  */
19
- export interface PickTypeClass {
19
+ export interface VariantLightDto {
20
20
  _id?: string;
21
21
  /**
22
22
  *
23
23
  * @type {string}
24
- * @memberof PickTypeClass
24
+ * @memberof VariantLightDto
25
25
  */
26
- id?: string;
26
+ id: string;
27
27
  /**
28
28
  *
29
29
  * @type {string}
30
- * @memberof PickTypeClass
30
+ * @memberof VariantLightDto
31
31
  */
32
- firstname?: string;
32
+ name: string;
33
33
  /**
34
34
  *
35
- * @type {string}
36
- * @memberof PickTypeClass
35
+ * @type {number}
36
+ * @memberof VariantLightDto
37
37
  */
38
- lastname?: string;
38
+ retailPrice: number;
39
39
  /**
40
40
  *
41
- * @type {string}
42
- * @memberof PickTypeClass
41
+ * @type {number}
42
+ * @memberof VariantLightDto
43
+ */
44
+ finalPrice: number;
45
+ /**
46
+ *
47
+ * @type {number}
48
+ * @memberof VariantLightDto
43
49
  */
44
- avatar?: string;
50
+ inventoryCount: number;
45
51
  /**
46
52
  *
47
53
  * @type {string}
48
- * @memberof PickTypeClass
54
+ * @memberof VariantLightDto
49
55
  */
50
- phoneNumber?: string;
56
+ sku: string;
51
57
  }
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Discount Utility Functions
3
+ * Handles calculation and validation of discounts based on the V2 refactor
4
+ */
5
+
6
+ import { Discount } from '@/lib/Apis/models/discount';
7
+ import {
8
+ DiscountTypeEnum,
9
+ DiscountValueTypeEnum,
10
+ DiscountStateEnum
11
+ } from '@/lib/Apis/models/discount';
12
+
13
+ export { DiscountTypeEnum, DiscountValueTypeEnum, DiscountStateEnum };
14
+
15
+ export interface DiscountsInsightsDto {
16
+ totalDiscounts: number;
17
+ totalActive: number;
18
+ totalInactive: number;
19
+ totalExpired: number;
20
+ totalScheduled: number;
21
+ totalItemDiscounts: number;
22
+ totalCouponCodes: number;
23
+ }
24
+
25
+ /**
26
+ * Calculate discount amount for a given price
27
+ * @param price - Original price
28
+ * @param discount - Discount object
29
+ * @returns Calculated discount amount
30
+ */
31
+ export function calculateDiscountAmount(price: number, discount: Discount): number {
32
+ // Skip state validation - trust that backend already validated the discount
33
+ // Just check if we have the required fields to calculate
34
+ if (!discount || !discount.value) {
35
+ return 0;
36
+ }
37
+
38
+ let amount = 0;
39
+ const valueType = String(discount.valueType).toUpperCase();
40
+
41
+ if (valueType === 'PERCENTAGE') {
42
+ amount = (price * discount.value) / 100;
43
+ // Apply cap if specified (mainly for coupon codes)
44
+ if (discount.cappedAt && amount > discount.cappedAt) {
45
+ amount = discount.cappedAt;
46
+ }
47
+ } else if (valueType === 'FIXED_AMOUNT' || valueType === 'FIXEDAMOUNT') {
48
+ amount = discount.value;
49
+ }
50
+
51
+ const finalAmount = Math.min(amount, price);
52
+ return Math.round(finalAmount * 100) / 100; // Round to 2 decimal places
53
+ }
54
+
55
+ /**
56
+ * Calculate final price after discount
57
+ * @param price - Original price
58
+ * @param discount - Discount object
59
+ * @returns Final discounted price
60
+ */
61
+ export function calculateFinalPrice(price: number, discount: Discount): number {
62
+ const discountAmount = calculateDiscountAmount(price, discount);
63
+ return Math.max(0, price - discountAmount);
64
+ }
65
+
66
+ /**
67
+ * Check if a discount is currently valid (active and not expired/scheduled)
68
+ * @param discount - Discount object
69
+ * @returns True if discount is valid and can be applied
70
+ */
71
+ export function isDiscountValid(discount: Discount): boolean {
72
+ if (!discount.isActive) {
73
+ return false;
74
+ }
75
+
76
+ const now = new Date();
77
+
78
+ // Check if scheduled
79
+ if (discount.startsAt && new Date(discount.startsAt) > now) {
80
+ return false; // Not yet started
81
+ }
82
+
83
+ // Check if expired
84
+ if (discount.expiresAt && new Date(discount.expiresAt) < now) {
85
+ return false; // Already expired
86
+ }
87
+
88
+ // Check state
89
+ const activeStates = [
90
+ DiscountStateEnum.ACTIVE,
91
+ 'ACTIVE',
92
+ ];
93
+
94
+ if (!activeStates.includes(discount.state as any)) {
95
+ return false;
96
+ }
97
+
98
+ return true;
99
+ }
100
+
101
+ /**
102
+ * Check if a product is eligible for an item discount
103
+ * @param productId - Product ID
104
+ * @param variantId - Variant ID (optional)
105
+ * @param discount - Discount object
106
+ * @returns True if product/variant is eligible
107
+ */
108
+ export function isProductEligibleForDiscount(
109
+ productId: string,
110
+ variantId: string | undefined,
111
+ discount: Discount
112
+ ): boolean {
113
+ // Check variant eligibility first (most specific)
114
+ if (variantId && discount.variantsEligible?.includes(variantId)) {
115
+ return true;
116
+ }
117
+
118
+ // Check product eligibility
119
+ if (discount.productsEligible?.includes(productId)) {
120
+ return true;
121
+ }
122
+
123
+ return false;
124
+ }
125
+
126
+ /**
127
+ * Format discount display text
128
+ * @param discount - Discount object
129
+ * @returns Formatted string like "20% OFF" or "$10 OFF"
130
+ */
131
+ export function formatDiscountDisplay(discount: Discount): string {
132
+ const valueType = String(discount.valueType).toUpperCase();
133
+
134
+ if (valueType === 'PERCENTAGE') {
135
+ return `${discount.value}% OFF`;
136
+ } else {
137
+ return `$${discount.value.toFixed(2)} OFF`;
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Get discount state label
143
+ * @param state - Discount state
144
+ * @returns Human-readable state label
145
+ */
146
+ export function getDiscountStateLabel(state: DiscountStateEnum | string): string {
147
+ const stateStr = String(state).toUpperCase();
148
+ const stateMap: Record<string, string> = {
149
+ 'ACTIVE': 'Active',
150
+ 'INACTIVE': 'Inactive',
151
+ 'EXPIRED': 'Expired',
152
+ 'SCHEDULED': 'Scheduled',
153
+ };
154
+ return stateMap[stateStr] || 'Unknown';
155
+ }
@@ -0,0 +1,11 @@
1
+ import { z } from 'zod';
2
+
3
+ export const couponCodeSchema = z.object({
4
+ code: z
5
+ .string()
6
+ .min(2, 'Coupon code must be at least 2 characters')
7
+ .max(50, 'Coupon code must not exceed 50 characters')
8
+ .toUpperCase(),
9
+ });
10
+
11
+ export type CouponCodeFormData = z.infer<typeof couponCodeSchema>;
@@ -128,7 +128,7 @@ export function CartProvider({ children }: CartProviderProps) {
128
128
  }
129
129
  return item;
130
130
  });
131
- const newSubtotal = newItems.reduce((acc, item) => acc + (item.productVariantData.finalPrice || 0) * item.quantity, 0);
131
+ const newSubtotal = Math.round(newItems.reduce((acc, item) => acc + (item.productVariantData.finalPrice || 0) * item.quantity, 0) * 100) / 100;
132
132
  return {
133
133
  ...prev,
134
134
  subTotal: newSubtotal,
@@ -174,7 +174,7 @@ export function CartProvider({ children }: CartProviderProps) {
174
174
  setCart(prev => {
175
175
  if (!prev) return null;
176
176
  const newItems = prev.cartBody.items.filter(item => String(item.productVariantId) !== String(productId));
177
- const newSubtotal = newItems.reduce((acc, item) => acc + (item.productVariantData.finalPrice || 0) * item.quantity, 0);
177
+ const newSubtotal = Math.round(newItems.reduce((acc, item) => acc + (item.productVariantData.finalPrice || 0) * item.quantity, 0) * 100) / 100;
178
178
  return {
179
179
  ...prev,
180
180
  subTotal: newSubtotal,
@@ -0,0 +1,97 @@
1
+ 'use client';
2
+
3
+ import React, { createContext, useContext, useState, useCallback } from 'react';
4
+ import { Discount } from '@/lib/Apis/models/discount';
5
+ import { DiscountsApi } from '@/lib/Apis/apis/discounts-api';
6
+ import { AXIOS_CONFIG } from '@/lib/Apis/wrapper';
7
+ import { calculateDiscountAmount } from '@/lib/utils/discount';
8
+
9
+ interface DiscountContextType {
10
+ appliedCoupon: Discount | null;
11
+ couponError: string | null;
12
+ isValidatingCoupon: boolean;
13
+ validateAndApplyCoupon: (code: string, userId?: string) => Promise<{ success: boolean; error?: string; discount?: Discount }>;
14
+ removeCoupon: () => void;
15
+ calculateCouponDiscount: (subtotal: number) => number;
16
+ }
17
+
18
+ const DiscountContext = createContext<DiscountContextType | undefined>(undefined);
19
+
20
+ export function DiscountProvider({ children }: { children: React.ReactNode }) {
21
+ const [appliedCoupon, setAppliedCoupon] = useState<Discount | null>(null);
22
+ const [couponError, setCouponError] = useState<string | null>(null);
23
+ const [isValidatingCoupon, setIsValidatingCoupon] = useState(false);
24
+
25
+ const validateAndApplyCoupon = useCallback(
26
+ async (code: string, userId?: string) => {
27
+ setIsValidatingCoupon(true);
28
+ setCouponError(null);
29
+
30
+ try {
31
+ const api = new DiscountsApi(AXIOS_CONFIG);
32
+ const response = await api.validateCoupon(code, userId);
33
+ const discount = response.data;
34
+
35
+ if (!discount) {
36
+ setCouponError('Coupon code not found');
37
+ setAppliedCoupon(null);
38
+ return { success: false, error: 'Coupon code not found' };
39
+ }
40
+
41
+ setAppliedCoupon(discount);
42
+ setCouponError(null);
43
+ return { success: true, discount };
44
+ } catch (error: any) {
45
+ const errorMessage =
46
+ error.response?.data?.message || 'Failed to validate coupon code';
47
+ setCouponError(errorMessage);
48
+ setAppliedCoupon(null);
49
+ return { success: false, error: errorMessage };
50
+ } finally {
51
+ setIsValidatingCoupon(false);
52
+ }
53
+ },
54
+ []
55
+ );
56
+
57
+ const removeCoupon = useCallback(() => {
58
+ setAppliedCoupon(null);
59
+ setCouponError(null);
60
+ }, []);
61
+
62
+ const calculateCouponDiscount = useCallback(
63
+ (subtotal: number): number => {
64
+ if (!appliedCoupon) {
65
+ return 0;
66
+ }
67
+ const discount = calculateDiscountAmount(subtotal, appliedCoupon);
68
+ return discount;
69
+ },
70
+ [appliedCoupon]
71
+ );
72
+
73
+ const value: DiscountContextType = {
74
+ appliedCoupon,
75
+ couponError,
76
+ isValidatingCoupon,
77
+ validateAndApplyCoupon,
78
+ removeCoupon,
79
+ calculateCouponDiscount,
80
+ };
81
+
82
+ return (
83
+ <DiscountContext.Provider value={value}>
84
+ {children}
85
+ </DiscountContext.Provider>
86
+ );
87
+ }
88
+
89
+ export function useDiscountsContext() {
90
+ const context = useContext(DiscountContext);
91
+ if (!context) {
92
+ throw new Error('useDiscountsContext must be used within DiscountProvider');
93
+ }
94
+ return context;
95
+ }
96
+
97
+ export type { DiscountContextType };
@@ -7,10 +7,13 @@ import { AuthProvider } from './AuthProvider';
7
7
  import { CartProvider } from './CartProvider';
8
8
  import { WishlistProvider } from './WishlistProvider';
9
9
  import { BasePathProvider } from './BasePathProvider';
10
+ import { DiscountProvider } from './DiscountProvider';
10
11
  import { initializeApiAdapter } from '@/lib/api-adapter';
11
12
  import { QueryClientProvider } from '@tanstack/react-query';
12
13
  import { QueryClient } from '@tanstack/react-query';
13
14
  import { NotificationProvider } from './NotificationProvider';
15
+ import { NotificationCenterProvider } from './NotificationCenterProvider';
16
+ import { NotificationDrawer } from '@/components/NotificationDrawer';
14
17
 
15
18
  interface EcommerceProviderProps {
16
19
  config: EcommerceConfig;
@@ -35,11 +38,16 @@ export function EcommerceProvider({ config, children, withToaster = true, basePa
35
38
  <BasePathProvider basePath={basePath}>
36
39
  <AuthProvider>
37
40
  <NotificationProvider>
38
- <CartProvider>
39
- <WishlistProvider>
40
- {children}
41
- </WishlistProvider>
42
- </CartProvider>
41
+ <NotificationCenterProvider>
42
+ <CartProvider>
43
+ <DiscountProvider>
44
+ <WishlistProvider>
45
+ {children}
46
+ <NotificationDrawer />
47
+ </WishlistProvider>
48
+ </DiscountProvider>
49
+ </CartProvider>
50
+ </NotificationCenterProvider>
43
51
  </NotificationProvider>
44
52
  </AuthProvider>
45
53
  </BasePathProvider>