adata-ui 2.1.0 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (307) hide show
  1. package/.editorconfig +12 -12
  2. package/.nuxtrc +1 -1
  3. package/.playground/app.config.ts +5 -5
  4. package/README.md +75 -75
  5. package/app.config.ts +4 -3
  6. package/app.vue +1 -0
  7. package/assets/styles/index.scss +104 -0
  8. package/assets/styles/modules/_base.scss +5 -0
  9. package/assets/styles/modules/_typography.scss +152 -0
  10. package/components/elements/README.md +1 -1
  11. package/components/elements/accordion/AAccordion.vue +13 -12
  12. package/components/elements/curve-block/ACurveBlock.vue +94 -19
  13. package/components/elements/feature-description/AFeatureDescription.vue +4 -4
  14. package/components/elements/illustrations/noAccess.vue +11 -2
  15. package/components/elements/photos-animation/APhotosAnimation.vue +3 -1
  16. package/components/elements/select/ASelect.vue +65 -9
  17. package/components/features/lang-switcher/lang-switcher.vue +8 -1
  18. package/components/features/payment/banner/BasicPlusLimitBanner.vue +104 -0
  19. package/components/features/payment/banner/PaymentBanner.vue +108 -0
  20. package/components/features/payment/process/PaymentKaspiQrSidePanel.vue +158 -0
  21. package/components/features/payment/process/PaymentKaspiRedirectSidePanel.vue +112 -0
  22. package/components/features/payment/process/PaymentMethodSidePanel.vue +121 -0
  23. package/components/features/payment/process/PaymentProcess.vue +138 -0
  24. package/components/features/payment/process/PaymentTopUpSidePanel.vue +130 -0
  25. package/components/features/pk-mobile-services/APkMobileServices.vue +21 -2
  26. package/components/forms/README.md +1 -1
  27. package/components/forms/checkbox/ACheckbox.vue +0 -2
  28. package/components/forms/radio-button/ARadioButton.vue +21 -13
  29. package/components/forms/request-demo/ARequestDemo.vue +21 -7
  30. package/components/modals/AConfirmationEmail.vue +40 -0
  31. package/components/modals/AnotherDeviceModal.vue +3 -2
  32. package/components/modals/ConnectingTariffModal.vue +0 -1
  33. package/components/modals/ContentNavigationModal.vue +148 -75
  34. package/components/modals/Insufficient-funds-modal.vue +5 -2
  35. package/components/modals/LimitReachedModal.vue +4 -3
  36. package/components/modals/ReportBugModal.vue +3 -3
  37. package/components/modals/Resend.vue +82 -0
  38. package/components/modals/SubmitApplicationModal.vue +4 -0
  39. package/components/modals/id/AuthModal.vue +78 -0
  40. package/components/modals/id/IdAutoLogoutModal.vue +45 -0
  41. package/components/modals/id/IdBanner.vue +58 -0
  42. package/components/modals/id/IdConfirmAccountOtpModal.vue +186 -0
  43. package/components/modals/id/IdConfirmSuccessfulModal.vue +41 -0
  44. package/components/modals/id/IdLoginModal.vue +316 -0
  45. package/components/modals/id/IdModals.vue +114 -0
  46. package/components/modals/id/IdNewPasswordModal.vue +129 -0
  47. package/components/modals/id/IdOtpInput.vue +155 -0
  48. package/components/modals/id/IdPasswordSuccessfulModal.vue +25 -0
  49. package/components/modals/id/IdRecoveryModal.vue +117 -0
  50. package/components/modals/id/IdRegistrationModal.vue +215 -0
  51. package/components/modals/id/IdResetPasswordOtpModal.vue +158 -0
  52. package/components/modals/id/IdTwoFactorModal.vue +130 -0
  53. package/components/navigation/README.md +1 -1
  54. package/components/navigation/bottom-navigation/ABottomNavigation.vue +34 -29
  55. package/components/navigation/footer/AFooter.vue +210 -57
  56. package/components/navigation/footer/ui/{new-footer-accordion.vue → a-footer-accordion.vue} +2 -4
  57. package/components/navigation/header/AHeader.vue +59 -51
  58. package/components/navigation/header/AlmatyContacts.vue +16 -14
  59. package/components/navigation/header/AstanaContacts.vue +13 -11
  60. package/components/navigation/header/CardGallery.vue +5 -4
  61. package/components/navigation/header/HeaderLink.vue +16 -8
  62. package/components/navigation/header/NavList.vue +21 -5
  63. package/components/navigation/header/ProductMenu.vue +8 -78
  64. package/components/navigation/header/ProfileMenu.vue +10 -5
  65. package/components/navigation/header/TopHeader.vue +2 -2
  66. package/components/navigation/mobile-navigation/AMobileNavigation.vue +3 -3
  67. package/components/navigation/pill-tabs/APillTabs.vue +18 -4
  68. package/components/navigation/pill-tabs/types.ts +1 -0
  69. package/components/navigation/side-navigation/ASideNavigation.vue +23 -21
  70. package/components/overlays/README.md +1 -1
  71. package/components/overlays/side-panel/ASidePanel.vue +439 -0
  72. package/components/overlays/tooltip/ATooltip.vue +149 -154
  73. package/components/utils/removeTrailingSlash.ts +7 -0
  74. package/composables/useBuyTariffs.ts +91 -0
  75. package/composables/useHeaderNavigationLinks.ts +174 -297
  76. package/composables/useIdModals.ts +36 -0
  77. package/composables/usePayment.ts +74 -0
  78. package/composables/useUrls.ts +21 -0
  79. package/eslint.config.mjs +45 -0
  80. package/icons/adata-logo.vue +1 -1
  81. package/icons/ai-assistant.vue +13 -0
  82. package/icons/akimat.vue +20 -0
  83. package/icons/arrow/arrow-bottom-left-on-square.vue +1 -1
  84. package/icons/arrow/arrow-circle-reset.vue +1 -1
  85. package/icons/arrow/arrow-corner.vue +1 -1
  86. package/icons/arrow/arrow-graph-down.vue +1 -1
  87. package/icons/arrow/arrow-graph-up.vue +1 -1
  88. package/icons/arrow/arrow-square-down.vue +1 -1
  89. package/icons/arrow/arrow-top-right-on-square.vue +1 -1
  90. package/icons/arrow-currency-gray.vue +1 -1
  91. package/icons/arrow-currency-green.vue +1 -1
  92. package/icons/arrow-currency-red.vue +1 -1
  93. package/icons/avatar.vue +1 -1
  94. package/icons/bank.vue +5 -0
  95. package/icons/block.vue +1 -1
  96. package/icons/bookmark/bookmark-filled.vue +1 -1
  97. package/icons/bookmark/bookmark.vue +1 -1
  98. package/icons/browsers/browser-duck.vue +1 -1
  99. package/icons/browsers/browser-google.vue +7 -1
  100. package/icons/browsers/browser-yandex.vue +1 -1
  101. package/icons/building-vector.vue +1 -1
  102. package/icons/calculator.vue +1 -1
  103. package/icons/calendar.vue +1 -1
  104. package/icons/car.vue +1 -1
  105. package/icons/chart-bar.vue +5 -0
  106. package/icons/chart-pie.vue +16 -0
  107. package/icons/check/check-circle.vue +1 -1
  108. package/icons/check/check.vue +1 -1
  109. package/icons/check/checkmark-circle.vue +1 -1
  110. package/icons/check-sb.vue +7 -0
  111. package/icons/checkbox/checkbox-active.vue +1 -1
  112. package/icons/checkbox/checkbox-empty.vue +1 -1
  113. package/icons/checkbox/checkbox-intermediate.vue +1 -1
  114. package/icons/chevron/chevron-down.vue +1 -1
  115. package/icons/chevron/chevron-left.vue +1 -1
  116. package/icons/chevron/chevron-right.vue +1 -1
  117. package/icons/chevron/chevron-up.vue +1 -1
  118. package/icons/chevron/double-chevron-right.vue +1 -1
  119. package/icons/clipboard-text.vue +1 -1
  120. package/icons/clock.vue +1 -1
  121. package/icons/company/company-bazis.vue +3 -3
  122. package/icons/company/company-bereke.vue +13 -13
  123. package/icons/company/company-bigroup.vue +5 -5
  124. package/icons/company/company-erg.vue +6 -6
  125. package/icons/company/company-forte.vue +11 -11
  126. package/icons/company/company-halyk.vue +4 -4
  127. package/icons/company/company-jusan.vue +20 -3
  128. package/icons/company/company-kaspi.vue +3 -3
  129. package/icons/company/company-mycar.vue +2 -2
  130. package/icons/company/company-samruk.vue +9 -9
  131. package/icons/company-egov-small.vue +1 -1
  132. package/icons/company.vue +1 -1
  133. package/icons/copy.vue +1 -1
  134. package/icons/currency/currency-dollar.vue +1 -1
  135. package/icons/currency/currency-down.vue +1 -1
  136. package/icons/currency/currency-eur.vue +1 -1
  137. package/icons/currency/currency-rub.vue +1 -1
  138. package/icons/currency/currency-tenge.vue +9 -0
  139. package/icons/currency/currency-usd.vue +1 -1
  140. package/icons/currency/currency-yuan.vue +1 -1
  141. package/icons/delete.vue +1 -1
  142. package/icons/document.vue +1 -1
  143. package/icons/download.vue +1 -1
  144. package/icons/edit.vue +1 -1
  145. package/icons/education.vue +1 -1
  146. package/icons/egov-small.vue +1 -1
  147. package/icons/excel-icon.vue +14 -0
  148. package/icons/expand-window.vue +1 -1
  149. package/icons/eye-closed.vue +1 -1
  150. package/icons/eye-open.vue +1 -1
  151. package/icons/eye-opened.vue +1 -1
  152. package/icons/file/file.vue +1 -1
  153. package/icons/filter.vue +1 -1
  154. package/icons/flag.vue +1 -1
  155. package/icons/gift.vue +1 -1
  156. package/icons/globe.vue +1 -1
  157. package/icons/google.vue +41 -0
  158. package/icons/hand/hand-thumb-up-filled.vue +1 -1
  159. package/icons/hand/hand-thumb-up.vue +1 -1
  160. package/icons/hand-with-phone/hand-with-phone-dark.vue +1 -1
  161. package/icons/hand-with-phone/hand-with-phone-light.vue +1 -1
  162. package/icons/handshake.vue +1 -1
  163. package/icons/hcheck.vue +1 -1
  164. package/icons/hdocument.vue +1 -1
  165. package/icons/history.vue +1 -1
  166. package/icons/horizontal-more.vue +1 -1
  167. package/icons/hot-line.vue +6 -0
  168. package/icons/hummer.vue +1 -1
  169. package/icons/info/info-circle.vue +1 -1
  170. package/icons/invoice.vue +1 -1
  171. package/icons/kaspi-qr.vue +13 -0
  172. package/icons/link-chain.vue +1 -1
  173. package/icons/link.vue +1 -1
  174. package/icons/linkedin.vue +24 -24
  175. package/icons/loader-circle.vue +1 -1
  176. package/icons/location.vue +1 -1
  177. package/icons/lock.vue +1 -1
  178. package/icons/logo.vue +1 -1
  179. package/icons/logout.vue +1 -1
  180. package/icons/magnify/magnifying-glass-minus.vue +1 -1
  181. package/icons/magnify/magnifying-glass-plus.vue +1 -1
  182. package/icons/magnify/magnifying-glass.vue +1 -1
  183. package/icons/mail.vue +1 -1
  184. package/icons/mailru.vue +34 -0
  185. package/icons/main-filter.vue +1 -1
  186. package/icons/map/map-pin-filled.vue +1 -1
  187. package/icons/map/map-pin-rect.vue +1 -1
  188. package/icons/map/map-pin.vue +1 -1
  189. package/icons/map-marker-start.vue +1 -1
  190. package/icons/medal.vue +1 -1
  191. package/icons/menu-filled.vue +1 -1
  192. package/icons/menu.vue +1 -1
  193. package/icons/message/message.vue +1 -1
  194. package/icons/minus/minus-circle.vue +1 -1
  195. package/icons/money.vue +1 -1
  196. package/icons/monitoring.vue +1 -1
  197. package/icons/moon.vue +1 -1
  198. package/icons/more.vue +1 -1
  199. package/icons/notification.vue +1 -1
  200. package/icons/paperclip.vue +1 -1
  201. package/icons/payment/payment-card.vue +1 -1
  202. package/icons/payment/payment-kaspi.vue +1 -1
  203. package/icons/person-vector.vue +1 -1
  204. package/icons/person-with-briefcase.vue +1 -1
  205. package/icons/phone-filled.vue +1 -1
  206. package/icons/phone.vue +1 -1
  207. package/icons/plus/plus-circle.vue +1 -1
  208. package/icons/plus/plus.vue +1 -1
  209. package/icons/profile.vue +1 -1
  210. package/icons/radio/radio-check.vue +1 -1
  211. package/icons/radio/radio-empty.vue +1 -1
  212. package/icons/receipt.vue +1 -1
  213. package/icons/sanctions.vue +8 -0
  214. package/icons/scales/scale.vue +1 -1
  215. package/icons/scales/scales.vue +1 -1
  216. package/icons/scales/standing-scales.vue +1 -1
  217. package/icons/search.vue +1 -1
  218. package/icons/share/share-alt.vue +1 -1
  219. package/icons/share/share.vue +1 -1
  220. package/icons/socials/face-book.vue +1 -1
  221. package/icons/socials/instagram.vue +1 -1
  222. package/icons/socials/telegram.vue +1 -1
  223. package/icons/socials/tik-tok.vue +1 -1
  224. package/icons/socials/youtube.vue +1 -1
  225. package/icons/sort/sort-asc.vue +1 -1
  226. package/icons/sort/sort-desc.vue +1 -1
  227. package/icons/splitting-arrows.vue +1 -1
  228. package/icons/star/star-filled.vue +1 -1
  229. package/icons/star/star-half-filled.vue +1 -1
  230. package/icons/star/star.vue +1 -1
  231. package/icons/sun.vue +14 -14
  232. package/icons/sviazi.vue +1 -1
  233. package/icons/tag.vue +1 -1
  234. package/icons/tasks.vue +10 -0
  235. package/icons/tender-search.vue +1 -1
  236. package/icons/toasts/check-circle-toast.vue +1 -1
  237. package/icons/toasts/warning-triangle-toast.vue +1 -1
  238. package/icons/trash.vue +1 -1
  239. package/icons/triangle.vue +1 -1
  240. package/icons/truck.vue +1 -1
  241. package/icons/user.vue +1 -1
  242. package/icons/users-focus.vue +1 -1
  243. package/icons/users.vue +1 -1
  244. package/icons/warning/warning-circle.vue +1 -1
  245. package/icons/warning/warning-triangle-filled.vue +1 -1
  246. package/icons/warning/warning-triangle.vue +1 -1
  247. package/icons/whatsapp.vue +1 -1
  248. package/icons/work-bag.vue +1 -1
  249. package/icons/work-case.vue +9 -0
  250. package/icons/work-search.vue +1 -1
  251. package/icons/work.vue +1 -1
  252. package/icons/x-mark.vue +1 -1
  253. package/icons/yandex.vue +28 -0
  254. package/illustrations/address-location.vue +1 -1
  255. package/illustrations/ball-with-chain.vue +1 -1
  256. package/illustrations/bill.vue +1 -1
  257. package/illustrations/buildings.vue +1 -1
  258. package/illustrations/calendar.vue +1 -1
  259. package/illustrations/chains.vue +1 -1
  260. package/illustrations/coin-percent.vue +1 -1
  261. package/illustrations/coins-stack.vue +1 -1
  262. package/illustrations/delete-dark.vue +1 -1
  263. package/illustrations/delete.vue +1 -1
  264. package/illustrations/doc-with-stamp.vue +1 -1
  265. package/illustrations/document.vue +1 -1
  266. package/illustrations/door.vue +1 -1
  267. package/illustrations/empty-box.vue +1 -1
  268. package/illustrations/empty-wallet.vue +1 -1
  269. package/illustrations/graph-in-coin.vue +1 -1
  270. package/illustrations/hammer.vue +1 -1
  271. package/illustrations/hand-cash.vue +1 -1
  272. package/illustrations/info.vue +1 -1
  273. package/illustrations/mail.vue +1 -1
  274. package/illustrations/ok.vue +1 -1
  275. package/illustrations/people-group.vue +1 -1
  276. package/illustrations/person-with-phone.vue +1 -1
  277. package/illustrations/person.vue +1 -1
  278. package/illustrations/phone-check.vue +1 -1
  279. package/illustrations/phone-payment-method.vue +1 -1
  280. package/illustrations/stop-hand.vue +1 -1
  281. package/illustrations/stop-sign.vue +1 -1
  282. package/illustrations/suit.vue +1 -1
  283. package/illustrations/suitcase.vue +1 -1
  284. package/illustrations/terminal-dark.vue +1 -1
  285. package/illustrations/terminal.vue +1 -1
  286. package/illustrations/trash-can.vue +1 -1
  287. package/illustrations/turn-on-tariff.vue +1 -1
  288. package/illustrations/two-persons.vue +1 -1
  289. package/lang/en.ts +475 -270
  290. package/lang/kk.ts +476 -271
  291. package/lang/ru.ts +315 -107
  292. package/layouts/default.vue +13 -13
  293. package/nuxt.config.ts +42 -14
  294. package/package.json +69 -53
  295. package/public/kaspi/logo.svg +4 -0
  296. package/shared/constans/pages.ts +17 -2
  297. package/tailwind.config.ts +163 -0
  298. package/vitest.config.ts +14 -0
  299. package/.eslintrc.cjs +0 -4
  300. package/.playground/app.vue +0 -9
  301. package/.playground/pages/index.vue +0 -13
  302. package/.prettierignore +0 -24
  303. package/.prettierrc +0 -10
  304. package/assets/styles/index.css +0 -226
  305. package/components/modals/AuthModal.vue +0 -50
  306. package/components/navigation/footer/NewFooter.vue +0 -276
  307. package/components/navigation/footer/ui/footer-acccordion.vue +0 -119
@@ -68,12 +68,12 @@ onUnmounted(() => clearTimer())
68
68
  <component
69
69
  :is="item.icon"
70
70
  class="shrink-0 size-6 transition-colors duration-300 ease-in-out"
71
- :class="selected === idx ? 'text-white dark:text-gray-900' : 'text-gray-600 dark:text-gray-200'"
71
+ :class="selected === idx ? 'text-white dark:text-gray-900' : 'text-gray-600 dark:text-[#9DA3AC]'"
72
72
  />
73
73
  </div>
74
74
  <p
75
- class="font-semibold text-xl transition-colors duration-300 ease-in-out"
76
- :class="selected === idx ? 'dark:text-[#E3E5E8]' : 'text-gray-600 dark:text-gray-200'"
75
+ class="font-semibold text-base lg:text-xl transition-colors duration-300 ease-in-out"
76
+ :class="selected === idx ? 'dark:text-[#E3E5E8]' : 'text-gray-600 dark:text-[#9DA3AC]'"
77
77
  >
78
78
  {{ item.title }}
79
79
  </p>
@@ -88,7 +88,7 @@ onUnmounted(() => clearTimer())
88
88
  <div
89
89
  v-if="selected === idx"
90
90
  >
91
- <p class="text-sm pt-3">
91
+ <p class="text-xs lg:text-sm pt-3">
92
92
  {{ item.description }}
93
93
  </p>
94
94
  </div>
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { type LocalizedNavigationParams, navigateToLocalizedPage } from '#adata-ui/utils/localizedNavigation'
3
+ import { useUrls } from '#adata-ui/composables/useUrls'
3
4
 
4
5
  interface Props {
5
6
  illClass?: string
@@ -14,11 +15,19 @@ interface Props {
14
15
  const props = defineProps<Props>()
15
16
 
16
17
  const appConfig = useAppConfig()
17
- const loginUrl = appConfig.myLayer.loginUrl
18
+ const { login } = useUrls()
19
+ const authMode = appConfig.myLayer.authMode
18
20
  const device = useAdaptive()
19
21
  const { t, locale } = useI18n()
20
22
 
23
+ const { loginModal } = useIdModals()
24
+
21
25
  function goAuth() {
26
+ if (authMode === 'local') {
27
+ loginModal.value = true
28
+ return
29
+ }
30
+
22
31
  if (window) {
23
32
  if (props.to) {
24
33
  navigateToLocalizedPage(props.to)
@@ -26,7 +35,7 @@ function goAuth() {
26
35
  const fullPath = encodeURIComponent(window.location.toString())
27
36
  navigateToLocalizedPage({
28
37
  locale,
29
- projectUrl: loginUrl.slice(0, -1),
38
+ projectUrl: login,
30
39
  path: `/?url=${fullPath}`,
31
40
  target: '_self'
32
41
  })
@@ -17,10 +17,12 @@ const getAnimationClass = (index: number) => {
17
17
 
18
18
  <template>
19
19
  <div class="relative size-full">
20
- <nuxt-img
20
+ <img
21
21
  v-for="(photo, index) in photos"
22
22
  :key="index"
23
23
  :src="photo.src"
24
+ :alt="photo.src"
25
+ loading="lazy"
24
26
  :class="['photo', getAnimationClass(index), photo.classes]"
25
27
  />
26
28
  </div>
@@ -6,10 +6,13 @@ import { onClickOutside, useToggle } from '@vueuse/core'
6
6
  import { isEqual } from 'lodash-es'
7
7
  import type { Ref } from 'vue'
8
8
 
9
+ import { useFloating, offset, flip, shift, size as floatingSize, autoUpdate } from '@floating-ui/vue'
10
+
9
11
  type Props = {
10
12
  label: string
11
13
  required?: boolean
12
14
  options: T[]
15
+ hideOverflow?: boolean
13
16
  keyToShow?: string
14
17
  getOnlyKey?: boolean
15
18
  keyToSelect?: string
@@ -39,6 +42,7 @@ type Emits = {
39
42
 
40
43
  const props = withDefaults(defineProps<Props>(), {
41
44
  required: false,
45
+ hideOverflow: false,
42
46
  getOnlyKey: false,
43
47
  keyToShow: 'name',
44
48
  keyToSelect: 'id',
@@ -61,6 +65,32 @@ const props = withDefaults(defineProps<Props>(), {
61
65
 
62
66
  const emits = defineEmits<Emits>()
63
67
 
68
+
69
+
70
+ const reference = ref<HTMLElement | null>(null)
71
+ const floating = ref<HTMLElement | null>(null)
72
+ const { x, y, strategy, update } = useFloating(reference, floating, {
73
+ placement: 'bottom-start',
74
+ strategy: 'absolute',
75
+ middleware: [
76
+ offset(6),
77
+ flip({ fallbackPlacements: ['top-start'] }),
78
+ shift({ padding: 8 }),
79
+ floatingSize({
80
+ apply({ availableHeight, elements, rects }) {
81
+ const el = elements.floating as HTMLElement
82
+ el.style.width = `${rects.reference.width}px`
83
+ el.style.maxHeight = `${Math.max(140, Math.min(availableHeight, 320))}px`
84
+ el.style.overflowY = props.hideOverflow ? 'hidden' : 'auto'
85
+ },
86
+ }),
87
+ ],
88
+ })
89
+
90
+ let cleanup: (() => void) | null = null
91
+
92
+
93
+
64
94
  const modelValue = defineModel<T | T[] | null | any>({ required: true })
65
95
  const search = defineModel<T | null | any>('search')
66
96
  const isOpen = defineModel<T | null | any>('open', {
@@ -77,9 +107,14 @@ const disabledStyles =
77
107
  'disabled:pointer-events-none disabled:opacity-40 disabled:bg-deepblue-50 disabled:text-deepblue-200 disabled:dark:text-gray-500'
78
108
 
79
109
  const componentOptions = computed(() =>
80
- props.options.filter((item: T) =>
81
- item[props.searchKey as keyof T].toLowerCase().includes(searchValue.value.toLowerCase())
82
- )
110
+ props.options.filter((item: T) => {
111
+ if (Array.isArray(props.searchKey) && props.searchKey.length === 2) {
112
+ return item[props.searchKey[0] as keyof T].toLowerCase().includes(searchValue.value.toLowerCase()) ||
113
+ item[props.searchKey[1] as keyof T].toLowerCase().includes(searchValue.value.toLowerCase())
114
+ } else {
115
+ return item[props.searchKey as keyof T].toLowerCase().includes(searchValue.value.toLowerCase())
116
+ }
117
+ })
83
118
  )
84
119
 
85
120
  onClickOutside(wrapper, () => {
@@ -230,10 +265,21 @@ onUpdated(() => (countedVisibleItems.value = countOfVisibleItems.value))
230
265
  defineExpose({
231
266
  onClear
232
267
  })
268
+ watch(() => isOpen.value, (open) => {
269
+ if (open && reference.value && floating.value) {
270
+ cleanup = autoUpdate(reference.value, floating.value, update)
271
+ } else {
272
+ cleanup?.()
273
+ cleanup = null
274
+ }
275
+ })
276
+
277
+ onUnmounted(() => cleanup?.())
233
278
  </script>
234
279
  <template>
235
280
  <div ref="wrapper" class="relative w-full rounded-md text-sm">
236
281
  <button
282
+ ref="reference"
237
283
  :class="[
238
284
  clearable ? 'pr-16' : 'pr-9',
239
285
  {
@@ -245,7 +291,7 @@ defineExpose({
245
291
  buttonBg,
246
292
  ]"
247
293
  :disabled="disabled"
248
- class="hover:border-blue focus-visible:outline-blue relative flex h-8 w-full items-center gap-2 rounded-md border border-solid py-1.5 pl-4 focus-visible:shadow-[0_0_0_4px#1B64B3] dark:hover:border-blue-700"
294
+ class="hover:border-blue-700 focus-visible:outline-blue relative flex h-8 w-full items-center gap-2 rounded-md border border-solid py-1.5 pl-4 focus-visible:shadow-[0_0_0_4px#1B64B3] dark:hover:border-blue-500"
249
295
  @click="toggleOpen()"
250
296
  >
251
297
  <span
@@ -257,7 +303,7 @@ defineExpose({
257
303
  </span>
258
304
  <component :is="startIcon" v-if="startIcon" />
259
305
 
260
- <span :class="{ 'py-1': size === 'md' }" class="w-full text-start">
306
+ <span :class="{ 'py-1': size === 'md' }" class=" w-full text-start">
261
307
  <span v-if="valueToShow">
262
308
  <span v-if="!multiple" class="line-clamp-1 text-deepblue-900 dark:text-gray-200">
263
309
  <slot name="single-selected" :value="valueToShow">{{ valueToShow[keyToShow] }}</slot>
@@ -305,10 +351,16 @@ defineExpose({
305
351
  </span>
306
352
  </button>
307
353
  <transition>
308
- <div
309
- v-if="isOpen"
310
- class="absolute z-20 mt-1 w-full rounded-md bg-white shadow-[0_1px_8px_0_rgba(139,146,156,0.3)] dark:bg-gray-900"
311
- >
354
+ <div
355
+ v-if="isOpen"
356
+ ref="floating"
357
+ class="z-[10000] rounded-md bg-white shadow-[0_1px_8px_0_rgba(139,146,156,0.3)] dark:bg-gray-900"
358
+ :style="{
359
+ position: strategy,
360
+ left: x != null ? `${x}px` : '',
361
+ top: y != null ? `${y}px` : '',
362
+ }"
363
+ >
312
364
  <slot :options="options" name="options">
313
365
  <ul class="select m-2 max-h-[250px] overflow-y-auto overflow-x-hidden">
314
366
  <li v-if="searchFromOut" class="p-1">
@@ -385,5 +437,9 @@ defineExpose({
385
437
  &::-webkit-scrollbar-thumb {
386
438
  background: #888;
387
439
  }
440
+ button {
441
+ user-select: text !important;
442
+ cursor: pointer;
443
+ }
388
444
  }
389
445
  </style>
@@ -16,6 +16,12 @@ onClickOutside(langSwitcher,() => {
16
16
 
17
17
  const { t } = useI18n()
18
18
 
19
+ function onClick(loc: any) {
20
+ if ((loc?.code || loc) === locale.value) return
21
+
22
+ isOpen.value = false
23
+ window.location.assign(switchLocalePath(loc?.code || loc))
24
+ }
19
25
  </script>
20
26
 
21
27
  <template>
@@ -27,6 +33,7 @@ const { t } = useI18n()
27
33
  <button
28
34
  class="text-xs font-semibold py-1 px-2 bg-gray-100 dark:bg-gray-200/5 rounded"
29
35
  @click="isOpen = true"
36
+ aria-hidden="true"
30
37
  >
31
38
  {{ t(`lang.${locale}.short`) }}
32
39
  </button>
@@ -41,7 +48,7 @@ const { t } = useI18n()
41
48
  {'text-primary bg-deepblue-500/5': locale === loc.code}
42
49
  ]"
43
50
  :to="switchLocalePath(loc?.code || loc)"
44
- @click="isOpen=false"
51
+ @click.prevent="onClick(loc)"
45
52
  >
46
53
  {{ t(`lang.${loc?.code || loc}.long`) }}
47
54
  </nuxt-link>
@@ -0,0 +1,104 @@
1
+ <script setup lang="ts">
2
+ import { removeTrailingSlash } from '#adata-ui/components/utils/removeTrailingSlash'
3
+
4
+ const props = defineProps<{
5
+ balance: number
6
+ }>()
7
+
8
+ const { $toast } = useNuxtApp()
9
+ const { t, locale } = useI18n()
10
+ const { commonAuth } = useAppConfig()
11
+ const accessToken = useCookie('accessToken')
12
+
13
+ const { topUpSidePanel } = usePayment()
14
+
15
+ const userApiURL = commonAuth.userApiURL
16
+
17
+ const isLoading = ref(false)
18
+
19
+ async function updateUserRate() {
20
+ try {
21
+ isLoading.value = true
22
+ await $fetch(`${removeTrailingSlash(userApiURL)}/user/rate`, {
23
+ method: 'PUT',
24
+ credentials: 'include',
25
+ headers: {
26
+ Authorization: `Bearer ${accessToken.value}`,
27
+ lang: locale.value,
28
+ },
29
+ body: {
30
+ rate_code: 'basic_plus',
31
+ },
32
+ })
33
+ window.location.reload()
34
+ }
35
+ catch (error) {
36
+ $toast.error(error.data.message)
37
+ }
38
+ finally {
39
+ isLoading.value = false
40
+ }
41
+ }
42
+
43
+ function onUpdate() {
44
+ if (props.balance >= 1000) {
45
+ updateUserRate()
46
+ }
47
+ else {
48
+ topUpSidePanel.value = true
49
+ }
50
+ }
51
+ </script>
52
+
53
+ <template>
54
+ <div class="gradient flex justify-between rounded-lg p-4 ring-1 ring-inset ring-blue-700/80 dark:bg-gray-800">
55
+ <div>
56
+ <p class="mb-2 font-semibold">
57
+ {{ t('modals.id.banners.basicPlusLimit.title') }}
58
+ </p>
59
+ <i18n-t
60
+ keypath="modals.id.banners.basicPlusLimit.subtitle"
61
+ tag="p"
62
+ class="text-sm text-gray-600 dark:text-gray-200"
63
+ >
64
+ <template #topUp>
65
+ <button
66
+ class="font-semibold text-blue-700 dark:text-blue-500"
67
+ @click="topUpSidePanel = true"
68
+ >
69
+ {{ t('modals.id.banners.basicPlusLimit.topUp') }}
70
+ </button>
71
+ </template>
72
+ </i18n-t>
73
+ </div>
74
+
75
+ <a-button
76
+ size="md"
77
+ class="self-end"
78
+ @click="onUpdate"
79
+ :loading="isLoading"
80
+ >
81
+ {{ t('actions.update') }}
82
+ </a-button>
83
+ </div>
84
+ </template>
85
+
86
+ <style scoped>
87
+ .gradient {
88
+ background: linear-gradient(
89
+ 239.71deg,
90
+ #B3D5F9 -7.48%,
91
+ #E6F1FD 39.62%,
92
+ #CCE2FB 85.82%
93
+ );
94
+ }
95
+
96
+ .dark .gradient {
97
+ background: linear-gradient(
98
+ 239.71deg,
99
+ #17303F -7.48%,
100
+ #161617 39.62%,
101
+ #17303F 85.82%
102
+ );
103
+ }
104
+ </style>
@@ -0,0 +1,108 @@
1
+ <script setup lang="ts">
2
+ import { buildLocalizedUrl } from '#adata-ui/utils/localizedNavigation'
3
+ import {removeTrailingSlash} from "#adata-ui/components/utils/removeTrailingSlash";
4
+ import { useUrls } from '#adata-ui/composables/useUrls'
5
+
6
+ const props = defineProps<{
7
+ balance?: number
8
+ }>()
9
+
10
+ const { $toast } = useNuxtApp()
11
+
12
+ const { t, locale } = useI18n()
13
+ const accessToken = useCookie('accessToken')
14
+
15
+ const { topUpSidePanel } = usePayment()
16
+ const { commonAuth } = useAppConfig()
17
+
18
+ const { landing } = useUrls()
19
+ const userApiURL = commonAuth.userApiURL
20
+
21
+ const isLoading = ref(false)
22
+
23
+ async function updateUserRate() {
24
+ try {
25
+ isLoading.value = true
26
+ await $fetch(`${removeTrailingSlash(userApiURL)}/user/rate`, {
27
+ method: 'PUT',
28
+ credentials: 'include',
29
+ headers: {
30
+ Authorization: `Bearer ${accessToken.value}`,
31
+ lang: locale.value,
32
+ },
33
+ body: {
34
+ rate_code: 'basic_plus',
35
+ },
36
+ })
37
+ window.location.reload()
38
+ }
39
+ catch (error) {
40
+ $toast.error(error.data.message)
41
+ }
42
+ finally {
43
+ isLoading.value = false
44
+ }
45
+ }
46
+
47
+ function onBuy() {
48
+ if (props.balance && props.balance >= 1000) {
49
+ updateUserRate()
50
+ }
51
+ else {
52
+ topUpSidePanel.value = true
53
+ }
54
+ }
55
+
56
+ </script>
57
+
58
+ <template>
59
+ <div class="gradient flex justify-between rounded-lg p-4 ring-1 ring-inset ring-blue-700/80 dark:bg-gray-800">
60
+ <div>
61
+ <p class="mb-2 font-semibold">
62
+ {{ t('payment.banner.title') }}
63
+ </p>
64
+ <i18n-t
65
+ keypath="payment.banner.subtitle"
66
+ tag="p"
67
+ class="text-sm text-gray-600 dark:text-gray-200"
68
+ >
69
+ <template #tariffs>
70
+ <nuxt-link
71
+ class="font-semibold text-blue-700 dark:text-blue-500"
72
+ :to="buildLocalizedUrl(locale, landing, '/tariffs')"
73
+ >
74
+ {{ t('payment.banner.tariffs') }}
75
+ </nuxt-link>
76
+ </template>
77
+ </i18n-t>
78
+ </div>
79
+
80
+ <a-button
81
+ size="md"
82
+ class="self-end"
83
+ @click="onBuy"
84
+ >
85
+ {{ t('actions.buy') }}
86
+ </a-button>
87
+ </div>
88
+ </template>
89
+
90
+ <style scoped>
91
+ .gradient {
92
+ background: linear-gradient(
93
+ 239.71deg,
94
+ #B3D5F9 -7.48%,
95
+ #E6F1FD 39.62%,
96
+ #CCE2FB 85.82%
97
+ );
98
+ }
99
+
100
+ .dark .gradient {
101
+ background: linear-gradient(
102
+ 239.71deg,
103
+ #17303F -7.48%,
104
+ #161617 39.62%,
105
+ #17303F 85.82%
106
+ );
107
+ }
108
+ </style>
@@ -0,0 +1,158 @@
1
+ <script setup lang="ts">
2
+ import { toCanvas } from 'qrcode'
3
+ import { removeTrailingSlash } from '#adata-ui/components/utils/removeTrailingSlash'
4
+
5
+ const props = defineProps<{
6
+ requestUrl: string
7
+ requestId: number
8
+ }>()
9
+
10
+ const emit = defineEmits<{
11
+ (e: 'back', type: 'kaspi'): void
12
+ (e: 'updateUserRate'): void
13
+ }>()
14
+
15
+ const isOpen = defineModel({ default: false })
16
+
17
+ const { t, locale } = useI18n()
18
+ const { commonAuth } = useAppConfig()
19
+ const accessToken = useCookie('accessToken')
20
+ const colorMode = useColorMode()
21
+
22
+ const userApiURL = commonAuth.userApiURL
23
+
24
+ const paymentSuccess = ref(false)
25
+ let intervalId: NodeJS.Timeout | null = null
26
+
27
+ function onBack() {
28
+ emit('back', 'kaspi')
29
+ }
30
+
31
+ async function checkPayment() {
32
+ try {
33
+ const { data } = await $fetch(`${removeTrailingSlash(userApiURL)}/user/kaspi-balance/invoice-status`, {
34
+ method: 'GET',
35
+ credentials: 'include',
36
+ headers: {
37
+ Authorization: `Bearer ${accessToken.value}`,
38
+ lang: locale.value,
39
+ },
40
+ params: {
41
+ request_id: props.requestId,
42
+ initial: 0,
43
+ },
44
+ })
45
+
46
+ if (data.status === 'completed') {
47
+ paymentSuccess.value = true
48
+ }
49
+ }
50
+ catch (e) {
51
+ console.error(e)
52
+ }
53
+ }
54
+
55
+ watch(isOpen, async (value) => {
56
+ if (!value) {
57
+ if (intervalId) {
58
+ clearInterval(intervalId)
59
+ }
60
+ return
61
+ }
62
+
63
+ await nextTick()
64
+
65
+ const canvas = document.getElementById('kaspiQr') as HTMLCanvasElement | null
66
+ if (!canvas) return
67
+
68
+ toCanvas(
69
+ canvas,
70
+ props.requestUrl,
71
+ {
72
+ errorCorrectionLevel: 'H',
73
+ width: 300,
74
+ margin: 0,
75
+ color: {
76
+ dark: colorMode.value === 'dark' ? '#FFFFFF' : '#000000', // квадраты
77
+ light: colorMode.value === 'dark' ? '#000000' : '#FFFFFF' // фон
78
+ }
79
+ },
80
+ (error) => {
81
+
82
+ if (error) {
83
+ console.error(error)
84
+ return
85
+ }
86
+
87
+ const ctx = canvas.getContext('2d')
88
+ if (!ctx) return
89
+
90
+ const logo = new Image()
91
+ logo.src = '/kaspi/logo.svg'
92
+ logo.onload = () => {
93
+ const size = canvas.width * 0.2
94
+ const x = (canvas.width - size) / 2
95
+ const y = (canvas.height - size) / 2
96
+
97
+ ctx.fillStyle = colorMode.value === 'dark' ? '#000000' : '#fff'
98
+ ctx.fillRect(x - 6, y - 6, size + 12, size + 12)
99
+
100
+ ctx.drawImage(logo, x, y, size, size)
101
+ }
102
+ })
103
+
104
+ intervalId = setInterval(() => {
105
+ if (paymentSuccess.value && intervalId) {
106
+ clearInterval(intervalId)
107
+ emit('updateUserRate')
108
+ return
109
+ }
110
+ checkPayment()
111
+ }, 1000)
112
+ })
113
+
114
+ </script>
115
+
116
+ <template>
117
+ <a-side-panel v-model="isOpen" width="484px">
118
+ <template #header>
119
+ <div class="flex items-center gap-2">
120
+ <button @click="onBack">
121
+ <a-icon-chevron-left class="size-6" />
122
+ </button>
123
+ <p class="text-xl font-semibold">
124
+ {{ t('payment.kaspi.title') }}
125
+ </p>
126
+ </div>
127
+ </template>
128
+
129
+ <div class="flex flex-col gap-6">
130
+ <div class="flex flex-col items-center text-center text-black dark:text-white">
131
+ <p class="mb-4 font-semibold text-sm">
132
+ {{ t('payment.kaspi.description') }}
133
+ </p>
134
+
135
+ <div class="flex gap-3 items-center">
136
+ <a-icon-kaspi-qr class="size-16" />
137
+ <span class="font-bold text-[44px]">
138
+ {{ t('payment.kaspi.qr') }}
139
+ </span>
140
+ </div>
141
+
142
+ <p class="mt-5 font-bold text-2xl">{{ t('payment.kaspi.scan') }}</p>
143
+
144
+ <canvas class="mt-5" id="kaspiQr"></canvas>
145
+
146
+ <p class="mt-10 text-lg">{{ t('payment.kaspi.adata') }}</p>
147
+ </div>
148
+
149
+ <a-button @click="onBack" view="outline">
150
+ {{ t('payment.kaspi.changeMethod') }}
151
+ </a-button>
152
+ </div>
153
+ </a-side-panel>
154
+ </template>
155
+
156
+ <style scoped>
157
+
158
+ </style>