hey-pharmacist-ecommerce 1.1.15 → 1.1.17

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hey-pharmacist-ecommerce",
3
- "version": "1.1.15",
3
+ "version": "1.1.17",
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",
package/src/index.ts CHANGED
@@ -15,7 +15,7 @@ export { CartScreen } from './screens/CartScreen';
15
15
  export { CheckoutScreen } from './screens/CheckoutScreen';
16
16
  export { LoginScreen } from './screens/LoginScreen';
17
17
  export { RegisterScreen } from './screens/RegisterScreen';
18
- export { ProfileScreen } from './screens/ProfileScreen';
18
+ export { default as ProfileScreen } from './screens/ProfileScreen';
19
19
  export { OrdersScreen } from './screens/OrdersScreen';
20
20
  export { CurrentOrdersScreen } from './screens/CurrentOrdersScreen';
21
21
  export { AddressesScreen } from './screens/AddressesScreen';
@@ -1,42 +1,63 @@
1
1
  'use client';
2
2
 
3
3
  import React, { useState } from 'react';
4
- import { motion } from 'framer-motion';
5
- import Link from 'next/link';
6
4
  import { useRouter } from 'next/navigation';
7
5
  import {
8
- ArrowRight,
6
+ User,
7
+ Package,
9
8
  Heart,
10
- HeartPulse,
11
- LogOut,
12
- Mail,
9
+ CreditCard,
13
10
  MapPin,
14
- Package,
15
- Phone,
16
- ShieldCheck,
17
- Sparkles,
18
- User,
11
+ Settings,
12
+ LogOut,
13
+ ChevronDown,
19
14
  } from 'lucide-react';
20
- import { Button } from '@/components/ui/Button';
21
15
  import { useAuth } from '@/providers/AuthProvider';
22
- import { getInitials } from '@/lib/utils/format';
23
16
  import { useBasePath } from '@/providers/BasePathProvider';
17
+ import { getInitials } from '@/lib/utils/format';
18
+ import { TabNavigation } from '@/components/TabNavigation';
19
+ import { AccountOverviewTab } from '@/components/AccountOverviewTab';
20
+ import { AccountOrdersTab } from '@/components/AccountOrdersTab';
21
+ import { AccountSavedItemsTab } from '@/components/AccountSavedItemsTab';
22
+ import { AccountPaymentTab } from '@/components/AccountPaymentTab';
23
+ import { AccountAddressesTab } from '@/components/AccountAddressesTab';
24
+ import { AccountSettingsTab } from '@/components/AccountSettingsTab';
24
25
 
25
- export function ProfileScreen() {
26
+ const tabs = [
27
+ { id: 'overview', label: 'Overview', icon: User },
28
+ { id: 'orders', label: 'Orders', icon: Package },
29
+ { id: 'saved-items', label: 'Saved Items', icon: Heart },
30
+ // { id: 'payment', label: 'Payment', icon: CreditCard },
31
+ { id: 'addresses', label: 'Addresses', icon: MapPin },
32
+ { id: 'settings', label: 'Settings', icon: Settings },
33
+ ];
34
+
35
+ export default function AccountPage() {
26
36
  const router = useRouter();
27
37
  const { user, logout, isLoading } = useAuth();
28
38
  const { buildPath } = useBasePath();
39
+ const [activeTab, setActiveTab] = useState('overview');
40
+ const [isDropdownOpen, setIsDropdownOpen] = useState(false);
29
41
  const [isLoggingOut, setIsLoggingOut] = useState(false);
30
- const [logoutError, setLogoutError] = useState<string | null>(null);
42
+
43
+ // Listen for tab switch events from child components
44
+ React.useEffect(() => {
45
+ const handleTabSwitch = (event: CustomEvent) => {
46
+ setActiveTab(event.detail);
47
+ };
48
+ window.addEventListener('switchTab', handleTabSwitch as EventListener);
49
+ return () => {
50
+ window.removeEventListener('switchTab', handleTabSwitch as EventListener);
51
+ };
52
+ }, []);
31
53
 
32
54
  const handleLogout = async () => {
33
55
  setIsLoggingOut(true);
34
- setLogoutError(null);
35
56
  try {
36
57
  await logout();
37
58
  router.push(buildPath('/'));
38
59
  } catch (error: any) {
39
- setLogoutError(error?.response?.data?.message || 'Failed to logout. Please try again.');
60
+ console.error('Logout failed:', error);
40
61
  } finally {
41
62
  setIsLoggingOut(false);
42
63
  }
@@ -48,230 +69,49 @@ export function ProfileScreen() {
48
69
  return null;
49
70
  }
50
71
 
51
- const quickLinks = [
52
- {
53
- icon: Package,
54
- label: 'Orders & invoices',
55
- description: 'Track shipments, invoices, and receipts',
56
- href: buildPath('/orders'),
57
- },
58
- {
59
- icon: Heart,
60
- label: 'Wishlist',
61
- description: 'Curate go-to remedies and favorites',
62
- href: buildPath('/wishlist'),
63
- },
64
- {
65
- icon: MapPin,
66
- label: 'Delivery addresses',
67
- description: 'Manage saved delivery locations',
68
- href: buildPath('/account/addresses'),
69
- },
70
- ];
72
+ const renderTabContent = () => {
73
+ switch (activeTab) {
74
+ case 'overview':
75
+ return <AccountOverviewTab />;
76
+ case 'orders':
77
+ return <AccountOrdersTab />;
78
+ case 'saved-items':
79
+ return <AccountSavedItemsTab />;
80
+ // case 'payment':
81
+ // return <AccountPaymentTab />;
82
+ case 'addresses':
83
+ return <AccountAddressesTab />;
84
+ case 'settings':
85
+ return <AccountSettingsTab />;
86
+ default:
87
+ return <AccountOverviewTab />;
88
+ }
89
+ };
71
90
 
72
91
  return (
73
- <div className="min-h-screen bg-slate-50 text-slate-900">
74
- <div className="relative container mx-auto px-4 pb-16 pt-10">
75
- <div className="rounded-3xl border border-slate-200 bg-white/95 p-8 shadow-xl shadow-primary-50 backdrop-blur">
76
- <div className="mb-6 flex flex-wrap items-center justify-between gap-3">
77
- <div className="text-sm text-slate-500">
78
- Stay signed in to keep prescriptions and deliveries synced.
79
- </div>
80
- <div className="flex items-center gap-3">
81
- {logoutError && (
82
- <div className="flex items-start gap-2 rounded-xl border border-red-200 bg-red-50 px-3 py-2 text-xs text-red-700">
83
- <ShieldCheck className="mt-[1px] h-4 w-4 text-red-500" />
84
- <span>{logoutError}</span>
85
- </div>
86
- )}
87
- <Button
88
- type="button"
89
- variant="outline"
90
- size="sm"
91
- className="border-red-200 text-red-600 hover:bg-red-50"
92
- onClick={handleLogout}
93
- disabled={isLoggingOut}
94
- >
95
- <LogOut className="h-4 w-4" />
96
- {isLoggingOut ? 'Logging out...' : 'Log out'}
97
- </Button>
98
- </div>
99
- </div>
100
- <motion.div
101
- initial={{ opacity: 0, y: 18 }}
102
- animate={{ opacity: 1, y: 0 }}
103
- className="grid gap-8 md:grid-cols-[minmax(0,1.1fr)_minmax(0,0.9fr)] md:items-center"
104
- >
105
- <div className="space-y-4">
106
- <div className="inline-flex items-center gap-2 rounded-full bg-slate-900 text-white px-4 py-1 text-xs font-semibold uppercase tracking-[0.32em]">
107
- <HeartPulse className="h-4 w-4" />
108
- Account
109
- </div>
110
- <div>
111
- <h1 className="text-4xl font-semibold md:text-5xl">
112
- Welcome back, {user.firstname} {user.lastname}
113
- </h1>
114
- <p className="mt-3 max-w-2xl text-base text-slate-600">
115
- Keep your identity and delivery details accurate for smoother refills, faster
116
- shipping, and timely pharmacist guidance.
117
- </p>
118
- </div>
119
- <div className="flex flex-wrap gap-3 text-sm text-slate-600">
120
- <span className="inline-flex items-center gap-2 rounded-full border border-slate-200 bg-slate-50 px-3 py-2">
121
- <ShieldCheck className="h-4 w-4 text-primary-600" />
122
- Secure login enabled
123
- </span>
124
- <span className="inline-flex items-center gap-2 rounded-full border border-slate-200 bg-slate-50 px-3 py-2">
125
- <Mail className="h-4 w-4 text-primary-600" />
126
- {user.email}
127
- </span>
128
- <span className="inline-flex items-center gap-2 rounded-full border border-slate-200 bg-slate-50 px-3 py-2">
129
- <Phone className="h-4 w-4 text-primary-600" />
130
- {user.phoneNumber || 'Add phone for urgent updates'}
131
- </span>
132
- </div>
92
+ <div className="min-h-screen bg-gradient-to-b from-[#F8FAFC] to-[#EBF4FB]">
93
+ {/* Header */}
94
+ <div className="">
95
+ <div className="container mx-auto px-4 py-4">
96
+ <div className="flex items-center justify-between">
97
+ <div>
98
+ <h1 className="text-2xl font-semibold text-secondary">My Account</h1>
99
+ <p className="text-sm text-muted">
100
+ Manage your profile, orders, and preferences
101
+ </p>
133
102
  </div>
134
103
 
135
- <div className="flex flex-col gap-4 rounded-2xl border border-slate-200 bg-slate-50/70 p-6 shadow-sm">
136
- <div className="flex items-center gap-4">
137
- <div className="flex h-16 w-16 items-center justify-center rounded-2xl bg-white text-2xl font-semibold text-slate-900 shadow-sm">
138
- {getInitials(user?.firstname || '', user?.lastname || '') || ''}
139
- </div>
140
- <div className="space-y-1">
141
- <p className="text-sm text-slate-500">Signed in as</p>
142
- <p className="text-lg font-semibold text-slate-900">{user.firstname}</p>
143
- <p className="text-sm text-slate-500">{user.email}</p>
144
- </div>
145
- </div>
146
- <div className="grid gap-3 sm:grid-cols-2">
147
- <Button
148
- variant="outline"
149
- size="md"
150
- className="border-slate-300 text-slate-800 hover:bg-white"
151
- onClick={() => router.push(buildPath('/account/edit'))}
152
- >
153
- Edit profile
154
- </Button>
155
- <Button
156
- variant="ghost"
157
- size="md"
158
- className="text-slate-700 hover:bg-white"
159
- onClick={() => router.push(buildPath('/account/change-password'))}
160
- >
161
- Change password
162
- </Button>
163
- </div>
164
- </div>
165
- </motion.div>
104
+ </div>
166
105
  </div>
106
+ </div>
167
107
 
168
- <div className="mt-10 grid gap-8 lg:grid-cols-[minmax(0,1.08fr)_minmax(0,0.92fr)] items-stretch">
169
- <motion.div
170
- initial={{ opacity: 0, y: 18 }}
171
- animate={{ opacity: 1, y: 0 }}
172
- className="space-y-6"
173
- >
174
- <section className="rounded-3xl border border-slate-200 bg-white p-7 shadow-lg shadow-primary-50/40 min-h-[420px] h-full flex flex-col">
175
- <div className="flex items-center justify-between gap-3">
176
- <div>
177
- <p className="text-xs font-semibold uppercase tracking-[0.3em] text-slate-400">
178
- Essentials
179
- </p>
180
- <h2 className="mt-1 text-xl font-semibold text-slate-900">Profile snapshot</h2>
181
- </div>
182
- <Sparkles className="h-5 w-5 text-primary-500" />
183
- </div>
184
- <div className="mt-6 grid gap-4 md:grid-cols-2">
185
- <div className="rounded-2xl border border-slate-200 bg-slate-50/70 p-5">
186
- <p className="text-xs font-semibold uppercase tracking-[0.28em] text-slate-500">
187
- Contact
188
- </p>
189
- <div className="mt-3 space-y-2 text-sm text-slate-600">
190
- <p className="flex items-center gap-2 font-semibold text-slate-900">
191
- <User className="h-4 w-4 text-primary-500" />
192
- {user.firstname} {user.lastname}
193
- </p>
194
- <p className="flex items-center gap-2">
195
- <Mail className="h-4 w-4 text-primary-500" />
196
- {user.email}
197
- </p>
198
- <p className="flex items-center gap-2">
199
- <Phone className="h-4 w-4 text-primary-500" />
200
- {user.phoneNumber || 'Not provided'}
201
- </p>
202
- </div>
203
- <Button
204
- variant="outline"
205
- size="md"
206
- className="mt-4 w-full border-slate-300 text-slate-800 hover:bg-white"
207
- onClick={() => router.push(buildPath('/account/edit'))}
208
- >
209
- Update information
210
- </Button>
211
- </div>
212
-
213
- <div className="rounded-2xl border border-slate-200 bg-slate-50/70 p-5">
214
- <p className="text-xs font-semibold uppercase tracking-[0.28em] text-slate-500">
215
- Security
216
- </p>
217
- <div className="mt-3 space-y-2 text-sm text-slate-600">
218
- <p className="flex items-center gap-2 font-semibold text-slate-900">
219
- <ShieldCheck className="h-4 w-4 text-primary-500" />
220
- Multi-factor ready
221
- </p>
222
- <p>Protect your account with a fresh password and keep notifications on.</p>
223
- </div>
224
- <Button
225
- variant="primary"
226
- size="md"
227
- className="mt-4 w-full"
228
- onClick={() => router.push(buildPath('/account/change-password'))}
229
- >
230
- Change password
231
- </Button>
232
- </div>
233
- </div>
234
- </section>
235
- </motion.div>
108
+ {/* Tab Navigation */}
109
+ <TabNavigation tabs={tabs} activeTab={activeTab} onTabChange={setActiveTab} />
236
110
 
237
- <motion.aside
238
- initial={{ opacity: 0, y: 18 }}
239
- animate={{ opacity: 1, y: 0 }}
240
- transition={{ delay: 0.05 }}
241
- className="space-y-4 h-full"
242
- >
243
- <section className="rounded-3xl border border-slate-200 bg-white p-6 shadow-sm min-h-[420px] h-full flex flex-col">
244
- <div className="flex items-center justify-between">
245
- <div>
246
- <p className="text-xs font-semibold uppercase tracking-[0.3em] text-slate-400">
247
- Quick links
248
- </p>
249
- <h3 className="mt-1 text-lg font-semibold text-slate-900">Next steps</h3>
250
- </div>
251
- <Heart className="h-5 w-5 text-primary-500" />
252
- </div>
253
- <div className="mt-5 grid gap-3">
254
- {quickLinks.map((item) => (
255
- <Link
256
- key={item.href}
257
- href={item.href}
258
- className="group relative flex items-start gap-3 rounded-2xl border border-slate-200 bg-slate-50/70 p-4 transition duration-200 hover:-translate-y-0.5 hover:border-primary-200 hover:bg-white hover:shadow-md"
259
- >
260
- <span className="flex h-11 w-11 items-center justify-center rounded-xl bg-white text-primary-600 shadow-sm group-hover:bg-primary-600 group-hover:text-white">
261
- <item.icon className="h-5 w-5" />
262
- </span>
263
- <div className="flex-1">
264
- <p className="flex items-center gap-2 text-base font-semibold text-slate-900 group-hover:text-primary-700">
265
- {item.label}
266
- <ArrowRight className="h-4 w-4 opacity-0 transition group-hover:opacity-100" />
267
- </p>
268
- <p className="text-sm text-slate-500">{item.description}</p>
269
- </div>
270
- </Link>
271
- ))}
272
- </div>
273
- </section>
274
- </motion.aside>
111
+ {/* Tab Content */}
112
+ <div className="container mx-auto max-w-7xl">
113
+ <div className="rounded-2xl shadow-sm">
114
+ {renderTabContent()}
275
115
  </div>
276
116
  </div>
277
117
  </div>
@@ -921,7 +921,7 @@ export function ShopScreen({ initialFilters = {}, categoryName }: ShopScreenProp
921
921
 
922
922
  {/* Shop by Category Section */}
923
923
  <section className="py-8 bg-white">
924
- <div className="max-w-[1400px] mx-auto px-8 md:px-12">
924
+ <div className="container mx-auto px-4">
925
925
  <h2 className="text-2xl md:text-3xl font-['Poppins',sans-serif] font-semibold text-secondary mb-6">
926
926
  Shop by Category
927
927
  </h2>