hey-pharmacist-ecommerce 1.1.13 → 1.1.14
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.d.mts +2 -4
- package/dist/index.d.ts +2 -4
- package/dist/index.js +1039 -857
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1039 -856
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/components/AccountAddressesTab.tsx +209 -0
- package/src/components/AccountOrdersTab.tsx +151 -0
- package/src/components/AccountOverviewTab.tsx +209 -0
- package/src/components/AccountPaymentTab.tsx +116 -0
- package/src/components/AccountSavedItemsTab.tsx +76 -0
- package/src/components/AccountSettingsTab.tsx +116 -0
- package/src/components/AddressFormModal.tsx +23 -10
- package/src/components/CartItem.tsx +60 -56
- package/src/components/Header.tsx +69 -16
- package/src/components/Notification.tsx +148 -0
- package/src/components/ProductCard.tsx +215 -178
- package/src/components/QuickViewModal.tsx +314 -0
- package/src/components/TabNavigation.tsx +48 -0
- package/src/components/ui/Button.tsx +1 -1
- package/src/components/ui/ConfirmModal.tsx +84 -0
- package/src/hooks/usePaymentMethods.ts +58 -0
- package/src/index.ts +0 -1
- package/src/providers/CartProvider.tsx +22 -6
- package/src/providers/EcommerceProvider.tsx +8 -7
- package/src/providers/FavoritesProvider.tsx +10 -3
- package/src/providers/NotificationProvider.tsx +79 -0
- package/src/providers/WishlistProvider.tsx +34 -9
- package/src/screens/AddressesScreen.tsx +72 -61
- package/src/screens/CartScreen.tsx +48 -32
- package/src/screens/ChangePasswordScreen.tsx +155 -0
- package/src/screens/CheckoutScreen.tsx +162 -125
- package/src/screens/EditProfileScreen.tsx +165 -0
- package/src/screens/LoginScreen.tsx +59 -72
- package/src/screens/NewAddressScreen.tsx +16 -10
- package/src/screens/ProductDetailScreen.tsx +334 -234
- package/src/screens/ProfileScreen.tsx +190 -200
- package/src/screens/RegisterScreen.tsx +51 -70
- package/src/screens/SearchResultsScreen.tsx +2 -1
- package/src/screens/ShopScreen.tsx +260 -384
- package/src/screens/WishlistScreen.tsx +226 -224
- package/src/styles/globals.css +9 -0
- package/src/screens/CategoriesScreen.tsx +0 -122
- package/src/screens/HomeScreen.tsx +0 -211
|
@@ -0,0 +1,165 @@
|
|
|
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 { z } from 'zod';
|
|
7
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
8
|
+
import { useRouter } from 'next/navigation';
|
|
9
|
+
import { Mail, Phone, User } from 'lucide-react';
|
|
10
|
+
import { Input } from '@/components/ui/Input';
|
|
11
|
+
import { Button } from '@/components/ui/Button';
|
|
12
|
+
import { useAuth } from '@/providers/AuthProvider';
|
|
13
|
+
import { useBasePath } from '@/providers/BasePathProvider';
|
|
14
|
+
|
|
15
|
+
const profileSchema = z.object({
|
|
16
|
+
firstname: z.string().min(2, 'First name is required'),
|
|
17
|
+
lastname: z.string().min(2, 'Last name is required'),
|
|
18
|
+
email: z.string().email('Enter a valid email address'),
|
|
19
|
+
phoneNumber: z.string().optional(),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
type ProfileFormData = z.infer<typeof profileSchema>;
|
|
23
|
+
|
|
24
|
+
export function EditProfileScreen() {
|
|
25
|
+
const router = useRouter();
|
|
26
|
+
const { user, updateUser } = useAuth();
|
|
27
|
+
const { buildPath } = useBasePath();
|
|
28
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
29
|
+
const [status, setStatus] = useState<{ type: 'success' | 'error'; message: string } | null>(
|
|
30
|
+
null
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const {
|
|
34
|
+
register,
|
|
35
|
+
handleSubmit,
|
|
36
|
+
formState: { errors },
|
|
37
|
+
} = useForm<ProfileFormData>({
|
|
38
|
+
resolver: zodResolver(profileSchema),
|
|
39
|
+
defaultValues: {
|
|
40
|
+
firstname: user?.firstname || '',
|
|
41
|
+
lastname: user?.lastname || '',
|
|
42
|
+
email: user?.email || '',
|
|
43
|
+
phoneNumber: user?.phoneNumber || '',
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (!user) {
|
|
48
|
+
router.push(buildPath('/login'));
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const onSubmit = async (data: ProfileFormData) => {
|
|
53
|
+
setIsSubmitting(true);
|
|
54
|
+
setStatus(null);
|
|
55
|
+
try {
|
|
56
|
+
await updateUser(data);
|
|
57
|
+
setStatus({ type: 'success', message: 'Profile updated successfully' });
|
|
58
|
+
router.push(buildPath('/account'));
|
|
59
|
+
} catch (error: any) {
|
|
60
|
+
setStatus({
|
|
61
|
+
type: 'error',
|
|
62
|
+
message: error.response?.data?.message || 'Failed to update profile',
|
|
63
|
+
});
|
|
64
|
+
} finally {
|
|
65
|
+
setIsSubmitting(false);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div className="min-h-screen bg-slate-50 text-slate-900">
|
|
71
|
+
<div className="container mx-auto px-4 pb-16 pt-10">
|
|
72
|
+
<motion.div
|
|
73
|
+
initial={{ opacity: 0, y: 18 }}
|
|
74
|
+
animate={{ opacity: 1, y: 0 }}
|
|
75
|
+
className="mx-auto max-w-3xl rounded-3xl border border-slate-200 bg-white p-8 shadow-xl shadow-primary-50"
|
|
76
|
+
>
|
|
77
|
+
<div className="flex items-center gap-3">
|
|
78
|
+
<span className="flex h-11 w-11 items-center justify-center rounded-2xl bg-primary-50 text-primary-600">
|
|
79
|
+
<User className="h-5 w-5" />
|
|
80
|
+
</span>
|
|
81
|
+
<div>
|
|
82
|
+
<p className="text-xs font-semibold uppercase tracking-[0.32em] text-slate-500">
|
|
83
|
+
Profile
|
|
84
|
+
</p>
|
|
85
|
+
<h1 className="text-2xl font-semibold text-slate-900">Edit core information</h1>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
<p className="mt-3 text-sm text-slate-600">
|
|
89
|
+
Update your name, email, and contact information so we can keep your deliveries and
|
|
90
|
+
pharmacist support aligned with your preferences.
|
|
91
|
+
</p>
|
|
92
|
+
|
|
93
|
+
{status && (
|
|
94
|
+
<div
|
|
95
|
+
className={`mt-4 flex items-start gap-2 rounded-2xl border px-4 py-3 text-sm ${
|
|
96
|
+
status.type === 'success'
|
|
97
|
+
? 'border-green-200 bg-green-50 text-green-800'
|
|
98
|
+
: 'border-red-200 bg-red-50 text-red-700'
|
|
99
|
+
}`}
|
|
100
|
+
>
|
|
101
|
+
<span className="mt-[2px] text-base">{status.type === 'success' ? '✔' : '!'}</span>
|
|
102
|
+
<span>{status.message}</span>
|
|
103
|
+
</div>
|
|
104
|
+
)}
|
|
105
|
+
|
|
106
|
+
<form onSubmit={handleSubmit(onSubmit)} className="mt-8 space-y-6">
|
|
107
|
+
<div className="grid gap-4 md:grid-cols-2">
|
|
108
|
+
<Input
|
|
109
|
+
label="First name"
|
|
110
|
+
placeholder="Taylor"
|
|
111
|
+
{...register('firstname')}
|
|
112
|
+
error={errors.firstname?.message}
|
|
113
|
+
/>
|
|
114
|
+
<Input
|
|
115
|
+
label="Last name"
|
|
116
|
+
placeholder="Reed"
|
|
117
|
+
{...register('lastname')}
|
|
118
|
+
error={errors.lastname?.message}
|
|
119
|
+
/>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<div className="relative">
|
|
123
|
+
<Input
|
|
124
|
+
type="email"
|
|
125
|
+
label="Email address"
|
|
126
|
+
placeholder="you@example.com"
|
|
127
|
+
className="pl-10"
|
|
128
|
+
{...register('email')}
|
|
129
|
+
error={errors.email?.message}
|
|
130
|
+
/>
|
|
131
|
+
<Mail className="absolute left-3 top-[38px] h-4 w-4 text-slate-400" />
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
<div className="relative">
|
|
135
|
+
<Input
|
|
136
|
+
type="tel"
|
|
137
|
+
label="Phone number"
|
|
138
|
+
placeholder="+1 (555) 123-4567"
|
|
139
|
+
className="pl-10"
|
|
140
|
+
{...register('phoneNumber')}
|
|
141
|
+
error={errors.phoneNumber?.message}
|
|
142
|
+
/>
|
|
143
|
+
<Phone className="absolute left-3 top-[38px] h-4 w-4 text-slate-400" />
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<div className="flex flex-wrap gap-3">
|
|
147
|
+
<Button type="submit" size="lg" isLoading={isSubmitting}>
|
|
148
|
+
Save changes
|
|
149
|
+
</Button>
|
|
150
|
+
<Button
|
|
151
|
+
type="button"
|
|
152
|
+
variant="outline"
|
|
153
|
+
size="lg"
|
|
154
|
+
className="border-slate-300 text-slate-800 hover:bg-slate-50"
|
|
155
|
+
onClick={() => router.push(buildPath('/account'))}
|
|
156
|
+
>
|
|
157
|
+
Cancel
|
|
158
|
+
</Button>
|
|
159
|
+
</div>
|
|
160
|
+
</form>
|
|
161
|
+
</motion.div>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
import { Input } from '@/components/ui/Input';
|
|
20
20
|
import { Button } from '@/components/ui/Button';
|
|
21
21
|
import { useAuth } from '@/providers/AuthProvider';
|
|
22
|
-
import { toast } from 'sonner';
|
|
23
22
|
import { useBasePath } from '@/providers/BasePathProvider';
|
|
24
23
|
|
|
25
24
|
const loginSchema = z.object({
|
|
@@ -34,9 +33,12 @@ export function LoginScreen() {
|
|
|
34
33
|
const { buildPath } = useBasePath();
|
|
35
34
|
const searchParams = useSearchParams();
|
|
36
35
|
const redirectUrl = searchParams?.get('redirect') || buildPath('/');
|
|
37
|
-
const { login } = useAuth();
|
|
36
|
+
const { login, user, isLoading } = useAuth();
|
|
38
37
|
const [showPassword, setShowPassword] = useState(false);
|
|
39
38
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
39
|
+
const [status, setStatus] = useState<{ type: 'success' | 'error'; message: string } | null>(
|
|
40
|
+
null
|
|
41
|
+
);
|
|
40
42
|
|
|
41
43
|
const {
|
|
42
44
|
register,
|
|
@@ -48,95 +50,76 @@ export function LoginScreen() {
|
|
|
48
50
|
|
|
49
51
|
const onSubmit = async (data: LoginFormData) => {
|
|
50
52
|
setIsSubmitting(true);
|
|
53
|
+
setStatus(null);
|
|
51
54
|
try {
|
|
52
55
|
await login(data);
|
|
53
|
-
|
|
56
|
+
setStatus({ type: 'success', message: 'Welcome back!' });
|
|
54
57
|
router.push(redirectUrl);
|
|
55
58
|
} catch (error: any) {
|
|
56
|
-
|
|
59
|
+
setStatus({
|
|
60
|
+
type: 'error',
|
|
61
|
+
message: error?.response?.data?.message || 'Invalid credentials',
|
|
62
|
+
});
|
|
57
63
|
} finally {
|
|
58
64
|
setIsSubmitting(false);
|
|
59
65
|
}
|
|
60
66
|
};
|
|
61
67
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
initial={{ opacity: 0, x: -24 }}
|
|
67
|
-
animate={{ opacity: 1, x: 0 }}
|
|
68
|
-
transition={{ duration: 0.4 }}
|
|
69
|
-
className="relative flex flex-col justify-between bg-gradient-to-br from-slate-700 via-slate-600 to-slate-700 px-10 py-14 text-white"
|
|
70
|
-
>
|
|
71
|
-
<div className="space-y-6">
|
|
72
|
-
<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">
|
|
73
|
-
<HeartPulse className="h-4 w-4" />
|
|
74
|
-
Hey Pharmacist
|
|
75
|
-
</span>
|
|
76
|
-
<h1 className="text-4xl font-bold leading-tight lg:text-5xl">
|
|
77
|
-
Pharmacy-grade care for your household
|
|
78
|
-
</h1>
|
|
79
|
-
<p className="max-w-xl text-white/80">
|
|
80
|
-
Log in to unlock personalized regimens, pharmacist support, and fast delivery on
|
|
81
|
-
wellness essentials curated just for you.
|
|
82
|
-
</p>
|
|
83
|
-
</div>
|
|
84
|
-
|
|
85
|
-
<div className="grid gap-4 rounded-3xl bg-white/10 p-6 backdrop-blur">
|
|
86
|
-
<div className="flex items-center gap-3">
|
|
87
|
-
<ShieldCheck className="h-5 w-5 text-white" />
|
|
88
|
-
<p className="text-sm text-white/80">
|
|
89
|
-
HIPAA-compliant security keeps your health information protected.
|
|
90
|
-
</p>
|
|
91
|
-
</div>
|
|
92
|
-
<div className="flex items-center gap-3">
|
|
93
|
-
<Sparkles className="h-5 w-5 text-white" />
|
|
94
|
-
<p className="text-sm text-white/80">
|
|
95
|
-
Pharmacists ready to chat in under 10 minutes for medication support.
|
|
96
|
-
</p>
|
|
97
|
-
</div>
|
|
98
|
-
</div>
|
|
99
|
-
|
|
100
|
-
<div className="flex items-center gap-6 text-sm text-white/80">
|
|
101
|
-
<span>Need an account?</span>
|
|
102
|
-
<Link
|
|
103
|
-
href={buildPath('/register')}
|
|
104
|
-
className="inline-flex items-center gap-2 rounded-full bg-white/15 px-4 py-2 font-semibold transition hover:bg-white/25"
|
|
105
|
-
>
|
|
106
|
-
Create one now
|
|
107
|
-
<ArrowRight className="h-4 w-4" />
|
|
108
|
-
</Link>
|
|
109
|
-
</div>
|
|
110
|
-
</motion.section>
|
|
68
|
+
if (!isLoading && user) {
|
|
69
|
+
router.push(buildPath('/account'));
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
111
72
|
|
|
73
|
+
return (
|
|
74
|
+
<div className="min-h-screen bg-gradient-to-b from-[#F8FAFC] to-[#EBF4FB]">
|
|
75
|
+
<div className="grid min-h-screen overflow-hidden pb-12">
|
|
112
76
|
<motion.section
|
|
113
77
|
initial={{ opacity: 0, x: 24 }}
|
|
114
78
|
animate={{ opacity: 1, x: 0 }}
|
|
115
79
|
transition={{ duration: 0.4 }}
|
|
116
80
|
className="flex items-center justify-center px-6 py-12 lg:px-16"
|
|
117
81
|
>
|
|
118
|
-
<div className="w-full max-w-
|
|
82
|
+
<div className="w-full max-w-lg space-y-10 text-center">
|
|
119
83
|
<div className="space-y-2">
|
|
120
|
-
<
|
|
121
|
-
<
|
|
122
|
-
|
|
84
|
+
<Lock strokeWidth={2} className='h-16 w-16 mx-auto text-white rounded-full bg-secondary m-2 mb-4 px-4' />
|
|
85
|
+
<h2 className="text-4xl text-secondary">Welcome Back</h2>
|
|
86
|
+
<p className="text-sm text-muted">Sign in to access your patient portal
|
|
123
87
|
</p>
|
|
124
88
|
</div>
|
|
125
89
|
|
|
126
|
-
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6 rounded-3xl border
|
|
127
|
-
|
|
90
|
+
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6 rounded-3xl border bg-white p-8"
|
|
91
|
+
style={{
|
|
92
|
+
boxShadow: '0px 4px 6px -4px #0000001A, 0px 10px 15px -3px #0000001A',
|
|
93
|
+
}}>
|
|
94
|
+
{status && (
|
|
95
|
+
<div
|
|
96
|
+
className={`flex items-start gap-2 rounded-2xl border px-4 py-3 text-sm ${
|
|
97
|
+
status.type === 'success'
|
|
98
|
+
? 'border-green-200 bg-green-50 text-green-800'
|
|
99
|
+
: 'border-red-200 bg-red-50 text-red-700'
|
|
100
|
+
}`}
|
|
101
|
+
>
|
|
102
|
+
<span className="mt-[2px] text-base">{status.type === 'success' ? '✔' : '!'}</span>
|
|
103
|
+
<span>{status.message}</span>
|
|
104
|
+
</div>
|
|
105
|
+
)}
|
|
106
|
+
|
|
107
|
+
<div className='text-start text-secondary'>
|
|
108
|
+
<h2 className="text-sm text-secondary mb-3">Email Address <span className='text-primary-500'>*</span></h2>
|
|
128
109
|
<Input
|
|
129
110
|
type="email"
|
|
130
|
-
label="Email address"
|
|
111
|
+
// label="Email address"
|
|
131
112
|
placeholder="you@example.com"
|
|
132
113
|
{...register('email')}
|
|
133
114
|
error={errors.email?.message}
|
|
115
|
+
className='text-secondary'
|
|
134
116
|
/>
|
|
135
117
|
</div>
|
|
136
|
-
<div className="relative">
|
|
118
|
+
<div className="relative text-start text-secondary">
|
|
119
|
+
<h2 className="text-sm text-secondary mb-3">Password <span className='text-primary-500'>*</span></h2>
|
|
120
|
+
|
|
137
121
|
<Input
|
|
138
122
|
type={showPassword ? 'text' : 'password'}
|
|
139
|
-
label="Password"
|
|
140
123
|
placeholder="••••••••"
|
|
141
124
|
{...register('password')}
|
|
142
125
|
error={errors.password?.message}
|
|
@@ -160,23 +143,27 @@ export function LoginScreen() {
|
|
|
160
143
|
</label>
|
|
161
144
|
<Link
|
|
162
145
|
href={buildPath('/forgot-password')}
|
|
163
|
-
className="font-medium text-primary
|
|
146
|
+
className="font-medium text-primary transition hover:opacity-80"
|
|
164
147
|
>
|
|
165
148
|
Forgot password?
|
|
166
149
|
</Link>
|
|
167
150
|
</div>
|
|
168
151
|
|
|
169
|
-
<
|
|
152
|
+
<button
|
|
170
153
|
type="submit"
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
className="w-full"
|
|
154
|
+
disabled={isSubmitting}
|
|
155
|
+
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"
|
|
174
156
|
>
|
|
175
|
-
Sign in
|
|
176
|
-
</
|
|
157
|
+
{isSubmitting ? 'Signing in...' : 'Sign in'}
|
|
158
|
+
</button>
|
|
177
159
|
</form>
|
|
178
160
|
|
|
179
|
-
|
|
161
|
+
<div className="mt-4">
|
|
162
|
+
<p className="text-muted">Don't have an account? <Link href={buildPath('/register')} className="font-medium text-primary transition hover:opacity-90">Sign up</Link></p>
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
{/* <div className="rounded-3xl border border-slate-100 bg-slate-50 p-6 text-sm text-slate-600">
|
|
180
167
|
<div className="flex items-start gap-3">
|
|
181
168
|
<Lock className="mt-0.5 h-5 w-5 text-primary-500" />
|
|
182
169
|
<div>
|
|
@@ -187,10 +174,10 @@ export function LoginScreen() {
|
|
|
187
174
|
</p>
|
|
188
175
|
</div>
|
|
189
176
|
</div>
|
|
190
|
-
</div>
|
|
177
|
+
</div> */}
|
|
191
178
|
</div>
|
|
192
179
|
</motion.section>
|
|
193
180
|
</div>
|
|
194
181
|
</div>
|
|
195
182
|
);
|
|
196
|
-
}
|
|
183
|
+
}
|
|
@@ -9,12 +9,13 @@ import { Input } from '@/components/ui/Input';
|
|
|
9
9
|
import { addressSchema, type AddressFormData } from '@/lib/validations/address';
|
|
10
10
|
import { AddressesApi } from '@/lib/Apis/apis/addresses-api';
|
|
11
11
|
import { AXIOS_CONFIG } from '@/lib/Apis/wrapper';
|
|
12
|
-
import { toast } from 'sonner';
|
|
13
12
|
import { ArrowLeft, MapPin } from 'lucide-react';
|
|
13
|
+
import { useNotification } from '@/providers/NotificationProvider';
|
|
14
14
|
|
|
15
15
|
export default function NewAddressPage() {
|
|
16
16
|
const router = useRouter();
|
|
17
17
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
18
|
+
const notification = useNotification();
|
|
18
19
|
|
|
19
20
|
const {
|
|
20
21
|
register,
|
|
@@ -43,7 +44,10 @@ export default function NewAddressPage() {
|
|
|
43
44
|
phone: data.phone,
|
|
44
45
|
});
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
notification.success(
|
|
48
|
+
'Address added',
|
|
49
|
+
'Your new address has been saved to your account.'
|
|
50
|
+
);
|
|
47
51
|
router.back();
|
|
48
52
|
|
|
49
53
|
} catch (error: any) {
|
|
@@ -66,17 +70,19 @@ export default function NewAddressPage() {
|
|
|
66
70
|
errorMessage = 'Unable to connect to the server. Please check your internet connection.';
|
|
67
71
|
}
|
|
68
72
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
notification.error(
|
|
74
|
+
'Unable to save address',
|
|
75
|
+
errorMessage,
|
|
76
|
+
5000
|
|
77
|
+
);
|
|
73
78
|
|
|
74
79
|
// Show additional guidance for certain error types
|
|
75
80
|
if (error.response?.status === 422) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
notification.info(
|
|
82
|
+
'Address validation failed',
|
|
83
|
+
'Make sure your address is complete and formatted correctly.',
|
|
84
|
+
6000
|
|
85
|
+
);
|
|
80
86
|
}
|
|
81
87
|
} finally {
|
|
82
88
|
setIsSubmitting(false);
|