hey-pharmacist-ecommerce 1.1.3 → 1.1.5
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.js +206 -120
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +206 -120
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/Footer.tsx +17 -15
- package/src/components/Header.tsx +14 -12
- package/src/components/OrderCard.tsx +3 -1
- package/src/components/ui/Modal.tsx +2 -0
- package/src/lib/api-adapter/config.ts +8 -5
- package/src/providers/AuthProvider.tsx +19 -5
- package/src/providers/FavoritesProvider.tsx +12 -2
- package/src/screens/CartScreen.tsx +5 -3
- package/src/screens/CategoriesScreen.tsx +4 -2
- package/src/screens/CheckoutScreen.tsx +6 -4
- package/src/screens/CurrentOrdersScreen.tsx +4 -2
- package/src/screens/HomeScreen.tsx +4 -4
- package/src/screens/LoginScreen.tsx +3 -1
- package/src/screens/OrdersScreen.tsx +3 -1
- package/src/screens/ProductDetailScreen.tsx +5 -3
- package/src/screens/ProfileScreen.tsx +10 -8
- package/src/screens/RegisterScreen.tsx +3 -1
- package/src/screens/ShopScreen.tsx +2 -2
- package/src/screens/WishlistScreen.tsx +4 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hey-pharmacist-ecommerce",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
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",
|
|
@@ -3,34 +3,36 @@
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { Mail, Phone, MapPin, Facebook, Twitter, Instagram } from 'lucide-react';
|
|
5
5
|
import { useTheme } from '@/providers/ThemeProvider';
|
|
6
|
+
import { useBasePath } from '@/providers/BasePathProvider';
|
|
6
7
|
import Link from 'next/link';
|
|
7
8
|
|
|
8
9
|
export function Footer() {
|
|
9
10
|
const { config } = useTheme();
|
|
11
|
+
const { buildPath } = useBasePath();
|
|
10
12
|
|
|
11
13
|
const footerLinks = {
|
|
12
14
|
shop: [
|
|
13
|
-
{ label: 'All Products', href: '/shop' },
|
|
14
|
-
{ label: 'Categories', href: '/categories' },
|
|
15
|
-
{ label: 'Featured', href: '/featured' },
|
|
16
|
-
{ label: 'New Arrivals', href: '/new' },
|
|
15
|
+
{ label: 'All Products', href: buildPath('/shop') },
|
|
16
|
+
{ label: 'Categories', href: buildPath('/categories') },
|
|
17
|
+
{ label: 'Featured', href: buildPath('/featured') },
|
|
18
|
+
{ label: 'New Arrivals', href: buildPath('/new') },
|
|
17
19
|
],
|
|
18
20
|
account: [
|
|
19
|
-
{ label: 'My Account', href: '/account' },
|
|
20
|
-
{ label: 'Orders', href: '/orders' },
|
|
21
|
-
{ label: 'Wishlist', href: '/wishlist' },
|
|
22
|
-
{ label: 'Cart', href: '/cart' },
|
|
21
|
+
{ label: 'My Account', href: buildPath('/account') },
|
|
22
|
+
{ label: 'Orders', href: buildPath('/orders') },
|
|
23
|
+
{ label: 'Wishlist', href: buildPath('/wishlist') },
|
|
24
|
+
{ label: 'Cart', href: buildPath('/cart') },
|
|
23
25
|
],
|
|
24
26
|
support: [
|
|
25
|
-
{ label: 'Contact Us', href: '/contact' },
|
|
26
|
-
{ label: 'FAQs', href: '/faqs' },
|
|
27
|
-
{ label: 'Shipping Info', href: '/shipping' },
|
|
28
|
-
{ label: 'Returns', href: '/returns' },
|
|
27
|
+
{ label: 'Contact Us', href: buildPath('/contact') },
|
|
28
|
+
{ label: 'FAQs', href: buildPath('/faqs') },
|
|
29
|
+
{ label: 'Shipping Info', href: buildPath('/shipping') },
|
|
30
|
+
{ label: 'Returns', href: buildPath('/returns') },
|
|
29
31
|
],
|
|
30
32
|
legal: [
|
|
31
|
-
{ label: 'Privacy Policy', href: '/privacy' },
|
|
32
|
-
{ label: 'Terms of Service', href: '/terms' },
|
|
33
|
-
{ label: 'Cookie Policy', href: '/cookies' },
|
|
33
|
+
{ label: 'Privacy Policy', href: buildPath('/privacy') },
|
|
34
|
+
{ label: 'Terms of Service', href: buildPath('/terms') },
|
|
35
|
+
{ label: 'Cookie Policy', href: buildPath('/cookies') },
|
|
34
36
|
],
|
|
35
37
|
};
|
|
36
38
|
|
|
@@ -7,6 +7,7 @@ import { useAuth } from '@/providers/AuthProvider';
|
|
|
7
7
|
import { useCart } from '@/providers/CartProvider';
|
|
8
8
|
import { useTheme } from '@/providers/ThemeProvider';
|
|
9
9
|
import { useWishlist } from '@/providers/WishlistProvider';
|
|
10
|
+
import { useBasePath } from '@/providers/BasePathProvider';
|
|
10
11
|
import Link from 'next/link';
|
|
11
12
|
import Image from 'next/image';
|
|
12
13
|
|
|
@@ -19,13 +20,14 @@ export function Header() {
|
|
|
19
20
|
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
|
20
21
|
const [isSearchOpen, setIsSearchOpen] = useState(false);
|
|
21
22
|
const [searchQuery, setSearchQuery] = useState('');
|
|
23
|
+
const { buildPath } = useBasePath();
|
|
22
24
|
|
|
23
25
|
const navLinks = [
|
|
24
|
-
{ href: '/shop', label: 'Shop' },
|
|
25
|
-
{ href: '/categories', label: 'Categories' },
|
|
26
|
-
{ href: '/orders', label: 'Orders' },
|
|
27
|
-
{ href: '/about', label: 'About' },
|
|
28
|
-
{ href: '/contact', label: 'Contact' },
|
|
26
|
+
{ href: buildPath('/shop'), label: 'Shop' },
|
|
27
|
+
{ href: buildPath('/categories'), label: 'Categories' },
|
|
28
|
+
{ href: buildPath('/orders'), label: 'Orders' },
|
|
29
|
+
{ href: buildPath('/about'), label: 'About' },
|
|
30
|
+
{ href: buildPath('/contact'), label: 'Contact' },
|
|
29
31
|
];
|
|
30
32
|
|
|
31
33
|
return (
|
|
@@ -33,7 +35,7 @@ export function Header() {
|
|
|
33
35
|
<div className="container mx-auto px-4">
|
|
34
36
|
<div className="flex items-center justify-between h-20">
|
|
35
37
|
{/* Logo */}
|
|
36
|
-
<Link href=
|
|
38
|
+
<Link href={buildPath('/')} className="flex items-center gap-3">
|
|
37
39
|
<div className="relative w-12 h-12">
|
|
38
40
|
<Image
|
|
39
41
|
src={config.logo}
|
|
@@ -89,7 +91,7 @@ export function Header() {
|
|
|
89
91
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
90
92
|
onKeyDown={(e) => {
|
|
91
93
|
if (e.key === 'Enter' && searchQuery.trim()) {
|
|
92
|
-
window.location.href = `/search?q=${encodeURIComponent(searchQuery.trim())}
|
|
94
|
+
window.location.href = buildPath(`/search?q=${encodeURIComponent(searchQuery.trim())}`);
|
|
93
95
|
}
|
|
94
96
|
}}
|
|
95
97
|
placeholder="Search products..."
|
|
@@ -112,7 +114,7 @@ export function Header() {
|
|
|
112
114
|
|
|
113
115
|
{/* Wishlist and Cart */}
|
|
114
116
|
<div className="flex items-center gap-4">
|
|
115
|
-
<Link href=
|
|
117
|
+
<Link href={buildPath('/wishlist')} className="relative p-2 text-gray-700 hover:text-red-500 transition-colors">
|
|
116
118
|
<Heart className="w-6 h-6" />
|
|
117
119
|
{wishlistCount > 0 && (
|
|
118
120
|
<span className="absolute -top-1 -right-1 bg-red-500 text-white text-xs font-bold rounded-full w-5 h-5 flex items-center justify-center">
|
|
@@ -121,7 +123,7 @@ export function Header() {
|
|
|
121
123
|
)}
|
|
122
124
|
</Link>
|
|
123
125
|
|
|
124
|
-
<Link href=
|
|
126
|
+
<Link href={buildPath('/cart')} className="relative p-2 text-gray-700 hover:text-primary-600 transition-colors">
|
|
125
127
|
<ShoppingCart className="w-6 h-6" />
|
|
126
128
|
{cart?.cartBody?.items?.length && cart.cartBody?.items?.length > 0 ? (
|
|
127
129
|
<span className="absolute -top-1 -right-1 bg-red-500 text-white text-xs font-bold rounded-full w-5 h-5 flex items-center justify-center">
|
|
@@ -133,14 +135,14 @@ export function Header() {
|
|
|
133
135
|
{/* User Menu */}
|
|
134
136
|
{isAuthenticated ? (
|
|
135
137
|
<Link
|
|
136
|
-
href=
|
|
138
|
+
href={buildPath('/account')}
|
|
137
139
|
className="p-2 hover:bg-gray-100 rounded-lg transition-colors"
|
|
138
140
|
>
|
|
139
141
|
<User className="w-6 h-6 text-gray-700" />
|
|
140
142
|
</Link>
|
|
141
143
|
) : (
|
|
142
144
|
<Link
|
|
143
|
-
href=
|
|
145
|
+
href={buildPath('/login')}
|
|
144
146
|
className="hidden sm:block px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors font-medium"
|
|
145
147
|
>
|
|
146
148
|
Sign In
|
|
@@ -183,7 +185,7 @@ export function Header() {
|
|
|
183
185
|
))}
|
|
184
186
|
{!isAuthenticated && (
|
|
185
187
|
<Link
|
|
186
|
-
href=
|
|
188
|
+
href={buildPath('/login')}
|
|
187
189
|
onClick={() => setIsMobileMenuOpen(false)}
|
|
188
190
|
className="px-4 py-3 text-primary-600 hover:bg-gray-50 rounded-lg font-medium"
|
|
189
191
|
>
|
|
@@ -8,12 +8,14 @@ import { formatPrice, formatDate } from '@/lib/utils/format';
|
|
|
8
8
|
import { Badge } from './ui/Badge';
|
|
9
9
|
import Link from 'next/link';
|
|
10
10
|
import Image from 'next/image';
|
|
11
|
+
import { useBasePath } from '@/providers/BasePathProvider';
|
|
11
12
|
|
|
12
13
|
interface OrderCardProps {
|
|
13
14
|
order: PopulatedOrder;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
export function OrderCard({ order }: OrderCardProps) {
|
|
18
|
+
const { buildPath } = useBasePath();
|
|
17
19
|
const config = order.orderStatus;
|
|
18
20
|
|
|
19
21
|
return (
|
|
@@ -77,7 +79,7 @@ export function OrderCard({ order }: OrderCardProps) {
|
|
|
77
79
|
</a>
|
|
78
80
|
)}
|
|
79
81
|
{/* <Link
|
|
80
|
-
href={`/orders/${order.id}`}
|
|
82
|
+
href={buildPath(`/orders/${order.id}`)}
|
|
81
83
|
className="inline-flex items-center gap-2 px-4 py-2 border-2 border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
|
|
82
84
|
>
|
|
83
85
|
View Details
|
|
@@ -44,12 +44,15 @@ export function getCurrentConfig(): EcommerceConfig {
|
|
|
44
44
|
return currentConfig;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
// Token management
|
|
48
|
-
|
|
47
|
+
// Token management with store-scoped keys
|
|
48
|
+
function getTokenKey(): string {
|
|
49
|
+
const storeId = currentConfig?.storeId || 'default';
|
|
50
|
+
return `ecommerce_access_token_${storeId}`;
|
|
51
|
+
}
|
|
49
52
|
|
|
50
53
|
export function setAuthToken(token: string): void {
|
|
51
54
|
if (typeof window !== 'undefined') {
|
|
52
|
-
localStorage.setItem(
|
|
55
|
+
localStorage.setItem(getTokenKey(), token);
|
|
53
56
|
// Update the configuration with new token
|
|
54
57
|
if (apiConfiguration) {
|
|
55
58
|
apiConfiguration.accessToken = () => token;
|
|
@@ -59,14 +62,14 @@ export function setAuthToken(token: string): void {
|
|
|
59
62
|
|
|
60
63
|
export function getAuthToken(): string | null {
|
|
61
64
|
if (typeof window !== 'undefined') {
|
|
62
|
-
return localStorage.getItem(
|
|
65
|
+
return localStorage.getItem(getTokenKey());
|
|
63
66
|
}
|
|
64
67
|
return null;
|
|
65
68
|
}
|
|
66
69
|
|
|
67
70
|
export function clearAuthToken(): void {
|
|
68
71
|
if (typeof window !== 'undefined') {
|
|
69
|
-
localStorage.removeItem(
|
|
72
|
+
localStorage.removeItem(getTokenKey());
|
|
70
73
|
// Clear from configuration
|
|
71
74
|
if (apiConfiguration) {
|
|
72
75
|
apiConfiguration.accessToken = () => '';
|
|
@@ -34,7 +34,20 @@ interface AuthProviderProps {
|
|
|
34
34
|
export function AuthProvider({ children }: AuthProviderProps) {
|
|
35
35
|
const [user, setUser] = useState<UserEntity | null>(null);
|
|
36
36
|
const [isLoading, setIsLoading] = useState(true);
|
|
37
|
-
|
|
37
|
+
|
|
38
|
+
// Get store-scoped key for user cache
|
|
39
|
+
const getUserKey = () => {
|
|
40
|
+
if (typeof window === 'undefined') return 'ecommerce_user';
|
|
41
|
+
const token = getAuthToken();
|
|
42
|
+
if (!token) return 'ecommerce_user';
|
|
43
|
+
// Use a hash or storeId from token/config to namespace
|
|
44
|
+
try {
|
|
45
|
+
const config = require('@/lib/api-adapter/config').getCurrentConfig();
|
|
46
|
+
return `ecommerce_user_${config?.storeId || 'default'}`;
|
|
47
|
+
} catch {
|
|
48
|
+
return 'ecommerce_user';
|
|
49
|
+
}
|
|
50
|
+
};
|
|
38
51
|
|
|
39
52
|
const refreshUser = useCallback(async () => {
|
|
40
53
|
try {
|
|
@@ -55,7 +68,8 @@ export function AuthProvider({ children }: AuthProviderProps) {
|
|
|
55
68
|
// Hydrate user instantly from localStorage for fast UI, then validate in background
|
|
56
69
|
if (typeof window !== 'undefined') {
|
|
57
70
|
try {
|
|
58
|
-
const
|
|
71
|
+
const userKey = getUserKey();
|
|
72
|
+
const cached = localStorage.getItem(userKey);
|
|
59
73
|
if (cached) {
|
|
60
74
|
const parsed: UserEntity = JSON.parse(cached);
|
|
61
75
|
setUser(parsed);
|
|
@@ -79,7 +93,7 @@ export function AuthProvider({ children }: AuthProviderProps) {
|
|
|
79
93
|
}
|
|
80
94
|
setUser(response.data.userData);
|
|
81
95
|
if (typeof window !== 'undefined') {
|
|
82
|
-
localStorage.setItem(
|
|
96
|
+
localStorage.setItem(getUserKey(), JSON.stringify(response.data.userData));
|
|
83
97
|
}
|
|
84
98
|
return response.data;
|
|
85
99
|
} finally {
|
|
@@ -96,7 +110,7 @@ export function AuthProvider({ children }: AuthProviderProps) {
|
|
|
96
110
|
}
|
|
97
111
|
setUser(response.data.userData);
|
|
98
112
|
if (typeof window !== 'undefined') {
|
|
99
|
-
localStorage.setItem(
|
|
113
|
+
localStorage.setItem(getUserKey(), JSON.stringify(response.data.userData));
|
|
100
114
|
}
|
|
101
115
|
return response.data;
|
|
102
116
|
} finally {
|
|
@@ -110,7 +124,7 @@ export function AuthProvider({ children }: AuthProviderProps) {
|
|
|
110
124
|
clearAuthToken();
|
|
111
125
|
setUser(null);
|
|
112
126
|
if (typeof window !== 'undefined') {
|
|
113
|
-
localStorage.removeItem(
|
|
127
|
+
localStorage.removeItem(getUserKey());
|
|
114
128
|
}
|
|
115
129
|
} finally {
|
|
116
130
|
setIsLoading(false);
|
|
@@ -18,10 +18,20 @@ export function FavoritesProvider({ children }: { children: ReactNode }) {
|
|
|
18
18
|
const [favorites, setFavorites] = useState<string[]>([]);
|
|
19
19
|
const [isClient, setIsClient] = useState(false);
|
|
20
20
|
|
|
21
|
+
const getFavoritesKey = () => {
|
|
22
|
+
if (typeof window === 'undefined') return 'favorites';
|
|
23
|
+
try {
|
|
24
|
+
const config = require('@/lib/api-adapter/config').getCurrentConfig();
|
|
25
|
+
return `favorites_${config?.storeId || 'default'}`;
|
|
26
|
+
} catch {
|
|
27
|
+
return 'favorites';
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
21
31
|
// Initialize state on client-side to avoid hydration issues
|
|
22
32
|
useEffect(() => {
|
|
23
33
|
setIsClient(true);
|
|
24
|
-
const savedFavorites = localStorage?.getItem(
|
|
34
|
+
const savedFavorites = localStorage?.getItem(getFavoritesKey());
|
|
25
35
|
if (savedFavorites) {
|
|
26
36
|
try {
|
|
27
37
|
setFavorites(JSON.parse(savedFavorites));
|
|
@@ -34,7 +44,7 @@ export function FavoritesProvider({ children }: { children: ReactNode }) {
|
|
|
34
44
|
// Save to localStorage whenever favorites change
|
|
35
45
|
useEffect(() => {
|
|
36
46
|
if (isClient) {
|
|
37
|
-
localStorage.setItem(
|
|
47
|
+
localStorage.setItem(getFavoritesKey(), JSON.stringify(favorites));
|
|
38
48
|
}
|
|
39
49
|
}, [favorites, isClient]);
|
|
40
50
|
|
|
@@ -16,11 +16,13 @@ import { useCart } from '@/providers/CartProvider';
|
|
|
16
16
|
import { formatPrice } from '@/lib/utils/format';
|
|
17
17
|
import { useRouter } from 'next/navigation';
|
|
18
18
|
import { CartItemPopulated } from '@/lib/Apis';
|
|
19
|
+
import { useBasePath } from '@/providers/BasePathProvider';
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
export function CartScreen() {
|
|
22
23
|
const router = useRouter();
|
|
23
24
|
const { cart, isLoading } = useCart();
|
|
25
|
+
const { buildPath } = useBasePath();
|
|
24
26
|
|
|
25
27
|
if (!cart || cart.cartBody.items.length === 0) {
|
|
26
28
|
return (
|
|
@@ -31,7 +33,7 @@ export function CartScreen() {
|
|
|
31
33
|
title="Your bag feels a little empty"
|
|
32
34
|
description="Add pharmacy favorites to unlock quick shipping, curated bundles, and personalized recommendations."
|
|
33
35
|
actionLabel="Discover products"
|
|
34
|
-
onAction={() => router.push('/shop')}
|
|
36
|
+
onAction={() => router.push(buildPath('/shop'))}
|
|
35
37
|
/>
|
|
36
38
|
</div>
|
|
37
39
|
</div>
|
|
@@ -145,14 +147,14 @@ export function CartScreen() {
|
|
|
145
147
|
<Button
|
|
146
148
|
size="lg"
|
|
147
149
|
className="mt-6 w-full"
|
|
148
|
-
onClick={() => router.push('/checkout')}
|
|
150
|
+
onClick={() => router.push(buildPath('/checkout'))}
|
|
149
151
|
>
|
|
150
152
|
Secure checkout
|
|
151
153
|
<ArrowRight className="h-5 w-5" />
|
|
152
154
|
</Button>
|
|
153
155
|
<button
|
|
154
156
|
type="button"
|
|
155
|
-
onClick={() => router.push('/shop')}
|
|
157
|
+
onClick={() => router.push(buildPath('/shop'))}
|
|
156
158
|
className="mt-4 w-full text-sm font-semibold text-primary-600 transition hover:text-primary-700"
|
|
157
159
|
>
|
|
158
160
|
Continue shopping
|
|
@@ -8,10 +8,12 @@ import { useCategories } from '@/hooks/useProducts';
|
|
|
8
8
|
import Image from 'next/image';
|
|
9
9
|
import Link from 'next/link';
|
|
10
10
|
import { useRouter } from 'next/navigation';
|
|
11
|
+
import { useBasePath } from '@/providers/BasePathProvider';
|
|
11
12
|
|
|
12
13
|
export function CategoriesScreen() {
|
|
13
14
|
const { categories, isLoading } = useCategories();
|
|
14
15
|
const router = useRouter();
|
|
16
|
+
const { buildPath } = useBasePath();
|
|
15
17
|
|
|
16
18
|
return (
|
|
17
19
|
<div className="min-h-screen bg-slate-50">
|
|
@@ -74,7 +76,7 @@ export function CategoriesScreen() {
|
|
|
74
76
|
{categories.map((category) => (
|
|
75
77
|
<Link
|
|
76
78
|
key={category.id}
|
|
77
|
-
href={`/shop?category=${category.name}`}
|
|
79
|
+
href={buildPath(`/shop?category=${category.name}`)}
|
|
78
80
|
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
81
|
>
|
|
80
82
|
<div className="relative aspect-square w-full overflow-hidden rounded-lg bg-gray-50 mb-3">
|
|
@@ -109,7 +111,7 @@ export function CategoriesScreen() {
|
|
|
109
111
|
description="There are currently no product categories available."
|
|
110
112
|
icon={Package}
|
|
111
113
|
actionLabel="Shop products"
|
|
112
|
-
onAction={() => router.push('/shop')}
|
|
114
|
+
onAction={() => router.push(buildPath('/shop'))}
|
|
113
115
|
/>
|
|
114
116
|
)}
|
|
115
117
|
</motion.div>
|
|
@@ -29,6 +29,7 @@ import { AXIOS_CONFIG } from '@/lib/Apis/wrapper';
|
|
|
29
29
|
import { Card } from '@/components/ui/Card';
|
|
30
30
|
import { AddressFormModal } from '@/components/AddressFormModal';
|
|
31
31
|
import { Edit3, Trash2 } from 'lucide-react';
|
|
32
|
+
import { useBasePath } from '@/providers/BasePathProvider';
|
|
32
33
|
|
|
33
34
|
import { addressSchema } from '@/lib/validations/address';
|
|
34
35
|
import Image from 'next/image';
|
|
@@ -77,6 +78,7 @@ export function CheckoutScreen() {
|
|
|
77
78
|
const router = useRouter();
|
|
78
79
|
const { cart, clearCart } = useCart();
|
|
79
80
|
const { isAuthenticated, user } = useAuth();
|
|
81
|
+
const { buildPath } = useBasePath();
|
|
80
82
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
81
83
|
const [isDelivery, setIsDelivery] = useState(true); // delivery or pickup
|
|
82
84
|
const [paymentMethod, setPaymentMethod] = useState('Card');
|
|
@@ -282,7 +284,7 @@ export function CheckoutScreen() {
|
|
|
282
284
|
setError(null);
|
|
283
285
|
if (!isAuthenticated) {
|
|
284
286
|
toast.error('Please login to continue');
|
|
285
|
-
setTimeout(() => router.push('/login?redirect=/checkout'), 50);
|
|
287
|
+
setTimeout(() => router.push(buildPath('/login?redirect=/checkout')), 50);
|
|
286
288
|
return;
|
|
287
289
|
}
|
|
288
290
|
if (!cart || cart?.cartBody?.items?.length === 0 || !cart?.cartBody?.items) {
|
|
@@ -387,11 +389,11 @@ export function CheckoutScreen() {
|
|
|
387
389
|
}
|
|
388
390
|
toast.success('Order placed successfully!');
|
|
389
391
|
await clearCart();
|
|
390
|
-
router.push(`/orders/${response.data?.id}`);
|
|
392
|
+
router.push(buildPath(`/orders/${response.data?.id}`));
|
|
391
393
|
} else {
|
|
392
394
|
toast.success('Order placed successfully!');
|
|
393
395
|
await clearCart();
|
|
394
|
-
router.push(`/orders/${response.data?.id}`);
|
|
396
|
+
router.push(buildPath(`/orders/${response.data?.id}`));
|
|
395
397
|
}
|
|
396
398
|
} catch (err: any) {
|
|
397
399
|
const msg = err?.message || (err?.response?.data?.message) || 'Failed to place order';
|
|
@@ -403,7 +405,7 @@ export function CheckoutScreen() {
|
|
|
403
405
|
};
|
|
404
406
|
|
|
405
407
|
if (!cart || cart?.cartBody?.items?.length === 0 || !cart?.cartBody?.items) {
|
|
406
|
-
router.push('/cart');
|
|
408
|
+
router.push(buildPath('/cart'));
|
|
407
409
|
return null;
|
|
408
410
|
}
|
|
409
411
|
const subtotal = cart.total;
|
|
@@ -15,10 +15,12 @@ import {
|
|
|
15
15
|
Truck,
|
|
16
16
|
Warehouse,
|
|
17
17
|
} from 'lucide-react';
|
|
18
|
+
import { useBasePath } from '@/providers/BasePathProvider';
|
|
18
19
|
|
|
19
20
|
export function CurrentOrdersScreen() {
|
|
20
21
|
const router = useRouter();
|
|
21
22
|
const { orders, isLoading } = useCurrentOrders();
|
|
23
|
+
const { buildPath } = useBasePath();
|
|
22
24
|
|
|
23
25
|
const hasOrders = orders.length > 0;
|
|
24
26
|
|
|
@@ -54,7 +56,7 @@ export function CurrentOrdersScreen() {
|
|
|
54
56
|
</p>
|
|
55
57
|
<div className="mt-4 space-y-3 text-sm text-white/80">
|
|
56
58
|
<Link
|
|
57
|
-
href=
|
|
59
|
+
href={buildPath('/orders')}
|
|
58
60
|
className="flex items-center justify-between rounded-2xl bg-white/10 px-4 py-3 transition hover:bg-white/20"
|
|
59
61
|
>
|
|
60
62
|
<span className="font-semibold text-white">View order history</span>
|
|
@@ -97,7 +99,7 @@ export function CurrentOrdersScreen() {
|
|
|
97
99
|
title="No active orders"
|
|
98
100
|
description="Start a new order to see live preparation, packing, and delivery updates here."
|
|
99
101
|
actionLabel="Shop wellness essentials"
|
|
100
|
-
onAction={() => router.push('/shop')}
|
|
102
|
+
onAction={() => router.push(buildPath('/shop'))}
|
|
101
103
|
/>
|
|
102
104
|
</div>
|
|
103
105
|
)}
|
|
@@ -68,7 +68,7 @@ export default function HomeScreen() {
|
|
|
68
68
|
<Button
|
|
69
69
|
size="lg"
|
|
70
70
|
variant="secondary"
|
|
71
|
-
onClick={() => router.push('/shop')}
|
|
71
|
+
onClick={() => router.push(buildPath('/shop'))}
|
|
72
72
|
className="text-lg px-8 py-4"
|
|
73
73
|
>
|
|
74
74
|
Shop Now
|
|
@@ -77,7 +77,7 @@ export default function HomeScreen() {
|
|
|
77
77
|
<Button
|
|
78
78
|
size="lg"
|
|
79
79
|
variant="outline"
|
|
80
|
-
onClick={() => router.push('/about')}
|
|
80
|
+
onClick={() => router.push(buildPath('/about'))}
|
|
81
81
|
className="text-lg px-8 py-4 bg-white/10 backdrop-blur-sm border-white text-white hover:bg-white/20"
|
|
82
82
|
>
|
|
83
83
|
Learn More
|
|
@@ -169,7 +169,7 @@ export default function HomeScreen() {
|
|
|
169
169
|
<div className="text-center mt-12">
|
|
170
170
|
<Button
|
|
171
171
|
size="lg"
|
|
172
|
-
onClick={() => router.push('/shop')}
|
|
172
|
+
onClick={() => router.push(buildPath('/shop'))}
|
|
173
173
|
>
|
|
174
174
|
View All Products
|
|
175
175
|
<ArrowRight className="w-5 h-5" />
|
|
@@ -196,7 +196,7 @@ export default function HomeScreen() {
|
|
|
196
196
|
<Button
|
|
197
197
|
size="lg"
|
|
198
198
|
variant="secondary"
|
|
199
|
-
onClick={() => router.push('/register')}
|
|
199
|
+
onClick={() => router.push(buildPath('/register'))}
|
|
200
200
|
className="text-lg px-8 py-4"
|
|
201
201
|
>
|
|
202
202
|
Create an Account
|
|
@@ -20,6 +20,7 @@ import { Input } from '@/components/ui/Input';
|
|
|
20
20
|
import { Button } from '@/components/ui/Button';
|
|
21
21
|
import { useAuth } from '@/providers/AuthProvider';
|
|
22
22
|
import { toast } from 'sonner';
|
|
23
|
+
import { useBasePath } from '@/providers/BasePathProvider';
|
|
23
24
|
|
|
24
25
|
const loginSchema = z.object({
|
|
25
26
|
email: z.string().email('Enter a valid email address'),
|
|
@@ -30,8 +31,9 @@ type LoginFormData = z.infer<typeof loginSchema>;
|
|
|
30
31
|
|
|
31
32
|
export function LoginScreen() {
|
|
32
33
|
const router = useRouter();
|
|
34
|
+
const { buildPath } = useBasePath();
|
|
33
35
|
const searchParams = useSearchParams();
|
|
34
|
-
const redirectUrl = searchParams?.get('redirect') || '/';
|
|
36
|
+
const redirectUrl = searchParams?.get('redirect') || buildPath('/');
|
|
35
37
|
const { login } = useAuth();
|
|
36
38
|
const [showPassword, setShowPassword] = useState(false);
|
|
37
39
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
@@ -11,6 +11,7 @@ import { useOrders } from '@/hooks/useOrders';
|
|
|
11
11
|
import { FilterChips } from '@/components/FilterChips';
|
|
12
12
|
import { useRouter } from 'next/navigation';
|
|
13
13
|
import { ManualOrderDTOOrderStatusEnum, PaymentPaymentStatusEnum } from '@/lib/Apis';
|
|
14
|
+
import { useBasePath } from '@/providers/BasePathProvider';
|
|
14
15
|
|
|
15
16
|
const STATUS_FILTERS = ['All', ...Object.values(ManualOrderDTOOrderStatusEnum)];
|
|
16
17
|
const PAYMENT_FILTERS = ['All', ...Object.values(PaymentPaymentStatusEnum)];
|
|
@@ -19,6 +20,7 @@ type PaymentFilterType = (typeof PAYMENT_FILTERS)[number];
|
|
|
19
20
|
|
|
20
21
|
export function OrdersScreen() {
|
|
21
22
|
const router = useRouter();
|
|
23
|
+
const { buildPath } = useBasePath();
|
|
22
24
|
const [page, setPage] = useState(1);
|
|
23
25
|
const [selectedFilter, setSelectedFilter] = useState<StatusFilterType>('All');
|
|
24
26
|
const [selectedPaymentFilter, setSelectedPaymentFilter] = useState<PaymentFilterType>('All');
|
|
@@ -160,7 +162,7 @@ export function OrdersScreen() {
|
|
|
160
162
|
title="No orders found for these filters"
|
|
161
163
|
description="Adjust the status or payment filters, or browse the shop for new essentials."
|
|
162
164
|
actionLabel="Shop products"
|
|
163
|
-
onAction={() => router.push('/shop')}
|
|
165
|
+
onAction={() => router.push(buildPath('/shop'))}
|
|
164
166
|
/>
|
|
165
167
|
)}
|
|
166
168
|
</div>
|
|
@@ -32,6 +32,7 @@ import { ProductVariantsApi } from '@/lib/Apis/apis/product-variants-api';
|
|
|
32
32
|
import { AXIOS_CONFIG } from '@/lib/Apis/wrapper';
|
|
33
33
|
import { useWishlist } from '@/providers/WishlistProvider';
|
|
34
34
|
import { ProductsApi, ProductVariant, ProductVariantInventoryStatusEnum } from '@/lib/Apis';
|
|
35
|
+
import { useBasePath } from '@/providers/BasePathProvider';
|
|
35
36
|
|
|
36
37
|
const safeFormatDate = (date?: Date | string, format: 'long' | 'short' = 'long'): string => {
|
|
37
38
|
if (!date) return 'N/A';
|
|
@@ -51,6 +52,7 @@ interface ProductDetailScreenProps {
|
|
|
51
52
|
export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
|
|
52
53
|
// Hooks and state at the top level
|
|
53
54
|
const router = useRouter();
|
|
55
|
+
const { buildPath } = useBasePath();
|
|
54
56
|
const { product: productData, isLoading } = useProduct(productId);
|
|
55
57
|
const { addToCart } = useCart();
|
|
56
58
|
|
|
@@ -330,17 +332,17 @@ export function ProductDetailScreen({ productId }: ProductDetailScreenProps) {
|
|
|
330
332
|
<Button
|
|
331
333
|
variant="ghost"
|
|
332
334
|
className="text-white hover:bg-white/10"
|
|
333
|
-
onClick={() => router.push('/shop')}
|
|
335
|
+
onClick={() => router.push(buildPath('/shop'))}
|
|
334
336
|
>
|
|
335
337
|
<ArrowLeft className="h-5 w-5" />
|
|
336
338
|
Continue shopping
|
|
337
339
|
</Button>
|
|
338
340
|
<div className="hidden items-center gap-3 text-sm text-white/80 md:flex">
|
|
339
|
-
<Link href=
|
|
341
|
+
<Link href={buildPath('/')} className="transition hover:text-white">
|
|
340
342
|
Home
|
|
341
343
|
</Link>
|
|
342
344
|
<ChevronRight className="h-4 w-4" />
|
|
343
|
-
<Link href=
|
|
345
|
+
<Link href={buildPath('/shop')} className="transition hover:text-white">
|
|
344
346
|
Shop
|
|
345
347
|
</Link>
|
|
346
348
|
<ChevronRight className="h-4 w-4" />
|