This commit is contained in:
Никита Круглицкий 2024-11-26 18:01:19 +03:00
parent e87c62aaeb
commit 75cdae594a
17 changed files with 29 additions and 424 deletions

8
app.config.ts Normal file
View File

@ -0,0 +1,8 @@
export default defineAppConfig({
instagramUrl: 'https://www.instagram.com/samatk7/?igsh=Mm9keGxnbXBrdGQ4',
telegramUrl: 'https://t.me/+vOQDGC3VoUJmY2Qy',
whatsappUrl: 'https://api.whatsapp.com/send/?phone=7077407714&text&type=phone_number&app_absent=0',
phoneNumber: '+7 (705) 400 20 66',
email: 'support@quantumbot.kz',
appUrl: 'https://app.quantumbot.kz',
})

View File

@ -13,13 +13,13 @@
</div> </div>
<div class="footer__socials"> <div class="footer__socials">
<SocialLink href="https://www.instagram.com/samatk7/?igsh=Mm9keGxnbXBrdGQ4"> <SocialLink :href="app.instagramUrl">
<IMonoInstagram /> <IMonoInstagram />
</SocialLink> </SocialLink>
<SocialLink href="https://t.me/+vOQDGC3VoUJmY2Qy"> <SocialLink :href="app.telegramUrl">
<IMonoTelegram /> <IMonoTelegram />
</SocialLink> </SocialLink>
<SocialLink href="https://api.whatsapp.com/send/?phone=7077407714&text&type=phone_number&app_absent=0"> <SocialLink :href="app.whatsappUrl">
<IMonoWhatsapp /> <IMonoWhatsapp />
</SocialLink> </SocialLink>
</div> </div>
@ -35,6 +35,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
const app = useAppConfig()
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@ -12,13 +12,13 @@
</a> </a>
<div class="header__socials"> <div class="header__socials">
<SocialLink href="https://www.instagram.com/samatk7/?igsh=Mm9keGxnbXBrdGQ4"> <SocialLink :href="app.instagramUrl">
<IMonoInstagram /> <IMonoInstagram />
</SocialLink> </SocialLink>
<SocialLink href="https://t.me/+vOQDGC3VoUJmY2Qy"> <SocialLink :href="app.telegramUrl">
<IMonoTelegram /> <IMonoTelegram />
</SocialLink> </SocialLink>
<SocialLink href="https://api.whatsapp.com/send/?phone=7077407714&text&type=phone_number&app_absent=0"> <SocialLink :href="app.whatsappUrl">
<IMonoWhatsapp /> <IMonoWhatsapp />
</SocialLink> </SocialLink>
</div> </div>
@ -32,7 +32,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import SocialLink from '~/components/social-link.vue' const app = useAppConfig()
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@ -10,8 +10,8 @@
Остались вопросы? Остались вопросы?
</div> </div>
<a class="any-questions__link" href="mailto:support@quantumbot.kz"> <a class="any-questions__link" :href="`mailto:${app.email}`">
support@quantumbot.kz {{ app.email }}
</a> </a>
</div> </div>
</div> </div>
@ -29,6 +29,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
const app = useAppConfig()
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@ -72,7 +72,7 @@
<li>Просмотрите пример добавления товаров и управления ими через приложение</li> <li>Просмотрите пример добавления товаров и управления ими через приложение</li>
</ul> </ul>
<UiButton class="how-it-works-demo__action" href="https://app.quantumbot.kz" rel="noopener noreferrer"> <UiButton class="how-it-works-demo__action" :href="app.appUrl" rel="noopener noreferrer">
Попробовать бесплатно Попробовать бесплатно
</UiButton> </UiButton>
</div> </div>
@ -80,6 +80,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
const app = useAppConfig()
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@ -19,7 +19,7 @@
<li>Высокая скорость обновления цен</li> <li>Высокая скорость обновления цен</li>
</ul> </ul>
<UiButton class="tariff__action" href="https://app.quantumbot.kz" rel="noopener noreferrer"> <UiButton class="tariff__action" :href="app.appUrl" rel="noopener noreferrer">
Попробовать 7 дней бесплатно Попробовать 7 дней бесплатно
</UiButton> </UiButton>
</div> </div>
@ -27,6 +27,7 @@
</template> </template>
<script setup> <script setup>
const app = useAppConfig()
const img = useImage() const img = useImage()
const desktopBg = computed(() => { const desktopBg = computed(() => {

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="info-button"> <div class="info-button">
<UiButton class="info-button__button" href="https://app.quantumbot.kz" rel="noopener noreferrer"> <UiButton class="info-button__button" :href="app.appUrl" rel="noopener noreferrer">
<slot>Начать бесплатно</slot> <slot>Начать бесплатно</slot>
</UiButton> </UiButton>
@ -22,6 +22,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { Tooltip } from 'floating-vue' import { Tooltip } from 'floating-vue'
const app = useAppConfig()
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@ -2,13 +2,14 @@
<div class="phone-number"> <div class="phone-number">
<IDuoPhone class="phone-number__icon" /> <IDuoPhone class="phone-number__icon" />
<a href="tel:+77054002066" class="phone-number__number"> <a :href="`tel:${app.phoneNumber}`" class="phone-number__number">
+7 (705) 400 20 66 {{ app.phoneNumber }}
</a> </a>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
const app = useAppConfig()
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@ -1,128 +0,0 @@
<template>
<Transition
:name="cn.b()"
@before-leave="$emit('close')"
@after-leave="$emit('destroy')"
>
<UiAlert
v-show="visible"
:id="id"
:class="[cn.b(), verticalSide, horizontalSide]"
:title="title"
:type="type"
@mouseenter="clearTimer"
@mouseleave="startTimer"
>
<slot />
</UiAlert>
</Transition>
</template>
<script setup lang="ts">
import type { NotificationPlacement, NotificationType } from './types'
import { computed, onMounted, ref } from 'vue'
export interface Props {
duration?: number
id?: string | number
offset?: number
placement?: NotificationPlacement
type?: NotificationType
title?: string
zIndex?: number
}
defineOptions({
name: 'UiNotification',
})
const props = withDefaults(defineProps<Props>(), {
type: 'neutral',
duration: 5000,
offset: 0,
placement: 'top-right',
zIndex: 0,
})
defineEmits(['close', 'destroy'])
const cn = useClassname('ui-notification')
const timerId = ref()
const visible = ref(false)
const verticalSide = computed(() => props.placement.split('-')[0])
const horizontalSide = computed(() => props.placement.split('-')[1])
function close() {
visible.value = false
}
function startTimer() {
if (props.duration > 0) {
timerId.value = setTimeout(() => {
if (visible.value)
close()
}, props.duration)
}
}
function clearTimer() {
if (timerId.value)
clearTimeout(timerId.value)
}
onMounted(() => {
startTimer()
visible.value = true
})
defineExpose({
visible,
close,
})
</script>
<style lang="scss">
.ui-notification {
position: fixed;
transition-duration: $transition-duration;
transition-property: opacity, transform, left, right, top, bottom;
z-index: calc(8000 + v-bind('zIndex'));
width: 400px;
transform-origin: right top;
@include mobile {
width: calc(100% - 32px);
}
&.top {
top: calc(v-bind('`${offset}px`') + 80px);
@include mobile {
top: auto;
bottom: v-bind('`${offset}px`');
}
}
&.bottom {
bottom: v-bind('`${offset}px`');
}
&.left {
left: 32px;
@include mobile {
left: 16px;
}
}
&.right {
right: 32px;
@include mobile {
right: 16px;
}
}
}
</style>

View File

@ -1,132 +0,0 @@
import { createVNode, render } from 'vue'
import { defu } from 'defu'
import NotificationConstructor from './notification.vue'
import type {
NotificationOptions,
NotificationPlacement,
NotificationQueue,
} from './types'
const notifications: Record<NotificationPlacement, NotificationQueue> = {
'top-left': [],
'top-right': [],
'bottom-left': [],
'bottom-right': [],
}
const GAP_SIZE = 16
let SEED = 1
const DEFAULT_OPTIONS: NotificationOptions = {
text: '',
placement: 'top-right',
duration: 5000,
onClose: () => {},
}
const notify = function (options: NotificationOptions, context = null) {
// if (process.server) return { close: () => undefined };
options = defu(options, DEFAULT_OPTIONS)
const orientedNotifications = notifications[options.placement!]
const id = options.id
? `${options.placement!}_${options.id}`
: `notification_${SEED++}`
const idx = orientedNotifications.findIndex(
({ vm }) => vm.component?.props.id === id,
)
if (idx > -1)
return
let verticalOffset = options.offset || 0
notifications[options.placement!].forEach(({ vm }) => {
verticalOffset += (vm.el?.offsetHeight || 0) + GAP_SIZE
})
verticalOffset += GAP_SIZE
const userOnClose = options.onClose
const props = {
...options,
offset: verticalOffset,
id,
onClose: () => {
close(id, options.placement!, userOnClose)
},
}
const container = document.createElement('div')
const vm = createVNode(
NotificationConstructor,
props,
options.text
? {
default: () => options.text,
}
: null,
)
vm.appContext = context ?? notify._context
vm.props!.onDestroy = () => {
render(null, container)
}
render(vm, container)
notifications[options.placement!].push({ vm })
document.body.appendChild(container.firstElementChild!)
return {
close: () => {
vm.component!.exposed!.close()
},
}
}
export function close(
id: NotificationOptions['id'],
placement: NotificationOptions['placement'],
userOnClose: NotificationOptions['onClose'],
) {
const orientedNotifications = notifications[placement!]
const idx = orientedNotifications.findIndex(
({ vm }) => vm.component?.props.id === id,
)
if (idx === -1)
return
const { vm } = orientedNotifications[idx]
if (!vm)
return
userOnClose?.(vm)
const removedHeight = vm.el!.offsetHeight
const verticalPos = placement!.split('-')[0]
orientedNotifications.splice(idx, 1)
if (orientedNotifications.length < 1)
return
for (let i = idx; i < orientedNotifications.length; i++) {
const { el, component } = orientedNotifications[i].vm
const styles = getComputedStyle(el as Element)
const pos = Number.parseInt(styles.getPropertyValue(verticalPos), 10)
component!.props.offset = pos - removedHeight - GAP_SIZE
}
}
export function closeAll() {
for (const orientedNotifications of Object.values(notifications)) {
orientedNotifications.forEach(({ vm }) => {
vm.component!.exposed!.close()
})
}
}
notify.closeAll = closeAll
notify._context = null
export default notify

View File

@ -1,33 +0,0 @@
import type { VNode } from 'vue'
export const NOTIFICATION_TYPES = [
'neutral',
'positive',
'warning',
'negative',
] as const
export type NotificationType = (typeof NOTIFICATION_TYPES)[number]
export type NotificationPlacement =
| 'top-right'
| 'top-left'
| 'bottom-right'
| 'bottom-left'
export interface NotificationOptions {
type?: NotificationType
text: string
title?: string
duration?: number
placement?: NotificationPlacement
id?: string | number
offset?: number
onClose?: (vm?: VNode) => void
}
export interface NotificationItem {
vm: VNode
}
export type NotificationQueue = NotificationItem[]

View File

@ -1,44 +0,0 @@
import { useField } from 'vee-validate'
import { computed, ref, toRef } from 'vue'
import type { ComponentInternalInstance } from 'vue'
interface Props {
id: string
label?: string
disabled?: boolean
modelValue?: boolean | string | number
trueValue?: boolean | string | number
falseValue?: boolean | string | number
required?: boolean
}
export default (props: Props, slots: ComponentInternalInstance['slots']) => {
const { checked, errorMessage, handleChange } = useField(
toRef(props, 'id'),
computed(() => ({
required: props.required,
})),
{
type: 'checkbox',
initialValue: props.modelValue,
checkedValue: props.trueValue ?? true,
uncheckedValue: props.falseValue ?? false,
syncVModel: true,
},
)
const focused = ref(false)
const active = computed(() => !props.disabled)
const invalid = computed(() => active.value && !!errorMessage.value)
const hasLabel = computed(() => !!props.label || slots.default)
return {
focused,
checked,
active,
invalid,
hasLabel,
handleChange,
}
}

View File

@ -1,3 +0,0 @@
import notify from '../components/ui/notification/notify'
export default () => notify

View File

@ -1,40 +0,0 @@
import { useField } from 'vee-validate'
import { computed, ref, toRef } from 'vue'
import type { ComponentInternalInstance } from 'vue'
interface Props {
id: string
value: string | number
label?: string
disabled?: boolean
modelValue?: string | number
required?: boolean
}
export default (props: Props, slots: ComponentInternalInstance['slots']) => {
const { checked, handleChange } = useField(
toRef(props, 'id'),
computed(() => ({
required: props.required,
})),
{
type: 'radio',
initialValue: props.modelValue,
checkedValue: props.value,
syncVModel: true,
},
)
const focused = ref(false)
const active = computed(() => !props.disabled)
const hasLabel = computed(() => !!props.label || slots.default)
return {
focused,
active,
checked,
hasLabel,
handleChange,
}
}

View File

@ -1,7 +0,0 @@
import notify from '../components/ui/notification/notify'
export default defineNuxtPlugin((nuxtApp) => {
notify._context = nuxtApp.vueApp._context
return {}
})

View File

@ -4,7 +4,6 @@
@use 'mixins'; @use 'mixins';
@use "normalize"; @use "normalize";
@use "utility"; @use "utility";
@use "typography";
@use "floating-vue"; @use "floating-vue";
*, *,

View File

@ -1,22 +0,0 @@
@use "./mixins" as *;
p, h1, h2, h3, h4, h5, h6 {
margin: 0;
}
//a {
// @include font(18px, 500, 23px);
//
// color: inherit;
// text-decoration: none;
// transition: color .2s ease-out;
// text-underline-offset: 2px;
//
// &:hover {
// color: #EE6A32;
// }
//
// &:active {
// color: #F57B47;
// }
//}