hey-pharmacist-ecommerce 1.1.12 → 1.1.14

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 (49) hide show
  1. package/dist/index.d.mts +2 -4
  2. package/dist/index.d.ts +2 -4
  3. package/dist/index.js +1123 -972
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +1123 -971
  6. package/dist/index.mjs.map +1 -1
  7. package/package.json +3 -3
  8. package/src/components/AccountAddressesTab.tsx +209 -0
  9. package/src/components/AccountOrdersTab.tsx +151 -0
  10. package/src/components/AccountOverviewTab.tsx +209 -0
  11. package/src/components/AccountPaymentTab.tsx +116 -0
  12. package/src/components/AccountSavedItemsTab.tsx +76 -0
  13. package/src/components/AccountSettingsTab.tsx +116 -0
  14. package/src/components/AddressFormModal.tsx +23 -10
  15. package/src/components/CartItem.tsx +60 -56
  16. package/src/components/FilterChips.tsx +54 -80
  17. package/src/components/Header.tsx +69 -16
  18. package/src/components/Notification.tsx +148 -0
  19. package/src/components/OrderCard.tsx +89 -56
  20. package/src/components/ProductCard.tsx +215 -178
  21. package/src/components/QuickViewModal.tsx +314 -0
  22. package/src/components/TabNavigation.tsx +48 -0
  23. package/src/components/ui/Button.tsx +1 -1
  24. package/src/components/ui/ConfirmModal.tsx +84 -0
  25. package/src/hooks/useOrders.ts +1 -0
  26. package/src/hooks/usePaymentMethods.ts +58 -0
  27. package/src/index.ts +0 -1
  28. package/src/providers/CartProvider.tsx +22 -6
  29. package/src/providers/EcommerceProvider.tsx +8 -7
  30. package/src/providers/FavoritesProvider.tsx +10 -3
  31. package/src/providers/NotificationProvider.tsx +79 -0
  32. package/src/providers/WishlistProvider.tsx +34 -9
  33. package/src/screens/AddressesScreen.tsx +72 -61
  34. package/src/screens/CartScreen.tsx +48 -32
  35. package/src/screens/ChangePasswordScreen.tsx +155 -0
  36. package/src/screens/CheckoutScreen.tsx +162 -125
  37. package/src/screens/EditProfileScreen.tsx +165 -0
  38. package/src/screens/LoginScreen.tsx +59 -72
  39. package/src/screens/NewAddressScreen.tsx +16 -10
  40. package/src/screens/OrdersScreen.tsx +91 -148
  41. package/src/screens/ProductDetailScreen.tsx +334 -234
  42. package/src/screens/ProfileScreen.tsx +190 -200
  43. package/src/screens/RegisterScreen.tsx +51 -70
  44. package/src/screens/SearchResultsScreen.tsx +2 -1
  45. package/src/screens/ShopScreen.tsx +260 -384
  46. package/src/screens/WishlistScreen.tsx +226 -224
  47. package/src/styles/globals.css +9 -0
  48. package/src/screens/CategoriesScreen.tsx +0 -122
  49. package/src/screens/HomeScreen.tsx +0 -211
@@ -0,0 +1,79 @@
1
+ 'use client';
2
+
3
+ import React, { createContext, useContext, useState, useCallback } from 'react';
4
+ import { NotificationContainer, NotificationData, NotificationType } from '@/components/Notification';
5
+
6
+ interface NotificationContextValue {
7
+ success: (message: string, description?: string, duration?: number) => void;
8
+ error: (message: string, description?: string, duration?: number) => void;
9
+ warning: (message: string, description?: string, duration?: number) => void;
10
+ info: (message: string, description?: string, duration?: number) => void;
11
+ }
12
+
13
+ const NotificationContext = createContext<NotificationContextValue | undefined>(undefined);
14
+
15
+ export function useNotification() {
16
+ const context = useContext(NotificationContext);
17
+ if (!context) {
18
+ throw new Error('useNotification must be used within NotificationProvider');
19
+ }
20
+ return context;
21
+ }
22
+
23
+ interface NotificationProviderProps {
24
+ children: React.ReactNode;
25
+ }
26
+
27
+ export function NotificationProvider({ children }: NotificationProviderProps) {
28
+ const [notifications, setNotifications] = useState<NotificationData[]>([]);
29
+
30
+ const addNotification = useCallback(
31
+ (type: NotificationType, message: string, description?: string, duration?: number) => {
32
+ const id = Math.random().toString(36).substring(2, 9);
33
+ const notification: NotificationData = {
34
+ id,
35
+ type,
36
+ message,
37
+ description,
38
+ duration: duration || 4000,
39
+ };
40
+
41
+ setNotifications((prev) => [...prev, notification]);
42
+ },
43
+ []
44
+ );
45
+
46
+ const dismissNotification = useCallback((id: string) => {
47
+ setNotifications((prev) => prev.filter((n) => n.id !== id));
48
+ }, []);
49
+
50
+ const value: NotificationContextValue = {
51
+ success: useCallback(
52
+ (message: string, description?: string, duration?: number) =>
53
+ addNotification('success', message, description, duration),
54
+ [addNotification]
55
+ ),
56
+ error: useCallback(
57
+ (message: string, description?: string, duration?: number) =>
58
+ addNotification('error', message, description, duration),
59
+ [addNotification]
60
+ ),
61
+ warning: useCallback(
62
+ (message: string, description?: string, duration?: number) =>
63
+ addNotification('warning', message, description, duration),
64
+ [addNotification]
65
+ ),
66
+ info: useCallback(
67
+ (message: string, description?: string, duration?: number) =>
68
+ addNotification('info', message, description, duration),
69
+ [addNotification]
70
+ ),
71
+ };
72
+
73
+ return (
74
+ <NotificationContext.Provider value={value}>
75
+ {children}
76
+ <NotificationContainer notifications={notifications} onDismiss={dismissNotification} />
77
+ </NotificationContext.Provider>
78
+ );
79
+ }
@@ -4,10 +4,10 @@ import React, { createContext, useContext, useState, useEffect, ReactNode, useCa
4
4
  import { WishlistApi } from '@/lib/Apis/apis/wishlist-api';
5
5
  import { AXIOS_CONFIG } from '@/lib/Apis/wrapper';
6
6
  import { useAuth } from './AuthProvider';
7
- import { toast } from 'sonner';
8
7
  import { useMemo } from 'react';
9
8
  import { Wishlist } from '@/lib/Apis';
10
9
  import { Product } from '@/lib/Apis/models';
10
+ import { useNotification } from './NotificationProvider';
11
11
 
12
12
 
13
13
  interface WishlistContextType extends Wishlist {
@@ -31,6 +31,7 @@ export function WishlistProvider({ children }: { children: ReactNode }) {
31
31
  } as unknown as Wishlist);
32
32
 
33
33
  const { isAuthenticated } = useAuth() || {};
34
+ const notification = useNotification();
34
35
  const wishlistApi = useMemo(() => new WishlistApi(AXIOS_CONFIG), []);
35
36
 
36
37
  const fetchWishlist = useCallback(async () => {
@@ -74,14 +75,20 @@ export function WishlistProvider({ children }: { children: ReactNode }) {
74
75
 
75
76
  const addToWishlist = async (product: Product) => {
76
77
  if (!isAuthenticated) {
77
- toast.error('Please sign in to add items to your wishlist');
78
+ notification.error(
79
+ 'Sign-in required',
80
+ 'Please sign in to add items to your wishlist.'
81
+ );
78
82
  return;
79
83
  }
80
84
 
81
85
  try {
82
86
  // First check if the item is already in the wishlist
83
87
  if (isInWishlist(product?._id || '')) {
84
- toast.info('This item is already in your wishlist');
88
+ notification.info(
89
+ 'Already saved',
90
+ 'This item is already in your wishlist.'
91
+ );
85
92
  return;
86
93
  }
87
94
 
@@ -92,10 +99,16 @@ export function WishlistProvider({ children }: { children: ReactNode }) {
92
99
  // This ensures we're in sync with the server and prevents duplicates
93
100
  await fetchWishlist();
94
101
 
95
- toast.success('Added to wishlist');
102
+ notification.success(
103
+ 'Added to wishlist',
104
+ `${product?.name || 'Item'} was added to your wishlist.`
105
+ );
96
106
  } catch (error) {
97
107
  console.error('Error adding to wishlist:', error);
98
- toast.error('Failed to add to wishlist');
108
+ notification.error(
109
+ 'Could not add to wishlist',
110
+ 'Something went wrong while adding this item. Please try again.'
111
+ );
99
112
  throw error;
100
113
  }
101
114
  };
@@ -113,10 +126,16 @@ export function WishlistProvider({ children }: { children: ReactNode }) {
113
126
 
114
127
  });
115
128
 
116
- toast.success('Removed from wishlist');
129
+ notification.success(
130
+ 'Removed from wishlist',
131
+ 'The item has been removed from your wishlist.'
132
+ );
117
133
  } catch (error) {
118
134
  console.error('Error removing from wishlist:', error);
119
- toast.error('Failed to remove from wishlist');
135
+ notification.error(
136
+ 'Could not remove item',
137
+ 'Something went wrong while removing this item. Please try again.'
138
+ );
120
139
  throw error;
121
140
  }
122
141
  };
@@ -128,10 +147,16 @@ export function WishlistProvider({ children }: { children: ReactNode }) {
128
147
  ...prev,
129
148
  products: [],
130
149
  }));
131
- toast.success('Wishlist cleared');
150
+ notification.success(
151
+ 'Wishlist cleared',
152
+ 'All items have been removed from your wishlist.'
153
+ );
132
154
  } catch (error) {
133
155
  console.error('Error clearing wishlist:', error);
134
- toast.error('Failed to clear wishlist');
156
+ notification.error(
157
+ 'Could not clear wishlist',
158
+ 'We could not clear your wishlist. Please try again.'
159
+ );
135
160
  throw error;
136
161
  }
137
162
  };
@@ -23,7 +23,6 @@ import { Input } from '@/components/ui/Input';
23
23
  import { Modal } from '@/components/ui/Modal';
24
24
  import { EmptyState } from '@/components/EmptyState';
25
25
  import { useAddresses } from '@/hooks/useAddresses';
26
- import { toast } from 'sonner';
27
26
  import { Address, AddressAddressTypeEnum, CreateAddressDtoAddressTypeEnum, UpdateAddressDto, UpdateAddressDtoAddressTypeEnum } from '@/lib/Apis';
28
27
 
29
28
  const addressFormSchema = z.object({
@@ -86,6 +85,9 @@ export function AddressesScreen() {
86
85
 
87
86
  const [isModalOpen, setIsModalOpen] = useState(false);
88
87
  const [editingAddress, setEditingAddress] = useState<Address | null>(null);
88
+ const [banner, setBanner] = useState<{ type: 'success' | 'error'; message: string } | null>(
89
+ null
90
+ );
89
91
 
90
92
  const {
91
93
  register,
@@ -147,10 +149,10 @@ export function AddressesScreen() {
147
149
  if (editingAddress) {
148
150
  const response = await updateAddress(editingAddress.id, payload);
149
151
  if (response) {
150
- toast.success('Address updated');
152
+ setBanner({ type: 'success', message: 'Address updated successfully' });
151
153
  closeModal();
152
154
  } else {
153
- toast.error('Unable to update address');
155
+ setBanner({ type: 'error', message: 'Unable to update address' });
154
156
  }
155
157
  } else {
156
158
  const response = await addAddress({
@@ -165,10 +167,10 @@ export function AddressesScreen() {
165
167
  addressType: values.addressType as CreateAddressDtoAddressTypeEnum,
166
168
  });
167
169
  if (response) {
168
- toast.success('Address added successfu lly');
170
+ setBanner({ type: 'success', message: 'Address added successfully' });
169
171
  closeModal();
170
172
  } else {
171
- toast.error('Failed to add address');
173
+ setBanner({ type: 'error', message: 'Failed to add address' });
172
174
  }
173
175
  }
174
176
  };
@@ -181,10 +183,12 @@ export function AddressesScreen() {
181
183
 
182
184
  try {
183
185
  await removeAddress(address.id);
184
- toast.success('Address removed successfully');
186
+ setBanner({ type: 'success', message: 'Address removed successfully' });
185
187
  } catch (error) {
186
- toast.error('Failed to remove address', {
187
- description: error instanceof Error ? error.message : 'An unexpected error occurred',
188
+ setBanner({
189
+ type: 'error',
190
+ message:
191
+ error instanceof Error ? error.message : 'Failed to remove address. Please try again.',
188
192
  });
189
193
  }
190
194
  };
@@ -192,10 +196,12 @@ export function AddressesScreen() {
192
196
  const handleSetDefault = async (address: Address) => {
193
197
  try {
194
198
  await markAsDefault(address.id);
195
- toast.success(`${address.name} is now your default address`);
199
+ setBanner({ type: 'success', message: `${address.name} is now your default address` });
196
200
  } catch (error) {
197
- toast.error('Failed to set default address', {
198
- description: error instanceof Error ? error.message : 'An unexpected error occurred',
201
+ setBanner({
202
+ type: 'error',
203
+ message:
204
+ error instanceof Error ? error.message : 'Failed to set default address. Try again.',
199
205
  });
200
206
  }
201
207
  };
@@ -226,65 +232,70 @@ export function AddressesScreen() {
226
232
  }, [addresses]);
227
233
 
228
234
  return (
229
- <div className="min-h-screen bg-slate-50">
230
- <section className="relative overflow-hidden bg-gradient-to-br from-[rgb(var(--header-from))] via-[rgb(var(--header-via))] to-[rgb(var(--header-to))] text-white mb-8">
231
- <div className="absolute inset-0 bg-[radial-gradient(circle_at_top_left,_rgba(255,255,255,0.3),_transparent_60%)]" />
232
- <div className="relative container mx-auto px-4 py-16">
233
- <div className="flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
234
- <motion.div
235
- initial={{ opacity: 0, y: 24 }}
236
- animate={{ opacity: 1, y: 0 }}
237
- className="space-y-5"
238
- >
239
- <span className="inline-flex items-center gap-2 rounded-full bg-white/15 px-3 py-1 text-sm font-semibold uppercase tracking-[0.35em] text-white/70 backdrop-blur">
235
+ <div className="min-h-screen bg-slate-50 text-slate-900">
236
+ <div className="container mx-auto px-4 pb-16 pt-10">
237
+ <div className="rounded-3xl border border-slate-200 bg-white/95 p-6 shadow-xl shadow-primary-50">
238
+ <div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
239
+ <div className="space-y-2">
240
+ <div className="inline-flex items-center gap-2 rounded-full bg-slate-900 text-white px-4 py-1 text-xs font-semibold uppercase tracking-[0.32em]">
240
241
  <MapPin className="h-4 w-4" />
241
242
  Address book
242
- </span>
243
- <h1 className="text-4xl font-bold md:text-5xl">
244
- Manage where your care arrives
245
- </h1>
246
- <p className="max-w-2xl text-white/80 md:text-lg">
247
- Add home, office, or loved ones&apos; addresses and toggle a default for lightning-fast
248
- checkout and delivery.
243
+ </div>
244
+ <h1 className="text-3xl font-semibold md:text-4xl">Manage where your care arrives</h1>
245
+ <p className="max-w-2xl text-sm text-slate-600">
246
+ Save home, office, or loved ones&apos; addresses and choose a default for faster checkout and delivery.
249
247
  </p>
250
- <div className="flex flex-wrap items-center gap-3 text-sm text-white/75">
251
- <span className="inline-flex items-center gap-2 rounded-full bg-white/10 px-3 py-1">
252
- <Sparkles className="h-4 w-4 text-white" />
253
- Default address: {defaultAddress ? defaultAddress.name : 'Not set'}
248
+ <div className="flex flex-wrap gap-3 text-xs text-slate-500">
249
+ <span className="inline-flex items-center gap-2 rounded-full border border-slate-200 bg-slate-50 px-3 py-2">
250
+ <Sparkles className="h-4 w-4 text-primary-600" />
251
+ Default: {defaultAddress ? defaultAddress.name : 'Not set'}
254
252
  </span>
255
- <Button variant="ghost" className="text-white hover:bg-white/10" onClick={openCreateModal}>
256
- <Plus className="h-5 w-5" />
257
- Add address
258
- </Button>
259
253
  </div>
260
- </motion.div>
254
+ </div>
255
+ <div className="flex items-center gap-3">
256
+ {banner && (
257
+ <div
258
+ className={`flex items-start gap-2 rounded-2xl border px-3 py-2 text-xs ${
259
+ banner.type === 'success'
260
+ ? 'border-green-200 bg-green-50 text-green-700'
261
+ : 'border-red-200 bg-red-50 text-red-700'
262
+ }`}
263
+ >
264
+ <span className="mt-[1px] text-base">{banner.type === 'success' ? '✔' : '!'}</span>
265
+ <span>{banner.message}</span>
266
+ </div>
267
+ )}
268
+ <Button
269
+ variant="outline"
270
+ size="md"
271
+ className="border-slate-300 text-slate-800 hover:bg-white"
272
+ onClick={openCreateModal}
273
+ >
274
+ <Plus className="h-5 w-5" />
275
+ Add address
276
+ </Button>
277
+ </div>
278
+ </div>
261
279
 
262
- <motion.div
263
- initial={{ opacity: 0, y: 24 }}
264
- animate={{ opacity: 1, y: 0 }}
265
- transition={{ delay: 0.1 }}
266
- className="grid gap-4 rounded-3xl bg-white/15 p-6 text-sm text-white/80 backdrop-blur"
267
- >
268
- {stats.map((stat) => (
269
- <div key={stat.id} className="flex items-center justify-between">
270
- <div>
271
- <p className="text-xs font-semibold uppercase tracking-[0.3em] text-white/70">
272
- {stat.label}
273
- </p>
274
- <p className="text-white">{stat.helper}</p>
275
- </div>
276
- <span className="text-3xl font-semibold text-white">{stat.value}</span>
280
+ <div className="mt-6 grid gap-4 sm:grid-cols-3">
281
+ {stats.map((stat) => (
282
+ <div
283
+ key={stat.id}
284
+ className="rounded-2xl border border-slate-200 bg-slate-50/70 px-4 py-3 text-sm text-slate-600"
285
+ >
286
+ <p className="text-xs font-semibold uppercase tracking-[0.28em] text-slate-500">
287
+ {stat.label}
288
+ </p>
289
+ <div className="mt-1 flex items-center justify-between">
290
+ <p className="text-slate-800">{stat.helper}</p>
291
+ <span className="text-xl font-semibold text-slate-900">{stat.value}</span>
277
292
  </div>
278
- ))}
279
- </motion.div>
293
+ </div>
294
+ ))}
280
295
  </div>
281
296
  </div>
282
- </section>
283
-
284
- <div className="relative -mt-16 pb-20">
285
- <div className="container mx-auto px-4">
286
-
287
297
 
298
+ <div className="mt-10">
288
299
  {isLoading ? (
289
300
  <div className="grid gap-4 sm:grid-cols-2 xl:grid-cols-3">
290
301
  {Array.from({ length: 3 }).map((_, index) => (
@@ -317,7 +328,7 @@ export function AddressesScreen() {
317
328
  key={address.id}
318
329
  initial={{ opacity: 0, y: 24 }}
319
330
  animate={{ opacity: 1, y: 0 }}
320
- className="group relative flex h-full flex-col rounded-3xl border border-slate-100 bg-white p-6 shadow-sm transition hover:-translate-y-1 hover:shadow-xl"
331
+ className="group relative flex h-full flex-col rounded-3xl border border-slate-200 bg-white p-6 shadow-sm transition hover:-translate-y-1 hover:border-primary-200 hover:shadow-lg"
321
332
  >
322
333
  {address.isDefault && (
323
334
  <span className="absolute right-6 top-6 inline-flex items-center gap-2 rounded-full bg-amber-100 px-3 py-1 text-xs font-semibold text-amber-700">
@@ -17,22 +17,22 @@ import { formatPrice } from '@/lib/utils/format';
17
17
  import { useRouter } from 'next/navigation';
18
18
  import { CartItemPopulated } from '@/lib/Apis';
19
19
  import { useBasePath } from '@/providers/BasePathProvider';
20
+ import { useAuth } from '@/providers/AuthProvider';
21
+ import { useNotification } from '@/providers/NotificationProvider';
20
22
 
21
23
 
22
24
  export function CartScreen() {
23
25
  const router = useRouter();
24
26
  const { cart, isLoading } = useCart();
25
27
  const { buildPath } = useBasePath();
28
+ const { isAuthenticated, user } = useAuth();
29
+ const notification = useNotification();
26
30
 
27
31
  if (!cart || cart.cartBody.items.length === 0) {
28
32
  return (
29
33
  <div className="bg-white">
30
34
  <div className="min-h-screen bg-white max-w-6xl mx-auto">
31
35
  <div className="container mx-auto px-4 py-8">
32
- <div className="mb-6">
33
- <h1 className="text-2xl font-bold text-slate-900">Shopping Cart</h1>
34
- <p className="text-sm text-gray-500 mt-1">0 items in your cart</p>
35
- </div>
36
36
 
37
37
  <div className="flex flex-col items-center justify-center py-20">
38
38
  <motion.div
@@ -42,15 +42,15 @@ export function CartScreen() {
42
42
  >
43
43
  <div className="flex justify-center">
44
44
  <div className="rounded-full bg-gray-100 p-6">
45
- <ShoppingBag className="h-12 w-12 text-gray-400" />
45
+ <ShoppingBag className="h-12 w-12 text-secondary" />
46
46
  </div>
47
47
  </div>
48
48
 
49
49
  <div className="space-y-2">
50
- <h2 className="text-2xl font-bold text-slate-900">
50
+ <h2 className="text-2xl font-bold text-secondary">
51
51
  Your cart is empty
52
52
  </h2>
53
- <p className="text-gray-500">
53
+ <p className="text-muted">
54
54
  Start adding products to your cart to see them here.
55
55
  </p>
56
56
  </div>
@@ -59,7 +59,7 @@ export function CartScreen() {
59
59
  <button
60
60
  type="button"
61
61
  onClick={() => router.push(buildPath('/shop'))}
62
- className="rounded-full border-2 border-primary-500 bg-primary-500 hover:bg-primary-600 text-white px-6 py-3 text-sm font-medium transition-colors flex items-center justify-center gap-2"
62
+ className="rounded-xl border-2 border-primary bg-primary text-white px-6 py-3 text-sm font-medium transition-colors flex items-center justify-center gap-2 hover:opacity-80"
63
63
  >
64
64
  Discover products
65
65
  <ArrowRight className="h-5 w-5" />
@@ -68,15 +68,15 @@ export function CartScreen() {
68
68
 
69
69
  <div className="mt-8 space-y-3 pt-6 border-t border-gray-200">
70
70
  <div className="flex items-start gap-3 text-sm text-slate-600">
71
- <CheckCircle2 className="h-5 w-5 text-primary-600 flex-shrink-0 mt-0.5" />
71
+ <CheckCircle2 className="h-5 w-5 text-secondary flex-shrink-0 mt-0.5" />
72
72
  <span>Free shipping on all orders</span>
73
73
  </div>
74
74
  <div className="flex items-start gap-3 text-sm text-slate-600">
75
- <CheckCircle2 className="h-5 w-5 text-primary-600 flex-shrink-0 mt-0.5" />
75
+ <CheckCircle2 className="h-5 w-5 text-secondary flex-shrink-0 mt-0.5" />
76
76
  <span>Easy returns within 30 days</span>
77
77
  </div>
78
78
  <div className="flex items-start gap-3 text-sm text-slate-600">
79
- <CheckCircle2 className="h-5 w-5 text-primary-600 flex-shrink-0 mt-0.5" />
79
+ <CheckCircle2 className="h-5 w-5 text-secondary flex-shrink-0 mt-0.5" />
80
80
  <span>Secure checkout process</span>
81
81
  </div>
82
82
  </div>
@@ -94,22 +94,38 @@ export function CartScreen() {
94
94
  const total = subtotal + shipping + tax;
95
95
  const itemCount = cart.cartBody.items.length;
96
96
 
97
+ const handleSubmit = async (e?: React.FormEvent<HTMLFormElement> | React.MouseEvent) => {
98
+ if (!isAuthenticated) {
99
+ notification.error(
100
+ 'Sign-in required',
101
+ 'Please log in to complete your checkout.'
102
+ );
103
+ setTimeout(() => router.push(buildPath('/login?redirect=/checkout')), 50);
104
+ return;
105
+ }
106
+
107
+ router.push(buildPath('/checkout'));
108
+ };
109
+
97
110
  return (
98
- <div className="bg-white">
99
- <div className="min-h-screen bg-white max-w-6xl mx-auto">
111
+ <div className="min-h-screen bg-white">
112
+ <div className="max-w-[1400px] mx-auto px-8 md:px-12">
100
113
  <div className="container mx-auto px-4 py-8">
101
- <div className="mb-6">
102
- <h1 className="text-2xl font-bold text-slate-900">Shopping Cart</h1>
103
- <p className="text-sm text-gray-500 mt-1">
114
+ {/* Header */}
115
+ <div className="mb-12">
116
+ <h1 className="font-['Poppins',sans-serif] font-semibold text-secondary tracking-[-2px] mb-2 text-4xl">
117
+ Shopping Cart
118
+ </h1>
119
+ <p className="font-['Poppins',sans-serif] text-[16px] text-muted">
104
120
  {itemCount} {itemCount === 1 ? 'item' : 'items'} in your cart
105
121
  </p>
106
122
  </div>
107
- <div className="grid gap-8 lg:grid-cols-[1.5fr_1fr]">
123
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
108
124
  {/* Shopping Cart Section */}
109
125
  <motion.section
110
126
  initial={{ opacity: 0, y: 24 }}
111
127
  animate={{ opacity: 1, y: 0 }}
112
- className="space-y-6"
128
+ className="space-y-6 lg:col-span-2"
113
129
  >
114
130
 
115
131
 
@@ -132,15 +148,15 @@ export function CartScreen() {
132
148
  initial={{ opacity: 0, y: 24 }}
133
149
  animate={{ opacity: 1, y: 0 }}
134
150
  transition={{ delay: 0.1 }}
135
- className="space-y-6 lg:sticky lg:top-8 h-fit"
151
+ className="space-y-6 lg:sticky lg:top-24 h-fit lg:col-span-1"
136
152
  >
137
- <div className="rounded-xl border-2 border-primary-200 bg-white p-6 shadow-md">
138
- <h2 className="text-xl font-bold text-slate-900 mb-6">Order Summary</h2>
153
+ <div className="bg-gradient-to-br from-[#5B9BD5]/10 to-[#2B4B7C]/10 rounded-[24px] p-8 border-2 border-[#5B9BD5]/20 sticky top-24">
154
+ <h2 className="font-['Poppins',sans-serif] font-semibold text-secondary mb-6">Order Summary</h2>
139
155
 
140
156
  <div className="space-y-4 mb-6">
141
- <div className="flex items-center justify-between text-sm">
142
- <span className="text-gray-600">Subtotal ({itemCount} {itemCount === 1 ? 'item' : 'items'})</span>
143
- <span className="font-semibold text-slate-900">{formatPrice(subtotal)}</span>
157
+ <div className="flex items-center justify-between">
158
+ <span className="font-['Poppins',sans-serif] text-[14px] text-muted">Subtotal ({itemCount} {itemCount === 1 ? 'item' : 'items'})</span>
159
+ <span className="font-['Poppins',sans-serif] font-semibold text-[14px] text-secondary">{formatPrice(subtotal)}</span>
144
160
  </div>
145
161
  <div className="flex items-center justify-between text-sm">
146
162
  <span className="text-gray-600">Shipping</span>
@@ -150,7 +166,7 @@ export function CartScreen() {
150
166
  </div>
151
167
  <div className="border-t border-gray-200 pt-4 mt-4">
152
168
  <div className="flex items-center justify-between">
153
- <span className="text-lg font-bold text-slate-900">Total</span>
169
+ <span className="text-lg font-bold text-secondary">Total</span>
154
170
  <span className="text-2xl font-bold text-orange-600">{formatPrice(total)}</span>
155
171
  </div>
156
172
  </div>
@@ -158,8 +174,8 @@ export function CartScreen() {
158
174
 
159
175
  <div className="space-y-3">
160
176
  <button
161
- type="button"
162
- onClick={() => router.push(buildPath('/checkout'))}
177
+ type="submit"
178
+ onClick={handleSubmit}
163
179
  className="w-full rounded-full border-2 border-primary-500 bg-primary-500 hover:bg-primary-600 text-white px-4 py-3 text-sm font-medium transition-colors flex items-center justify-center gap-2"
164
180
  >
165
181
  Proceed to Checkout
@@ -168,7 +184,7 @@ export function CartScreen() {
168
184
  <button
169
185
  type="button"
170
186
  onClick={() => router.push(buildPath('/shop'))}
171
- className="w-full rounded-full border-2 border-primary-300 bg-white px-4 py-3 text-sm font-medium text-slate-700 hover:bg-gray-50 transition-colors"
187
+ className="w-full rounded-full border-2 border-muted bg-white px-4 py-3 text-sm font-medium text-slate-700 hover:bg-gray-50 transition-colors"
172
188
  >
173
189
  Continue Shopping
174
190
  </button>
@@ -176,23 +192,23 @@ export function CartScreen() {
176
192
 
177
193
  <div className="mt-6 space-y-3 pt-6 border-t border-gray-200">
178
194
  <div className="flex items-start gap-3 text-sm text-slate-600">
179
- <CheckCircle2 className="h-5 w-5 text-primary-600 flex-shrink-0 mt-0.5" />
195
+ <CheckCircle2 className="h-5 w-5 text-muted flex-shrink-0 mt-0.5" />
180
196
  <span>Easy returns within 30 days</span>
181
197
  </div>
182
198
  <div className="flex items-start gap-3 text-sm text-slate-600">
183
- <CheckCircle2 className="h-5 w-5 text-primary-600 flex-shrink-0 mt-0.5" />
199
+ <CheckCircle2 className="h-5 w-5 text-muted flex-shrink-0 mt-0.5" />
184
200
  <span>Secure checkout process</span>
185
201
  </div>
186
202
  </div>
187
203
  </div>
188
204
 
189
- <div className="rounded-3xl border border-primary-100 bg-primary-50/70 p-6 text-sm text-primary-700 shadow-sm">
205
+ {/* <div className="rounded-3xl border border-primary-100 bg-primary-50/70 p-6 text-sm text-primary-700 shadow-sm">
190
206
  <p className="font-semibold uppercase tracking-[0.3em]">Need help?</p>
191
207
  <p className="mt-2 leading-relaxed">
192
208
  Chat with a pharmacist to optimize your regimen or discuss substitutions before you
193
209
  check out.
194
210
  </p>
195
- </div>
211
+ </div> */}
196
212
  </motion.aside>
197
213
  </div>
198
214
  </div>