hey-pharmacist-ecommerce 1.1.13 → 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 (45) hide show
  1. package/dist/index.d.mts +2 -4
  2. package/dist/index.d.ts +2 -4
  3. package/dist/index.js +1039 -857
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +1039 -856
  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/Header.tsx +69 -16
  17. package/src/components/Notification.tsx +148 -0
  18. package/src/components/ProductCard.tsx +215 -178
  19. package/src/components/QuickViewModal.tsx +314 -0
  20. package/src/components/TabNavigation.tsx +48 -0
  21. package/src/components/ui/Button.tsx +1 -1
  22. package/src/components/ui/ConfirmModal.tsx +84 -0
  23. package/src/hooks/usePaymentMethods.ts +58 -0
  24. package/src/index.ts +0 -1
  25. package/src/providers/CartProvider.tsx +22 -6
  26. package/src/providers/EcommerceProvider.tsx +8 -7
  27. package/src/providers/FavoritesProvider.tsx +10 -3
  28. package/src/providers/NotificationProvider.tsx +79 -0
  29. package/src/providers/WishlistProvider.tsx +34 -9
  30. package/src/screens/AddressesScreen.tsx +72 -61
  31. package/src/screens/CartScreen.tsx +48 -32
  32. package/src/screens/ChangePasswordScreen.tsx +155 -0
  33. package/src/screens/CheckoutScreen.tsx +162 -125
  34. package/src/screens/EditProfileScreen.tsx +165 -0
  35. package/src/screens/LoginScreen.tsx +59 -72
  36. package/src/screens/NewAddressScreen.tsx +16 -10
  37. package/src/screens/ProductDetailScreen.tsx +334 -234
  38. package/src/screens/ProfileScreen.tsx +190 -200
  39. package/src/screens/RegisterScreen.tsx +51 -70
  40. package/src/screens/SearchResultsScreen.tsx +2 -1
  41. package/src/screens/ShopScreen.tsx +260 -384
  42. package/src/screens/WishlistScreen.tsx +226 -224
  43. package/src/styles/globals.css +9 -0
  44. package/src/screens/CategoriesScreen.tsx +0 -122
  45. package/src/screens/HomeScreen.tsx +0 -211
@@ -0,0 +1,155 @@
1
+ 'use client';
2
+
3
+ import React, { useState } from 'react';
4
+ import { motion } from 'framer-motion';
5
+ import { useForm } from 'react-hook-form';
6
+ import { z } from 'zod';
7
+ import { zodResolver } from '@hookform/resolvers/zod';
8
+ import { useRouter } from 'next/navigation';
9
+ import { Lock, ShieldCheck } from 'lucide-react';
10
+ import { Input } from '@/components/ui/Input';
11
+ import { Button } from '@/components/ui/Button';
12
+ import { AuthApi } from '@/lib/Apis/apis/auth-api';
13
+ import { getApiConfiguration } from '@/lib/api-adapter/config';
14
+ import { useAuth } from '@/providers/AuthProvider';
15
+ import { useBasePath } from '@/providers/BasePathProvider';
16
+
17
+ const changePasswordSchema = z
18
+ .object({
19
+ currentPassword: z.string().min(6, 'Current password is required'),
20
+ newPassword: z.string().min(8, 'Password must be at least 8 characters'),
21
+ confirmPassword: z.string().min(8, 'Confirm your new password'),
22
+ })
23
+ .refine((data) => data.newPassword === data.confirmPassword, {
24
+ path: ['confirmPassword'],
25
+ message: 'Passwords do not match',
26
+ });
27
+
28
+ type ChangePasswordFormData = z.infer<typeof changePasswordSchema>;
29
+
30
+ export function ChangePasswordScreen() {
31
+ const router = useRouter();
32
+ const { user } = useAuth();
33
+ const { buildPath } = useBasePath();
34
+ const [isSubmitting, setIsSubmitting] = useState(false);
35
+ const [status, setStatus] = useState<{ type: 'success' | 'error'; message: string } | null>(
36
+ null
37
+ );
38
+
39
+ const {
40
+ register,
41
+ handleSubmit,
42
+ formState: { errors },
43
+ } = useForm<ChangePasswordFormData>({
44
+ resolver: zodResolver(changePasswordSchema),
45
+ });
46
+
47
+ if (!user) {
48
+ router.push(buildPath('/login'));
49
+ return null;
50
+ }
51
+
52
+ const onSubmit = async (data: ChangePasswordFormData) => {
53
+ setIsSubmitting(true);
54
+ setStatus(null);
55
+ try {
56
+ await new AuthApi(getApiConfiguration()).changePassword(
57
+ data.currentPassword,
58
+ data.newPassword
59
+ );
60
+ setStatus({ type: 'success', message: 'Password updated successfully' });
61
+ setTimeout(() => router.push(buildPath('/account')), 600);
62
+ } catch (error: any) {
63
+ setStatus({
64
+ type: 'error',
65
+ message: error?.response?.data?.message || 'Unable to update password',
66
+ });
67
+ } finally {
68
+ setIsSubmitting(false);
69
+ }
70
+ };
71
+
72
+ return (
73
+ <div className="min-h-screen bg-slate-50 text-slate-900">
74
+ <div className="container mx-auto px-4 pb-16 pt-10">
75
+ <motion.div
76
+ initial={{ opacity: 0, y: 18 }}
77
+ animate={{ opacity: 1, y: 0 }}
78
+ className="mx-auto max-w-2xl rounded-3xl border border-slate-200 bg-white p-8 shadow-xl shadow-primary-50"
79
+ >
80
+ <div className="flex items-center gap-3">
81
+ <span className="flex h-11 w-11 items-center justify-center rounded-2xl bg-primary-50 text-primary-600">
82
+ <Lock className="h-5 w-5" />
83
+ </span>
84
+ <div>
85
+ <p className="text-xs font-semibold uppercase tracking-[0.32em] text-slate-500">
86
+ Security
87
+ </p>
88
+ <h1 className="text-2xl font-semibold text-slate-900">Change password</h1>
89
+ </div>
90
+ </div>
91
+ <p className="mt-3 text-sm text-slate-600">
92
+ Use a strong password that you have not used elsewhere. Updating your password will sign
93
+ you out of other active sessions.
94
+ </p>
95
+
96
+ {status && (
97
+ <div
98
+ className={`mt-4 flex items-start gap-2 rounded-2xl border px-4 py-3 text-sm ${
99
+ status.type === 'success'
100
+ ? 'border-green-200 bg-green-50 text-green-800'
101
+ : 'border-red-200 bg-red-50 text-red-700'
102
+ }`}
103
+ >
104
+ <span className="mt-[2px] text-base">{status.type === 'success' ? '✔' : '!'}</span>
105
+ <span>{status.message}</span>
106
+ </div>
107
+ )}
108
+
109
+ <form onSubmit={handleSubmit(onSubmit)} className="mt-8 space-y-5">
110
+ <Input
111
+ type="password"
112
+ label="Current password"
113
+ placeholder="Enter current password"
114
+ {...register('currentPassword')}
115
+ error={errors.currentPassword?.message}
116
+ />
117
+ <Input
118
+ type="password"
119
+ label="New password"
120
+ placeholder="Enter new password"
121
+ {...register('newPassword')}
122
+ error={errors.newPassword?.message}
123
+ />
124
+ <Input
125
+ type="password"
126
+ label="Confirm new password"
127
+ placeholder="Re-type new password"
128
+ {...register('confirmPassword')}
129
+ error={errors.confirmPassword?.message}
130
+ />
131
+ <div className="flex flex-wrap gap-3">
132
+ <Button type="submit" size="lg" isLoading={isSubmitting}>
133
+ Save password
134
+ </Button>
135
+ <Button
136
+ type="button"
137
+ variant="outline"
138
+ size="lg"
139
+ className="border-slate-300 text-slate-800 hover:bg-slate-50"
140
+ onClick={() => router.push(buildPath('/account'))}
141
+ >
142
+ Cancel
143
+ </Button>
144
+ </div>
145
+ </form>
146
+
147
+ <div className="mt-6 flex items-center gap-2 rounded-2xl border border-slate-200 bg-slate-50 px-4 py-3 text-sm text-slate-600">
148
+ <ShieldCheck className="h-4 w-4 text-primary-600" />
149
+ Strong passwords and up-to-date contact details help pharmacists verify changes quickly.
150
+ </div>
151
+ </motion.div>
152
+ </div>
153
+ </div>
154
+ );
155
+ }
@@ -14,6 +14,7 @@ import {
14
14
  Plus,
15
15
  ShieldCheck,
16
16
  Truck,
17
+ Check,
17
18
  } from 'lucide-react';
18
19
  import { Button } from '@/components/ui/Button';
19
20
  import { Input } from '@/components/ui/Input';
@@ -24,7 +25,6 @@ import { ShippingApi } from '@/lib/Apis/apis/shipping-api';
24
25
  import { useAddresses } from '@/hooks/useAddresses';
25
26
  import { formatPrice } from '@/lib/utils/format';
26
27
  import { useRouter } from 'next/navigation';
27
- import { toast } from 'sonner';
28
28
  import { AXIOS_CONFIG } from '@/lib/Apis/wrapper';
29
29
  import { Card } from '@/components/ui/Card';
30
30
  import { AddressFormModal } from '@/components/AddressFormModal';
@@ -33,6 +33,7 @@ import { useBasePath } from '@/providers/BasePathProvider';
33
33
 
34
34
  import { addressSchema } from '@/lib/validations/address';
35
35
  import Image from 'next/image';
36
+ import { useNotification } from '@/providers/NotificationProvider';
36
37
 
37
38
  const checkoutSchema = z.object({
38
39
  shipping: addressSchema,
@@ -89,6 +90,7 @@ export function CheckoutScreen() {
89
90
  const [isAddressModalOpen, setIsAddressModalOpen] = useState(false);
90
91
  const [editingAddress, setEditingAddress] = useState<any | null>(null);
91
92
  const [shippingPrice, setShippingPrice] = useState(0);
93
+ const notification = useNotification();
92
94
 
93
95
 
94
96
  // Use the addresses hook
@@ -175,7 +177,7 @@ export function CheckoutScreen() {
175
177
  // Sort rates by price (cheapest first) and select the first one
176
178
  const sortedRates = [...rates].sort((a, b) => parseFloat(a.amount) - parseFloat(b.amount));
177
179
  const cheapestRate = sortedRates[0];
178
-
180
+
179
181
  setShippingRates(rates);
180
182
  setShippingRatesError(null);
181
183
  setSelectedShippingRateId(cheapestRate.objectId);
@@ -284,7 +286,10 @@ export function CheckoutScreen() {
284
286
  const onSubmit = async (data: CheckoutFormData) => {
285
287
  setError(null);
286
288
  if (!isAuthenticated) {
287
- toast.error('Please login to continue');
289
+ notification.error(
290
+ 'Sign-in required',
291
+ 'Please log in to complete your checkout.'
292
+ );
288
293
  setTimeout(() => router.push(buildPath('/login?redirect=/checkout')), 50);
289
294
  return;
290
295
  }
@@ -337,7 +342,10 @@ export function CheckoutScreen() {
337
342
  }
338
343
 
339
344
  setIsSubmitting(true);
340
- toast('Submitting order...', { icon: <CreditCard className="h-4 w-4" /> });
345
+ notification.info(
346
+ 'Submitting order',
347
+ 'We are processing your order and preparing your payment options.'
348
+ );
341
349
  try {
342
350
  const items = (cart?.cartBody?.items || []).map((item: any) => ({
343
351
  productVariantId: String(item.productVariantId),
@@ -371,7 +379,10 @@ export function CheckoutScreen() {
371
379
  );
372
380
  if (response?.data?.payment && response.data.payment.hostedInvoiceUrl === 'INSUFFICIENT_CREDIT') {
373
381
  setError('You have insufficient credit to complete this order.');
374
- toast.error('You have insufficient credit to complete this order.');
382
+ notification.error(
383
+ 'Insufficient credit',
384
+ 'You do not have enough credit to complete this order.'
385
+ );
375
386
  return;
376
387
  }
377
388
  if (paymentMethod === 'Card') {
@@ -390,22 +401,34 @@ export function CheckoutScreen() {
390
401
  } else if (!paymentUrl) {
391
402
  console.error('No payment URL received in response:', response.data);
392
403
  setError('Failed to initiate payment. Please try again or contact support.');
393
- toast.error('Failed to initiate payment');
404
+ notification.error(
405
+ 'Payment setup failed',
406
+ 'We could not start the payment flow. Please try again or contact support.'
407
+ );
394
408
  return;
395
409
  }
396
-
397
- toast.success('Order placed successfully!');
410
+
411
+ notification.success(
412
+ 'Order placed',
413
+ 'Your order has been placed successfully. You will be redirected to payment.'
414
+ );
398
415
  await clearCart();
399
416
  router.push(buildPath(`/orders/${response.data?.id}`));
400
417
  } else {
401
- toast.success('Order placed successfully!');
418
+ notification.success(
419
+ 'Order placed',
420
+ 'Your order has been placed successfully.'
421
+ );
402
422
  await clearCart();
403
423
  router.push(buildPath(`/orders/${response.data?.id}`));
404
424
  }
405
425
  } catch (err: any) {
406
426
  const msg = err?.message || (err?.response?.data?.message) || 'Failed to place order';
407
427
  setError(msg);
408
- toast.error(msg);
428
+ notification.error(
429
+ 'Could not place order',
430
+ msg
431
+ );
409
432
  } finally {
410
433
  setIsSubmitting(false);
411
434
  }
@@ -420,8 +443,8 @@ export function CheckoutScreen() {
420
443
  const total = subtotal + shippingPrice + tax;
421
444
 
422
445
  return (
423
- <div className="min-h-screen bg-slate-50 mb-16">
424
-
446
+ <div className="min-h-screen bg-white pb-16">
447
+
425
448
 
426
449
  <form onSubmit={handleSubmit(onSubmit)}>
427
450
  {error && <div className="mb-4 text-red-600 font-semibold">{error}</div>}
@@ -431,11 +454,20 @@ export function CheckoutScreen() {
431
454
  animate={{ opacity: 1, y: 0 }}
432
455
  className="space-y-8"
433
456
  >
434
- <section className="rounded-3xl border border-slate-100 bg-white p-6 shadow-lg shadow-primary-50">
457
+ {/* Header */}
458
+ <div className="mb-12">
459
+ <h1 className="font-['Poppins',sans-serif] font-semibold text-[#2B4B7C] mb-2 text-3xl">
460
+ Checkout
461
+ </h1>
462
+ <p className="font-['Poppins',sans-serif] text-[14px] text-[#676c80] text-md leading-relaxed">
463
+ Complete your order information below
464
+ </p>
465
+ </div>
466
+ <section className="bg-white border-2 border-gray-100 rounded-[24px] p-8 text-[#2B4B7C]">
435
467
  <div className="flex flex-wrap items-center justify-between gap-4">
436
468
  <div>
437
- <h2 className="text-xl font-semibold text-slate-900">
438
- Shipping information
469
+ <h2 className="font-['Poppins',sans-serif] font-semibold text-[#2B4B7C] mb-2 text-2xl">
470
+ Contact Information
439
471
  </h2>
440
472
  <p className="text-sm text-slate-500">
441
473
  We use temperature-aware packaging and real-time tracking on every shipment.
@@ -527,9 +559,9 @@ export function CheckoutScreen() {
527
559
  try {
528
560
  await removeAddress(addr.id);
529
561
  if (selectedAddressId === addr.id) setSelectedAddressId(null);
530
- toast.success('Address deleted');
562
+ notification.success('Address deleted');
531
563
  } catch (e) {
532
- toast.error('Failed to delete address');
564
+ notification.error('Failed to delete address');
533
565
  }
534
566
  }}
535
567
  className="inline-flex items-center gap-1 rounded-full border border-red-200 px-2.5 py-0.5 text-xs font-semibold text-red-600 hover:border-red-300 hover:text-red-700"
@@ -570,10 +602,10 @@ export function CheckoutScreen() {
570
602
 
571
603
  {/* Shipping Options - Only show if there's a shipping cost */}
572
604
  {isDelivery && selectedAddressId && (
573
- <Card className="p-6 border border-gray-200 rounded-xl shadow-sm">
574
- <div className="flex items-center gap-3 text-xl font-semibold text-gray-900 pb-4 mb-6 border-b">
575
- <Truck className="text-accent w-6 h-6" />
576
- <span>Shipping Options</span>
605
+ <div className="bg-white border-2 border-gray-100 rounded-[24px] p-8 text-[#2B4B7C]">
606
+ <div className="flex items-center gap-3 text-xl font-semibold text-gray-900 pb-4 mb-8 border-b">
607
+ <Truck className="text-accent w-8 h-8 flex items-center justify-center text-[#2B4B7C]" />
608
+ <h2 className="font-['Poppins',sans-serif] font-semibold text-[#2B4B7C] text-2xl">Shipping Options</h2>
577
609
  </div>
578
610
 
579
611
  {shippingRatesLoading ? (
@@ -735,25 +767,18 @@ export function CheckoutScreen() {
735
767
  </div>
736
768
  )}
737
769
 
738
- </Card>
770
+ </div>
739
771
  )}
740
- </motion.div>
741
-
742
- <motion.aside
743
- initial={{ opacity: 0, y: 32 }}
744
- animate={{ opacity: 1, y: 0 }}
745
- transition={{ duration: 0.5, ease: 'easeOut', delay: 0.1 }}
746
- className="space-y-10 lg:sticky lg:top-24"
747
- >
748
- {/* Order Summary */}
749
- <div className="rounded-3xl border border-slate-100 bg-white p-6 shadow-xl shadow-primary-50/50 transition hover:shadow-primary-100/60">
750
- <h2 className="text-2xl font-semibold text-slate-900">Order Summary</h2>
751
772
 
773
+ {/* Delivery and Payment Methods Section */}
774
+ <div className="space-y-6">
752
775
  {/* Delivery Method */}
753
- <section className="mt-6 space-y-4">
754
- <h3 className="text-xs font-semibold text-slate-700 uppercase tracking-wider">
755
- Delivery Method
756
- </h3>
776
+ <div className="bg-white border-2 border-gray-100 rounded-[24px] p-8">
777
+ <div className="flex items-center gap-3 mb-6">
778
+ <Truck className="w-8 h-8 text-[#2B4B7C]" />
779
+ <h2 className="text-2xl font-semibold text-[#2B4B7C]">Delivery Method</h2>
780
+ </div>
781
+
757
782
  <div className="grid grid-cols-1 gap-3">
758
783
  {[
759
784
  {
@@ -769,88 +794,106 @@ export function CheckoutScreen() {
769
794
  desc: 'Collect from pharmacy',
770
795
  },
771
796
  ].map((option) => {
772
- const active = isDelivery === option.value
797
+ const active = isDelivery === option.value;
773
798
  return (
774
799
  <button
775
800
  key={option.label}
776
801
  type="button"
777
- onClick={() => setIsDelivery(option.value)}
778
- aria-pressed={active}
779
- className={`relative flex w-full items-center justify-between rounded-xl border-2 p-3 transition-all duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-400 focus-visible:ring-offset-2 ${active
780
- ? 'border-primary-500 bg-primary-50 shadow-sm'
781
- : 'border-slate-200 bg-white hover:border-primary-200 hover:bg-primary-50/40'
802
+ onClick={() => {
803
+ setIsDelivery(option.value);
804
+ // Reset selected shipping rate when changing delivery method
805
+ if (option.value) {
806
+ setSelectedShippingRateId(null);
807
+ setShippingPrice(0);
808
+ }
809
+ }}
810
+ className={`relative flex w-full items-center justify-between rounded-xl border-2 p-4 transition-all duration-200 ${active
811
+ ? 'border-primary-500 bg-primary-50'
812
+ : 'border-gray-200 hover:border-primary-300'
782
813
  }`}
783
814
  >
784
815
  <div className="flex items-center gap-3">
785
816
  <div
786
- className={`p-2 rounded-lg ${active
787
- ? 'bg-primary-100 text-primary-600'
788
- : 'bg-slate-100 text-slate-600 group-hover:bg-slate-50'
817
+ className={`p-2 rounded-lg ${active ? 'bg-primary-100 text-primary-600' : 'bg-gray-100 text-gray-600'
789
818
  }`}
790
819
  >
791
820
  {option.icon}
792
821
  </div>
793
822
  <div className="text-left">
794
- <div className="font-medium text-slate-900">
795
- {option.label}
796
- </div>
797
- <p className="text-xs text-slate-500">{option.desc}</p>
823
+ <div className="font-medium text-gray-900">{option.label}</div>
824
+ <p className="text-sm text-gray-500">{option.desc}</p>
798
825
  </div>
799
826
  </div>
800
827
  {active && (
801
- <div className="w-5 h-5 rounded-full bg-primary-500 flex items-center justify-center text-white shadow-sm">
802
- <CheckIcon className="w-3 h-3" />
828
+ <div className="w-5 h-5 rounded-full bg-primary-500 flex items-center justify-center">
829
+ <Check className="w-3 h-3 text-white" />
803
830
  </div>
804
831
  )}
805
832
  </button>
806
- )
833
+ );
807
834
  })}
808
835
  </div>
809
- </section>
836
+ </div>
810
837
 
811
838
  {/* Payment Method */}
812
- <section className="mt-8 space-y-4">
813
- <h3 className="text-xs font-semibold text-slate-700 uppercase tracking-wider">
814
- Payment Method
815
- </h3>
816
- <div className="space-y-3">
839
+ <div className="bg-white border-2 border-gray-100 rounded-[24px] p-8">
840
+ <div className="flex items-center gap-3 mb-6">
841
+ <CreditCard className="w-8 h-8 text-[#2B4B7C]" />
842
+ <h2 className="text-2xl font-semibold text-[#2B4B7C]">Payment Method</h2>
843
+ </div>
844
+
845
+ <div className="grid gap-4 md:grid-cols-3">
817
846
  {PAYMENT_METHODS.map((pm) => {
818
- const active = paymentMethod === pm.value
847
+ const active = paymentMethod === pm.value;
848
+
819
849
  return (
820
850
  <button
821
851
  key={pm.value}
822
852
  type="button"
823
853
  onClick={() => setPaymentMethod(pm.value)}
824
- className={`w-full flex items-center justify-between rounded-xl border-2 p-3 transition-all duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 ${active
825
- ? `${pm.activeClass} border-current shadow-sm`
826
- : `${pm.className} border-slate-200 hover:border-primary-200 hover:shadow-sm`
827
- }`}
854
+ className={`
855
+ group relative flex w-full items-center justify-between rounded-xl border-2 p-3
856
+ transition-all duration-200 ease-out
857
+ focus:outline-none f
858
+ ${active
859
+ ? `${pm.activeClass} shadow-md scale-[1.02]`
860
+ : `${pm.className} border-gray-200 bg-white hover:shadow-sm hover:-translate-y-0.5`
861
+ }
862
+ `}
828
863
  >
829
864
  <div className="flex items-center gap-3">
830
865
  <div
831
- className={`p-1.5 rounded-md ${pm.value === 'Card'
832
- ? 'bg-blue-100 text-blue-600'
833
- : pm.value === 'Cash'
834
- ? 'bg-amber-100 text-amber-600'
835
- : 'bg-emerald-100 text-emerald-600'
836
- } ${active ? 'bg-opacity-30' : 'bg-opacity-100'}`}
866
+ className={`
867
+ flex items-center justify-center rounded-lg p-2
868
+ transition-colors duration-200
869
+ ${pm.value === 'Card'
870
+ ? ' text-blue-600'
871
+ : pm.value === 'Cash'
872
+ ? ' text-amber-600'
873
+ : ' text-emerald-600'
874
+ }
875
+ ${active ? 'opacity-80' : 'group-hover:opacity-90'}
876
+ `}
837
877
  >
838
878
  {pm.icon}
839
879
  </div>
840
- <span className="text-sm font-medium text-slate-900">
880
+
881
+ <span className="text-sm font-semibold text-gray-900">
841
882
  {pm.label}
842
883
  </span>
843
884
  </div>
885
+
844
886
  {active && (
845
- <div className="w-4 h-4 rounded-full bg-primary-500 flex items-center justify-center text-white shadow-sm">
846
- <CheckIcon className="w-2.5 h-2.5" />
887
+ <div className="flex h-6 w-6 items-center justify-center rounded-full shadow-sm">
888
+ <Check className="h-3.5 w-3.5 text-white" />
847
889
  </div>
848
890
  )}
849
891
  </button>
850
- )
892
+ );
851
893
  })}
852
894
  </div>
853
- <p className="text-xs text-slate-500 mt-2 pl-1 leading-relaxed">
895
+
896
+ <p className="text-sm text-gray-500 mt-4">
854
897
  {paymentMethod === 'Card' &&
855
898
  'You will be redirected to a secure payment page.'}
856
899
  {paymentMethod === 'Cash' &&
@@ -858,39 +901,48 @@ export function CheckoutScreen() {
858
901
  {paymentMethod === 'Credit' &&
859
902
  'Use your available account credit for this order.'}
860
903
  </p>
861
- </section>
904
+ </div>
905
+ </div>
906
+ </motion.div>
907
+
908
+ <motion.aside
909
+ initial={{ opacity: 0, y: 32 }}
910
+ animate={{ opacity: 1, y: 0 }}
911
+ transition={{ duration: 0.5, ease: 'easeOut', delay: 0.1 }}
912
+ className="space-y-10 lg:sticky lg:top-24 lg:col-span-1"
913
+ >
914
+ {/* Order Summary */}
915
+ <div className="bg-gradient-to-br from-[#5B9BD5]/10 to-[#2B4B7C]/10 rounded-[24px] p-8 border-2 border-[#5B9BD5]/20 sticky top-24">
916
+ <h2 className="font-['Poppins',sans-serif] font-semibold text-[#2B4B7C] mb-6 text-2xl">Order Summary</h2>
917
+
862
918
 
863
919
  {/* Cart Summary */}
864
920
  <section className="mt-8 pt-6 border-t border-slate-100">
865
- <h3 className="text-xs font-semibold text-slate-700 uppercase tracking-wider">
866
- Cart Summary
867
- </h3>
868
- <div className="max-h-60 space-y-4 overflow-y-auto pr-2 scrollbar-thin scrollbar-thumb-slate-200 hover:scrollbar-thumb-slate-300">
921
+ <div className="space-y-4 mb-6">
869
922
  {cart?.cartBody?.items?.map((item: any) => (
870
- <div
871
- key={item.productId}
872
- className="flex gap-4 rounded-2xl border border-slate-100 p-4 hover:bg-slate-50/50 transition"
873
- >
874
- <div className="flex h-16 w-16 items-center justify-center rounded-xl bg-slate-100 text-slate-400">
875
- <Image src={item.productVariantData.productMedia?.[0]?.file || '/placeholder-product.jpg'}
876
- alt={item.productVariantData.name} width={64} height={64} className="object-contain" />
923
+ <div key={`${item.productId}-${item.color}-${item.size}`} className="flex gap-3">
924
+ <div className="w-16 h-16 rounded-xl overflow-hidden bg-white shrink-0">
925
+ <Image src={item.productVariantData.productMedia?.[0]?.file || '/placeholder-product.jpg'} alt={item.productVariantData.name} className="w-full h-full object-cover" height={200} width={200} />
877
926
  </div>
878
-
879
- <div className="flex-1">
880
- <p className="text-sm font-semibold text-slate-900">
927
+ <div className="flex-1 min-w-0">
928
+ <p className="font-['Poppins',sans-serif] font-medium text-[12px] text-[#2B4B7C] mb-1">
881
929
  {item?.productVariantData?.name}
882
930
  </p>
883
- <p className="text-xs text-slate-500">Qty {item.quantity}</p>
931
+ <p className="font-['Poppins',sans-serif] text-[11px] text-[#676c80]">
932
+ {item?.productVariantData?.brand} • Qty: {item.quantity}
933
+ </p>
934
+ <p className="font-['Poppins',sans-serif] font-semibold text-[12px] text-[#E67E50] mt-1">
935
+ {formatPrice(item.productVariantData.finalPrice * item.quantity)}
936
+ </p>
884
937
  </div>
885
- <p className="text-sm font-semibold text-slate-900">
886
- {formatPrice(item.productVariantData.finalPrice * item.quantity)}
887
- </p>
888
938
  </div>
889
939
  ))}
890
940
  </div>
891
941
 
942
+ <div className="h-px bg-[#5B9BD5]/20 my-4" />
943
+
892
944
  {/* Totals */}
893
- <div className="mt-6 space-y-3 text-sm text-slate-600">
945
+ <div className="text-sm text-slate-600 space-y-3 py-4">
894
946
  <div className="flex items-center justify-between">
895
947
  <span>Subtotal</span>
896
948
  <span className="font-semibold text-slate-900">
@@ -909,24 +961,29 @@ export function CheckoutScreen() {
909
961
  <span>Estimated tax</span>
910
962
  <span className="font-semibold">{formatPrice(tax)}</span>
911
963
  </div>
912
- <div className="rounded-2xl bg-slate-50 p-4">
913
- <div className="flex items-center justify-between text-base font-semibold text-slate-900">
914
- <span>Total Due</span>
915
- <span>{formatPrice(total)}</span>
916
- </div>
917
- <p className="mt-1 text-xs text-slate-500">
918
- Tax is estimated. Final amount confirmed after payment.
919
- </p>
964
+ <div className="h-px bg-[#5B9BD5]/20 mt-6" />
965
+
966
+ <div className="flex items-center justify-between mb-6">
967
+ <span className="font-['Poppins',sans-serif] font-semibold text-[16px] text-[#2B4B7C]">Total</span>
968
+ <span className="font-['Poppins',sans-serif] font-bold text-[24px] text-[#E67E50]">{formatPrice(total)}</span>
920
969
  </div>
970
+ {/* <p className="mt-1 text-xs text-slate-500">
971
+ Tax is estimated. Final amount confirmed after payment.
972
+ </p> */}
973
+ </div>
974
+
975
+ <div className="bg-white/80 rounded-xl p-4">
976
+ <p className="font-['Poppins',sans-serif] text-[11px] text-[#676c80] leading-[1.6]">
977
+ <strong className="text-[#2B4B7C]">Payment:</strong> We'll contact you to arrange payment upon pickup or delivery. We accept cash, credit cards, and all major payment methods.
978
+ </p>
921
979
  </div>
922
980
  </section>
923
981
 
924
982
  {/* Checkout Button */}
925
983
  <Button
926
984
  type="submit"
927
- size="lg"
928
985
  isLoading={isSubmitting}
929
- className="mt-6 w-full transition-transform hover:scale-[1.02] active:scale-[0.99]"
986
+ className="font-['Poppins',sans-serif] font-medium text-[14px] px-6 py-2 rounded-full bg-[#E67E50] text-white hover:bg-[#d66f45] hover:shadow-lg transition-all duration-300 mt-4 w-full"
930
987
  >
931
988
  <CreditCard className="h-5 w-5" />
932
989
  {isSubmitting ? 'Placing order...' : 'Place Secure Order'}
@@ -938,26 +995,6 @@ export function CheckoutScreen() {
938
995
  </p>
939
996
  </div>
940
997
 
941
- {/* Why Patients Choose Us */}
942
- <div className="rounded-3xl border border-primary-100 bg-primary-50/80 p-6 text-sm text-primary-700 shadow-sm hover:shadow-md transition-shadow">
943
- <p className="font-semibold uppercase tracking-[0.3em] text-primary-800">
944
- Why Patients Choose Us
945
- </p>
946
- <ul className="mt-4 space-y-3 text-primary-700 leading-relaxed">
947
- <li className="flex items-start gap-3">
948
- <PackageCheck className="mt-0.5 h-4 w-4 shrink-0" />
949
- <span>Pharmacy-grade verification on every order.</span>
950
- </li>
951
- <li className="flex items-start gap-3">
952
- <ShieldCheck className="mt-0.5 h-4 w-4 shrink-0" />
953
- <span>Cold-chain logistics for sensitive medications.</span>
954
- </li>
955
- <li className="flex items-start gap-3">
956
- <Truck className="mt-0.5 h-4 w-4 shrink-0" />
957
- <span>Real-time tracking and SMS updates from prep to delivery.</span>
958
- </li>
959
- </ul>
960
- </div>
961
998
  </motion.aside>
962
999
 
963
1000
  </div>