hey-pharmacist-ecommerce 1.1.17 → 1.1.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +43 -43
- package/dist/index.d.ts +43 -43
- package/dist/index.js +4288 -2606
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4047 -2365
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/components/AccountOrdersTab.tsx +1 -1
- package/src/components/AccountSettingsTab.tsx +6 -6
- package/src/components/FilterChips.tsx +2 -2
- package/src/components/Header.tsx +2 -2
- package/src/components/Notification.tsx +3 -3
- package/src/components/OrderCard.tsx +2 -2
- package/src/components/ProductCard.tsx +32 -12
- package/src/components/QuickViewModal.tsx +13 -9
- package/src/components/ui/Button.tsx +3 -3
- package/src/components/ui/Card.tsx +1 -1
- package/src/components/ui/Input.tsx +1 -1
- package/src/components/ui/Modal.tsx +1 -1
- package/src/components/ui/Skeleton.tsx +3 -3
- package/src/screens/AddressesScreen.tsx +7 -7
- package/src/screens/CartScreen.tsx +8 -8
- package/src/screens/ChangePasswordScreen.tsx +1 -1
- package/src/screens/CheckoutScreen.tsx +10 -10
- package/src/screens/CurrentOrdersScreen.tsx +6 -6
- package/src/screens/EditProfileScreen.tsx +1 -1
- package/src/screens/LoginScreen.tsx +2 -2
- package/src/screens/NewAddressScreen.tsx +3 -3
- package/src/screens/OrdersScreen.tsx +2 -2
- package/src/screens/ProductDetailScreen.tsx +13 -13
- package/src/screens/ProfileScreen.tsx +2 -2
- package/src/screens/RegisterScreen.tsx +1 -1
- package/src/screens/ShopScreen.tsx +19 -19
- package/src/screens/WishlistScreen.tsx +4 -4
- package/src/styles/globals.css +114 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hey-pharmacist-ecommerce",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.19",
|
|
4
4
|
"description": "Production-ready, multi-tenant e‑commerce UI + API adapter for Next.js with auth, carts, checkout, orders, theming, and pharmacist-focused UX.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -31,17 +31,17 @@
|
|
|
31
31
|
"zustand": "^4.4.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
+
"@tailwindcss/postcss": "^4.1.18",
|
|
34
35
|
"@types/node": "^20.0.0",
|
|
35
36
|
"@types/react": "^18.2.0",
|
|
36
37
|
"@types/react-dom": "^18.2.0",
|
|
37
|
-
"autoprefixer": "^10.4.0",
|
|
38
38
|
"eslint": "^8.0.0",
|
|
39
39
|
"eslint-config-next": "^14.0.0",
|
|
40
40
|
"next": "^16.0.10",
|
|
41
41
|
"postcss": "^8.4.0",
|
|
42
42
|
"react": "^18.2.0",
|
|
43
43
|
"react-dom": "^18.2.0",
|
|
44
|
-
"tailwindcss": "^
|
|
44
|
+
"tailwindcss": "^4.1.18",
|
|
45
45
|
"tsup": "^8.0.0",
|
|
46
46
|
"typescript": "^5.3.0"
|
|
47
47
|
},
|
|
@@ -100,7 +100,7 @@ export function AccountOrdersTab() {
|
|
|
100
100
|
|
|
101
101
|
return (
|
|
102
102
|
<div key={item.productVariantId || index} className="flex items-center gap-3">
|
|
103
|
-
<div className="relative w-12 h-12 rounded-lg bg-slate-100
|
|
103
|
+
<div className="relative w-12 h-12 rounded-lg bg-slate-100 shrink-0 overflow-hidden">
|
|
104
104
|
<Image
|
|
105
105
|
src={item?.productVariantData?.productMedia?.[0]?.file || '/placeholder-product.jpg'}
|
|
106
106
|
alt={item?.productVariantData?.name || 'Product image'}
|
|
@@ -31,7 +31,7 @@ export function AccountSettingsTab() {
|
|
|
31
31
|
type="checkbox"
|
|
32
32
|
checked={emailNotifications}
|
|
33
33
|
onChange={(e) => setEmailNotifications(e.target.checked)}
|
|
34
|
-
className="h-4 w-4 rounded border-primary-300 text-secondary focus:ring-primary-500"
|
|
34
|
+
className="h-4 w-4 rounded-sm border-primary-300 text-secondary focus:ring-primary-500"
|
|
35
35
|
/>
|
|
36
36
|
</label>
|
|
37
37
|
<label className="flex items-center justify-between gap-4 rounded-lg border border-slate-200 bg-slate-50 px-4 py-3">
|
|
@@ -43,7 +43,7 @@ export function AccountSettingsTab() {
|
|
|
43
43
|
type="checkbox"
|
|
44
44
|
checked={orderUpdates}
|
|
45
45
|
onChange={(e) => setOrderUpdates(e.target.checked)}
|
|
46
|
-
className="h-4 w-4 rounded border-primary-300 text-secondary focus:ring-primary-500"
|
|
46
|
+
className="h-4 w-4 rounded-sm border-primary-300 text-secondary focus:ring-primary-500"
|
|
47
47
|
/>
|
|
48
48
|
</label>
|
|
49
49
|
<label className="flex items-center justify-between gap-4 rounded-lg border border-slate-200 bg-slate-50 px-4 py-3">
|
|
@@ -55,7 +55,7 @@ export function AccountSettingsTab() {
|
|
|
55
55
|
type="checkbox"
|
|
56
56
|
checked={promotionalEmails}
|
|
57
57
|
onChange={(e) => setPromotionalEmails(e.target.checked)}
|
|
58
|
-
className="h-4 w-4 rounded border-primary-300 text-secondary focus:ring-primary-500"
|
|
58
|
+
className="h-4 w-4 rounded-sm border-primary-300 text-secondary focus:ring-primary-500"
|
|
59
59
|
/>
|
|
60
60
|
</label>
|
|
61
61
|
</div>
|
|
@@ -74,7 +74,7 @@ export function AccountSettingsTab() {
|
|
|
74
74
|
<p className="text-xs text-muted">Update your account password</p>
|
|
75
75
|
</div>
|
|
76
76
|
<Button
|
|
77
|
-
variant="outline"
|
|
77
|
+
variant="outline-solid"
|
|
78
78
|
size="sm"
|
|
79
79
|
onClick={() => router.push(buildPath('/account/change-password'))}
|
|
80
80
|
>
|
|
@@ -86,7 +86,7 @@ export function AccountSettingsTab() {
|
|
|
86
86
|
<p className="text-sm font-medium text-secondary">Two-Factor Authentication</p>
|
|
87
87
|
<p className="text-xs text-muted">Add an extra layer of security</p>
|
|
88
88
|
</div>
|
|
89
|
-
<Button variant="outline" size="sm">
|
|
89
|
+
<Button variant="outline-solid" size="sm">
|
|
90
90
|
Enable
|
|
91
91
|
</Button>
|
|
92
92
|
</div> */}
|
|
@@ -96,7 +96,7 @@ export function AccountSettingsTab() {
|
|
|
96
96
|
<p className="text-xs text-red-600">Permanently delete your account and data</p>
|
|
97
97
|
</div>
|
|
98
98
|
<Button
|
|
99
|
-
variant="outline"
|
|
99
|
+
variant="outline-solid"
|
|
100
100
|
size="sm"
|
|
101
101
|
className="border-red-200 text-red-600 hover:bg-red-50"
|
|
102
102
|
onClick={() => {
|
|
@@ -94,8 +94,8 @@ export function FilterChips({
|
|
|
94
94
|
className={`inline-flex items-center gap-1.5 rounded-lg border px-3 py-1.5 text-sm font-medium transition-all whitespace-nowrap ${
|
|
95
95
|
isSelected
|
|
96
96
|
? isPrimary
|
|
97
|
-
? 'border-primary-500 bg-primary-500 text-white shadow-
|
|
98
|
-
: 'border-secondary-500 bg-secondary-500 text-white shadow-
|
|
97
|
+
? 'border-primary-500 bg-primary-500 text-white shadow-xs'
|
|
98
|
+
: 'border-secondary-500 bg-secondary-500 text-white shadow-xs'
|
|
99
99
|
: 'border-slate-200 bg-white text-slate-700 hover:border-slate-300 hover:bg-slate-50'
|
|
100
100
|
}`}
|
|
101
101
|
>
|
|
@@ -45,7 +45,7 @@ export function Header() {
|
|
|
45
45
|
];
|
|
46
46
|
|
|
47
47
|
return (
|
|
48
|
-
<header className="sticky top-0 z-10 bg-white/80 backdrop-blur-xl border-b border-gray-200 shadow-
|
|
48
|
+
<header className="sticky top-0 z-10 bg-white/80 backdrop-blur-xl border-b border-gray-200 shadow-xs">
|
|
49
49
|
<div className="container mx-auto px-4">
|
|
50
50
|
<div className="flex items-center justify-between h-20">
|
|
51
51
|
{/* Logo */}
|
|
@@ -106,7 +106,7 @@ export function Header() {
|
|
|
106
106
|
}
|
|
107
107
|
}}
|
|
108
108
|
placeholder="Search products..."
|
|
109
|
-
className="w-full outline-
|
|
109
|
+
className="w-full outline-hidden text-gray-700"
|
|
110
110
|
autoFocus
|
|
111
111
|
/>
|
|
112
112
|
{searchQuery && (
|
|
@@ -81,7 +81,7 @@ export function Notification({ notification, onDismiss }: NotificationProps) {
|
|
|
81
81
|
className={`relative bg-white rounded-2xl border-2 ${config.borderColor} shadow-xl overflow-hidden min-w-[320px] max-w-[420px]`}
|
|
82
82
|
>
|
|
83
83
|
{/* Gradient accent bar */}
|
|
84
|
-
<div className={`h-1 bg-
|
|
84
|
+
<div className={`h-1 bg-linear-to-r ${config.gradient}`} />
|
|
85
85
|
|
|
86
86
|
<div className="p-4 flex items-start gap-3">
|
|
87
87
|
{/* Icon */}
|
|
@@ -119,7 +119,7 @@ export function Notification({ notification, onDismiss }: NotificationProps) {
|
|
|
119
119
|
{/* Progress bar */}
|
|
120
120
|
<div className="h-1 bg-gray-100">
|
|
121
121
|
<motion.div
|
|
122
|
-
className={`h-full bg-
|
|
122
|
+
className={`h-full bg-linear-to-r ${config.gradient}`}
|
|
123
123
|
style={{ width: `${progress}%` }}
|
|
124
124
|
transition={{ duration: 0.016, ease: 'linear' }}
|
|
125
125
|
/>
|
|
@@ -135,7 +135,7 @@ interface NotificationContainerProps {
|
|
|
135
135
|
|
|
136
136
|
export function NotificationContainer({ notifications, onDismiss }: NotificationContainerProps) {
|
|
137
137
|
return (
|
|
138
|
-
<div className="fixed top-4 right-4 z-
|
|
138
|
+
<div className="fixed top-4 right-4 z-9999 flex flex-col gap-3 pointer-events-none">
|
|
139
139
|
<AnimatePresence mode="popLayout">
|
|
140
140
|
{notifications.map((notification) => (
|
|
141
141
|
<div key={notification.id} className="pointer-events-auto">
|
|
@@ -23,7 +23,7 @@ export function OrderCard({ order }: OrderCardProps) {
|
|
|
23
23
|
<motion.div
|
|
24
24
|
initial={{ opacity: 0, y: 20 }}
|
|
25
25
|
animate={{ opacity: 1, y: 0 }}
|
|
26
|
-
className="rounded-lg border border-slate-200 bg-white p-4 shadow-
|
|
26
|
+
className="rounded-lg border border-slate-200 bg-white p-4 shadow-xs hover:shadow-md transition-shadow"
|
|
27
27
|
>
|
|
28
28
|
{/* Header - Compact */}
|
|
29
29
|
<div className="flex items-center justify-between mb-4 pb-4 border-b border-gray-200">
|
|
@@ -53,7 +53,7 @@ export function OrderCard({ order }: OrderCardProps) {
|
|
|
53
53
|
|
|
54
54
|
return (
|
|
55
55
|
<div key={item.productVariantId || item._id} className="flex items-center gap-2 text-sm">
|
|
56
|
-
<div className="relative w-12 h-12 rounded bg-gray-100
|
|
56
|
+
<div className="relative w-12 h-12 rounded-sm bg-gray-100 shrink-0 overflow-hidden">
|
|
57
57
|
<Image
|
|
58
58
|
src={item?.productVariantData?.productMedia?.[0]?.file || '/placeholder-product.jpg'}
|
|
59
59
|
alt={item?.productVariantData?.name || 'Product image'}
|
|
@@ -141,6 +141,26 @@ export function ProductCard({
|
|
|
141
141
|
return selectedVariant?.name || product.name;
|
|
142
142
|
}, [selectedVariant, product.name]);
|
|
143
143
|
|
|
144
|
+
const displayFinalPrice = useMemo(() => {
|
|
145
|
+
return selectedVariant?.finalPrice ?? product.finalPrice;
|
|
146
|
+
}, [selectedVariant, product.finalPrice]);
|
|
147
|
+
|
|
148
|
+
const displayPriceBeforeDiscount = useMemo(() => {
|
|
149
|
+
return selectedVariant?.retailPrice ?? product.priceBeforeDiscount;
|
|
150
|
+
}, [selectedVariant, product.priceBeforeDiscount]);
|
|
151
|
+
|
|
152
|
+
const displayIsDiscounted = useMemo(() => {
|
|
153
|
+
return selectedVariant ? selectedVariant.isDiscounted : product.isDiscounted;
|
|
154
|
+
}, [selectedVariant, product.isDiscounted]);
|
|
155
|
+
|
|
156
|
+
const displayDiscountAmount = useMemo(() => {
|
|
157
|
+
return selectedVariant ? selectedVariant.discountAmount : product.discountAmount;
|
|
158
|
+
}, [selectedVariant, product.discountAmount]);
|
|
159
|
+
|
|
160
|
+
const displayInventoryCount = useMemo(() => {
|
|
161
|
+
return selectedVariant ? selectedVariant.inventoryCount : product.inventoryCount;
|
|
162
|
+
}, [selectedVariant, product.inventoryCount]);
|
|
163
|
+
|
|
144
164
|
const imageSource = useMemo(() => {
|
|
145
165
|
const src = selectedVariantImage || product.productMedia?.[0]?.file || '/placeholder-product.jpg';
|
|
146
166
|
return {
|
|
@@ -171,7 +191,7 @@ export function ProductCard({
|
|
|
171
191
|
>
|
|
172
192
|
<div
|
|
173
193
|
onClick={handleCardClick}
|
|
174
|
-
className="relative aspect-square overflow-hidden bg-gray-50 cursor-pointer
|
|
194
|
+
className="relative aspect-square overflow-hidden bg-gray-50 cursor-pointer shrink-0"
|
|
175
195
|
>
|
|
176
196
|
<Image
|
|
177
197
|
src={imageSource.src}
|
|
@@ -189,13 +209,13 @@ export function ProductCard({
|
|
|
189
209
|
e.stopPropagation();
|
|
190
210
|
setShowQuickView(true);
|
|
191
211
|
}}
|
|
192
|
-
className="absolute top-2 left-2 p-2 rounded-full bg-white/90 backdrop-blur-
|
|
212
|
+
className="absolute top-2 left-2 p-2 rounded-full bg-white/90 backdrop-blur-xs hover:bg-white shadow-md opacity-0 group-hover:opacity-100 transition-all"
|
|
193
213
|
>
|
|
194
214
|
<Eye className="size-4 text-[#2B4B7C]" />
|
|
195
215
|
</button>
|
|
196
216
|
|
|
197
|
-
{
|
|
198
|
-
<div className="absolute inset-0 bg-black/50 backdrop-blur-
|
|
217
|
+
{displayInventoryCount === 0 && (
|
|
218
|
+
<div className="absolute inset-0 bg-black/50 backdrop-blur-xs flex items-center justify-center">
|
|
199
219
|
<div className="bg-white rounded-full px-4 py-2">
|
|
200
220
|
<span className="font-['Poppins',sans-serif] font-bold text-[11px] text-[#2B4B7C] uppercase">
|
|
201
221
|
Out of Stock
|
|
@@ -210,13 +230,13 @@ export function ProductCard({
|
|
|
210
230
|
<div className="p-0 flex-1 flex flex-col">
|
|
211
231
|
{/* ALL Status Badges */}
|
|
212
232
|
<div className="flex items-center gap-1 mb-2 flex-wrap">
|
|
213
|
-
{
|
|
233
|
+
{displayIsDiscounted && (
|
|
214
234
|
<span className="bg-[#E67E50] text-white rounded-full px-2 py-0.5 flex items-center gap-1">
|
|
215
|
-
<span className="font-['Poppins',sans-serif] font-bold text-[8px] uppercase">-{
|
|
235
|
+
<span className="font-['Poppins',sans-serif] font-bold text-[8px] uppercase">-{displayDiscountAmount}%</span>
|
|
216
236
|
</span>
|
|
217
237
|
)}
|
|
218
238
|
{/* {product.dealOfWeek && (
|
|
219
|
-
<span className="bg-
|
|
239
|
+
<span className="bg-linear-to-r from-[#E67E50] to-[#d66f45] text-white rounded-full px-2 py-0.5 flex items-center gap-1">
|
|
220
240
|
<TrendingUp className="size-2.5" />
|
|
221
241
|
<span className="font-['Poppins',sans-serif] font-semibold text-[8px] uppercase">Deal</span>
|
|
222
242
|
</span>
|
|
@@ -264,11 +284,11 @@ export function ProductCard({
|
|
|
264
284
|
{/* Price */}
|
|
265
285
|
<div className="flex items-center gap-1.5 mb-3">
|
|
266
286
|
<span className="font-['Poppins',sans-serif] font-bold text-md text-primary-600">
|
|
267
|
-
${
|
|
287
|
+
${displayFinalPrice.toFixed(2)}
|
|
268
288
|
</span>
|
|
269
|
-
{
|
|
289
|
+
{displayIsDiscounted && (
|
|
270
290
|
<span className="font-['Poppins',sans-serif] text-sm text-[#676c80] line-through">
|
|
271
|
-
${formatPrice(
|
|
291
|
+
${formatPrice(displayPriceBeforeDiscount)}
|
|
272
292
|
</span>
|
|
273
293
|
)}
|
|
274
294
|
</div>
|
|
@@ -331,11 +351,11 @@ export function ProductCard({
|
|
|
331
351
|
console.error('Failed to add to cart', error);
|
|
332
352
|
}
|
|
333
353
|
}}
|
|
334
|
-
disabled={isAddingToCart || (variantImages.length > 0 && !selectedVariantId)}
|
|
354
|
+
disabled={isAddingToCart || (variantImages.length > 0 && !selectedVariantId) || displayInventoryCount === 0}
|
|
335
355
|
className="w-full font-['Poppins',sans-serif] font-medium text-[11px] px-3 py-2 rounded-full bg-[#5B9BD5] text-white hover:bg-[#4a8ac4] hover:shadow-lg transition-all duration-300 flex items-center justify-center gap-1.5 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
336
356
|
>
|
|
337
357
|
<ShoppingCart className="h-4 w-4" />
|
|
338
|
-
{
|
|
358
|
+
{displayInventoryCount === 0 ? 'Out of Stock' : 'Add to Cart'}
|
|
339
359
|
</button>
|
|
340
360
|
</div>
|
|
341
361
|
|
|
@@ -29,7 +29,11 @@ export function QuickViewModal({ product, onClose, onNavigateToProduct }: QuickV
|
|
|
29
29
|
};
|
|
30
30
|
const selectedVariant = product.productVariants[selectedVariantIndex];
|
|
31
31
|
const selectedSize = selectedVariant.productMedia[selectedSizeIndex];
|
|
32
|
-
const displayPrice =
|
|
32
|
+
const displayPrice = selectedVariant.finalPrice;
|
|
33
|
+
const displayOriginalPrice = selectedVariant.retailPrice;
|
|
34
|
+
const isDiscounted = selectedVariant.isDiscounted;
|
|
35
|
+
const discountAmount = selectedVariant.discountAmount;
|
|
36
|
+
const displayName = selectedVariant.name || product.name;
|
|
33
37
|
|
|
34
38
|
const handleAddToCart = async () => {
|
|
35
39
|
if (!product || !selectedVariant) return;
|
|
@@ -59,7 +63,7 @@ export function QuickViewModal({ product, onClose, onNavigateToProduct }: QuickV
|
|
|
59
63
|
|
|
60
64
|
return (
|
|
61
65
|
<div
|
|
62
|
-
className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-
|
|
66
|
+
className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-xs"
|
|
63
67
|
onClick={onClose}
|
|
64
68
|
>
|
|
65
69
|
<div
|
|
@@ -74,7 +78,7 @@ export function QuickViewModal({ product, onClose, onNavigateToProduct }: QuickV
|
|
|
74
78
|
{product.brand} • {product.parentCategories[0].name}
|
|
75
79
|
</p>
|
|
76
80
|
<h2 className="font-['Poppins',sans-serif] font-semibold text-secondary tracking-[-1px]">
|
|
77
|
-
{
|
|
81
|
+
{displayName}
|
|
78
82
|
</h2>
|
|
79
83
|
|
|
80
84
|
{/* Rating */}
|
|
@@ -107,7 +111,7 @@ export function QuickViewModal({ product, onClose, onNavigateToProduct }: QuickV
|
|
|
107
111
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
|
108
112
|
{/* Product Image */}
|
|
109
113
|
<div className="space-y-4">
|
|
110
|
-
<div className="relative aspect-
|
|
114
|
+
<div className="relative aspect-3/4 rounded-[24px] overflow-hidden bg-gray-50">
|
|
111
115
|
<img
|
|
112
116
|
src={selectedVariant.productMedia[selectedImageIndex]?.file || selectedVariant.productMedia[0]?.file}
|
|
113
117
|
alt={product.name}
|
|
@@ -115,10 +119,10 @@ export function QuickViewModal({ product, onClose, onNavigateToProduct }: QuickV
|
|
|
115
119
|
/>
|
|
116
120
|
{/* Badges */}
|
|
117
121
|
<div className="absolute top-4 left-4 flex flex-col gap-2">
|
|
118
|
-
{
|
|
122
|
+
{isDiscounted && (
|
|
119
123
|
<div className="bg-accent text-white rounded-full px-3 py-1.5">
|
|
120
124
|
<span className="font-['Poppins',sans-serif] font-bold text-[11px] uppercase">
|
|
121
|
-
-{
|
|
125
|
+
-{discountAmount}%
|
|
122
126
|
</span>
|
|
123
127
|
</div>
|
|
124
128
|
)}
|
|
@@ -159,9 +163,9 @@ export function QuickViewModal({ product, onClose, onNavigateToProduct }: QuickV
|
|
|
159
163
|
<span className="font-['Poppins',sans-serif] font-bold text-[32px] text-accent">
|
|
160
164
|
${displayPrice.toFixed(2)}
|
|
161
165
|
</span>
|
|
162
|
-
{
|
|
166
|
+
{isDiscounted && (
|
|
163
167
|
<span className="font-['Poppins',sans-serif] text-[20px] text-muted line-through">
|
|
164
|
-
${
|
|
168
|
+
${displayOriginalPrice.toFixed(2)}
|
|
165
169
|
</span>
|
|
166
170
|
)}
|
|
167
171
|
</div>
|
|
@@ -173,7 +177,7 @@ export function QuickViewModal({ product, onClose, onNavigateToProduct }: QuickV
|
|
|
173
177
|
Out of Stock
|
|
174
178
|
</span>
|
|
175
179
|
) : selectedVariant.inventoryCount <= 10 ? (
|
|
176
|
-
<span className="font-['Poppins',sans-serif] text-[12px] text-
|
|
180
|
+
<span className="font-['Poppins',sans-serif] text-[12px] text-primary font-medium flex items-center gap-1">
|
|
177
181
|
<Package className="size-3" />
|
|
178
182
|
Only {selectedVariant.inventoryCount} left in stock
|
|
179
183
|
</span>
|
|
@@ -8,7 +8,7 @@ const MotionDiv = dynamic(() => import('framer-motion').then((mod) => mod.motion
|
|
|
8
8
|
});
|
|
9
9
|
|
|
10
10
|
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
11
|
-
variant?: 'primary' | 'secondary'
|
|
11
|
+
variant?: 'primary' | 'secondary'| 'ghost' | 'outline-solid';
|
|
12
12
|
size?: 'sm' | 'md' | 'lg';
|
|
13
13
|
isLoading?: boolean;
|
|
14
14
|
children: React.ReactNode;
|
|
@@ -23,12 +23,12 @@ export function Button({
|
|
|
23
23
|
children,
|
|
24
24
|
...props
|
|
25
25
|
}: ButtonProps) {
|
|
26
|
-
const baseStyles = 'font-medium rounded-full transition-all duration-200 inline-flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed focus:outline-
|
|
26
|
+
const baseStyles = 'font-medium rounded-full transition-all duration-200 inline-flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed focus:outline-hidden focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-primary-500';
|
|
27
27
|
|
|
28
28
|
const variants = {
|
|
29
29
|
primary: 'bg-primary-600 text-white hover:bg-primary-700 shadow-lg shadow-primary-500/30 hover:shadow-xl hover:shadow-primary-500/40',
|
|
30
30
|
secondary: 'bg-secondary-600 text-white hover:bg-secondary-700 shadow-lg shadow-secondary-500/30 hover:shadow-xl hover:shadow-secondary-500/40',
|
|
31
|
-
outline: 'border-2 border-primary-600 text-primary-600 hover:bg-primary-50',
|
|
31
|
+
'outline-solid': 'border-2 border-primary-600 text-primary-600 hover:bg-primary-50',
|
|
32
32
|
ghost: 'text-gray-700 hover:bg-gray-100',
|
|
33
33
|
};
|
|
34
34
|
|
|
@@ -8,7 +8,7 @@ interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
8
8
|
export function Card({ className = '', ...props }: CardProps) {
|
|
9
9
|
return (
|
|
10
10
|
<div
|
|
11
|
-
className={`rounded-lg border bg-white shadow-
|
|
11
|
+
className={`rounded-lg border bg-white shadow-xs ${className}`}
|
|
12
12
|
{...props}
|
|
13
13
|
/>
|
|
14
14
|
);
|
|
@@ -23,7 +23,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
|
|
|
23
23
|
? 'border-red-500 focus:border-red-600 focus:ring-red-500/20'
|
|
24
24
|
: 'border-gray-200 focus:border-primary-500 focus:ring-primary-500/20'
|
|
25
25
|
}
|
|
26
|
-
focus:outline-
|
|
26
|
+
focus:outline-hidden focus:ring-4
|
|
27
27
|
placeholder:text-gray-400
|
|
28
28
|
disabled:bg-gray-50 disabled:cursor-not-allowed
|
|
29
29
|
${className}
|
|
@@ -43,7 +43,7 @@ export function Modal({ isOpen, onClose, title, children, size = 'md' }: ModalPr
|
|
|
43
43
|
animate={{ opacity: 1 }}
|
|
44
44
|
exit={{ opacity: 0 }}
|
|
45
45
|
onClick={onClose}
|
|
46
|
-
className="fixed inset-0 bg-black/50 backdrop-blur-
|
|
46
|
+
className="fixed inset-0 bg-black/50 backdrop-blur-xs z-50"
|
|
47
47
|
/>
|
|
48
48
|
|
|
49
49
|
{/* Modal */}
|
|
@@ -6,13 +6,13 @@ interface SkeletonProps {
|
|
|
6
6
|
|
|
7
7
|
export function Skeleton({ className = '' }: SkeletonProps) {
|
|
8
8
|
return (
|
|
9
|
-
<div className={`animate-pulse bg-
|
|
9
|
+
<div className={`animate-pulse bg-linear-to-r from-gray-200 via-gray-300 to-gray-200 bg-size-[200%_100%] rounded-sm ${className}`} />
|
|
10
10
|
);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export function ProductCardSkeleton() {
|
|
14
14
|
return (
|
|
15
|
-
<div className="bg-white rounded-2xl overflow-hidden shadow-
|
|
15
|
+
<div className="bg-white rounded-2xl overflow-hidden shadow-xs">
|
|
16
16
|
<Skeleton className="h-64 w-full" />
|
|
17
17
|
<div className="p-4 space-y-3">
|
|
18
18
|
<Skeleton className="h-6 w-3/4" />
|
|
@@ -28,7 +28,7 @@ export function ProductCardSkeleton() {
|
|
|
28
28
|
|
|
29
29
|
export function OrderCardSkeleton() {
|
|
30
30
|
return (
|
|
31
|
-
<div className="bg-white rounded-2xl p-6 shadow-
|
|
31
|
+
<div className="bg-white rounded-2xl p-6 shadow-xs">
|
|
32
32
|
<div className="flex justify-between items-start mb-4">
|
|
33
33
|
<div className="space-y-2 flex-1">
|
|
34
34
|
<Skeleton className="h-6 w-32" />
|
|
@@ -261,12 +261,12 @@ export function AddressesScreen() {
|
|
|
261
261
|
: 'border-red-200 bg-red-50 text-red-700'
|
|
262
262
|
}`}
|
|
263
263
|
>
|
|
264
|
-
<span className="mt-
|
|
264
|
+
<span className="mt-px text-base">{banner.type === 'success' ? '✔' : '!'}</span>
|
|
265
265
|
<span>{banner.message}</span>
|
|
266
266
|
</div>
|
|
267
267
|
)}
|
|
268
268
|
<Button
|
|
269
|
-
variant="outline"
|
|
269
|
+
variant="outline-solid"
|
|
270
270
|
size="md"
|
|
271
271
|
className="border-slate-300 text-slate-800 hover:bg-white"
|
|
272
272
|
onClick={openCreateModal}
|
|
@@ -310,7 +310,7 @@ export function AddressesScreen() {
|
|
|
310
310
|
{error.message}
|
|
311
311
|
</div>
|
|
312
312
|
) : addresses.length === 0 ? (
|
|
313
|
-
<div className="rounded-3xl border border-slate-100 bg-white p-12 shadow-
|
|
313
|
+
<div className="rounded-3xl border border-slate-100 bg-white p-12 shadow-xs">
|
|
314
314
|
<EmptyState
|
|
315
315
|
icon={MapPin}
|
|
316
316
|
title="No addresses yet"
|
|
@@ -328,7 +328,7 @@ export function AddressesScreen() {
|
|
|
328
328
|
key={address.id}
|
|
329
329
|
initial={{ opacity: 0, y: 24 }}
|
|
330
330
|
animate={{ opacity: 1, y: 0 }}
|
|
331
|
-
className="group relative flex h-full flex-col rounded-3xl border border-slate-200 bg-white p-6 shadow-
|
|
331
|
+
className="group relative flex h-full flex-col rounded-3xl border border-slate-200 bg-white p-6 shadow-xs transition hover:-translate-y-1 hover:border-primary-200 hover:shadow-lg"
|
|
332
332
|
>
|
|
333
333
|
{address.isDefault && (
|
|
334
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">
|
|
@@ -463,7 +463,7 @@ export function AddressesScreen() {
|
|
|
463
463
|
<span className="text-sm font-semibold text-slate-700">Address type</span>
|
|
464
464
|
<select
|
|
465
465
|
{...register('addressType')}
|
|
466
|
-
className="rounded-xl border border-slate-200 px-3 py-2 text-sm text-slate-700 focus:border-primary-400 focus:outline-
|
|
466
|
+
className="rounded-xl border border-slate-200 px-3 py-2 text-sm text-slate-700 focus:border-primary-400 focus:outline-hidden focus:ring-2 focus:ring-primary-500/20"
|
|
467
467
|
>
|
|
468
468
|
<option value="Shipping">Shipping</option>
|
|
469
469
|
<option value="Billing">Billing</option>
|
|
@@ -475,13 +475,13 @@ export function AddressesScreen() {
|
|
|
475
475
|
<input
|
|
476
476
|
type="checkbox"
|
|
477
477
|
{...register('isDefault')}
|
|
478
|
-
className="h-4 w-4 rounded border-primary-300 text-primary-600 focus:ring-primary-500"
|
|
478
|
+
className="h-4 w-4 rounded-sm border-primary-300 text-primary-600 focus:ring-primary-500"
|
|
479
479
|
/>
|
|
480
480
|
</label>
|
|
481
481
|
</div>
|
|
482
482
|
|
|
483
483
|
<div className="flex items-center justify-end gap-3">
|
|
484
|
-
<Button type="button" variant="outline" onClick={closeModal}>
|
|
484
|
+
<Button type="button" variant="outline-solid" onClick={closeModal}>
|
|
485
485
|
Cancel
|
|
486
486
|
</Button>
|
|
487
487
|
<Button type="submit" isLoading={isSubmitting}>
|
|
@@ -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-secondary
|
|
71
|
+
<CheckCircle2 className="h-5 w-5 text-secondary 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-secondary
|
|
75
|
+
<CheckCircle2 className="h-5 w-5 text-secondary 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-secondary
|
|
79
|
+
<CheckCircle2 className="h-5 w-5 text-secondary shrink-0 mt-0.5" />
|
|
80
80
|
<span>Secure checkout process</span>
|
|
81
81
|
</div>
|
|
82
82
|
</div>
|
|
@@ -150,7 +150,7 @@ export function CartScreen() {
|
|
|
150
150
|
transition={{ delay: 0.1 }}
|
|
151
151
|
className="space-y-6 lg:sticky lg:top-24 h-fit lg:col-span-1"
|
|
152
152
|
>
|
|
153
|
-
<div className="bg-
|
|
153
|
+
<div className="bg-linear-to-br from-[#5B9BD5]/10 to-[#2B4B7C]/10 rounded-[24px] p-8 border-2 border-[#5B9BD5]/20 sticky top-24">
|
|
154
154
|
<h2 className="font-['Poppins',sans-serif] font-semibold text-secondary mb-6">Order Summary</h2>
|
|
155
155
|
|
|
156
156
|
<div className="space-y-4 mb-6">
|
|
@@ -167,7 +167,7 @@ export function CartScreen() {
|
|
|
167
167
|
<div className="border-t border-gray-200 pt-4 mt-4">
|
|
168
168
|
<div className="flex items-center justify-between">
|
|
169
169
|
<span className="text-lg font-bold text-secondary">Total</span>
|
|
170
|
-
<span className="text-2xl font-bold text-
|
|
170
|
+
<span className="text-2xl font-bold text-primary">{formatPrice(total)}</span>
|
|
171
171
|
</div>
|
|
172
172
|
</div>
|
|
173
173
|
</div>
|
|
@@ -192,17 +192,17 @@ export function CartScreen() {
|
|
|
192
192
|
|
|
193
193
|
<div className="mt-6 space-y-3 pt-6 border-t border-gray-200">
|
|
194
194
|
<div className="flex items-start gap-3 text-sm text-slate-600">
|
|
195
|
-
<CheckCircle2 className="h-5 w-5 text-muted
|
|
195
|
+
<CheckCircle2 className="h-5 w-5 text-muted shrink-0 mt-0.5" />
|
|
196
196
|
<span>Easy returns within 30 days</span>
|
|
197
197
|
</div>
|
|
198
198
|
<div className="flex items-start gap-3 text-sm text-slate-600">
|
|
199
|
-
<CheckCircle2 className="h-5 w-5 text-muted
|
|
199
|
+
<CheckCircle2 className="h-5 w-5 text-muted shrink-0 mt-0.5" />
|
|
200
200
|
<span>Secure checkout process</span>
|
|
201
201
|
</div>
|
|
202
202
|
</div>
|
|
203
203
|
</div>
|
|
204
204
|
|
|
205
|
-
{/* <div className="rounded-3xl border border-primary-100 bg-primary-50/70 p-6 text-sm text-primary-700 shadow-
|
|
205
|
+
{/* <div className="rounded-3xl border border-primary-100 bg-primary-50/70 p-6 text-sm text-primary-700 shadow-xs">
|
|
206
206
|
<p className="font-semibold uppercase tracking-[0.3em]">Need help?</p>
|
|
207
207
|
<p className="mt-2 leading-relaxed">
|
|
208
208
|
Chat with a pharmacist to optimize your regimen or discuss substitutions before you
|
|
@@ -134,7 +134,7 @@ export function ChangePasswordScreen() {
|
|
|
134
134
|
</Button>
|
|
135
135
|
<Button
|
|
136
136
|
type="button"
|
|
137
|
-
variant="outline"
|
|
137
|
+
variant="outline-solid"
|
|
138
138
|
size="lg"
|
|
139
139
|
className="border-slate-300 text-slate-800 hover:bg-slate-50"
|
|
140
140
|
onClick={() => router.push(buildPath('/account'))}
|
|
@@ -487,7 +487,7 @@ export function CheckoutScreen() {
|
|
|
487
487
|
<label className="block font-semibold">Select Address</label>
|
|
488
488
|
<Button
|
|
489
489
|
type="button"
|
|
490
|
-
variant="outline"
|
|
490
|
+
variant="outline-solid"
|
|
491
491
|
size="sm"
|
|
492
492
|
onClick={() => { setEditingAddress(null); setIsAddressModalOpen(true); }}
|
|
493
493
|
>
|
|
@@ -502,7 +502,7 @@ export function CheckoutScreen() {
|
|
|
502
502
|
<label
|
|
503
503
|
key={addr.id}
|
|
504
504
|
className={`group relative flex items-start gap-3 p-4 rounded-2xl border ${selectedAddressId === addr.id
|
|
505
|
-
? 'border-primary-500 bg-primary-50 shadow-
|
|
505
|
+
? 'border-primary-500 bg-primary-50 shadow-xs'
|
|
506
506
|
: 'border-slate-200 bg-white'
|
|
507
507
|
} cursor-pointer hover:border-primary-300 transition-colors`}
|
|
508
508
|
>
|
|
@@ -587,7 +587,7 @@ export function CheckoutScreen() {
|
|
|
587
587
|
<div className="md:col-span-2">
|
|
588
588
|
<label className="block mb-2 font-semibold">Select Pickup Location</label>
|
|
589
589
|
<select
|
|
590
|
-
className="w-full border rounded p-2"
|
|
590
|
+
className="w-full border rounded-sm p-2"
|
|
591
591
|
value={selectedStoreAddressId || ''}
|
|
592
592
|
onChange={e => setSelectedStoreAddressId(e.target.value)}
|
|
593
593
|
>
|
|
@@ -647,7 +647,7 @@ export function CheckoutScreen() {
|
|
|
647
647
|
<div className="flex items-start justify-between">
|
|
648
648
|
<div className="flex items-start gap-4 flex-1">
|
|
649
649
|
{/* Provider Logo */}
|
|
650
|
-
<div className="
|
|
650
|
+
<div className="shrink-0">
|
|
651
651
|
<Image
|
|
652
652
|
src={rate.providerImage75 || '/placeholder-product.jpg'}
|
|
653
653
|
alt={rate.provider}
|
|
@@ -668,7 +668,7 @@ export function CheckoutScreen() {
|
|
|
668
668
|
{rate.provider} {rate.servicelevel?.name}
|
|
669
669
|
</h3>
|
|
670
670
|
{isTest && (
|
|
671
|
-
<span className="px-2 py-1 text-xs font-medium bg-
|
|
671
|
+
<span className="px-2 py-1 text-xs font-medium bg-primary-100 text-primary-800 rounded-full">
|
|
672
672
|
TEST
|
|
673
673
|
</span>
|
|
674
674
|
)}
|
|
@@ -854,10 +854,10 @@ export function CheckoutScreen() {
|
|
|
854
854
|
className={`
|
|
855
855
|
group relative flex w-full items-center justify-between rounded-xl border-2 p-3
|
|
856
856
|
transition-all duration-200 ease-out
|
|
857
|
-
focus:outline-
|
|
857
|
+
focus:outline-hidden f
|
|
858
858
|
${active
|
|
859
859
|
? `${pm.activeClass} shadow-md scale-[1.02]`
|
|
860
|
-
: `${pm.className} border-gray-200 bg-white hover:shadow-
|
|
860
|
+
: `${pm.className} border-gray-200 bg-white hover:shadow-xs hover:-translate-y-0.5`
|
|
861
861
|
}
|
|
862
862
|
`}
|
|
863
863
|
>
|
|
@@ -884,7 +884,7 @@ export function CheckoutScreen() {
|
|
|
884
884
|
</div>
|
|
885
885
|
|
|
886
886
|
{active && (
|
|
887
|
-
<div className="flex h-6 w-6 items-center justify-center rounded-full shadow-
|
|
887
|
+
<div className="flex h-6 w-6 items-center justify-center rounded-full shadow-xs">
|
|
888
888
|
<Check className="h-3.5 w-3.5 text-white" />
|
|
889
889
|
</div>
|
|
890
890
|
)}
|
|
@@ -912,7 +912,7 @@ export function CheckoutScreen() {
|
|
|
912
912
|
className="space-y-10 lg:sticky lg:top-24 lg:col-span-1"
|
|
913
913
|
>
|
|
914
914
|
{/* Order Summary */}
|
|
915
|
-
<div className="bg-
|
|
915
|
+
<div className="bg-linear-to-br from-[#5B9BD5]/10 to-[#2B4B7C]/10 rounded-[24px] p-8 border-2 border-[#5B9BD5]/20 sticky top-24">
|
|
916
916
|
<h2 className="font-['Poppins',sans-serif] font-semibold text-[#2B4B7C] mb-6 text-2xl">Order Summary</h2>
|
|
917
917
|
|
|
918
918
|
|
|
@@ -974,7 +974,7 @@ export function CheckoutScreen() {
|
|
|
974
974
|
|
|
975
975
|
<div className="bg-white/80 rounded-xl p-4">
|
|
976
976
|
<p className="font-['Poppins',sans-serif] text-[11px] text-[#676c80] leading-[1.6]">
|
|
977
|
-
<strong className="text-[#2B4B7C]">Payment:</strong> We
|
|
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
978
|
</p>
|
|
979
979
|
</div>
|
|
980
980
|
</section>
|