11<template >
2- <div class =" email-otp-container " >
3- <div v-if =" step === 1" class =" request -step" >
2+ <div class =" email-otp-wrapper " >
3+ <div v-if =" step === 1" class =" auth -step-fade " >
44 <div class =" input-group" >
5- <label >Email Address</label >
6- <input
7- v-model =" email"
8- type =" email"
9- placeholder =" e.g. admin@company.com"
10- :disabled =" loading"
11- class =" email-input"
12- />
5+ <label class =" portal-label" >Email Address</label >
6+ <div class =" input-wrapper" >
7+ <v-icon small class =" input-icon" >mdi-email-outline</v-icon >
8+ <input
9+ v-model =" email"
10+ type =" email"
11+ placeholder =" e.g. admin@company.com"
12+ :disabled =" loading"
13+ class =" portal-input"
14+ />
15+ </div >
1316 </div >
1417
1518 <button
1619 @click =" requestOtp"
17- :disabled =" loading || !isValidEmail"
18- class =" action-btn"
20+ :disabled =" loading || !isValidEmailTest"
21+ class =" portal-action-btn"
22+ :class =" { 'btn-loading': loading }"
1923 >
20- {{ loading ? 'Sending...' : 'Send OTP' }}
24+ <span >{{ loading ? 'Sending...' : 'Continue' }}</span >
25+ <v-icon v-if =" !loading" right small color =" white" >mdi-arrow-right</v-icon >
2126 </button >
2227 </div >
2328
24- <div v-if =" step === 2" class =" verify-step" >
25- <div class =" step-header" >
26- <p >An OTP has been sent to <strong >{{ email }}</strong ></p >
27- <button @click =" goBackToEmail" class =" link-btn" >Change Email</button >
29+ <div v-if =" step === 2" class =" auth-step-fade" >
30+ <div class =" verify-header mb-6" >
31+ <p class =" verify-text" >Verification code sent to</p >
32+ <div class =" d-flex align-center justify-center gap-2" >
33+ <span class =" active-email" >{{ email }}</span >
34+ <button @click =" goBackToEmail" class =" change-email-btn" title =" Edit Email" >
35+ <v-icon x-small color =" blue" >mdi-pencil-outline</v-icon >
36+ </button >
37+ </div >
2838 </div >
2939
30- <!-- Show PIN input only if OTP is still valid -->
3140 <div v-if =" !isOtpExpired" >
32- <PIN @pinTakenEvent =" verifyOtp" />
33- <p v-if =" loading" class =" loading-text" >Verifying code...</p >
41+ <div class =" pin-container py-2" >
42+ <PIN @pinTakenEvent =" verifyOtp" />
43+ </div >
44+ <p v-if =" loading" class =" verifying-status mt-4" >
45+ <v-progress-circular indeterminate size =" 16" width =" 2" color =" blue" class =" mr-2" ></v-progress-circular >
46+ Verifying identity...
47+ </p >
3448 </div >
3549
36- <!-- Show regenerate button if OTP is expired -- >
37- < div v-if = " isOtpExpired " class =" expired-section " >
38- <p class =" expired-message " >Your OTP has expired. Please request a new one .</p >
50+ <div v-if = " isOtpExpired " class = " expired-alert " >
51+ < v-icon color = " red lighten-1 " class =" mb-2 " >mdi-alert-circle-outline</ v-icon >
52+ <p class =" expired-text " >Your security code has expired.</p >
3953 <button
4054 @click =" requestOtp"
4155 :disabled =" loading"
42- class =" action-btn"
56+ class =" portal- action-btn outline mt-2 "
4357 >
44- {{ loading ? 'Sending...' : 'Request New OTP ' }}
58+ {{ loading ? 'Sending...' : 'Request New Code ' }}
4559 </button >
4660 </div >
4761 </div >
4862 </div >
4963</template >
5064
65+ <style scoped>
66+
67+
68+ .email-otp-wrapper {
69+ width : 100% ;
70+ }
71+
72+ .input-group {
73+ display : flex ;
74+ flex-direction : column ; /* This forces the label to be on top of the input */
75+ align-items : flex-start ;
76+ text-align : left ;
77+ margin-bottom : 24px ;
78+ width : 100% ;
79+ }
80+
81+ .portal-label {
82+ display : block ; /* Ensure it takes full width */
83+ width : 100% ;
84+ font-size : 0.7rem ;
85+ font-weight : 800 ;
86+ color : #64748b ;
87+ text-transform : uppercase ;
88+ letter-spacing : 0.05em ;
89+ margin-bottom : 8px ; /* Added spacing between label and input */
90+ }
91+
92+ /* Ensure the wrapper also takes full width */
93+ .input-wrapper {
94+ position : relative ;
95+ display : flex ;
96+ align-items : center ;
97+ width : 100% ;
98+ }
99+
100+
101+ .input-icon {
102+ position : absolute ;
103+ left : 14px ;
104+ color : #94a3b8 ;
105+ }
106+
107+ .portal-input {
108+ width : 100% ;
109+ padding : 12px 12px 12px 42px ;
110+ background-color : #f8fafc ;
111+ border : 1px solid #e2e8f0 ;
112+ border-radius : 8px ;
113+ font-size : 0.95rem ;
114+ color : #1e293b ;
115+ transition : all 0.2s ease ;
116+ }
117+
118+ .portal-input :focus {
119+ outline : none ;
120+ border-color : #3b82f6 ;
121+ background-color : #ffffff ;
122+ box-shadow : 0 0 0 4px rgba (59 , 130 , 246 , 0.1 );
123+ }
124+
125+ /* Button Styling */
126+ .portal-action-btn {
127+ width : 100% ;
128+ height : 48px ;
129+ background-color : #1e293b ;
130+ color : #ffffff ;
131+ border : none ;
132+ border-radius : 8px ;
133+ font-weight : 700 ;
134+ font-size : 0.9rem ;
135+ display : flex ;
136+ align-items : center ;
137+ justify-content : center ;
138+ gap : 8px ;
139+ cursor : pointer ;
140+ transition : all 0.2s ease ;
141+ margin-top : 24px ;
142+ }
143+
144+ .portal-action-btn :hover:not (:disabled ) {
145+ background-color : #0f172a ;
146+ transform : translateY (-1px );
147+ }
148+
149+ .portal-action-btn :disabled {
150+ background-color : #e2e8f0 ;
151+ color : #94a3b8 ;
152+ cursor : not-allowed ;
153+ }
154+
155+ .portal-action-btn.outline {
156+ background : transparent ;
157+ border : 1px solid #e2e8f0 ;
158+ color : #64748b ;
159+ }
160+
161+ /* Step 2 Specifics */
162+ .verify-text {
163+ font-size : 0.875rem ;
164+ color : #64748b ;
165+ margin-bottom : 4px ;
166+ text-align : center ;
167+ }
168+
169+ .active-email {
170+ font-weight : 700 ;
171+ color : #1e293b ;
172+ font-size : 0.95rem ;
173+ }
174+
175+ .change-email-btn {
176+ background : #eff6ff ;
177+ border : none ;
178+ border-radius : 4px ;
179+ padding : 2px 6px ;
180+ cursor : pointer ;
181+ display : flex ;
182+ align-items : center ;
183+ }
184+
185+ .verifying-status {
186+ font-size : 0.8rem ;
187+ color : #3b82f6 ;
188+ font-weight : 600 ;
189+ text-align : center ;
190+ }
191+
192+ /* Alert States */
193+ .expired-alert {
194+ background-color : #fff1f2 ;
195+ border-radius : 8px ;
196+ padding : 16px ;
197+ text-align : center ;
198+ border : 1px dashed #fecaca ;
199+ }
200+
201+ .expired-text {
202+ font-size : 0.85rem ;
203+ color : #be123c ;
204+ font-weight : 600 ;
205+ }
206+
207+ /* Utilities */
208+ .gap-2 { gap : 8px ; }
209+ .auth-step-fade {
210+ animation : fadeIn 0.3s ease-in-out ;
211+ }
212+
213+ @keyframes fadeIn {
214+ from { opacity : 0 ; transform : translateY (5px ); }
215+ to { opacity : 1 ; transform : translateY (0 ); }
216+ }
217+ </style >
218+
219+
51220<script >
52221import PIN from ' ./mfa/PIN.vue' ; // Adjust path as needed
53222import { mapActions } from " vuex/dist/vuex.common.js" ;
@@ -68,7 +237,7 @@ export default {
68237 };
69238 },
70239 computed: {
71- isValidEmail () {
240+ isValidEmailTest () {
72241 const re = / ^ (([^ <>()[\]\\ . ,;:\s @"] + (\. [^ <>()[\]\\ . ,;:\s @"] + )* )| (". + "))@((\[ [0-9 ] {1,3} \. [0-9 ] {1,3} \. [0-9 ] {1,3} \. [0-9 ] {1,3} \] )| (([a-zA-Z\-0 -9] + \. )+ [a-zA-Z ] {2,} ))$ / ;
73242 return re .test (String (this .email ).toLowerCase ());
74243 }
@@ -146,83 +315,3 @@ export default {
146315 }
147316};
148317 </script >
149-
150- <style scoped>
151- .button-theme :hover {
152- box-shadow : 0 2px 4px 0 rgba (0 , 0 , 0 , 0.16 ), 0 2px 10px 0 rgba (0 , 0 , 0 , 0.12 );
153- background : aliceblue !important ;
154- border : 1px solid #905ab0 !important ;
155- color : #905ab0 !important ;
156- }
157- .email-otp-container {
158- max-width : 400px ;
159- margin : 0 auto ;
160- padding : 20px ;
161- text-align : center ;
162- }
163-
164- .input-group {
165- text-align : left ;
166- margin-bottom : 20px ;
167- }
168-
169- label {
170- display : block ;
171- font-weight : 600 ;
172- margin-bottom : 8px ;
173- color : #34495e ;
174- }
175-
176- .email-input {
177- width : 100% ;
178- padding : 12px ;
179- border : 1px solid #ddd ;
180- border-radius : 4px ;
181- font-size : 16px ;
182- }
183-
184- .action-btn {
185- width : 100% ;
186- background-color : rgb (66 , 66 , 66 );
187- color : white ;
188- padding : 12px ;
189- border : none ;
190- border-radius : 4px ;
191- cursor : pointer ;
192- font-weight : bold ;
193- }
194-
195- .action-btn :disabled {
196- background-color : #bdc3c7 ;
197- }
198-
199- .step-header {
200- margin-bottom : 20px ;
201- }
202-
203- .link-btn {
204- background : none ;
205- border : none ;
206- color : #3498db ;
207- text-decoration : underline ;
208- cursor : pointer ;
209- font-size : 0.9em ;
210- }
211-
212- .loading-text {
213- color : #7f8c8d ;
214- font-size : 0.8em ;
215- margin-top : 10px ;
216- }
217-
218- .expired-section {
219- text-align : center ;
220- margin-top : 20px ;
221- }
222-
223- .expired-message {
224- color : #c0392b ;
225- font-size : 0.95em ;
226- margin-bottom : 15px ;
227- }
228- </style >
0 commit comments