adata-ui 2.1.41-beta.22 → 2.1.41-beta.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.
- package/components/navigation/footer/AFooter.vue +34 -1
- package/components/navigation/header/AAppHeader.vue +187 -0
- package/components/navigation/header/HeaderAccount.vue +159 -0
- package/components/navigation/header/HeaderUsage.vue +30 -21
- package/components/navigation/sidebar/AAppSidebar.vue +110 -0
- package/composables/useHeaderNavigationLinks.ts +17 -31
- package/lang/en.ts +14 -16
- package/lang/kk.ts +14 -16
- package/lang/ru.ts +14 -16
- package/package.json +1 -1
- package/shared/constans/pages.ts +13 -14
|
@@ -21,31 +21,37 @@ const socialMedia = [
|
|
|
21
21
|
icon: LinkedinIcon,
|
|
22
22
|
link: 'https://www.linkedin.com/company/adata-kz/?viewAsMember=true',
|
|
23
23
|
name: 'Linkedin',
|
|
24
|
+
dataTestId: 'footer-linkedin-link',
|
|
24
25
|
},
|
|
25
26
|
{
|
|
26
27
|
icon: YouTubeIcon,
|
|
27
28
|
link: 'https://www.youtube.com/channel/UCPkbtgwgTZbMJXjmTi3R8Uw/featured',
|
|
28
29
|
name: 'YouTube',
|
|
30
|
+
dataTestId: 'footer-youtube-link',
|
|
29
31
|
},
|
|
30
32
|
{
|
|
31
33
|
icon: InstagramIcon,
|
|
32
34
|
link: 'https://www.instagram.com/adata.kz/?igshid=253i4qxg3els',
|
|
33
35
|
name: 'Instagram',
|
|
36
|
+
dataTestId: 'footer-instagram-link',
|
|
34
37
|
},
|
|
35
38
|
{
|
|
36
39
|
icon: TelegramIcon,
|
|
37
40
|
link: 'https://t.me/adatakz_official',
|
|
38
41
|
name: 'Telegram',
|
|
42
|
+
dataTestId: 'footer-telegram-link',
|
|
39
43
|
},
|
|
40
44
|
{
|
|
41
45
|
icon: FaceBookIcon,
|
|
42
46
|
link: 'https://www.facebook.com/adata.kz',
|
|
43
47
|
name: 'Facebook',
|
|
48
|
+
dataTestId: 'footer-facebook-link',
|
|
44
49
|
},
|
|
45
50
|
{
|
|
46
51
|
icon: TikTokIcon,
|
|
47
52
|
link: 'https://www.tiktok.com/@adata.kz?lang=ru-RU',
|
|
48
53
|
name: 'Tiktok',
|
|
54
|
+
dataTestId: 'footer-tiktok-link',
|
|
49
55
|
},
|
|
50
56
|
]
|
|
51
57
|
|
|
@@ -53,28 +59,34 @@ const mainLinks = computed(() => ({
|
|
|
53
59
|
services: {
|
|
54
60
|
title: t('footer.productsAndSolutions'),
|
|
55
61
|
link: buildLocalizedUrl(locale.value, urls.landing, PAGES.allServices),
|
|
62
|
+
dataTestId: 'footer-products-solutions-link',
|
|
56
63
|
items: [
|
|
57
64
|
[
|
|
58
65
|
{
|
|
59
66
|
title: t('footer.counterparties.title'),
|
|
60
67
|
link: buildLocalizedUrl(locale.value, urls.pk, ''),
|
|
68
|
+
dataTestId: 'footer-counterparties-link',
|
|
61
69
|
},
|
|
62
70
|
{
|
|
63
71
|
title: t('footer.fines.title'),
|
|
64
|
-
link: buildLocalizedUrl(locale.value, urls.avto, PAGES.fines.
|
|
72
|
+
link: buildLocalizedUrl(locale.value, urls.avto, PAGES.fines.fines),
|
|
73
|
+
dataTestId: 'footer-fines-link',
|
|
65
74
|
},
|
|
66
75
|
{
|
|
67
76
|
title: t('footer.work.title'),
|
|
68
77
|
link: urls.work + PAGES.work.vacancy,
|
|
78
|
+
dataTestId: 'footer-work-link',
|
|
69
79
|
},
|
|
70
80
|
{
|
|
71
81
|
title: t('footer.analytics.title'),
|
|
72
82
|
link: buildLocalizedUrl(locale.value, urls.analytics, PAGES.analytics.main),
|
|
83
|
+
dataTestId: 'footer-analytics-link',
|
|
73
84
|
},
|
|
74
85
|
{
|
|
75
86
|
title: t('footer.zakupki.title'),
|
|
76
87
|
link: urls.zakupki + PAGES.tender.main,
|
|
77
88
|
isNew: true,
|
|
89
|
+
dataTestId: 'footer-purchases-link',
|
|
78
90
|
},
|
|
79
91
|
],
|
|
80
92
|
[
|
|
@@ -82,16 +94,19 @@ const mainLinks = computed(() => ({
|
|
|
82
94
|
title: t('footer.compliance.title'),
|
|
83
95
|
link: buildLocalizedUrl(locale.value, urls.compliance, PAGES.compliance.l),
|
|
84
96
|
isNew: true,
|
|
97
|
+
dataTestId: 'footer-compliance-link',
|
|
85
98
|
},
|
|
86
99
|
{
|
|
87
100
|
title: t('header.products.edo.label'),
|
|
88
101
|
link: urls.edo_docs,
|
|
89
102
|
isNew: true,
|
|
103
|
+
dataTestId: 'footer-edo-link',
|
|
90
104
|
},
|
|
91
105
|
{
|
|
92
106
|
title: t('footer.fea.title'),
|
|
93
107
|
link: buildLocalizedUrl(locale.value, urls.tnved, PAGES.fea.main),
|
|
94
108
|
isNew: true,
|
|
109
|
+
dataTestId: 'footer-tnved-link',
|
|
95
110
|
},
|
|
96
111
|
],
|
|
97
112
|
],
|
|
@@ -99,31 +114,38 @@ const mainLinks = computed(() => ({
|
|
|
99
114
|
useful: {
|
|
100
115
|
title: t('footer.useful'),
|
|
101
116
|
link: buildLocalizedUrl(locale.value, urls.landing, PAGES.usefulMain),
|
|
117
|
+
dataTestId: 'footer-useful-link',
|
|
102
118
|
items: [
|
|
103
119
|
[
|
|
104
120
|
{
|
|
105
121
|
title: t('footer.info.api'),
|
|
106
122
|
link: buildLocalizedUrl(locale.value, urls.landing, PAGES.apiDescription),
|
|
123
|
+
dataTestId: 'footer-api-description-link',
|
|
107
124
|
},
|
|
108
125
|
{
|
|
109
126
|
title: t('footer.info.userAgreement'),
|
|
110
127
|
link: buildLocalizedUrl(locale.value, urls.landing, PAGES.userAgreement),
|
|
128
|
+
dataTestId: 'footer-user-agreement-link',
|
|
111
129
|
},
|
|
112
130
|
{
|
|
113
131
|
title: t('footer.info.privacyPolicy'),
|
|
114
132
|
link: buildLocalizedUrl(locale.value, urls.landing, PAGES.privacy),
|
|
133
|
+
dataTestId: 'footer-privacy-policy-link',
|
|
115
134
|
},
|
|
116
135
|
{
|
|
117
136
|
title: t('footer.info.vacancy'),
|
|
118
137
|
link: buildLocalizedUrl(locale.value, urls.landing, PAGES.vacancy),
|
|
138
|
+
dataTestId: 'footer-vacancies-link',
|
|
119
139
|
},
|
|
120
140
|
{
|
|
121
141
|
title: t('footer.info.counterparty'),
|
|
122
142
|
link: buildLocalizedUrl(locale.value, urls.pk, PAGES.pk.counterparty),
|
|
143
|
+
dataTestId: 'footer-company-catalog-link',
|
|
123
144
|
},
|
|
124
145
|
{
|
|
125
146
|
title: t('footer.info.onlineServices'),
|
|
126
147
|
link: buildLocalizedUrl(locale.value, urls.pk, `${PAGES.services}/taxpayer`),
|
|
148
|
+
dataTestId: 'footer-online-services-link',
|
|
127
149
|
},
|
|
128
150
|
],
|
|
129
151
|
],
|
|
@@ -133,24 +155,28 @@ const mainLinks = computed(() => ({
|
|
|
133
155
|
const contactLinks = computed(() => ({
|
|
134
156
|
title: t('footer.contacts.title'),
|
|
135
157
|
link: buildLocalizedUrl(locale.value, urls.landing, PAGES.contacts),
|
|
158
|
+
dataTestId: 'footer-contacts-link',
|
|
136
159
|
items: [
|
|
137
160
|
{
|
|
138
161
|
title: t('footer.contacts.address'),
|
|
139
162
|
link: 'https://go.2gis.com/30j2f',
|
|
140
163
|
startIcon: AIconLocation,
|
|
141
164
|
endIcon: null,
|
|
165
|
+
dataTestId: 'footer-address-link',
|
|
142
166
|
},
|
|
143
167
|
{
|
|
144
168
|
title: '+7 (747) 120 34 67',
|
|
145
169
|
link: 'tel:+77471203467',
|
|
146
170
|
startIcon: AIconPhone,
|
|
147
171
|
endIcon: null,
|
|
172
|
+
dataTestId: 'footer-phone-link',
|
|
148
173
|
},
|
|
149
174
|
{
|
|
150
175
|
title: 'info@adata.kz',
|
|
151
176
|
link: 'mailto:info@adata.kz',
|
|
152
177
|
startIcon: AIconMail,
|
|
153
178
|
endIcon: null,
|
|
179
|
+
dataTestId: 'footer-email-link',
|
|
154
180
|
},
|
|
155
181
|
{
|
|
156
182
|
title: t('footer.contacts.goToChat'),
|
|
@@ -158,6 +184,7 @@ const contactLinks = computed(() => ({
|
|
|
158
184
|
startIcon: null,
|
|
159
185
|
endIcon: AIconExpandWindow,
|
|
160
186
|
class: 'font-bold',
|
|
187
|
+
dataTestId: 'footer-support-chat-link',
|
|
161
188
|
},
|
|
162
189
|
],
|
|
163
190
|
}))
|
|
@@ -166,6 +193,7 @@ const contactLinks = computed(() => ({
|
|
|
166
193
|
<template>
|
|
167
194
|
<footer
|
|
168
195
|
id="footer"
|
|
196
|
+
data-test-id="footer-container"
|
|
169
197
|
class="rounded-t-[32px] bg-secondary-500 py-4 lg:py-5 dark:bg-black"
|
|
170
198
|
>
|
|
171
199
|
<div class="a-container mobile-padding flex flex-col justify-between gap-8 py-5 text-white lg:flex-row">
|
|
@@ -188,6 +216,7 @@ const contactLinks = computed(() => ({
|
|
|
188
216
|
target="_blank"
|
|
189
217
|
:to="social.link"
|
|
190
218
|
:aria-label="social.name"
|
|
219
|
+
:data-test-id="social.dataTestId"
|
|
191
220
|
>
|
|
192
221
|
<span class="visually-hidden"> {{ social.name }}</span>
|
|
193
222
|
<component
|
|
@@ -215,6 +244,7 @@ const contactLinks = computed(() => ({
|
|
|
215
244
|
:label="item.title"
|
|
216
245
|
:link="item?.link?.replace(/\/$/, '')"
|
|
217
246
|
:content="item.items"
|
|
247
|
+
:data-test-id="item.dataTestId"
|
|
218
248
|
/>
|
|
219
249
|
</div>
|
|
220
250
|
</div>
|
|
@@ -223,6 +253,7 @@ const contactLinks = computed(() => ({
|
|
|
223
253
|
<nuxt-link
|
|
224
254
|
class="cursor-pointer border-b-[0.5px] border-current pr-4 pb-3 text-body-md-strong"
|
|
225
255
|
:to="contactLinks.link?.replace(/\/$/, '')"
|
|
256
|
+
:data-test-id="contactLinks.dataTestId"
|
|
226
257
|
>
|
|
227
258
|
{{ contactLinks.title }}
|
|
228
259
|
</nuxt-link>
|
|
@@ -237,6 +268,7 @@ const contactLinks = computed(() => ({
|
|
|
237
268
|
class="flex cursor-pointer items-center gap-2 text-body-sm-medium"
|
|
238
269
|
:class="link.class"
|
|
239
270
|
:to="link.link?.replace(/\/$/, '')"
|
|
271
|
+
:data-test-id="link.dataTestId"
|
|
240
272
|
>
|
|
241
273
|
<component
|
|
242
274
|
:is="link.startIcon"
|
|
@@ -265,6 +297,7 @@ const contactLinks = computed(() => ({
|
|
|
265
297
|
target="_blank"
|
|
266
298
|
:to="social.link"
|
|
267
299
|
:aria-label="social.name"
|
|
300
|
+
:data-test-id="social.dataTestId"
|
|
268
301
|
>
|
|
269
302
|
<component
|
|
270
303
|
:is="social.icon"
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import type { ProjectKeys } from '#adata-ui/components/navigation/header/types'
|
|
3
|
+
import HeaderAccount from '#adata-ui/components/navigation/header/HeaderAccount.vue'
|
|
4
|
+
import { useCurrentModule } from '#adata-ui/composables/projectState'
|
|
5
|
+
import DoubleChevronRight from '#adata-ui/icons/chevron/double-chevron-right.vue'
|
|
6
|
+
import Logo from '#adata-ui/icons/logo.vue'
|
|
7
|
+
import { twMerge } from '#adata-ui/utils/twMerge'
|
|
8
|
+
import { useElementBounding } from '@vueuse/core'
|
|
9
|
+
import HeaderLink from './HeaderLink.vue'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Шапка оболочки с боковым меню: ВЭД `layouts/app.vue`, Комплаенс
|
|
13
|
+
* `pages/main.vue`.
|
|
14
|
+
*
|
|
15
|
+
* Отдельный компонент, а не режим `a-header`: с шапкой лендинга у неё общая
|
|
16
|
+
* только правая группа (`header-account`), которая и вынесена. Левые зоны
|
|
17
|
+
* разные целиком — здесь кнопка сворачивания сайдбара, там логотип с мега-меню
|
|
18
|
+
* и поиском; пропы и события тоже не пересекаются.
|
|
19
|
+
*
|
|
20
|
+
* Системного баннера (`system-notification`) здесь нет намеренно: это баннер
|
|
21
|
+
* публичных страниц, за оболочкой он никогда не показывался. Понадобится —
|
|
22
|
+
* ставится потребителем над шапкой.
|
|
23
|
+
*
|
|
24
|
+
* Проектные различия — слотами: `actions` (иконки перед профилем — настройки,
|
|
25
|
+
* уведомления Комплаенса) и `profile-menu` (своё меню).
|
|
26
|
+
*/
|
|
27
|
+
defineOptions({ name: 'AAppHeader' })
|
|
28
|
+
|
|
29
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
30
|
+
limitRemaining: 0,
|
|
31
|
+
daysRemaining: 0,
|
|
32
|
+
maxLimit: 0,
|
|
33
|
+
balance: 0,
|
|
34
|
+
rate: 'Базовый',
|
|
35
|
+
module: 'pk',
|
|
36
|
+
collapsed: false,
|
|
37
|
+
peeking: false,
|
|
38
|
+
mobileLogo: false,
|
|
39
|
+
showDays: true,
|
|
40
|
+
showProfileOnMobile: false,
|
|
41
|
+
headerClass: undefined,
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
/* `toggleCollapsed` в camelCase по правилу eslint; в шаблонах потребителей —
|
|
45
|
+
`@toggle-collapsed`, Vue нормализует. */
|
|
46
|
+
const emit = defineEmits(['logout', 'toggleCollapsed'])
|
|
47
|
+
|
|
48
|
+
defineSlots<{
|
|
49
|
+
/** Иконки-действия между переключателями и меню профиля (настройки, уведомления). */
|
|
50
|
+
'actions'?: () => any
|
|
51
|
+
/** Своё содержимое дропдауна профиля. `close` закрывает дропдаун. */
|
|
52
|
+
'profile-menu'?: (props: { close: () => void }) => any
|
|
53
|
+
}>()
|
|
54
|
+
|
|
55
|
+
interface Props {
|
|
56
|
+
email: string | undefined
|
|
57
|
+
isAuthenticated: boolean
|
|
58
|
+
rate?: string
|
|
59
|
+
replenish?: string
|
|
60
|
+
daysRemaining?: number
|
|
61
|
+
limitRemaining?: number
|
|
62
|
+
maxLimit?: number
|
|
63
|
+
balance?: number
|
|
64
|
+
module?: ProjectKeys
|
|
65
|
+
oldVersion?: string
|
|
66
|
+
/** Состояние бокового меню: свёрнуто ли. */
|
|
67
|
+
collapsed?: boolean
|
|
68
|
+
/** Свёрнутое меню выехало поверх контента — кнопка означает «закрепить». */
|
|
69
|
+
peeking?: boolean
|
|
70
|
+
/** Компактный знак логотипа ниже `lg` — когда сайдбара на телефоне нет. */
|
|
71
|
+
mobileLogo?: boolean
|
|
72
|
+
/** Блок «остаток дней» в счётчике лимита. */
|
|
73
|
+
showDays?: boolean
|
|
74
|
+
/** Кнопка профиля ниже `lg`. По умолчанию скрыта: профиль живёт в нижней панели. */
|
|
75
|
+
showProfileOnMobile?: boolean
|
|
76
|
+
/**
|
|
77
|
+
* Классы поверх `<header>`, мержатся через twMerge. Высота задана по
|
|
78
|
+
* брейкпоинтам (`h-[62px] lg:h-[66px]`), поэтому свою задавать тоже с
|
|
79
|
+
* брейкпоинтом: `header-class="h-18 lg:h-18"`.
|
|
80
|
+
*/
|
|
81
|
+
headerClass?: string
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const { t } = useI18n()
|
|
85
|
+
|
|
86
|
+
useCurrentModule().value = props.module
|
|
87
|
+
|
|
88
|
+
const headerRef = ref<HTMLElement | null>(null)
|
|
89
|
+
const { bottom: headerBottom } = useElementBounding(headerRef)
|
|
90
|
+
|
|
91
|
+
/* На телефоне шапка оболочки ниже: 62px против 66 — четыре пикселя заметны,
|
|
92
|
+
когда весь экран 812. На широком экране 66 — с ней совпадает строка логотипа
|
|
93
|
+
бокового меню, и линия под ними идёт одной. */
|
|
94
|
+
const headerClasses = computed(() => twMerge(
|
|
95
|
+
'relative h-[62px] shrink-0 border-b border-default bg-main/10 backdrop-blur-lg backdrop-saturate-140 lg:h-[66px]',
|
|
96
|
+
props.headerClass,
|
|
97
|
+
))
|
|
98
|
+
|
|
99
|
+
/*
|
|
100
|
+
Подпись кнопки сворачивания. Пока меню выехало поверх контента, оно всё ещё
|
|
101
|
+
считается свёрнутым, поэтому кнопка означает «закрепить», а не «развернуть».
|
|
102
|
+
*/
|
|
103
|
+
const collapseLabel = computed(() => {
|
|
104
|
+
if (!props.collapsed) return t('header.shell.collapse')
|
|
105
|
+
return props.peeking ? t('header.shell.pin') : t('header.shell.expand')
|
|
106
|
+
})
|
|
107
|
+
</script>
|
|
108
|
+
|
|
109
|
+
<template>
|
|
110
|
+
<header
|
|
111
|
+
ref="headerRef"
|
|
112
|
+
:class="headerClasses"
|
|
113
|
+
data-variant="app"
|
|
114
|
+
>
|
|
115
|
+
<div class="flex size-full items-center justify-between gap-2 px-3 sm:px-4">
|
|
116
|
+
<nav class="flex min-w-0 grow items-center gap-2">
|
|
117
|
+
<!-- Ниже lg бокового меню может не быть — тогда логотип переезжает в
|
|
118
|
+
шапку знаком, чтобы не пропасть совсем. Полное начертание съедало
|
|
119
|
+
бы треть строки, а место нужно кнопкам. -->
|
|
120
|
+
<nuxt-link-locale
|
|
121
|
+
v-if="mobileLogo"
|
|
122
|
+
to="/"
|
|
123
|
+
data-test-id="header-logo-compact"
|
|
124
|
+
:title="t('header.shell.home')"
|
|
125
|
+
:aria-label="t('header.shell.home')"
|
|
126
|
+
class="mr-1 flex shrink-0 items-center text-primary transition-opacity hover:opacity-80 lg:hidden"
|
|
127
|
+
>
|
|
128
|
+
<logo />
|
|
129
|
+
</nuxt-link-locale>
|
|
130
|
+
|
|
131
|
+
<!-- Кнопка сворачивания живёт в шапке, а не в самом меню: она не должна
|
|
132
|
+
уезжать вместе с панелью, которой управляет. Ниже lg панели нет —
|
|
133
|
+
и кнопки тоже. -->
|
|
134
|
+
<button
|
|
135
|
+
type="button"
|
|
136
|
+
data-test-id="collapse-toggle"
|
|
137
|
+
:aria-label="collapseLabel"
|
|
138
|
+
:aria-expanded="!collapsed"
|
|
139
|
+
:title="collapseLabel"
|
|
140
|
+
class="hidden size-9 shrink-0 cursor-pointer items-center justify-center rounded-md text-icon-default transition-colors hover:bg-action-secondary-hover hover:text-icon-strong lg:flex"
|
|
141
|
+
@click="emit('toggleCollapsed')"
|
|
142
|
+
>
|
|
143
|
+
<!-- Двойной шеврон смотрит в сторону, куда уедет панель. -->
|
|
144
|
+
<double-chevron-right
|
|
145
|
+
class="size-5 transition-transform"
|
|
146
|
+
:class="{ 'rotate-180': !collapsed }"
|
|
147
|
+
/>
|
|
148
|
+
</button>
|
|
149
|
+
|
|
150
|
+
<!-- Верхняя навигация — только с xl: на планшете ей не хватает места
|
|
151
|
+
рядом со счётчиками. -->
|
|
152
|
+
<div class="hidden xl:block">
|
|
153
|
+
<header-link :menu-top="headerBottom || 66" />
|
|
154
|
+
</div>
|
|
155
|
+
</nav>
|
|
156
|
+
|
|
157
|
+
<header-account
|
|
158
|
+
divider
|
|
159
|
+
:is-authenticated="isAuthenticated"
|
|
160
|
+
:email="email"
|
|
161
|
+
:rate="rate"
|
|
162
|
+
:replenish="replenish"
|
|
163
|
+
:balance="balance"
|
|
164
|
+
:days-remaining="daysRemaining"
|
|
165
|
+
:limit-remaining="limitRemaining"
|
|
166
|
+
:max-limit="maxLimit"
|
|
167
|
+
:show-days="showDays"
|
|
168
|
+
:show-profile-on-mobile="showProfileOnMobile"
|
|
169
|
+
:old-version="oldVersion"
|
|
170
|
+
@logout="emit('logout')"
|
|
171
|
+
>
|
|
172
|
+
<template #actions>
|
|
173
|
+
<slot name="actions" />
|
|
174
|
+
</template>
|
|
175
|
+
<template
|
|
176
|
+
v-if="$slots['profile-menu']"
|
|
177
|
+
#profile-menu="{ close }"
|
|
178
|
+
>
|
|
179
|
+
<slot
|
|
180
|
+
name="profile-menu"
|
|
181
|
+
:close="close"
|
|
182
|
+
/>
|
|
183
|
+
</template>
|
|
184
|
+
</header-account>
|
|
185
|
+
</div>
|
|
186
|
+
</header>
|
|
187
|
+
</template>
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import AColorMode from '#adata-ui/components/features/color-mode/AColorMode.client.vue'
|
|
3
|
+
import ADropdownV2 from '#adata-ui/components/features/dropdown/ADropdownV2.vue'
|
|
4
|
+
import LangSwitcher from '#adata-ui/components/features/lang-switcher/lang-switcher.vue'
|
|
5
|
+
import AChangeVersion from '#adata-ui/components/features/сhange-version/AChangeVersion.vue'
|
|
6
|
+
import HeaderUsage from '#adata-ui/components/navigation/header/HeaderUsage.vue'
|
|
7
|
+
import ProfileMenu from '#adata-ui/components/navigation/header/ProfileMenu.vue'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Правая группа шапки: счётчики тарифа, переключатели темы и языка, действия
|
|
11
|
+
* проекта и меню профиля.
|
|
12
|
+
*
|
|
13
|
+
* Отдельный компонент, потому что это единственное, что у шапки лендинга
|
|
14
|
+
* (`a-header`) и шапки оболочки (`a-app-header`) совпадает — и одновременно
|
|
15
|
+
* самое живое место: тарифы, лимиты, авторизация. Левые зоны у них общего не
|
|
16
|
+
* имеют вовсе, поэтому делить шапку флагом `variant` смысла не было.
|
|
17
|
+
*/
|
|
18
|
+
defineOptions({ name: 'HeaderAccount' })
|
|
19
|
+
|
|
20
|
+
withDefaults(defineProps<Props>(), {
|
|
21
|
+
limitRemaining: 0,
|
|
22
|
+
daysRemaining: 0,
|
|
23
|
+
maxLimit: 0,
|
|
24
|
+
balance: 0,
|
|
25
|
+
rate: 'Базовый',
|
|
26
|
+
showDays: true,
|
|
27
|
+
showLang: true,
|
|
28
|
+
showLogIn: false,
|
|
29
|
+
showProfileOnMobile: false,
|
|
30
|
+
divider: false,
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const emit = defineEmits(['logout', 'login'])
|
|
34
|
+
|
|
35
|
+
defineSlots<{
|
|
36
|
+
/** Иконки-действия между переключателями и меню профиля (настройки, уведомления). */
|
|
37
|
+
'actions'?: () => any
|
|
38
|
+
/** Своё содержимое дропдауна профиля. `close` закрывает дропдаун. */
|
|
39
|
+
'profile-menu'?: (props: { close: () => void }) => any
|
|
40
|
+
}>()
|
|
41
|
+
|
|
42
|
+
interface Props {
|
|
43
|
+
isAuthenticated: boolean
|
|
44
|
+
email: string | undefined
|
|
45
|
+
rate?: string
|
|
46
|
+
replenish?: string
|
|
47
|
+
daysRemaining?: number
|
|
48
|
+
limitRemaining?: number
|
|
49
|
+
maxLimit?: number
|
|
50
|
+
balance?: number
|
|
51
|
+
/** Блок «остаток дней» в счётчике лимита. */
|
|
52
|
+
showDays?: boolean
|
|
53
|
+
/** Переключатель языка. */
|
|
54
|
+
showLang?: boolean
|
|
55
|
+
/** Кнопка входа в конце группы — там, где страницы открыты неавторизованным. */
|
|
56
|
+
showLogIn?: boolean
|
|
57
|
+
/** Кнопка профиля ниже `lg`. По умолчанию скрыта: профиль живёт в нижней панели. */
|
|
58
|
+
showProfileOnMobile?: boolean
|
|
59
|
+
/** Вертикальная черта между счётчиками и переключателями. */
|
|
60
|
+
divider?: boolean
|
|
61
|
+
/** Ссылка на старую версию проекта — кнопка `a-change-version`. */
|
|
62
|
+
oldVersion?: string
|
|
63
|
+
}
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<template>
|
|
67
|
+
<!-- Адаптив — классами на элементах, а не двумя копиями группы: слот
|
|
68
|
+
`actions` должен монтироваться один раз (в нём у потребителей живёт
|
|
69
|
+
polling уведомлений). -->
|
|
70
|
+
<div class="flex shrink-0 items-center gap-1 text-primary lg:gap-4">
|
|
71
|
+
<template v-if="isAuthenticated">
|
|
72
|
+
<div class="hidden md:flex">
|
|
73
|
+
<header-usage
|
|
74
|
+
:limit-remaining="limitRemaining"
|
|
75
|
+
:max-limit="maxLimit"
|
|
76
|
+
:days-remaining="daysRemaining"
|
|
77
|
+
:show-days="showDays"
|
|
78
|
+
/>
|
|
79
|
+
</div>
|
|
80
|
+
<!-- Разделитель отделяет счётчики от переключателей: без него ряд
|
|
81
|
+
читается одной длинной строкой. -->
|
|
82
|
+
<span
|
|
83
|
+
v-if="divider"
|
|
84
|
+
class="hidden h-4 w-px shrink-0 bg-(--border-color-default) md:block"
|
|
85
|
+
aria-hidden="true"
|
|
86
|
+
/>
|
|
87
|
+
</template>
|
|
88
|
+
|
|
89
|
+
<div class="flex items-center gap-1 lg:gap-2">
|
|
90
|
+
<!-- Обёртка, а не класс на компоненте: у его корня свой `flex`,
|
|
91
|
+
и `hidden` с ним конфликтовал бы по порядку в CSS. -->
|
|
92
|
+
<div
|
|
93
|
+
v-if="oldVersion"
|
|
94
|
+
class="hidden lg:block"
|
|
95
|
+
>
|
|
96
|
+
<a-change-version :url="oldVersion" />
|
|
97
|
+
</div>
|
|
98
|
+
<a-color-mode />
|
|
99
|
+
<template v-if="showLang">
|
|
100
|
+
<lang-switcher
|
|
101
|
+
class="lg:hidden"
|
|
102
|
+
is-mobile
|
|
103
|
+
/>
|
|
104
|
+
<lang-switcher class="hidden lg:block" />
|
|
105
|
+
</template>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<slot name="actions" />
|
|
109
|
+
|
|
110
|
+
<a-dropdown-v2
|
|
111
|
+
v-show="isAuthenticated"
|
|
112
|
+
placement="bottom-end"
|
|
113
|
+
>
|
|
114
|
+
<template #default="{ toggle, isOpen }">
|
|
115
|
+
<button
|
|
116
|
+
type="button"
|
|
117
|
+
class="h-8 cursor-pointer items-center gap-1.5 rounded-full px-3 text-body-md-medium transition-colors duration-150 hover:bg-action-secondary-hover focus-visible:ring-2 focus-visible:ring-brand-500/50 focus-visible:ring-offset-1 focus-visible:outline-hidden"
|
|
118
|
+
:class="[
|
|
119
|
+
showProfileOnMobile ? 'inline-flex' : 'hidden lg:inline-flex',
|
|
120
|
+
isOpen ? 'bg-action-secondary-hover text-primary' : 'text-primary',
|
|
121
|
+
]"
|
|
122
|
+
data-test-id="header-email-text"
|
|
123
|
+
aria-haspopup="menu"
|
|
124
|
+
:aria-expanded="isOpen"
|
|
125
|
+
:aria-label="email"
|
|
126
|
+
@click.stop="toggle"
|
|
127
|
+
>
|
|
128
|
+
<span class="max-w-35 truncate lg:max-w-none">{{ email }}</span>
|
|
129
|
+
<a-icon-chevron-down
|
|
130
|
+
class="shrink-0 transition-transform duration-200"
|
|
131
|
+
:class="{ 'rotate-180': isOpen }"
|
|
132
|
+
/>
|
|
133
|
+
</button>
|
|
134
|
+
</template>
|
|
135
|
+
<template #content="{ close }">
|
|
136
|
+
<slot
|
|
137
|
+
name="profile-menu"
|
|
138
|
+
:close="close"
|
|
139
|
+
>
|
|
140
|
+
<profile-menu
|
|
141
|
+
:email="email"
|
|
142
|
+
:balance="balance"
|
|
143
|
+
:days-remaining="daysRemaining"
|
|
144
|
+
:limit-remaining="limitRemaining"
|
|
145
|
+
:rate="rate"
|
|
146
|
+
:replenish="replenish"
|
|
147
|
+
@logout="emit('logout')"
|
|
148
|
+
/>
|
|
149
|
+
</slot>
|
|
150
|
+
</template>
|
|
151
|
+
</a-dropdown-v2>
|
|
152
|
+
|
|
153
|
+
<!-- Кнопка сама скрыта ниже lg. -->
|
|
154
|
+
<a-button-login
|
|
155
|
+
v-show="showLogIn && !isAuthenticated"
|
|
156
|
+
@click="emit('login')"
|
|
157
|
+
/>
|
|
158
|
+
</div>
|
|
159
|
+
</template>
|
|
@@ -8,12 +8,19 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
8
8
|
limitRemaining: 0,
|
|
9
9
|
maxLimit: 300,
|
|
10
10
|
daysRemaining: 0,
|
|
11
|
+
showDays: true,
|
|
11
12
|
})
|
|
12
13
|
|
|
13
14
|
interface Props {
|
|
14
15
|
limitRemaining?: number
|
|
15
16
|
maxLimit?: number
|
|
16
17
|
daysRemaining?: number
|
|
18
|
+
/**
|
|
19
|
+
* Блок «остаток дней тарифа». По умолчанию виден (лендинги); выключается в
|
|
20
|
+
* оболочке Комплаенса, где дни не показывают. Аддитивно — default сохраняет
|
|
21
|
+
* прежнее поведение `a-header`.
|
|
22
|
+
*/
|
|
23
|
+
showDays?: boolean
|
|
17
24
|
}
|
|
18
25
|
|
|
19
26
|
const percent = computed(() => {
|
|
@@ -99,29 +106,31 @@ const valueColorClass = computed(() => {
|
|
|
99
106
|
</template>
|
|
100
107
|
</a-tooltip-v2>
|
|
101
108
|
|
|
102
|
-
<
|
|
109
|
+
<template v-if="showDays">
|
|
110
|
+
<span class="h-4 w-px shrink-0 bg-(--border-color-default)" />
|
|
103
111
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
>
|
|
111
|
-
<div
|
|
112
|
-
class="flex shrink-0 items-center gap-1.5"
|
|
113
|
-
data-test-id="header-usage-days"
|
|
112
|
+
<a-tooltip-v2
|
|
113
|
+
class="shrink-0"
|
|
114
|
+
placement="bottom"
|
|
115
|
+
arrow
|
|
116
|
+
:z-index="10001"
|
|
117
|
+
content-class="py-0.5"
|
|
114
118
|
>
|
|
115
|
-
<
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
119
|
+
<div
|
|
120
|
+
class="flex shrink-0 items-center gap-1.5"
|
|
121
|
+
data-test-id="header-usage-days"
|
|
122
|
+
>
|
|
123
|
+
<calendar class="size-4 shrink-0 text-quaternary" />
|
|
124
|
+
<span class="text-body-md font-bold tabular-nums text-primary">{{ daysRemaining }}</span>
|
|
125
|
+
</div>
|
|
126
|
+
|
|
127
|
+
<template #content>
|
|
128
|
+
<span class="whitespace-nowrap text-body-sm leading-none text-primary">
|
|
129
|
+
Остаток дней: {{ daysRemaining }}
|
|
130
|
+
</span>
|
|
131
|
+
</template>
|
|
132
|
+
</a-tooltip-v2>
|
|
133
|
+
</template>
|
|
125
134
|
</div>
|
|
126
135
|
</client-only>
|
|
127
136
|
</template>
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import AdataLogo from '#adata-ui/icons/adata-logo.vue'
|
|
3
|
+
import Logo from '#adata-ui/icons/logo.vue'
|
|
4
|
+
import { twMerge } from '#adata-ui/utils/twMerge'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Каркас-оболочка бокового меню оболочек с сайдбаром (ВЭД `layouts/app.vue`,
|
|
8
|
+
* Комплаенс `pages/main.vue`).
|
|
9
|
+
*
|
|
10
|
+
* Намеренно тонкий: общего у сайдбаров ВЭД и Комплаенса — только хром (рамка
|
|
11
|
+
* `<aside>`, строка логотипа, поворот знак/начертание по свёрнутости). Данные,
|
|
12
|
+
* источник схлопывания (cookie у Комплаенса, проп у ВЭД), способ разворота
|
|
13
|
+
* (resize у Комплаенса, выезд-peek у ВЭД) и рендер пунктов у каждого свои и
|
|
14
|
+
* живут в проекте — сюда они приезжают содержимым слота по умолчанию.
|
|
15
|
+
*
|
|
16
|
+
* На корне НЕТ `overflow`, `transform`, `filter`, `contain`: сайдбар ВЭД в
|
|
17
|
+
* свёрнутом виде выезжает вложенным `<nav>` за пределы 80px, и любой из этих
|
|
18
|
+
* стилей на предке обрезал бы панель. Ширину, высоту, переход и `display`
|
|
19
|
+
* (у ВЭД корень `hidden lg:flex`) задаёт потребитель пропом `sidebar-class` —
|
|
20
|
+
* он мержится через twMerge, поэтому его `hidden` корректно перебивает базовый
|
|
21
|
+
* `flex`. `style` и `data-*` долетают до корня обычным наследованием атрибутов.
|
|
22
|
+
*/
|
|
23
|
+
defineOptions({ name: 'AAppSidebar' })
|
|
24
|
+
|
|
25
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
26
|
+
collapsed: false,
|
|
27
|
+
logoTo: '/',
|
|
28
|
+
logoLocalized: false,
|
|
29
|
+
border: 'full',
|
|
30
|
+
sidebarClass: undefined,
|
|
31
|
+
logoRowClass: undefined,
|
|
32
|
+
logoLinkClass: undefined,
|
|
33
|
+
logoTitle: undefined,
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
defineSlots<{
|
|
37
|
+
/** Знак в строке логотипа. По умолчанию — `logo` (свёрнуто) / `adata-logo`. */
|
|
38
|
+
'logo'?: (props: { collapsed: boolean }) => any
|
|
39
|
+
/** Полная замена строки логотипа, если проектная разметка не укладывается в пропы. */
|
|
40
|
+
'logo-row'?: () => any
|
|
41
|
+
/** Содержимое меню: `<nav>` со своими пунктами (у ВЭД — с выездом, у AC — с resize). */
|
|
42
|
+
'default'?: () => any
|
|
43
|
+
}>()
|
|
44
|
+
|
|
45
|
+
interface Props {
|
|
46
|
+
/** Свёрнут ли сайдбар — управляет знаком логотипа (знак/начертание). */
|
|
47
|
+
collapsed?: boolean
|
|
48
|
+
/** Куда ведёт логотип. */
|
|
49
|
+
logoTo?: string
|
|
50
|
+
/** Локализованная ссылка (`nuxt-link-locale`) вместо обычной (`nuxt-link`). */
|
|
51
|
+
logoLocalized?: boolean
|
|
52
|
+
/** Рамка: `full` — по кругу (Комплаенс), `right` — только справа (ВЭД). */
|
|
53
|
+
border?: 'full' | 'right'
|
|
54
|
+
/** Классы корня (ширина, высота, `display`, переход) — мержатся через twMerge. */
|
|
55
|
+
sidebarClass?: string | (string | false | null | undefined)[]
|
|
56
|
+
/** Классы строки логотипа: высота, видимость, паддинги, выравнивание. */
|
|
57
|
+
logoRowClass?: string
|
|
58
|
+
/** Классы ссылки-логотипа (например, `m-5` у Комплаенса). */
|
|
59
|
+
logoLinkClass?: string
|
|
60
|
+
/** `title`/`aria-label` ссылки-логотипа; при наличии добавляет `data-home-link`. */
|
|
61
|
+
logoTitle?: string
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const asideClass = computed(() => twMerge(
|
|
65
|
+
'relative flex flex-col bg-main',
|
|
66
|
+
props.border === 'right' ? 'border-r border-default' : 'border border-default',
|
|
67
|
+
props.sidebarClass,
|
|
68
|
+
))
|
|
69
|
+
|
|
70
|
+
const logoRowClasses = computed(() => twMerge(
|
|
71
|
+
'shrink-0 overflow-hidden border-b border-default',
|
|
72
|
+
props.logoRowClass,
|
|
73
|
+
))
|
|
74
|
+
</script>
|
|
75
|
+
|
|
76
|
+
<template>
|
|
77
|
+
<aside :class="asideClass">
|
|
78
|
+
<slot name="logo-row">
|
|
79
|
+
<div :class="logoRowClasses">
|
|
80
|
+
<nuxt-link-locale
|
|
81
|
+
v-if="logoLocalized"
|
|
82
|
+
:to="logoTo"
|
|
83
|
+
:class="logoLinkClass"
|
|
84
|
+
:data-home-link="logoTitle ? '' : undefined"
|
|
85
|
+
:title="logoTitle"
|
|
86
|
+
:aria-label="logoTitle"
|
|
87
|
+
>
|
|
88
|
+
<slot name="logo" :collapsed="collapsed">
|
|
89
|
+
<logo v-if="collapsed" />
|
|
90
|
+
<adata-logo v-else />
|
|
91
|
+
</slot>
|
|
92
|
+
</nuxt-link-locale>
|
|
93
|
+
<nuxt-link
|
|
94
|
+
v-else
|
|
95
|
+
:to="logoTo"
|
|
96
|
+
:class="logoLinkClass"
|
|
97
|
+
:title="logoTitle"
|
|
98
|
+
:aria-label="logoTitle"
|
|
99
|
+
>
|
|
100
|
+
<slot name="logo" :collapsed="collapsed">
|
|
101
|
+
<logo v-if="collapsed" />
|
|
102
|
+
<adata-logo v-else />
|
|
103
|
+
</slot>
|
|
104
|
+
</nuxt-link>
|
|
105
|
+
</div>
|
|
106
|
+
</slot>
|
|
107
|
+
|
|
108
|
+
<slot />
|
|
109
|
+
</aside>
|
|
110
|
+
</template>
|
|
@@ -19,7 +19,6 @@ import IconFilePen from '#adata-ui/icons/file-pen.vue'
|
|
|
19
19
|
import IconFileStack from '#adata-ui/icons/file-stack.vue'
|
|
20
20
|
import IconGitCompare from '#adata-ui/icons/git-compare.vue'
|
|
21
21
|
import IconGlobe from '#adata-ui/icons/globe.vue'
|
|
22
|
-
import IconHandshake from '#adata-ui/icons/handshake.vue'
|
|
23
22
|
import IconDocument from '#adata-ui/icons/hdocument.vue'
|
|
24
23
|
import IconHistory from '#adata-ui/icons/history.vue'
|
|
25
24
|
import HotLine from '#adata-ui/icons/hot-line.vue'
|
|
@@ -121,7 +120,7 @@ export function useHeaderNavigationLinks() {
|
|
|
121
120
|
{
|
|
122
121
|
key: 'fines',
|
|
123
122
|
name: t('header.products.fines.label'),
|
|
124
|
-
link: buildLocalizedUrl(locale, urls.avto, PAGES.fines.
|
|
123
|
+
link: buildLocalizedUrl(locale, urls.avto, PAGES.fines.fines),
|
|
125
124
|
icon: AIconCarSide,
|
|
126
125
|
data_attribute: 'navigation-menu-fines-block',
|
|
127
126
|
items: [
|
|
@@ -129,13 +128,13 @@ export function useHeaderNavigationLinks() {
|
|
|
129
128
|
title: t('header.products.fines.items.fines.title'),
|
|
130
129
|
subtitle: t('header.products.fines.items.fines.subtitle'),
|
|
131
130
|
icon: IconCheckCircle,
|
|
132
|
-
to: buildLocalizedUrl(locale, urls.avto, PAGES.fines.
|
|
131
|
+
to: buildLocalizedUrl(locale, urls.avto, PAGES.fines.fines),
|
|
133
132
|
},
|
|
134
133
|
{
|
|
135
134
|
title: t('header.products.fines.items.auto.title'),
|
|
136
135
|
subtitle: t('header.products.fines.items.auto.subtitle'),
|
|
137
136
|
icon: IconCar,
|
|
138
|
-
to: buildLocalizedUrl(locale, urls.avto, PAGES.fines.
|
|
137
|
+
to: buildLocalizedUrl(locale, urls.avto, PAGES.fines.main),
|
|
139
138
|
},
|
|
140
139
|
// {
|
|
141
140
|
// title: t('header.products.fines.items.wholesaleAuto.title'),
|
|
@@ -175,23 +174,16 @@ export function useHeaderNavigationLinks() {
|
|
|
175
174
|
data_attribute: 'navigation-menu-ved-block',
|
|
176
175
|
items: [
|
|
177
176
|
{
|
|
178
|
-
title: t('header.products.fea.items.
|
|
179
|
-
subtitle: t('header.products.fea.items.
|
|
177
|
+
title: t('header.products.fea.items.product.t'),
|
|
178
|
+
subtitle: t('header.products.fea.items.product.st'),
|
|
180
179
|
icon: Connections,
|
|
181
|
-
to: buildLocalizedUrl(locale, urls.tnved, PAGES.fea.
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
title: t('header.products.fea.items.i.t'),
|
|
185
|
-
subtitle: t('header.products.fea.items.i.st'),
|
|
186
|
-
icon: IconHandshake,
|
|
187
|
-
to: buildLocalizedUrl(locale, urls.tnved, PAGES.fea.t),
|
|
180
|
+
to: buildLocalizedUrl(locale, urls.tnved, PAGES.fea.productSearch),
|
|
188
181
|
},
|
|
189
182
|
{
|
|
190
|
-
title: t('header.products.fea.items.
|
|
191
|
-
subtitle: t('header.products.fea.items.
|
|
192
|
-
icon:
|
|
193
|
-
to: buildLocalizedUrl(locale, urls.tnved, PAGES.fea.
|
|
194
|
-
|
|
183
|
+
title: t('header.products.fea.items.importer.t'),
|
|
184
|
+
subtitle: t('header.products.fea.items.importer.st'),
|
|
185
|
+
icon: MapPinRect,
|
|
186
|
+
to: buildLocalizedUrl(locale, urls.tnved, PAGES.fea.importer),
|
|
195
187
|
},
|
|
196
188
|
{
|
|
197
189
|
title: t('header.products.fea.items.ca.t'),
|
|
@@ -200,22 +192,16 @@ export function useHeaderNavigationLinks() {
|
|
|
200
192
|
to: buildLocalizedUrl(locale, urls.tnved, PAGES.fea.ca),
|
|
201
193
|
},
|
|
202
194
|
{
|
|
203
|
-
title: t('header.products.fea.items.
|
|
204
|
-
subtitle: t('header.products.fea.items.
|
|
205
|
-
icon:
|
|
206
|
-
to: buildLocalizedUrl(locale, urls.tnved, PAGES.fea.
|
|
195
|
+
title: t('header.products.fea.items.cp.t'),
|
|
196
|
+
subtitle: t('header.products.fea.items.cp.st'),
|
|
197
|
+
icon: IconTruck,
|
|
198
|
+
to: buildLocalizedUrl(locale, urls.tnved, PAGES.fea.customs),
|
|
207
199
|
},
|
|
208
200
|
{
|
|
209
|
-
title: t('header.products.fea.items.
|
|
210
|
-
subtitle: t('header.products.fea.items.
|
|
201
|
+
title: t('header.products.fea.items.container.t'),
|
|
202
|
+
subtitle: t('header.products.fea.items.container.st'),
|
|
211
203
|
icon: MapPaper,
|
|
212
|
-
to: buildLocalizedUrl(locale, urls.tnved, PAGES.fea.
|
|
213
|
-
},
|
|
214
|
-
{
|
|
215
|
-
title: t('header.products.fea.items.importerMap.t'),
|
|
216
|
-
subtitle: t('header.products.fea.items.importerMap.st'),
|
|
217
|
-
icon: MapPinRect,
|
|
218
|
-
to: buildLocalizedUrl(locale, urls.tnved, PAGES.fea.importerMap),
|
|
204
|
+
to: buildLocalizedUrl(locale, urls.tnved, PAGES.fea.container),
|
|
219
205
|
},
|
|
220
206
|
],
|
|
221
207
|
},
|
package/lang/en.ts
CHANGED
|
@@ -28,6 +28,12 @@ const EnLocale: RuLocale = {
|
|
|
28
28
|
requestLimit: 'Daily request limit',
|
|
29
29
|
daysLeft: 'Days left',
|
|
30
30
|
},
|
|
31
|
+
shell: {
|
|
32
|
+
collapse: 'Collapse menu',
|
|
33
|
+
expand: 'Expand menu',
|
|
34
|
+
pin: 'Pin menu',
|
|
35
|
+
home: 'Home',
|
|
36
|
+
},
|
|
31
37
|
usage: {
|
|
32
38
|
requests: 'Requests',
|
|
33
39
|
days: 'days',
|
|
@@ -212,13 +218,13 @@ const EnLocale: RuLocale = {
|
|
|
212
218
|
fea: {
|
|
213
219
|
label: 'Foreign Economic Activity',
|
|
214
220
|
items: {
|
|
215
|
-
|
|
216
|
-
t: '
|
|
217
|
-
st: '
|
|
221
|
+
product: {
|
|
222
|
+
t: 'Product analysis',
|
|
223
|
+
st: '',
|
|
218
224
|
},
|
|
219
|
-
|
|
220
|
-
t: '
|
|
221
|
-
st: '
|
|
225
|
+
importer: {
|
|
226
|
+
t: 'Importer analysis',
|
|
227
|
+
st: '',
|
|
222
228
|
},
|
|
223
229
|
cp: {
|
|
224
230
|
t: 'Customs-related sphere',
|
|
@@ -228,16 +234,8 @@ const EnLocale: RuLocale = {
|
|
|
228
234
|
t: 'Calculator',
|
|
229
235
|
st: 'Calculation of Customs Payments. Customs Duties. Customs Fees',
|
|
230
236
|
},
|
|
231
|
-
|
|
232
|
-
t: '
|
|
233
|
-
st: 'Customs Tariffs and Quotas. Measures for Protecting the Domestic Market. Regulatory Requirements',
|
|
234
|
-
},
|
|
235
|
-
importerMap: {
|
|
236
|
-
t: 'Map of importers',
|
|
237
|
-
st: '',
|
|
238
|
-
},
|
|
239
|
-
containers: {
|
|
240
|
-
t: 'Containers',
|
|
237
|
+
container: {
|
|
238
|
+
t: 'Container tracking',
|
|
241
239
|
st: '',
|
|
242
240
|
},
|
|
243
241
|
},
|
package/lang/kk.ts
CHANGED
|
@@ -28,6 +28,12 @@ const KkLocale: RuLocale = {
|
|
|
28
28
|
requestLimit: 'Күнделікті сұраныс лимиті',
|
|
29
29
|
daysLeft: 'Қалған күндер',
|
|
30
30
|
},
|
|
31
|
+
shell: {
|
|
32
|
+
collapse: 'Мәзірді жию',
|
|
33
|
+
expand: 'Мәзірді жаю',
|
|
34
|
+
pin: 'Мәзірді бекіту',
|
|
35
|
+
home: 'Басты бетке',
|
|
36
|
+
},
|
|
31
37
|
usage: {
|
|
32
38
|
requests: 'Сұраныстар',
|
|
33
39
|
days: 'күн',
|
|
@@ -212,13 +218,13 @@ const KkLocale: RuLocale = {
|
|
|
212
218
|
fea: {
|
|
213
219
|
label: 'СЭҚ',
|
|
214
220
|
items: {
|
|
215
|
-
|
|
216
|
-
t: '
|
|
217
|
-
st: '
|
|
221
|
+
product: {
|
|
222
|
+
t: 'Тауарды талдау',
|
|
223
|
+
st: '',
|
|
218
224
|
},
|
|
219
|
-
|
|
220
|
-
t: '
|
|
221
|
-
st: '
|
|
225
|
+
importer: {
|
|
226
|
+
t: 'Импорттаушыны талдау',
|
|
227
|
+
st: '',
|
|
222
228
|
},
|
|
223
229
|
cp: {
|
|
224
230
|
t: 'Кеденге қатысты сала',
|
|
@@ -228,16 +234,8 @@ const KkLocale: RuLocale = {
|
|
|
228
234
|
t: 'Калькулятор',
|
|
229
235
|
st: 'Кедендік төлемдерді есептеу. Кедендік баждар. Кедендік алымдар',
|
|
230
236
|
},
|
|
231
|
-
|
|
232
|
-
t: '
|
|
233
|
-
st: 'Кедендік тарифтер мен квоталар. Ішкі нарықты қорғау шаралары. Нормативтік талаптар',
|
|
234
|
-
},
|
|
235
|
-
importerMap: {
|
|
236
|
-
t: 'Импорттаушының картасы',
|
|
237
|
-
st: '',
|
|
238
|
-
},
|
|
239
|
-
containers: {
|
|
240
|
-
t: 'Контейнерлер',
|
|
237
|
+
container: {
|
|
238
|
+
t: 'Контейнерді қадағалау',
|
|
241
239
|
st: '',
|
|
242
240
|
},
|
|
243
241
|
},
|
package/lang/ru.ts
CHANGED
|
@@ -24,6 +24,12 @@ const RuLocale = {
|
|
|
24
24
|
requestLimit: 'Cуточный лимит запросов',
|
|
25
25
|
daysLeft: 'Остаток дней',
|
|
26
26
|
},
|
|
27
|
+
shell: {
|
|
28
|
+
collapse: 'Свернуть меню',
|
|
29
|
+
expand: 'Развернуть меню',
|
|
30
|
+
pin: 'Закрепить меню',
|
|
31
|
+
home: 'На главную',
|
|
32
|
+
},
|
|
27
33
|
usage: {
|
|
28
34
|
requests: 'Запросы',
|
|
29
35
|
days: 'дн.',
|
|
@@ -213,13 +219,13 @@ const RuLocale = {
|
|
|
213
219
|
fea: {
|
|
214
220
|
label: 'ВЭД',
|
|
215
221
|
items: {
|
|
216
|
-
|
|
217
|
-
t: '
|
|
218
|
-
st: '
|
|
222
|
+
product: {
|
|
223
|
+
t: 'Анализ товара',
|
|
224
|
+
st: '',
|
|
219
225
|
},
|
|
220
|
-
|
|
221
|
-
t: '
|
|
222
|
-
st: '
|
|
226
|
+
importer: {
|
|
227
|
+
t: 'Анализ импортера',
|
|
228
|
+
st: '',
|
|
223
229
|
},
|
|
224
230
|
cp: {
|
|
225
231
|
t: 'Околотаможенная сфера',
|
|
@@ -229,16 +235,8 @@ const RuLocale = {
|
|
|
229
235
|
t: 'Калькулятор',
|
|
230
236
|
st: 'Расчет таможенных платежей. Таможенные пошлины. Таможенные сборы',
|
|
231
237
|
},
|
|
232
|
-
|
|
233
|
-
t: '
|
|
234
|
-
st: 'Таможенные тарифы и квоты. Меры защиты внутреннего рынка. Нормативные требования',
|
|
235
|
-
},
|
|
236
|
-
importerMap: {
|
|
237
|
-
t: 'Карта импортёра',
|
|
238
|
-
st: '',
|
|
239
|
-
},
|
|
240
|
-
containers: {
|
|
241
|
-
t: 'Контейнеры',
|
|
238
|
+
container: {
|
|
239
|
+
t: 'Отслеживание контейнера',
|
|
242
240
|
st: '',
|
|
243
241
|
},
|
|
244
242
|
},
|
package/package.json
CHANGED
package/shared/constans/pages.ts
CHANGED
|
@@ -21,7 +21,7 @@ export const PAGES = {
|
|
|
21
21
|
reports: '/profile/reports',
|
|
22
22
|
browsingHistory: '/profile/view-history',
|
|
23
23
|
myGroups: '/profile/my-groups',
|
|
24
|
-
notes: '/profile/notes'
|
|
24
|
+
notes: '/profile/notes',
|
|
25
25
|
},
|
|
26
26
|
pk: {
|
|
27
27
|
main: '/',
|
|
@@ -32,12 +32,12 @@ export const PAGES = {
|
|
|
32
32
|
sanctions: '/sanctions',
|
|
33
33
|
employees: '/employees-check',
|
|
34
34
|
counterparty: '/counterparty',
|
|
35
|
-
unload: '/unload'
|
|
35
|
+
unload: '/unload',
|
|
36
36
|
},
|
|
37
37
|
work: {
|
|
38
38
|
main: '/',
|
|
39
39
|
summary: '/summary',
|
|
40
|
-
vacancy: '/vacancy'
|
|
40
|
+
vacancy: '/vacancy',
|
|
41
41
|
},
|
|
42
42
|
tender: {
|
|
43
43
|
customers: '/main/analytics/customers',
|
|
@@ -49,12 +49,13 @@ export const PAGES = {
|
|
|
49
49
|
akimat: '/main/eoz',
|
|
50
50
|
main: '/',
|
|
51
51
|
contracts: '/main/contracts',
|
|
52
|
-
plans: '/main/plans'
|
|
52
|
+
plans: '/main/plans',
|
|
53
53
|
},
|
|
54
54
|
fines: {
|
|
55
|
-
main: '
|
|
55
|
+
main: '/',
|
|
56
56
|
avto: '/check-car',
|
|
57
|
-
|
|
57
|
+
fines: '/fines',
|
|
58
|
+
bulk: '/mass-check',
|
|
58
59
|
},
|
|
59
60
|
analytics: {
|
|
60
61
|
main: '/',
|
|
@@ -67,13 +68,11 @@ export const PAGES = {
|
|
|
67
68
|
},
|
|
68
69
|
fea: {
|
|
69
70
|
main: '/',
|
|
70
|
-
t: '/fea',
|
|
71
|
-
cp: '/custom',
|
|
72
|
-
o: '/tree',
|
|
73
71
|
ca: '/calculator',
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
productSearch: '/product/search',
|
|
73
|
+
importer: '/importer',
|
|
74
|
+
customs: '/customs',
|
|
75
|
+
container: '/container',
|
|
77
76
|
},
|
|
78
77
|
compliance: {
|
|
79
78
|
l: '/compliance',
|
|
@@ -83,7 +82,7 @@ export const PAGES = {
|
|
|
83
82
|
hotline: '/hotline',
|
|
84
83
|
sanctions: '/sanctions',
|
|
85
84
|
suppliers: '/dossier/suppliers',
|
|
86
|
-
sb: '/sb'
|
|
85
|
+
sb: '/sb',
|
|
87
86
|
},
|
|
88
87
|
edo: {
|
|
89
88
|
l: '/',
|
|
@@ -92,5 +91,5 @@ export const PAGES = {
|
|
|
92
91
|
docRisk: '/create?step=2&source=import&postImportAction=risks',
|
|
93
92
|
docCompare: '/create?step=2&source=import',
|
|
94
93
|
docAi: '/create?step=3&source=blank',
|
|
95
|
-
}
|
|
94
|
+
},
|
|
96
95
|
}
|