This commit is contained in:
Binary file not shown.
@@ -1,19 +1,17 @@
|
||||
<template>
|
||||
<div class="flex row items-center no-wrap logo-component">
|
||||
<LogoIcon
|
||||
v-bind="$attrs"
|
||||
class="logo-svg"
|
||||
:class="{
|
||||
'is-animated': animated,
|
||||
'hide-bg': !withBackground
|
||||
}"
|
||||
:style="{ color: iconColor }"
|
||||
:style="{ color: iconColor, transform: 'scale(1.08)', height: '1cap' }"
|
||||
/>
|
||||
|
||||
<div
|
||||
:class="'text-' + textColor"
|
||||
class="logo-text"
|
||||
style="margin-left: 0.05em;"
|
||||
style="margin-left: 0.15em;"
|
||||
>
|
||||
<span>tg</span>
|
||||
<span class="text-bold">Crew</span>
|
||||
@@ -23,7 +21,6 @@
|
||||
|
||||
<script setup>
|
||||
import LogoIcon from 'components/BaseLogoSvg.vue'
|
||||
|
||||
defineProps({
|
||||
textColor: {
|
||||
type: String,
|
||||
@@ -45,38 +42,4 @@
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.logo-svg {
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
|
||||
:deep(.logo-bg) {
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
&.hide-bg :deep(.logo-bg) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
:deep(.x), :deep(.c) {
|
||||
transform-box: fill-box;
|
||||
}
|
||||
|
||||
&.is-animated {
|
||||
:deep(.x) { animation: expand-r 6s ease-in-out infinite; }
|
||||
:deep(.c) { animation: o 6s ease-in-out infinite; }
|
||||
:deep(.l) {
|
||||
stroke-dasharray: 1;
|
||||
stroke-dashoffset: 1;
|
||||
stroke-width: 1.5;
|
||||
animation: draw 6s ease-in-out infinite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes expand-r { 50% { r: 8px; } }
|
||||
@keyframes o { 50% { transform: var(--t); } }
|
||||
@keyframes draw {
|
||||
0%, 100% { stroke-dashoffset: 0; }
|
||||
50% { stroke-dashoffset: 1; }
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,13 +2,22 @@
|
||||
<svg
|
||||
viewBox="0 0 32 32"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
v-bind="$attrs"
|
||||
:class="{ 'is-animated': animated }"
|
||||
class="base-logo-svg"
|
||||
>
|
||||
<defs>
|
||||
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="logo-gradient">
|
||||
<stop stop-color="#2AABEE" offset="0%"></stop>
|
||||
<stop stop-color="#229ED9" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<radialGradient id="logo-gradient" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
|
||||
<stop offset="0%" stop-color="white" />
|
||||
<stop offset="25%" stop-color="var(--q-primary, #1976D2)" stop-opacity="0.5" />
|
||||
<stop offset="50%" stop-color="var(--q-primary, #1976D2)">
|
||||
<animate
|
||||
attributeName="offset"
|
||||
values="1;0.6;1"
|
||||
dur="6s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</stop>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<rect class="logo-bg" width="32" height="32" rx="4" ry="4" fill="url(#logo-gradient)" />
|
||||
@@ -26,8 +35,35 @@
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineOptions({
|
||||
inheritAttrs: false
|
||||
})
|
||||
<script setup>
|
||||
defineProps({
|
||||
animated: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.base-logo-svg {
|
||||
.x, .c { transform-box: fill-box; }
|
||||
.l { stroke-width: 1.5; }
|
||||
|
||||
&.is-animated {
|
||||
.x { animation: expand-r 6s infinite; }
|
||||
.c { animation: o 6s infinite; }
|
||||
.l {
|
||||
stroke-dasharray: 1;
|
||||
stroke-dashoffset: 1;
|
||||
animation: draw 6s infinite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes expand-r { 50% { r: 8px; } }
|
||||
@keyframes o { 50% { transform: var(--t); } }
|
||||
@keyframes draw {
|
||||
0%, 100% { stroke-dashoffset: 0; }
|
||||
50% { stroke-dashoffset: 1; }
|
||||
}
|
||||
</style>
|
||||
179
src/components/CustomTabs.vue
Normal file
179
src/components/CustomTabs.vue
Normal file
@@ -0,0 +1,179 @@
|
||||
<template>
|
||||
<div class="w100">
|
||||
<div class="row justify-center w100">
|
||||
<q-resize-observer @resize="checkSpace" />
|
||||
<div
|
||||
class="tabs-wrapper no-pointer-events"
|
||||
style="position: absolute !important; visibility: hidden; height: 0"
|
||||
>
|
||||
<q-tabs
|
||||
ref="hiddenTabsRef"
|
||||
dense
|
||||
shrink
|
||||
align="center"
|
||||
class="text-grey custom-tabs"
|
||||
active-color="primary"
|
||||
indicator-color="primary"
|
||||
narrow-indicator
|
||||
:arrows="false"
|
||||
:outside-arrows="false"
|
||||
:mobile-arrows="false"
|
||||
>
|
||||
<q-tab
|
||||
v-for="tabItem in tabItems"
|
||||
:key="tabItem.name"
|
||||
:name="tabItem.name"
|
||||
:label="$t(tabItem.label)"
|
||||
class="custom-tab-item"
|
||||
/>
|
||||
</q-tabs>
|
||||
</div>
|
||||
|
||||
<div :class="['tabs-wrapper', fogClass]">
|
||||
<q-tabs
|
||||
v-model="tab"
|
||||
ref="tabsRef"
|
||||
dense
|
||||
shrink
|
||||
align="center"
|
||||
class="text-grey custom-tabs"
|
||||
active-color="primary"
|
||||
indicator-color="primary"
|
||||
narrow-indicator
|
||||
:arrows="false"
|
||||
:outside-arrows="false"
|
||||
:mobile-arrows="false"
|
||||
:no-caps="isOverflowing || useTabsNoCaps"
|
||||
@update:model-value="onTabChange"
|
||||
>
|
||||
<q-tab
|
||||
v-for="tabItem in tabItems"
|
||||
:key="tabItem.name"
|
||||
:name="tabItem.name"
|
||||
:label="$t(tabItem.label)"
|
||||
class="custom-tab-item"
|
||||
/>
|
||||
<q-resize-observer @resize="updateScrollInfo" />
|
||||
</q-tabs>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column items-center w100">
|
||||
<slot :item="activeItem"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, nextTick, onMounted } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
tabItems: { type: Array, required: true },
|
||||
useTabsNoCaps: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:tabs-no-caps'])
|
||||
|
||||
const tab = ref(props.tabItems[0]?.name)
|
||||
const tabsRef = ref(null)
|
||||
const hiddenTabsRef = ref(null)
|
||||
const canScroll = ref(false)
|
||||
const isOverflowing = ref(false)
|
||||
|
||||
const updateScrollInfo = () => {
|
||||
const el = tabsRef.value?.$el.querySelector('.q-tabs__content')
|
||||
if (el) canScroll.value = el.scrollWidth > el.clientWidth + 1
|
||||
}
|
||||
|
||||
const checkSpace = (size) => {
|
||||
const hiddenTabs = hiddenTabsRef.value?.$el.querySelector('.q-tabs__content')
|
||||
if (hiddenTabs) {
|
||||
isOverflowing.value = hiddenTabs.scrollWidth > size.width
|
||||
if (isOverflowing.value !== props.useTabsNoCaps) {
|
||||
emit('update:tabs-no-caps', isOverflowing.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const activeItem = computed(() =>
|
||||
props.tabItems.find(t => t.name === tab.value) || props.tabItems[0]
|
||||
)
|
||||
|
||||
const fogClass = computed(() => {
|
||||
const activeNoCaps = isOverflowing.value || props.useTabsNoCaps
|
||||
if (!canScroll.value || !activeNoCaps) return ''
|
||||
|
||||
const index = props.tabItems.findIndex(t => t.name === tab.value)
|
||||
if (index === 0) return 'is-right'
|
||||
if (index === props.tabItems.length - 1) return 'is-left'
|
||||
return 'is-both'
|
||||
})
|
||||
|
||||
const onTabChange = async() => {
|
||||
await nextTick()
|
||||
if (tabsRef.value) updateScrollInfo()
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await nextTick()
|
||||
updateScrollInfo()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.tabs-wrapper {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 60px;
|
||||
z-index: 10;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.3s;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&::before {
|
||||
left: 0;
|
||||
background: linear-gradient(to right, $grey-11, transparent);
|
||||
}
|
||||
|
||||
&::after {
|
||||
right: 0;
|
||||
background: linear-gradient(to left, $grey-11, transparent);
|
||||
}
|
||||
|
||||
&.is-left::before,
|
||||
&.is-both::before { opacity: 1; }
|
||||
|
||||
&.is-right::after,
|
||||
&.is-both::after { opacity: 1; }
|
||||
}
|
||||
|
||||
.custom-tabs {
|
||||
background: transparent !important;
|
||||
:deep(.q-tabs__content) {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
:deep(.q-tabs__arrow),
|
||||
:deep(.q-icon) {
|
||||
display: none !important;
|
||||
}
|
||||
:deep(.q-tab--no-caps) {
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-tab-item {
|
||||
flex-shrink: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="column bg-grey-4 rounded-card q-px-xl q-pt-md">
|
||||
<div class="row no-wrap items-center q-pb-md">
|
||||
<div class="text-h6" style="flex-grow: 2;">
|
||||
{{ $t(question) }}
|
||||
{{ $t(question ?? '') }}
|
||||
</div>
|
||||
<div class="q-pl-md">
|
||||
<q-btn
|
||||
@@ -20,7 +20,7 @@
|
||||
<q-slide-transition>
|
||||
<div v-if="showAnswer">
|
||||
<div class="text-body text-grey-9 q-pb-md">
|
||||
{{ $t(answer) }}
|
||||
{{ $t(answer ?? '') }}
|
||||
</div>
|
||||
</div>
|
||||
</q-slide-transition>
|
||||
@@ -28,7 +28,7 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineProps({
|
||||
|
||||
@@ -1,48 +1,74 @@
|
||||
<template>
|
||||
<div class="w100 flex justify-between text-caption q-pa-md q-gutter-y-md text-grey-9">
|
||||
<div class="flex column col-12 col-sm">
|
||||
<base-logo class="text-body1 q-pb-sm"/>
|
||||
<div class="flex items-center">
|
||||
<q-icon name="mdi-map-marker-outline" color="brand" class="q-pr-sm"/>
|
||||
<span>{{ $t('footer__contacts_location') }}</span>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<q-icon name="mdi-phone-outline" color="brand" class="q-pr-sm" />
|
||||
<span>+7 (916) 439-04-25</span>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<q-icon name="mdi-email-outline" color="brand" class="q-pr-sm"/>
|
||||
<a
|
||||
href="mailto:info@tgcrew.ru"
|
||||
style="text-decoration: none; color: inherit"
|
||||
>
|
||||
info@tgcrew.ru
|
||||
</a>
|
||||
<div class="q-pl-sm">
|
||||
<base-logo
|
||||
withBackground
|
||||
animated
|
||||
class="text-h4"
|
||||
/>
|
||||
<div class="flex items-center no-wrap">
|
||||
<q-icon name="mdi-map-marker-outline" color="brand" class="q-pr-sm"/>
|
||||
<span>{{ $t('footer__contacts_location') }}</span>
|
||||
</div>
|
||||
<div class="flex items-center no-wrap">
|
||||
<q-icon name="mdi-phone-outline" color="brand" class="q-pr-sm" />
|
||||
<span>+7 (916) 439-04-25</span>
|
||||
</div>
|
||||
<div class="flex items-center no-wrap">
|
||||
<q-icon name="mdi-email-outline" color="brand" class="q-pr-sm"/>
|
||||
<a
|
||||
href="mailto:info@tgcrew.ru"
|
||||
style="text-decoration: none; color: inherit"
|
||||
>
|
||||
info@tgcrew.ru
|
||||
</a>
|
||||
</div>
|
||||
<br>
|
||||
<div class="">
|
||||
{{ $t('footer__contacts_ogrnip') + ' ' }}
|
||||
<span class="text-no-wrap">
|
||||
318774600262084
|
||||
</span>
|
||||
</div>
|
||||
<div class="">
|
||||
{{ $t('footer__contacts_inn') + ' ' }}
|
||||
<span class="text-no-wrap">
|
||||
366316608346
|
||||
</span>
|
||||
</div>
|
||||
<div class="">
|
||||
{{ $t('footer__contacts_pdn') + ' ' }}
|
||||
<span class="text-no-wrap">
|
||||
77-25-471585
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex column col-12 col-sm">
|
||||
<span class="text-body1 text-bold">
|
||||
{{ $t('footer__docs') }}
|
||||
</span>
|
||||
<div
|
||||
v-for="item in docs"
|
||||
:key="item.id"
|
||||
class="text-caption"
|
||||
>
|
||||
<span
|
||||
@click="router.push({ name: item.route })"
|
||||
class="cursor-pointer"
|
||||
style="text-decoration: underline; color: inherit"
|
||||
>
|
||||
{{ $t(item.translationKey) }}
|
||||
<div class="q-pl-sm">
|
||||
<span class="text-body1 text-bold">
|
||||
{{ $t('footer__docs') }}
|
||||
</span>
|
||||
<div
|
||||
v-for="item in docs"
|
||||
:key="item.id"
|
||||
class="text-caption"
|
||||
>
|
||||
<span
|
||||
@click="router.push({ name: item.route })"
|
||||
class="cursor-pointer"
|
||||
style="text-decoration: underline; color: inherit"
|
||||
>
|
||||
{{ $t(item.translationKey) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex column col-12 col-sm">
|
||||
<div class="text-grey">
|
||||
<div class="text-grey q-pl-sm">
|
||||
{{ $t('footer__description_user_data') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,71 +1,56 @@
|
||||
<template>
|
||||
<slide-template
|
||||
title="how_it_works__title"
|
||||
>
|
||||
<q-tabs
|
||||
v-model="tab"
|
||||
dense
|
||||
class="text-grey"
|
||||
active-color="primary"
|
||||
indicator-color="primary"
|
||||
align="justify"
|
||||
narrow-indicator
|
||||
outside-arrows
|
||||
mobile-arrows
|
||||
<slide-template
|
||||
title="how_it_works__title"
|
||||
class="overflow-hidden w100"
|
||||
>
|
||||
<q-tab
|
||||
v-for="tab in tabs"
|
||||
:key="tab.name"
|
||||
:name="tab.name"
|
||||
:label="$t(tab.label)"
|
||||
/>
|
||||
</q-tabs>
|
||||
|
||||
<q-tab-panels
|
||||
v-model="tab"
|
||||
animated
|
||||
class="w60 bg-transparent"
|
||||
>
|
||||
<q-tab-panel
|
||||
v-for="tab in tabs"
|
||||
:key="tab.name"
|
||||
:name="tab.name"
|
||||
<custom-tabs
|
||||
v-bind="$attrs"
|
||||
:tabItems
|
||||
:useTabsNoCaps
|
||||
>
|
||||
<div
|
||||
class="relative-position w100">
|
||||
<div
|
||||
class="abolute-position w100"
|
||||
style="height: 60vh;"
|
||||
>
|
||||
<iframe
|
||||
src="https://kinescope.io/embed/5yesvjfi6XAYRuxzbsYGHZ"
|
||||
allow="autoplay; fullscreen; picture-in-picture; encrypted-media; gyroscope; accelerometer; clipboard-write; screen-wake-lock; fullscreen;" frameborder="0" style="position: absolute; width: 100%; height: 100%; top: 0; left: 0;"
|
||||
>
|
||||
</iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <q-skeleton
|
||||
style="height: calc(100vw * 0.5 / 16 * 9)"
|
||||
square
|
||||
/> -->
|
||||
</q-tab-panel>
|
||||
</q-tab-panels>
|
||||
</slide-template>
|
||||
|
||||
<template #default="{ item }">
|
||||
<div class="relative-position w60 q-pt-md">
|
||||
<div style="height: 60vh; position: relative;">
|
||||
<iframe
|
||||
:key="item.name"
|
||||
:src="item.url"
|
||||
allow="autoplay; fullscreen; picture-in-picture; encrypted-media; gyroscope; accelerometer; clipboard-write; screen-wake-lock; fullscreen;"
|
||||
frameborder="0"
|
||||
style="position: absolute; width: 100%; height: 100%; top: 0; left: 0;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</custom-tabs>
|
||||
</slide-template>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
<script setup>
|
||||
import SlideTemplate from 'components/SlideTemplate.vue'
|
||||
const tabs = [
|
||||
{ name: 'intro', label: 'how_it_works__intro'},
|
||||
{ name: 'admin', label: 'how_it_works__admin'},
|
||||
{ name: 'user', label: 'how_it_works__user'}
|
||||
import CustomTabs from 'components/CustomTabs.vue'
|
||||
|
||||
defineProps({
|
||||
useTabsNoCaps: Boolean
|
||||
})
|
||||
|
||||
const tabItems = [
|
||||
{
|
||||
name: 'intro',
|
||||
label: 'how_it_works__intro',
|
||||
url: 'https://kinescope.io/embed/5yesvjfi6XAYRuxzbsYGHZ'
|
||||
},
|
||||
{
|
||||
name: 'admin',
|
||||
label: 'how_it_works__admin',
|
||||
url: 'https://kinescope.io/embed/5yesvjfi6XAYRuxzbsYGHZ'
|
||||
},
|
||||
{
|
||||
name: 'user',
|
||||
label: 'how_it_works__user',
|
||||
url: 'https://kinescope.io/embed/5yesvjfi6XAYRuxzbsYGHZ'
|
||||
}
|
||||
]
|
||||
const tab = ref(tabs[0].name)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
<style scoped lang="scss">
|
||||
</style>
|
||||
|
||||
@@ -3,92 +3,87 @@
|
||||
title="price__title"
|
||||
subtitle="price__subtitle"
|
||||
>
|
||||
<q-tabs
|
||||
v-model="tab"
|
||||
dense
|
||||
class="text-grey"
|
||||
active-color="primary"
|
||||
indicator-color="primary"
|
||||
align="justify"
|
||||
narrow-indicator
|
||||
<custom-tabs
|
||||
v-bind="$attrs"
|
||||
:tabItems
|
||||
:useTabsNoCaps
|
||||
>
|
||||
<q-tab
|
||||
v-for="tab in tabs"
|
||||
:key="tab.name"
|
||||
:name="tab.name"
|
||||
:label="$t(tab.label)"
|
||||
/>
|
||||
</q-tabs>
|
||||
<div
|
||||
class="fit row q-pb-lg"
|
||||
>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12 q-pa-lg"
|
||||
v-for="(item, idx) in tariff"
|
||||
:key="idx"
|
||||
>
|
||||
<price-section-item
|
||||
:name="item.name"
|
||||
:chats-qty="item.chatsQty"
|
||||
:price="tab === 'legal' ? item.price_rub : item.price"
|
||||
:price_unit="tab === 'legal' ? 'rub' : 'stars'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<q-card
|
||||
flat
|
||||
class="bg-white rounded-card"
|
||||
>
|
||||
<q-item v-if="tab === 'legal'">
|
||||
<q-item-section avatar>
|
||||
<span class="text-h4 text-grey q-px-sm">
|
||||
₽
|
||||
</span>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label class="text-grey">
|
||||
{{ $t('price__rub_pay') }}
|
||||
</q-item-label>
|
||||
<q-item-label class="text-h6">
|
||||
{{ $t('price__rub_resident') }}
|
||||
</q-item-label>
|
||||
<q-item-label class="text-grey">
|
||||
{{ $t('price__rub_closing_documents') }}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item v-if="tab === 'individual'">
|
||||
<q-item-section avatar>
|
||||
<telegram-star color="gold" size="48px"/>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label class="text-grey">
|
||||
{{ $t('price__stars_pay') }}
|
||||
</q-item-label>
|
||||
<q-item-label class="text-h6">
|
||||
Telegram Stars
|
||||
</q-item-label>
|
||||
<q-item-label class="text-grey">
|
||||
{{ $t('price__stars_description') }}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-card>
|
||||
<template #default="{ item }">
|
||||
<div
|
||||
class="fit row q-pb-lg"
|
||||
>
|
||||
<div class="col-md-3 col-sm-6 col-xs-12 q-pa-lg"
|
||||
v-for="(tariff, idx) in tariffs"
|
||||
:key="idx"
|
||||
>
|
||||
<price-section-item
|
||||
:name="tariff.name"
|
||||
:chats-qty="tariff.chatsQty ?? 0"
|
||||
:price="item.name === 'legal' ? tariff.price_rub : tariff.price"
|
||||
:price_unit="item.name === 'legal' ? 'rub' : 'stars'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<q-card
|
||||
flat
|
||||
class="bg-white rounded-card"
|
||||
>
|
||||
<q-item v-if="item.name === 'legal'">
|
||||
<q-item-section avatar>
|
||||
<span class="text-h4 text-grey q-px-sm">
|
||||
₽
|
||||
</span>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label class="text-grey">
|
||||
{{ $t('price__rub_pay') }}
|
||||
</q-item-label>
|
||||
<q-item-label class="text-h6">
|
||||
{{ $t('price__rub_resident') }}
|
||||
</q-item-label>
|
||||
<q-item-label class="text-grey">
|
||||
{{ $t('price__rub_closing_documents') }}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
<q-item v-if="item.name === 'individual'">
|
||||
<q-item-section avatar>
|
||||
<telegram-star color="gold" size="48px"/>
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label class="text-grey">
|
||||
{{ $t('price__stars_pay') }}
|
||||
</q-item-label>
|
||||
<q-item-label class="text-h6">
|
||||
Telegram Stars
|
||||
</q-item-label>
|
||||
<q-item-label class="text-grey">
|
||||
{{ $t('price__stars_description') }}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-card>
|
||||
</template>
|
||||
</custom-tabs>
|
||||
</slide-template>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
<script setup>
|
||||
import SlideTemplate from 'components/SlideTemplate.vue'
|
||||
import CustomTabs from 'components/CustomTabs.vue'
|
||||
import PriceSectionItem from 'components/PriceSectionItem.vue'
|
||||
import telegramStar from 'components/TelegramStar.vue'
|
||||
import TelegramStar from 'components/TelegramStar.vue'
|
||||
|
||||
const tabs = [
|
||||
defineProps({
|
||||
useTabsNoCaps: Boolean
|
||||
})
|
||||
|
||||
const tabItems = [
|
||||
{ name: 'legal', label: 'price__legal'},
|
||||
{ name: 'individual', label: 'price__individual'}
|
||||
]
|
||||
const tab = ref(tabs[0].name)
|
||||
|
||||
const tariff = [
|
||||
const tariffs = [
|
||||
{ id: 1, name: 'TEST', price: null, price_rub: null, chatsQty: 5 },
|
||||
{ id: 2, name: 'START', price: 1000, price_rub: 2000, chatsQty: 15 },
|
||||
{ id: 3, name: 'PRO', price: 5000, price_rub: 10000, chatsQty: 40 },
|
||||
@@ -97,5 +92,5 @@
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style scoped lang="scss">
|
||||
</style>
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
color="primary"
|
||||
/>
|
||||
<div class="text-bold text-h5 text-center q-pt-md">
|
||||
{{ $t(title) }}
|
||||
{{ $t(title ?? '') }}
|
||||
</div>
|
||||
<div class="text-grey text-body1 text-center q-pt-sm text-wrap">
|
||||
{{ $t(description) }}
|
||||
{{ $t(description ?? '') }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,3 +1,40 @@
|
||||
// app global css in SCSS form
|
||||
.bg-t-primary {
|
||||
background: $t-bg-color !important;
|
||||
}
|
||||
|
||||
.bg-t-secondary {
|
||||
background: $t-secondary-bg-color !important;
|
||||
}
|
||||
|
||||
.bg-t-bottom-bar {
|
||||
background: $t-bottom-bar-bg-color !important;
|
||||
}
|
||||
|
||||
.t-section-separator {
|
||||
color: $t-section-separator-color !important;
|
||||
}
|
||||
|
||||
.t-text {
|
||||
color: $t-text-color !important;
|
||||
}
|
||||
|
||||
.t-text-hint {
|
||||
color: $t-hint-color !important;
|
||||
}
|
||||
|
||||
.t-text-subtitle {
|
||||
color: $t-subtitle-text-color !important;
|
||||
}
|
||||
|
||||
.t-text-accent {
|
||||
color: $t-accent-text-color !important;
|
||||
}
|
||||
|
||||
.t-text-section-header {
|
||||
color: $t-section-header-text-color !important;
|
||||
}
|
||||
|
||||
.text-brand {
|
||||
color: $brand !important;
|
||||
}
|
||||
@@ -6,6 +43,18 @@
|
||||
background: $brand !important;
|
||||
}
|
||||
|
||||
.bg-base {
|
||||
background: $base-bg !important;
|
||||
}
|
||||
|
||||
.text-tgcolor {
|
||||
color: $tgcolor !important;
|
||||
}
|
||||
|
||||
.bg-tgcolor {
|
||||
background: $tgcolor !important;
|
||||
}
|
||||
|
||||
$base-width: 100;
|
||||
@while $base-width > 0 {
|
||||
.w#{$base-width} { width: #{$base-width}+'%'; }
|
||||
@@ -14,6 +63,7 @@ $base-width: 100;
|
||||
|
||||
body, html, #q-app {
|
||||
font-family: $typography-font-family;
|
||||
background-color: transparent !important;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
@@ -26,6 +76,26 @@ body, html, #q-app {
|
||||
--body-width: 1240px;
|
||||
--logo-color-bg-white: grey;
|
||||
--base-radius: 24px;
|
||||
|
||||
|
||||
--t-bg-color: #{$t-bg-color};
|
||||
--t-secondary-bg-color: #{$t-secondary-bg-color};
|
||||
--t-section-bg-color: #{$t-section-bg-color};
|
||||
--t-header-bg-color: #{$t-header-bg-color};
|
||||
--t-bottom-bar-bg-color: #{$t-bottom-bar-bg-color};
|
||||
|
||||
--t-text-color: #{$t-text-color};
|
||||
--t-hint-color: #{$t-hint-color};
|
||||
--t-subtitle-text-color: #{$t-subtitle-text-color};
|
||||
--t-accent-text-color: #{$t-accent-text-color};
|
||||
--t-section-header-text-color: #{$t-section-header-text-color};
|
||||
|
||||
--t-button-color: #{$t-button-color};
|
||||
--t-button-text-color: #{$t-button-text-color};
|
||||
--t-link-color: #{$t-link-color};
|
||||
--t-destructive-text-color: #{$t-destructive-text-color};
|
||||
|
||||
--t-section-separator-color: #{$t-section-separator-color};
|
||||
}
|
||||
|
||||
.main-content {
|
||||
|
||||
@@ -1,18 +1,45 @@
|
||||
// Quasar SCSS (& Sass) Variables
|
||||
// --------------------------------------------------
|
||||
// To customize the look and feel of this app, you can override
|
||||
// the Sass/SCSS variables found in Quasar's source Sass/SCSS files.
|
||||
/* telegram theme on Android 2026
|
||||
bg-color: #ffffff
|
||||
secondary-bg-color: #f1f1f3
|
||||
section-bg-color: #ffffff
|
||||
header-bg-color: #ffffff
|
||||
bottom-bar-bg-color: #f1f1f3
|
||||
|
||||
// Check documentation for full list of Quasar variables
|
||||
text-color: #1a1d21
|
||||
hint-color: #a8a8a8
|
||||
subtitle-text-color: #82868a
|
||||
accent-text-color: #1c93e3
|
||||
section-header-text-color: #298acf
|
||||
|
||||
// Your own variables (that are declared here) and Quasar's own
|
||||
// ones will be available out of the box in your .vue/.scss/.sass files
|
||||
|
||||
// It's highly recommended to change the default colors
|
||||
// to match your app's branding.
|
||||
// Tip: Use the "Theme Builder" on Quasar's documentation website.
|
||||
button-color: #229af0
|
||||
button-text-color: #ffffff
|
||||
link-color: #2678b6
|
||||
destructive-text-color: #cc2929
|
||||
|
||||
$primary : #3390ec;
|
||||
section-separator-color: #d9d9d9
|
||||
*/
|
||||
|
||||
$t-bg-color: #ffffff;
|
||||
$t-secondary-bg-color: #f1f1f3;
|
||||
$t-section-bg-color: #ffffff;
|
||||
$t-header-bg-color: #ffffff;
|
||||
$t-bottom-bar-bg-color: #f1f1f3;
|
||||
|
||||
$t-text-color: #1a1d21;
|
||||
$t-hint-color: #a8a8a8;
|
||||
$t-subtitle-text-color: #82868a;
|
||||
$t-accent-text-color: #1c93e3;
|
||||
$t-section-header-text-color: #298acf;
|
||||
|
||||
$t-button-color: #229af0;
|
||||
$t-button-text-color: #ffffff;
|
||||
$t-link-color: #2678b6;
|
||||
$t-destructive-text-color: #cc2929;
|
||||
|
||||
$t-section-separator-color: #d9d9d9;
|
||||
|
||||
$primary : $t-button-color;
|
||||
$secondary : #26A69A;
|
||||
$accent : #9C27B0;
|
||||
|
||||
@@ -20,11 +47,17 @@ $dark : #1D1D1D;
|
||||
$dark-page : #121212;
|
||||
|
||||
$positive : #21BA45;
|
||||
$negative : #C10015;
|
||||
$negative : $t-destructive-text-color;
|
||||
$info : #31CCEC;
|
||||
$warning : #F2C037;
|
||||
|
||||
$lightgrey : $t-secondary-bg-color;
|
||||
|
||||
$brand: #419FD9;
|
||||
$base-bg: #517DA2;
|
||||
|
||||
$typography-font-family : 'myFont', Roboto !default;
|
||||
$tgcolor: #419FD9;
|
||||
|
||||
$body-font-size: var(--dynamic-font-size);
|
||||
|
||||
$typography-font-family: 'myFont', Roboto !default;
|
||||
|
||||
@@ -1 +1 @@
|
||||
export default { EN: 'EN', RU: 'RU', '': '', main__how_it_works: 'How it works', main__price: 'Tariff', main__faq: 'FAQ', main__contacts: 'Contacts', banner__slogan_prepend: 'More than just chats', banner__slogan_body: 'Your project\'s workspace in', banner__main_btn: 'Let’s Fly!', banner__main_btn_description: 'Continue in Telegram', problem__title: 'Keep your workflow, boost your efficiency', problem__1: 'Unified Workspace', problem__1_description: 'Gathers tasks, meetings, and files from all your connected chats into a single, unified space.', problem__2: 'Team Directory', problem__2_description: 'Shows all members and their roles so you can instantly see who is responsible for what.', problem__3: 'Chat Integration', problem__3_description: 'Updates to tasks and meetings appear right in your chats — where the conversation happens.', problem__4: 'Smart Permissions', problem__4_description: 'Everyone sees only their relevant info. We take care of privacy and order, so you don\'t have to.', how_it_works__title: 'How it works', how_it_works__intro: 'Intro', how_it_works__admin: 'Administrator', how_it_works__user: 'Users', price__title: 'Tariff', price__subtitle: 'All plans include full functionality: an unlimited number of users, projects, tasks, and meetings.', price__per_month: 'per month', price__free_tax: 'FREE', price__chats: 'chats', price__stars_pay: 'Payment via', price__stars_description: 'Telegram\'s internal currency', price__legal: 'Business', price__individual: 'Individuals', price__rub_pay: 'Bank transfer', price__rub_resident: 'For RU residents', price__rub_closing_documents: 'Accounting documents', FAQ__title: 'Frequently Asked Questions (FAQ)', faq__question_1: 'Who is this app for?', faq__answer_1: 'For project teams conducting work communications in Telegram: IT development, construction, event industry, consulting, and others. The app creates a unified information environment and is essential when external contractors are involved and parties lack a shared infrastructure for collaboration.', faq__question_2: 'Is it mandatory to grant the bot message access and assign it as an administrator in chats?', faq__answer_2: 'No. Chats can be connected in two modes: "Private" (no message access or admin rights required) and "Full Functionality". The mode can be changed at any time. In Private mode, automatic backup of chat files and their addition to the project\'s general list is not performed.', faq__question_3: 'Can the app be connected to already existing chats?', faq__answer_3: 'Yes, any existing chat can be connected. There is also an option to share a special link with colleagues so they can connect their own chats to the project. No new groups are required — work continues within the familiar Telegram interface with added features for managing multiple project chats.', faq__question_4: 'Where are my files and data stored?', faq__answer_4: 'The app does not store chat history. Files are kept within Telegram or cloud services (Yandex Disk or Google Drive).', faq__question_5: 'Why is some contact information in the address book not displayed fully?', faq__answer_5: 'Contact details are displayed only with the user\'s consent and depend on the information provided by the administrator.', faq__question_6: 'Can participants of one project see data from another, and can one chat be linked to multiple projects?', faq__answer_6: 'A chat can be linked to only one project to maintain order. Projects are completely independent: information (chats, files, tasks, contacts) never overlaps. The app provides quick switching between workspaces, displaying only the information relevant to the currently selected project.', faq__question_7: 'What happens if I remove the app\'s bot from a chat or stop tracking in the app?', faq__answer_7: 'New messages and files will no longer be processed, and it will become impossible to attach tasks or meetings to the chat. All previously collected information is preserved and remains searchable. This allows the app to be used as an archive after a project is completed.', faq__question_8: 'How do I pay for the subscription and who pays for it?', faq__answer_8: 'Individuals pay via Telegram Stars; legal entities (RU residents) pay by invoice. Only the admin pays for the subscription—access is free for all other members.', faq__description: 'Still have questions? We\'re here to help:', footer__docs: 'App documents', footer__doc_terms_of_use: 'Term of use', footer__doc_privacy_policy: 'Privacy Policy', footer__doc_consent: 'Consent to the processing of my personal data', footer__doc_subscription_guide: 'Provision on subscription tariffs', footer__contacts_location: 'Russia, Moscow/Voronezh', footer__description_user_data: 'The site does not collect user data, use cookies, or track user activity.' }
|
||||
export default { EN: 'EN', RU: 'RU', '': '', main__how_it_works: 'How it works', main__price: 'Tariff', main__faq: 'FAQ', main__contacts: 'Contacts', banner__slogan_prepend: 'More than just chats', banner__slogan_body: 'Your project\'s workspace in', banner__main_btn: 'Let’s Fly!', banner__main_btn_description: 'Continue in Telegram', problem__title: 'Keep your workflow, boost your efficiency', problem__1: 'Unified Workspace', problem__1_description: 'Gathers tasks, meetings, and files from all your connected chats into a single, unified space', problem__2: 'Team Directory', problem__2_description: 'Shows all members and their roles so you can instantly see who is responsible for what', problem__3: 'Chat Integration', problem__3_description: 'Updates to tasks and meetings appear right in your chats — where the conversation happens', problem__4: 'Smart Permissions', problem__4_description: 'Everyone sees only their relevant info. We take care of privacy and order, so you don\'t have to', how_it_works__title: 'How it works', how_it_works__intro: 'Intro', how_it_works__admin: 'Administrator', how_it_works__user: 'Users', price__title: 'Tariff', price__subtitle: 'All plans include full functionality: an unlimited number of users, projects, tasks, and meetings.', price__per_month: 'per month', price__free_tax: 'FREE', price__chats: 'chats', price__stars_pay: 'Payment via', price__stars_description: 'Telegram\'s internal currency', price__legal: 'Corporate', price__individual: 'Personal', price__rub_pay: 'Bank transfer', price__rub_resident: 'For RU residents', price__rub_closing_documents: 'Accounting documents', FAQ__title: 'Frequently Asked Questions (FAQ)', faq__question_1: 'Who is this app for?', faq__answer_1: 'For project teams conducting work communications in Telegram: IT development, construction, event industry, consulting, and others. The app creates a unified information environment and is essential when external contractors are involved and parties lack a shared infrastructure for collaboration.', faq__question_2: 'Is it mandatory to grant the bot message access and assign it as an administrator in chats?', faq__answer_2: 'No. Chats can be connected in two modes: "Private" (no message access or admin rights required) and "Full Functionality". The mode can be changed at any time. In Private mode, automatic backup of chat files and their addition to the project\'s general list is not performed.', faq__question_3: 'Can the app be connected to already existing chats?', faq__answer_3: 'Yes, any existing chat can be connected. There is also an option to share a special link with colleagues so they can connect their own chats to the project. No new groups are required — work continues within the familiar Telegram interface with added features for managing multiple project chats.', faq__question_4: 'Where are my files and data stored?', faq__answer_4: 'The app does not store chat history. Files are kept within Telegram or cloud services (Yandex Disk or Google Drive).', faq__question_5: 'Why is some contact information in the address book not displayed fully?', faq__answer_5: 'Contact details are displayed only with the user\'s consent and depend on the information provided by the administrator.', faq__question_6: 'Can participants of one project see data from another, and can one chat be linked to multiple projects?', faq__answer_6: 'A chat can be linked to only one project to maintain order. Projects are completely independent: information (chats, files, tasks, contacts) never overlaps. The app provides quick switching between workspaces, displaying only the information relevant to the currently selected project.', faq__question_7: 'What happens if I remove the app\'s bot from a chat or stop tracking in the app?', faq__answer_7: 'New messages and files will no longer be processed, and it will become impossible to attach tasks or meetings to the chat. All previously collected information is preserved and remains searchable. This allows the app to be used as an archive after a project is completed.', faq__question_8: 'How do I pay for the subscription and who pays for it?', faq__answer_8: 'Individuals pay via Telegram Stars; legal entities (RU residents) pay by invoice. Only the admin pays for the subscription—access is free for all other members.', faq__description: 'Still have questions? We\'re here to help:', footer__docs: 'App documents', footer__doc_terms_of_use: 'Term of use', footer__doc_privacy_policy: 'Privacy Policy', footer__doc_consent: 'Consent to the processing of my personal data', footer__doc_subscription_guide: 'Provision on subscription tariffs', footer__contacts_location: 'Russia, Moscow/Voronezh', footer__description_user_data: 'The site does not collect user data, use cookies, or track user activity.', footer__contacts_ogrnip: 'PSRNSP', footer__contacts_inn: 'ITN', footer__contacts_pdn: 'Data Controller' }
|
||||
File diff suppressed because one or more lines are too long
@@ -20,7 +20,11 @@
|
||||
ref="headerContainer"
|
||||
class="flex justify-between no-wrap items-center"
|
||||
>
|
||||
<base-logo ref="logo" class="text-h6" />
|
||||
<base-logo
|
||||
ref="logo"
|
||||
class="text-h5"
|
||||
:text-color="isHeroScroll ? 'white' : 'primary'"
|
||||
/>
|
||||
<div
|
||||
ref="menuContainer"
|
||||
class="row items-center q-ml-md no-wrap"
|
||||
@@ -115,8 +119,16 @@
|
||||
id="hero_banner"
|
||||
/>
|
||||
<problem-section id="problems"/>
|
||||
<how-works-section id="how_works" />
|
||||
<price-section id="price" />
|
||||
<how-works-section
|
||||
id="how_works"
|
||||
@update:tabs-no-caps="val => tab1 = val"
|
||||
:useTabsNoCaps
|
||||
/>
|
||||
<price-section
|
||||
id="price"
|
||||
@update:tabs-no-caps="val => tab2 = val"
|
||||
:useTabsNoCaps
|
||||
/>
|
||||
<faq-section id="FAQ" />
|
||||
<footer-section id="contacts" />
|
||||
</q-page>
|
||||
@@ -125,7 +137,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, nextTick } from 'vue'
|
||||
import { ref, onMounted, nextTick, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { scroll } from 'quasar'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
@@ -259,6 +271,12 @@
|
||||
}, 450)
|
||||
}
|
||||
})
|
||||
|
||||
const useTabsNoCaps = ref(false)
|
||||
const tab1 = ref(false)
|
||||
const tab2 = ref(false)
|
||||
|
||||
watch([tab1, tab2], () => useTabsNoCaps.value = tab1.value || tab2.value)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
BIN
~$i18n-2-landing.xlsm
Normal file
BIN
~$i18n-2-landing.xlsm
Normal file
Binary file not shown.
Reference in New Issue
Block a user