hey-pharmacist-ecommerce 1.1.28 → 1.1.29

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 (60) hide show
  1. package/dist/index.d.mts +344 -640
  2. package/dist/index.d.ts +344 -640
  3. package/dist/index.js +1807 -838
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +1807 -840
  6. package/dist/index.mjs.map +1 -1
  7. package/package.json +1 -1
  8. package/src/components/AccountOrdersTab.tsx +1 -1
  9. package/src/components/AccountSettingsTab.tsx +88 -6
  10. package/src/components/CartItem.tsx +1 -1
  11. package/src/components/Header.tsx +8 -2
  12. package/src/components/OrderCard.tsx +4 -4
  13. package/src/components/ProductCard.tsx +59 -42
  14. package/src/components/QuickViewModal.tsx +13 -13
  15. package/src/hooks/useAddresses.ts +4 -1
  16. package/src/hooks/usePaymentMethods.ts +26 -31
  17. package/src/hooks/useProducts.ts +63 -64
  18. package/src/hooks/useWishlistProducts.ts +4 -5
  19. package/src/index.ts +2 -0
  20. package/src/lib/Apis/api.ts +0 -1
  21. package/src/lib/Apis/apis/auth-api.ts +18 -29
  22. package/src/lib/Apis/apis/products-api.ts +845 -405
  23. package/src/lib/Apis/models/category-populated.ts +0 -12
  24. package/src/lib/Apis/models/category-sub-category-populated.ts +2 -2
  25. package/src/lib/Apis/models/category.ts +0 -18
  26. package/src/lib/Apis/models/{table-cell-dto.ts → change-password-dto.ts} +6 -6
  27. package/src/lib/Apis/models/create-product-dto.ts +30 -23
  28. package/src/lib/Apis/models/create-sub-category-dto.ts +6 -0
  29. package/src/lib/Apis/models/create-variant-dto.ts +29 -29
  30. package/src/lib/Apis/models/index.ts +5 -7
  31. package/src/lib/Apis/models/paginated-products-dto.ts +6 -6
  32. package/src/lib/Apis/models/product-summary.ts +69 -0
  33. package/src/lib/Apis/models/product-variant.ts +34 -65
  34. package/src/lib/Apis/models/product.ts +138 -0
  35. package/src/lib/Apis/models/products-insights-dto.ts +12 -0
  36. package/src/lib/Apis/models/single-product-media.ts +0 -12
  37. package/src/lib/Apis/models/sub-category.ts +6 -12
  38. package/src/lib/Apis/models/update-product-dto.ts +30 -19
  39. package/src/lib/Apis/models/update-sub-category-dto.ts +6 -0
  40. package/src/lib/Apis/models/{update-product-variant-dto.ts → update-variant-dto.ts} +51 -45
  41. package/src/lib/Apis/models/{shallow-parent-category-dto.ts → variant-id-inventory-body.ts} +5 -11
  42. package/src/lib/api-adapter/config.ts +53 -0
  43. package/src/lib/validations/address.ts +1 -1
  44. package/src/providers/FavoritesProvider.tsx +5 -5
  45. package/src/providers/WishlistProvider.tsx +4 -4
  46. package/src/screens/CartScreen.tsx +1 -1
  47. package/src/screens/ChangePasswordScreen.tsx +2 -6
  48. package/src/screens/CheckoutScreen.tsx +40 -11
  49. package/src/screens/ForgotPasswordScreen.tsx +153 -0
  50. package/src/screens/ProductDetailScreen.tsx +51 -60
  51. package/src/screens/RegisterScreen.tsx +31 -31
  52. package/src/screens/ResetPasswordScreen.tsx +202 -0
  53. package/src/screens/SearchResultsScreen.tsx +264 -26
  54. package/src/screens/ShopScreen.tsx +42 -45
  55. package/src/screens/WishlistScreen.tsx +35 -31
  56. package/src/lib/Apis/apis/product-variants-api.ts +0 -552
  57. package/src/lib/Apis/models/create-single-variant-product-dto.ts +0 -154
  58. package/src/lib/Apis/models/extended-product-dto.ts +0 -206
  59. package/src/lib/Apis/models/frequently-bought-product-dto.ts +0 -71
  60. package/src/lib/Apis/models/table-dto.ts +0 -34
@@ -83,3 +83,56 @@ export function clearAuthToken(): void {
83
83
  }
84
84
  }
85
85
 
86
+ /**
87
+ * Change user password
88
+ * Custom implementation to fix the auto-generated API which incorrectly sends passwords as headers
89
+ */
90
+ export async function changePassword(oldPassword: string, newPassword: string): Promise<void> {
91
+ if (!currentConfig) {
92
+ throw new Error('API adapter not initialized.');
93
+ }
94
+
95
+ const token = getAuthToken();
96
+ if (!token) {
97
+ throw new Error('User is not authenticated');
98
+ }
99
+
100
+ const url = `${currentConfig.apiBaseUrl}/auth/change-password`;
101
+
102
+ console.log('Change Password Request:', {
103
+ url,
104
+ storeId: currentConfig.storeId,
105
+ hasToken: !!token,
106
+ tokenPrefix: token.substring(0, 20) + '...',
107
+ });
108
+
109
+ const response = await fetch(url, {
110
+ method: 'POST',
111
+ headers: {
112
+ 'Content-Type': 'application/json',
113
+ 'X-Store-Key': currentConfig.storeId,
114
+ 'Authorization': `Bearer ${token}`,
115
+ },
116
+ body: JSON.stringify({
117
+ oldPassword,
118
+ newPassword,
119
+ }),
120
+ });
121
+
122
+ console.log('Change Password Response:', {
123
+ status: response.status,
124
+ statusText: response.statusText,
125
+ });
126
+
127
+ if (!response.ok) {
128
+ const errorData = await response.json().catch(() => ({}));
129
+ console.error('Change Password Error:', errorData);
130
+ throw {
131
+ response: {
132
+ data: errorData,
133
+ status: response.status,
134
+ },
135
+ };
136
+ }
137
+ }
138
+
@@ -8,7 +8,7 @@ export const addressSchema = z.object({
8
8
  state: z.string().min(2, 'State is required'),
9
9
  zip: z.string().min(4, 'ZIP code is required'),
10
10
  country: z.string().min(2, 'Country is required'),
11
- phone: z.string().min(10, 'Phone number is required'),
11
+ phone: z.string().min(10, 'Phone number must be at least 10 characters').optional().or(z.literal('')),
12
12
  });
13
13
 
14
14
  export type AddressFormData = z.infer<typeof addressSchema>;
@@ -1,14 +1,14 @@
1
1
  'use client';
2
2
 
3
- import { ExtendedProductDTO } from '@/lib/Apis';
3
+ import { Product } from '@/lib/Apis';
4
4
  import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
5
5
  import { useNotification } from './NotificationProvider';
6
6
 
7
7
  interface FavoritesContextType {
8
8
  favorites: string[];
9
9
  isFavorite: (productId: string) => boolean;
10
- toggleFavorite: (product: ExtendedProductDTO) => void;
11
- addToFavorites: (product: ExtendedProductDTO) => void;
10
+ toggleFavorite: (product: Product) => void;
11
+ addToFavorites: (product: Product) => void;
12
12
  removeFromFavorites: (productId: string) => void;
13
13
  }
14
14
 
@@ -53,7 +53,7 @@ export function FavoritesProvider({ children }: { children: ReactNode }) {
53
53
  return favorites.includes(productId);
54
54
  };
55
55
 
56
- const addToFavorites = (product: ExtendedProductDTO) => {
56
+ const addToFavorites = (product: Product) => {
57
57
  if (!favorites.includes(product.id)) {
58
58
  setFavorites(prev => [...prev, product.id]);
59
59
  notification.success(
@@ -71,7 +71,7 @@ export function FavoritesProvider({ children }: { children: ReactNode }) {
71
71
  );
72
72
  };
73
73
 
74
- const toggleFavorite = (product: ExtendedProductDTO) => {
74
+ const toggleFavorite = (product: Product) => {
75
75
  if (isFavorite(product.id)) {
76
76
  removeFromFavorites(product.id);
77
77
  } else {
@@ -5,12 +5,12 @@ import { WishlistApi } from '@/lib/Apis/apis/wishlist-api';
5
5
  import { AXIOS_CONFIG } from '@/lib/Apis/wrapper';
6
6
  import { useAuth } from './AuthProvider';
7
7
  import { useMemo } from 'react';
8
- import { ExtendedProductDTO, Wishlist } from '@/lib/Apis';
8
+ import { ProductsApi, Wishlist, Product } from '@/lib/Apis';
9
9
  import { useNotification } from './NotificationProvider';
10
10
 
11
11
 
12
12
  interface WishlistContextType extends Wishlist {
13
- addToWishlist: (product: ExtendedProductDTO) => Promise<void>;
13
+ addToWishlist: (product: Product) => Promise<void>;
14
14
  removeFromWishlist: (productId: string) => Promise<void>;
15
15
  isInWishlist: (productId: string) => boolean;
16
16
  getWishlistCount: () => number;
@@ -64,7 +64,7 @@ export function WishlistProvider({ children }: { children: ReactNode }) {
64
64
  error: error instanceof Error ? error.message : 'Failed to load wishlist',
65
65
  }));
66
66
  }
67
- // eslint-disable-next-line react-hooks/exhaustive-deps
67
+ // eslint-disable-next-line react-hooks/exhaustive-deps
68
68
  }, [isAuthenticated]);
69
69
 
70
70
 
@@ -72,7 +72,7 @@ export function WishlistProvider({ children }: { children: ReactNode }) {
72
72
  fetchWishlist();
73
73
  }, [fetchWishlist]);
74
74
 
75
- const addToWishlist = async (product: any) => {
75
+ const addToWishlist = async (product: Product) => {
76
76
  if (!isAuthenticated) {
77
77
  notification.error(
78
78
  'Sign-in required',
@@ -88,7 +88,7 @@ export function CartScreen() {
88
88
  );
89
89
  }
90
90
 
91
- const subtotal = cart.total;
91
+ const subtotal = cart.cartBody.items.reduce((total, item) => total + item.productVariantData.finalPrice * item.quantity, 0);
92
92
  const shipping = 0;
93
93
  const tax = 0;
94
94
  const total = subtotal + shipping + tax;
@@ -9,8 +9,7 @@ import { useRouter } from 'next/navigation';
9
9
  import { Lock, ShieldCheck } from 'lucide-react';
10
10
  import { Input } from '@/components/ui/Input';
11
11
  import { Button } from '@/components/ui/Button';
12
- import { AuthApi } from '@/lib/Apis/apis/auth-api';
13
- import { getApiConfiguration } from '@/lib/api-adapter/config';
12
+ import { changePassword } from '@/lib/api-adapter/config';
14
13
  import { useAuth } from '@/providers/AuthProvider';
15
14
  import { useBasePath } from '@/providers/BasePathProvider';
16
15
 
@@ -53,10 +52,7 @@ export function ChangePasswordScreen() {
53
52
  setIsSubmitting(true);
54
53
  setStatus(null);
55
54
  try {
56
- await new AuthApi(getApiConfiguration()).changePassword(
57
- data.currentPassword,
58
- data.newPassword
59
- );
55
+ await changePassword(data.currentPassword, data.newPassword);
60
56
  setStatus({ type: 'success', message: 'Password updated successfully' });
61
57
  setTimeout(() => router.push(buildPath('/account')), 600);
62
58
  } catch (error: any) {
@@ -116,13 +116,21 @@ export function CheckoutScreen() {
116
116
  sameAsShipping: true,
117
117
  shipping: {
118
118
  name: user ? `${user.firstname} ${user.lastname}` : '',
119
- phone: user?.phoneNumber || undefined,
119
+ phone: user?.phoneNumber || '',
120
120
  country: 'United States',
121
+ // street1: '',
122
+ // city: '',
123
+ // state: '',
124
+ // zip: '',
121
125
  },
122
126
  billing: {
123
127
  name: user ? `${user.firstname} ${user.lastname}` : '',
124
- phone: user?.phoneNumber || undefined,
128
+ phone: user?.phoneNumber || '',
125
129
  country: 'United States',
130
+ // street1: '',
131
+ // city: '',
132
+ // state: '',
133
+ // zip: '',
126
134
  },
127
135
  },
128
136
  });
@@ -153,7 +161,7 @@ export function CheckoutScreen() {
153
161
  setSelectedAddressId(defaultAddress.id);
154
162
  // Update form with default address
155
163
  setValue('shipping.name', defaultAddress.name);
156
- setValue('shipping.phone', defaultAddress.phone || (undefined as unknown as string));
164
+ setValue('shipping.phone', defaultAddress.phone || '');
157
165
  setValue('shipping.street1', defaultAddress.street1);
158
166
  setValue('shipping.street2', defaultAddress.street2 || '');
159
167
  setValue('shipping.city', defaultAddress.city);
@@ -342,7 +350,6 @@ export function CheckoutScreen() {
342
350
  items,
343
351
  paymentMethod,
344
352
  orderStatus: 'Pending',
345
- chargeTax: true,
346
353
  orderRemindingDates: [],
347
354
  shippingAddress: data.shipping,
348
355
  billingAddress: sameAsShipping ? data.shipping : data.billing,
@@ -352,7 +359,7 @@ export function CheckoutScreen() {
352
359
  const response = await api.createCheckout(
353
360
  orderDTO,
354
361
  isDelivery,
355
- user?.id,
362
+ undefined, // dont pass userId
356
363
  isDelivery ? (selectedShippingRateId || undefined) : undefined,
357
364
  billingAddressId
358
365
  );
@@ -425,7 +432,7 @@ export function CheckoutScreen() {
425
432
  router.push(buildPath('/cart'));
426
433
  return null;
427
434
  }
428
- const subtotal = cart.total;
435
+ const subtotal = cart.cartBody.items.reduce((total, item) => total + item.productVariantData.finalPrice * item.quantity, 0);
429
436
  const tax = 0;
430
437
  const total = subtotal + shippingPrice + tax;
431
438
 
@@ -500,7 +507,7 @@ export function CheckoutScreen() {
500
507
  onChange={() => {
501
508
  setSelectedAddressId(addr.id);
502
509
  setValue('shipping.name', addr.name);
503
- setValue('shipping.phone', addr.phone || (undefined as unknown as string));
510
+ setValue('shipping.phone', addr.phone || '');
504
511
  setValue('shipping.street1', addr.street1);
505
512
  setValue('shipping.street2', addr.street2 || '');
506
513
  setValue('shipping.city', addr.city);
@@ -880,9 +887,9 @@ export function CheckoutScreen() {
880
887
  <section className="mt-8 pt-6 border-t border-slate-100">
881
888
  <div className="space-y-4 mb-6">
882
889
  {cart?.cartBody?.items?.map((item: any) => (
883
- <div key={`${item.productId}-${item.color}-${item.size}`} className="flex gap-3">
890
+ <div key={item.productVariantId || item.id} className="flex gap-3">
884
891
  <div className="w-16 h-16 rounded-xl overflow-hidden bg-white shrink-0">
885
- <Image src={item.productVariantData.productMedia?.[0]?.file || '/placeholder-product.jpg'} alt={item.productVariantData.name} className="w-full h-full object-cover" height={200} width={200} />
892
+ <Image src={item.productVariantData?.media?.[0]?.file || '/placeholder-product.jpg'} alt={item.productVariantData.name} className="w-full h-full object-cover" height={200} width={200} />
886
893
  </div>
887
894
  <div className="flex-1 min-w-0">
888
895
  <p className="font-['Poppins',sans-serif] font-medium text-[12px] text-[#2B4B7C] mb-1">
@@ -948,9 +955,31 @@ export function CheckoutScreen() {
948
955
  </div>
949
956
  </div>
950
957
  )}
958
+ {Object.keys(errors).length > 0 && (
959
+ <div className="mt-4 p-4 rounded-xl bg-amber-50 border border-amber-200 text-amber-700 text-sm font-medium">
960
+ <div className="flex items-start gap-2">
961
+ <AlertCircle className="h-4 w-4 shrink-0 mt-0.5" />
962
+ <div>
963
+ <p className="font-semibold mb-1">Please fix the following errors:</p>
964
+ <ul className="list-disc list-inside space-y-1">
965
+ {errors.shipping?.name && <li>{errors.shipping.name.message}</li>}
966
+ {errors.shipping?.phone && <li>{errors.shipping.phone.message}</li>}
967
+ {errors.shipping?.street1 && <li>{errors.shipping.street1.message}</li>}
968
+ {errors.shipping?.city && <li>{errors.shipping.city.message}</li>}
969
+ {errors.shipping?.state && <li>{errors.shipping.state.message}</li>}
970
+ {errors.shipping?.zip && <li>{errors.shipping.zip.message}</li>}
971
+ {errors.shipping?.country && <li>{errors.shipping.country.message}</li>}
972
+ {!sameAsShipping && errors.billing?.name && <li>Billing: {errors.billing.name.message}</li>}
973
+ {!sameAsShipping && errors.billing?.street1 && <li>Billing: {errors.billing.street1.message}</li>}
974
+ </ul>
975
+ </div>
976
+ </div>
977
+ </div>
978
+ )}
951
979
  <button
952
980
  type="submit"
953
- className="font-['Poppins',sans-serif] font-medium text-[14px] px-6 py-3 rounded-full text-white hover:bg-[#d66f45] hover:shadow-lg transition-all duration-300 mt-4 w-full bg-[#E67E50] hover:bg-[#2B4B7C] flex items-center justify-center gap-2"
981
+ disabled={isSubmitting}
982
+ className="font-['Poppins',sans-serif] font-medium text-[14px] px-6 py-3 rounded-full text-white hover:bg-[#d66f45] hover:shadow-lg transition-all duration-300 mt-4 w-full bg-[#E67E50] hover:bg-[#2B4B7C] flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed"
954
983
  >
955
984
  <CreditCard className="h-5 w-5" />
956
985
  {isSubmitting ? 'Placing order...' : 'Place Secure Order'}
@@ -974,7 +1003,7 @@ export function CheckoutScreen() {
974
1003
  refresh().then(() => {
975
1004
  setSelectedAddressId(addr.id);
976
1005
  setValue('shipping.name', addr.name);
977
- setValue('shipping.phone', addr.phone || (undefined as unknown as string));
1006
+ setValue('shipping.phone', addr.phone || '');
978
1007
  setValue('shipping.street1', addr.street1);
979
1008
  setValue('shipping.street2', addr.street2 || '');
980
1009
  setValue('shipping.city', addr.city);
@@ -0,0 +1,153 @@
1
+ 'use client';
2
+
3
+ import React, { useState } from 'react';
4
+ import { motion } from 'framer-motion';
5
+ import { useForm } from 'react-hook-form';
6
+ import { zodResolver } from '@hookform/resolvers/zod';
7
+ import { z } from 'zod';
8
+ import Link from 'next/link';
9
+ import { ArrowLeft, Mail, Send } from 'lucide-react';
10
+ import { Input } from '@/components/ui/Input';
11
+ import { Button } from '@/components/ui/Button';
12
+ import { AuthApi } from '@/lib/Apis/apis/auth-api';
13
+ import { AXIOS_CONFIG } from '@/lib/Apis/sharedConfig';
14
+ import { useBasePath } from '@/providers/BasePathProvider';
15
+
16
+ const forgotPasswordSchema = z.object({
17
+ email: z.string().email('Enter a valid email address'),
18
+ });
19
+
20
+ type ForgotPasswordFormData = z.infer<typeof forgotPasswordSchema>;
21
+
22
+ export function ForgotPasswordScreen() {
23
+ const { buildPath } = useBasePath();
24
+ const [isSubmitting, setIsSubmitting] = useState(false);
25
+ const [status, setStatus] = useState<{ type: 'success' | 'error'; message: string } | null>(
26
+ null
27
+ );
28
+
29
+ const {
30
+ register,
31
+ handleSubmit,
32
+ formState: { errors },
33
+ } = useForm<ForgotPasswordFormData>({
34
+ resolver: zodResolver(forgotPasswordSchema),
35
+ });
36
+
37
+ const onSubmit = async (data: ForgotPasswordFormData) => {
38
+ setIsSubmitting(true);
39
+ setStatus(null);
40
+ try {
41
+ const authApi = new AuthApi(AXIOS_CONFIG);
42
+ await authApi.sendForgetPasswordEmail({ email: data.email });
43
+ setStatus({
44
+ type: 'success',
45
+ message: 'Password reset link sent! Check your email inbox.',
46
+ });
47
+ } catch (error: any) {
48
+ setStatus({
49
+ type: 'error',
50
+ message: error?.response?.data?.message || 'Unable to send reset email. Please try again.',
51
+ });
52
+ } finally {
53
+ setIsSubmitting(false);
54
+ }
55
+ };
56
+
57
+ return (
58
+ <div className="min-h-screen bg-linear-to-b from-[#F8FAFC] to-[#EBF4FB]">
59
+ <div className="grid min-h-screen overflow-hidden pb-12">
60
+ <motion.section
61
+ initial={{ opacity: 0, x: 24 }}
62
+ animate={{ opacity: 1, x: 0 }}
63
+ transition={{ duration: 0.4 }}
64
+ className="flex items-center justify-center px-6 py-12 lg:px-16"
65
+ >
66
+ <div className="w-full max-w-lg space-y-10 text-center">
67
+ <div className="space-y-2">
68
+ <Mail
69
+ strokeWidth={2}
70
+ className="h-16 w-16 mx-auto text-white rounded-full bg-secondary m-2 mb-4 px-4"
71
+ />
72
+ <h2 className="text-4xl text-secondary">Forgot Password?</h2>
73
+ <p className="text-sm text-muted">
74
+ No worries! Enter your email and we'll send you reset instructions.
75
+ </p>
76
+ </div>
77
+
78
+ <form
79
+ onSubmit={handleSubmit(onSubmit)}
80
+ className="space-y-6 rounded-3xl border bg-white p-8"
81
+ style={{
82
+ boxShadow: '0px 4px 6px -4px #0000001A, 0px 10px 15px -3px #0000001A',
83
+ }}
84
+ >
85
+ {status && (
86
+ <div
87
+ className={`flex items-start gap-2 rounded-2xl border px-4 py-3 text-sm ${
88
+ status.type === 'success'
89
+ ? 'border-green-200 bg-green-50 text-green-800'
90
+ : 'border-red-200 bg-red-50 text-red-700'
91
+ }`}
92
+ >
93
+ <span className="mt-[2px] text-base">{status.type === 'success' ? '✔' : '!'}</span>
94
+ <span>{status.message}</span>
95
+ </div>
96
+ )}
97
+
98
+ <div className="text-start text-secondary">
99
+ <h2 className="text-sm text-secondary mb-3">
100
+ Email Address <span className="text-primary-500">*</span>
101
+ </h2>
102
+ <Input
103
+ type="email"
104
+ placeholder="you@example.com"
105
+ {...register('email')}
106
+ error={errors.email?.message}
107
+ className="text-secondary"
108
+ />
109
+ </div>
110
+
111
+ <button
112
+ type="submit"
113
+ disabled={isSubmitting}
114
+ className="w-full bg-secondary hover:opacity-80 text-white font-medium py-3 px-4 rounded-lg transition-colors disabled:opacity-70 disabled:cursor-not-allowed flex items-center justify-center gap-2"
115
+ >
116
+ {isSubmitting ? (
117
+ 'Sending...'
118
+ ) : (
119
+ <>
120
+ <Send className="h-4 w-4" />
121
+ Send Reset Link
122
+ </>
123
+ )}
124
+ </button>
125
+
126
+ <div className="pt-4 border-t border-slate-200">
127
+ <Link
128
+ href={buildPath('/login')}
129
+ className="flex items-center justify-center gap-2 text-sm font-medium text-primary transition hover:opacity-80"
130
+ >
131
+ <ArrowLeft className="h-4 w-4" />
132
+ Back to login
133
+ </Link>
134
+ </div>
135
+ </form>
136
+
137
+ <div className="mt-4">
138
+ <p className="text-muted">
139
+ Don't have an account?{' '}
140
+ <Link
141
+ href={buildPath('/register')}
142
+ className="font-medium text-primary transition hover:opacity-90"
143
+ >
144
+ Sign up
145
+ </Link>
146
+ </p>
147
+ </div>
148
+ </div>
149
+ </motion.section>
150
+ </div>
151
+ </div>
152
+ );
153
+ }