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.
@@ -0,0 +1,127 @@
1
+ <script setup lang="ts">
2
+ import OtpInput from '#adata-ui/components/modals/id/otp-input.vue'
3
+ import { useAuthStore } from '#adata-ui/stores/auth.store'
4
+
5
+ const { $toast } = useNuxtApp()
6
+ const { t, locale } = useI18n()
7
+ const { commonAuth } = useAppConfig()
8
+
9
+ const authStore = useAuthStore()
10
+ const { intermediateState } = storeToRefs(authStore)
11
+
12
+ const { twoFactorModal } = useIdModals()
13
+
14
+ const authApiURL = commonAuth.authApiURL
15
+
16
+ const otp = ref(['', '', '', '', '', ''])
17
+ const otpFormatted = computed(() => {
18
+ return otp.value.join('')
19
+ })
20
+ const showError = ref(false)
21
+ const isLoading = ref(false)
22
+
23
+ async function onConfirm() {
24
+ isLoading.value = true
25
+ const login = await fetch(`${authApiURL}/login`, {
26
+ method: 'POST',
27
+ credentials: 'include',
28
+ headers: {
29
+ 'Content-Type': 'application/json',
30
+ 'lang': locale.value,
31
+ },
32
+ body: JSON.stringify({
33
+ 'username': intermediateState.value.email,
34
+ 'password': intermediateState.value.password,
35
+ '2fa_code': otpFormatted.value,
36
+ }),
37
+ })
38
+ const { data, message } = await login.json().catch(() => ({}))
39
+
40
+ if (login.status > 202) {
41
+ if (login.status === 403) {
42
+ showError.value = true
43
+ isLoading.value = false
44
+ }
45
+ }
46
+ else {
47
+ const response = await fetch(`${authApiURL}/access/cookie`, {
48
+ method: 'GET',
49
+ credentials: 'include',
50
+ headers: {
51
+ lang: locale.value,
52
+ },
53
+ })
54
+ const { data: cookiesData } = await response.json()
55
+ if (cookiesData?.access_token) {
56
+ const { access_token, expire_in } = cookiesData
57
+ const hostname = location.hostname.split('.').reverse()
58
+
59
+ useCookie('accessToken', {
60
+ maxAge: expire_in,
61
+ domain: `.${hostname[1]}.${hostname[0]}`,
62
+ path: '/',
63
+ secure: true,
64
+ }).value = access_token
65
+ }
66
+ $toast.success(t('login.successfully'))
67
+ twoFactorModal.value = false
68
+ window.location.reload()
69
+ }
70
+ isLoading.value = false
71
+ }
72
+
73
+ function onClose() {
74
+ otp.value = ['', '', '', '', '', '']
75
+ showError.value = false
76
+ twoFactorModal.value = false
77
+ }
78
+
79
+ function handleEnter(e: KeyboardEvent) {
80
+ if (e.key === 'Enter') {
81
+ onConfirm()
82
+ }
83
+ }
84
+
85
+ onMounted(() => {
86
+ document.addEventListener('keyup', handleEnter)
87
+ })
88
+
89
+ onBeforeUnmount(() => {
90
+ document.removeEventListener('keyup', handleEnter)
91
+ })
92
+ </script>
93
+
94
+ <template>
95
+ <div class="flex flex-col items-center gap-4">
96
+ <p class="heading-02 text-center">
97
+ {{ t('modals.id.twoFactor.title') }}
98
+ </p>
99
+ <a-icon-hand-with-phone-light class="size-32 dark:hidden" />
100
+ <a-icon-hand-with-phone-dark class="hidden size-32 dark:block" />
101
+ <otp-input v-model="otp" v-model:error="showError" />
102
+ <p class="body-400 text-center">
103
+ {{ t('modals.id.twoFactor.code') }}
104
+ </p>
105
+ <div class="flex w-full gap-2">
106
+ <a-button
107
+ class="w-full"
108
+ view="outline"
109
+ @click="onClose"
110
+ >
111
+ {{ t('actions.close') }}
112
+ </a-button>
113
+ <a-button
114
+ :loading="isLoading"
115
+ :disabled="otpFormatted.length < 6"
116
+ class="w-full"
117
+ @click="onConfirm"
118
+ >
119
+ {{ t('actions.confirm') }}
120
+ </a-button>
121
+ </div>
122
+ </div>
123
+ </template>
124
+
125
+ <style scoped>
126
+
127
+ </style>
@@ -1,4 +1,8 @@
1
1
  <script lang="ts" setup>
2
+ defineProps<{
3
+ attempts?: number
4
+ }>()
5
+
2
6
  const length = 6 // Длина OTP
3
7
  const showError = defineModel('error')
4
8
  const otp = defineModel()
@@ -133,6 +137,10 @@ function moveFocus(index: number, to: string) {
133
137
  >
134
138
  {{ t('error.otp') }}
135
139
  </a-alert>
140
+
141
+ <p v-show="showError" class="mt-1 text-xs text-red-500 dark:text-red-400">
142
+ {{ t('modals.id.resetPasswordOtp.attempts', { n: attempts }) }}
143
+ </p>
136
144
  </div>
137
145
  </template>
138
146
 
@@ -1,19 +1,20 @@
1
1
  <script setup lang="ts">
2
2
  import AdataLogo from '#adata-ui/icons/adata-logo.vue'
3
+ import LinkedinIcon from '#adata-ui/icons/linkedin.vue'
3
4
  import YouTubeIcon from '#adata-ui/icons/socials/youtube.vue'
4
5
  import InstagramIcon from '#adata-ui/icons/socials/instagram.vue'
5
6
  import TelegramIcon from '#adata-ui/icons/socials/telegram.vue'
6
7
  import FaceBookIcon from '#adata-ui/icons/socials/face-book.vue'
7
8
  import TikTokIcon from '#adata-ui/icons/socials/tik-tok.vue'
8
- import LinkedinIcon from '#adata-ui/icons/linkedin.vue'
9
- import FooterAccordion from './ui/footer-acccordion.vue'
10
- import { PAGES } from '#adata-ui/shared/constans/pages'
9
+ import AFooterAccordion from '#adata-ui/components/navigation/footer/ui/a-footer-accordion.vue'
11
10
  import { buildLocalizedUrl } from '#adata-ui/utils/localizedNavigation'
12
- import { useFooterInfoLinks, useFooterNavigationLinks } from '#adata-ui/composables/useHeaderNavigationLinks'
11
+ import { PAGES } from '#adata-ui/shared/constans/pages'
12
+ import { AIconExpandWindow, AIconLocation, AIconMail, AIconPhone } from '#components'
13
13
 
14
- const { myLayer }: any = useAppConfig()
15
- const { t, locale } = useI18n()
16
14
  const year = new Date().getFullYear()
15
+ const { t, locale } = useI18n()
16
+ const { myLayer } = useAppConfig()
17
+ const mode = myLayer.mode
17
18
 
18
19
  const socialMedia = [
19
20
  {
@@ -48,35 +49,212 @@ const socialMedia = [
48
49
  }
49
50
  ]
50
51
 
51
- const info = useFooterInfoLinks()
52
+ const mainLinks = computed(() => ({
53
+ services: {
54
+ title: t('footer.services'),
55
+ link: buildLocalizedUrl(locale.value, `https://${mode}.kz`, PAGES.allServices),
56
+ items: [
57
+ [
58
+ {
59
+ title: t('footer.counterparties.title'),
60
+ link: buildLocalizedUrl(locale.value, `https://pk.${mode}.kz`, ''),
61
+ },
62
+ {
63
+ title: t('footer.fines.title'),
64
+ link: buildLocalizedUrl(locale.value, `https://avto.${mode}.kz`, PAGES.fines.main),
65
+ },
66
+ {
67
+ title: t('footer.work.title'),
68
+ link: `https://work.${mode}.kz` + PAGES.work.vacancy,
69
+ },
70
+ {
71
+ title: t('footer.analytics.title'),
72
+ link: buildLocalizedUrl(locale.value, `https://analytics-new.${mode}.kz`, PAGES.analytics.main),
73
+ },
74
+ {
75
+ title: t('footer.tenders.title'),
76
+ link: `https://tender.${mode}.kz` + PAGES.tender.main,
77
+ }
78
+ ],
79
+ [
80
+ {
81
+ title: t('footer.compliance.title'),
82
+ link: `https://ac.${mode}.kz` + PAGES.compliance.l,
83
+ isNew: true
84
+ },
85
+ {
86
+ title: t('header.products.edo.label'),
87
+ link: `https://edo.${mode}.kz` + PAGES.edo.l,
88
+ isNew: true
89
+ },
90
+ {
91
+ title: t('footer.fea.title'),
92
+ link: buildLocalizedUrl(locale.value, `https://tnved.${mode}.kz`, PAGES.fea.main),
93
+ isNew: true
94
+ },
95
+ ]
96
+ ]
97
+ },
98
+ useful: {
99
+ title: t('footer.useful'),
100
+ link: buildLocalizedUrl(locale.value, `https://${mode}.kz`, PAGES.usefulMain),
101
+ items: [
102
+ [
103
+ {
104
+ title: t('footer.info.api'),
105
+ link: buildLocalizedUrl(locale.value, `https://${mode}.kz`, PAGES.apiDescription),
106
+ },
107
+ {
108
+ title: t('footer.info.userAgreement'),
109
+ link: buildLocalizedUrl(locale.value, `https://${mode}.kz`, PAGES.userAgreement),
110
+ },
111
+ {
112
+ title: t('footer.info.privacyPolicy'),
113
+ link: buildLocalizedUrl(locale.value, `https://${mode}.kz`, PAGES.privacy),
114
+ },
115
+ {
116
+ title: t('footer.info.vacancy'),
117
+ link: buildLocalizedUrl(locale.value, `https://${mode}.kz`, PAGES.vacancy),
118
+ },
119
+ {
120
+ title: t('footer.info.counterparty'),
121
+ link: buildLocalizedUrl(locale.value, `https://pk.${mode}.kz`, PAGES.pk.counterparty),
122
+ },
123
+ ]
124
+ ]
125
+ }
126
+ }))
127
+
128
+ const contactLinks = computed(() => ({
129
+ title: t('footer.contacts.title'),
130
+ link: buildLocalizedUrl(locale.value, `https://${mode}.kz`, PAGES.contacts),
131
+ items: [
132
+ {
133
+ title: t('footer.contacts.address'),
134
+ link: 'https://go.2gis.com/30j2f',
135
+ startIcon: AIconLocation,
136
+ endIcon: null,
137
+ },
138
+ {
139
+ title: '+7 (747) 120 34 67',
140
+ link: 'tel:+77471203467',
141
+ startIcon: AIconPhone,
142
+ endIcon: null,
143
+ },
144
+ {
145
+ title: 'info@adata.kz',
146
+ link: 'mailto:info@adata.kz',
147
+ startIcon: AIconMail,
148
+ endIcon: null,
149
+ },
150
+ {
151
+ title: t('footer.contacts.goToChat'),
152
+ link: 'https://api.whatsapp.com/send?phone=77712289041',
153
+ startIcon: null,
154
+ endIcon: AIconExpandWindow,
155
+ class: 'font-bold',
156
+ },
157
+ ],
158
+ }))
52
159
 
53
- const links = useFooterNavigationLinks()
54
160
  </script>
55
161
 
56
162
  <template>
57
- <div
163
+ <footer
58
164
  id="footer"
59
- class="bg-deepblue-900 dark:bg-gray-900 lg:pt-10"
165
+ class="bg-deepblue-900 dark:bg-gray-900 py-4 lg:py-5 rounded-t-3xl"
60
166
  >
61
- <div class="a-container mobile-padding py-5 text-white">
62
- <adata-logo
63
- filled
64
- :font-controlled="false"
65
- class="mb-8 h-[26px] w-[92px] lg:hidden"
66
- />
67
- <div class="grid gap-5 lg:grid-cols-5 lg:gap-10 xl:grid-cols-6">
68
- <div
69
- v-for="(link, key) in links"
70
- :key="key"
71
- >
72
- <footer-accordion
73
- :content="link.items"
74
- :label="t(link.title)"
75
- :link="link.link.replace(/\/$/, '')"
167
+ <div class="a-container mobile-padding py-5 text-white flex flex-col lg:flex-row justify-between gap-8">
168
+ <div class="flex flex-col gap-8 justify-between">
169
+ <div class="flex flex-col gap-4">
170
+ <adata-logo
171
+ filled
172
+ :font-controlled="false"
173
+ class="shrink-0"
76
174
  />
175
+
176
+ <p class="text-sm">
177
+ {{ t('footer.subtitle') }}
178
+ </p>
179
+
180
+ <div class="hidden lg:flex gap-8">
181
+ <nuxt-link
182
+ v-for="(social, idx) in socialMedia"
183
+ :key="idx"
184
+ target="_blank"
185
+ :to="social.link"
186
+ :aria-label="social.name"
187
+ >
188
+ <span class="visually-hidden"> {{ social.name }}</span>
189
+ <component
190
+ :is="social.icon"
191
+ filled
192
+ :font-controlled="false"
193
+ class="social-icon size-7"
194
+ />
195
+ </nuxt-link>
196
+ </div>
77
197
  </div>
198
+
199
+ <p class="hidden lg:block text-[10px]">
200
+ {{ year }} © {{ t('footer.LLP') }} "Alldata"
201
+ </p>
78
202
  </div>
79
- <div class="flex grow justify-end gap-8 py-5">
203
+
204
+ <div class="flex gap-6 lg:gap-[75px] flex-col lg:flex-row">
205
+ <div class="flex gap-[18px] lg:gap-[75px] flex-col lg:flex-row">
206
+ <div
207
+ v-for="(item, idx) in mainLinks"
208
+ :key="idx"
209
+ >
210
+ <a-footer-accordion
211
+ :label="item.title"
212
+ :link="item?.link?.replace(/\/$/, '')"
213
+ :content="item.items"
214
+ />
215
+ </div>
216
+ </div>
217
+ <!--noindex-->
218
+ <div class="flex flex-col gap-2 items-start" data-nosnippet>
219
+ <nuxt-link
220
+ class="text-sm font-semibold pb-3 pr-4 border-b-[0.5px] border-white cursor-pointer"
221
+ :to="contactLinks.link?.replace(/\/$/, '')"
222
+ >
223
+ {{ contactLinks.title }}
224
+ </nuxt-link>
225
+
226
+ <div class="flex flex-col gap-3 lg:gap-2">
227
+ <div
228
+ v-for="(link, idx) in contactLinks.items"
229
+ :key="idx"
230
+ class="flex gap-2 items-center"
231
+ >
232
+ <nuxt-link
233
+ class="text-xs font-medium flex gap-2 items-center cursor-pointer"
234
+ :class="link.class"
235
+ :to="link.link?.replace(/\/$/, '')"
236
+ >
237
+ <component
238
+ :is="link.startIcon"
239
+ v-if="link.startIcon"
240
+ class="size-4 shrink-0"
241
+ />
242
+
243
+ <p>{{ link.title }}</p>
244
+
245
+ <component
246
+ :is="link.endIcon"
247
+ v-if="link.endIcon"
248
+ class="size-4 shrink-0"
249
+ />
250
+ </nuxt-link>
251
+ </div>
252
+ </div>
253
+ </div>
254
+ <!--/noindex-->
255
+ </div>
256
+
257
+ <div class="lg:hidden flex gap-8 flex-wrap">
80
258
  <nuxt-link
81
259
  v-for="(social, idx) in socialMedia"
82
260
  :key="idx"
@@ -88,39 +266,14 @@ const links = useFooterNavigationLinks()
88
266
  :is="social.icon"
89
267
  filled
90
268
  :font-controlled="false"
91
- class="social-icon h-[28px] w-[28px]"
269
+ class="social-icon size-7"
92
270
  />
93
271
  </nuxt-link>
94
272
  </div>
273
+
274
+ <p class="lg:hidden text-[10px]">
275
+ {{ year }} © {{ t('footer.LLP') }} "Alldata"
276
+ </p>
95
277
  </div>
96
- <div class="relative">
97
- <div class="a-container mobile-padding py-5">
98
- <div
99
- class="flex flex-col gap-10 text-xs text-white before:absolute before:left-0 before:top-0 before:h-[0.5px] before:w-full before:bg-white lg:flex-row lg:items-center lg:gap-5"
100
- >
101
- <div class="flex flex-col gap-4 lg:flex-row lg:gap-6">
102
- <nuxt-link-locale
103
- v-for="(item, idx) in info"
104
- :key="idx"
105
- target="_blank"
106
- class="text-[10px]"
107
- :to="item.link"
108
- >
109
- {{ t(item.title) }}
110
- </nuxt-link-locale>
111
- </div>
112
- <div class="flex justify-center lg:grow lg:items-center lg:justify-end lg:gap-4">
113
- <div class="flex flex-col gap-1">
114
- <span>{{ year }} © {{ t('footer.LLP') }} "Alldata"</span>
115
- </div>
116
- <adata-logo
117
- filled
118
- :font-controlled="false"
119
- class="hidden h-[26px] w-[92px] lg:block"
120
- />
121
- </div>
122
- </div>
123
- </div>
124
- </div>
125
- </div>
278
+ </footer>
126
279
  </template>
@@ -16,8 +16,6 @@ const props = defineProps<Props>()
16
16
 
17
17
  const isDesktop = useMediaQuery('(max-width: 1024px)')
18
18
 
19
- const { t } = useI18n()
20
-
21
19
  const open = ref(false)
22
20
  function toFinalValues(el: HTMLDivElement) {
23
21
  el.style.height = el.scrollHeight + 'px'
@@ -46,7 +44,7 @@ const filterCheck = computed(() => {
46
44
  :class="{ 'cursor-pointer': link }"
47
45
  :to="link?.replace(/\/$/, '')"
48
46
  >
49
- {{ t(label) }}
47
+ {{ label }}
50
48
  </nuxt-link>
51
49
 
52
50
  <div class="transition-all lg:hidden cursor-pointer">
@@ -85,7 +83,7 @@ const filterCheck = computed(() => {
85
83
  class="text-xs lg:font-medium flex gap-2 items-center cursor-pointer"
86
84
  :to="link.link?.replace(/\/$/, '')"
87
85
  >
88
- {{ t(link.title) }}
86
+ {{ link.title }}
89
87
  </nuxt-link>
90
88
 
91
89
  <div
@@ -11,7 +11,7 @@
11
11
  class="flex flex-col gap-2 "
12
12
  >
13
13
  <div class="heading-04">
14
- {{ t(item.title) }}
14
+ {{ item.title }}
15
15
  </div>
16
16
  <a
17
17
  class="flex items-center gap-1 text-deepblue-900 dark:text-white"
@@ -22,7 +22,7 @@
22
22
  class="shrink-0"
23
23
  />
24
24
  <div class=" body-400">
25
- {{ t(item.subtitle) }}
25
+ {{ item.subtitle }}
26
26
  </div>
27
27
  </a>
28
28
  </div>
@@ -61,7 +61,7 @@
61
61
  target="_blank"
62
62
  >
63
63
  <div class="body-400 line-clamp-1 text-blue-600">
64
- {{ t(link.subtitle) }}
64
+ {{ link.subtitle }}
65
65
  </div>
66
66
  <component
67
67
  :is="link.icon"
@@ -105,33 +105,33 @@ import {useContacts} from "#adata-ui/composables/projectState";
105
105
 
106
106
  const {t} = useI18n()
107
107
  const contacts = ref(useContacts())
108
- const AlmatyItems = [
108
+ const AlmatyItems = computed(() => ([
109
109
  {
110
- title: 'header.contacts.almaty.adress.title',
111
- subtitle: 'header.contacts.almaty.adress.subtitle',
110
+ title: t('header.contacts.almaty.adress.title'),
111
+ subtitle: t('header.contacts.almaty.adress.subtitle'),
112
112
  icon: Location,
113
113
  to: 'https://yandex.kz/maps/ru/org/avrora_kholding/1099794306/?ll=76.912479%2C43.208928&z=17.72'
114
114
  },
115
115
  {
116
- title: 'header.contacts.almaty.email.title',
116
+ title: t('header.contacts.almaty.email.title'),
117
117
  subtitle: 'info@adata.kz',
118
118
  icon: Mail,
119
119
  to: 'mailto:info@adata.kz'
120
120
  },
121
121
  {
122
- title: 'header.contacts.almaty.schedule.title',
123
- subtitle: 'header.contacts.almaty.schedule.subtitle',
122
+ title: t('header.contacts.almaty.schedule.title'),
123
+ subtitle: t('header.contacts.almaty.schedule.subtitle'),
124
124
  icon: Clock,
125
125
  },
126
- ]
126
+ ]))
127
127
 
128
- const technicalSupport = [
128
+ const technicalSupport = computed(() => ([
129
129
  {
130
- subtitle: 'header.contacts.almaty.support.linkTitle',
130
+ subtitle: t('header.contacts.almaty.support.linkTitle'),
131
131
  icon: ExpandWindow,
132
132
  to: "https://api.whatsapp.com/send/?phone=77712289041"
133
133
  }
134
- ]
134
+ ]))
135
135
 
136
136
  const newContacts = computed(()=> contacts.value.map(item => ({
137
137
  subtitle: item.contact,
@@ -8,7 +8,7 @@
8
8
  <div class="xl:grid grid-cols-2 grid-rows-3 flex flex-wrap gap-4 ">
9
9
  <div v-for="(item, index) in AstanaItems" class="flex flex-col gap-2 " :class="{'col-span-2':index===0 }">
10
10
  <div class="heading-04">
11
- {{ t(item.title) }}
11
+ {{ item.title }}
12
12
  </div>
13
13
  <a
14
14
  class="flex items-center gap-1 text-deepblue-900 dark:text-white"
@@ -17,7 +17,7 @@
17
17
  >
18
18
  <component :is="item.icon"/>
19
19
  <div class=" body-400">
20
- {{ t(item.subtitle) }}
20
+ {{ item.subtitle }}
21
21
  </div>
22
22
  </a>
23
23
  </div>
@@ -35,31 +35,31 @@ import Clock from "#adata-ui/icons/clock.vue";
35
35
  import Whatsapp from "#adata-ui/icons/whatsapp.vue";
36
36
 
37
37
  const {t} = useI18n()
38
- const AstanaItems = [
38
+ const AstanaItems = computed(() => ([
39
39
  {
40
- title: 'header.contacts.astana.adress.title',
41
- subtitle: 'header.contacts.astana.adress.subtitle',
40
+ title: t('header.contacts.astana.adress.title'),
41
+ subtitle: t('header.contacts.astana.adress.subtitle'),
42
42
  icon: Location,
43
43
  to: 'https://yandex.kz/maps/ru/org/adata_consulting/214484722242/?ll=71.429573%2C51.131319&z=20.56'
44
44
  },
45
45
  {
46
- title: 'header.contacts.astana.email.title',
46
+ title: t('header.contacts.astana.email.title'),
47
47
  subtitle: 'info@adata-c.kz',
48
48
  icon: Mail,
49
49
  to: 'mailto:info@adata-c.kz'
50
50
  },
51
51
  {
52
- title: 'header.contacts.astana.schedule.title',
53
- subtitle: 'header.contacts.astana.schedule.subtitle',
52
+ title: t('header.contacts.astana.schedule.title'),
53
+ subtitle: t('header.contacts.astana.schedule.subtitle'),
54
54
  icon: Clock,
55
55
  },
56
56
  {
57
- title: 'header.contacts.astana.sellDep.title',
57
+ title: t('header.contacts.astana.sellDep.title'),
58
58
  subtitle: '+7 700 744 00 10',
59
59
  icon: Whatsapp,
60
60
  to: "https://api.whatsapp.com/send/?phone=77007440010"
61
61
  },
62
- ]
62
+ ]))
63
63
  </script>
64
64
 
65
65
  <style scoped>