adata-ui 2.0.28 → 2.0.30

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.
@@ -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('register.form.errors.required')).email(t('register.form.errors.email')),
31
- password: z.string().nonempty(t('register.form.errors.required')),
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
- else {
87
- intermediateState.value.email = form.username
88
- intermediateState.value.password = form.password
86
+ if (login.status === 206) {
87
+ confirmAccountOtpModal.value = true
89
88
 
90
- await fetch(`${authApiURL}/email/resend-otp`, {
89
+ $fetch(`${authApiURL}/email/resend-otp`, {
91
90
  method: 'GET',
92
91
  credentials: 'include',
93
92
  headers: {
94
- 'Content-Type': 'application/json',
95
- 'lang': locale.value,
93
+ lang: locale.value,
96
94
  },
97
- body: JSON.stringify({
95
+ params: {
98
96
  email: intermediateState.value.email,
99
- }),
97
+ },
100
98
  }).catch(() => {})
101
-
102
- confirmAccountOtpModal.value = true
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
- isTwoFactorOpen.value = true
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
- document.cookie = `accessToken=${access_token}; max-age=${expire_in}; domain=.${hostname[1]}.${hostname[0]}; path=/`
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
- body: {
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
- {{ $t('login.form.title') }}
202
+ {{ t('modals.id.login.title') }}
262
203
  </h1>
263
204
  <p class="body-400 text-center">
264
- {{ $t('login.form.subtitle') }}
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="$t('login.form.labels.email')"
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="$t('login.form.labels.password')"
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
- $t('login.form.remember_me')
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
- {{ $t('login.form.forget_password') }}
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
- {{ $t('actions.login') }}
269
+ {{ t('actions.login') }}
329
270
  </a-button>
330
271
  <p class="body-400 text-center">
331
- {{ $t('login.form.first_time') }}
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
- {{ $t('actions.register') }}
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
- {{ $t('actions.toTariffs') }}
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
- {{ $t('info.userAgreement') }}
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,14 +26,17 @@ 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
- <a-modal v-model="confirmSuccessfulModal">
39
+ <a-modal v-model="confirmSuccessfulModal" prevent-close>
36
40
  <id-confirm-successful-modal v-if="confirmSuccessfulModal" />
37
41
  </a-modal>
38
42
 
@@ -21,16 +21,18 @@ const loading = ref(false)
21
21
  const resetSchema = z.object({
22
22
  password: z
23
23
  .string()
24
- .min(8, t('register.form.errors.low_security', { length: 8 }))
25
- .regex(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/, t('register.form.errors.low_security')),
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
- .min(8, t('register.form.errors.low_security', { length: 8 }))
29
- .regex(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/, t('register.form.errors.low_security')),
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.form.errors.sameAs'),
35
+ message: t('modals.id.register.errors.sameAs'),
34
36
  path: ['password_confirmation'],
35
37
  },
36
38
  )
@@ -1,10 +1,20 @@
1
1
  <script lang="ts" setup>
2
+ const emit = defineEmits<{
3
+ (e: 'onCompleted', value: string): void
4
+ }>()
5
+
2
6
  const length = 6 // Длина OTP
3
7
  const showError = defineModel('error')
4
8
  const otp = defineModel()
5
9
  const otpRefs = ref([])
6
10
  const { t } = useI18n()
7
11
 
12
+ function checkCompletion() {
13
+ if (otp.value.every(val => val !== '')) {
14
+ emit('onCompleted', otp.value.join(''))
15
+ }
16
+ }
17
+
8
18
  function onInput(event, index) {
9
19
  const value = event.target.value
10
20
  showError.value = false
@@ -16,6 +26,7 @@ function onInput(event, index) {
16
26
  }
17
27
 
18
28
  otp.value[index] = value
29
+ checkCompletion()
19
30
 
20
31
  // Перемещаем фокус на следующий элемент, если введен символ
21
32
  if (value && index < length - 1) {
@@ -59,6 +70,7 @@ function onPaste(event) {
59
70
  otp.value[index] = char
60
71
  }
61
72
  })
73
+ checkCompletion()
62
74
 
63
75
  // Ставим фокус на следующий незаполненный элемент
64
76
  const firstEmptyIndex = otp.value.findIndex(val => val === '')
@@ -113,8 +125,8 @@ function moveFocus(index: number, to: string) {
113
125
  v-model="otp[index]"
114
126
  type="text"
115
127
  maxlength="1"
116
- class="bg-deepblue-900/5 dark:bg-[#E3E5E80D] size-10 rounded text-center text-lg focus:border-blue-500 focus:outline-none focus:border caret-transparent"
117
- :class="{ 'border border-[#E74135] dark:border-[#F47E75] dark:bg-[#F47E751A] bg-[#E741351A]': showError }"
128
+ class="size-10 rounded bg-deepblue-900/5 text-center text-lg caret-transparent focus:border focus:border-blue-500 focus:outline-none dark:bg-[#E3E5E80D]"
129
+ :class="{ 'border border-[#E74135] bg-[#E741351A] dark:border-[#F47E75] dark:bg-[#F47E751A]': showError }"
118
130
  @input="onInput($event, index)"
119
131
  @keydown.backspace="onBackspace(index, $event)"
120
132
  @keydown.enter.prevent
@@ -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('register.form.errors.required')).email(t('register.form.errors.email')),
37
+ email: z.string().nonempty(t('error.required')).email(t('error.email')),
38
38
  password: z
39
39
  .string()
40
- .nonempty(t('register.form.errors.required'))
41
- .min(8, t('register.form.errors.low_security', { length: 8 }))
42
- .regex(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/, t('register.form.errors.low_security')),
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('register.form.errors.required'))
46
- .min(8, t('register.form.errors.low_security', { length: 8 }))
47
- .regex(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/, t('register.form.errors.low_security')),
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.form.errors.sameAs'),
51
+ message: t('modals.id.register.errors.sameAs'),
52
52
  path: ['password_confirmation'],
53
53
  },
54
54
  )
@@ -84,9 +84,10 @@ async function onSubmit() {
84
84
  })
85
85
  if (success) {
86
86
  intermediateState.value.email = form.email
87
+ intermediateState.value.password = form.password
88
+
87
89
  registrationModal.value = false
88
90
  confirmAccountOtpModal.value = true
89
- $toast.success(message)
90
91
  }
91
92
  }
92
93
  catch (error) {
@@ -117,7 +118,7 @@ function getUrl() {
117
118
  }
118
119
 
119
120
  function handleEnter(event: KeyboardEvent) {
120
- if (event.key === 'Enter' && !confirmAccountOtpModal.value) {
121
+ if (event.key === 'Enter') {
121
122
  onSubmit()
122
123
  }
123
124
  }
@@ -152,31 +153,31 @@ onBeforeUnmount(() => {
152
153
  @submit.prevent="onSubmit"
153
154
  >
154
155
  <h2 class="text-center text-2xl font-bold">
155
- {{ t('register.form.title') }}
156
+ {{ t('modals.id.register.title') }}
156
157
  </h2>
157
158
  <p class="text-center text-sm">
158
- {{ t('register.form.subtitle') }}
159
+ {{ t('modals.id.register.subtitle') }}
159
160
  </p>
160
161
  <a-input-standard
161
162
  v-model="form.email"
162
- :label="t('register.form.labels.email')"
163
+ :label="t('modals.id.register.labels.email')"
163
164
  :error="getError('email')"
164
165
  type="email"
165
166
  />
166
167
 
167
168
  <a-input-password
168
169
  v-model="form.password"
169
- :label="t('register.form.labels.password')"
170
+ :label="t('modals.id.register.labels.password')"
170
171
  :error="getError('password')"
171
172
  />
172
173
 
173
174
  <a-input-password
174
175
  v-model="form.password_confirmation"
175
- :label="t('register.form.labels.password_confirmation')"
176
+ :label="t('modals.id.register.labels.password_confirmation')"
176
177
  :error="getError('password_confirmation')"
177
178
  />
178
179
  <a-alert color="blue">
179
- {{ t('register.form.alert') }}
180
+ {{ t('modals.id.register.alert') }}
180
181
  <template #icon>
181
182
  <a-icon-info-circle />
182
183
  </template>
@@ -185,13 +186,13 @@ onBeforeUnmount(() => {
185
186
  v-model="agreement"
186
187
  side="right"
187
188
  >
188
- <i18n-t keypath="register.form.agreement.text">
189
+ <i18n-t keypath="modals.id.register.agreement.text">
189
190
  <template #link>
190
191
  <nuxt-link-locale
191
192
  class="text-blue-700"
192
193
  @click="getUrl"
193
194
  >
194
- {{ t('register.form.agreement.link') }}
195
+ {{ t('modals.id.register.agreement.link') }}
195
196
  </nuxt-link-locale>
196
197
  </template>
197
198
  </i18n-t>
@@ -200,17 +201,17 @@ onBeforeUnmount(() => {
200
201
  :disabled="!agreement"
201
202
  :loading="loading"
202
203
  >
203
- {{ t('register.form.continue') }}
204
+ {{ t('modals.id.register.continue') }}
204
205
  </a-button>
205
206
  <p class="text-center text-sm">
206
- {{ t('register.form.haveAcc') }}
207
+ {{ t('modals.id.register.haveAcc') }}
207
208
  </p>
208
209
  <a-button
209
210
  type="button"
210
211
  view="outline"
211
212
  @click="onLogin"
212
213
  >
213
- {{ t('register.form.enter') }}
214
+ {{ t('modals.id.register.enter') }}
214
215
  </a-button>
215
216
  </form>
216
217
  </template>
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import OtpInput from '#adata-ui/components/modals/two-factor/otp-input.vue'
2
+ import IdOtpInput from '#adata-ui/components/modals/id/IdOtpInput.vue'
3
3
  import { useAuthStore } from '#adata-ui/stores/auth.store'
4
4
 
5
5
  const { $toast } = useNuxtApp()
@@ -44,13 +44,22 @@ async function onConfirm() {
44
44
  }
45
45
  catch (error) {
46
46
  showError.value = true
47
- $toast.error(error.data.message)
48
47
  }
49
48
  finally {
50
49
  isLoading.value = false
51
50
  }
52
51
  }
53
52
 
53
+ function resetOtp() {
54
+ otp.value = ['', '', '', '', '', '']
55
+ showError.value = false
56
+ }
57
+
58
+ function onClose() {
59
+ resetOtp()
60
+ resetPasswordOtpModal.value = false
61
+ }
62
+
54
63
  async function onResend() {
55
64
  try {
56
65
  await $fetch(`${authApiURL}/password/email-otp`, {
@@ -63,6 +72,7 @@ async function onResend() {
63
72
  email: intermediateState.value.email,
64
73
  },
65
74
  })
75
+ resetOtp()
66
76
  runTimer()
67
77
  }
68
78
  catch (error) {
@@ -70,12 +80,6 @@ async function onResend() {
70
80
  }
71
81
  }
72
82
 
73
- function onClose() {
74
- otp.value = ['', '', '', '', '', '']
75
- showError.value = false
76
- resetPasswordOtpModal.value = false
77
- }
78
-
79
83
  function handleEnter(e: KeyboardEvent) {
80
84
  if (e.key === 'Enter') {
81
85
  onConfirm()
@@ -85,6 +89,7 @@ function handleEnter(e: KeyboardEvent) {
85
89
  const timer = ref(60)
86
90
 
87
91
  function runTimer() {
92
+ timer.value = 60
88
93
  const intervalId = setInterval(() => {
89
94
  if (!timer.value) clearInterval(intervalId)
90
95
  return timer.value--
@@ -116,7 +121,7 @@ onBeforeUnmount(() => {
116
121
  </p>
117
122
 
118
123
  <div v-if="timer > 0" class="text-2xl font-bold">
119
- {{ timer }} {{ t('register.modal.seconds') }}
124
+ {{ timer }} {{ t('modals.id.resetPasswordOtp.seconds') }}
120
125
  </div>
121
126
  <button
122
127
  v-else
@@ -127,25 +132,19 @@ onBeforeUnmount(() => {
127
132
  </button>
128
133
  </div>
129
134
 
130
- <otp-input v-model="otp" v-model:error="showError" />
131
-
132
- <div class="flex w-full gap-2">
133
- <a-button
134
- block
135
- view="outline"
136
- @click="onClose"
137
- >
138
- {{ t('actions.close') }}
139
- </a-button>
140
- <a-button
141
- block
142
- :loading="isLoading"
143
- :disabled="otpFormatted.length < 6"
144
- @click="onConfirm"
145
- >
146
- {{ t('actions.confirm') }}
147
- </a-button>
148
- </div>
135
+ <id-otp-input
136
+ v-model="otp"
137
+ v-model:error="showError"
138
+ @on-completed="onConfirm"
139
+ />
140
+
141
+ <a-button
142
+ block
143
+ view="outline"
144
+ @click="onClose"
145
+ >
146
+ {{ t('actions.close') }}
147
+ </a-button>
149
148
  </div>
150
149
  </template>
151
150