adata-ui 2.0.22 → 2.0.24

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 (30) hide show
  1. package/.editorconfig +12 -12
  2. package/app.config.ts +3 -2
  3. package/components/elements/curve-block/ACurveBlock.vue +3 -6
  4. package/components/elements/feature-description/AFeatureDescription.vue +2 -2
  5. package/components/elements/select/ASelect.vue +50 -5
  6. package/components/modals/SubmitApplicationModal.vue +3 -0
  7. package/components/modals/id/AuthModal.vue +76 -0
  8. package/components/modals/id/IdEmailModal.vue +29 -0
  9. package/components/{forms/login/ALogin.vue → modals/id/IdLoginModal.vue} +38 -38
  10. package/components/modals/id/IdModals.vue +19 -0
  11. package/components/modals/id/IdNewPasswordModal.vue +137 -0
  12. package/components/modals/id/IdPasswordSuccessfulModal.vue +26 -0
  13. package/components/modals/id/IdRecoveryModal.vue +114 -0
  14. package/components/{forms/registration/ARegistration.vue → modals/id/IdRegistrationModal.vue} +14 -17
  15. package/components/modals/two-factor/two-factor.vue +9 -8
  16. package/components/navigation/bottom-navigation/ABottomNavigation.vue +33 -29
  17. package/components/navigation/footer/NewFooter.vue +3 -2
  18. package/components/navigation/header/AHeader.vue +32 -39
  19. package/components/navigation/header/AlmatyContacts.vue +3 -1
  20. package/components/navigation/header/AstanaContacts.vue +3 -1
  21. package/composables/modalsState.ts +0 -3
  22. package/composables/useIdModals.ts +17 -0
  23. package/eslint.config.mjs +45 -0
  24. package/lang/ru.ts +139 -107
  25. package/nuxt.config.ts +15 -7
  26. package/package.json +7 -5
  27. package/.eslintrc.cjs +0 -4
  28. package/.prettierignore +0 -24
  29. package/.prettierrc +0 -10
  30. package/components/modals/AuthModal.vue +0 -50
package/.editorconfig CHANGED
@@ -1,12 +1,12 @@
1
- root = true
2
-
3
- [*]
4
- indent_size = 2
5
- indent_style = space
6
- end_of_line = lf
7
- charset = utf-8
8
- trim_trailing_whitespace = true
9
- insert_final_newline = true
10
-
11
- [*.md]
12
- trim_trailing_whitespace = false
1
+ root = true
2
+
3
+ [*]
4
+ indent_style = space
5
+ indent_size = 2
6
+ end_of_line = lf
7
+ charset = utf-8
8
+ trim_trailing_whitespace = true
9
+ insert_final_newline = true
10
+
11
+ [*.md]
12
+ trim_trailing_whitespace = false
package/app.config.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export default defineAppConfig({
2
2
  myLayer: {
3
- name: 'Adata UI v2'
4
- }
3
+ name: 'Adata UI v2',
4
+ },
5
5
  })
6
6
 
7
7
  declare module '@nuxt/schema' {
@@ -22,6 +22,7 @@ declare module '@nuxt/schema' {
22
22
  complianceUrl?: string
23
23
  counterParty?: string
24
24
  langIsOn?: boolean
25
+ authMode?: 'local' | 'separate'
25
26
  }
26
27
  }
27
28
  }
@@ -13,12 +13,11 @@ const props = defineProps<{
13
13
  <component
14
14
  :is="to ? NuxtLinkLocale : 'button'"
15
15
  :to="to"
16
- class="curve-block relative w-fit cursor-pointer"
16
+ class="curve-block relative w-full cursor-pointer"
17
17
  active-class="active-item"
18
18
  >
19
19
  <svg
20
- width="306"
21
- height="182"
20
+ class="w-full"
22
21
  viewBox="-1 -1 308 184"
23
22
  xmlns="http://www.w3.org/2000/svg"
24
23
  >
@@ -79,9 +78,7 @@ const props = defineProps<{
79
78
  </div>
80
79
 
81
80
  <svg
82
- class="absolute bottom-0 right-0"
83
- width="48"
84
- height="48"
81
+ class="absolute bottom-0 right-0 size-12 2xl:size-14"
85
82
  viewBox="0 0 48 48"
86
83
  xmlns="http://www.w3.org/2000/svg"
87
84
  >
@@ -72,7 +72,7 @@ onUnmounted(() => clearTimer())
72
72
  />
73
73
  </div>
74
74
  <p
75
- class="font-semibold text-xl transition-colors duration-300 ease-in-out"
75
+ class="font-semibold text-base lg:text-xl transition-colors duration-300 ease-in-out"
76
76
  :class="selected === idx ? 'dark:text-[#E3E5E8]' : 'text-gray-600 dark:text-gray-200'"
77
77
  >
78
78
  {{ item.title }}
@@ -88,7 +88,7 @@ onUnmounted(() => clearTimer())
88
88
  <div
89
89
  v-if="selected === idx"
90
90
  >
91
- <p class="text-sm pt-3">
91
+ <p class="text-xs lg:text-sm pt-3">
92
92
  {{ item.description }}
93
93
  </p>
94
94
  </div>
@@ -6,6 +6,8 @@ import { onClickOutside, useToggle } from '@vueuse/core'
6
6
  import { isEqual } from 'lodash-es'
7
7
  import type { Ref } from 'vue'
8
8
 
9
+ import { useFloating, offset, flip, shift, size as floatingSize, autoUpdate } from '@floating-ui/vue'
10
+
9
11
  type Props = {
10
12
  label: string
11
13
  required?: boolean
@@ -61,6 +63,32 @@ const props = withDefaults(defineProps<Props>(), {
61
63
 
62
64
  const emits = defineEmits<Emits>()
63
65
 
66
+
67
+
68
+ const reference = ref<HTMLElement | null>(null)
69
+ const floating = ref<HTMLElement | null>(null)
70
+ const { x, y, strategy, update } = useFloating(reference, floating, {
71
+ placement: 'bottom-start',
72
+ strategy: 'absolute',
73
+ middleware: [
74
+ offset(6),
75
+ flip({ fallbackPlacements: ['top-start'] }),
76
+ shift({ padding: 8 }),
77
+ floatingSize({
78
+ apply({ availableHeight, elements, rects }) {
79
+ const el = elements.floating as HTMLElement
80
+ el.style.width = `${rects.reference.width}px`
81
+ el.style.maxHeight = `${Math.max(140, Math.min(availableHeight, 320))}px`
82
+ el.style.overflowY = 'auto'
83
+ },
84
+ }),
85
+ ],
86
+ })
87
+
88
+ let cleanup: (() => void) | null = null
89
+
90
+
91
+
64
92
  const modelValue = defineModel<T | T[] | null | any>({ required: true })
65
93
  const search = defineModel<T | null | any>('search')
66
94
  const isOpen = defineModel<T | null | any>('open', {
@@ -235,10 +263,21 @@ onUpdated(() => (countedVisibleItems.value = countOfVisibleItems.value))
235
263
  defineExpose({
236
264
  onClear
237
265
  })
266
+ watch(() => isOpen.value, (open) => {
267
+ if (open && reference.value && floating.value) {
268
+ cleanup = autoUpdate(reference.value, floating.value, update)
269
+ } else {
270
+ cleanup?.()
271
+ cleanup = null
272
+ }
273
+ })
274
+
275
+ onUnmounted(() => cleanup?.())
238
276
  </script>
239
277
  <template>
240
278
  <div ref="wrapper" class="relative w-full rounded-md text-sm">
241
279
  <button
280
+ ref="reference"
242
281
  :class="[
243
282
  clearable ? 'pr-16' : 'pr-9',
244
283
  {
@@ -262,7 +301,7 @@ defineExpose({
262
301
  </span>
263
302
  <component :is="startIcon" v-if="startIcon" />
264
303
 
265
- <span :class="{ 'py-1': size === 'md' }" class="w-full text-start">
304
+ <span :class="{ 'py-1': size === 'md' }" class=" w-full text-start">
266
305
  <span v-if="valueToShow">
267
306
  <span v-if="!multiple" class="line-clamp-1 text-deepblue-900 dark:text-gray-200">
268
307
  <slot name="single-selected" :value="valueToShow">{{ valueToShow[keyToShow] }}</slot>
@@ -310,10 +349,16 @@ defineExpose({
310
349
  </span>
311
350
  </button>
312
351
  <transition>
313
- <div
314
- v-if="isOpen"
315
- class="absolute z-20 mt-1 w-full rounded-md bg-white shadow-[0_1px_8px_0_rgba(139,146,156,0.3)] dark:bg-gray-900"
316
- >
352
+ <div
353
+ v-if="isOpen"
354
+ ref="floating"
355
+ class="z-[10000] rounded-md bg-white shadow-[0_1px_8px_0_rgba(139,146,156,0.3)] dark:bg-gray-900"
356
+ :style="{
357
+ position: strategy,
358
+ left: x != null ? `${x}px` : '',
359
+ top: y != null ? `${y}px` : '',
360
+ }"
361
+ >
317
362
  <slot :options="options" name="options">
318
363
  <ul class="select m-2 max-h-[250px] overflow-y-auto overflow-x-hidden">
319
364
  <li v-if="searchFromOut" class="p-1">
@@ -64,11 +64,13 @@ defineExpose({
64
64
  v-model="form.name"
65
65
  :label="t('modals.submit_application_modal.labels.name')"
66
66
  :error="getError('name')"
67
+ required
67
68
  />
68
69
  <a-input-standard
69
70
  v-model="form.email"
70
71
  :label="t('modals.submit_application_modal.labels.email')"
71
72
  :error="getError('email')"
73
+ required
72
74
  />
73
75
  <a-input-standard
74
76
  v-model="form.phone"
@@ -83,6 +85,7 @@ defineExpose({
83
85
  v-model="form.comment"
84
86
  :label="t('modals.submit_application_modal.labels.comment')"
85
87
  :error="getError('comment')"
88
+ required
86
89
  />
87
90
  </div>
88
91
  </div>
@@ -0,0 +1,76 @@
1
+ <script setup lang="ts">
2
+ const emit = defineEmits<{
3
+ (e: 'close'): void
4
+ }>()
5
+ const { t } = useI18n()
6
+ const isOpen = useAuthModal()
7
+ const { myLayer } = useAppConfig()
8
+ const loginUrl = myLayer.loginUrl
9
+
10
+ const { loginModal, registrationModal } = useIdModals()
11
+
12
+ function goAuth() {
13
+ if (myLayer.authMode !== 'local') {
14
+ if (window) {
15
+ let fullPath = encodeURIComponent(window.location.toString())
16
+ if (fullPath.includes('basic-info')) {
17
+ fullPath = fullPath.replace('%2Fcounterparty%2Fmain', '').replace('%2Fbasic-info', '')
18
+ }
19
+ window.location.href = `${loginUrl}?url=${fullPath}`
20
+ }
21
+ }
22
+ else {
23
+ isOpen.value = false
24
+ loginModal.value = true
25
+ }
26
+ }
27
+
28
+ function goRegister() {
29
+ if (myLayer.authMode !== 'local') {
30
+ if (window) {
31
+ let fullPath = encodeURIComponent(window.location.toString())
32
+ if (fullPath.includes('basic-info')) {
33
+ fullPath = fullPath.replace('%2Fcounterparty%2Fmain', '').replace('%2Fbasic-info', '')
34
+ }
35
+ window.location.href = `${loginUrl}register?url=${fullPath}`
36
+ }
37
+ }
38
+ else {
39
+ isOpen.value = false
40
+ registrationModal.value = true
41
+ }
42
+ }
43
+ </script>
44
+
45
+ <template>
46
+ <a-modal v-model="isOpen" :title="t('modals.auth.title')">
47
+ <div class="flex flex-col items-center gap-5 text-center text-sm">
48
+ <a-ill-door />
49
+ <div>{{ t('modals.auth.content') }}</div>
50
+ <a-button
51
+ block
52
+ variant="success"
53
+ @click="goAuth"
54
+ >
55
+ {{ t('modals.buttons.logIn') }}
56
+ </a-button>
57
+ <div>{{ t('modals.auth.firstTime') }}</div>
58
+ </div>
59
+ <template #footer>
60
+ <div class="flex gap-2">
61
+ <a-button
62
+ view="outline"
63
+ block
64
+ @click="emit('close')"
65
+ >
66
+ {{ t('modals.buttons.close') }}
67
+ </a-button>
68
+ <a-button block @click="goRegister">
69
+ {{ t('modals.buttons.register') }}
70
+ </a-button>
71
+ </div>
72
+ </template>
73
+ </a-modal>
74
+ </template>
75
+
76
+ <style scoped></style>
@@ -0,0 +1,29 @@
1
+ <script setup lang="ts">
2
+ const { emailModal } = useIdModals()
3
+ const { t } = useI18n()
4
+ </script>
5
+
6
+ <template>
7
+ <a-modal v-model="emailModal">
8
+ <div class="flex flex-col items-stretch gap-5 rounded-lg text-center">
9
+ <h2 class="text-center text-2xl font-bold">
10
+ {{ t('modals.id.email.title') }}
11
+ </h2>
12
+ <div class="flex justify-center">
13
+ <span class="flex size-[100px] items-center justify-center rounded-full bg-[#BDC7CE26]">
14
+ <a-ill-info />
15
+ </span>
16
+ </div>
17
+ <p class="text-center text-sm">
18
+ {{ t('modals.id.email.content') }}
19
+ </p>
20
+ <a-button @click="emailModal = false">
21
+ {{ t('actions.close') }}
22
+ </a-button>
23
+ </div>
24
+ </a-modal>
25
+ </template>
26
+
27
+ <style scoped>
28
+
29
+ </style>
@@ -1,10 +1,10 @@
1
1
  <script setup lang="ts">
2
- import { useToggle } from '@vueuse/shared'
3
- import * as z from 'zod'
4
2
  import AConfirmationEmail from '#adata-ui/components/modals/AConfirmationEmail.vue'
5
3
  import Resend from '#adata-ui/components/modals/Resend.vue'
6
4
  import TwoFactor from '#adata-ui/components/modals/two-factor/two-factor.vue'
7
5
  import { navigateToLocalizedPage } from '#adata-ui/utils/localizedNavigation'
6
+ import { useToggle } from '@vueuse/shared'
7
+ import * as z from 'zod'
8
8
 
9
9
  const { myLayer, commonAuth } = useAppConfig()
10
10
  const authApiURL = commonAuth.authApiURL
@@ -19,8 +19,7 @@ const { t, locale } = useI18n()
19
19
  const route = useRoute()
20
20
  const submitted = ref(false)
21
21
  const accessToken = ref(null)
22
- const loginModal = useLoginModal()
23
- const registrationModal = useRegistrationModal()
22
+ const { loginModal, registrationModal, recoveryModal } = useIdModals()
24
23
 
25
24
  export interface ILoginForm {
26
25
  username: string
@@ -29,7 +28,7 @@ export interface ILoginForm {
29
28
 
30
29
  const loginSchema = z.object({
31
30
  username: z.string().nonempty(t('register.form.errors.required')).email(t('register.form.errors.email')),
32
- password: z.string().nonempty(t('register.form.errors.required'))
31
+ password: z.string().nonempty(t('register.form.errors.required')),
33
32
  })
34
33
 
35
34
  const validation = computed(() => {
@@ -39,13 +38,13 @@ const validation = computed(() => {
39
38
  })
40
39
 
41
40
  function getError(path: string) {
42
- return validation.value?.issues.find((issue) => issue.path[0] === path)?.message
41
+ return validation.value?.issues.find(issue => issue.path[0] === path)?.message
43
42
  }
44
43
 
45
- function savePassAndLogin(form: { username: string; password: string }) {
44
+ function savePassAndLogin(form: { username: string, password: string }) {
46
45
  const { username, password } = form
47
46
  const cookieOptions = {
48
- expires: rememberMe.value ? new Date(Date.now() + 14 * 24 * 60 * 60 * 1000) : undefined // Срок действия 14 дней
47
+ expires: rememberMe.value ? new Date(Date.now() + 14 * 24 * 60 * 60 * 1000) : undefined, // Срок действия 14 дней
49
48
  }
50
49
  useCookie('username', cookieOptions).value = username
51
50
  useCookie('password', cookieOptions).value = password
@@ -58,7 +57,7 @@ function clearAuthCookie() {
58
57
  const loading = ref(false)
59
58
  const form: ILoginForm = reactive({
60
59
  username: useCookie('username').value || '',
61
- password: useCookie('password').value || ''
60
+ password: useCookie('password').value || '',
62
61
  })
63
62
  const rememberMe = ref(false)
64
63
 
@@ -71,33 +70,36 @@ async function submit() {
71
70
  credentials: 'include',
72
71
  headers: {
73
72
  'Content-Type': 'application/json',
74
- lang: locale.value
73
+ 'lang': locale.value,
75
74
  },
76
75
  body: JSON.stringify({
77
76
  username: form.username.trim().toLowerCase(),
78
- password: form.password.toString()
79
- })
77
+ password: form.password.toString(),
78
+ }),
80
79
  })
81
80
  const { data, message } = await login.json().catch(() => ({}))
82
81
  if (data) accessToken.value = data?.access_token
83
82
  if (login.status > 202) {
84
83
  if (login.status === 422) {
85
84
  $toast.error(t('error.validation'))
86
- } else {
85
+ }
86
+ else {
87
87
  $toast.error(message)
88
88
  }
89
- } else {
89
+ }
90
+ else {
90
91
  rememberMe.value ? savePassAndLogin(form) : clearAuthCookie()
91
92
  if (data.is_2fa_enabled) {
92
93
  isTwoFactorOpen.value = true
93
- } else if (data.email_is_verified) {
94
+ }
95
+ else if (data.email_is_verified) {
94
96
  const response = await fetch(`${authApiURL}/access/cookie`, {
95
97
  method: 'GET',
96
98
  credentials: 'include',
97
99
  headers: {
98
100
  Authorization: `Bearer ${accessToken.value}`,
99
- lang: locale.value
100
- }
101
+ lang: locale.value,
102
+ },
101
103
  })
102
104
  const { data: cookiesData } = await response.json()
103
105
  if (cookiesData) {
@@ -108,7 +110,8 @@ async function submit() {
108
110
  $toast.success(t('login.successfully'))
109
111
  loginModal.value = false
110
112
  window.location.reload()
111
- } else {
113
+ }
114
+ else {
112
115
  isConfirmationEmailModal.value = true
113
116
  }
114
117
  }
@@ -140,7 +143,7 @@ function toTariffs() {
140
143
  locale,
141
144
  projectUrl: myLayer.landingUrl,
142
145
  path: '/tariffs',
143
- target: '_self'
146
+ target: '_self',
144
147
  })
145
148
  }
146
149
 
@@ -159,13 +162,13 @@ async function handleConfirmOtp(otpCode: string) {
159
162
  credentials: 'include',
160
163
  headers: {
161
164
  'Content-Type': 'application/json',
162
- lang: locale.value
165
+ 'lang': locale.value,
163
166
  },
164
167
  body: JSON.stringify({
165
- username: form.username.trim(),
166
- password: form.password.toString(),
167
- '2fa_code': otpCode
168
- })
168
+ 'username': form.username.trim(),
169
+ 'password': form.password.toString(),
170
+ '2fa_code': otpCode,
171
+ }),
169
172
  })
170
173
  const { data, message } = await login.json().catch(() => ({}))
171
174
 
@@ -174,14 +177,15 @@ async function handleConfirmOtp(otpCode: string) {
174
177
  showOtpError.value = true
175
178
  isLoadingOtp.value = false
176
179
  }
177
- } else {
180
+ }
181
+ else {
178
182
  const response = await fetch(`${authApiURL}/access/cookie`, {
179
183
  method: 'GET',
180
184
  credentials: 'include',
181
185
  headers: {
182
186
  Authorization: `Bearer ${accessToken.value}`,
183
- lang: locale.value
184
- }
187
+ lang: locale.value,
188
+ },
185
189
  })
186
190
  const { data: cookiesData } = await response.json()
187
191
  if (cookiesData?.access_token) {
@@ -199,22 +203,18 @@ async function handleConfirmOtp(otpCode: string) {
199
203
 
200
204
  function handleEnter(e: KeyboardEvent) {
201
205
  if (
202
- e.key === 'Enter' &&
203
- !isTwoFactorOpen.value &&
204
- !isConfirmationEmailModal.value &&
205
- !isResendModal.value
206
+ e.key === 'Enter'
207
+ && !isTwoFactorOpen.value
208
+ && !isConfirmationEmailModal.value
209
+ && !isResendModal.value
206
210
  ) {
207
211
  submit()
208
212
  }
209
213
  }
210
214
 
211
215
  function onForgotPassword() {
212
- return navigateToLocalizedPage({
213
- locale,
214
- projectUrl: myLayer.loginUrl.slice(0, -1),
215
- path: `/password-recovery`,
216
- target: '_self',
217
- })
216
+ loginModal.value = false
217
+ recoveryModal.value = true
218
218
  }
219
219
 
220
220
  watch(loginModal, (value) => {
@@ -236,7 +236,7 @@ onBeforeUnmount(() => {
236
236
 
237
237
  <template>
238
238
  <a-modal v-model="loginModal">
239
- <div class="flex flex-col gap-5 rounded-lg bg-white dark:bg-gray-900">
239
+ <div class="flex flex-col gap-5">
240
240
  <h1 class="heading-02 text-center">
241
241
  {{ $t('login.form.title') }}
242
242
  </h1>
@@ -0,0 +1,19 @@
1
+ <script setup lang="ts">
2
+ import IdEmailModal from '#adata-ui/components/modals/id/IdEmailModal.vue'
3
+ import IdLoginModal from '#adata-ui/components/modals/id/IdLoginModal.vue'
4
+ import IdNewPasswordModal from '#adata-ui/components/modals/id/IdNewPasswordModal.vue'
5
+ import IdPasswordSuccessfulModal from '#adata-ui/components/modals/id/IdPasswordSuccessfulModal.vue'
6
+ import IdRecoveryModal from '#adata-ui/components/modals/id/IdRecoveryModal.vue'
7
+ import IdRegistrationModal from '#adata-ui/components/modals/id/IdRegistrationModal.vue'
8
+ </script>
9
+
10
+ <template>
11
+ <id-login-modal />
12
+ <id-registration-modal />
13
+ <id-recovery-modal />
14
+ <id-email-modal />
15
+ <id-new-password-modal />
16
+ <id-password-successful-modal />
17
+ </template>
18
+
19
+ <style scoped></style>
@@ -0,0 +1,137 @@
1
+ <script setup lang="ts">
2
+ import * as z from 'zod'
3
+
4
+ const { $toast } = useNuxtApp()
5
+ const { commonAuth } = useAppConfig()
6
+ const route = useRoute()
7
+ const { t, locale } = useI18n()
8
+
9
+ const { email, token } = route.query
10
+ const authApiURL = commonAuth.authApiURL
11
+ const { loginModal, newPasswordModal, passwordSuccessfulModal } = useIdModals()
12
+
13
+ const form = reactive({
14
+ password: '',
15
+ password_confirmation: '',
16
+ })
17
+ const loading = ref(false)
18
+
19
+ const resetSchema = z.object({
20
+ password: z
21
+ .string()
22
+ .min(8, t('register.form.errors.low_security', { length: 8 }))
23
+ .regex(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/, t('register.form.errors.low_security')),
24
+ password_confirmation: z
25
+ .string()
26
+ .min(8, t('register.form.errors.low_security', { length: 8 }))
27
+ .regex(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/, t('register.form.errors.low_security')),
28
+ }).refine(
29
+ data => data.password === data.password_confirmation,
30
+ {
31
+ message: t('register.form.errors.sameAs'),
32
+ path: ['password_confirmation'],
33
+ },
34
+ )
35
+
36
+ const submitted = ref(false)
37
+
38
+ const validation = computed(() => {
39
+ if (!submitted.value) return null
40
+ const result = resetSchema.safeParse(form)
41
+ return result.success ? null : result.error
42
+ })
43
+
44
+ function getError(path: string) {
45
+ return validation.value?.issues?.find(issue => issue.path[0] === path)?.message
46
+ }
47
+
48
+ async function onSubmit() {
49
+ try {
50
+ submitted.value = true
51
+ if (validation.value) return
52
+ loading.value = true
53
+
54
+ const { data } = await $fetch(`${authApiURL}/password/reset`, {
55
+ method: 'POST',
56
+ credentials: 'include',
57
+ headers: {
58
+ lang: locale.value,
59
+ },
60
+ body: {
61
+ token,
62
+ email,
63
+ ...form,
64
+ },
65
+ })
66
+ if (data?.success) {
67
+ newPasswordModal.value = false
68
+ passwordSuccessfulModal.value = true
69
+ $toast.success(data.message)
70
+ }
71
+ }
72
+ catch (error) {
73
+ $toast.error(error?.data.message.password as string)
74
+ }
75
+ finally {
76
+ loading.value = false
77
+ }
78
+ }
79
+
80
+ // onMounted(() => {
81
+ // if (!email || !token) {
82
+ // newPasswordModal.value = false
83
+ // loginModal.value = true
84
+ // }
85
+ // })
86
+ </script>
87
+
88
+ <template>
89
+ <a-modal v-model="newPasswordModal">
90
+ <form
91
+ class="flex flex-col items-stretch gap-5"
92
+ novalidate
93
+ @submit.prevent="onSubmit"
94
+ >
95
+ <h2 class="text-center text-2xl font-bold">
96
+ {{ t('resetPassword.title') }}
97
+ </h2>
98
+ <p class="text-center text-sm">
99
+ {{ t('resetPassword.subtitle') }}
100
+ </p>
101
+
102
+ <a-input-password
103
+ v-model="form.password"
104
+ :label="t('register.form.labels.password')"
105
+ :error="getError('password')"
106
+ />
107
+
108
+ <a-alert color="blue">
109
+ {{ t('register.form.alert') }}
110
+ </a-alert>
111
+
112
+ <a-input-password
113
+ v-model="form.password_confirmation"
114
+ :label="t('register.form.labels.password_confirmation')"
115
+ :error="getError('password_confirmation')"
116
+ />
117
+
118
+ <a-button
119
+ :loading="loading"
120
+ >
121
+ {{ t('passwordRecovery.form.recover') }}
122
+ </a-button>
123
+
124
+ <p class="text-center text-sm">
125
+ {{ t('passwordRecovery.form.or') }}
126
+ </p>
127
+
128
+ <a-button
129
+ type="button"
130
+ view="outline"
131
+ @click="newPasswordModal = false"
132
+ >
133
+ {{ t('actions.cancel') }}
134
+ </a-button>
135
+ </form>
136
+ </a-modal>
137
+ </template>
@@ -0,0 +1,26 @@
1
+ <script setup lang="ts">
2
+ const { passwordSuccessfulModal } = useIdModals()
3
+ </script>
4
+
5
+ <template>
6
+ <a-modal v-model="passwordSuccessfulModal">
7
+ <section class="flex flex-col items-stretch gap-4 text-center">
8
+ <h2 class="text-2xl font-bold">
9
+ {{ $t('resetPassword.successModal.title') }}
10
+ </h2>
11
+ <span class="mx-auto size-[100px] rounded-full bg-[#BDC7CE26]">
12
+ <a-ill-ok />
13
+ </span>
14
+ <p class="text-sm">
15
+ {{ $t('resetPassword.successModal.subtitle') }}
16
+ </p>
17
+ <a-button @click="passwordSuccessfulModal = false">
18
+ {{ $t('resetPassword.successModal.goToAuthorization') }}
19
+ </a-button>
20
+ </section>
21
+ </a-modal>
22
+ </template>
23
+
24
+ <style scoped>
25
+
26
+ </style>