adata-ui 2.1.40-beta.4 → 2.1.40-beta.5
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/button/AButtonV2.vue +29 -6
- 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/overlays/modal/AModalV2.vue +10 -9
- package/package.json +1 -1
|
@@ -3,9 +3,11 @@ import type { Component } from 'vue'
|
|
|
3
3
|
|
|
4
4
|
type View = 'solid' | 'outline' | 'ghost' | 'soft'
|
|
5
5
|
type Size = 'sm' | 'md' | 'lg'
|
|
6
|
+
type Color = 'blue' | 'green' | 'red' | 'gray'
|
|
6
7
|
|
|
7
8
|
const props = withDefaults(defineProps<{
|
|
8
9
|
view?: View
|
|
10
|
+
color?: Color
|
|
9
11
|
size?: Size
|
|
10
12
|
icon?: Component
|
|
11
13
|
iconRight?: boolean
|
|
@@ -15,6 +17,7 @@ const props = withDefaults(defineProps<{
|
|
|
15
17
|
type?: 'button' | 'submit' | 'reset'
|
|
16
18
|
}>(), {
|
|
17
19
|
view: 'solid',
|
|
20
|
+
color: 'blue',
|
|
18
21
|
size: 'md',
|
|
19
22
|
iconRight: false,
|
|
20
23
|
loading: false,
|
|
@@ -25,11 +28,31 @@ const props = withDefaults(defineProps<{
|
|
|
25
28
|
|
|
26
29
|
const emit = defineEmits<{ click: [event: MouseEvent] }>()
|
|
27
30
|
|
|
28
|
-
const VIEW_CLASS: Record<View, string
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
const VIEW_CLASS: Record<Color, Record<View, string>> = {
|
|
32
|
+
blue: {
|
|
33
|
+
solid: 'bg-blue-600 text-white shadow-sm hover:bg-blue-700 hover:shadow-md active:bg-blue-800 dark:bg-blue-500 dark:hover:bg-blue-400',
|
|
34
|
+
outline: 'border border-blue-500 bg-white text-blue-700 hover:bg-blue-50 active:bg-blue-100 dark:border-blue-500 dark:bg-gray-900 dark:text-blue-300 dark:hover:bg-blue-950/40',
|
|
35
|
+
ghost: 'text-deepblue-900 hover:bg-gray-100 active:bg-gray-200 dark:text-gray-100 dark:hover:bg-white/[0.06] dark:active:bg-white/[0.1]',
|
|
36
|
+
soft: 'bg-blue-50 text-blue-700 hover:bg-blue-100 active:bg-blue-200/70 dark:bg-blue-950/40 dark:text-blue-300 dark:hover:bg-blue-900/40',
|
|
37
|
+
},
|
|
38
|
+
green: {
|
|
39
|
+
solid: 'bg-green-600 text-white shadow-sm hover:bg-green-700 hover:shadow-md active:bg-green-800 dark:bg-green-500 dark:hover:bg-green-400',
|
|
40
|
+
outline: 'border border-green-500 bg-white text-green-700 hover:bg-green-50 active:bg-green-100 dark:border-green-500 dark:bg-gray-900 dark:text-green-300 dark:hover:bg-green-950/40',
|
|
41
|
+
ghost: 'text-green-700 hover:bg-green-50 active:bg-green-100 dark:text-green-300 dark:hover:bg-green-950/40',
|
|
42
|
+
soft: 'bg-green-50 text-green-700 hover:bg-green-100 active:bg-green-200/70 dark:bg-green-950/40 dark:text-green-300 dark:hover:bg-green-900/40',
|
|
43
|
+
},
|
|
44
|
+
red: {
|
|
45
|
+
solid: 'bg-red-600 text-white shadow-sm hover:bg-red-700 hover:shadow-md active:bg-red-800 dark:bg-red-500 dark:hover:bg-red-400',
|
|
46
|
+
outline: 'border border-red-500 bg-white text-red-700 hover:bg-red-50 active:bg-red-100 dark:border-red-500 dark:bg-gray-900 dark:text-red-300 dark:hover:bg-red-950/40',
|
|
47
|
+
ghost: 'text-red-700 hover:bg-red-50 active:bg-red-100 dark:text-red-300 dark:hover:bg-red-950/40',
|
|
48
|
+
soft: 'bg-red-50 text-red-700 hover:bg-red-100 active:bg-red-200/70 dark:bg-red-950/40 dark:text-red-300 dark:hover:bg-red-900/40',
|
|
49
|
+
},
|
|
50
|
+
gray: {
|
|
51
|
+
solid: 'bg-gray-600 text-white shadow-sm hover:bg-gray-700 hover:shadow-md active:bg-gray-800 dark:bg-gray-500 dark:hover:bg-gray-400',
|
|
52
|
+
outline: 'border border-gray-300 bg-white text-deepblue-900 hover:border-gray-400 hover:bg-gray-50 active:bg-gray-100 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-100 dark:hover:border-gray-600 dark:hover:bg-white/[0.06]',
|
|
53
|
+
ghost: 'text-deepblue-900 hover:bg-gray-100 active:bg-gray-200 dark:text-gray-100 dark:hover:bg-white/[0.06] dark:active:bg-white/[0.1]',
|
|
54
|
+
soft: 'bg-gray-100 text-gray-700 hover:bg-gray-200 active:bg-gray-200/70 dark:bg-gray-800/60 dark:text-gray-200 dark:hover:bg-gray-700/60',
|
|
55
|
+
},
|
|
33
56
|
}
|
|
34
57
|
|
|
35
58
|
const SIZE_CLASS: Record<Size, string> = {
|
|
@@ -56,7 +79,7 @@ function onClick(event: MouseEvent) {
|
|
|
56
79
|
:disabled="disabled || loading"
|
|
57
80
|
class="button-v2 inline-flex select-none items-center justify-center rounded-xl font-semibold transition-all duration-150 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
|
|
58
81
|
:class="[
|
|
59
|
-
VIEW_CLASS[view],
|
|
82
|
+
VIEW_CLASS[color][view],
|
|
60
83
|
SIZE_CLASS[size],
|
|
61
84
|
block ? 'w-full' : '',
|
|
62
85
|
iconRight ? 'flex-row-reverse' : '',
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { twMerge } from 'tailwind-merge'
|
|
3
|
+
|
|
4
|
+
defineOptions({ name: 'StatusBadgeV2', inheritAttrs: false })
|
|
5
|
+
|
|
6
|
+
type BadgeType = 'primary' | 'success' | 'danger' | 'gray' | 'orange' | 'warning'
|
|
7
|
+
type BadgeView = 'default' | 'inverted' | 'transparent'
|
|
8
|
+
type BadgeSize = 'xs' | 'sm' | 'md' | 'lg'
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
type?: BadgeType
|
|
12
|
+
view?: BadgeView
|
|
13
|
+
size?: BadgeSize
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
17
|
+
type: 'primary',
|
|
18
|
+
view: 'inverted',
|
|
19
|
+
size: 'sm',
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const base
|
|
23
|
+
= 'inline-flex w-fit items-center justify-center gap-1.5 rounded-full font-semibold select-none'
|
|
24
|
+
|
|
25
|
+
const sizeClasses: Record<BadgeSize, string> = {
|
|
26
|
+
xs: 'px-2 py-0.5 text-[11px] leading-none',
|
|
27
|
+
sm: 'px-2.5 py-1 text-xs leading-none',
|
|
28
|
+
md: 'px-3 py-1.5 text-sm',
|
|
29
|
+
lg: 'px-4 py-2 text-base',
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const typeClasses: Record<BadgeView, Partial<Record<BadgeType, string>>> = {
|
|
33
|
+
default: {
|
|
34
|
+
primary: 'bg-blue-700 text-white dark:bg-blue-500 dark:text-gray-900',
|
|
35
|
+
success: 'bg-green-500 text-white dark:bg-green-400 dark:text-gray-900',
|
|
36
|
+
danger: 'bg-red-500 text-white dark:text-gray-100',
|
|
37
|
+
warning: 'bg-yellow-400 text-white dark:text-gray-900',
|
|
38
|
+
gray: 'bg-deepblue-900 text-white dark:bg-gray-200/10 dark:text-gray-200',
|
|
39
|
+
orange: 'bg-orange-600 text-white dark:text-gray-100',
|
|
40
|
+
},
|
|
41
|
+
inverted: {
|
|
42
|
+
primary: 'bg-blue-700/10 text-blue-700 dark:bg-blue-500/10 dark:text-blue-500',
|
|
43
|
+
success: 'bg-green-500/15 text-green-600 dark:bg-green-400/15 dark:text-green-400',
|
|
44
|
+
danger: 'bg-red-500/10 text-red-500 dark:bg-red-400/10 dark:text-red-400',
|
|
45
|
+
warning: 'bg-yellow-400/15 text-yellow-500 dark:bg-yellow-400/15 dark:text-yellow-400',
|
|
46
|
+
gray: 'bg-deepblue-900/10 text-gray-600 dark:bg-gray-200/10 dark:text-gray-300',
|
|
47
|
+
orange: 'bg-orange-600/10 text-orange-600 dark:text-orange-500',
|
|
48
|
+
},
|
|
49
|
+
transparent: {
|
|
50
|
+
primary: 'bg-transparent text-blue-700 dark:text-blue-500',
|
|
51
|
+
success: 'bg-transparent text-green-600 dark:text-green-400',
|
|
52
|
+
danger: 'bg-transparent text-red-500 dark:text-red-400',
|
|
53
|
+
warning: 'bg-transparent text-yellow-500 dark:text-yellow-400',
|
|
54
|
+
gray: 'bg-transparent text-gray-600 dark:text-gray-300',
|
|
55
|
+
orange: 'bg-transparent text-orange-600 dark:text-orange-500',
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const attrs = useAttrs()
|
|
60
|
+
|
|
61
|
+
// twMerge resolves Tailwind conflicts: any class passed via `class` wins over
|
|
62
|
+
// the defaults below, so padding / rounded / bg / text can all be overridden.
|
|
63
|
+
const classes = computed(() =>
|
|
64
|
+
twMerge(
|
|
65
|
+
base,
|
|
66
|
+
sizeClasses[props.size],
|
|
67
|
+
typeClasses[props.view][props.type],
|
|
68
|
+
attrs.class as string,
|
|
69
|
+
),
|
|
70
|
+
)
|
|
71
|
+
</script>
|
|
72
|
+
|
|
73
|
+
<template>
|
|
74
|
+
<div :class="classes" v-bind="{ ...$attrs, class: undefined }">
|
|
75
|
+
<slot name="point" />
|
|
76
|
+
<slot />
|
|
77
|
+
</div>
|
|
78
|
+
</template>
|
|
@@ -152,26 +152,26 @@ onMounted(() => {
|
|
|
152
152
|
</AButton>
|
|
153
153
|
</section>
|
|
154
154
|
|
|
155
|
-
<a-modal
|
|
155
|
+
<a-modal-v2
|
|
156
156
|
v-model="isModalOpen"
|
|
157
157
|
width="424px"
|
|
158
158
|
@close="isModalOpen = false"
|
|
159
159
|
>
|
|
160
|
-
<div class="flex flex-col items-center gap-
|
|
161
|
-
<
|
|
160
|
+
<div class="flex flex-col items-center gap-4 px-6 py-4">
|
|
161
|
+
<a-ill-mail />
|
|
162
|
+
<h4 class="text-[18px] font-semibold">
|
|
162
163
|
{{ t('forms.feedback.modalTitle') }}
|
|
163
164
|
</h4>
|
|
164
|
-
<a-ill-mail />
|
|
165
165
|
<p class="body-400 max-[352xp]">
|
|
166
166
|
{{ t('forms.feedback.modalSubtitle') }}
|
|
167
167
|
</p>
|
|
168
|
-
<
|
|
169
|
-
block
|
|
168
|
+
<a-button-v2
|
|
170
169
|
view="outline"
|
|
170
|
+
block
|
|
171
171
|
@click="isModalOpen = false"
|
|
172
172
|
>
|
|
173
173
|
{{ t('forms.feedback.close') }}
|
|
174
|
-
</
|
|
174
|
+
</a-button-v2>
|
|
175
175
|
</div>
|
|
176
|
-
</a-modal>
|
|
176
|
+
</a-modal-v2>
|
|
177
177
|
</template>
|
|
@@ -257,7 +257,7 @@ const layoutClass = computed(() => {
|
|
|
257
257
|
if (!props.label) {
|
|
258
258
|
return props.size === 'md' ? 'h-10 py-1.5' : 'h-9 py-1.5'
|
|
259
259
|
}
|
|
260
|
-
return props.size === 'md' ? 'h-10 pt-
|
|
260
|
+
return props.size === 'md' ? 'h-10 pt-4 pb-0.5' : 'h-9 pt-3 pb-0.5'
|
|
261
261
|
})
|
|
262
262
|
|
|
263
263
|
const iconOffsetClass = computed(() => {
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
defineOptions({ inheritAttrs: false })
|
|
3
|
+
|
|
4
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
5
|
+
label: '',
|
|
6
|
+
placeholder: '',
|
|
7
|
+
required: false,
|
|
8
|
+
disabled: false,
|
|
9
|
+
readonly: false,
|
|
10
|
+
error: undefined,
|
|
11
|
+
size: 'md',
|
|
12
|
+
variant: 'default',
|
|
13
|
+
rows: 4,
|
|
14
|
+
resize: 'vertical',
|
|
15
|
+
maxlength: undefined,
|
|
16
|
+
autoResize: false,
|
|
17
|
+
inputId: undefined,
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const emit = defineEmits<{
|
|
21
|
+
(e: 'focus', ev: FocusEvent): void
|
|
22
|
+
(e: 'blur', ev: FocusEvent): void
|
|
23
|
+
}>()
|
|
24
|
+
|
|
25
|
+
interface Props {
|
|
26
|
+
label?: string
|
|
27
|
+
placeholder?: string
|
|
28
|
+
required?: boolean
|
|
29
|
+
disabled?: boolean
|
|
30
|
+
readonly?: boolean
|
|
31
|
+
error?: string | string[]
|
|
32
|
+
size?: 'sm' | 'md'
|
|
33
|
+
variant?: 'default' | 'gray' | 'blue'
|
|
34
|
+
rows?: number
|
|
35
|
+
resize?: 'none' | 'vertical' | 'both'
|
|
36
|
+
maxlength?: number
|
|
37
|
+
autoResize?: boolean
|
|
38
|
+
inputId?: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const modelValue = defineModel<string>({ default: '' })
|
|
42
|
+
|
|
43
|
+
const textareaRef = ref<HTMLTextAreaElement | null>(null)
|
|
44
|
+
const isFocused = ref(false)
|
|
45
|
+
|
|
46
|
+
const internalId = useId()
|
|
47
|
+
const inputId = computed(() => props.inputId ?? `textarea-v2-${internalId}`)
|
|
48
|
+
|
|
49
|
+
const hasValue = computed(() => modelValue.value != null && modelValue.value !== '')
|
|
50
|
+
const hasError = computed(() => Array.isArray(props.error) ? props.error.length > 0 : !!props.error)
|
|
51
|
+
const isLabelFloated = computed(() => hasValue.value || isFocused.value)
|
|
52
|
+
|
|
53
|
+
const errorMessages = computed(() => {
|
|
54
|
+
if (!props.error) return [] as string[]
|
|
55
|
+
return Array.isArray(props.error) ? props.error : [props.error]
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
const fieldClass = computed(() => {
|
|
59
|
+
if (props.disabled) {
|
|
60
|
+
return 'border-gray-200 bg-gray-100/70 dark:border-gray-700 dark:bg-white/[0.03]'
|
|
61
|
+
}
|
|
62
|
+
if (props.variant === 'gray') {
|
|
63
|
+
return 'border-transparent bg-gray-50 dark:border-transparent dark:bg-white/[0.04]'
|
|
64
|
+
}
|
|
65
|
+
if (props.variant === 'blue') {
|
|
66
|
+
return 'border-blue-200 bg-blue-50 dark:border-blue-900/50 dark:bg-blue-900/20'
|
|
67
|
+
}
|
|
68
|
+
return 'border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-900'
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
const resizeClass = computed(() => {
|
|
72
|
+
if (props.autoResize) return 'resize-none'
|
|
73
|
+
switch (props.resize) {
|
|
74
|
+
case 'none': return 'resize-none'
|
|
75
|
+
case 'both': return 'resize'
|
|
76
|
+
case 'vertical':
|
|
77
|
+
default: return 'resize-y'
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
const paddingClass = computed(() => {
|
|
82
|
+
// Паддинг постоянный (не зависит от фокуса/значения) — textarea не меняет размер при фокусе.
|
|
83
|
+
const pt = !props.label ? 'pt-2.5' : (props.size === 'md' ? 'pt-4' : 'pt-3.5')
|
|
84
|
+
const pb = props.maxlength ? 'pb-6' : (!props.label ? 'pb-2.5' : (props.size === 'md' ? 'pb-2' : 'pb-1.5'))
|
|
85
|
+
return `px-4 ${pt} ${pb}`
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
const attrs = useAttrs()
|
|
89
|
+
const wrapperAttrs = computed(() => ({ class: attrs.class, style: attrs.style }))
|
|
90
|
+
const inputAttrs = computed(() => {
|
|
91
|
+
const { class: _c, style: _s, ...rest } = attrs as Record<string, unknown>
|
|
92
|
+
return rest
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
function adjustHeight() {
|
|
96
|
+
const el = textareaRef.value
|
|
97
|
+
if (!props.autoResize || !el) return
|
|
98
|
+
el.style.height = 'auto'
|
|
99
|
+
el.style.height = `${el.scrollHeight}px`
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function onFocus(ev: FocusEvent) {
|
|
103
|
+
isFocused.value = true
|
|
104
|
+
emit('focus', ev)
|
|
105
|
+
}
|
|
106
|
+
function onBlur(ev: FocusEvent) {
|
|
107
|
+
isFocused.value = false
|
|
108
|
+
emit('blur', ev)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function focus() {
|
|
112
|
+
textareaRef.value?.focus()
|
|
113
|
+
}
|
|
114
|
+
function blur() {
|
|
115
|
+
textareaRef.value?.blur()
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
watch(modelValue, () => nextTick(adjustHeight))
|
|
119
|
+
onMounted(adjustHeight)
|
|
120
|
+
|
|
121
|
+
defineExpose({ focus, blur, textareaRef })
|
|
122
|
+
</script>
|
|
123
|
+
|
|
124
|
+
<template>
|
|
125
|
+
<div class="textarea-v2 relative w-full text-sm" v-bind="wrapperAttrs">
|
|
126
|
+
<div
|
|
127
|
+
class="textarea-v2__field relative rounded-[10px] border border-solid transition-colors duration-200 focus-within:border-blue-600 focus-within:ring-2 focus-within:ring-blue-600/20 hover:border-blue-500 dark:focus-within:border-blue-400 dark:focus-within:ring-blue-400/20 dark:hover:border-blue-400"
|
|
128
|
+
:class="[fieldClass, { 'textarea-v2__field--error': hasError, 'textarea-v2__field--disabled': disabled }]"
|
|
129
|
+
>
|
|
130
|
+
<label
|
|
131
|
+
v-if="label"
|
|
132
|
+
:for="inputId"
|
|
133
|
+
:data-size="size"
|
|
134
|
+
class="textarea-v2__label pointer-events-none absolute left-4 max-w-[calc(100%-2rem)] truncate text-gray-500 dark:text-gray-400"
|
|
135
|
+
:class="{ 'textarea-v2__label--floated': isLabelFloated }"
|
|
136
|
+
>
|
|
137
|
+
{{ label }}<span v-if="required" class="text-red-500 dark:text-red-400"> *</span>
|
|
138
|
+
</label>
|
|
139
|
+
|
|
140
|
+
<textarea
|
|
141
|
+
:id="inputId"
|
|
142
|
+
ref="textareaRef"
|
|
143
|
+
v-model="modelValue"
|
|
144
|
+
:rows="rows"
|
|
145
|
+
:maxlength="maxlength"
|
|
146
|
+
:placeholder="(isLabelFloated || !label) ? placeholder : ''"
|
|
147
|
+
:disabled="disabled"
|
|
148
|
+
:readonly="readonly"
|
|
149
|
+
:tabindex="disabled ? -1 : 0"
|
|
150
|
+
class="textarea-v2__input text-deepblue-900 block w-full bg-transparent leading-snug outline-none placeholder:text-gray-500 disabled:cursor-not-allowed dark:text-gray-200 dark:placeholder:text-gray-400"
|
|
151
|
+
:class="[resizeClass, paddingClass]"
|
|
152
|
+
v-bind="inputAttrs"
|
|
153
|
+
@focus="onFocus"
|
|
154
|
+
@blur="onBlur"
|
|
155
|
+
/>
|
|
156
|
+
|
|
157
|
+
<span
|
|
158
|
+
v-if="maxlength"
|
|
159
|
+
class="textarea-v2__counter pointer-events-none absolute bottom-1.5 right-3 text-[10px] text-gray-400 dark:text-gray-500"
|
|
160
|
+
>
|
|
161
|
+
{{ modelValue?.length ?? 0 }}/{{ maxlength }}
|
|
162
|
+
</span>
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<transition
|
|
166
|
+
enter-active-class="textarea-v2__error-enter-active"
|
|
167
|
+
enter-from-class="textarea-v2__error-enter-from"
|
|
168
|
+
enter-to-class="textarea-v2__error-enter-to"
|
|
169
|
+
leave-active-class="textarea-v2__error-leave-active"
|
|
170
|
+
leave-from-class="textarea-v2__error-leave-from"
|
|
171
|
+
leave-to-class="textarea-v2__error-leave-to"
|
|
172
|
+
>
|
|
173
|
+
<p v-if="hasError" class="mt-1 flex flex-col text-xs text-red-500 dark:text-red-400">
|
|
174
|
+
<span v-for="(err, idx) in errorMessages" :key="idx">{{ err }}</span>
|
|
175
|
+
</p>
|
|
176
|
+
</transition>
|
|
177
|
+
</div>
|
|
178
|
+
</template>
|
|
179
|
+
|
|
180
|
+
<style scoped>
|
|
181
|
+
.textarea-v2__field--error {
|
|
182
|
+
border-color: theme('colors.red.500');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.textarea-v2__field--disabled {
|
|
186
|
+
pointer-events: none;
|
|
187
|
+
cursor: not-allowed;
|
|
188
|
+
opacity: 0.7;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.textarea-v2__field--disabled .textarea-v2__input {
|
|
192
|
+
cursor: not-allowed;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.textarea-v2__input {
|
|
196
|
+
scrollbar-width: thin;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.textarea-v2__label {
|
|
200
|
+
top: 26px;
|
|
201
|
+
transform: translateY(-50%);
|
|
202
|
+
font-size: 14px;
|
|
203
|
+
line-height: 1.3;
|
|
204
|
+
transition:
|
|
205
|
+
transform 300ms cubic-bezier(0.22, 1, 0.36, 1),
|
|
206
|
+
font-size 300ms cubic-bezier(0.22, 1, 0.36, 1),
|
|
207
|
+
color 300ms ease-out;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.textarea-v2__label[data-size="sm"] {
|
|
211
|
+
top: 24px;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.textarea-v2__label--floated {
|
|
215
|
+
font-size: 10px;
|
|
216
|
+
transform: translateY(-21px);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.textarea-v2__label--floated[data-size="sm"] {
|
|
220
|
+
transform: translateY(-19px);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.textarea-v2__error-enter-active {
|
|
224
|
+
transition: transform 0.3s, opacity 0.3s;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.textarea-v2__error-enter-from {
|
|
228
|
+
opacity: 0;
|
|
229
|
+
transform: translateY(-4px);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.textarea-v2__error-leave-active {
|
|
233
|
+
transition: transform 0.2s, opacity 0.2s;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.textarea-v2__error-leave-to {
|
|
237
|
+
opacity: 0;
|
|
238
|
+
transform: translateY(-4px);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
@media (prefers-reduced-motion: reduce) {
|
|
242
|
+
.textarea-v2__label,
|
|
243
|
+
.textarea-v2__error-enter-active,
|
|
244
|
+
.textarea-v2__error-leave-active {
|
|
245
|
+
transition: none;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
</style>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { useAnotherDeviceModal } from '#adata-ui/composables/modalsState'
|
|
3
2
|
import { useUrls } from '#adata-ui/composables/useUrls'
|
|
3
|
+
|
|
4
4
|
const { t, locale } = useI18n()
|
|
5
5
|
const localePath = useLocalePath()
|
|
6
6
|
|
|
@@ -14,47 +14,46 @@ function onClose() {
|
|
|
14
14
|
</script>
|
|
15
15
|
|
|
16
16
|
<template>
|
|
17
|
-
<a-modal
|
|
17
|
+
<a-modal-v2
|
|
18
18
|
v-model="isOpen"
|
|
19
|
-
|
|
19
|
+
width="424px"
|
|
20
20
|
@close="onClose"
|
|
21
21
|
>
|
|
22
|
-
<div class="flex flex-col items-center justify-center gap-
|
|
22
|
+
<div class="flex flex-col items-center justify-center gap-4 px-6 pb-4">
|
|
23
23
|
<a-ill-phone-check />
|
|
24
24
|
<p class="body-400 text-center">
|
|
25
25
|
{{ t('modals.another_device_modal.info') }}
|
|
26
26
|
</p>
|
|
27
27
|
<div
|
|
28
|
-
class="body-400 flex gap-2 rounded-lg border border-red-500 dark:border-red-400
|
|
28
|
+
class="body-400 flex gap-2 rounded-lg border border-red-500 bg-[#FDECEB] p-3 text-red-500 dark:border-red-400 dark:bg-red-400/10 dark:text-red-400"
|
|
29
29
|
>
|
|
30
30
|
<a-icon-warning-triangle
|
|
31
|
-
class="shrink-0"
|
|
32
|
-
width="24"
|
|
33
|
-
height="24"
|
|
31
|
+
class="size-4 shrink-0"
|
|
34
32
|
/>
|
|
35
|
-
<
|
|
33
|
+
<span class="text-xs">
|
|
36
34
|
{{ t('modals.another_device_modal.alert') }}
|
|
37
|
-
</
|
|
35
|
+
</span>
|
|
38
36
|
</div>
|
|
39
37
|
</div>
|
|
40
38
|
<template #footer>
|
|
41
39
|
<div class="flex w-full gap-2">
|
|
42
|
-
<a-button
|
|
40
|
+
<a-button-v2
|
|
43
41
|
view="outline"
|
|
44
|
-
|
|
42
|
+
color="blue"
|
|
43
|
+
block
|
|
45
44
|
@click="onClose"
|
|
46
45
|
>
|
|
47
46
|
{{ t('modals.buttons.close') }}
|
|
48
|
-
</a-button>
|
|
49
|
-
<a-button
|
|
50
|
-
|
|
47
|
+
</a-button-v2>
|
|
48
|
+
<a-button-v2
|
|
49
|
+
block
|
|
51
50
|
:to="buildLocalizedUrl(locale, landing)"
|
|
52
51
|
>
|
|
53
52
|
{{ t('modals.buttons.toMain') }}
|
|
54
|
-
</a-button>
|
|
53
|
+
</a-button-v2>
|
|
55
54
|
</div>
|
|
56
55
|
</template>
|
|
57
|
-
</a-modal>
|
|
56
|
+
</a-modal-v2>
|
|
58
57
|
</template>
|
|
59
58
|
|
|
60
59
|
<style scoped></style>
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
|
|
3
2
|
const { t } = useI18n()
|
|
4
3
|
|
|
5
|
-
const isOpen = defineModel<boolean>({default: false})
|
|
4
|
+
const isOpen = defineModel<boolean>({ default: false })
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
function cancel() {
|
|
8
7
|
isOpen.value = false
|
|
9
8
|
}
|
|
10
9
|
|
|
@@ -16,30 +15,28 @@ const modalText = {
|
|
|
16
15
|
</script>
|
|
17
16
|
|
|
18
17
|
<template>
|
|
19
|
-
<a-modal
|
|
18
|
+
<a-modal-v2
|
|
20
19
|
v-model="isOpen"
|
|
21
|
-
|
|
22
|
-
:
|
|
20
|
+
width="424px"
|
|
21
|
+
:persistent="true"
|
|
23
22
|
>
|
|
24
|
-
<div class="flex flex-col gap-
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
</p>
|
|
28
|
-
<a-ill-ok class="w-full" />
|
|
29
|
-
<p class="text-center body-400">
|
|
23
|
+
<div class="flex flex-col items-center justify-center gap-4 px-6 py-4">
|
|
24
|
+
<a-ill-ok />
|
|
25
|
+
<p class="body-400 text-center">
|
|
30
26
|
{{ modalText.info }}
|
|
31
27
|
</p>
|
|
32
28
|
</div>
|
|
33
29
|
<template #footer>
|
|
34
|
-
<a-button
|
|
30
|
+
<a-button-v2
|
|
35
31
|
view="outline"
|
|
32
|
+
color="blue"
|
|
36
33
|
block
|
|
37
34
|
@click="cancel"
|
|
38
35
|
>
|
|
39
36
|
{{ modalText.close }}
|
|
40
|
-
</a-button>
|
|
37
|
+
</a-button-v2>
|
|
41
38
|
</template>
|
|
42
|
-
</a-modal>
|
|
39
|
+
</a-modal-v2>
|
|
43
40
|
</template>
|
|
44
41
|
|
|
45
42
|
<style scoped>
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { handleElementPosition } from '#adata-ui/utils/getMaxZindex'
|
|
3
|
-
|
|
4
2
|
const props = withDefaults(
|
|
5
3
|
defineProps<{
|
|
6
|
-
tariff
|
|
4
|
+
tariff?: string
|
|
7
5
|
deleteFromDom?: boolean
|
|
8
6
|
|
|
9
7
|
}>(),
|
|
@@ -24,42 +22,37 @@ function confirm() {
|
|
|
24
22
|
function cancel() {
|
|
25
23
|
isOpen.value = false
|
|
26
24
|
}
|
|
27
|
-
|
|
28
|
-
if (props.deleteFromDom) {
|
|
29
|
-
handleElementPosition('connecting-tariff')
|
|
30
|
-
}
|
|
25
|
+
// `deleteFromDom` сохранён для совместимости API; ручной z-index в AModalV2 не нужен.
|
|
31
26
|
</script>
|
|
32
27
|
|
|
33
28
|
<template>
|
|
34
|
-
<a-modal
|
|
29
|
+
<a-modal-v2
|
|
35
30
|
v-model="isOpen"
|
|
36
|
-
|
|
31
|
+
width="424px"
|
|
37
32
|
>
|
|
38
|
-
<div class="flex flex-col gap-
|
|
39
|
-
<
|
|
40
|
-
{{ t('modals.connecting.title') }}
|
|
41
|
-
</p>
|
|
42
|
-
<a-ill-turn-on-tariff class="w-full"/>
|
|
33
|
+
<div class="flex flex-col items-center justify-center gap-4 px-6 py-4">
|
|
34
|
+
<a-ill-turn-on-tariff />
|
|
43
35
|
<p class="body-400 whitespace-pre-wrap text-center">
|
|
44
36
|
{{ t('modals.connecting.questions') }}
|
|
45
37
|
</p>
|
|
46
38
|
</div>
|
|
47
39
|
|
|
48
40
|
<template #footer>
|
|
49
|
-
<div class="flex gap-2
|
|
50
|
-
<a-button
|
|
41
|
+
<div class="flex w-full gap-2">
|
|
42
|
+
<a-button-v2
|
|
51
43
|
view="outline"
|
|
44
|
+
color="blue"
|
|
52
45
|
block
|
|
53
46
|
@click="cancel"
|
|
54
47
|
>
|
|
55
48
|
{{ t('modals.buttons.close') }}
|
|
56
|
-
</a-button>
|
|
57
|
-
<a-button block @click="confirm">
|
|
49
|
+
</a-button-v2>
|
|
50
|
+
<a-button-v2 block @click="confirm">
|
|
58
51
|
{{ t('reuse.yes') }}
|
|
59
|
-
</a-button>
|
|
52
|
+
</a-button-v2>
|
|
60
53
|
</div>
|
|
61
54
|
</template>
|
|
62
|
-
</a-modal>
|
|
55
|
+
</a-modal-v2>
|
|
63
56
|
</template>
|
|
64
57
|
|
|
65
58
|
<style scoped>
|
|
@@ -1,20 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { UserInfo } from '~/api/modules/user/types'
|
|
3
|
-
import { handleElementPosition } from '#adata-ui/utils/getMaxZindex'
|
|
4
|
-
|
|
5
|
-
const props = withDefaults(defineProps<Props>(), {
|
|
6
|
-
deleteFromDom: false,
|
|
7
|
-
})
|
|
8
|
-
|
|
9
|
-
const emit = defineEmits<{
|
|
10
|
-
(e: 'next'): void
|
|
11
|
-
}>()
|
|
12
3
|
|
|
13
4
|
const { t } = useI18n()
|
|
14
5
|
|
|
15
|
-
interface Props {
|
|
16
|
-
deleteFromDom?: boolean
|
|
17
|
-
}
|
|
18
6
|
const { topUpSidePanel } = usePayment()
|
|
19
7
|
|
|
20
8
|
const isOpen = defineModel<boolean>({ default: false })
|
|
@@ -27,49 +15,46 @@ function confirm() {
|
|
|
27
15
|
function cancel() {
|
|
28
16
|
isOpen.value = false
|
|
29
17
|
}
|
|
30
|
-
|
|
31
|
-
if (props.deleteFromDom) {
|
|
32
|
-
handleElementPosition('insufficient-funds')
|
|
33
|
-
}
|
|
34
18
|
</script>
|
|
35
19
|
|
|
36
20
|
<template>
|
|
37
|
-
<a-modal
|
|
21
|
+
<a-modal-v2
|
|
38
22
|
v-model="isOpen"
|
|
39
|
-
|
|
23
|
+
width="424px"
|
|
40
24
|
>
|
|
41
|
-
<div class="flex flex-col gap-
|
|
42
|
-
<
|
|
25
|
+
<div class="flex flex-col gap-4 px-6 py-4">
|
|
26
|
+
<a-ill-empty-wallet class="self-center" />
|
|
27
|
+
<p class="text-center text-[18px] font-semibold">
|
|
43
28
|
{{ t('modals.low_balance.title') }}
|
|
44
29
|
</p>
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
|
|
30
|
+
<p class="text-center">
|
|
31
|
+
{{ t('modals.low_balance.questions') }}
|
|
32
|
+
</p>
|
|
33
|
+
<div class="flex flex-col rounded-xl border border-blue-500 bg-blue-50 px-4 py-2 dark:border-blue-500 dark:bg-blue-950/40">
|
|
34
|
+
<p class="body-600 text-center text-blue-700 dark:text-blue-300">
|
|
48
35
|
{{ t('modals.low_balance.current_balance') }}
|
|
49
36
|
</p>
|
|
50
|
-
<p class="body-600 text-center text-xl text-
|
|
37
|
+
<p class="body-600 text-center text-xl text-blue-700 dark:text-blue-300">
|
|
51
38
|
{{ userInfo?.balance ? userInfo?.balance.toLocaleString('ru-RU') : 0 }}₸
|
|
52
39
|
</p>
|
|
53
40
|
</div>
|
|
54
|
-
<p class="text-center">
|
|
55
|
-
{{ t('modals.low_balance.questions') }}
|
|
56
|
-
</p>
|
|
57
41
|
</div>
|
|
58
42
|
<template #footer>
|
|
59
|
-
<div class="flex gap-2
|
|
60
|
-
<a-button
|
|
43
|
+
<div class="flex gap-2">
|
|
44
|
+
<a-button-v2
|
|
61
45
|
view="outline"
|
|
46
|
+
color="blue"
|
|
62
47
|
block
|
|
63
48
|
@click="cancel"
|
|
64
49
|
>
|
|
65
50
|
{{ t('modals.buttons.close') }}
|
|
66
|
-
</a-button>
|
|
67
|
-
<a-button block @click="confirm">
|
|
51
|
+
</a-button-v2>
|
|
52
|
+
<a-button-v2 block @click="confirm">
|
|
68
53
|
{{ t('modals.buttons.topUp') }}
|
|
69
|
-
</a-button>
|
|
54
|
+
</a-button-v2>
|
|
70
55
|
</div>
|
|
71
56
|
</template>
|
|
72
|
-
</a-modal>
|
|
57
|
+
</a-modal-v2>
|
|
73
58
|
</template>
|
|
74
59
|
|
|
75
60
|
<style scoped>
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
|
|
3
2
|
import { useUrls } from '#adata-ui/composables/useUrls'
|
|
4
3
|
|
|
5
4
|
const { t } = useI18n()
|
|
@@ -16,11 +15,13 @@ function goTariffs() {
|
|
|
16
15
|
<template>
|
|
17
16
|
<a-modal v-model="isOpen" :title="t('modals.limit.title')">
|
|
18
17
|
<div class="flex flex-col items-center gap-5 text-center text-sm">
|
|
19
|
-
<a-ill-empty-wallet
|
|
18
|
+
<a-ill-empty-wallet />
|
|
20
19
|
<div>{{ t('modals.limit.content') }}</div>
|
|
21
20
|
</div>
|
|
22
21
|
<template #footer>
|
|
23
|
-
<a-button @click="goTariffs"
|
|
22
|
+
<a-button block @click="goTariffs">
|
|
23
|
+
{{ t('modals.buttons.goTariffs') }}
|
|
24
|
+
</a-button>
|
|
24
25
|
</template>
|
|
25
26
|
</a-modal>
|
|
26
27
|
</template>
|
|
@@ -5,33 +5,31 @@ const { t } = useI18n()
|
|
|
5
5
|
|
|
6
6
|
const isOpen = useNoAccessModal()
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
function onClose() {
|
|
9
9
|
isOpen.value = false
|
|
10
10
|
}
|
|
11
11
|
</script>
|
|
12
12
|
|
|
13
13
|
<template>
|
|
14
|
-
<a-modal
|
|
14
|
+
<a-modal-v2
|
|
15
15
|
v-model="isOpen"
|
|
16
|
-
|
|
16
|
+
width="424px"
|
|
17
17
|
>
|
|
18
|
-
<div class="flex flex-col gap-
|
|
19
|
-
<
|
|
20
|
-
{{ t('modals.no_access.title') }}
|
|
21
|
-
</p>
|
|
22
|
-
<a-ill-door class="w-full" />
|
|
18
|
+
<div class="flex flex-col items-center justify-center gap-4 px-6 py-4">
|
|
19
|
+
<a-ill-door />
|
|
23
20
|
<p class="body-400 whitespace-pre-wrap text-center">
|
|
24
21
|
{{ t('modals.no_access.info') }}
|
|
25
22
|
</p>
|
|
26
23
|
</div>
|
|
27
24
|
<template #footer>
|
|
28
|
-
<a-button
|
|
25
|
+
<a-button-v2
|
|
29
26
|
view="outline"
|
|
27
|
+
color="blue"
|
|
30
28
|
block
|
|
31
29
|
@click="onClose"
|
|
32
30
|
>
|
|
33
31
|
{{ t('modals.buttons.close') }}
|
|
34
|
-
</a-button>
|
|
32
|
+
</a-button-v2>
|
|
35
33
|
</template>
|
|
36
|
-
</a-modal>
|
|
34
|
+
</a-modal-v2>
|
|
37
35
|
</template>
|
|
@@ -66,7 +66,7 @@ if (props.deleteFromDom) {
|
|
|
66
66
|
:name="`${item.name}`"
|
|
67
67
|
side="right"
|
|
68
68
|
:value="item.id"
|
|
69
|
-
class="flex items-center justify-between gap-2 bg-gray-50 p-[10px_16px]
|
|
69
|
+
class="flex items-center justify-between gap-2 rounded-md bg-gray-50 p-[10px_16px] dark:bg-[#393D40]"
|
|
70
70
|
>
|
|
71
71
|
<div class="flex w-full items-center gap-2">
|
|
72
72
|
<component :is="item.img" />
|
|
@@ -3,28 +3,30 @@ const { t } = useI18n()
|
|
|
3
3
|
|
|
4
4
|
const isOpen = defineModel<boolean>({ required: true })
|
|
5
5
|
</script>
|
|
6
|
+
|
|
6
7
|
<template>
|
|
7
|
-
<a-modal
|
|
8
|
+
<a-modal-v2
|
|
8
9
|
v-model="isOpen"
|
|
9
10
|
width="424px"
|
|
10
11
|
>
|
|
11
|
-
<
|
|
12
|
+
<div class="flex flex-col items-center justify-center gap-4 px-6 py-4">
|
|
13
|
+
<a-ill-ok />
|
|
12
14
|
<p class="heading-02 text-center font-bold">
|
|
13
15
|
{{ t('modals.report_bug.confirmed') }}
|
|
14
16
|
</p>
|
|
15
|
-
|
|
16
|
-
<div class="flex flex-col gap-5 items-center">
|
|
17
|
-
<a-ill-ok />
|
|
18
|
-
<p class="text-sm text-center">
|
|
17
|
+
<p class="body-400 text-center">
|
|
19
18
|
{{ t('modals.report_bug.gratitude') }}
|
|
20
19
|
</p>
|
|
21
|
-
|
|
20
|
+
</div>
|
|
21
|
+
<template #footer>
|
|
22
|
+
<a-button-v2
|
|
22
23
|
view="outline"
|
|
24
|
+
color="blue"
|
|
23
25
|
block
|
|
24
26
|
@click="isOpen = false"
|
|
25
27
|
>
|
|
26
28
|
{{ t('actions.close') }}
|
|
27
|
-
</a-button>
|
|
28
|
-
</
|
|
29
|
-
</a-modal>
|
|
29
|
+
</a-button-v2>
|
|
30
|
+
</template>
|
|
31
|
+
</a-modal-v2>
|
|
30
32
|
</template>
|
|
@@ -18,7 +18,7 @@ const env = myLayer.mode
|
|
|
18
18
|
|
|
19
19
|
const mask = new Mask({ mask: '# (###) ### ## ##' })
|
|
20
20
|
|
|
21
|
-
const isOpen = defineModel<boolean>()
|
|
21
|
+
const isOpen = defineModel<boolean>({ default: false })
|
|
22
22
|
const form: any = reactive({
|
|
23
23
|
name: '',
|
|
24
24
|
email: '',
|
|
@@ -35,6 +35,7 @@ const scheme = z.object({
|
|
|
35
35
|
})
|
|
36
36
|
|
|
37
37
|
const submitted = ref(false)
|
|
38
|
+
const sending = ref(false)
|
|
38
39
|
|
|
39
40
|
const { validation, getError } = useUIValidation(
|
|
40
41
|
scheme,
|
|
@@ -58,6 +59,7 @@ watch(phone, () => {
|
|
|
58
59
|
async function send() {
|
|
59
60
|
submitted.value = true
|
|
60
61
|
if (validation.value) return
|
|
62
|
+
sending.value = true
|
|
61
63
|
try {
|
|
62
64
|
await $fetch<any>(`https://users.${env}.kz/api/v1/message/error`, {
|
|
63
65
|
method: 'POST',
|
|
@@ -79,86 +81,90 @@ async function send() {
|
|
|
79
81
|
form.comment = ''
|
|
80
82
|
phone.value = ''
|
|
81
83
|
submitted.value = false
|
|
84
|
+
sending.value = false
|
|
82
85
|
emit('close')
|
|
83
86
|
}
|
|
84
87
|
}
|
|
85
88
|
</script>
|
|
86
89
|
|
|
87
90
|
<template>
|
|
88
|
-
<a-modal v-model="isOpen"
|
|
91
|
+
<a-modal-v2 v-model="isOpen" width="424px">
|
|
89
92
|
<template #header>
|
|
90
|
-
<
|
|
93
|
+
<h2 class="text-base font-semibold text-gray-900 dark:text-gray-100" data-test-id="report-form-title">
|
|
91
94
|
{{ t('modals.report_bug.title') }}
|
|
92
|
-
</
|
|
95
|
+
</h2>
|
|
93
96
|
</template>
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
+
|
|
98
|
+
<div class="flex flex-col gap-5 px-6 py-4">
|
|
99
|
+
<div class="flex flex-col items-center justify-center gap-4">
|
|
100
|
+
<a-ill-stop-sign class="size-[140px]" />
|
|
97
101
|
<slot name="title">
|
|
98
102
|
<p class="label-400 text-center">
|
|
99
103
|
{{ name }}
|
|
100
104
|
</p>
|
|
101
105
|
</slot>
|
|
102
106
|
</div>
|
|
103
|
-
|
|
104
|
-
|
|
107
|
+
|
|
108
|
+
<div class="flex flex-col gap-3">
|
|
109
|
+
<a-input-v2
|
|
105
110
|
v-model="form.name"
|
|
106
111
|
required
|
|
107
|
-
:data-test-id="'report-form-name-input'"
|
|
108
112
|
:label="t('modals.report_bug.labels.name')"
|
|
109
113
|
:error="getError('name')"
|
|
114
|
+
data-test-id="report-form-name-input"
|
|
110
115
|
/>
|
|
111
|
-
<a-input-
|
|
116
|
+
<a-input-v2
|
|
112
117
|
v-model="form.email"
|
|
113
118
|
required
|
|
114
|
-
|
|
119
|
+
type="email"
|
|
115
120
|
:label="t('modals.report_bug.labels.email')"
|
|
116
121
|
:error="getError('email')"
|
|
122
|
+
data-test-id="report-form-email-input"
|
|
117
123
|
/>
|
|
118
|
-
<a-input-
|
|
124
|
+
<a-input-v2
|
|
119
125
|
v-model="phone"
|
|
120
|
-
v-maska
|
|
121
126
|
required
|
|
122
|
-
|
|
127
|
+
type="tel"
|
|
123
128
|
clearable
|
|
124
|
-
|
|
129
|
+
mask="8 (###) ### ## ##"
|
|
125
130
|
:label="t('modals.report_bug.labels.phone')"
|
|
126
131
|
:error="getError('phone_number')"
|
|
132
|
+
data-test-id="report-form-phone-input"
|
|
133
|
+
/>
|
|
134
|
+
<a-textarea-v2
|
|
135
|
+
v-model="form.comment"
|
|
136
|
+
required
|
|
137
|
+
:rows="4"
|
|
138
|
+
resize="none"
|
|
139
|
+
:label="t('modals.report_bug.labels.comment')"
|
|
140
|
+
:error="getError('comment')"
|
|
141
|
+
data-test-id="report-form-comment-textarea"
|
|
127
142
|
/>
|
|
128
|
-
<div>
|
|
129
|
-
<a-textarea
|
|
130
|
-
v-model="form.comment"
|
|
131
|
-
required
|
|
132
|
-
:data-test-id="'report-form-comment-textarea'"
|
|
133
|
-
:label="t('modals.report_bug.labels.comment')"
|
|
134
|
-
size="md"
|
|
135
|
-
:error="getError('comment')"
|
|
136
|
-
class="min-h-[120px]"
|
|
137
|
-
:resizeable="false"
|
|
138
|
-
/>
|
|
139
|
-
</div>
|
|
140
143
|
</div>
|
|
141
144
|
</div>
|
|
145
|
+
|
|
142
146
|
<template #footer>
|
|
143
|
-
<div class="flex
|
|
144
|
-
<a-button
|
|
147
|
+
<div class="flex gap-2">
|
|
148
|
+
<a-button-v2
|
|
145
149
|
view="outline"
|
|
146
|
-
|
|
150
|
+
color="blue"
|
|
151
|
+
block
|
|
147
152
|
data-test-id="report-form-close-button"
|
|
148
153
|
@click="emit('close')"
|
|
149
154
|
>
|
|
150
155
|
{{ t('actions.close') }}
|
|
151
|
-
</a-button>
|
|
152
|
-
<a-button
|
|
153
|
-
|
|
156
|
+
</a-button-v2>
|
|
157
|
+
<a-button-v2
|
|
158
|
+
block
|
|
159
|
+
:loading="sending"
|
|
154
160
|
data-test-id="report-form-submit-button"
|
|
155
161
|
@click="send"
|
|
156
162
|
>
|
|
157
163
|
{{ t('actions.send') }}
|
|
158
|
-
</a-button>
|
|
164
|
+
</a-button-v2>
|
|
159
165
|
</div>
|
|
160
166
|
</template>
|
|
161
|
-
</a-modal>
|
|
167
|
+
</a-modal-v2>
|
|
162
168
|
</template>
|
|
163
169
|
|
|
164
170
|
<style scoped></style>
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
|
|
3
2
|
import { useUrls } from '#adata-ui/composables/useUrls'
|
|
4
3
|
|
|
5
4
|
const emit = defineEmits<{
|
|
@@ -17,33 +16,33 @@ function runTimer() {
|
|
|
17
16
|
const intervalId = setInterval(() => {
|
|
18
17
|
if (timer.value <= 0) {
|
|
19
18
|
isResend.value = true
|
|
20
|
-
clearInterval(intervalId)
|
|
21
|
-
return
|
|
19
|
+
clearInterval(intervalId)
|
|
20
|
+
return
|
|
22
21
|
}
|
|
23
|
-
timer.value
|
|
24
|
-
}, 1000)
|
|
22
|
+
timer.value--
|
|
23
|
+
}, 1000)
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
onMounted(() => resend())
|
|
28
27
|
|
|
29
|
-
|
|
28
|
+
function resend() {
|
|
30
29
|
isResend.value = false
|
|
31
30
|
runTimer()
|
|
32
31
|
emit('resend')
|
|
33
32
|
}
|
|
34
|
-
|
|
33
|
+
function goToMain() {
|
|
35
34
|
if (route.query.url) {
|
|
36
35
|
document.location.replace(route.query.url)
|
|
37
|
-
}
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
38
|
document.location.replace(landing)
|
|
39
39
|
}
|
|
40
40
|
emit('close')
|
|
41
|
-
|
|
42
41
|
}
|
|
43
42
|
</script>
|
|
44
43
|
|
|
45
44
|
<template>
|
|
46
|
-
<div class="flex flex-col
|
|
45
|
+
<div class="flex flex-col items-center justify-center gap-5">
|
|
47
46
|
<p class="heading-02">
|
|
48
47
|
{{ $t('login.modal.title') }}
|
|
49
48
|
</p>
|
|
@@ -57,65 +57,68 @@ onMounted(() => {
|
|
|
57
57
|
</script>
|
|
58
58
|
|
|
59
59
|
<template>
|
|
60
|
-
<a-modal
|
|
60
|
+
<a-modal-v2
|
|
61
61
|
v-model="isOpen"
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
width="424px"
|
|
63
|
+
persistent
|
|
64
64
|
>
|
|
65
|
-
<div class="flex flex-col gap-5">
|
|
66
|
-
<p class="text-center
|
|
65
|
+
<div class="flex flex-col gap-5 px-6 py-4">
|
|
66
|
+
<p class="heading-02 text-center">
|
|
67
67
|
{{ t('modals.submit_application_modal.title') }}
|
|
68
68
|
</p>
|
|
69
69
|
<a-ill-mail class="w-full" />
|
|
70
|
-
<div class="flex flex-col gap-
|
|
71
|
-
<a-input-
|
|
70
|
+
<div class="flex flex-col gap-3">
|
|
71
|
+
<a-input-v2
|
|
72
72
|
v-model="form.name"
|
|
73
|
+
required
|
|
73
74
|
:label="t('modals.submit_application_modal.labels.name')"
|
|
74
75
|
:error="getError('name')"
|
|
75
|
-
required
|
|
76
76
|
/>
|
|
77
|
-
<a-input-
|
|
77
|
+
<a-input-v2
|
|
78
78
|
v-model="form.email"
|
|
79
|
+
required
|
|
80
|
+
type="email"
|
|
79
81
|
:label="t('modals.submit_application_modal.labels.email')"
|
|
80
82
|
:error="getError('email')"
|
|
81
|
-
required
|
|
82
83
|
/>
|
|
83
|
-
<a-input-
|
|
84
|
+
<a-input-v2
|
|
84
85
|
v-model="form.phone"
|
|
85
|
-
|
|
86
|
+
required
|
|
87
|
+
type="tel"
|
|
88
|
+
clearable
|
|
89
|
+
mask="8 (###) ###-##-##"
|
|
86
90
|
:label="t('modals.submit_application_modal.labels.phone')"
|
|
87
|
-
data-maska="8 (###) ###-##-##"
|
|
88
91
|
:error="getError('phone')"
|
|
89
|
-
clearable
|
|
90
|
-
required
|
|
91
92
|
/>
|
|
92
|
-
<a-textarea
|
|
93
|
+
<a-textarea-v2
|
|
93
94
|
v-model="form.comment"
|
|
95
|
+
required
|
|
96
|
+
:rows="4"
|
|
97
|
+
resize="none"
|
|
94
98
|
:label="t('modals.submit_application_modal.labels.comment')"
|
|
95
99
|
:error="getError('comment')"
|
|
96
|
-
resizeable="none"
|
|
97
|
-
required
|
|
98
100
|
/>
|
|
99
101
|
</div>
|
|
100
102
|
</div>
|
|
101
103
|
<template #footer>
|
|
102
|
-
<div class="flex gap-2
|
|
103
|
-
<a-button
|
|
104
|
+
<div class="flex gap-2">
|
|
105
|
+
<a-button-v2
|
|
104
106
|
view="outline"
|
|
107
|
+
color="blue"
|
|
105
108
|
block
|
|
106
109
|
@click="cancel"
|
|
107
110
|
>
|
|
108
111
|
{{ t('reuse.close') }}
|
|
109
|
-
</a-button>
|
|
110
|
-
<a-button
|
|
112
|
+
</a-button-v2>
|
|
113
|
+
<a-button-v2
|
|
111
114
|
block
|
|
112
115
|
@click="send"
|
|
113
116
|
>
|
|
114
117
|
{{ t('reuse.send') }}
|
|
115
|
-
</a-button>
|
|
118
|
+
</a-button-v2>
|
|
116
119
|
</div>
|
|
117
120
|
</template>
|
|
118
|
-
</a-modal>
|
|
121
|
+
</a-modal-v2>
|
|
119
122
|
</template>
|
|
120
123
|
|
|
121
124
|
<style scoped>
|
|
@@ -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">
|