adata-ui 2.0.27 → 2.0.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.
- package/components/modals/ReportBugModal.vue +3 -3
- package/components/modals/id/IdConfirmAccountOtpModal.vue +19 -6
- package/components/modals/id/IdLoginModal.vue +45 -128
- package/components/modals/id/IdModals.vue +5 -1
- package/components/modals/id/IdNewPasswordModal.vue +7 -5
- package/components/modals/id/IdRegistrationModal.vue +19 -19
- package/components/modals/id/IdResetPasswordOtpModal.vue +17 -4
- package/components/modals/id/IdTwoFactorModal.vue +127 -0
- package/components/modals/{two-factor → id}/otp-input.vue +8 -0
- package/components/navigation/footer/AFooter.vue +210 -57
- package/components/navigation/footer/ui/{new-footer-accordion.vue → a-footer-accordion.vue} +2 -4
- package/components/navigation/header/AlmatyContacts.vue +13 -13
- package/components/navigation/header/AstanaContacts.vue +10 -10
- package/components/navigation/header/ProductMenu.vue +0 -75
- package/composables/useHeaderNavigationLinks.ts +50 -239
- package/lang/en.ts +351 -330
- package/lang/kk.ts +352 -331
- package/lang/ru.ts +42 -66
- package/package.json +69 -69
- package/shared/constans/pages.ts +1 -0
- package/components/modals/two-factor/two-factor.vue +0 -79
- package/components/navigation/footer/NewFooter.vue +0 -277
- package/components/navigation/footer/ui/footer-acccordion.vue +0 -119
|
@@ -4,8 +4,8 @@ import * as z from 'zod'
|
|
|
4
4
|
import { useUIValidation } from '#adata-ui/composables/useUIValidation'
|
|
5
5
|
|
|
6
6
|
const props = defineProps<{
|
|
7
|
-
name
|
|
8
|
-
email
|
|
7
|
+
name?: string
|
|
8
|
+
email?: string
|
|
9
9
|
}>()
|
|
10
10
|
|
|
11
11
|
const emit = defineEmits<{
|
|
@@ -16,7 +16,7 @@ const { t } = useI18n()
|
|
|
16
16
|
|
|
17
17
|
const mask = new Mask({ mask: '# (###) ### ## ##' })
|
|
18
18
|
|
|
19
|
-
const isOpen = defineModel<boolean>(
|
|
19
|
+
const isOpen = defineModel<boolean>()
|
|
20
20
|
const form: any = reactive({
|
|
21
21
|
name: '',
|
|
22
22
|
email: '',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import OtpInput from '#adata-ui/components/modals/
|
|
2
|
+
import OtpInput from '#adata-ui/components/modals/id/otp-input.vue'
|
|
3
3
|
import { useAuthStore } from '#adata-ui/stores/auth.store'
|
|
4
4
|
|
|
5
5
|
const { $toast } = useNuxtApp()
|
|
@@ -17,6 +17,7 @@ const otp = ref(['', '', '', '', '', ''])
|
|
|
17
17
|
const otpFormatted = computed(() => {
|
|
18
18
|
return otp.value.join('')
|
|
19
19
|
})
|
|
20
|
+
const attempts = ref(5)
|
|
20
21
|
const showError = ref(false)
|
|
21
22
|
const isLoading = ref(false)
|
|
22
23
|
|
|
@@ -28,7 +29,7 @@ async function onResend() {
|
|
|
28
29
|
headers: {
|
|
29
30
|
lang: locale.value,
|
|
30
31
|
},
|
|
31
|
-
|
|
32
|
+
params: {
|
|
32
33
|
email: intermediateState.value.email,
|
|
33
34
|
},
|
|
34
35
|
})
|
|
@@ -59,6 +60,12 @@ async function onConfirm() {
|
|
|
59
60
|
confirmSuccessfulModal.value = true
|
|
60
61
|
}
|
|
61
62
|
catch (error) {
|
|
63
|
+
if (error.data?.retry_after) {
|
|
64
|
+
attempts.value = 0
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
attempts.value--
|
|
68
|
+
}
|
|
62
69
|
showError.value = true
|
|
63
70
|
$toast.error(error.data.message)
|
|
64
71
|
}
|
|
@@ -70,12 +77,14 @@ async function onConfirm() {
|
|
|
70
77
|
function onClose() {
|
|
71
78
|
otp.value = ['', '', '', '', '', '']
|
|
72
79
|
showError.value = false
|
|
80
|
+
attempts.value = 5
|
|
73
81
|
confirmAccountOtpModal.value = false
|
|
74
82
|
}
|
|
75
83
|
|
|
76
84
|
const timer = ref(60)
|
|
77
85
|
|
|
78
86
|
function runTimer() {
|
|
87
|
+
timer.value = 60
|
|
79
88
|
const intervalId = setInterval(() => {
|
|
80
89
|
if (!timer.value) clearInterval(intervalId)
|
|
81
90
|
return timer.value--
|
|
@@ -101,7 +110,7 @@ onBeforeUnmount(() => {
|
|
|
101
110
|
<template>
|
|
102
111
|
<div class="flex flex-col items-center gap-4 text-center">
|
|
103
112
|
<h2 class="text-2xl font-bold">
|
|
104
|
-
{{ t('
|
|
113
|
+
{{ t('modals.id.confirmAccount.title') }}
|
|
105
114
|
</h2>
|
|
106
115
|
|
|
107
116
|
<a-icon-hand-with-phone-light class="size-32 dark:hidden" />
|
|
@@ -113,7 +122,7 @@ onBeforeUnmount(() => {
|
|
|
113
122
|
</p>
|
|
114
123
|
|
|
115
124
|
<div v-if="timer > 0" class="text-2xl font-bold">
|
|
116
|
-
{{ timer }} {{ t('
|
|
125
|
+
{{ timer }} {{ t('modals.id.resetPasswordOtp.seconds') }}
|
|
117
126
|
</div>
|
|
118
127
|
<button
|
|
119
128
|
v-else
|
|
@@ -124,7 +133,11 @@ onBeforeUnmount(() => {
|
|
|
124
133
|
</button>
|
|
125
134
|
</div>
|
|
126
135
|
|
|
127
|
-
<otp-input
|
|
136
|
+
<otp-input
|
|
137
|
+
v-model="otp"
|
|
138
|
+
v-model:error="showError"
|
|
139
|
+
:attempts="attempts"
|
|
140
|
+
/>
|
|
128
141
|
|
|
129
142
|
<div class="flex w-full gap-2">
|
|
130
143
|
<a-button
|
|
@@ -137,7 +150,7 @@ onBeforeUnmount(() => {
|
|
|
137
150
|
<a-button
|
|
138
151
|
block
|
|
139
152
|
:loading="isLoading"
|
|
140
|
-
:disabled="otpFormatted.length < 6"
|
|
153
|
+
:disabled="otpFormatted.length < 6 || attempts === 0"
|
|
141
154
|
@click="onConfirm"
|
|
142
155
|
>
|
|
143
156
|
{{ t('actions.confirm') }}
|
|
@@ -1,34 +1,30 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import TwoFactor from '#adata-ui/components/modals/two-factor/two-factor.vue'
|
|
3
2
|
import { useAuthStore } from '#adata-ui/stores/auth.store'
|
|
4
3
|
import { navigateToLocalizedPage } from '#adata-ui/utils/localizedNavigation'
|
|
5
|
-
import { useToggle } from '@vueuse/shared'
|
|
6
4
|
import * as z from 'zod'
|
|
7
5
|
|
|
6
|
+
const { $toast } = useNuxtApp()
|
|
8
7
|
const { myLayer, commonAuth } = useAppConfig()
|
|
9
|
-
const authApiURL = commonAuth.authApiURL
|
|
10
|
-
const [isResendModal, toggleResendModal] = useToggle()
|
|
11
|
-
const isConfirmationEmailModal = ref(false)
|
|
12
|
-
const isTwoFactorOpen = ref(false)
|
|
13
|
-
const isLoadingOtp = ref(false)
|
|
14
|
-
const { $api, $toast } = useNuxtApp()
|
|
15
|
-
const showOtpError = ref(false)
|
|
16
8
|
const { t, locale } = useI18n()
|
|
17
|
-
const submitted = ref(false)
|
|
18
|
-
const accessToken = ref(null)
|
|
19
|
-
const { loginModal, registrationModal, recoveryModal, confirmAccountOtpModal } = useIdModals()
|
|
20
9
|
|
|
21
10
|
const authStore = useAuthStore()
|
|
22
11
|
const { intermediateState } = storeToRefs(authStore)
|
|
23
12
|
|
|
13
|
+
const { loginModal, registrationModal, recoveryModal, confirmAccountOtpModal, twoFactorModal } = useIdModals()
|
|
14
|
+
|
|
15
|
+
const authApiURL = commonAuth.authApiURL
|
|
16
|
+
|
|
17
|
+
const submitted = ref(false)
|
|
18
|
+
const accessToken = ref(null)
|
|
19
|
+
|
|
24
20
|
export interface ILoginForm {
|
|
25
21
|
username: string
|
|
26
22
|
password: string
|
|
27
23
|
}
|
|
28
24
|
|
|
29
25
|
const loginSchema = z.object({
|
|
30
|
-
username: z.string().nonempty(t('
|
|
31
|
-
password: z.string().nonempty(t('
|
|
26
|
+
username: z.string().nonempty(t('error.required')).email(t('error.email')),
|
|
27
|
+
password: z.string().nonempty(t('error.required')),
|
|
32
28
|
})
|
|
33
29
|
|
|
34
30
|
const validation = computed(() => {
|
|
@@ -78,34 +74,38 @@ async function submit() {
|
|
|
78
74
|
}),
|
|
79
75
|
})
|
|
80
76
|
const { data, message } = await login.json().catch(() => ({}))
|
|
77
|
+
|
|
78
|
+
intermediateState.value.email = form.username
|
|
79
|
+
intermediateState.value.password = form.password
|
|
80
|
+
|
|
81
81
|
if (data) accessToken.value = data?.access_token
|
|
82
82
|
if (login.status > 202) {
|
|
83
83
|
if (login.status === 422) {
|
|
84
84
|
$toast.error(t('error.validation'))
|
|
85
85
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
intermediateState.value.password = form.password
|
|
86
|
+
if (login.status === 206) {
|
|
87
|
+
confirmAccountOtpModal.value = true
|
|
89
88
|
|
|
90
|
-
|
|
89
|
+
$fetch(`${authApiURL}/email/resend-otp`, {
|
|
91
90
|
method: 'GET',
|
|
92
91
|
credentials: 'include',
|
|
93
92
|
headers: {
|
|
94
|
-
|
|
95
|
-
'lang': locale.value,
|
|
93
|
+
lang: locale.value,
|
|
96
94
|
},
|
|
97
|
-
|
|
95
|
+
params: {
|
|
98
96
|
email: intermediateState.value.email,
|
|
99
|
-
}
|
|
97
|
+
},
|
|
100
98
|
}).catch(() => {})
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
$toast.error(message)
|
|
103
102
|
}
|
|
104
103
|
}
|
|
105
104
|
else {
|
|
106
105
|
rememberMe.value ? savePassAndLogin(form) : clearAuthCookie()
|
|
107
106
|
if (data.is_2fa_enabled) {
|
|
108
|
-
|
|
107
|
+
loginModal.value = false
|
|
108
|
+
twoFactorModal.value = true
|
|
109
109
|
}
|
|
110
110
|
else if (data.email_is_verified) {
|
|
111
111
|
const response = await fetch(`${authApiURL}/access/cookie`, {
|
|
@@ -120,15 +120,19 @@ async function submit() {
|
|
|
120
120
|
if (cookiesData) {
|
|
121
121
|
const { access_token, expire_in } = cookiesData
|
|
122
122
|
const hostname = location.hostname.split('.').reverse()
|
|
123
|
-
|
|
123
|
+
|
|
124
|
+
useCookie('accessToken', {
|
|
125
|
+
maxAge: expire_in,
|
|
126
|
+
domain: `.${hostname[1]}.${hostname[0]}`,
|
|
127
|
+
path: '/',
|
|
128
|
+
secure: true,
|
|
129
|
+
}).value = access_token
|
|
124
130
|
}
|
|
125
131
|
$toast.success(t('login.successfully'))
|
|
126
132
|
loginModal.value = false
|
|
127
133
|
window.location.reload()
|
|
128
134
|
}
|
|
129
135
|
else {
|
|
130
|
-
intermediateState.value.email = form.username
|
|
131
|
-
intermediateState.value.password = form.password
|
|
132
136
|
confirmAccountOtpModal.value = true
|
|
133
137
|
|
|
134
138
|
$fetch(`${authApiURL}/email/resend-otp`, {
|
|
@@ -137,7 +141,7 @@ async function submit() {
|
|
|
137
141
|
headers: {
|
|
138
142
|
lang: locale.value,
|
|
139
143
|
},
|
|
140
|
-
|
|
144
|
+
params: {
|
|
141
145
|
email: intermediateState.value.email,
|
|
142
146
|
},
|
|
143
147
|
}).catch(() => {})
|
|
@@ -154,13 +158,6 @@ function authWithSocial(social: string) {
|
|
|
154
158
|
document.location.replace(`https://auth.${mode}.kz/api/login/social?source=${social}`)
|
|
155
159
|
}
|
|
156
160
|
|
|
157
|
-
async function onResend() {
|
|
158
|
-
const { data, success, message } = await $api.auth.emailResend()
|
|
159
|
-
if (success) {
|
|
160
|
-
$toast.success(message)
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
161
|
function onRegister() {
|
|
165
162
|
loginModal.value = false
|
|
166
163
|
registrationModal.value = true
|
|
@@ -179,64 +176,8 @@ onMounted(() => {
|
|
|
179
176
|
rememberMe.value = !!useCookie('username').value && !!useCookie('password').value
|
|
180
177
|
})
|
|
181
178
|
|
|
182
|
-
function confirmationEmailResend() {
|
|
183
|
-
isConfirmationEmailModal.value = false
|
|
184
|
-
toggleResendModal()
|
|
185
|
-
}
|
|
186
|
-
async function handleConfirmOtp(otpCode: string) {
|
|
187
|
-
isLoadingOtp.value = true
|
|
188
|
-
const login = await fetch(`${authApiURL}/login`, {
|
|
189
|
-
method: 'POST',
|
|
190
|
-
credentials: 'include',
|
|
191
|
-
headers: {
|
|
192
|
-
'Content-Type': 'application/json',
|
|
193
|
-
'lang': locale.value,
|
|
194
|
-
},
|
|
195
|
-
body: JSON.stringify({
|
|
196
|
-
'username': intermediateState.value.email.trim(),
|
|
197
|
-
'password': intermediateState.value.password.toString(),
|
|
198
|
-
'2fa_code': otpCode,
|
|
199
|
-
}),
|
|
200
|
-
})
|
|
201
|
-
const { data, message } = await login.json().catch(() => ({}))
|
|
202
|
-
|
|
203
|
-
if (login.status > 202) {
|
|
204
|
-
if (login.status === 403) {
|
|
205
|
-
showOtpError.value = true
|
|
206
|
-
isLoadingOtp.value = false
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
else {
|
|
210
|
-
const response = await fetch(`${authApiURL}/access/cookie`, {
|
|
211
|
-
method: 'GET',
|
|
212
|
-
credentials: 'include',
|
|
213
|
-
headers: {
|
|
214
|
-
Authorization: `Bearer ${accessToken.value}`,
|
|
215
|
-
lang: locale.value,
|
|
216
|
-
},
|
|
217
|
-
})
|
|
218
|
-
const { data: cookiesData } = await response.json()
|
|
219
|
-
if (cookiesData?.access_token) {
|
|
220
|
-
const { access_token, expire_in } = cookiesData
|
|
221
|
-
const hostname = location.hostname.split('.').reverse()
|
|
222
|
-
document.cookie = `accessToken=${access_token}; max-age=${expire_in}; domain=.${hostname[1]}.${hostname[0]}; path=/`
|
|
223
|
-
}
|
|
224
|
-
$toast.success(t('login.successfully'))
|
|
225
|
-
loginModal.value = false
|
|
226
|
-
window.location.reload()
|
|
227
|
-
|
|
228
|
-
isTwoFactorOpen.value = false
|
|
229
|
-
}
|
|
230
|
-
isLoadingOtp.value = false
|
|
231
|
-
}
|
|
232
|
-
|
|
233
179
|
function handleEnter(e: KeyboardEvent) {
|
|
234
|
-
if (
|
|
235
|
-
e.key === 'Enter'
|
|
236
|
-
&& !isTwoFactorOpen.value
|
|
237
|
-
&& !isConfirmationEmailModal.value
|
|
238
|
-
&& !isResendModal.value
|
|
239
|
-
) {
|
|
180
|
+
if (e.key === 'Enter') {
|
|
240
181
|
submit()
|
|
241
182
|
}
|
|
242
183
|
}
|
|
@@ -258,21 +199,21 @@ onBeforeUnmount(() => {
|
|
|
258
199
|
<template>
|
|
259
200
|
<div class="flex flex-col gap-5">
|
|
260
201
|
<h1 class="heading-02 text-center">
|
|
261
|
-
{{
|
|
202
|
+
{{ t('modals.id.login.title') }}
|
|
262
203
|
</h1>
|
|
263
204
|
<p class="body-400 text-center">
|
|
264
|
-
{{
|
|
205
|
+
{{ t('modals.id.login.subtitle') }}
|
|
265
206
|
</p>
|
|
266
207
|
<div class="flex flex-col gap-4">
|
|
267
208
|
<a-input-standard
|
|
268
209
|
v-model="form.username"
|
|
269
210
|
type="email"
|
|
270
|
-
:label="
|
|
211
|
+
:label="t('modals.id.login.labels.email')"
|
|
271
212
|
:error="getError('username')"
|
|
272
213
|
/>
|
|
273
214
|
<a-input-password
|
|
274
215
|
v-model="form.password"
|
|
275
|
-
:label="
|
|
216
|
+
:label="t('modals.id.login.labels.password')"
|
|
276
217
|
:error="getError('password')"
|
|
277
218
|
/>
|
|
278
219
|
<div class="flex items-center justify-between">
|
|
@@ -285,14 +226,14 @@ onBeforeUnmount(() => {
|
|
|
285
226
|
for="remember_me"
|
|
286
227
|
class="cursor-pointer"
|
|
287
228
|
>{{
|
|
288
|
-
|
|
229
|
+
t('modals.id.login.remember_me')
|
|
289
230
|
}}</label>
|
|
290
231
|
</div>
|
|
291
232
|
<button
|
|
292
233
|
class="link-s-400"
|
|
293
234
|
@click="onForgotPassword"
|
|
294
235
|
>
|
|
295
|
-
{{
|
|
236
|
+
{{ t('modals.id.login.forget_password') }}
|
|
296
237
|
</button>
|
|
297
238
|
</div>
|
|
298
239
|
</div>
|
|
@@ -325,10 +266,10 @@ onBeforeUnmount(() => {
|
|
|
325
266
|
type="submit"
|
|
326
267
|
@click="submit"
|
|
327
268
|
>
|
|
328
|
-
{{
|
|
269
|
+
{{ t('actions.login') }}
|
|
329
270
|
</a-button>
|
|
330
271
|
<p class="body-400 text-center">
|
|
331
|
-
{{
|
|
272
|
+
{{ t('modals.id.login.first_time') }}
|
|
332
273
|
</p>
|
|
333
274
|
|
|
334
275
|
<a-button
|
|
@@ -337,7 +278,7 @@ onBeforeUnmount(() => {
|
|
|
337
278
|
class="w-full"
|
|
338
279
|
@click="onRegister"
|
|
339
280
|
>
|
|
340
|
-
{{
|
|
281
|
+
{{ t('actions.register') }}
|
|
341
282
|
</a-button>
|
|
342
283
|
|
|
343
284
|
<a-button
|
|
@@ -346,40 +287,16 @@ onBeforeUnmount(() => {
|
|
|
346
287
|
class="w-full"
|
|
347
288
|
@click="toTariffs"
|
|
348
289
|
>
|
|
349
|
-
{{
|
|
290
|
+
{{ t('actions.toTariffs') }}
|
|
350
291
|
</a-button>
|
|
351
292
|
|
|
352
293
|
<a-alert
|
|
353
294
|
class="max-w-screen-sm !text-[10px]"
|
|
354
295
|
size="xs"
|
|
355
296
|
>
|
|
356
|
-
{{
|
|
297
|
+
{{ t('info.userAgreement') }}
|
|
357
298
|
</a-alert>
|
|
358
299
|
</div>
|
|
359
|
-
|
|
360
|
-
<!-- <a-modal v-model="isResendModal"> -->
|
|
361
|
-
<!-- <resend -->
|
|
362
|
-
<!-- v-if="isResendModal" -->
|
|
363
|
-
<!-- @close="isResendModal = false" -->
|
|
364
|
-
<!-- @resend="onResend" -->
|
|
365
|
-
<!-- /> -->
|
|
366
|
-
<!-- </a-modal> -->
|
|
367
|
-
|
|
368
|
-
<!-- <a-modal v-model="isConfirmationEmailModal"> -->
|
|
369
|
-
<!-- <a-confirmation-email -->
|
|
370
|
-
<!-- v-if="isConfirmationEmailModal" -->
|
|
371
|
-
<!-- @close="isConfirmationEmailModal = false" -->
|
|
372
|
-
<!-- @resend="confirmationEmailResend" -->
|
|
373
|
-
<!-- /> -->
|
|
374
|
-
<!-- </a-modal> -->
|
|
375
|
-
|
|
376
|
-
<two-factor
|
|
377
|
-
v-model="isTwoFactorOpen"
|
|
378
|
-
v-model:error="showOtpError"
|
|
379
|
-
:loading="isLoadingOtp"
|
|
380
|
-
@confirm="handleConfirmOtp"
|
|
381
|
-
@close="isTwoFactorOpen = false"
|
|
382
|
-
/>
|
|
383
300
|
</template>
|
|
384
301
|
|
|
385
302
|
<style scoped></style>
|
|
@@ -7,6 +7,7 @@ import IdPasswordSuccessfulModal from '#adata-ui/components/modals/id/IdPassword
|
|
|
7
7
|
import IdRecoveryModal from '#adata-ui/components/modals/id/IdRecoveryModal.vue'
|
|
8
8
|
import IdRegistrationModal from '#adata-ui/components/modals/id/IdRegistrationModal.vue'
|
|
9
9
|
import IdResetPasswordOtpModal from '#adata-ui/components/modals/id/IdResetPasswordOtpModal.vue'
|
|
10
|
+
import IdTwoFactorModal from '#adata-ui/components/modals/id/IdTwoFactorModal.vue'
|
|
10
11
|
|
|
11
12
|
const {
|
|
12
13
|
loginModal,
|
|
@@ -25,11 +26,14 @@ const {
|
|
|
25
26
|
<a-modal v-model="loginModal">
|
|
26
27
|
<id-login-modal />
|
|
27
28
|
</a-modal>
|
|
29
|
+
<a-modal v-model="twoFactorModal">
|
|
30
|
+
<id-two-factor-modal />
|
|
31
|
+
</a-modal>
|
|
28
32
|
|
|
29
33
|
<a-modal v-model="registrationModal">
|
|
30
34
|
<id-registration-modal v-if="registrationModal" />
|
|
31
35
|
</a-modal>
|
|
32
|
-
<a-modal v-model="confirmAccountOtpModal">
|
|
36
|
+
<a-modal v-model="confirmAccountOtpModal" prevent-close>
|
|
33
37
|
<id-confirm-account-otp-modal v-if="confirmAccountOtpModal" />
|
|
34
38
|
</a-modal>
|
|
35
39
|
<a-modal v-model="confirmSuccessfulModal">
|
|
@@ -21,16 +21,18 @@ const loading = ref(false)
|
|
|
21
21
|
const resetSchema = z.object({
|
|
22
22
|
password: z
|
|
23
23
|
.string()
|
|
24
|
-
.
|
|
25
|
-
.
|
|
24
|
+
.nonempty(t('error.required'))
|
|
25
|
+
.min(8, t('modals.id.register.errors.low_security', { length: 8 }))
|
|
26
|
+
.regex(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/, t('modals.id.register.errors.low_security')),
|
|
26
27
|
password_confirmation: z
|
|
27
28
|
.string()
|
|
28
|
-
.
|
|
29
|
-
.
|
|
29
|
+
.nonempty(t('error.required'))
|
|
30
|
+
.min(8, t('modals.id.register.errors.low_security', { length: 8 }))
|
|
31
|
+
.regex(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/, t('modals.id.register.errors.low_security')),
|
|
30
32
|
}).refine(
|
|
31
33
|
data => data.password === data.password_confirmation,
|
|
32
34
|
{
|
|
33
|
-
message: t('register.
|
|
35
|
+
message: t('modals.id.register.errors.sameAs'),
|
|
34
36
|
path: ['password_confirmation'],
|
|
35
37
|
},
|
|
36
38
|
)
|
|
@@ -34,21 +34,21 @@ const agreement = ref(false)
|
|
|
34
34
|
const loading = ref(false)
|
|
35
35
|
|
|
36
36
|
const registerSchema = z.object({
|
|
37
|
-
email: z.string().nonempty(t('
|
|
37
|
+
email: z.string().nonempty(t('error.required')).email(t('error.email')),
|
|
38
38
|
password: z
|
|
39
39
|
.string()
|
|
40
|
-
.nonempty(t('
|
|
41
|
-
.min(8, t('register.
|
|
42
|
-
.regex(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/, t('register.
|
|
40
|
+
.nonempty(t('error.required'))
|
|
41
|
+
.min(8, t('modals.id.register.errors.low_security', { length: 8 }))
|
|
42
|
+
.regex(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/, t('modals.id.register.errors.low_security')),
|
|
43
43
|
password_confirmation: z
|
|
44
44
|
.string()
|
|
45
|
-
.nonempty(t('
|
|
46
|
-
.min(8, t('register.
|
|
47
|
-
.regex(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/, t('register.
|
|
45
|
+
.nonempty(t('error.required'))
|
|
46
|
+
.min(8, t('modals.id.register.errors.low_security', { length: 8 }))
|
|
47
|
+
.regex(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/, t('modals.id.register.errors.low_security')),
|
|
48
48
|
}).refine(
|
|
49
49
|
data => data.password === data.password_confirmation,
|
|
50
50
|
{
|
|
51
|
-
message: t('register.
|
|
51
|
+
message: t('modals.id.register.errors.sameAs'),
|
|
52
52
|
path: ['password_confirmation'],
|
|
53
53
|
},
|
|
54
54
|
)
|
|
@@ -152,31 +152,31 @@ onBeforeUnmount(() => {
|
|
|
152
152
|
@submit.prevent="onSubmit"
|
|
153
153
|
>
|
|
154
154
|
<h2 class="text-center text-2xl font-bold">
|
|
155
|
-
{{ t('register.
|
|
155
|
+
{{ t('modals.id.register.title') }}
|
|
156
156
|
</h2>
|
|
157
157
|
<p class="text-center text-sm">
|
|
158
|
-
{{ t('register.
|
|
158
|
+
{{ t('modals.id.register.subtitle') }}
|
|
159
159
|
</p>
|
|
160
160
|
<a-input-standard
|
|
161
161
|
v-model="form.email"
|
|
162
|
-
:label="t('register.
|
|
162
|
+
:label="t('modals.id.register.labels.email')"
|
|
163
163
|
:error="getError('email')"
|
|
164
164
|
type="email"
|
|
165
165
|
/>
|
|
166
166
|
|
|
167
167
|
<a-input-password
|
|
168
168
|
v-model="form.password"
|
|
169
|
-
:label="t('register.
|
|
169
|
+
:label="t('modals.id.register.labels.password')"
|
|
170
170
|
:error="getError('password')"
|
|
171
171
|
/>
|
|
172
172
|
|
|
173
173
|
<a-input-password
|
|
174
174
|
v-model="form.password_confirmation"
|
|
175
|
-
:label="t('register.
|
|
175
|
+
:label="t('modals.id.register.labels.password_confirmation')"
|
|
176
176
|
:error="getError('password_confirmation')"
|
|
177
177
|
/>
|
|
178
178
|
<a-alert color="blue">
|
|
179
|
-
{{ t('register.
|
|
179
|
+
{{ t('modals.id.register.alert') }}
|
|
180
180
|
<template #icon>
|
|
181
181
|
<a-icon-info-circle />
|
|
182
182
|
</template>
|
|
@@ -185,13 +185,13 @@ onBeforeUnmount(() => {
|
|
|
185
185
|
v-model="agreement"
|
|
186
186
|
side="right"
|
|
187
187
|
>
|
|
188
|
-
<i18n-t keypath="register.
|
|
188
|
+
<i18n-t keypath="modals.id.register.agreement.text">
|
|
189
189
|
<template #link>
|
|
190
190
|
<nuxt-link-locale
|
|
191
191
|
class="text-blue-700"
|
|
192
192
|
@click="getUrl"
|
|
193
193
|
>
|
|
194
|
-
{{ t('register.
|
|
194
|
+
{{ t('modals.id.register.agreement.link') }}
|
|
195
195
|
</nuxt-link-locale>
|
|
196
196
|
</template>
|
|
197
197
|
</i18n-t>
|
|
@@ -200,17 +200,17 @@ onBeforeUnmount(() => {
|
|
|
200
200
|
:disabled="!agreement"
|
|
201
201
|
:loading="loading"
|
|
202
202
|
>
|
|
203
|
-
{{ t('register.
|
|
203
|
+
{{ t('modals.id.register.continue') }}
|
|
204
204
|
</a-button>
|
|
205
205
|
<p class="text-center text-sm">
|
|
206
|
-
{{ t('register.
|
|
206
|
+
{{ t('modals.id.register.haveAcc') }}
|
|
207
207
|
</p>
|
|
208
208
|
<a-button
|
|
209
209
|
type="button"
|
|
210
210
|
view="outline"
|
|
211
211
|
@click="onLogin"
|
|
212
212
|
>
|
|
213
|
-
{{ t('register.
|
|
213
|
+
{{ t('modals.id.register.enter') }}
|
|
214
214
|
</a-button>
|
|
215
215
|
</form>
|
|
216
216
|
</template>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import OtpInput from '#adata-ui/components/modals/
|
|
2
|
+
import OtpInput from '#adata-ui/components/modals/id/otp-input.vue'
|
|
3
3
|
import { useAuthStore } from '#adata-ui/stores/auth.store'
|
|
4
4
|
|
|
5
5
|
const { $toast } = useNuxtApp()
|
|
@@ -17,6 +17,7 @@ const otp = ref(['', '', '', '', '', ''])
|
|
|
17
17
|
const otpFormatted = computed(() => {
|
|
18
18
|
return otp.value.join('')
|
|
19
19
|
})
|
|
20
|
+
const attempts = ref(5)
|
|
20
21
|
const showError = ref(false)
|
|
21
22
|
const isLoading = ref(false)
|
|
22
23
|
|
|
@@ -43,6 +44,12 @@ async function onConfirm() {
|
|
|
43
44
|
newPasswordModal.value = true
|
|
44
45
|
}
|
|
45
46
|
catch (error) {
|
|
47
|
+
if (error.data?.retry_after) {
|
|
48
|
+
attempts.value = 0
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
attempts.value--
|
|
52
|
+
}
|
|
46
53
|
showError.value = true
|
|
47
54
|
$toast.error(error.data.message)
|
|
48
55
|
}
|
|
@@ -72,6 +79,7 @@ async function onResend() {
|
|
|
72
79
|
|
|
73
80
|
function onClose() {
|
|
74
81
|
otp.value = ['', '', '', '', '', '']
|
|
82
|
+
attempts.value = 5
|
|
75
83
|
showError.value = false
|
|
76
84
|
resetPasswordOtpModal.value = false
|
|
77
85
|
}
|
|
@@ -85,6 +93,7 @@ function handleEnter(e: KeyboardEvent) {
|
|
|
85
93
|
const timer = ref(60)
|
|
86
94
|
|
|
87
95
|
function runTimer() {
|
|
96
|
+
timer.value = 60
|
|
88
97
|
const intervalId = setInterval(() => {
|
|
89
98
|
if (!timer.value) clearInterval(intervalId)
|
|
90
99
|
return timer.value--
|
|
@@ -116,7 +125,7 @@ onBeforeUnmount(() => {
|
|
|
116
125
|
</p>
|
|
117
126
|
|
|
118
127
|
<div v-if="timer > 0" class="text-2xl font-bold">
|
|
119
|
-
{{ timer }} {{ t('
|
|
128
|
+
{{ timer }} {{ t('modals.id.resetPasswordOtp.seconds') }}
|
|
120
129
|
</div>
|
|
121
130
|
<button
|
|
122
131
|
v-else
|
|
@@ -127,7 +136,11 @@ onBeforeUnmount(() => {
|
|
|
127
136
|
</button>
|
|
128
137
|
</div>
|
|
129
138
|
|
|
130
|
-
<otp-input
|
|
139
|
+
<otp-input
|
|
140
|
+
v-model="otp"
|
|
141
|
+
v-model:error="showError"
|
|
142
|
+
:attempts="attempts"
|
|
143
|
+
/>
|
|
131
144
|
|
|
132
145
|
<div class="flex w-full gap-2">
|
|
133
146
|
<a-button
|
|
@@ -140,7 +153,7 @@ onBeforeUnmount(() => {
|
|
|
140
153
|
<a-button
|
|
141
154
|
block
|
|
142
155
|
:loading="isLoading"
|
|
143
|
-
:disabled="otpFormatted.length < 6"
|
|
156
|
+
:disabled="otpFormatted.length < 6 || attempts === 0"
|
|
144
157
|
@click="onConfirm"
|
|
145
158
|
>
|
|
146
159
|
{{ t('actions.confirm') }}
|