hey-pharmacist-ecommerce 1.1.13 → 1.1.15

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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hey-pharmacist-ecommerce",
3
- "version": "1.1.13",
3
+ "version": "1.1.15",
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",
@@ -37,7 +37,7 @@
37
37
  "autoprefixer": "^10.4.0",
38
38
  "eslint": "^8.0.0",
39
39
  "eslint-config-next": "^14.0.0",
40
- "next": "^14.0.0",
40
+ "next": "^16.0.10",
41
41
  "postcss": "^8.4.0",
42
42
  "react": "^18.2.0",
43
43
  "react-dom": "^18.2.0",
@@ -46,7 +46,7 @@
46
46
  "typescript": "^5.3.0"
47
47
  },
48
48
  "peerDependencies": {
49
- "next": "^14.0.0",
49
+ "next": "^16.0.10",
50
50
  "react": "^18.0.0",
51
51
  "react-dom": "^18.0.0",
52
52
  "react-hook-form": "^7.0.0"
@@ -0,0 +1,209 @@
1
+ 'use client';
2
+
3
+ import React, { useState } from 'react';
4
+ import { MapPin, Plus, Edit3, Trash2, Star } from 'lucide-react';
5
+ import { useAddresses } from '@/hooks/useAddresses';
6
+ import { EmptyState } from './EmptyState';
7
+ import { Button } from './ui/Button';
8
+ import { Address } from '@/lib/Apis';
9
+ import { AddressFormModal } from './AddressFormModal';
10
+ import { useNotification } from '@/providers/NotificationProvider';
11
+ import { ConfirmModal } from './ui/ConfirmModal';
12
+
13
+ export function AccountAddressesTab() {
14
+ const { addresses, isLoading, error, removeAddress, markAsDefault, refresh } = useAddresses();
15
+ const [deletingId, setDeletingId] = useState<string | null>(null);
16
+ const [editingAddress, setEditingAddress] = useState<Address | undefined>(undefined);
17
+ const [isAddressModalOpen, setIsAddressModalOpen] = useState(false);
18
+ const [selectedAddressId, setSelectedAddressId] = useState<string | null>(null);
19
+ const [addressToDelete, setAddressToDelete] = useState<Address | null>(null);
20
+ const notification = useNotification();
21
+
22
+ const handleDelete = async (address: Address) => {
23
+ if (!confirm(`Remove ${address.name}'s address?\nYou can add it back at any time.`)) return;
24
+
25
+ setDeletingId(address.id);
26
+ try {
27
+ await removeAddress(address.id);
28
+ } catch (error) {
29
+ console.error('Failed to delete address:', error);
30
+ alert('Failed to delete address. Please try again.');
31
+ } finally {
32
+ setDeletingId(null);
33
+ }
34
+ };
35
+
36
+ const handleSetDefault = async (address: Address) => {
37
+ try {
38
+ await markAsDefault(address.id);
39
+ } catch (error) {
40
+ console.error('Failed to set default address:', error);
41
+ alert('Failed to set default address. Please try again.');
42
+ }
43
+ };
44
+
45
+ const handleConfirmDelete = async () => {
46
+ if (!addressToDelete) return;
47
+
48
+ setDeletingId(addressToDelete.id);
49
+ try {
50
+ await removeAddress(addressToDelete.id);
51
+ if (selectedAddressId === addressToDelete.id) {
52
+ setSelectedAddressId(null);
53
+ }
54
+ notification.success('Address deleted successfully');
55
+ } catch (error) {
56
+ console.error('Failed to delete address:', error);
57
+ notification.error('Failed to delete address. Please try again.');
58
+ } finally {
59
+ setDeletingId(null);
60
+ setAddressToDelete(null);
61
+ }
62
+ };
63
+
64
+ if (isLoading) {
65
+ return (
66
+ <div className="p-6">
67
+ <div className="grid gap-4 sm:grid-cols-2">
68
+ {Array.from({ length: 2 }).map((_, index) => (
69
+ <div
70
+ key={index}
71
+ className="h-48 animate-pulse rounded-lg border border-slate-100 bg-slate-50"
72
+ />
73
+ ))}
74
+ </div>
75
+ </div>
76
+ );
77
+ }
78
+
79
+ if (error) {
80
+ return (
81
+ <div className="p-6">
82
+ <div className="rounded-lg border border-red-100 bg-red-50 p-6 text-sm text-red-700">
83
+ {error.message}
84
+ </div>
85
+ </div>
86
+ );
87
+ }
88
+
89
+ return (
90
+ <>
91
+ <div className="p-6 bg-white rounded-xl">
92
+ <div className="mb-6 flex items-center justify-between">
93
+ <div>
94
+ <h3 className="text-lg font-semibold text-secondary">Saved Addresses</h3>
95
+ <p className="text-sm text-muted">Manage your delivery and billing addresses</p>
96
+ </div>
97
+ <button
98
+ className='flex flex-row items-center gap-2 px-6 py-2 border border-slate-200 rounded-xl text-slate-700 hover:opacity-80 transition-colors bg-secondary text-white text-sm'
99
+ onClick={() => {
100
+ setEditingAddress(undefined);
101
+ setIsAddressModalOpen(true);
102
+ }}
103
+ >
104
+ <Plus className="h-4 w-4" />
105
+ Add Address
106
+ </button>
107
+ </div>
108
+
109
+ {addresses.length === 0 ? (
110
+ <EmptyState
111
+ icon={MapPin}
112
+ title="No addresses yet"
113
+ description="Save a shipping or billing address to speed through checkout."
114
+ actionLabel="Add your first address"
115
+ onAction={() => {
116
+ setEditingAddress(undefined);
117
+ setIsAddressModalOpen(true);
118
+ }}
119
+ />
120
+ ) : (
121
+ <div className="grid gap-4 sm:grid-cols-2">
122
+ {addresses.map((address) => (
123
+ <div
124
+ key={address.id}
125
+ className="relative rounded-xl border border-slate-200 bg-white p-4 hover:shadow-md transition-shadow"
126
+ >
127
+
128
+ <div className="mb-3">
129
+ <div className='justify-between flex items-center'>
130
+ <div className="flex items-center gap-x-2">
131
+ <MapPin className="h-4 w-4 text-secondary" />
132
+ <p className="font-semibold text-secondary">{address.name}</p>
133
+ {address.isDefault && (
134
+ <span className="inline-flex items-center gap-1 rounded-full bg-green-100 px-2 py-1 text-xs font-semibold text-green-700">
135
+ Default
136
+ </span>
137
+ )}
138
+ </div>
139
+ <div className="flex items-center gap-x-2">
140
+ <button
141
+ onClick={() => {
142
+ setEditingAddress(address);
143
+ setIsAddressModalOpen(true);
144
+ }}
145
+ className="inline-flex items-center gap-1 rounded-full border border-slate-200 px-2 py-1 text-xs font-medium text-muted transition hover:border-primary-300 hover:text-primary-600"
146
+ >
147
+ <Edit3 className="h-3 w-3" />
148
+ </button>
149
+ {/* {!address.isDefault && (
150
+ <button
151
+ onClick={() => handleSetDefault(address)}
152
+ className="inline-flex items-center gap-1 rounded-full border border-amber-200 px-2 py-1 text-xs font-semibold text-amber-600 transition hover:border-amber-300 hover:text-amber-700"
153
+ >
154
+ <Star className="h-3 w-3" />
155
+ Make default
156
+ </button>
157
+ )} */}
158
+ <button
159
+ onClick={() => setAddressToDelete(address)}
160
+ disabled={deletingId === address.id}
161
+ className="inline-flex items-center gap-1 rounded-full border border-red-200 px-2 py-1 text-xs font-semibold text-red-600 transition hover:border-red-300 hover:text-red-700 disabled:opacity-50"
162
+ >
163
+ <Trash2 className="h-3 w-3" />
164
+ </button>
165
+ </div>
166
+ </div>
167
+ <p className="text-sm text-muted mt-1">
168
+ {address.street1}
169
+ {address.street2 && `, ${address.street2}`}
170
+ </p>
171
+ <p className="text-sm text-muted">
172
+ {address.city}, {address.state} {address.zip}
173
+ </p>
174
+ <p className="text-sm text-muted">{address.country}</p>
175
+ {address.phone && (
176
+ <p className="text-sm text-slate-500 mt-1">{address.phone}</p>
177
+ )}
178
+ </div>
179
+ </div>
180
+ ))}
181
+ </div>
182
+ )}
183
+ </div>
184
+ <AddressFormModal
185
+ isOpen={isAddressModalOpen}
186
+ onClose={() => setIsAddressModalOpen(false)}
187
+ initialAddress={editingAddress}
188
+ onAddressAdded={() => {
189
+ refresh();
190
+ }}
191
+ onAddressUpdated={() => {
192
+ refresh();
193
+ }}
194
+ />
195
+
196
+ <ConfirmModal
197
+ isOpen={!!addressToDelete}
198
+ onClose={() => setAddressToDelete(null)}
199
+ onConfirm={handleConfirmDelete}
200
+ title="Delete Address"
201
+ message={`Are you sure you want to delete ${addressToDelete?.name}'s address? This action cannot be undone.`}
202
+ confirmText="Delete"
203
+ cancelText="Cancel"
204
+ variant="danger"
205
+ isLoading={deletingId === addressToDelete?.id}
206
+ />
207
+ </>
208
+ );
209
+ }
@@ -0,0 +1,151 @@
1
+ 'use client';
2
+
3
+ import React from 'react';
4
+ import { Package, Truck } from 'lucide-react';
5
+ import { useCurrentOrders } from '@/hooks/useOrders';
6
+ import { EmptyState } from './EmptyState';
7
+ import { Badge } from './ui/Badge';
8
+ import { formatPrice, formatDate } from '@/lib/utils/format';
9
+ import Image from 'next/image';
10
+
11
+ export function AccountOrdersTab() {
12
+ const { orders, isLoading, error } = useCurrentOrders();
13
+
14
+ if (isLoading) {
15
+ return (
16
+ <div className="py-6 px-3 pb-24">
17
+ <div className="space-y-4">
18
+ {Array.from({ length: 3 }).map((_, index) => (
19
+ <div
20
+ key={index}
21
+ className="h-64 animate-pulse rounded-xl bg-slate-50"
22
+ />
23
+ ))}
24
+ </div>
25
+ </div>
26
+ );
27
+ }
28
+
29
+ if (error) {
30
+ return (
31
+ <div className="py-6 px-3 pb-24">
32
+ <div className="rounded-lg border border-red-100 bg-red-50 p-6 text-sm text-red-700">
33
+ {error.message}
34
+ </div>
35
+ </div>
36
+ );
37
+ }
38
+
39
+ if (orders.length === 0) {
40
+ return (
41
+ <div className="py-6 px-3 pb-24">
42
+ <EmptyState
43
+ icon={Package}
44
+ title="No orders yet"
45
+ description="When you place orders, they'll appear here for easy tracking."
46
+ />
47
+ </div>
48
+ );
49
+ }
50
+
51
+ return (
52
+ <div className="py-6 px-3 space-y-4 pb-24">
53
+ {orders.map((order) => {
54
+ const itemCount = order.items?.length || 0;
55
+
56
+ return (
57
+ <div
58
+ key={order._id}
59
+ className="rounded-xl border border-slate-200 bg-white p-6 hover:shadow-md transition-shadow"
60
+ >
61
+ {/* Order Header */}
62
+ <div className="flex items-start justify-between mb-4 pb-4 border-b border-slate-200">
63
+ <div className="flex items-center gap-3">
64
+ <div>
65
+ <h3 className="text-base font-semibold text-secondary">
66
+ Order ORD-{order._id?.slice(-6).toUpperCase()}
67
+ </h3>
68
+ {/* Order Date */}
69
+ <p className="text-xs text-muted mb-4">
70
+ Placed on {formatDate(order.createdAt || new Date(), 'long')}
71
+ </p>
72
+ </div>
73
+ <Badge
74
+ className="text-xs"
75
+ variant={
76
+ order.orderStatus === 'Delivered' ? 'success' :
77
+ order.orderStatus === 'In Transit' ? 'primary' :
78
+ order.orderStatus === 'Pending' ? 'warning' : 'gray'
79
+ }
80
+ >
81
+ {order.orderStatus}
82
+ </Badge>
83
+ </div>
84
+ <div className="text-right">
85
+ <p className="text-xs text-muted mb-1">Total Amount</p>
86
+ <p className="text-lg font-bold text-secondary">
87
+ {formatPrice(order.grandTotal || 0)}
88
+ </p>
89
+ </div>
90
+ </div>
91
+
92
+
93
+
94
+ {/* Order Items */}
95
+ <div className="space-y-3 mb-4">
96
+ {order.items && order.items.length > 0 ? (
97
+ order.items.map((item, index) => {
98
+ const itemPrice = item.productVariantData?.finalPrice || 0;
99
+ const itemTotal = itemPrice * item.quantity;
100
+
101
+ return (
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 flex-shrink-0 overflow-hidden">
104
+ <Image
105
+ src={item?.productVariantData?.productMedia?.[0]?.file || '/placeholder-product.jpg'}
106
+ alt={item?.productVariantData?.name || 'Product image'}
107
+ fill
108
+ className="object-cover"
109
+ sizes="48px"
110
+ />
111
+ </div>
112
+ <div className="flex-1 min-w-0">
113
+ <p className="font-medium text-secondary text-sm truncate">
114
+ {item.productVariantData?.name || 'Unknown Product'}
115
+ </p>
116
+ <p className="text-xs text-muted">Qty: {item.quantity}</p>
117
+ </div>
118
+ <p className="font-semibold text-secondary text-sm">
119
+ {formatPrice(itemTotal)}
120
+ </p>
121
+ </div>
122
+ );
123
+ })
124
+ ) : (
125
+ <p className="text-sm text-muted text-center py-2">No items found</p>
126
+ )}
127
+ </div>
128
+
129
+ {/* Tracking Information */}
130
+ {/* {order.trackingNumber && (
131
+ <div className="mt-4 pt-4 border-t border-slate-200">
132
+ <div className="flex items-center gap-2 bg-blue-50 rounded-lg p-3">
133
+ <Truck className="h-4 w-4 text-blue-600" />
134
+ <div className="flex-1">
135
+ <p className="text-xs text-muted">Tracking Number:</p>
136
+ <p className="text-sm font-medium text-secondary">
137
+ {order.trackingNumber}
138
+ </p>
139
+ </div>
140
+ <button className="text-xs text-primary-600 hover:text-primary-700 font-medium">
141
+ Track
142
+ </button>
143
+ </div>
144
+ </div>
145
+ )} */}
146
+ </div>
147
+ );
148
+ })}
149
+ </div>
150
+ );
151
+ }
@@ -0,0 +1,209 @@
1
+ 'use client';
2
+
3
+ import React from 'react';
4
+ import { User, Mail, Phone, MapPin, Package, CheckCircle, Heart, Clock, Edit } from 'lucide-react';
5
+ import { useAuth } from '@/providers/AuthProvider';
6
+ import { useCurrentOrders } from '@/hooks/useOrders';
7
+ import { useWishlist } from '@/providers/WishlistProvider';
8
+ import { formatPrice, formatDate } from '@/lib/utils/format';
9
+ import { Badge } from './ui/Badge';
10
+ import { Button } from './ui/Button';
11
+ import { useRouter } from 'next/navigation';
12
+ import { useBasePath } from '@/providers/BasePathProvider';
13
+
14
+ export function AccountOverviewTab() {
15
+ const { user } = useAuth();
16
+ const { orders, isLoading: ordersLoading } = useCurrentOrders();
17
+ const { getWishlistCount } = useWishlist();
18
+ const router = useRouter();
19
+ const { buildPath } = useBasePath();
20
+
21
+ if (!user) return null;
22
+
23
+ // Calculate stats
24
+ const totalOrders = orders?.length || 0;
25
+ const deliveredOrders = orders?.filter(order => order.orderStatus === 'Delivered')?.length || 0;
26
+ const inTransitOrders = orders?.filter(order => order.orderStatus === 'In Transit')?.length || 0;
27
+ const savedItemsCount = getWishlistCount();
28
+
29
+ // Get recent orders (last 3)
30
+ const recentOrders = orders?.slice(0, 3) || [];
31
+
32
+ const stats = [
33
+ {
34
+ icon: Package,
35
+ label: 'Total Orders',
36
+ value: totalOrders,
37
+ color: 'bg-[#DBEAFE] text-[#5B9BD5]',
38
+ },
39
+ {
40
+ icon: CheckCircle,
41
+ label: 'Delivered',
42
+ value: deliveredOrders,
43
+ color: 'bg-[#DCFCE7] text-[#00A63E]',
44
+ },
45
+ {
46
+ icon: Heart,
47
+ label: 'Saved Items',
48
+ value: savedItemsCount,
49
+ color: 'bg-[#FFEDD4] text-[#FF6B35]',
50
+ },
51
+ {
52
+ icon: Clock,
53
+ label: 'In Transit',
54
+ value: inTransitOrders,
55
+ color: 'bg-[#F3E8FF] text-[#9810FA]',
56
+ },
57
+ ];
58
+
59
+ return (
60
+ <div className="py-6 px-3 space-y-6 pb-24">
61
+ {/* Stats Cards */}
62
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
63
+ {stats.map((stat, index) => {
64
+ const Icon = stat.icon;
65
+ return (
66
+ <div
67
+ key={index}
68
+ className="rounded-xl border border-slate-200 bg-white p-4 hover:shadow-md transition-shadow"
69
+ >
70
+ <div className="flex items-center gap-3">
71
+ <div className={`flex h-12 w-12 items-center justify-center rounded-lg ${stat.color}`}>
72
+ <Icon className="h-6 w-6" />
73
+ </div>
74
+ <div>
75
+ <p className="text-2xl font-bold text-secondary">{stat.value}</p>
76
+ <p className="text-sm text-slate-600">{stat.label}</p>
77
+ </div>
78
+ </div>
79
+ </div>
80
+ );
81
+ })}
82
+ </div>
83
+
84
+ {/* Profile Information */}
85
+ <div className="rounded-xl border border-slate-200 bg-white p-6">
86
+ <div className="flex items-center justify-between mb-4">
87
+ <h3 className="text-lg font-semibold text-secondary">Profile Information</h3>
88
+ <button
89
+ onClick={() => router.push(buildPath('/account/edit'))}
90
+ className="flex items-center gap-1 text-sm text-primary-600 hover:text-primary-700"
91
+ >
92
+ <Edit className="h-4 w-4" />
93
+ Edit
94
+ </button>
95
+ </div>
96
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
97
+ <div className="flex items-start gap-3">
98
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-[#DBEAFE]">
99
+ <User className="h-5 w-5 text-[#5B9BD5]" />
100
+ </div>
101
+ <div>
102
+ <p className="text-xs text-muted">Full Name</p>
103
+ <p className="text-sm font-medium text-secondary">
104
+ {user.firstname} {user.lastname}
105
+ </p>
106
+ </div>
107
+ </div>
108
+ <div className="flex items-start gap-3">
109
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-[#DBEAFE]">
110
+ <Mail className="h-5 w-5 text-[#5B9BD5]" />
111
+ </div>
112
+ <div>
113
+ <p className="text-xs text-muted">E-mail Address</p>
114
+ <p className="text-sm font-medium text-secondary">{user.email}</p>
115
+ </div>
116
+ </div>
117
+ <div className="flex items-start gap-3">
118
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-[#DBEAFE]">
119
+ <Phone className="h-5 w-5 text-[#5B9BD5]" />
120
+ </div>
121
+ <div>
122
+ <p className="text-xs text-muted">Phone Number</p>
123
+ <p className="text-sm font-medium text-secondary">
124
+ {user.phoneNumber || 'Not provided'}
125
+ </p>
126
+ </div>
127
+ </div>
128
+ <div className="flex items-start gap-3">
129
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-[#DBEAFE]">
130
+ <MapPin className="h-5 w-5 text-[#5B9BD5]" />
131
+ </div>
132
+ <div>
133
+ <p className="text-xs text-muted">Date of Birth</p>
134
+ <p className="text-sm font-medium text-secondary">Not provided</p>
135
+ </div>
136
+ </div>
137
+ </div>
138
+ </div>
139
+
140
+ {/* Recent Orders */}
141
+ <div className="rounded-xl border border-slate-200 bg-white p-6">
142
+ <div className="flex items-center justify-between mb-4">
143
+ <h3 className="text-lg font-semibold text-secondary">Recent Orders</h3>
144
+ <button
145
+ onClick={() => {
146
+ // Switch to orders tab
147
+ const event = new CustomEvent('switchTab', { detail: 'orders' });
148
+ window.dispatchEvent(event);
149
+ }}
150
+ className="text-sm text-primary-600 hover:text-primary-700"
151
+ >
152
+ View All →
153
+ </button>
154
+ </div>
155
+ {ordersLoading ? (
156
+ <div className="space-y-3">
157
+ {Array.from({ length: 3 }).map((_, index) => (
158
+ <div
159
+ key={index}
160
+ className="h-16 animate-pulse rounded-lg bg-slate-50"
161
+ />
162
+ ))}
163
+ </div>
164
+ ) : recentOrders.length === 0 ? (
165
+ <p className="text-sm text-muted text-center py-8">No orders yet</p>
166
+ ) : (
167
+ <div className="space-y-3">
168
+ {recentOrders.map((order) => (
169
+ <div
170
+ key={order._id}
171
+ className="flex items-center justify-between rounded-lg bg-slate-50 p-4 hover:bg-slate-100 transition-colors"
172
+ >
173
+ <div className="flex items-center gap-3">
174
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-white">
175
+ <Package className="h-5 w-5 text-slate-600" />
176
+ </div>
177
+ <div>
178
+ <div className='flex items-center gap-2'>
179
+ <p className="text-sm font-medium text-secondary">
180
+ ORD-{order._id?.slice(-6).toUpperCase()}
181
+ </p>
182
+ <Badge className='p-1 text-xs'
183
+ variant={
184
+ order.orderStatus === 'Delivered' ? 'success' :
185
+ order.orderStatus === 'In Transit' ? 'primary' :
186
+ order.orderStatus === 'Pending' ? 'warning' : 'gray'
187
+ }>
188
+ {order.orderStatus}
189
+ </Badge>
190
+ </div>
191
+ <p className="text-xs text-muted">
192
+ {formatDate(order.createdAt || new Date(), 'short')}
193
+ </p>
194
+ </div>
195
+ </div>
196
+ <div className="flex items-center gap-3">
197
+
198
+ <p className="text-sm font-semibold text-secondary">
199
+ {formatPrice(order.grandTotal || 0)}
200
+ </p>
201
+ </div>
202
+ </div>
203
+ ))}
204
+ </div>
205
+ )}
206
+ </div>
207
+ </div>
208
+ );
209
+ }