hey-pharmacist-ecommerce 1.0.4 → 1.0.6

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 (74) hide show
  1. package/README.md +107 -1
  2. package/dist/index.d.mts +3636 -316
  3. package/dist/index.d.ts +3636 -316
  4. package/dist/index.js +6802 -3865
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +6756 -3817
  7. package/dist/index.mjs.map +1 -1
  8. package/package.json +17 -14
  9. package/src/components/AddressFormModal.tsx +171 -0
  10. package/src/components/CartItem.tsx +17 -12
  11. package/src/components/FilterChips.tsx +195 -0
  12. package/src/components/Header.tsx +121 -71
  13. package/src/components/OrderCard.tsx +18 -25
  14. package/src/components/ProductCard.tsx +209 -72
  15. package/src/components/ui/Button.tsx +13 -5
  16. package/src/components/ui/Card.tsx +46 -0
  17. package/src/hooks/useAddresses.ts +83 -0
  18. package/src/hooks/useOrders.ts +37 -19
  19. package/src/hooks/useProducts.ts +55 -63
  20. package/src/hooks/useWishlistProducts.ts +75 -0
  21. package/src/index.ts +3 -19
  22. package/src/lib/Apis/api.ts +1 -0
  23. package/src/lib/Apis/apis/cart-api.ts +3 -3
  24. package/src/lib/Apis/apis/inventory-api.ts +0 -108
  25. package/src/lib/Apis/apis/stores-api.ts +70 -0
  26. package/src/lib/Apis/apis/wishlist-api.ts +447 -0
  27. package/src/lib/Apis/models/cart-item-populated.ts +0 -1
  28. package/src/lib/Apis/models/create-single-variant-product-dto.ts +3 -10
  29. package/src/lib/Apis/models/create-variant-dto.ts +26 -33
  30. package/src/lib/Apis/models/extended-product-dto.ts +20 -24
  31. package/src/lib/Apis/models/index.ts +2 -1
  32. package/src/lib/Apis/models/order-time-line-dto.ts +49 -0
  33. package/src/lib/Apis/models/order.ts +3 -8
  34. package/src/lib/Apis/models/populated-order.ts +3 -8
  35. package/src/lib/Apis/models/product-variant.ts +29 -0
  36. package/src/lib/Apis/models/update-product-variant-dto.ts +16 -23
  37. package/src/lib/Apis/models/wishlist.ts +51 -0
  38. package/src/lib/Apis/wrapper.ts +18 -7
  39. package/src/lib/api-adapter/index.ts +0 -12
  40. package/src/lib/types/index.ts +16 -61
  41. package/src/lib/utils/colors.ts +7 -4
  42. package/src/lib/utils/format.ts +1 -1
  43. package/src/lib/validations/address.ts +14 -0
  44. package/src/providers/AuthProvider.tsx +61 -31
  45. package/src/providers/CartProvider.tsx +18 -28
  46. package/src/providers/EcommerceProvider.tsx +7 -0
  47. package/src/providers/FavoritesProvider.tsx +86 -0
  48. package/src/providers/ThemeProvider.tsx +16 -1
  49. package/src/providers/WishlistProvider.tsx +174 -0
  50. package/src/screens/AddressesScreen.tsx +484 -0
  51. package/src/screens/CartScreen.tsx +120 -84
  52. package/src/screens/CategoriesScreen.tsx +120 -0
  53. package/src/screens/CheckoutScreen.tsx +919 -241
  54. package/src/screens/CurrentOrdersScreen.tsx +125 -61
  55. package/src/screens/HomeScreen.tsx +209 -0
  56. package/src/screens/LoginScreen.tsx +133 -88
  57. package/src/screens/NewAddressScreen.tsx +187 -0
  58. package/src/screens/OrdersScreen.tsx +162 -50
  59. package/src/screens/ProductDetailScreen.tsx +641 -190
  60. package/src/screens/ProfileScreen.tsx +192 -116
  61. package/src/screens/RegisterScreen.tsx +193 -144
  62. package/src/screens/SearchResultsScreen.tsx +165 -0
  63. package/src/screens/ShopScreen.tsx +1110 -146
  64. package/src/screens/WishlistScreen.tsx +428 -0
  65. package/src/lib/Apis/models/inventory-paginated-response.ts +0 -75
  66. package/src/lib/api/auth.ts +0 -81
  67. package/src/lib/api/cart.ts +0 -42
  68. package/src/lib/api/orders.ts +0 -53
  69. package/src/lib/api/products.ts +0 -51
  70. package/src/lib/api-adapter/auth-adapter.ts +0 -196
  71. package/src/lib/api-adapter/cart-adapter.ts +0 -193
  72. package/src/lib/api-adapter/mappers.ts +0 -147
  73. package/src/lib/api-adapter/orders-adapter.ts +0 -195
  74. package/src/lib/api-adapter/products-adapter.ts +0 -194
@@ -5,16 +5,24 @@ import { motion } from 'framer-motion';
5
5
  import { useForm } from 'react-hook-form';
6
6
  import { zodResolver } from '@hookform/resolvers/zod';
7
7
  import { z } from 'zod';
8
- import { Lock, Mail, Eye, EyeOff } from 'lucide-react';
9
- import { Button } from '@/components/ui/Button';
8
+ import { useRouter, useSearchParams } from 'next/navigation';
9
+ import Link from 'next/link';
10
+ import {
11
+ ArrowRight,
12
+ Eye,
13
+ EyeOff,
14
+ HeartPulse,
15
+ Lock,
16
+ ShieldCheck,
17
+ Sparkles,
18
+ } from 'lucide-react';
10
19
  import { Input } from '@/components/ui/Input';
20
+ import { Button } from '@/components/ui/Button';
11
21
  import { useAuth } from '@/providers/AuthProvider';
12
- import { useRouter, useSearchParams } from 'next/navigation';
13
22
  import { toast } from 'sonner';
14
- import Link from 'next/link';
15
23
 
16
24
  const loginSchema = z.object({
17
- email: z.string().email('Invalid email address'),
25
+ email: z.string().email('Enter a valid email address'),
18
26
  password: z.string().min(6, 'Password must be at least 6 characters'),
19
27
  });
20
28
 
@@ -50,100 +58,137 @@ export function LoginScreen() {
50
58
  };
51
59
 
52
60
  return (
53
- <div className="min-h-screen bg-gradient-to-br from-primary-600 via-primary-700 to-secondary-600 flex items-center justify-center p-4">
54
- <motion.div
55
- initial={{ opacity: 0, y: 20 }}
56
- animate={{ opacity: 1, y: 0 }}
57
- className="w-full max-w-md"
58
- >
59
- <div className="bg-white rounded-3xl shadow-2xl p-8">
60
- {/* Header */}
61
- <div className="text-center mb-8">
62
- <div className="w-16 h-16 bg-primary-100 rounded-full flex items-center justify-center mx-auto mb-4">
63
- <Lock className="w-8 h-8 text-primary-600" />
64
- </div>
65
- <h1 className="text-3xl font-bold text-gray-900 mb-2">Welcome Back</h1>
66
- <p className="text-gray-600">Sign in to your account to continue</p>
61
+ <div className="min-h-screen bg-slate-50">
62
+ <div className="grid min-h-screen overflow-hidden bg-white lg:grid-cols-[1.1fr_0.9fr]">
63
+ <motion.section
64
+ initial={{ opacity: 0, x: -24 }}
65
+ animate={{ opacity: 1, x: 0 }}
66
+ transition={{ duration: 0.4 }}
67
+ className="relative flex flex-col justify-between bg-gradient-to-br from-[rgb(var(--header-from))] via-[rgb(var(--header-via))] to-[rgb(var(--header-to))] px-10 py-14 text-white"
68
+ >
69
+ <div className="space-y-6">
70
+ <span className="inline-flex items-center gap-2 rounded-full bg-white/15 px-3 py-1 text-sm font-semibold uppercase tracking-[0.35em] text-white/70 backdrop-blur">
71
+ <HeartPulse className="h-4 w-4" />
72
+ Hey Pharmacist
73
+ </span>
74
+ <h1 className="text-4xl font-bold leading-tight lg:text-5xl">
75
+ Pharmacy-grade care for your household
76
+ </h1>
77
+ <p className="max-w-xl text-white/80">
78
+ Log in to unlock personalized regimens, pharmacist support, and fast delivery on
79
+ wellness essentials curated just for you.
80
+ </p>
67
81
  </div>
68
82
 
69
- {/* Form */}
70
- <form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
71
- <div>
72
- <Input
73
- type="email"
74
- label="Email Address"
75
- placeholder="you@example.com"
76
- {...register('email')}
77
- error={errors.email?.message}
78
- />
83
+ <div className="grid gap-4 rounded-3xl bg-white/10 p-6 backdrop-blur">
84
+ <div className="flex items-center gap-3">
85
+ <ShieldCheck className="h-5 w-5 text-white" />
86
+ <p className="text-sm text-white/80">
87
+ HIPAA-compliant security keeps your health information protected.
88
+ </p>
89
+ </div>
90
+ <div className="flex items-center gap-3">
91
+ <Sparkles className="h-5 w-5 text-white" />
92
+ <p className="text-sm text-white/80">
93
+ Pharmacists ready to chat in under 10 minutes for medication support.
94
+ </p>
79
95
  </div>
96
+ </div>
80
97
 
81
- <div className="relative">
82
- <Input
83
- type={showPassword ? 'text' : 'password'}
84
- label="Password"
85
- placeholder="••••••••"
86
- {...register('password')}
87
- error={errors.password?.message}
88
- />
89
- <button
90
- type="button"
91
- onClick={() => setShowPassword(!showPassword)}
92
- className="absolute right-3 top-[42px] text-gray-500 hover:text-gray-700"
93
- >
94
- {showPassword ? (
95
- <EyeOff className="w-5 h-5" />
96
- ) : (
97
- <Eye className="w-5 h-5" />
98
- )}
99
- </button>
98
+ <div className="flex items-center gap-6 text-sm text-white/80">
99
+ <span>Need an account?</span>
100
+ <Link
101
+ href="/register"
102
+ className="inline-flex items-center gap-2 rounded-full bg-white/15 px-4 py-2 font-semibold transition hover:bg-white/25"
103
+ >
104
+ Create one now
105
+ <ArrowRight className="h-4 w-4" />
106
+ </Link>
107
+ </div>
108
+ </motion.section>
109
+
110
+ <motion.section
111
+ initial={{ opacity: 0, x: 24 }}
112
+ animate={{ opacity: 1, x: 0 }}
113
+ transition={{ duration: 0.4 }}
114
+ className="flex items-center justify-center px-6 py-12 lg:px-16"
115
+ >
116
+ <div className="w-full max-w-md space-y-10">
117
+ <div className="space-y-2">
118
+ <h2 className="text-3xl font-bold text-slate-900">Sign in</h2>
119
+ <p className="text-sm text-slate-500">
120
+ Welcome back! Enter your details to continue your personalized care plan.
121
+ </p>
100
122
  </div>
101
123
 
102
- <div className="flex items-center justify-between">
103
- <label className="flex items-center gap-2 cursor-pointer">
104
- <input
105
- type="checkbox"
106
- className="w-4 h-4 text-primary-600 rounded"
124
+ <form onSubmit={handleSubmit(onSubmit)} className="space-y-6 rounded-3xl border border-slate-100 bg-white p-8 shadow-lg shadow-primary-50">
125
+ <div>
126
+ <Input
127
+ type="email"
128
+ label="Email address"
129
+ placeholder="you@example.com"
130
+ {...register('email')}
131
+ error={errors.email?.message}
107
132
  />
108
- <span className="text-sm text-gray-700">Remember me</span>
109
- </label>
110
- <Link
111
- href="/forgot-password"
112
- className="text-sm text-primary-600 hover:text-primary-700 font-medium"
113
- >
114
- Forgot password?
115
- </Link>
116
- </div>
133
+ </div>
134
+ <div className="relative">
135
+ <Input
136
+ type={showPassword ? 'text' : 'password'}
137
+ label="Password"
138
+ placeholder="••••••••"
139
+ {...register('password')}
140
+ error={errors.password?.message}
141
+ />
142
+ <button
143
+ type="button"
144
+ onClick={() => setShowPassword((prev) => !prev)}
145
+ className="absolute right-3 top-[42px] text-slate-400 transition hover:text-slate-600"
146
+ >
147
+ {showPassword ? <EyeOff className="h-5 w-5" /> : <Eye className="h-5 w-5" />}
148
+ </button>
149
+ </div>
117
150
 
118
- <Button
119
- type="submit"
120
- size="lg"
121
- isLoading={isSubmitting}
122
- className="w-full"
123
- >
124
- Sign In
125
- </Button>
126
- </form>
151
+ <div className="flex items-center justify-between text-sm">
152
+ <label className="flex items-center gap-2 text-slate-600">
153
+ <input
154
+ type="checkbox"
155
+ className="h-4 w-4 rounded border-slate-300 text-primary-600 focus:ring-primary-500"
156
+ />
157
+ Remember me
158
+ </label>
159
+ <Link
160
+ href="/forgot-password"
161
+ className="font-medium text-primary-600 transition hover:text-primary-700"
162
+ >
163
+ Forgot password?
164
+ </Link>
165
+ </div>
127
166
 
128
- {/* Divider */}
129
- <div className="relative my-8">
130
- <div className="absolute inset-0 flex items-center">
131
- <div className="w-full border-t border-gray-200"></div>
132
- </div>
133
- <div className="relative flex justify-center text-sm">
134
- <span className="px-4 bg-white text-gray-500">New to our store?</span>
167
+ <Button
168
+ type="submit"
169
+ size="lg"
170
+ isLoading={isSubmitting}
171
+ className="w-full"
172
+ >
173
+ Sign in securely
174
+ </Button>
175
+ </form>
176
+
177
+ <div className="rounded-3xl border border-slate-100 bg-slate-50 p-6 text-sm text-slate-600">
178
+ <div className="flex items-start gap-3">
179
+ <Lock className="mt-0.5 h-5 w-5 text-primary-500" />
180
+ <div>
181
+ <p className="font-semibold text-slate-800">Secure by design</p>
182
+ <p>
183
+ Encrypted sessions, multi-factor ready, and privacy-first policies keep your
184
+ personal data safe.
185
+ </p>
186
+ </div>
187
+ </div>
135
188
  </div>
136
189
  </div>
137
-
138
- {/* Register Link */}
139
- <Link href="/register">
140
- <Button variant="outline" size="lg" className="w-full">
141
- Create an Account
142
- </Button>
143
- </Link>
144
- </div>
145
- </motion.div>
190
+ </motion.section>
191
+ </div>
146
192
  </div>
147
193
  );
148
194
  }
149
-
@@ -0,0 +1,187 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import { useRouter } from 'next/navigation';
5
+ import { useForm } from 'react-hook-form';
6
+ import { zodResolver } from '@hookform/resolvers/zod';
7
+ import { Button } from '@/components/ui/Button';
8
+ import { Input } from '@/components/ui/Input';
9
+ import { addressSchema, type AddressFormData } from '@/lib/validations/address';
10
+ import { AddressesApi } from '@/lib/Apis/apis/addresses-api';
11
+ import { AXIOS_CONFIG } from '@/lib/Apis/wrapper';
12
+ import { toast } from 'sonner';
13
+ import { ArrowLeft, MapPin } from 'lucide-react';
14
+
15
+ export default function NewAddressPage() {
16
+ const router = useRouter();
17
+ const [isSubmitting, setIsSubmitting] = useState(false);
18
+
19
+ const {
20
+ register,
21
+ handleSubmit,
22
+ formState: { errors }
23
+ } = useForm<AddressFormData>({
24
+ resolver: zodResolver(addressSchema),
25
+ defaultValues: {
26
+ country: 'United States'
27
+ }
28
+ });
29
+
30
+ const onSubmit = async (data: AddressFormData) => {
31
+ setIsSubmitting(true);
32
+ try {
33
+ console.log('Submitting address data:', data);
34
+ const api = new AddressesApi(AXIOS_CONFIG);
35
+ const response = await api.createAddressForUser({
36
+ name: data.name,
37
+ street1: data.street1,
38
+ street2: data.street2,
39
+ city: data.city,
40
+ state: data.state,
41
+ zip: data.zip,
42
+ country: data.country,
43
+ phone: data.phone,
44
+ });
45
+
46
+ toast.success('Address added successfully');
47
+ router.back();
48
+
49
+ } catch (error: any) {
50
+ let errorMessage = 'An unexpected error occurred';
51
+
52
+ // Handle specific API errors
53
+ if (error.response?.data?.message) {
54
+ errorMessage = error.response.data.message;
55
+ } else if (error.message) {
56
+ errorMessage = error.message;
57
+ }
58
+
59
+ // Handle validation errors
60
+ if (error.response?.status === 400) {
61
+ errorMessage = error.response.data?.message || 'Please check your address details and try again';
62
+ }
63
+
64
+ // Handle network errors
65
+ if (error.message === 'Network Error') {
66
+ errorMessage = 'Unable to connect to the server. Please check your internet connection.';
67
+ }
68
+
69
+ toast.error('Unable to save address', {
70
+ description: errorMessage,
71
+ duration: 5000
72
+ });
73
+
74
+ // Show additional guidance for certain error types
75
+ if (error.response?.status === 422) {
76
+ toast.info('Address validation failed', {
77
+ description: 'Make sure your address is complete and formatted correctly.',
78
+ duration: 6000
79
+ });
80
+ }
81
+ } finally {
82
+ setIsSubmitting(false);
83
+ }
84
+ };
85
+
86
+ return (
87
+ <div className="min-h-screen bg-slate-50">
88
+ <div className="relative bg-gradient-to-br from-[rgb(var(--header-from))] via-[rgb(var(--header-via))] to-[rgb(var(--header-to))] text-white">
89
+ <div className="absolute inset-0 bg-[radial-gradient(circle_at_top_left,_rgba(255,255,255,0.35),_transparent_60%)]" />
90
+ <div className="relative container mx-auto px-4 py-8">
91
+ <button
92
+ onClick={() => router.back()}
93
+ className="inline-flex items-center gap-2 rounded-lg bg-white/10 px-4 py-2 text-sm font-medium text-white hover:bg-white/20 transition-colors"
94
+ >
95
+ <ArrowLeft className="h-4 w-4" />
96
+ Back
97
+ </button>
98
+ <div className="mt-6 flex items-center gap-4">
99
+ <div className="rounded-full bg-white/10 p-3">
100
+ <MapPin className="h-6 w-6" />
101
+ </div>
102
+ <div>
103
+ <h1 className="text-3xl font-bold">Add New Address</h1>
104
+ <p className="mt-1 text-white/75">
105
+ Enter your delivery address details below.
106
+ </p>
107
+ </div>
108
+ </div>
109
+ </div>
110
+ </div>
111
+
112
+ <div className="container mx-auto px-4 py-8">
113
+ <div className="max-w-2xl mx-auto">
114
+ <div className="bg-white rounded-2xl shadow-xl p-6">
115
+ <form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
116
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
117
+ <Input
118
+ label="Full name"
119
+ placeholder="John Doe"
120
+ {...register('name')}
121
+ error={errors.name?.message}
122
+ />
123
+ <Input
124
+ label="Phone number"
125
+ placeholder="+1 (555) 123-4567"
126
+ {...register('phone')}
127
+ error={errors.phone?.message}
128
+ />
129
+ <div className="md:col-span-2">
130
+ <Input
131
+ label="Address line 1"
132
+ placeholder="123 Main St"
133
+ {...register('street1')}
134
+ error={errors.street1?.message}
135
+ />
136
+ </div>
137
+ <div className="md:col-span-2">
138
+ <Input
139
+ label="Address line 2 (optional)"
140
+ placeholder="Apt 4B"
141
+ {...register('street2')}
142
+ />
143
+ </div>
144
+ <Input
145
+ label="City"
146
+ placeholder="New York"
147
+ {...register('city')}
148
+ error={errors.city?.message}
149
+ />
150
+ <Input
151
+ label="State"
152
+ placeholder="NY"
153
+ {...register('state')}
154
+ error={errors.state?.message}
155
+ />
156
+ <Input
157
+ label="ZIP code"
158
+ placeholder="10001"
159
+ {...register('zip')}
160
+ error={errors.zip?.message}
161
+ />
162
+ <Input
163
+ label="Country"
164
+ placeholder="United States"
165
+ {...register('country')}
166
+ error={errors.country?.message}
167
+ />
168
+ </div>
169
+ <div className="flex justify-end gap-4">
170
+ <Button
171
+ type="button"
172
+ variant="outline"
173
+ onClick={() => router.back()}
174
+ >
175
+ Cancel
176
+ </Button>
177
+ <Button type="submit" disabled={isSubmitting}>
178
+ {isSubmitting ? 'Adding Address...' : 'Add Address'}
179
+ </Button>
180
+ </div>
181
+ </form>
182
+ </div>
183
+ </div>
184
+ </div>
185
+ </div>
186
+ );
187
+ }