adata-ui 2.1.40-beta.1 → 2.1.40-beta.10
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/elements/a-select-row/ASelectRowV2.vue +214 -0
- package/components/elements/button/AButtonV2.vue +112 -0
- package/components/elements/leave-note/ALeaveNote.vue +38 -17
- package/components/elements/segmented/ASegmentedV2.vue +58 -0
- package/components/elements/select/ASelectV2.vue +589 -0
- package/components/elements/show-more/AShowMoreV2.vue +26 -0
- package/components/elements/star-rating/AStarRating.vue +12 -4
- package/components/elements/status-badge-v2/StatusBadgeV2.vue +78 -0
- package/components/forms/checkbox/ACheckboxV2.vue +229 -0
- package/components/forms/feedback-form/FeedbackForm.vue +8 -8
- package/components/forms/input/AInputV2.vue +542 -0
- package/components/forms/input/textarea/ATextareaV2.vue +248 -0
- package/components/forms/toggle/AToggleV2.vue +71 -0
- package/components/modals/AnotherDeviceModal.vue +22 -18
- package/components/modals/ApplicationAcceptedModal.vue +12 -15
- package/components/modals/ConnectingTariffModal.vue +13 -20
- package/components/modals/Insufficient-funds-modal.vue +18 -33
- package/components/modals/LimitReachedModal.vue +4 -3
- package/components/modals/NoAccessModal.vue +9 -11
- package/components/modals/PaymentMethodModal.vue +1 -1
- package/components/modals/ReportBugConfirmModal.vue +12 -10
- package/components/modals/ReportBugModal.vue +42 -36
- package/components/modals/Resend.vue +9 -10
- package/components/modals/SubmitApplicationModal.vue +27 -24
- package/components/modals/id/IdConfirmAccountOtpModal.vue +4 -9
- package/components/modals/id/IdLoginModal.vue +3 -8
- package/components/modals/id/IdModals.vue +23 -10
- package/components/modals/id/IdTwoFactorModal.vue +3 -8
- package/components/navigation/header/AHeader.vue +5 -5
- package/components/navigation/header/HeaderLink.vue +15 -21
- package/components/navigation/header/NavList.vue +8 -32
- package/components/navigation/header/ProductMenu.vue +52 -50
- package/components/navigation/pill-tabs/APillTabsV2.vue +118 -0
- package/components/overlays/modal/AModalV2.vue +388 -0
- package/composables/useAccessTokenCookie.ts +25 -0
- package/composables/useChipOverflow.ts +92 -0
- package/composables/useHeaderNavigationLinks.ts +53 -23
- package/icons/arrow/arrow-right.vue +14 -0
- package/icons/briefcase.vue +14 -0
- package/icons/car-side.vue +16 -0
- package/icons/cart.vue +15 -0
- package/icons/download-rounded.vue +15 -0
- package/icons/shield-verified.vue +14 -0
- package/lang/en.ts +34 -4
- package/lang/kk.ts +35 -5
- package/lang/ru.ts +36 -6
- package/package.json +1 -1
- package/shared/constans/pages.ts +8 -2
|
@@ -3,28 +3,30 @@ const { t } = useI18n()
|
|
|
3
3
|
|
|
4
4
|
const isOpen = defineModel<boolean>({ required: true })
|
|
5
5
|
</script>
|
|
6
|
+
|
|
6
7
|
<template>
|
|
7
|
-
<a-modal
|
|
8
|
+
<a-modal-v2
|
|
8
9
|
v-model="isOpen"
|
|
9
10
|
width="424px"
|
|
10
11
|
>
|
|
11
|
-
<
|
|
12
|
+
<div class="flex flex-col items-center justify-center gap-4 px-6 py-4">
|
|
13
|
+
<a-ill-ok />
|
|
12
14
|
<p class="heading-02 text-center font-bold">
|
|
13
15
|
{{ t('modals.report_bug.confirmed') }}
|
|
14
16
|
</p>
|
|
15
|
-
|
|
16
|
-
<div class="flex flex-col gap-5 items-center">
|
|
17
|
-
<a-ill-ok />
|
|
18
|
-
<p class="text-sm text-center">
|
|
17
|
+
<p class="body-400 text-center">
|
|
19
18
|
{{ t('modals.report_bug.gratitude') }}
|
|
20
19
|
</p>
|
|
21
|
-
|
|
20
|
+
</div>
|
|
21
|
+
<template #footer>
|
|
22
|
+
<a-button-v2
|
|
22
23
|
view="outline"
|
|
24
|
+
color="blue"
|
|
23
25
|
block
|
|
24
26
|
@click="isOpen = false"
|
|
25
27
|
>
|
|
26
28
|
{{ t('actions.close') }}
|
|
27
|
-
</a-button>
|
|
28
|
-
</
|
|
29
|
-
</a-modal>
|
|
29
|
+
</a-button-v2>
|
|
30
|
+
</template>
|
|
31
|
+
</a-modal-v2>
|
|
30
32
|
</template>
|
|
@@ -18,7 +18,7 @@ const env = myLayer.mode
|
|
|
18
18
|
|
|
19
19
|
const mask = new Mask({ mask: '# (###) ### ## ##' })
|
|
20
20
|
|
|
21
|
-
const isOpen = defineModel<boolean>()
|
|
21
|
+
const isOpen = defineModel<boolean>({ default: false })
|
|
22
22
|
const form: any = reactive({
|
|
23
23
|
name: '',
|
|
24
24
|
email: '',
|
|
@@ -35,6 +35,7 @@ const scheme = z.object({
|
|
|
35
35
|
})
|
|
36
36
|
|
|
37
37
|
const submitted = ref(false)
|
|
38
|
+
const sending = ref(false)
|
|
38
39
|
|
|
39
40
|
const { validation, getError } = useUIValidation(
|
|
40
41
|
scheme,
|
|
@@ -58,6 +59,7 @@ watch(phone, () => {
|
|
|
58
59
|
async function send() {
|
|
59
60
|
submitted.value = true
|
|
60
61
|
if (validation.value) return
|
|
62
|
+
sending.value = true
|
|
61
63
|
try {
|
|
62
64
|
await $fetch<any>(`https://users.${env}.kz/api/v1/message/error`, {
|
|
63
65
|
method: 'POST',
|
|
@@ -79,86 +81,90 @@ async function send() {
|
|
|
79
81
|
form.comment = ''
|
|
80
82
|
phone.value = ''
|
|
81
83
|
submitted.value = false
|
|
84
|
+
sending.value = false
|
|
82
85
|
emit('close')
|
|
83
86
|
}
|
|
84
87
|
}
|
|
85
88
|
</script>
|
|
86
89
|
|
|
87
90
|
<template>
|
|
88
|
-
<a-modal v-model="isOpen"
|
|
91
|
+
<a-modal-v2 v-model="isOpen" width="424px">
|
|
89
92
|
<template #header>
|
|
90
|
-
<
|
|
93
|
+
<h2 class="text-base font-semibold text-gray-900 dark:text-gray-100" data-test-id="report-form-title">
|
|
91
94
|
{{ t('modals.report_bug.title') }}
|
|
92
|
-
</
|
|
95
|
+
</h2>
|
|
93
96
|
</template>
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
+
|
|
98
|
+
<div class="flex flex-col gap-5 px-6 py-4">
|
|
99
|
+
<div class="flex flex-col items-center justify-center gap-4">
|
|
100
|
+
<a-ill-stop-sign class="size-[140px]" />
|
|
97
101
|
<slot name="title">
|
|
98
102
|
<p class="label-400 text-center">
|
|
99
103
|
{{ name }}
|
|
100
104
|
</p>
|
|
101
105
|
</slot>
|
|
102
106
|
</div>
|
|
103
|
-
|
|
104
|
-
|
|
107
|
+
|
|
108
|
+
<div class="flex flex-col gap-3">
|
|
109
|
+
<a-input-v2
|
|
105
110
|
v-model="form.name"
|
|
106
111
|
required
|
|
107
|
-
:data-test-id="'report-form-name-input'"
|
|
108
112
|
:label="t('modals.report_bug.labels.name')"
|
|
109
113
|
:error="getError('name')"
|
|
114
|
+
data-test-id="report-form-name-input"
|
|
110
115
|
/>
|
|
111
|
-
<a-input-
|
|
116
|
+
<a-input-v2
|
|
112
117
|
v-model="form.email"
|
|
113
118
|
required
|
|
114
|
-
|
|
119
|
+
type="email"
|
|
115
120
|
:label="t('modals.report_bug.labels.email')"
|
|
116
121
|
:error="getError('email')"
|
|
122
|
+
data-test-id="report-form-email-input"
|
|
117
123
|
/>
|
|
118
|
-
<a-input-
|
|
124
|
+
<a-input-v2
|
|
119
125
|
v-model="phone"
|
|
120
|
-
v-maska
|
|
121
126
|
required
|
|
122
|
-
|
|
127
|
+
type="tel"
|
|
123
128
|
clearable
|
|
124
|
-
|
|
129
|
+
mask="8 (###) ### ## ##"
|
|
125
130
|
:label="t('modals.report_bug.labels.phone')"
|
|
126
131
|
:error="getError('phone_number')"
|
|
132
|
+
data-test-id="report-form-phone-input"
|
|
133
|
+
/>
|
|
134
|
+
<a-textarea-v2
|
|
135
|
+
v-model="form.comment"
|
|
136
|
+
required
|
|
137
|
+
:rows="4"
|
|
138
|
+
resize="none"
|
|
139
|
+
:label="t('modals.report_bug.labels.comment')"
|
|
140
|
+
:error="getError('comment')"
|
|
141
|
+
data-test-id="report-form-comment-textarea"
|
|
127
142
|
/>
|
|
128
|
-
<div>
|
|
129
|
-
<a-textarea
|
|
130
|
-
v-model="form.comment"
|
|
131
|
-
required
|
|
132
|
-
:data-test-id="'report-form-comment-textarea'"
|
|
133
|
-
:label="t('modals.report_bug.labels.comment')"
|
|
134
|
-
size="md"
|
|
135
|
-
:error="getError('comment')"
|
|
136
|
-
class="min-h-[120px]"
|
|
137
|
-
:resizeable="false"
|
|
138
|
-
/>
|
|
139
|
-
</div>
|
|
140
143
|
</div>
|
|
141
144
|
</div>
|
|
145
|
+
|
|
142
146
|
<template #footer>
|
|
143
|
-
<div class="flex
|
|
144
|
-
<a-button
|
|
147
|
+
<div class="flex gap-2">
|
|
148
|
+
<a-button-v2
|
|
145
149
|
view="outline"
|
|
146
|
-
|
|
150
|
+
color="blue"
|
|
151
|
+
block
|
|
147
152
|
data-test-id="report-form-close-button"
|
|
148
153
|
@click="emit('close')"
|
|
149
154
|
>
|
|
150
155
|
{{ t('actions.close') }}
|
|
151
|
-
</a-button>
|
|
152
|
-
<a-button
|
|
153
|
-
|
|
156
|
+
</a-button-v2>
|
|
157
|
+
<a-button-v2
|
|
158
|
+
block
|
|
159
|
+
:loading="sending"
|
|
154
160
|
data-test-id="report-form-submit-button"
|
|
155
161
|
@click="send"
|
|
156
162
|
>
|
|
157
163
|
{{ t('actions.send') }}
|
|
158
|
-
</a-button>
|
|
164
|
+
</a-button-v2>
|
|
159
165
|
</div>
|
|
160
166
|
</template>
|
|
161
|
-
</a-modal>
|
|
167
|
+
</a-modal-v2>
|
|
162
168
|
</template>
|
|
163
169
|
|
|
164
170
|
<style scoped></style>
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
|
|
3
2
|
import { useUrls } from '#adata-ui/composables/useUrls'
|
|
4
3
|
|
|
5
4
|
const emit = defineEmits<{
|
|
@@ -17,33 +16,33 @@ function runTimer() {
|
|
|
17
16
|
const intervalId = setInterval(() => {
|
|
18
17
|
if (timer.value <= 0) {
|
|
19
18
|
isResend.value = true
|
|
20
|
-
clearInterval(intervalId)
|
|
21
|
-
return
|
|
19
|
+
clearInterval(intervalId)
|
|
20
|
+
return
|
|
22
21
|
}
|
|
23
|
-
timer.value
|
|
24
|
-
}, 1000)
|
|
22
|
+
timer.value--
|
|
23
|
+
}, 1000)
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
onMounted(() => resend())
|
|
28
27
|
|
|
29
|
-
|
|
28
|
+
function resend() {
|
|
30
29
|
isResend.value = false
|
|
31
30
|
runTimer()
|
|
32
31
|
emit('resend')
|
|
33
32
|
}
|
|
34
|
-
|
|
33
|
+
function goToMain() {
|
|
35
34
|
if (route.query.url) {
|
|
36
35
|
document.location.replace(route.query.url)
|
|
37
|
-
}
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
38
|
document.location.replace(landing)
|
|
39
39
|
}
|
|
40
40
|
emit('close')
|
|
41
|
-
|
|
42
41
|
}
|
|
43
42
|
</script>
|
|
44
43
|
|
|
45
44
|
<template>
|
|
46
|
-
<div class="flex flex-col
|
|
45
|
+
<div class="flex flex-col items-center justify-center gap-5">
|
|
47
46
|
<p class="heading-02">
|
|
48
47
|
{{ $t('login.modal.title') }}
|
|
49
48
|
</p>
|
|
@@ -57,65 +57,68 @@ onMounted(() => {
|
|
|
57
57
|
</script>
|
|
58
58
|
|
|
59
59
|
<template>
|
|
60
|
-
<a-modal
|
|
60
|
+
<a-modal-v2
|
|
61
61
|
v-model="isOpen"
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
width="424px"
|
|
63
|
+
persistent
|
|
64
64
|
>
|
|
65
|
-
<div class="flex flex-col gap-5">
|
|
66
|
-
<p class="text-center
|
|
65
|
+
<div class="flex flex-col gap-5 px-6 py-4">
|
|
66
|
+
<p class="heading-02 text-center">
|
|
67
67
|
{{ t('modals.submit_application_modal.title') }}
|
|
68
68
|
</p>
|
|
69
69
|
<a-ill-mail class="w-full" />
|
|
70
|
-
<div class="flex flex-col gap-
|
|
71
|
-
<a-input-
|
|
70
|
+
<div class="flex flex-col gap-3">
|
|
71
|
+
<a-input-v2
|
|
72
72
|
v-model="form.name"
|
|
73
|
+
required
|
|
73
74
|
:label="t('modals.submit_application_modal.labels.name')"
|
|
74
75
|
:error="getError('name')"
|
|
75
|
-
required
|
|
76
76
|
/>
|
|
77
|
-
<a-input-
|
|
77
|
+
<a-input-v2
|
|
78
78
|
v-model="form.email"
|
|
79
|
+
required
|
|
80
|
+
type="email"
|
|
79
81
|
:label="t('modals.submit_application_modal.labels.email')"
|
|
80
82
|
:error="getError('email')"
|
|
81
|
-
required
|
|
82
83
|
/>
|
|
83
|
-
<a-input-
|
|
84
|
+
<a-input-v2
|
|
84
85
|
v-model="form.phone"
|
|
85
|
-
|
|
86
|
+
required
|
|
87
|
+
type="tel"
|
|
88
|
+
clearable
|
|
89
|
+
mask="8 (###) ###-##-##"
|
|
86
90
|
:label="t('modals.submit_application_modal.labels.phone')"
|
|
87
|
-
data-maska="8 (###) ###-##-##"
|
|
88
91
|
:error="getError('phone')"
|
|
89
|
-
clearable
|
|
90
|
-
required
|
|
91
92
|
/>
|
|
92
|
-
<a-textarea
|
|
93
|
+
<a-textarea-v2
|
|
93
94
|
v-model="form.comment"
|
|
95
|
+
required
|
|
96
|
+
:rows="4"
|
|
97
|
+
resize="none"
|
|
94
98
|
:label="t('modals.submit_application_modal.labels.comment')"
|
|
95
99
|
:error="getError('comment')"
|
|
96
|
-
resizeable="none"
|
|
97
|
-
required
|
|
98
100
|
/>
|
|
99
101
|
</div>
|
|
100
102
|
</div>
|
|
101
103
|
<template #footer>
|
|
102
|
-
<div class="flex gap-2
|
|
103
|
-
<a-button
|
|
104
|
+
<div class="flex gap-2">
|
|
105
|
+
<a-button-v2
|
|
104
106
|
view="outline"
|
|
107
|
+
color="blue"
|
|
105
108
|
block
|
|
106
109
|
@click="cancel"
|
|
107
110
|
>
|
|
108
111
|
{{ t('reuse.close') }}
|
|
109
|
-
</a-button>
|
|
110
|
-
<a-button
|
|
112
|
+
</a-button-v2>
|
|
113
|
+
<a-button-v2
|
|
111
114
|
block
|
|
112
115
|
@click="send"
|
|
113
116
|
>
|
|
114
117
|
{{ t('reuse.send') }}
|
|
115
|
-
</a-button>
|
|
118
|
+
</a-button-v2>
|
|
116
119
|
</div>
|
|
117
120
|
</template>
|
|
118
|
-
</a-modal>
|
|
121
|
+
</a-modal-v2>
|
|
119
122
|
</template>
|
|
120
123
|
|
|
121
124
|
<style scoped>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { removeTrailingSlash } from '#adata-ui/components/utils/removeTrailingSlash'
|
|
3
2
|
import IdOtpInput from '#adata-ui/components/modals/id/IdOtpInput.vue'
|
|
3
|
+
import { removeTrailingSlash } from '#adata-ui/components/utils/removeTrailingSlash'
|
|
4
|
+
import { writeAccessTokenCookie } from '#adata-ui/composables/useAccessTokenCookie'
|
|
4
5
|
|
|
5
6
|
const { $toast } = useNuxtApp()
|
|
6
7
|
const { t, locale } = useI18n()
|
|
@@ -40,17 +41,11 @@ async function loginUser() {
|
|
|
40
41
|
},
|
|
41
42
|
})
|
|
42
43
|
const { data: cookiesData } = await response.json()
|
|
43
|
-
if (cookiesData) {
|
|
44
|
+
if (cookiesData?.access_token) {
|
|
44
45
|
const { access_token, expire_in } = cookiesData
|
|
45
|
-
const hostname = location.hostname.split('.').reverse()
|
|
46
46
|
useCookie('autoLogout').value = true
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
maxAge: expire_in,
|
|
50
|
-
domain: `.${hostname[1]}.${hostname[0]}`,
|
|
51
|
-
path: '/',
|
|
52
|
-
secure: true,
|
|
53
|
-
}).value = access_token
|
|
48
|
+
await writeAccessTokenCookie(access_token, expire_in)
|
|
54
49
|
}
|
|
55
50
|
}
|
|
56
51
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { removeTrailingSlash } from '#adata-ui/components/utils/removeTrailingSlash'
|
|
3
|
+
import { writeAccessTokenCookie } from '#adata-ui/composables/useAccessTokenCookie'
|
|
3
4
|
import { navigateToLocalizedPage } from '#adata-ui/utils/localizedNavigation'
|
|
4
5
|
import * as z from 'zod'
|
|
5
6
|
import { useUrls } from '#adata-ui/composables/useUrls'
|
|
@@ -127,17 +128,11 @@ async function submit() {
|
|
|
127
128
|
},
|
|
128
129
|
})
|
|
129
130
|
const { data: cookiesData } = await response.json()
|
|
130
|
-
if (cookiesData) {
|
|
131
|
+
if (cookiesData?.access_token) {
|
|
131
132
|
const { access_token, expire_in } = cookiesData
|
|
132
|
-
const hostname = location.hostname.split('.').reverse()
|
|
133
133
|
useCookie('autoLogout').value = true
|
|
134
134
|
|
|
135
|
-
|
|
136
|
-
maxAge: expire_in,
|
|
137
|
-
domain: COOKIE_DOMAIN,
|
|
138
|
-
path: '/',
|
|
139
|
-
secure: true,
|
|
140
|
-
}).value = access_token
|
|
135
|
+
await writeAccessTokenCookie(access_token, expire_in)
|
|
141
136
|
}
|
|
142
137
|
$toast.success(t('reuse.successfully'))
|
|
143
138
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import
|
|
2
|
+
import IdAutoLogoutModal from '#adata-ui/components/modals/id/IdAutoLogoutModal.vue'
|
|
3
3
|
import IdConfirmAccountOtpModal from '#adata-ui/components/modals/id/IdConfirmAccountOtpModal.vue'
|
|
4
4
|
import IdConfirmSuccessfulModal from '#adata-ui/components/modals/id/IdConfirmSuccessfulModal.vue'
|
|
5
5
|
import IdLoginModal from '#adata-ui/components/modals/id/IdLoginModal.vue'
|
|
@@ -9,7 +9,8 @@ import IdRecoveryModal from '#adata-ui/components/modals/id/IdRecoveryModal.vue'
|
|
|
9
9
|
import IdRegistrationModal from '#adata-ui/components/modals/id/IdRegistrationModal.vue'
|
|
10
10
|
import IdResetPasswordOtpModal from '#adata-ui/components/modals/id/IdResetPasswordOtpModal.vue'
|
|
11
11
|
import IdTwoFactorModal from '#adata-ui/components/modals/id/IdTwoFactorModal.vue'
|
|
12
|
-
import
|
|
12
|
+
import { removeTrailingSlash } from '#adata-ui/components/utils/removeTrailingSlash'
|
|
13
|
+
import { writeAccessTokenCookie } from '#adata-ui/composables/useAccessTokenCookie'
|
|
13
14
|
|
|
14
15
|
const emit = defineEmits<{
|
|
15
16
|
(e: 'closed', value: boolean): void
|
|
@@ -35,6 +36,10 @@ const authApiURL = commonAuth.authApiURL
|
|
|
35
36
|
const accessToken = useCookie('accessToken')
|
|
36
37
|
const isAuthenticated = computed(() => !!accessToken.value)
|
|
37
38
|
|
|
39
|
+
// one-shot флаг: обмен по auth-сессии перезагружает страницу максимум один раз,
|
|
40
|
+
// иначе нечитаемая кука accessToken зацикливает reload
|
|
41
|
+
const AUTH_RELOAD_FLAG = 'auth-exchange-reloaded'
|
|
42
|
+
|
|
38
43
|
onMounted(async () => {
|
|
39
44
|
const response = await fetch(`${removeTrailingSlash(authApiURL)}/access/cookie`, {
|
|
40
45
|
method: 'GET',
|
|
@@ -46,17 +51,25 @@ onMounted(async () => {
|
|
|
46
51
|
const { data: cookiesData } = await response.json()
|
|
47
52
|
if (cookiesData?.access_token && !accessToken.value) {
|
|
48
53
|
const { access_token, expire_in } = cookiesData
|
|
49
|
-
const hostname = location.hostname.split('.').reverse()
|
|
50
54
|
useCookie('autoLogout').value = true
|
|
51
55
|
|
|
52
|
-
|
|
53
|
-
maxAge: expire_in,
|
|
54
|
-
domain: `.${hostname[1]}.${hostname[0]}`,
|
|
55
|
-
path: '/',
|
|
56
|
-
secure: true,
|
|
57
|
-
}).value = access_token
|
|
56
|
+
const persisted = await writeAccessTokenCookie(access_token, expire_in)
|
|
58
57
|
|
|
59
|
-
|
|
58
|
+
if (persisted && !sessionStorage.getItem(AUTH_RELOAD_FLAG)) {
|
|
59
|
+
sessionStorage.setItem(AUTH_RELOAD_FLAG, '1')
|
|
60
|
+
window.location.reload()
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
sessionStorage.removeItem(AUTH_RELOAD_FLAG)
|
|
64
|
+
console.warn(
|
|
65
|
+
persisted
|
|
66
|
+
? '[auth] accessToken cookie is erased between page loads (SSR resetTokens?), reload skipped to avoid a loop'
|
|
67
|
+
: '[auth] accessToken cookie was not persisted (domain/secure/maxAge rejected by browser), reload skipped',
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
sessionStorage.removeItem(AUTH_RELOAD_FLAG)
|
|
60
73
|
}
|
|
61
74
|
})
|
|
62
75
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { removeTrailingSlash } from '#adata-ui/components/utils/removeTrailingSlash'
|
|
3
2
|
import IdOtpInput from '#adata-ui/components/modals/id/IdOtpInput.vue'
|
|
3
|
+
import { removeTrailingSlash } from '#adata-ui/components/utils/removeTrailingSlash'
|
|
4
|
+
import { writeAccessTokenCookie } from '#adata-ui/composables/useAccessTokenCookie'
|
|
4
5
|
import { useUrls } from '#adata-ui/composables/useUrls'
|
|
5
6
|
|
|
6
7
|
const { $toast } = useNuxtApp()
|
|
@@ -54,15 +55,9 @@ async function onConfirm() {
|
|
|
54
55
|
const { data: cookiesData } = await response.json()
|
|
55
56
|
if (cookiesData?.access_token) {
|
|
56
57
|
const { access_token, expire_in } = cookiesData
|
|
57
|
-
const hostname = location.hostname.split('.').reverse()
|
|
58
58
|
useCookie('autoLogout').value = true
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
maxAge: expire_in,
|
|
62
|
-
domain: `.${hostname[1]}.${hostname[0]}`,
|
|
63
|
-
path: '/',
|
|
64
|
-
secure: true,
|
|
65
|
-
}).value = access_token
|
|
60
|
+
await writeAccessTokenCookie(access_token, expire_in)
|
|
66
61
|
}
|
|
67
62
|
$toast.success(t('login.successfully'))
|
|
68
63
|
twoFactorModal.value = false
|
|
@@ -109,7 +109,7 @@ onMounted(() => {
|
|
|
109
109
|
<system-notification />
|
|
110
110
|
<header
|
|
111
111
|
ref="headerRef"
|
|
112
|
-
class="relative h-16 border-b border-deepblue-900/10 bg-gray-50/
|
|
112
|
+
class="relative h-16 border-b border-deepblue-900/10 bg-gray-50/20 backdrop-blur-lg dark:border-gray-200/10 dark:bg-gray-900/30"
|
|
113
113
|
>
|
|
114
114
|
<div class="a-container mobile-padding flex h-full items-center justify-between gap-2">
|
|
115
115
|
<!-- Desktop hidden -->
|
|
@@ -135,18 +135,18 @@ onMounted(() => {
|
|
|
135
135
|
</div>
|
|
136
136
|
<div
|
|
137
137
|
v-else-if="mobileHeaderType === 'search'"
|
|
138
|
-
class="flex w-full items-center gap-2
|
|
138
|
+
class="flex w-full items-center gap-2 lg:hidden"
|
|
139
139
|
>
|
|
140
140
|
<logo
|
|
141
141
|
class="dark:text-gray-200"
|
|
142
142
|
@click="goToAnotherModule"
|
|
143
143
|
/>
|
|
144
144
|
<button
|
|
145
|
-
class="flex w-full items-center gap-2 rounded
|
|
145
|
+
class="flex w-full items-center gap-2 rounded-lg border border-gray-200 bg-white px-4 py-1.5 shadow-sm dark:border-gray-700 dark:bg-gray-900 dark:text-gray-400"
|
|
146
146
|
@click="$emit('search')"
|
|
147
147
|
>
|
|
148
|
-
<a-icon-search />
|
|
149
|
-
<span class="body-400">Найти</span>
|
|
148
|
+
<a-icon-search class="text-gray-400 dark:text-gray-500" />
|
|
149
|
+
<span class="body-400 text-gray-500 dark:text-gray-400">Найти</span>
|
|
150
150
|
</button>
|
|
151
151
|
</div>
|
|
152
152
|
</section>
|
|
@@ -22,18 +22,18 @@ interface HeaderNav {
|
|
|
22
22
|
|
|
23
23
|
const navs: HeaderNav[] = [
|
|
24
24
|
{ label: 'header.navs.products', dataAttribute: 'header-products-and-solutions-button', menu: ProductMenu },
|
|
25
|
-
{ label: 'header.navs.contacts', dataAttribute: 'header-contacts-button'
|
|
25
|
+
{ label: 'header.navs.contacts', dataAttribute: 'header-contacts-button' },
|
|
26
26
|
{ label: 'header.navs.tariffs', dataAttribute: 'header-tariffs-button' },
|
|
27
27
|
]
|
|
28
28
|
|
|
29
|
-
const OPEN_DELAY = 500
|
|
30
|
-
const SWITCH_DELAY = 80
|
|
29
|
+
const OPEN_DELAY = 500
|
|
30
|
+
const SWITCH_DELAY = 80
|
|
31
31
|
|
|
32
32
|
const menu = ref<HTMLElement | null>(null)
|
|
33
33
|
const show = ref(false)
|
|
34
|
-
const locked = ref(false)
|
|
34
|
+
const locked = ref(false)
|
|
35
35
|
const currentIndex = ref(0)
|
|
36
|
-
const swapName = ref('swap-fade')
|
|
36
|
+
const swapName = ref('swap-fade')
|
|
37
37
|
const hoverTimer = ref<ReturnType<typeof setTimeout> | null>(null)
|
|
38
38
|
|
|
39
39
|
const currentMenu = computed(() => navs[currentIndex.value]?.menu ?? null)
|
|
@@ -42,8 +42,6 @@ function isActive(index: number) {
|
|
|
42
42
|
return show.value && currentIndex.value === index
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
// Open `index`'s panel. Slide toward the direction of travel when switching
|
|
46
|
-
// between already-open panels; fade (no sideways shift) on a fresh open.
|
|
47
45
|
function openMenu(index: number) {
|
|
48
46
|
swapName.value = show.value && index !== currentIndex.value
|
|
49
47
|
? (index > currentIndex.value ? 'slide-next' : 'slide-prev')
|
|
@@ -57,6 +55,10 @@ function closeMenu() {
|
|
|
57
55
|
locked.value = false
|
|
58
56
|
}
|
|
59
57
|
|
|
58
|
+
function goToContacts() {
|
|
59
|
+
navigateToLocalizedPage({ locale, projectUrl: landing, path: PAGES.contacts, target: '_blank' })
|
|
60
|
+
}
|
|
61
|
+
|
|
60
62
|
function goToTariffs() {
|
|
61
63
|
const host = useRequestURL().hostname
|
|
62
64
|
const codeMap: Record<string, string> = {
|
|
@@ -73,11 +75,15 @@ function goToTariffs() {
|
|
|
73
75
|
navigateToLocalizedPage({ locale, projectUrl: landing, path: PAGES.tariffs + code, target: '_blank' })
|
|
74
76
|
}
|
|
75
77
|
|
|
76
|
-
// Click pins the menu open; items without a menu (Tariffs) navigate instead.
|
|
77
78
|
function toggleMenu(index: number) {
|
|
78
79
|
const nav = navs[index]
|
|
79
80
|
if (!nav?.menu) {
|
|
80
|
-
|
|
81
|
+
if (index === 1) {
|
|
82
|
+
goToContacts()
|
|
83
|
+
}
|
|
84
|
+
else if (index === 2) {
|
|
85
|
+
goToTariffs()
|
|
86
|
+
}
|
|
81
87
|
return
|
|
82
88
|
}
|
|
83
89
|
if (show.value && currentIndex.value === index) {
|
|
@@ -95,7 +101,6 @@ function clearHoverTimer() {
|
|
|
95
101
|
}
|
|
96
102
|
}
|
|
97
103
|
|
|
98
|
-
// Hover opens menu items; hovering Tariffs dismisses an unpinned menu.
|
|
99
104
|
function scheduleOpen(index: number) {
|
|
100
105
|
clearHoverTimer()
|
|
101
106
|
hoverTimer.value = setTimeout(() => {
|
|
@@ -104,14 +109,10 @@ function scheduleOpen(index: number) {
|
|
|
104
109
|
}, show.value ? SWITCH_DELAY : OPEN_DELAY)
|
|
105
110
|
}
|
|
106
111
|
|
|
107
|
-
// Overlay closes on click regardless of the pin; on hover only while unpinned.
|
|
108
112
|
function onOverlayHover() {
|
|
109
113
|
if (!locked.value) closeMenu()
|
|
110
114
|
}
|
|
111
115
|
|
|
112
|
-
// Lock body scroll while the mega-menu is open. `scrollbar-gutter: stable` is set
|
|
113
|
-
// globally, so we don't reserve the gap to avoid a double-compensated page shift.
|
|
114
|
-
// The panel is mounted via v-if, so wait a tick for the ref on open.
|
|
115
116
|
watch(show, async (open) => {
|
|
116
117
|
if (open) {
|
|
117
118
|
await nextTick()
|
|
@@ -157,7 +158,6 @@ onBeforeUnmount(() => {
|
|
|
157
158
|
</li>
|
|
158
159
|
</ul>
|
|
159
160
|
|
|
160
|
-
<!-- One transition drives the whole panel's open/close (appear + disappear). -->
|
|
161
161
|
<transition name="menu">
|
|
162
162
|
<div
|
|
163
163
|
v-if="show"
|
|
@@ -165,14 +165,12 @@ onBeforeUnmount(() => {
|
|
|
165
165
|
class="fixed inset-x-0 z-20"
|
|
166
166
|
:style="{ top: `${menuTop}px`, height: `calc(100dvh - ${menuTop}px)` }"
|
|
167
167
|
>
|
|
168
|
-
<!-- Full-page dimmed blur backdrop behind the panel. -->
|
|
169
168
|
<div
|
|
170
169
|
class="absolute inset-0 bg-gray-900/30 backdrop-blur dark:bg-black/50"
|
|
171
170
|
@click="closeMenu"
|
|
172
171
|
@mouseover="onOverlayHover"
|
|
173
172
|
/>
|
|
174
173
|
|
|
175
|
-
<!-- Panel on top; slides left/right when switching between menu items. -->
|
|
176
174
|
<transition
|
|
177
175
|
:name="swapName"
|
|
178
176
|
mode="out-in"
|
|
@@ -181,7 +179,6 @@ onBeforeUnmount(() => {
|
|
|
181
179
|
:is="currentMenu"
|
|
182
180
|
:key="currentIndex"
|
|
183
181
|
class="relative"
|
|
184
|
-
:url="buildLocalizedUrl(locale, landing, PAGES.contacts)"
|
|
185
182
|
/>
|
|
186
183
|
</transition>
|
|
187
184
|
</div>
|
|
@@ -193,7 +190,6 @@ onBeforeUnmount(() => {
|
|
|
193
190
|
$ease: cubic-bezier(0.22, 1, 0.36, 1);
|
|
194
191
|
$shift: 24px;
|
|
195
192
|
|
|
196
|
-
// Whole menu: open and close (vertical fade, no horizontal motion).
|
|
197
193
|
.menu-enter-active,
|
|
198
194
|
.menu-leave-active {
|
|
199
195
|
transition:
|
|
@@ -207,7 +203,6 @@ $shift: 24px;
|
|
|
207
203
|
transform: translateY(-12px);
|
|
208
204
|
}
|
|
209
205
|
|
|
210
|
-
// Switching panels: slide toward the direction of travel (out-in, so no overlap).
|
|
211
206
|
.slide-next-enter-active,
|
|
212
207
|
.slide-next-leave-active,
|
|
213
208
|
.slide-prev-enter-active,
|
|
@@ -239,7 +234,6 @@ $shift: 24px;
|
|
|
239
234
|
transform: translateX($shift);
|
|
240
235
|
}
|
|
241
236
|
|
|
242
|
-
// Fresh open (no switch): fade only, so the panel never slides sideways.
|
|
243
237
|
.swap-fade-enter-from,
|
|
244
238
|
.swap-fade-leave-to {
|
|
245
239
|
opacity: 0;
|