hey-pharmacist-ecommerce 1.0.5 → 1.0.7

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 (74) hide show
  1. package/README.md +157 -17
  2. package/dist/index.d.mts +3636 -316
  3. package/dist/index.d.ts +3636 -316
  4. package/dist/index.js +6802 -3866
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +6756 -3818
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +18 -15
  9. package/src/components/AddressFormModal.tsx +171 -0
  10. package/src/components/CartItem.tsx +17 -12
  11. package/src/components/FilterChips.tsx +195 -0
  12. package/src/components/Header.tsx +121 -71
  13. package/src/components/OrderCard.tsx +18 -25
  14. package/src/components/ProductCard.tsx +209 -72
  15. package/src/components/ui/Button.tsx +13 -5
  16. package/src/components/ui/Card.tsx +46 -0
  17. package/src/hooks/useAddresses.ts +83 -0
  18. package/src/hooks/useOrders.ts +37 -19
  19. package/src/hooks/useProducts.ts +55 -63
  20. package/src/hooks/useWishlistProducts.ts +75 -0
  21. package/src/index.ts +3 -19
  22. package/src/lib/Apis/api.ts +1 -0
  23. package/src/lib/Apis/apis/cart-api.ts +3 -3
  24. package/src/lib/Apis/apis/inventory-api.ts +0 -108
  25. package/src/lib/Apis/apis/stores-api.ts +70 -0
  26. package/src/lib/Apis/apis/wishlist-api.ts +447 -0
  27. package/src/lib/Apis/models/cart-item-populated.ts +0 -1
  28. package/src/lib/Apis/models/create-single-variant-product-dto.ts +3 -10
  29. package/src/lib/Apis/models/create-variant-dto.ts +26 -33
  30. package/src/lib/Apis/models/extended-product-dto.ts +20 -24
  31. package/src/lib/Apis/models/index.ts +2 -1
  32. package/src/lib/Apis/models/order-time-line-dto.ts +49 -0
  33. package/src/lib/Apis/models/order.ts +3 -8
  34. package/src/lib/Apis/models/populated-order.ts +3 -8
  35. package/src/lib/Apis/models/product-variant.ts +29 -0
  36. package/src/lib/Apis/models/update-product-variant-dto.ts +16 -23
  37. package/src/lib/Apis/models/wishlist.ts +51 -0
  38. package/src/lib/Apis/wrapper.ts +18 -7
  39. package/src/lib/api-adapter/index.ts +0 -12
  40. package/src/lib/types/index.ts +16 -61
  41. package/src/lib/utils/colors.ts +7 -4
  42. package/src/lib/utils/format.ts +1 -1
  43. package/src/lib/validations/address.ts +14 -0
  44. package/src/providers/AuthProvider.tsx +61 -31
  45. package/src/providers/CartProvider.tsx +18 -28
  46. package/src/providers/EcommerceProvider.tsx +7 -0
  47. package/src/providers/FavoritesProvider.tsx +86 -0
  48. package/src/providers/ThemeProvider.tsx +16 -1
  49. package/src/providers/WishlistProvider.tsx +174 -0
  50. package/src/screens/AddressesScreen.tsx +484 -0
  51. package/src/screens/CartScreen.tsx +120 -84
  52. package/src/screens/CategoriesScreen.tsx +120 -0
  53. package/src/screens/CheckoutScreen.tsx +919 -241
  54. package/src/screens/CurrentOrdersScreen.tsx +125 -61
  55. package/src/screens/HomeScreen.tsx +209 -0
  56. package/src/screens/LoginScreen.tsx +133 -88
  57. package/src/screens/NewAddressScreen.tsx +187 -0
  58. package/src/screens/OrdersScreen.tsx +162 -50
  59. package/src/screens/ProductDetailScreen.tsx +641 -190
  60. package/src/screens/ProfileScreen.tsx +192 -116
  61. package/src/screens/RegisterScreen.tsx +193 -144
  62. package/src/screens/SearchResultsScreen.tsx +165 -0
  63. package/src/screens/ShopScreen.tsx +1110 -146
  64. package/src/screens/WishlistScreen.tsx +428 -0
  65. package/src/lib/Apis/models/inventory-paginated-response.ts +0 -75
  66. package/src/lib/api/auth.ts +0 -81
  67. package/src/lib/api/cart.ts +0 -42
  68. package/src/lib/api/orders.ts +0 -53
  69. package/src/lib/api/products.ts +0 -51
  70. package/src/lib/api-adapter/auth-adapter.ts +0 -196
  71. package/src/lib/api-adapter/cart-adapter.ts +0 -193
  72. package/src/lib/api-adapter/mappers.ts +0 -152
  73. package/src/lib/api-adapter/orders-adapter.ts +0 -195
  74. package/src/lib/api-adapter/products-adapter.ts +0 -194
@@ -2,37 +2,35 @@
2
2
 
3
3
  import React from 'react';
4
4
  import { motion } from 'framer-motion';
5
- import { ShoppingBag, ArrowRight } from 'lucide-react';
5
+ import {
6
+ ArrowRight,
7
+ BadgePercent,
8
+ HeartPulse,
9
+ ShieldCheck,
10
+ ShoppingBag,
11
+ } from 'lucide-react';
6
12
  import { CartItem } from '@/components/CartItem';
7
13
  import { EmptyState } from '@/components/EmptyState';
8
14
  import { Button } from '@/components/ui/Button';
9
15
  import { useCart } from '@/providers/CartProvider';
10
16
  import { formatPrice } from '@/lib/utils/format';
11
17
  import { useRouter } from 'next/navigation';
18
+ import { CartItemPopulated } from '@/lib/Apis';
19
+
12
20
 
13
21
  export function CartScreen() {
14
22
  const router = useRouter();
15
23
  const { cart, isLoading } = useCart();
16
24
 
17
- if (isLoading) {
18
- return (
19
- <div className="min-h-screen bg-gray-50 py-12">
20
- <div className="container mx-auto px-4">
21
- <p>Loading cart...</p>
22
- </div>
23
- </div>
24
- );
25
- }
26
-
27
- if (!cart || cart.items.length === 0) {
25
+ if (!cart || cart.cartBody.items.length === 0) {
28
26
  return (
29
- <div className="min-h-screen bg-gray-50 py-12">
30
- <div className="container mx-auto px-4">
27
+ <div className="min-h-screen bg-gradient-to-br from-primary-700 via-primary-600 to-secondary-600 flex items-center justify-center">
28
+ <div className="mx-auto px-20 py-5 bg-white rounded-3xl">
31
29
  <EmptyState
32
30
  icon={ShoppingBag}
33
- title="Your cart is empty"
34
- description="Add some products to your cart and they will appear here"
35
- actionLabel="Continue Shopping"
31
+ title="Your bag feels a little empty"
32
+ description="Add pharmacy favorites to unlock quick shipping, curated bundles, and personalized recommendations."
33
+ actionLabel="Discover products"
36
34
  onAction={() => router.push('/shop')}
37
35
  />
38
36
  </div>
@@ -41,100 +39,138 @@ export function CartScreen() {
41
39
  }
42
40
 
43
41
  const subtotal = cart.total;
44
- const shipping = subtotal > 50 ? 0 : 10;
45
- const tax = subtotal * 0.1; // 10% tax
42
+ const shipping = 0;
43
+ const tax = 0;
46
44
  const total = subtotal + shipping + tax;
45
+
47
46
 
48
47
  return (
49
- <div className="min-h-screen bg-gray-50 py-12">
50
- <div className="container mx-auto px-4">
51
- {/* Header */}
52
- <motion.div
53
- initial={{ opacity: 0, y: 20 }}
54
- animate={{ opacity: 1, y: 0 }}
55
- className="mb-8"
56
- >
57
- <h1 className="text-4xl font-bold text-gray-900 mb-2">Shopping Cart</h1>
58
- <p className="text-gray-600">{cart.itemCount} items in your cart</p>
59
- </motion.div>
48
+ <div className="min-h-screen bg-slate-50">
49
+ <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">
50
+ <div className="absolute inset-0 bg-[radial-gradient(circle_at_top_left,_rgba(255,255,255,0.35),_transparent_60%)]" />
51
+ <div className="relative container mx-auto px-4 py-16 mb-8">
52
+ <motion.div
53
+ initial={{ opacity: 0, y: 24 }}
54
+ animate={{ opacity: 1, y: 0 }}
55
+ className="flex flex-col gap-6 md:flex-row md:items-center md:justify-between"
56
+ >
57
+ <div className="max-w-2xl space-y-4">
58
+ <span className="inline-flex items-center gap-2 rounded-full bg-white/15 px-3 py-1 text-sm font-semibold tracking-wide text-white/80 backdrop-blur">
59
+ <HeartPulse className="h-4 w-4" />
60
+ Wellness essentials, ready when you are
61
+ </span>
62
+ <h1 className="text-4xl font-bold md:text-5xl">Your curated cart</h1>
63
+ <p className="text-white/75 md:text-lg">
64
+ Review your selections, unlock exclusive perks, and check out with pharmacist-backed
65
+ confidence.
66
+ </p>
67
+ </div>
68
+ <div className="rounded-3xl bg-white/15 p-6 backdrop-blur-md">
69
+ <p className="text-sm font-semibold uppercase tracking-[0.35em] text-white/70">
70
+ Cart summary
71
+ </p>
72
+ <p className="mt-4 text-4xl font-semibold">{formatPrice(total)}</p>
73
+ <p className="text-sm text-white/70">Taxes and shipping calculated below</p>
74
+ </div>
75
+ </motion.div>
76
+ </div>
77
+ </section>
60
78
 
61
- <div className="grid lg:grid-cols-3 gap-8">
62
- {/* Cart Items */}
63
- <div className="lg:col-span-2">
64
- <div className="space-y-4">
65
- {cart.items.map((item) => (
66
- <CartItem key={item.productId} item={item} />
79
+ <div className="relative -mt-16 pb-20">
80
+ <div className="container mx-auto px-4">
81
+ <div className="grid gap-10 lg:grid-cols-[minmax(0,2fr)_minmax(0,1fr)]">
82
+ <motion.section
83
+ initial={{ opacity: 0, y: 24 }}
84
+ animate={{ opacity: 1, y: 0 }}
85
+ transition={{ delay: 0.05 }}
86
+ className="space-y-6 rounded-3xl border border-slate-100 bg-white p-6 shadow-lg shadow-primary-50"
87
+ >
88
+ <div className="flex flex-wrap items-center justify-between gap-4">
89
+ <h2 className="text-xl font-semibold text-slate-900">
90
+ Items ({cart.cartBody.items.length})
91
+ </h2>
92
+ <div className="inline-flex items-center gap-2 rounded-full bg-primary-50 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-primary-700">
93
+ <ShieldCheck className="h-4 w-4" />
94
+ Guaranteed cold-chain handling
95
+ </div>
96
+ </div>
97
+ {isLoading && (
98
+ <div className="flex items-center gap-2 rounded-xl border border-slate-200 bg-slate-50 px-3 py-2 text-sm text-slate-600">
99
+ <span className="inline-block h-3 w-3 animate-spin rounded-full border-2 border-slate-300 border-t-slate-600" />
100
+ Updating cart…
101
+ </div>
102
+ )}
103
+ <div className="space-y-5">
104
+ {cart.cartBody.items.map((item: CartItemPopulated) => (
105
+ <CartItem key={item.productVariantId} item={item} />
67
106
  ))}
68
107
  </div>
69
- </div>
70
-
71
- {/* Order Summary */}
72
- <div className="lg:col-span-1">
73
- <motion.div
74
- initial={{ opacity: 0, y: 20 }}
75
- animate={{ opacity: 1, y: 0 }}
76
- transition={{ delay: 0.1 }}
77
- className="bg-white rounded-2xl p-6 shadow-sm sticky top-24"
78
- >
79
- <h2 className="text-2xl font-bold text-gray-900 mb-6">Order Summary</h2>
108
+ </motion.section>
80
109
 
81
- <div className="space-y-4 mb-6">
82
- <div className="flex justify-between text-gray-700">
110
+ <motion.aside
111
+ initial={{ opacity: 0, y: 24 }}
112
+ animate={{ opacity: 1, y: 0 }}
113
+ transition={{ delay: 0.1 }}
114
+ className="space-y-6 lg:sticky lg:top-28"
115
+ >
116
+ <div className="rounded-3xl border border-slate-100 bg-white p-6 shadow-lg shadow-primary-50">
117
+ <div className="flex items-center justify-between">
118
+ <h2 className="text-xl font-semibold text-slate-900">Checkout summary</h2>
119
+ <span className="inline-flex items-center gap-1 rounded-full bg-primary-50 px-3 py-1 text-xs font-semibold text-primary-700">
120
+ <BadgePercent className="h-4 w-4" />
121
+ Savings applied
122
+ </span>
123
+ </div>
124
+ <div className="mt-6 space-y-4 text-sm text-slate-600">
125
+ <div className="flex items-center justify-between">
83
126
  <span>Subtotal</span>
84
- <span className="font-medium">{formatPrice(subtotal)}</span>
127
+ <span className="font-semibold text-slate-900">{formatPrice(subtotal)}</span>
85
128
  </div>
86
- <div className="flex justify-between text-gray-700">
129
+ <div className="flex items-center justify-between">
87
130
  <span>Shipping</span>
88
- <span className="font-medium">
89
- {shipping === 0 ? (
90
- <span className="text-green-600">FREE</span>
91
- ) : (
92
- formatPrice(shipping)
93
- )}
131
+ <span className="font-semibold">
132
+ Will be calculated at checkout
94
133
  </span>
95
134
  </div>
96
- <div className="flex justify-between text-gray-700">
97
- <span>Tax (10%)</span>
98
- <span className="font-medium">{formatPrice(tax)}</span>
99
- </div>
100
- <div className="border-t border-gray-200 pt-4">
101
- <div className="flex justify-between">
102
- <span className="text-xl font-bold text-gray-900">Total</span>
103
- <span className="text-2xl font-bold text-gray-900">
104
- {formatPrice(total)}
105
- </span>
135
+ <div className="rounded-2xl bg-slate-50 p-4">
136
+ <div className="flex items-center justify-between text-base font-semibold text-slate-900">
137
+ <span>Order total</span>
138
+ <span>{formatPrice(total)}</span>
106
139
  </div>
107
- </div>
108
- </div>
109
-
110
- {shipping > 0 && (
111
- <div className="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-6">
112
- <p className="text-sm text-blue-800">
113
- Add {formatPrice(50 - subtotal)} more to get FREE shipping!
140
+ <p className="mt-2 text-xs text-slate-500">
141
+ Prices include pharmacy-grade quality control and packaging.
114
142
  </p>
115
143
  </div>
116
- )}
117
-
144
+ </div>
118
145
  <Button
119
146
  size="lg"
147
+ className="mt-6 w-full"
120
148
  onClick={() => router.push('/checkout')}
121
- className="w-full"
122
149
  >
123
- Proceed to Checkout
124
- <ArrowRight className="w-5 h-5" />
150
+ Secure checkout
151
+ <ArrowRight className="h-5 w-5" />
125
152
  </Button>
126
-
127
153
  <button
154
+ type="button"
128
155
  onClick={() => router.push('/shop')}
129
- className="w-full mt-4 text-primary-600 hover:text-primary-700 font-medium transition-colors"
156
+ className="mt-4 w-full text-sm font-semibold text-primary-600 transition hover:text-primary-700"
130
157
  >
131
- Continue Shopping
158
+ Continue shopping
132
159
  </button>
133
- </motion.div>
134
- </div>
160
+ </div>
161
+
162
+ <div className="rounded-3xl border border-primary-100 bg-primary-50/70 p-6 text-sm text-primary-700 shadow-sm">
163
+ <p className="font-semibold uppercase tracking-[0.3em]">Need help?</p>
164
+ <p className="mt-2 leading-relaxed">
165
+ Chat with a pharmacist to optimize your regimen or discuss substitutions before you
166
+ check out.
167
+ </p>
168
+ </div>
169
+ </motion.aside>
135
170
  </div>
136
171
  </div>
137
172
  </div>
173
+ </div>
138
174
  );
139
175
  }
140
176
 
@@ -0,0 +1,120 @@
1
+ 'use client';
2
+
3
+ import React from 'react';
4
+ import { motion } from 'framer-motion';
5
+ import { Package, Sparkles } from 'lucide-react';
6
+ import { EmptyState } from '@/components/EmptyState';
7
+ import { useCategories } from '@/hooks/useProducts';
8
+ import Image from 'next/image';
9
+ import Link from 'next/link';
10
+ import { useRouter } from 'next/navigation';
11
+
12
+ export function CategoriesScreen() {
13
+ const { categories, isLoading } = useCategories();
14
+ const router = useRouter();
15
+
16
+ return (
17
+ <div className="min-h-screen bg-slate-50">
18
+ <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">
19
+ <div className="absolute inset-0 bg-[radial-gradient(circle_at_top_left,_rgba(255,255,255,0.35),_transparent_60%)]" />
20
+ <div className="relative container mx-auto px-4 py-16">
21
+ <motion.div
22
+ initial={{ opacity: 0, y: 24 }}
23
+ animate={{ opacity: 1, y: 0 }}
24
+ className="space-y-6"
25
+ >
26
+ <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">
27
+ <Package className="h-4 w-4" />
28
+ Product Categories
29
+ </span>
30
+ <div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
31
+ <div className="space-y-4">
32
+ <h1 className="text-4xl font-bold md:text-5xl">Browse Our Product Range</h1>
33
+ <p className="max-w-2xl text-white/80 md:text-lg">
34
+ Explore our comprehensive selection of healthcare products, carefully curated by our pharmacists to meet all your wellness needs.
35
+ </p>
36
+ </div>
37
+ <div className="rounded-3xl bg-white/15 p-6 backdrop-blur">
38
+ <p className="text-sm font-semibold uppercase tracking-[0.35em] text-white/70">
39
+ Quick tip
40
+ </p>
41
+ <p className="mt-3 text-sm text-white/80">
42
+ Use the categories below to quickly find the products you&apos;re looking for, or use the search function for specific items.
43
+ </p>
44
+ </div>
45
+ </div>
46
+ </motion.div>
47
+ </div>
48
+ </section>
49
+
50
+ <div className="relative -mt-16 pb-16">
51
+ <div className="container mx-auto px-4">
52
+ <motion.div
53
+ initial={{ opacity: 0, y: 24 }}
54
+ animate={{ opacity: 1, y: 0 }}
55
+ className="rounded-3xl border border-slate-100 bg-white p-6 shadow-lg shadow-primary-50"
56
+ >
57
+ <div className="flex items-center gap-3 text-sm text-slate-500 mb-6">
58
+ <Sparkles className="h-4 w-4 text-primary-500" />
59
+ <span>Browse our complete product catalog organized by categories.</span>
60
+ </div>
61
+
62
+ {isLoading ? (
63
+ <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
64
+ {[...Array(8)].map((_, i) => (
65
+ <div key={i} className="animate-pulse">
66
+ <div className="bg-gray-200 rounded-lg aspect-square mb-2"></div>
67
+ <div className="h-4 bg-gray-200 rounded w-3/4 mb-1"></div>
68
+ <div className="h-3 bg-gray-200 rounded w-1/2"></div>
69
+ </div>
70
+ ))}
71
+ </div>
72
+ ) : categories.length > 0 ? (
73
+ <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
74
+ {categories.map((category) => (
75
+ <Link
76
+ key={category.id}
77
+ href={`/shop?category=${category.name}`}
78
+ className="group block overflow-hidden rounded-xl border border-gray-100 bg-white p-4 text-center transition hover:shadow-lg hover:border-primary-500"
79
+ >
80
+ <div className="relative aspect-square w-full overflow-hidden rounded-lg bg-gray-50 mb-3">
81
+ {category.image ? (
82
+ <Image
83
+ src={category.image}
84
+ alt={category.name || 'Category Image'}
85
+ fill
86
+ className="object-cover transition-transform group-hover:scale-105"
87
+ sizes="(max-width: 768px) 50vw, (max-width: 1200px) 33vw, 25vw"
88
+ />
89
+ ) : (
90
+ <div className="flex h-full w-full items-center justify-center bg-gray-100 text-gray-400">
91
+ <Package className="h-12 w-12" />
92
+ </div>
93
+ )}
94
+ </div>
95
+ <h3 className="text-lg font-semibold text-gray-900 group-hover:text-primary-600 transition-colors">
96
+ {category.name}
97
+ </h3>
98
+ {category.productCount > 0 && (
99
+ <p className="mt-1 text-sm text-gray-500">
100
+ {category.productCount} {category.productCount === 1 ? 'product' : 'products'}
101
+ </p>
102
+ )}
103
+ </Link>
104
+ ))}
105
+ </div>
106
+ ) : (
107
+ <EmptyState
108
+ title="No categories found"
109
+ description="There are currently no product categories available."
110
+ icon={Package}
111
+ actionLabel="Shop products"
112
+ onAction={() => router.push('/shop')}
113
+ />
114
+ )}
115
+ </motion.div>
116
+ </div>
117
+ </div>
118
+ </div>
119
+ );
120
+ }