adata-ui 2.1.40-beta.4 → 2.1.40-beta.6
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 +5 -4
- package/components/elements/button/AButtonV2.vue +29 -6
- package/components/elements/select/ASelectV2.vue +17 -9
- package/components/elements/status-badge-v2/StatusBadgeV2.vue +78 -0
- package/components/forms/feedback-form/FeedbackForm.vue +8 -8
- package/components/forms/input/AInputV2.vue +1 -1
- package/components/forms/input/textarea/ATextareaV2.vue +248 -0
- package/components/modals/AnotherDeviceModal.vue +16 -17
- 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/navigation/header/HeaderLink.vue +15 -21
- package/components/navigation/header/NavList.vue +8 -32
- package/components/navigation/header/ProductMenu.vue +52 -50
- package/components/overlays/modal/AModalV2.vue +10 -9
- package/composables/useChipOverflow.ts +17 -7
- 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 +34 -4
- package/lang/ru.ts +36 -6
- package/package.json +1 -1
- package/shared/constans/pages.ts +8 -2
|
@@ -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;
|
|
@@ -27,19 +27,14 @@ function isActive(itemPath: string) {
|
|
|
27
27
|
</script>
|
|
28
28
|
|
|
29
29
|
<template>
|
|
30
|
-
<section
|
|
31
|
-
class="group/section overflow-hidden rounded-xl border p-3 shadow-sm transition-colors"
|
|
32
|
-
:class="currentModule
|
|
33
|
-
? 'border-blue-700/20 bg-blue-50 hover:border-blue-700/40 dark:border-blue-500/20 dark:bg-blue-500/5 dark:hover:border-blue-500/40'
|
|
34
|
-
: 'border-gray-200 bg-white hover:border-blue-700/40 dark:border-gray-800 dark:bg-gray-950 dark:hover:border-blue-500/40'"
|
|
35
|
-
>
|
|
30
|
+
<section class="group/section overflow-hidden flex flex-col ">
|
|
36
31
|
<div class="mb-3 flex items-start justify-between gap-2">
|
|
37
32
|
<nuxt-link-locale
|
|
38
33
|
:to="link"
|
|
39
|
-
class="group/title flex min-w-0 items-center gap-2 text-
|
|
34
|
+
class="group/title flex min-w-0 items-center gap-2 text-base font-semibold text-deepblue-900 transition-colors hover:text-blue-700 dark:text-gray-100 dark:hover:text-blue-500"
|
|
40
35
|
>
|
|
41
36
|
<span
|
|
42
|
-
class="flex size-8 shrink-0 items-center justify-center rounded-lg bg-blue-
|
|
37
|
+
class="flex size-8 shrink-0 items-center justify-center rounded-lg bg-blue-50 text-blue-500 transition-colors group-hover/title:bg-blue-700 group-hover/title:text-white dark:bg-blue-500/10 dark:text-blue-500 dark:group-hover/title:bg-blue-500">
|
|
43
38
|
<component
|
|
44
39
|
:is="icon"
|
|
45
40
|
class="size-4"
|
|
@@ -47,15 +42,6 @@ function isActive(itemPath: string) {
|
|
|
47
42
|
</span>
|
|
48
43
|
<span class="block truncate normal-case">{{ title }}</span>
|
|
49
44
|
</nuxt-link-locale>
|
|
50
|
-
|
|
51
|
-
<div class="flex shrink-0 items-center gap-1">
|
|
52
|
-
<span
|
|
53
|
-
v-if="badge"
|
|
54
|
-
class="rounded-full bg-blue-700/10 px-1.5 py-0.5 text-[10px] font-bold uppercase leading-none text-blue-700 dark:bg-blue-500/10 dark:text-blue-500"
|
|
55
|
-
>NEW</span>
|
|
56
|
-
<a-icon-arrow-side-up
|
|
57
|
-
class="size-4 text-gray-500 transition-colors group-hover/section:text-blue-700 dark:group-hover/section:text-blue-500"/>
|
|
58
|
-
</div>
|
|
59
45
|
</div>
|
|
60
46
|
|
|
61
47
|
<ul class="grid gap-1">
|
|
@@ -67,24 +53,12 @@ function isActive(itemPath: string) {
|
|
|
67
53
|
v-if="item.to"
|
|
68
54
|
:to="item.to"
|
|
69
55
|
itemprop="name"
|
|
70
|
-
class="group/item flex items-center gap-2 rounded-lg px-2 py-1.5 transition-colors hover:bg-
|
|
56
|
+
class="group/item flex items-center gap-2 rounded-lg px-2 py-1.5 transition-colors hover:bg-gray-50 dark:hover:bg-gray-200/10"
|
|
71
57
|
:class="{ 'bg-blue-100 dark:bg-gray-200/10': isActive(item.to) }"
|
|
72
58
|
>
|
|
59
|
+
<a-icon-chevron-right class="size-[14px] shrink-0 text-gray-300 group-hover/item:text-blue-500 dark:group-hover/item:text-blue-700" />
|
|
73
60
|
<span
|
|
74
|
-
class="
|
|
75
|
-
:class="{
|
|
76
|
-
'bg-blue-700/10 text-blue-700 dark:bg-blue-500/10 dark:text-blue-500': isActive(item.to),
|
|
77
|
-
'bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-300': !isActive(item.to),
|
|
78
|
-
// 'bg-white text-gray-600 dark:bg-white dark:text-gray-300': currentModule,
|
|
79
|
-
}"
|
|
80
|
-
>
|
|
81
|
-
<component
|
|
82
|
-
:is="item.icon"
|
|
83
|
-
class="size-[14px]"
|
|
84
|
-
/>
|
|
85
|
-
</span>
|
|
86
|
-
<span
|
|
87
|
-
class="truncate text-sm font-semibold normal-case transition-colors group-hover/item:text-deepblue-900 dark:group-hover/item:text-gray-100"
|
|
61
|
+
class="min-w-0 text-sm normal-case transition-colors group-hover/item:text-deepblue-900 group-hover/item:font-semibold dark:group-hover/item:text-gray-100"
|
|
88
62
|
:class="isActive(item.to)
|
|
89
63
|
? 'text-blue-700 dark:text-blue-500'
|
|
90
64
|
: 'text-gray-600 dark:text-gray-300'"
|
|
@@ -92,5 +66,7 @@ function isActive(itemPath: string) {
|
|
|
92
66
|
</nuxt-link-locale>
|
|
93
67
|
</li>
|
|
94
68
|
</ul>
|
|
69
|
+
|
|
70
|
+
<slot name="bottom" />
|
|
95
71
|
</section>
|
|
96
72
|
</template>
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import CardGallery from '#adata-ui/components/navigation/header/CardGallery.vue'
|
|
3
2
|
import NavList from '#adata-ui/components/navigation/header/NavList.vue'
|
|
4
3
|
import { useActiveNavigation } from '#adata-ui/composables/useActiveNavigation'
|
|
5
4
|
import { useHeaderNavigationLinks } from '#adata-ui/composables/useHeaderNavigationLinks'
|
|
5
|
+
import { PAGES } from '#adata-ui/shared/constans/pages'
|
|
6
|
+
import { useUrls } from '#adata-ui/composables/useUrls'
|
|
7
|
+
import AIconArrowRight from '#adata-ui/icons/arrow/arrow-right.vue'
|
|
8
|
+
import AIconDownloadRounded from '#adata-ui/icons/download-rounded.vue'
|
|
6
9
|
|
|
7
10
|
defineProps<{ url?: string }>()
|
|
8
11
|
defineEmits(['outerClick', 'mouseOver'])
|
|
9
12
|
|
|
10
|
-
const
|
|
13
|
+
const urls = useUrls()
|
|
14
|
+
const { t, locale } = useI18n()
|
|
11
15
|
const modules = useHeaderNavigationLinks()
|
|
12
16
|
const { isActiveModule } = useActiveNavigation()
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
const edoModule = computed(() => modules.value.find(module => module.key === 'edo') ?? null)
|
|
16
|
-
|
|
17
|
-
const COLUMN_KEYS = [['pk', 'fines', 'work'], ['fea', 'tenders'], ['compliance', 'analytics']]
|
|
18
|
+
const COLUMN_KEYS = [['edo'], ['pk'], ['compliance', 'fines'], ['tenders', 'analytics'], ['fea', 'work']]
|
|
18
19
|
const catalogColumns = computed(() =>
|
|
19
20
|
COLUMN_KEYS.map(keys =>
|
|
20
21
|
keys
|
|
@@ -22,6 +23,14 @@ const catalogColumns = computed(() =>
|
|
|
22
23
|
.filter((module): module is NonNullable<typeof module> => module !== undefined),
|
|
23
24
|
),
|
|
24
25
|
)
|
|
26
|
+
|
|
27
|
+
function handleExportClick() {
|
|
28
|
+
navigateToLocalizedPage({
|
|
29
|
+
locale: locale.value,
|
|
30
|
+
projectUrl: urls.pk,
|
|
31
|
+
path: PAGES.pk.unload,
|
|
32
|
+
})
|
|
33
|
+
}
|
|
25
34
|
</script>
|
|
26
35
|
|
|
27
36
|
<template>
|
|
@@ -29,52 +38,12 @@ const catalogColumns = computed(() =>
|
|
|
29
38
|
<div class="relative bg-white dark:bg-gray-950">
|
|
30
39
|
<div class="a-container max-h-[calc(100dvh-4rem)] overflow-y-auto overscroll-contain py-8">
|
|
31
40
|
<div class="flex justify-between gap-5">
|
|
32
|
-
|
|
33
|
-
<aside class="flex w-[320px] shrink-0 flex-col gap-5">
|
|
34
|
-
<nuxt-link-locale
|
|
35
|
-
v-if="edoModule"
|
|
36
|
-
:to="edoModule.link"
|
|
37
|
-
class="group block overflow-hidden rounded-xl border border-blue-700/20 p-4 shadow-sm transition-colors hover:border-blue-700/40 hover:bg-blue-100 dark:border-blue-500/20 dark:hover:border-blue-500/40 dark:hover:bg-blue-500/10"
|
|
38
|
-
:data-test-id="edoModule.data_attribute"
|
|
39
|
-
>
|
|
40
|
-
<div class="flex items-start gap-3">
|
|
41
|
-
<div
|
|
42
|
-
class="flex size-10 shrink-0 items-center justify-center rounded-lg bg-blue-700 text-white shadow-sm">
|
|
43
|
-
<component
|
|
44
|
-
:is="edoModule.icon"
|
|
45
|
-
class="size-5"
|
|
46
|
-
/>
|
|
47
|
-
</div>
|
|
48
|
-
<div class="min-w-0">
|
|
49
|
-
<div class="flex items-center justify-between gap-2">
|
|
50
|
-
<h3 class="text-base font-bold normal-case text-deepblue-900 dark:text-gray-100">
|
|
51
|
-
{{ edoModule.name }}
|
|
52
|
-
</h3>
|
|
53
|
-
<div class="flex items-center gap-1 justify-end">
|
|
54
|
-
<span
|
|
55
|
-
v-if="edoModule.is_new"
|
|
56
|
-
class="rounded-full bg-blue-700/10 px-1.5 py-0.5 text-[10px] font-bold uppercase leading-none text-blue-700 dark:bg-blue-500/10 dark:text-blue-500"
|
|
57
|
-
>NEW</span>
|
|
58
|
-
<a-icon-arrow-side-up
|
|
59
|
-
class="size-4 text-gray-500 transition group-hover:-translate-y-0.5 group-hover:translate-x-0.5 group-hover:text-blue-700 dark:text-gray-400 dark:group-hover:text-blue-500"/>
|
|
60
|
-
</div>
|
|
61
|
-
</div>
|
|
62
|
-
<p class="mt-1 text-sm normal-case leading-snug text-gray-600 dark:text-gray-300">
|
|
63
|
-
{{ t('header.products.edo.heroSubtitle') }}
|
|
64
|
-
</p>
|
|
65
|
-
</div>
|
|
66
|
-
</div>
|
|
67
|
-
</nuxt-link-locale>
|
|
68
|
-
|
|
69
|
-
<card-gallery class="hidden lg:block"/>
|
|
70
|
-
</aside>
|
|
71
|
-
|
|
72
|
-
<!-- Catalog grid: products as bordered section cards (EDO lives in the hero). -->
|
|
73
|
-
<div class="grid min-w-0 flex-1 grid-cols-3 gap-3">
|
|
41
|
+
<div class="grid min-w-0 flex-1 grid-cols-5 gap-x-3 gap-y-3">
|
|
74
42
|
<div
|
|
75
43
|
v-for="(column, index) in catalogColumns"
|
|
76
44
|
:key="index"
|
|
77
|
-
class="flex flex-col gap-
|
|
45
|
+
class="flex flex-col gap-[22px]"
|
|
46
|
+
:class="index > 0 ? 'border-l border-gray-100 pl-3 dark:border-gray-800' : ''"
|
|
78
47
|
>
|
|
79
48
|
<nav-list
|
|
80
49
|
v-for="module in column"
|
|
@@ -88,7 +57,40 @@ const catalogColumns = computed(() =>
|
|
|
88
57
|
:icon="module.icon"
|
|
89
58
|
:badge="module.is_new"
|
|
90
59
|
:data-test-id="module.data_attribute"
|
|
91
|
-
|
|
60
|
+
>
|
|
61
|
+
<template
|
|
62
|
+
v-if="module.key === 'edo'"
|
|
63
|
+
#bottom
|
|
64
|
+
>
|
|
65
|
+
<div class="mt-4 flex flex-col gap-4 rounded-xl bg-blue-700 dark:bg-blue-500 p-4 text-white normal-case">
|
|
66
|
+
<div class="flex items-center gap-3">
|
|
67
|
+
<span class="p-[10px] bg-white rounded-[10px] dark:bg-gray-950">
|
|
68
|
+
<AIconDownloadRounded class="size-5 text-blue-700 dark:text-blue-500" />
|
|
69
|
+
</span>
|
|
70
|
+
<span class="font-semibold text-white dark:text-gray-950">
|
|
71
|
+
{{ t('header.products.exportCard.title') }}
|
|
72
|
+
</span>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<div class="text-xs text-white dark:text-gray-950">
|
|
76
|
+
{{ t('header.products.exportCard.subTitle') }}
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<a-button-v2
|
|
80
|
+
view="soft"
|
|
81
|
+
class="!h-9 w-fit !bg-white dark:!bg-gray-950 text-blue-700 dark:text-blue-500"
|
|
82
|
+
size="sm"
|
|
83
|
+
:icon="AIconArrowRight"
|
|
84
|
+
icon-right
|
|
85
|
+
@click="handleExportClick"
|
|
86
|
+
>
|
|
87
|
+
<span class="text-sm">
|
|
88
|
+
{{ t('header.products.exportCard.receive') }}
|
|
89
|
+
</span>
|
|
90
|
+
</a-button-v2>
|
|
91
|
+
</div>
|
|
92
|
+
</template>
|
|
93
|
+
</nav-list>
|
|
92
94
|
</div>
|
|
93
95
|
</div>
|
|
94
96
|
</div>
|
|
@@ -298,7 +298,7 @@ function onTouchEnd() {
|
|
|
298
298
|
</div>
|
|
299
299
|
|
|
300
300
|
<header
|
|
301
|
-
v-if="!hideHeader
|
|
301
|
+
v-if="!hideHeader"
|
|
302
302
|
class="flex shrink-0 items-center justify-between gap-3 border-b border-gray-200 px-5 py-4 dark:border-gray-800"
|
|
303
303
|
@touchstart.passive="onTouchStart"
|
|
304
304
|
@touchmove="onTouchMove"
|
|
@@ -306,17 +306,18 @@ function onTouchEnd() {
|
|
|
306
306
|
@touchcancel="onTouchEnd"
|
|
307
307
|
>
|
|
308
308
|
<slot name="header">
|
|
309
|
-
<h2 class="text-base font-semibold text-gray-900 dark:text-gray-100">
|
|
309
|
+
<h2 v-if="title" class="text-base font-semibold text-gray-900 dark:text-gray-100">
|
|
310
310
|
{{ title }}
|
|
311
311
|
</h2>
|
|
312
|
-
<button
|
|
313
|
-
type="button"
|
|
314
|
-
class="rounded-md p-1 text-gray-500 transition hover:bg-gray-100 hover:text-gray-700 dark:hover:bg-gray-800 dark:hover:text-gray-200"
|
|
315
|
-
@click="closeIfAllowed"
|
|
316
|
-
>
|
|
317
|
-
<a-icon-x-mark class="size-4" />
|
|
318
|
-
</button>
|
|
319
312
|
</slot>
|
|
313
|
+
<!-- Кнопка закрытия не зависит от title и слота header — рендерится всегда (если header не скрыт). -->
|
|
314
|
+
<button
|
|
315
|
+
type="button"
|
|
316
|
+
class="ml-auto rounded-md p-1 text-gray-500 transition hover:bg-gray-100 hover:text-gray-700 dark:hover:bg-gray-800 dark:hover:text-gray-200"
|
|
317
|
+
@click="closeIfAllowed"
|
|
318
|
+
>
|
|
319
|
+
<a-icon-x-mark class="size-4" />
|
|
320
|
+
</button>
|
|
320
321
|
</header>
|
|
321
322
|
|
|
322
323
|
<div class="min-h-0 flex-1 overflow-auto" :class="contentClass">
|
|
@@ -4,6 +4,7 @@ interface UseChipOverflowOptions {
|
|
|
4
4
|
container: Ref<HTMLElement | null>
|
|
5
5
|
measure: Ref<HTMLElement | null>
|
|
6
6
|
count: Ref<number>
|
|
7
|
+
contentKey?: Ref<unknown>
|
|
7
8
|
gap?: number
|
|
8
9
|
}
|
|
9
10
|
|
|
@@ -13,11 +14,17 @@ interface UseChipOverflow {
|
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export function useChipOverflow(options: UseChipOverflowOptions): UseChipOverflow {
|
|
16
|
-
const { container, measure, count, gap = 6 } = options
|
|
17
|
+
const { container, measure, count, contentKey, gap = 6 } = options
|
|
17
18
|
|
|
18
19
|
const availableWidth = ref(0)
|
|
19
20
|
const visibleCount = ref(0)
|
|
20
21
|
|
|
22
|
+
// Ceil so a chip is only counted as fitting when it fully fits;
|
|
23
|
+
// sub-pixel underestimates would let flexbox squeeze visible chips into truncation.
|
|
24
|
+
function chipWidth(el: HTMLElement): number {
|
|
25
|
+
return Math.ceil(el.getBoundingClientRect().width)
|
|
26
|
+
}
|
|
27
|
+
|
|
21
28
|
function recompute() {
|
|
22
29
|
const total = count.value
|
|
23
30
|
if (total <= 0) {
|
|
@@ -38,7 +45,7 @@ export function useChipOverflow(options: UseChipOverflowOptions): UseChipOverflo
|
|
|
38
45
|
let used = 0
|
|
39
46
|
let fit = 0
|
|
40
47
|
for (let i = 0; i < chips.length; i++) {
|
|
41
|
-
const width = chips[i]
|
|
48
|
+
const width = chipWidth(chips[i]) + (i > 0 ? gap : 0)
|
|
42
49
|
if (used + width > avail) break
|
|
43
50
|
used += width
|
|
44
51
|
fit++
|
|
@@ -50,9 +57,9 @@ export function useChipOverflow(options: UseChipOverflowOptions): UseChipOverflo
|
|
|
50
57
|
}
|
|
51
58
|
|
|
52
59
|
const badge = root.children[total] as HTMLElement | undefined
|
|
53
|
-
const badgeWidth = (badge
|
|
60
|
+
const badgeWidth = (badge ? chipWidth(badge) : 0) + gap
|
|
54
61
|
while (fit > 1 && used + badgeWidth > avail) {
|
|
55
|
-
used -= chips[fit - 1]
|
|
62
|
+
used -= chipWidth(chips[fit - 1]) + gap
|
|
56
63
|
fit--
|
|
57
64
|
}
|
|
58
65
|
|
|
@@ -63,9 +70,9 @@ export function useChipOverflow(options: UseChipOverflowOptions): UseChipOverflo
|
|
|
63
70
|
|
|
64
71
|
onMounted(() => {
|
|
65
72
|
if (container.value) {
|
|
66
|
-
availableWidth.value = container.value.clientWidth
|
|
73
|
+
availableWidth.value = Math.floor(container.value.clientWidth)
|
|
67
74
|
resizeObserver = new ResizeObserver((entries) => {
|
|
68
|
-
availableWidth.value = entries[0]?.contentRect.width ?? 0
|
|
75
|
+
availableWidth.value = Math.floor(entries[0]?.contentRect.width ?? 0)
|
|
69
76
|
})
|
|
70
77
|
resizeObserver.observe(container.value)
|
|
71
78
|
}
|
|
@@ -74,7 +81,10 @@ export function useChipOverflow(options: UseChipOverflowOptions): UseChipOverflo
|
|
|
74
81
|
|
|
75
82
|
onUnmounted(() => resizeObserver?.disconnect())
|
|
76
83
|
|
|
77
|
-
watch(
|
|
84
|
+
watch(
|
|
85
|
+
contentKey ? [availableWidth, count, contentKey] : [availableWidth, count],
|
|
86
|
+
() => nextTick(recompute),
|
|
87
|
+
)
|
|
78
88
|
|
|
79
89
|
const hiddenCount = computed(() => Math.max(0, count.value - visibleCount.value))
|
|
80
90
|
|
|
@@ -9,12 +9,9 @@ import IconBlock from '#adata-ui/icons/block.vue'
|
|
|
9
9
|
import IconDollar from '#adata-ui/icons/currency/currency-dollar.vue'
|
|
10
10
|
import IconWork from '#adata-ui/icons/work-bag.vue'
|
|
11
11
|
import IconDocument from '#adata-ui/icons/hdocument.vue'
|
|
12
|
-
import IconCheck from '#adata-ui/icons/hcheck.vue'
|
|
13
|
-
import IconFile from '#adata-ui/icons/file/file.vue'
|
|
14
12
|
import IconCheckCircle from '#adata-ui/icons/check/check-circle.vue'
|
|
15
13
|
import IconCar from '#adata-ui/icons/car.vue'
|
|
16
14
|
import IconTruck from '#adata-ui/icons/truck.vue'
|
|
17
|
-
import IconClipboardText from '#adata-ui/icons/clipboard-text.vue'
|
|
18
15
|
import IconArrowGraph from '#adata-ui/icons/arrow/arrow-graph-up.vue'
|
|
19
16
|
import IconMedal from '#adata-ui/icons/medal.vue'
|
|
20
17
|
import IconCompany from '#adata-ui/icons/company.vue'
|
|
@@ -26,24 +23,27 @@ import { buildLocalizedUrl } from '#adata-ui/utils/localizedNavigation'
|
|
|
26
23
|
import IconLock from '#adata-ui/icons/lock.vue'
|
|
27
24
|
import IconReceipt from '#adata-ui/icons/receipt.vue'
|
|
28
25
|
import IconFavorite from '#adata-ui/icons/bookmark/bookmark.vue'
|
|
29
|
-
// import IconReport from '#adata-ui/icons/file/file.vue'
|
|
30
26
|
import IconHistory from '#adata-ui/icons/history.vue'
|
|
31
27
|
import IconMessage from '#adata-ui/icons/message/message.vue'
|
|
28
|
+
import AIconBriefcase from '#adata-ui/icons/briefcase.vue'
|
|
29
|
+
import AIconCarSide from '#adata-ui/icons/car-side.vue'
|
|
30
|
+
import AIconShieldVerified from '#adata-ui/icons/shield-verified.vue'
|
|
31
|
+
import AIconCart from '#adata-ui/icons/cart.vue'
|
|
32
32
|
import {
|
|
33
|
-
AIconWorkBag,
|
|
34
33
|
AIconUsersThree,
|
|
35
|
-
AIconReceipt,
|
|
36
|
-
AIconCar,
|
|
37
34
|
AIconArrowGraphUp,
|
|
38
35
|
AIconGlobe,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
AIconArrowSquareDown,
|
|
37
|
+
AIconExpandWindow,
|
|
38
|
+
AIconUsers,
|
|
39
|
+
AIconWarningTriangle,
|
|
40
|
+
AIconSearch,
|
|
41
|
+
AIconAkimat,
|
|
42
|
+
AIconCurrencyTenge,
|
|
43
|
+
AIconExcelIcon,
|
|
42
44
|
} from '#components'
|
|
43
45
|
import MapPinRect from '#adata-ui/icons/map/map-pin-rect.vue'
|
|
44
46
|
import MapPaper from '#adata-ui/icons/map-paper.vue'
|
|
45
|
-
import ChartBar from '#adata-ui/icons/chart-bar.vue'
|
|
46
|
-
import Bank from '#adata-ui/icons/bank.vue'
|
|
47
47
|
import { useUrls } from '#adata-ui/composables/useUrls'
|
|
48
48
|
import AiAssistant from '#adata-ui/icons/ai-assistant.vue'
|
|
49
49
|
import Tasks from '#adata-ui/icons/tasks.vue'
|
|
@@ -61,7 +61,7 @@ export const useHeaderNavigationLinks = () => {
|
|
|
61
61
|
key: 'pk',
|
|
62
62
|
name: t('header.products.counterparties.label'),
|
|
63
63
|
link: buildLocalizedUrl(locale, urls.pk, PAGES.pk.main),
|
|
64
|
-
icon:
|
|
64
|
+
icon: AIconUsers,
|
|
65
65
|
to: urls.pk,
|
|
66
66
|
data_attribute: 'navigation-menu-counterparty-block',
|
|
67
67
|
items: [
|
|
@@ -115,7 +115,7 @@ export const useHeaderNavigationLinks = () => {
|
|
|
115
115
|
key: 'fines',
|
|
116
116
|
name: t('header.products.fines.label'),
|
|
117
117
|
link: buildLocalizedUrl(locale, urls.avto, PAGES.fines.main),
|
|
118
|
-
icon:
|
|
118
|
+
icon: AIconCarSide,
|
|
119
119
|
data_attribute: 'navigation-menu-fines-block',
|
|
120
120
|
items: [
|
|
121
121
|
{
|
|
@@ -142,7 +142,7 @@ export const useHeaderNavigationLinks = () => {
|
|
|
142
142
|
key: 'work',
|
|
143
143
|
name: t('header.products.jobs.label'),
|
|
144
144
|
link: urls.work,
|
|
145
|
-
icon:
|
|
145
|
+
icon: AIconBriefcase,
|
|
146
146
|
data_attribute: 'navigation-menu-work-block',
|
|
147
147
|
items: [
|
|
148
148
|
{
|
|
@@ -216,7 +216,7 @@ export const useHeaderNavigationLinks = () => {
|
|
|
216
216
|
key: 'tenders',
|
|
217
217
|
name: t('header.products.tenders.label'),
|
|
218
218
|
link: urls.zakupki,
|
|
219
|
-
icon:
|
|
219
|
+
icon: AIconCart,
|
|
220
220
|
is_new: true,
|
|
221
221
|
to: urls.tender,
|
|
222
222
|
data_attribute: 'navigation-menu-zakupki-block',
|
|
@@ -264,15 +264,39 @@ export const useHeaderNavigationLinks = () => {
|
|
|
264
264
|
is_new: true,
|
|
265
265
|
name: t('header.products.edo.label'),
|
|
266
266
|
link: buildLocalizedUrl(locale, urls.edo_docs , ''),
|
|
267
|
-
icon:
|
|
267
|
+
icon: AIconUsers,
|
|
268
268
|
data_attribute: 'navigation-menu-edo-block',
|
|
269
269
|
items: [
|
|
270
270
|
{
|
|
271
|
-
title: t('header.products.edo.items.
|
|
272
|
-
subtitle: t('header.products.edo.items.
|
|
273
|
-
icon:
|
|
274
|
-
to: buildLocalizedUrl(locale, urls.edo_docs ,
|
|
275
|
-
}
|
|
271
|
+
title: t('header.products.edo.items.docSign.t'),
|
|
272
|
+
subtitle: t('header.products.edo.items.docSign.st'),
|
|
273
|
+
icon: AIconUsers,
|
|
274
|
+
to: buildLocalizedUrl(locale, urls.edo_docs , PAGES.edo.docSign)
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
title: t('header.products.edo.items.docTemplate.t'),
|
|
278
|
+
subtitle: t('header.products.edo.items.docTemplate.st'),
|
|
279
|
+
icon: AIconUsers,
|
|
280
|
+
to: buildLocalizedUrl(locale, urls.edo_docs , PAGES.edo.docTemplate)
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
title: t('header.products.edo.items.docRisk.t'),
|
|
284
|
+
subtitle: t('header.products.edo.items.docRisk.st'),
|
|
285
|
+
icon: AIconUsers,
|
|
286
|
+
to: buildLocalizedUrl(locale, urls.edo_docs , PAGES.edo.docRisk)
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
title: t('header.products.edo.items.docCompare.t'),
|
|
290
|
+
subtitle: t('header.products.edo.items.docCompare.st'),
|
|
291
|
+
icon: AIconUsers,
|
|
292
|
+
to: buildLocalizedUrl(locale, urls.edo_docs , PAGES.edo.docCompare)
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
title: t('header.products.edo.items.docAi.t'),
|
|
296
|
+
subtitle: t('header.products.edo.items.docAi.st'),
|
|
297
|
+
icon: AIconUsers,
|
|
298
|
+
to: buildLocalizedUrl(locale, urls.edo_docs , PAGES.edo.docAi)
|
|
299
|
+
},
|
|
276
300
|
]
|
|
277
301
|
},
|
|
278
302
|
{
|
|
@@ -280,7 +304,7 @@ export const useHeaderNavigationLinks = () => {
|
|
|
280
304
|
is_new: true,
|
|
281
305
|
name: t('header.products.compliance.label'),
|
|
282
306
|
link: buildLocalizedUrl(locale, urls.compliance, PAGES.compliance.l),
|
|
283
|
-
icon:
|
|
307
|
+
icon: AIconShieldVerified,
|
|
284
308
|
data_attribute: 'navigation-menu-compliance-block',
|
|
285
309
|
items: [
|
|
286
310
|
{
|
|
@@ -352,6 +376,12 @@ export const useHeaderNavigationLinks = () => {
|
|
|
352
376
|
subtitle: t('header.products.analytics.items.subjects.subtitle'),
|
|
353
377
|
icon: IconCompany,
|
|
354
378
|
to: buildLocalizedUrl(locale, urls.analytics, PAGES.analytics.business)
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
title: t('header.products.analytics.items.demography.title'),
|
|
382
|
+
subtitle: t('header.products.analytics.items.demography.subtitle'),
|
|
383
|
+
icon: IconCompany,
|
|
384
|
+
to: buildLocalizedUrl(locale, urls.analytics, PAGES.analytics.demography)
|
|
355
385
|
}
|
|
356
386
|
]
|
|
357
387
|
},
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<template>
|
|
6
|
+
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
7
|
+
<path d="M3.125 7.5H11.875" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
|
8
|
+
<path d="M7.5 3.125L11.875 7.5L7.5 11.875" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
|
9
|
+
</svg>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<style scoped>
|
|
13
|
+
|
|
14
|
+
</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<template>
|
|
6
|
+
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
7
|
+
<path d="M15 5.25H3C2.17157 5.25 1.5 5.92157 1.5 6.75V14.25C1.5 15.0784 2.17157 15.75 3 15.75H15C15.8284 15.75 16.5 15.0784 16.5 14.25V6.75C16.5 5.92157 15.8284 5.25 15 5.25Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
8
|
+
<path d="M12 15.75V3.75C12 3.35218 11.842 2.97064 11.5607 2.68934C11.2794 2.40804 10.8978 2.25 10.5 2.25H7.5C7.10218 2.25 6.72064 2.40804 6.43934 2.68934C6.15804 2.97064 6 3.35218 6 3.75V15.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
9
|
+
</svg>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<style scoped>
|
|
13
|
+
|
|
14
|
+
</style>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<template>
|
|
6
|
+
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
7
|
+
<path d="M14.25 12.75H15.75C16.2 12.75 16.5 12.45 16.5 12V9.75C16.5 9.075 15.975 8.475 15.375 8.325C14.025 7.95 12 7.5 12 7.5C12 7.5 11.025 6.45 10.35 5.775C9.975 5.475 9.525 5.25 9 5.25H3.75C3.3 5.25 2.925 5.55 2.7 5.925L1.65 8.1C1.55068 8.38967 1.5 8.69378 1.5 9V12C1.5 12.45 1.8 12.75 2.25 12.75H3.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
8
|
+
<path d="M5.25 14.25C6.07843 14.25 6.75 13.5784 6.75 12.75C6.75 11.9216 6.07843 11.25 5.25 11.25C4.42157 11.25 3.75 11.9216 3.75 12.75C3.75 13.5784 4.42157 14.25 5.25 14.25Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
9
|
+
<path d="M6.75 12.75H11.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
10
|
+
<path d="M12.75 14.25C13.5784 14.25 14.25 13.5784 14.25 12.75C14.25 11.9216 13.5784 11.25 12.75 11.25C11.9216 11.25 11.25 11.9216 11.25 12.75C11.25 13.5784 11.9216 14.25 12.75 14.25Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
11
|
+
</svg>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<style scoped>
|
|
15
|
+
|
|
16
|
+
</style>
|