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
@@ -14,7 +14,6 @@ import {
14
14
  Trash2,
15
15
  } from 'lucide-react';
16
16
  import { useRouter } from 'next/navigation';
17
- import { toast } from 'sonner';
18
17
  import { ProductCard } from '@/components/ProductCard';
19
18
  import { Button } from '@/components/ui/Button';
20
19
  import { useWishlist } from '@/providers/WishlistProvider';
@@ -24,6 +23,7 @@ import Image from 'next/image';
24
23
  import { formatPrice } from '@/lib/utils/format';
25
24
  import { ExtendedProductDTO } from '@/lib/Apis';
26
25
  import { useBasePath } from '@/providers/BasePathProvider';
26
+ import { useNotification } from '@/providers/NotificationProvider';
27
27
 
28
28
  type SortOption = 'featured' | 'price-low' | 'price-high' | 'name' | 'availability';
29
29
 
@@ -46,7 +46,7 @@ interface InsightCardProps {
46
46
 
47
47
  export default function WishlistScreen() {
48
48
  const router = useRouter();
49
- const { buildPath } = useBasePath();
49
+ const { buildPath } = useBasePath();
50
50
  const { isAuthenticated } = useAuth() || {};
51
51
  const {
52
52
  products: wishlistItems,
@@ -55,6 +55,7 @@ export default function WishlistScreen() {
55
55
  clearWishlist,
56
56
  refreshWishlist,
57
57
  } = useWishlist();
58
+ const notification = useNotification();
58
59
 
59
60
  const wishlistCount = getWishlistCount?.() ?? 0;
60
61
  const { products: wishlistProducts, isLoading, error } = useWishlistProducts(
@@ -67,9 +68,12 @@ export default function WishlistScreen() {
67
68
 
68
69
  useEffect(() => {
69
70
  if (error) {
70
- toast.error('We had trouble loading your saved products. Please try again.');
71
+ notification.error(
72
+ 'Could not load wishlist',
73
+ 'We had trouble loading your saved products. Please refresh the page or try again shortly.'
74
+ );
71
75
  }
72
- }, [error]);
76
+ }, [error, notification]);
73
77
 
74
78
  const handleRemoveFromWishlist = async (productId: string) => {
75
79
  try {
@@ -156,19 +160,19 @@ export default function WishlistScreen() {
156
160
  <div className="max-w-lg text-center space-y-6">
157
161
  <div className="flex justify-center">
158
162
  <div className="rounded-full bg-gray-100 p-6">
159
- <Heart className="h-12 w-12 text-gray-400" />
163
+ <Heart className="h-12 w-12 text-secondary" />
160
164
  </div>
161
165
  </div>
162
166
  <div className="space-y-2">
163
- <h2 className="text-2xl font-bold text-slate-900">Sign in to see your favourites</h2>
164
- <p className="text-gray-500">
167
+ <h2 className="text-2xl font-bold text-secondary">Sign in to see your favourites</h2>
168
+ <p className="text-muted">
165
169
  Create your curated shelf of products and we&apos;ll keep them ready whenever you return.
166
170
  </p>
167
171
  </div>
168
172
  <button
169
173
  type="button"
170
174
  onClick={() => router.push(buildPath('/login'))}
171
- className="rounded-full border-2 border-primary-500 bg-primary-500 hover:bg-primary-600 text-white px-6 py-3 text-sm font-medium transition-colors"
175
+ className="rounded-xl border-2 border-primary bg-primary text-white px-6 py-3 text-sm font-medium transition-colors hover:opacity-80"
172
176
  >
173
177
  Sign In
174
178
  </button>
@@ -179,8 +183,8 @@ export default function WishlistScreen() {
179
183
  {isAuthenticated && (
180
184
  <>
181
185
  <div className="mb-6">
182
- <h1 className="text-2xl font-bold text-slate-900">Wishlist</h1>
183
- <p className="text-sm text-gray-500 mt-1">
186
+ <h1 className="text-2xl font-bold text-secondary">Wishlist</h1>
187
+ <p className="text-sm text-muted mt-1">
184
188
  {wishlistCount} {wishlistCount === 1 ? 'item' : 'items'} saved
185
189
  {wishlistCount > 0 && ` • Total value: ${formatPrice(totalValue)}`}
186
190
  </p>
@@ -189,243 +193,241 @@ export default function WishlistScreen() {
189
193
  <div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between mb-6">
190
194
  <div className="space-y-1">
191
195
  {onlyInStock && (
192
- <p className="text-sm text-gray-500">
196
+ <p className="text-sm text-muted">
193
197
  Showing items ready to ship
194
198
  </p>
195
199
  )}
196
200
  </div>
197
201
 
198
- <div className="flex flex-wrap items-center gap-3">
199
- <label className="inline-flex cursor-pointer items-center gap-2 rounded-full border border-slate-200 bg-slate-50 px-3 py-1.5 text-sm font-medium text-slate-600 transition hover:border-primary-200 hover:text-primary-600">
200
- <input
201
- type="checkbox"
202
- checked={onlyInStock}
203
- onChange={(event) => setOnlyInStock(event.target.checked)}
204
- className="h-4 w-4 rounded border-slate-300 text-primary-600 focus:ring-primary-500"
205
- />
206
- Only show in-stock
207
- </label>
202
+ <div className="flex flex-wrap items-center gap-3">
203
+ <label className="inline-flex cursor-pointer items-center gap-2 rounded-full border border-slate-200 bg-slate-50 px-3 py-1.5 text-sm font-medium text-slate-600 transition hover:border-primary hover:text-secondary">
204
+ <input
205
+ type="checkbox"
206
+ checked={onlyInStock}
207
+ onChange={(event) => setOnlyInStock(event.target.checked)}
208
+ className="h-4 w-4 rounded border-slate-300 text-secondary focus:ring-secondary"
209
+ />
210
+ Only show in-stock
211
+ </label>
208
212
 
209
- <div className="flex items-center gap-2 rounded-full border border-slate-200 bg-slate-50 px-3 py-1.5 text-sm text-slate-600">
210
- <span>Sort</span>
211
- <select
212
- value={sortOption}
213
- onChange={(event) => setSortOption(event.target.value as SortOption)}
214
- className="bg-transparent text-sm font-medium text-slate-700 outline-none"
215
- >
216
- {SORT_OPTIONS.map((option) => (
217
- <option key={option.value} value={option.value}>
218
- {option.label}
219
- </option>
220
- ))}
221
- </select>
222
- </div>
213
+ <div className="flex items-center gap-2 rounded-full border border-slate-200 bg-slate-50 px-3 py-1.5 text-sm text-slate-600">
214
+ <span>Sort</span>
215
+ <select
216
+ value={sortOption}
217
+ onChange={(event) => setSortOption(event.target.value as SortOption)}
218
+ className="bg-transparent text-sm font-medium text-slate-700 outline-none"
219
+ >
220
+ {SORT_OPTIONS.map((option) => (
221
+ <option key={option.value} value={option.value}>
222
+ {option.label}
223
+ </option>
224
+ ))}
225
+ </select>
226
+ </div>
223
227
 
224
- <div className="flex overflow-hidden rounded-full border border-slate-200 bg-slate-50">
225
- <button
226
- type="button"
227
- onClick={() => setViewMode('grid')}
228
- className={`flex items-center gap-1 px-3 py-1.5 text-sm font-medium transition ${viewMode === 'grid'
229
- ? 'bg-primary-600 text-white shadow-lg shadow-primary-500/30'
230
- : 'text-slate-600 hover:bg-white'
231
- }`}
232
- >
233
- <Grid className="h-4 w-4" />
234
- Grid
235
- </button>
228
+ <div className="flex overflow-hidden rounded-full border border-slate-200 bg-gray-100 p-1">
229
+ <button
230
+ type="button"
231
+ onClick={() => setViewMode('grid')}
232
+ className={`flex items-center gap-1 px-3 py-1.5 text-sm font-medium transition rounded-full ${viewMode === 'grid'
233
+ ? 'bg-white text-primary shadow-md'
234
+ : 'text-slate-600 hover:bg-white'
235
+ }`}
236
+ >
237
+ <Grid className="h-4 w-4" />
238
+ </button>
239
+ <button
240
+ type="button"
241
+ onClick={() => setViewMode('list')}
242
+ className={`flex items-center gap-1 px-3 py-1.5 text-sm font-medium transition rounded-full ${viewMode === 'list'
243
+ ? 'bg-white text-primary shadow-md'
244
+ : 'text-slate-600 hover:bg-white'
245
+ }`}
246
+ >
247
+ <List className="h-4 w-4" />
248
+ </button>
249
+ </div>
250
+
251
+ {wishlistCount > 0 && (
252
+ <Button
253
+ variant="ghost"
254
+ className="text-sm font-semibold text-slate-500 hover:text-red-500"
255
+ onClick={handleClearWishlist}
256
+ >
257
+ <Trash2 className="h-4 w-4" />
258
+ Clear all
259
+ </Button>
260
+ )}
261
+ </div>
262
+ </div>
263
+
264
+ {isLoading && (
265
+ <div className="grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
266
+ {Array.from({ length: Math.min(wishlistCount || 3, 6) }).map((_, index) => (
267
+ <div
268
+ key={index}
269
+ className="h-72 animate-pulse rounded-2xl border border-slate-200 bg-slate-100"
270
+ />
271
+ ))}
272
+ </div>
273
+ )}
274
+
275
+ {!isLoading && wishlistCount === 0 && (
276
+ <div className="flex min-h-[30vh] items-center justify-center">
277
+ <div className="text-center space-y-6 max-w-md">
278
+ <div className="flex justify-center">
279
+ <div className="rounded-full bg-gray-100 p-6">
280
+ <Heart className="h-12 w-12 text-secondary" />
281
+ </div>
282
+ </div>
283
+ <div className="space-y-2">
284
+ <h2 className="text-2xl font-bold text-secondary">Your wishlist is empty</h2>
285
+ <p className="text-muted">
286
+ Start adding products to your wishlist to see them here.
287
+ </p>
288
+ </div>
289
+ <div className="flex flex-wrap justify-center gap-3">
236
290
  <button
237
291
  type="button"
238
- onClick={() => setViewMode('list')}
239
- className={`flex items-center gap-1 px-3 py-1.5 text-sm font-medium transition ${viewMode === 'list'
240
- ? 'bg-primary-600 text-white shadow-lg shadow-primary-500/30'
241
- : 'text-slate-600 hover:bg-white'
242
- }`}
292
+ onClick={() => router.push(buildPath('/shop'))}
293
+ className="rounded-xl border-2 border-primary bg-primary text-white px-6 py-3 text-sm font-medium transition-colors flex items-center justify-center gap-2 hover:opacity-80"
243
294
  >
244
- <List className="h-4 w-4" />
245
- List
295
+ Discover products
246
296
  </button>
247
297
  </div>
248
-
249
- {wishlistCount > 0 && (
250
- <Button
251
- variant="ghost"
252
- className="text-sm font-semibold text-slate-500 hover:text-red-500"
253
- onClick={handleClearWishlist}
254
- >
255
- <Trash2 className="h-4 w-4" />
256
- Clear all
257
- </Button>
258
- )}
259
298
  </div>
260
299
  </div>
300
+ )}
261
301
 
262
- {isLoading && (
263
- <div className="grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
264
- {Array.from({ length: Math.min(wishlistCount || 3, 6) }).map((_, index) => (
265
- <div
266
- key={index}
267
- className="h-72 animate-pulse rounded-2xl border border-slate-200 bg-slate-100"
268
- />
269
- ))}
270
- </div>
271
- )}
272
-
273
- {!isLoading && wishlistCount === 0 && (
274
- <div className="flex min-h-[30vh] items-center justify-center">
275
- <div className="text-center space-y-6 max-w-md">
276
- <div className="flex justify-center">
277
- <div className="rounded-full bg-gray-100 p-6">
278
- <Heart className="h-12 w-12 text-gray-400" />
279
- </div>
280
- </div>
281
- <div className="space-y-2">
282
- <h2 className="text-2xl font-bold text-slate-900">Your wishlist is empty</h2>
283
- <p className="text-gray-500">
284
- Start adding products to your wishlist to see them here.
285
- </p>
286
- </div>
287
- <div className="flex flex-wrap justify-center gap-3">
288
- <button
289
- type="button"
290
- onClick={() => router.push(buildPath('/shop'))}
291
- className="rounded-full border-2 border-primary-500 bg-primary-500 hover:bg-primary-600 text-white px-6 py-3 text-sm font-medium transition-colors flex items-center justify-center gap-2"
292
- >
293
- Discover products
294
- </button>
295
- </div>
296
- </div>
297
- </div>
298
- )}
299
-
300
302
 
301
- {!isLoading && processedProducts.length > 0 && (
302
- <>
303
- {viewMode === 'grid' ? (
304
- <motion.div
305
- layout
306
- className="grid grid-cols-1 gap-5 sm:grid-cols-2 xl:grid-cols-3"
307
- >
308
- <AnimatePresence>
309
- {processedProducts.map((product) => (
310
- <motion.div
311
- key={product.id}
312
- layout
313
- initial={{ opacity: 0, y: 20 }}
314
- animate={{ opacity: 1, y: 0 }}
315
- exit={{ opacity: 0, y: -20 }}
316
- transition={{ duration: 0.2 }}
317
- >
318
- <ProductCard
319
- product={product as ExtendedProductDTO}
320
- onClickProduct={(p) => router.push(buildPath(`/products/${p.id}`))}
321
- onFavorite={() => handleRemoveFromWishlist(product.id)}
322
- isFavorited
323
- />
324
- </motion.div>
325
- ))}
326
- </AnimatePresence>
327
- </motion.div>
328
- ) : (
329
- <motion.div layout className="space-y-4">
330
- <AnimatePresence>
331
- {processedProducts.map((product) => (
332
- <motion.div
333
- key={product.id}
334
- layout
335
- initial={{ opacity: 0, y: 20 }}
336
- animate={{ opacity: 1, y: 0 }}
337
- exit={{ opacity: 0, y: -20 }}
338
- transition={{ duration: 0.2 }}
339
- className="flex flex-col gap-4 rounded-2xl border border-slate-100 bg-slate-50 p-4 shadow-sm shadow-primary-50 sm:flex-row sm:items-center"
340
- >
341
- <div className="relative h-28 w-full overflow-hidden rounded-2xl bg-white sm:w-40">
342
- <Image
343
- fill
344
- src={product.productMedia?.[0]?.file || '/placeholder-product.jpg'}
345
- alt={product.name || 'Wishlist item'}
346
- className="h-full w-full object-cover"
303
+ {!isLoading && processedProducts.length > 0 && (
304
+ <>
305
+ {viewMode === 'grid' ? (
306
+ <motion.div
307
+ layout
308
+ className="grid grid-cols-1 gap-5 sm:grid-cols-2 xl:grid-cols-3"
309
+ >
310
+ <AnimatePresence>
311
+ {processedProducts.map((product) => (
312
+ <motion.div
313
+ key={product.id}
314
+ layout
315
+ initial={{ opacity: 0, y: 20 }}
316
+ animate={{ opacity: 1, y: 0 }}
317
+ exit={{ opacity: 0, y: -20 }}
318
+ transition={{ duration: 0.2 }}
319
+ >
320
+ <ProductCard
321
+ product={product as ExtendedProductDTO}
322
+ onClickProduct={(p) => router.push(buildPath(`/products/${p.id}`))}
323
+ onFavorite={() => handleRemoveFromWishlist(product.id)}
324
+ isFavorited
347
325
  />
348
- </div>
349
- <div className="flex flex-1 flex-col gap-2">
350
- <div className="flex flex-wrap items-center justify-between gap-3">
351
- <div>
352
- <h3 className="text-lg font-semibold text-slate-900">
353
- {product.name}
354
- </h3>
355
- <p className="text-sm text-slate-500">
356
- {product.parentCategories?.map((category) => category?.name).join(', ') || 'General wellness'}
357
- </p>
358
- </div>
359
- <div className="text-right">
360
- <p className="text-lg font-bold text-primary-600">
361
- {formatPrice(product.finalPrice ?? 0)}
362
- </p>
363
- {product.isDiscounted && (
364
- <p className="text-xs text-emerald-500">
365
- You save {formatPrice(Math.max((product.priceBeforeDiscount ?? 0) - (product.finalPrice ?? 0), 0))}
326
+ </motion.div>
327
+ ))}
328
+ </AnimatePresence>
329
+ </motion.div>
330
+ ) : (
331
+ <motion.div layout className="space-y-4">
332
+ <AnimatePresence>
333
+ {processedProducts.map((product) => (
334
+ <motion.div
335
+ key={product.id}
336
+ layout
337
+ initial={{ opacity: 0, y: 20 }}
338
+ animate={{ opacity: 1, y: 0 }}
339
+ exit={{ opacity: 0, y: -20 }}
340
+ transition={{ duration: 0.2 }}
341
+ className="flex flex-col gap-4 rounded-2xl border border-slate-100 bg-slate-50 p-4 shadow-sm shadow-primary-50 sm:flex-row sm:items-center"
342
+ >
343
+ <div className="relative h-28 w-full overflow-hidden rounded-2xl bg-white sm:w-40">
344
+ <Image
345
+ fill
346
+ src={product.productMedia?.[0]?.file || '/placeholder-product.jpg'}
347
+ alt={product.name || 'Wishlist item'}
348
+ className="h-full w-full object-cover"
349
+ />
350
+ </div>
351
+ <div className="flex flex-1 flex-col gap-2">
352
+ <div className="flex flex-wrap items-center justify-between gap-3">
353
+ <div>
354
+ <h3 className="text-lg font-semibold text-secondary">
355
+ {product.name}
356
+ </h3>
357
+ <p className="text-sm text-muted">
358
+ {product.parentCategories?.map((category) => category?.name).join(', ') || 'General wellness'}
366
359
  </p>
367
- )}
360
+ </div>
361
+ <div className="text-right">
362
+ <p className="text-lg font-bold text-primary">
363
+ {formatPrice(product.finalPrice ?? 0)}
364
+ </p>
365
+ {product.isDiscounted && (
366
+ <p className="text-xs text-emerald-500">
367
+ You save {formatPrice(Math.max((product.priceBeforeDiscount ?? 0) - (product.finalPrice ?? 0), 0))}
368
+ </p>
369
+ )}
370
+ </div>
368
371
  </div>
369
- </div>
370
- <div className="flex flex-wrap items-center gap-3 text-xs text-slate-500">
371
- <span className={`inline-flex items-center gap-1 rounded-full px-2.5 py-1 font-medium ${product.inventoryCount > 0 ? 'bg-emerald-100 text-emerald-700' : 'bg-rose-100 text-rose-700'}`}>
372
- <Package className="h-3.5 w-3.5" />
373
- {product.inventoryCount > 0 ? 'In stock' : 'Backordered'}
374
- </span>
375
- {product.totalSold > 0 && (
376
- <span className="inline-flex items-center gap-1 rounded-full bg-slate-200 px-2.5 py-1 font-medium text-slate-700">
377
- <Sparkles className="h-3.5 w-3.5" />
378
- {product.totalSold}+ purchased
372
+ <div className="flex flex-wrap items-center gap-3 text-xs text-slate-500">
373
+ <span className={`inline-flex items-center gap-1 rounded-full px-2.5 py-1 font-medium ${product.inventoryCount > 0 ? 'bg-emerald-100 text-emerald-700' : 'bg-rose-100 text-rose-700'}`}>
374
+ <Package className="h-3.5 w-3.5" />
375
+ {product.inventoryCount > 0 ? 'In stock' : 'Backordered'}
379
376
  </span>
380
- )}
381
- </div>
382
- <div className="flex flex-wrap gap-2">
383
- <Button
384
- size="sm"
385
- onClick={() => router.push(buildPath(`/products/${product.id}`))}
386
- >
387
- View details
388
- </Button>
389
- <Button
390
- size="sm"
391
- variant="outline"
392
- onClick={() => handleRemoveFromWishlist(product.id)}
393
- className="text-primary-600"
394
- >
395
- Remove
396
- </Button>
377
+ {product.totalSold > 0 && (
378
+ <span className="inline-flex items-center gap-1 rounded-full bg-slate-200 px-2.5 py-1 font-medium text-slate-700">
379
+ <Sparkles className="h-3.5 w-3.5" />
380
+ {product.totalSold}+ purchased
381
+ </span>
382
+ )}
383
+ </div>
384
+ <div className="flex flex-wrap gap-2">
385
+ <Button
386
+ size="sm"
387
+ onClick={() => router.push(buildPath(`/products/${product.id}`))}
388
+ >
389
+ View details
390
+ </Button>
391
+ <Button
392
+ size="sm"
393
+ variant="outline"
394
+ onClick={() => handleRemoveFromWishlist(product.id)}
395
+ className="text-secondary"
396
+ >
397
+ Remove
398
+ </Button>
399
+ </div>
397
400
  </div>
398
- </div>
399
- </motion.div>
400
- ))}
401
- </AnimatePresence>
402
- </motion.div>
403
- )}
404
- </>
405
- )}
401
+ </motion.div>
402
+ ))}
403
+ </AnimatePresence>
404
+ </motion.div>
405
+ )}
406
+ </>
407
+ )}
406
408
 
407
- {isAuthenticated && emptyAfterFiltering && (
408
- <div className="flex flex-col items-center justify-center rounded-lg border border-dashed border-gray-200 bg-gray-50 p-12 text-center">
409
- <div className="flex h-16 w-16 items-center justify-center rounded-full bg-gray-200 text-gray-500">
410
- <Package className="h-8 w-8" />
411
- </div>
412
- <h3 className="mt-6 text-xl font-semibold text-slate-900">
413
- Nothing matches those filters
414
- </h3>
415
- <p className="mt-2 max-w-md text-sm text-gray-500">
416
- Try showing out-of-stock items or adjust your sort order to revisit everything you&apos;ve saved.
417
- </p>
418
- <button
419
- type="button"
420
- className="mt-6 rounded-full border-2 border-primary-300 bg-white px-6 py-3 text-sm font-medium text-slate-700 hover:bg-gray-50 transition-colors"
421
- onClick={() => setOnlyInStock(false)}
422
- >
423
- Show all saved products
424
- </button>
425
- </div>
409
+ {isAuthenticated && emptyAfterFiltering && (
410
+ <div className="flex flex-col items-center justify-center rounded-lg border border-dashed border-gray-200 bg-gray-50 p-12 text-center">
411
+ <div className="flex h-16 w-16 items-center justify-center rounded-full bg-gray-200 text-gray-500">
412
+ <Package className="h-8 w-8" />
413
+ </div>
414
+ <h3 className="mt-6 text-xl font-semibold text-secondary">
415
+ Nothing matches those filters
416
+ </h3>
417
+ <p className="mt-2 max-w-md text-sm text-muted">
418
+ Try showing out-of-stock items or adjust your sort order to revisit everything you&apos;ve saved.
419
+ </p>
420
+ <button
421
+ type="button"
422
+ className="mt-6 rounded-xl border-2 border-primary bg-white px-6 py-3 text-sm font-medium text-secondary hover:opacity-80 transition-colors"
423
+ onClick={() => setOnlyInStock(false)}
424
+ >
425
+ Show all saved products
426
+ </button>
427
+ </div>
428
+ )}
429
+ </>
426
430
  )}
427
- </>
428
- )}
429
431
  </motion.div>
430
432
  </div>
431
433
  </div>
@@ -3,6 +3,15 @@
3
3
  @tailwind utilities;
4
4
 
5
5
  @layer base {
6
+ :root {
7
+ --color-primary: #5B9BD5;
8
+ --color-primary-dark: #4a8ac4;
9
+ --color-secondary: #2B4B7C;
10
+ --color-accent: #E67E50;
11
+ --color-accent-dark: #d66f45;
12
+ --color-text-muted: #676c80;
13
+ }
14
+
6
15
  * {
7
16
  @apply border-gray-200;
8
17
  }