add tabs
Deploy app frontend / app frontend (push) Successful in 1m46s

This commit is contained in:
2024-12-06 21:20:27 +02:00
parent 58a5c378ca
commit ef977f5742
17 changed files with 542 additions and 444 deletions
@@ -21,7 +21,11 @@
</Card>
</div>
<slot name="endOfMessages" :count="messages.length"></slot>
<slot
name="endOfMessages"
:count="messages.length"
:lastMessage="messages.length > 0 ? messages[messages.length - 1] : null"
></slot>
</div>
<div class="flex gap-3 px-4 pt-0 flex-col-reverse pb-safe-or-4">
@@ -0,0 +1,32 @@
<template>
<div
class="sticky bottom-0 bg-white shadow-md py-safe-or-4 sm:border-b sm:order-first sm:justify-center touch-none"
>
<div
class="flex justify-around sm:justify-between sm:gap-8 sm:px-4 sm:flex-row sm:bg-white sm:max-w-prose mx-auto"
>
<TabMenuItem
v-for="{ route, label, icon } in tabs"
:key="route"
:label
:icon
:to="{ name: route }"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { ROUTE_NAMES } from '@/constants/routes'
type Tab = {
route: (typeof ROUTE_NAMES)[keyof typeof ROUTE_NAMES]
label: string
icon: string
}
const tabs: Tab[] = [
{ route: ROUTE_NAMES.CREATE, label: 'Создать QR-код', icon: 'plus' },
{ route: ROUTE_NAMES.MANAGE_DASHBOARD_QR, label: 'Мои QR-коды', icon: 'qrcode' },
{ route: ROUTE_NAMES.MANAGE_DASHBOARD_CHATS, label: 'Чаты', icon: 'comments' },
// { to: ROUTE_NAMES., label: 'Профиль', icon: 'user' },
]
</script>
@@ -0,0 +1,29 @@
<template>
<RouterLink :to custom v-slot="{ isActive, href, navigate }">
<a
v-bind="$attrs"
:href="href"
@click="navigate"
class="flex flex-col items-center w-full sm:flex-row sm:items-center sm:w-auto gap-1"
:class="{ 'text-primary-700 font-bold': isActive }"
>
<i :class="['pi', `pi-${icon}`, 'text-2xl sm:mr-2', isActive && 'text-primary-700']"></i>
<span
class="text-xs font-medium sm:text-base text-center"
:class="{ 'text-primary-700': isActive, 'text-gray-700': !isActive }"
>
{{ label }}
</span>
</a>
</RouterLink>
</template>
<script setup lang="ts">
import { type RouteLocationRaw } from 'vue-router'
defineProps<{
readonly to: RouteLocationRaw | string
readonly icon: string
readonly label: string
}>()
</script>
+2 -1
View File
@@ -4,8 +4,9 @@ export const ROUTE_NAMES = {
MANAGE_QR_CODE: 'manage-qr-code',
MANAGE_QR_CODE_READY: 'manage-qr-code-ready',
MANAGE_QR_CODE_CHAT: 'manage-qr-code-chat',
MANAGE_DASHBOARD: 'manage-dashboard',
MANAGE_DASHBOARD_QR_CODES: 'manage-dashboard-qr-codes',
MANAGE_DASHBOARD_QR: 'manage-dashboard-qr',
MANAGE_DASHBOARD_CHATS: 'manage-dashboard-chats',
READ_QR_CODE: 'read-qr-code',
DONATE: 'donate',
PUBLIC_CHAT: 'public-chat',
@@ -0,0 +1,39 @@
<template>
<div class="grid h-full sm:grid-rows-[auto_1fr_auto] grid-rows-[1fr_auto_auto] overflow-hidden">
<div
class="flex flex-1 overflow-auto pb-safe-or-4 sm:pt-safe sm:max-w-prose mx-auto w-full px-4 touch-pan-y"
>
<slot />
</div>
<slot name="footer">
<footer
v-if="!slots.footer"
class="bg-gray-100 text-center py-1.5 border-t text-xs sm:text-base text-gray-700 sm:order-last sm:pb-safe-or-1.5"
>
<span class="text-nowrap">Сделайте НаСвязи удобнее.</span>
<span> </span>
<span class="text-nowrap">
Будем рады
<a href="/donate" class="underline underline-offset-2">вашей поддержке</a>
</span>
</footer>
</slot>
<TabBar />
</div>
</template>
<script setup lang="ts">
const slots = defineSlots<{
default?: () => void
footer?: () => void
}>()
</script>
<style>
html,
body {
overflow: hidden;
}
</style>
+10 -4
View File
@@ -11,7 +11,7 @@ const routes = [
name: ROUTE_NAMES.HOME,
component: () =>
isRunningStandalone()
? import('@/views/ManageDashboardView.vue')
? import('@/views/ManageQRCodesView.vue')
: import('@/views/HomeView.vue'),
},
{
@@ -21,14 +21,20 @@ const routes = [
},
{
path: '/manage',
name: ROUTE_NAMES.MANAGE_DASHBOARD,
component: () => import('@/views/ManageDashboardView.vue'),
name: ROUTE_NAMES.MANAGE_DASHBOARD_QR_CODES,
component: () => import('@/views/ManageQRCodesView.vue'),
props: true,
},
{
path: '/manage/qr',
name: ROUTE_NAMES.MANAGE_DASHBOARD_QR,
component: () => import('@/views/ManageDashboardView.vue'),
component: () => import('@/views/ManageQRCodesView.vue'),
props: true,
},
{
path: '/manage/chats',
name: ROUTE_NAMES.MANAGE_DASHBOARD_CHATS,
component: () => import('@/views/ManageChatsView.vue'),
props: true,
},
{
+37 -41
View File
@@ -1,52 +1,48 @@
<template>
<MetaHead>
<title>Создание QR-кода НаСвязи</title>
<meta
name="description"
content="Создайте свой QR-код для безопасного и конфиденциального взаимодействия"
/>
</MetaHead>
<LayoutWithTabs>
<MetaHead>
<title>Создание QR-кода НаСвязи</title>
<meta
name="description"
content="Создайте свой QR-код для безопасного и конфиденциального взаимодействия"
/>
</MetaHead>
<div class="max-w-prose mx-auto py-6 px-4 md:px-6">
<section class="content">
<div class="max-w-prose mx-auto py-8">
<CreateSectionManager
:placement="placement"
:selectedActions="selectedActions"
:messageDeliveryMethod="messageDeliveryMethod"
>
<template #placement>
<CreatePlacementSection v-model="placement" />
</template>
<CreateSectionManager
:placement="placement"
:selectedActions="selectedActions"
:messageDeliveryMethod="messageDeliveryMethod"
>
<template #placement>
<CreatePlacementSection v-model="placement" />
</template>
<template #actions>
<CreateActionsSection v-model="selectedActions" />
</template>
<template #actions>
<CreateActionsSection v-model="selectedActions" />
</template>
<template #delivery>
<CreateMessageDeliveryMethodSection v-model="messageDeliveryMethod" />
</template>
<template #delivery>
<CreateMessageDeliveryMethodSection v-model="messageDeliveryMethod" />
</template>
<template #messages>
<CreatePredefinedMessagesSection :placement v-model="messages" />
</template>
<template #messages>
<CreatePredefinedMessagesSection :placement v-model="messages" />
</template>
<template #summary>
<CreateSummarySection
v-model:name="name"
buttonLabel="Создать QR‑код"
:isPending
@nextStep="createQrCode"
/>
</template>
</CreateSectionManager>
<template #summary>
<CreateSummarySection
v-model:name="name"
buttonLabel="Создать QR‑код"
:isPending
@nextStep="createQrCode"
/>
</template>
</CreateSectionManager>
<p v-if="isError" class="text-red-500 mt-4">Ошибка создания QRкода: {{ error }}</p>
<p v-if="isError" class="text-red-500 mt-4">Ошибка создания QRкода: {{ error }}</p>
<div class="h-[30dvh] md:h-[10dvh] overflow-hidden"></div>
</div>
</section>
</div>
<div class="h-[30dvh] md:h-[10dvh] overflow-hidden"></div>
</LayoutWithTabs>
</template>
<script setup lang="ts">
+74 -66
View File
@@ -1,80 +1,88 @@
<template>
<MetaHead>
<title>Поддержите развитие НаСвязи</title>
<meta name="description" content="" />
</MetaHead>
<LayoutWithTabs>
<template #footer>
<footer class="py-4 border-t text-center text-sm hidden sm:block">
<p>НаСвязи © 2024</p>
</footer>
</template>
<section class="max-w-2xl mx-auto py-8 px-4 text-center">
<h1 class="text-2xl font-bold mb-4">Поддержите развитие НаСвязи</h1>
<p class="text-gray-600 mb-6">
Мы делаем всё, чтобы НаСвязи оставался удобным и полезным. Вы можете помочь нам, поддержав
проект на Бусти.
</p>
<MetaHead>
<title>Поддержите развитие НаСвязи</title>
<meta name="description" content="" />
</MetaHead>
<!-- Ссылка на Бусти -->
<div class="mb-8">
<a
href="https://boosty.to/hereconnect/donate"
target="_blank"
rel="noopener noreferrer"
class="inline-block bg-orange-500 text-white font-semibold py-3 px-6 rounded-lg shadow hover:bg-orange-600 transition duration-300"
>
Поддержать через Бусти
</a>
</div>
<section class="max-w-2xl mx-auto py-8 px-4 text-center">
<h1 class="text-2xl font-bold mb-4">Поддержите развитие НаСвязи</h1>
<p class="text-gray-600 mb-6">
Мы делаем всё, чтобы НаСвязи оставался удобным и полезным. Вы можете помочь нам, поддержав
проект на Бусти.
</p>
<!-- Рассказать друзьям -->
<div class="mb-8">
<h2 class="text-xl font-semibold mb-4">Рассказать друзьям</h2>
<p class="text-gray-600 mb-4">Поделитесь ссылкой на НаСвязи:</p>
<div class="flex justify-center gap-6">
<!-- Telegram -->
<!-- Ссылка на Бусти -->
<div class="mb-8">
<a
:href="telegramShareUrl"
href="https://boosty.to/hereconnect/donate"
target="_blank"
rel="noopener noreferrer"
class="p-button-rounded p-button-text p-button-lg inline-flex items-center justify-center"
class="inline-block bg-orange-500 text-white font-semibold py-3 px-6 rounded-lg shadow hover:bg-orange-600 transition duration-300"
>
<i class="pi pi-telegram !text-3xl text-blue-500"></i>
</a>
<!-- WhatsApp -->
<a
:href="whatsappShareUrl"
target="_blank"
rel="noopener noreferrer"
class="p-button-rounded p-button-text p-button-lg inline-flex items-center justify-center"
>
<i class="pi pi-whatsapp !text-3xl text-green-500"></i>
Поддержать через Бусти
</a>
</div>
</div>
<!-- Обратная связь -->
<!-- <div>-->
<!-- <h2 class="text-xl font-semibold mb-4">Оставить отзыв или идею</h2>-->
<!-- <p class="text-gray-600 mb-4">Мы рады любым предложениям и обратной связи.</p>-->
<!-- <form @submit.prevent="sendFeedback">-->
<!-- <div class="mb-4">-->
<!-- <InputText v-model="email" class="w-full" placeholder="Ваш email" />-->
<!-- </div>-->
<!-- <div class="mb-4">-->
<!-- <Textarea-->
<!-- v-model="message"-->
<!-- rows="4"-->
<!-- class="w-full"-->
<!-- placeholder="Ваше сообщение"-->
<!-- ></Textarea>-->
<!-- </div>-->
<!-- <Button-->
<!-- label="Отправить"-->
<!-- class="p-button-md"-->
<!-- :disabled="!email.trim() || !message.trim()"-->
<!-- />-->
<!-- </form>-->
<!-- <p v-if="success" class="text-green-500 mt-4">Спасибо за ваше сообщение!</p>-->
<!-- </div>-->
</section>
<!-- Рассказать друзьям -->
<div class="mb-8">
<h2 class="text-xl font-semibold mb-4">Рассказать друзьям</h2>
<p class="text-gray-600 mb-4">Поделитесь ссылкой на НаСвязи:</p>
<div class="flex justify-center gap-6">
<!-- Telegram -->
<a
:href="telegramShareUrl"
target="_blank"
rel="noopener noreferrer"
class="p-button-rounded p-button-text p-button-lg inline-flex items-center justify-center"
>
<i class="pi pi-telegram !text-3xl text-blue-500"></i>
</a>
<!-- WhatsApp -->
<a
:href="whatsappShareUrl"
target="_blank"
rel="noopener noreferrer"
class="p-button-rounded p-button-text p-button-lg inline-flex items-center justify-center"
>
<i class="pi pi-whatsapp !text-3xl text-green-500"></i>
</a>
</div>
</div>
<!-- Обратная связь -->
<!-- <div>-->
<!-- <h2 class="text-xl font-semibold mb-4">Оставить отзыв или идею</h2>-->
<!-- <p class="text-gray-600 mb-4">Мы рады любым предложениям и обратной связи.</p>-->
<!-- <form @submit.prevent="sendFeedback">-->
<!-- <div class="mb-4">-->
<!-- <InputText v-model="email" class="w-full" placeholder="Ваш email" />-->
<!-- </div>-->
<!-- <div class="mb-4">-->
<!-- <Textarea-->
<!-- v-model="message"-->
<!-- rows="4"-->
<!-- class="w-full"-->
<!-- placeholder="Ваше сообщение"-->
<!-- ></Textarea>-->
<!-- </div>-->
<!-- <Button-->
<!-- label="Отправить"-->
<!-- class="p-button-md"-->
<!-- :disabled="!email.trim() || !message.trim()"-->
<!-- />-->
<!-- </form>-->
<!-- <p v-if="success" class="text-green-500 mt-4">Спасибо за ваше сообщение!</p>-->
<!-- </div>-->
</section>
</LayoutWithTabs>
</template>
<script setup lang="ts">
@@ -0,0 +1,52 @@
<template>
<LayoutWithTabs>
<MetaHead>
<title>Ваши Чаты НаСвязи</title>
<meta
name="description"
content="Просматривайте и управляйте сообщениями, отправленными через ваш QR-код. Полная конфиденциальность и удобный интерфейс чата"
/>
</MetaHead>
<QueryRender :query="query">
<template #default="{ data }">
<div
v-if="data.rows.length > 0"
class="flex flex-col gap-5 my-4 grid grid-cols-1 sm:grid-cols-3 gap-4"
/>
<div
v-else
class="flex-1 flex flex-col items-center place-self-center text-center text-gray-500"
>
<h1 class="text-2xl font-bold mb-4 text-center sm:text-left">Страница в разработке</h1>
</div>
</template>
<template #error="{ reload }">
<div class="text-center text-red-500">
<p>Произошла ошибка при загрузке данных. Пожалуйста, попробуйте позже.</p>
<Button label="Повторить попытку" class="p-button mt-4" @click="reload()" />
</div>
</template>
<template #loading>
<div class="text-center text-gray-500">
<p>Загрузка QR-кодов...</p>
<ProgressSpinner styleClass="mt-4" />
</div>
</template>
</QueryRender>
</LayoutWithTabs>
</template>
<script setup lang="ts">
import { useQuery } from '@tanstack/vue-query'
const query = useQuery({
queryKey: ['qrCodes'],
queryFn: async () => {
return { rows: [] } as const
},
})
</script>
@@ -1,107 +0,0 @@
<template>
<MetaHead>
<title>Ваши QR-коды НаСвязи</title>
<meta
name="description"
content="Просматривайте и управляйте сообщениями, отправленными через ваш QR-код. Полная конфиденциальность и удобный интерфейс чата"
/>
</MetaHead>
<div class="flex flex-col min-h-full">
<main class="flex-grow flex justify-center py-6 px-4 md:px-6">
<QueryRender :query="query">
<template #default="{ data }">
<div v-if="data.rows.length > 0">
<h1 class="text-2xl font-bold mb-4 text-center lg:text-left">Управление QR-кодами</h1>
<div class="flex flex-col gap-5">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<QRCodeCard
v-for="{ doc: qrCode } in data.rows"
:key="qrCode!.uri"
:qrCode="qrCode!"
/>
<RouterLink
to="/create"
class="flex justify-stretch place-items-center p-4 rounded-lg bg-white text-primary shadow-md flex-col items-center gap-2"
>
<i class="pi pi-plus text-4xl" />
<span class="text-lg font-semibold">Создать QRкод</span>
</RouterLink>
</div>
</div>
</div>
<div v-else class="text-center text-gray-500 place-self-center">
<h1 class="text-2xl font-bold mb-4 text-center lg:text-left">Нет созданных QR-кодов</h1>
<p class="mb-6">Вы можете создать их прямо сейчас!</p>
<Button
icon="pi pi-qrcode"
label="Создать QR‑код"
to="/create"
as="router-link"
class="p-button-lg"
/>
</div>
</template>
<template #error="{ reload }">
<div class="text-center text-red-500">
<p>Произошла ошибка при загрузке данных. Пожалуйста, попробуйте позже.</p>
<Button label="Повторить попытку" class="p-button mt-4" @click="reload()" />
</div>
</template>
<template #loading>
<div class="text-center text-gray-500">
<p>Загрузка QR-кодов...</p>
<ProgressSpinner styleClass="mt-4" />
</div>
</template>
</QueryRender>
</main>
<footer class="bg-gray-100 text-center py-4 border-t text-sm text-gray-700">
Сделайте НаСвязи удобнее. Будем рады вашей&nbsp;<a
href="/donate"
class="underline underline-offset-2"
>поддержке</a
>.
</footer>
</div>
</template>
<script setup lang="ts">
import { useQuery } from '@tanstack/vue-query'
import { qrCodeDbLocal, qrCodeDbRemote } from '@/api/dbs'
import { getUserUuid } from '@/utils/getUserUuid'
import { onMounted } from 'vue'
import type { QRCodeDocument } from '@hereconnect/types'
const query = useQuery({
queryKey: ['qrCodes'],
queryFn: async () => {
return qrCodeDbLocal.allDocs({
include_docs: true,
startkey: 'qr_code:',
endkey: 'qr_code:\uffff',
})
},
})
let replication: PouchDB.Replication.Replication<QRCodeDocument> | null = null
onMounted(() => {
replication = qrCodeDbLocal.replicate.from(qrCodeDbRemote, {
live: true,
selector: {
type: 'qr_code',
user_uuid: getUserUuid(),
},
})
replication.on('change', () => {
query.refetch()
})
})
onBeforeUnmount(() => {
if (replication) {
replication.cancel()
}
})
</script>
@@ -1,55 +1,51 @@
<template>
<MetaHead>
<title>Чат {{ chatDoc?.name ? `«${chatDoc.name}»` : '' }} НаСвязи</title>
<meta
name="description"
content="Ваш QR-код успешно создан! Узнайте, как его использовать и настройте все доступные функции для удобного взаимодействия с окружающими."
/>
</MetaHead>
<LayoutWithTabs>
<MetaHead>
<title>Чат {{ chatDoc?.name ? `«${chatDoc.name}»` : '' }} НаСвязи</title>
<meta
name="description"
content="Ваш QR-код успешно создан! Узнайте, как его использовать и настройте все доступные функции для удобного взаимодействия с окружающими."
/>
</MetaHead>
<div class="flex flex-col min-h-full">
<!-- Основной контент -->
<main class="flex-grow flex flex-col">
<section class="flex flex-col flex-grow max-w-prose w-full mx-auto">
<ChatComponent userRole="owner" :qr_code_uri="qr_code_uri" :chat="chat">
<template #endOfMessages="{ count }">
<!-- Уведомление, если гость отправил только одно сообщение -->
<div
v-if="count === 2"
class="flex flex-col items-center justify-center text-center bg-gray-50 border border-gray-200 rounded-lg shadow-md p-6 mt-6"
>
<p class="text-gray-700 font-medium text-lg">Ваше сообщение отправлено.</p>
<p class="text-gray-600 mt-2">
Вы можете написать ещё одно сообщение или подождать ответа.
</p>
</div>
<div class="flex flex-col min-h-full">
<!-- Основной контент -->
<main class="flex-grow flex flex-col">
<section class="flex flex-col flex-grow max-w-prose w-full mx-auto">
<ChatComponent userRole="owner" :qr_code_uri="qr_code_uri" :chat="chat">
<template #endOfMessages="{ count, lastMessage }">
<div
v-if="!count"
class="flex flex-col items-center justify-center flex-grow text-center text-gray-500"
>
<h1 class="text-2xl font-bold mb-4">Пока нет сообщений</h1>
<p class="text-gray-600">Вы можете начать общение, отправив первое сообщение.</p>
</div>
<div
v-else-if="lastMessage && lastMessage.from === 'owner'"
class="flex flex-col items-center justify-center text-center bg-gray-50 border border-gray-200 rounded-lg shadow-md p-6 mt-6"
>
<p class="text-gray-700 font-medium text-lg">Ваше сообщение отправлено.</p>
<p class="text-gray-600 mt-2">
Вы можете написать ещё одно сообщение или подождать ответа.
</p>
</div>
</template>
</ChatComponent>
</section>
</main>
<p class="mt-10 text-sm text-gray-500 text-center">
Хотите создать еще один QRкод?
<a href="/create" class="underline underline-offset-2">Создайте его здесь</a>.
</p>
<p class="mt-1 text-sm text-gray-500 text-center">
Сделайте НаСвязи удобнее. Мы будем рады вашей&nbsp;<RouterLink
to="/donate"
class="underline"
>поддержке</RouterLink
>.
</p>
</template>
</ChatComponent>
</section>
</main>
<!-- Футер -->
<!-- <footer class="bg-gray-100 text-center py-4 border-t text-sm text-gray-700">-->
<!-- <p><a href="/create" class="underline underline-offset-2">Создайте свой QRкод</a>.</p>-->
<!-- <p class="mt-1">-->
<!-- Сделайте НаСвязи удобнее. Будем рады вашей&nbsp;<a href="/donate" class="underline underline-offset-2"-->
<!-- >поддержке</a-->
<!-- >.-->
<!-- </p>-->
<!-- </footer>-->
</div>
<!-- Футер -->
<!-- <footer class="bg-gray-100 text-center py-4 border-t text-sm text-gray-700">-->
<!-- <p><a href="/create" class="underline underline-offset-2">Создайте свой QRкод</a>.</p>-->
<!-- <p class="mt-1">-->
<!-- Сделайте НаСвязи удобнее. Будем рады вашей&nbsp;<a href="/donate" class="underline underline-offset-2"-->
<!-- >поддержке</a-->
<!-- >.-->
<!-- </p>-->
<!-- </footer>-->
</div>
</LayoutWithTabs>
</template>
<script setup lang="ts">
@@ -1,13 +1,13 @@
<template>
<MetaHead>
<title>Ваш QR-код готов НаСвязи</title>
<meta
name="description"
content="Просматривайте и управляйте сообщениями, отправленными через ваш QR-код. Полная конфиденциальность и удобный интерфейс чата"
/>
</MetaHead>
<LayoutWithTabs>
<MetaHead>
<title>Ваш QR-код готов НаСвязи</title>
<meta
name="description"
content="Просматривайте и управляйте сообщениями, отправленными через ваш QR-код. Полная конфиденциальность и удобный интерфейс чата"
/>
</MetaHead>
<div class="flex flex-col min-h-full">
<main
class="flex-grow flex justify-center py-6 px-4 md:px-6"
:class="{
@@ -22,10 +22,10 @@
>
<ProgressSpinner styleClass="mt-4" />
</div>
<p v-else-if="svgQueryIsError" class="text-red-500 mt-4">
<p v-else-if="svgQueryIsError" class="text-red-500 mt-4 text-center">
Ошибка генерации QRкода: {{ svgQueryError?.message }}
</p>
<p v-else-if="qrCodeQueryIsError" class="text-red-500 mt-4">
<p v-else-if="qrCodeQueryIsError" class="text-red-500 mt-4 text-center">
Ошибка загрузки данных QRкода: {{ qrCodeQueryError!.message }}
</p>
<template v-else>
@@ -84,21 +84,7 @@
</template>
</section>
</main>
<footer class="bg-gray-100 text-center py-4 border-t text-sm text-gray-700">
<p>
Нужен еще один?
<a href="/create" class="underline underline-offset-2">Создайте QRкод</a>.
</p>
<p class="mt-1">
Сделайте НаСвязи удобнее. Будем рады вашей&nbsp;<a
href="/donate"
class="underline underline-offset-2"
>поддержке</a
>.
</p>
</footer>
</div>
</LayoutWithTabs>
</template>
<script setup lang="ts">
+70 -101
View File
@@ -1,108 +1,77 @@
<template>
<div class="flex flex-col min-h-full">
<main class="flex-grow flex justify-center py-6 px-4 md:px-6">
<div>
<h1 class="text-2xl font-bold mb-4 text-center lg:text-left">Управление QR-кодом</h1>
<QueryRender :query="qrCodeQuery">
<template #default="{ data: qrCodeDoc }">
<MetaHead>
<title>{{ qrCodeDoc.name }}: управление QR-кодом НаСвязи</title>
<meta
name="description"
content="Просматривайте и управляйте сообщениями, отправленными через ваш QR-код. Полная конфиденциальность и удобный интерфейс чата"
/>
</MetaHead>
<div class="bg-white p-6 rounded-lg shadow-md mb-6">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center">
<QRCodePlacementIcon :placement="qrCodeDoc.placement" />
<h2 class="text-xl font-semibold">{{ qrCodeDoc.name }}</h2>
</div>
<div class="flex gap-2">
<!-- <Button-->
<!-- icon="pi pi-cog"-->
<!-- label="Настроить"-->
<!-- :to="`/manage/qr/${qrCodeDoc.uri}/settings`"-->
<!-- as="router-link"-->
<!-- severity="secondary"-->
<!-- size="small"-->
<!-- outlined-->
<!-- />-->
<Button
icon="pi pi-qrcode"
label="QR-код и инструкции"
:to="`/manage/qr/${qrCodeDoc.uri}/ready`"
as="router-link"
severity="secondary"
size="small"
outlined
/>
</div>
</div>
<div class="mb-4 flex gap-2 items-baseline flex-wrap">
<strong>Ссылка: </strong>
<LinkQRCode :url="qrCodeDoc.url" />
<div class="flex gap-2 items-baseline flex-wrap">
<LinkCopyButton :url="qrCodeDoc.url" />
<LinkShareTelegramButton
:shareUrl="qrCodeDoc.url"
shareText="Используйте эту ссылку НаСвязи!"
/>
</div>
</div>
<div>
<p>
<strong>Создано: </strong>
<span class="text-nowrap">{{ formatDate(qrCodeDoc.created_at) }}</span>
</p>
<!-- <p><strong>Количество сканирований:</strong> {{ qrCodeDoc.scanCount }}</p>-->
<!-- <p><strong>Последнее сканирование:</strong> {{ formatDate(qrCodeDoc.lastScan) }}</p>-->
</div>
<LayoutWithTabs>
<QueryRender :query="qrCodeQuery">
<template #default="{ data: qrCodeDoc }">
<MetaHead>
<title>{{ qrCodeDoc.name }} НаСвязи</title>
<meta
name="description"
content="Просматривайте и управляйте сообщениями, отправленными через ваш QR-код. Полная конфиденциальность и удобный интерфейс чата"
/>
</MetaHead>
<div class="bg-white p-6 rounded-lg shadow-md my-5">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center">
<QRCodePlacementIcon :placement="qrCodeDoc.placement" />
<h2 class="text-xl font-semibold">{{ qrCodeDoc.name }}</h2>
</div>
</template>
</QueryRender>
<div class="flex gap-2">
<!-- <Button-->
<!-- icon="pi pi-cog"-->
<!-- label="Настроить"-->
<!-- :to="`/manage/qr/${qrCodeDoc.uri}/settings`"-->
<!-- as="router-link"-->
<!-- severity="secondary"-->
<!-- size="small"-->
<!-- outlined-->
<!-- />-->
<Button
icon="pi pi-qrcode"
label="QR-код и инструкции"
:to="`/manage/qr/${qrCodeDoc.uri}/ready`"
as="router-link"
severity="secondary"
size="small"
outlined
/>
</div>
</div>
<div class="mb-4 flex gap-2 items-baseline flex-wrap">
<strong>Ссылка: </strong>
<LinkQRCode :url="qrCodeDoc.url" />
<div class="flex gap-2 items-baseline flex-wrap">
<LinkCopyButton :url="qrCodeDoc.url" />
<LinkShareTelegramButton
:shareUrl="qrCodeDoc.url"
shareText="Используйте эту ссылку НаСвязи!"
/>
</div>
</div>
<div>
<p>
<strong>Создано: </strong>
<span class="text-nowrap">{{ formatDate(qrCodeDoc.created_at) }}</span>
</p>
<!-- <p><strong>Количество сканирований:</strong> {{ qrCodeDoc.scanCount }}</p>-->
<!-- <p><strong>Последнее сканирование:</strong> {{ formatDate(qrCodeDoc.lastScan) }}</p>-->
</div>
</div>
</template>
<!-- <div class="bg-white p-6 rounded-lg shadow-md mb-6">-->
<!-- <h2 class="text-xl font-bold mb-4">Чаты</h2>-->
<!-- <div v-if="chats.length > 0" class="grid grid-cols-1 gap-4">-->
<!-- <div v-for="chat in chats" :key="chat._id" class="p-4 rounded-lg bg-white shadow-md">-->
<!-- <div class="flex items-center justify-between">-->
<!-- <div>-->
<!-- <p class="text-lg font-semibold">Чат #{{ chat._id }}</p>-->
<!-- <p class="text-gray-500">-->
<!-- Последнее сообщение: {{ formatDate(chat.lastMessageDate) }}-->
<!-- </p>-->
<!-- </div>-->
<!-- <Button-->
<!-- label="Открыть"-->
<!-- :to="`/manage/qr/${qrCodeDoc.uri}/chat/${chat._id}`"-->
<!-- as="router-link"-->
<!-- class="p-button-outlined p-button-sm"-->
<!-- />-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div v-else class="text-center text-gray-500">-->
<!-- <p>Нет активных чатов для этого QR-кода.</p>-->
<!-- </div>-->
<!-- </div>-->
</div>
</main>
<template #loading>
<div class="flex-1 flex items-center place-self-center">
<ProgressSpinner styleClass="mt-4" />
</div>
</template>
<footer class="bg-gray-100 text-center py-4 border-t text-sm text-gray-700">
<p>
Нужен еще один?
<a href="/create" class="underline underline-offset-2">Создайте QRкод</a>.
</p>
<p class="mt-1">
Сделайте НаСвязи удобнее. Будем рады вашей&nbsp;<a
href="/donate"
class="underline underline-offset-2"
>поддержке</a
>.
</p>
</footer>
</div>
<template #error="{ reload, error }">
<div class="flex-1 flex flex-col items-center place-self-center text-center text-red-500">
<p>Произошла ошибка при загрузке данных: {{ error.message }}</p>
<Button label="Повторить попытку" class="p-button mt-4" @click="reload()" />
</div>
</template>
</QueryRender>
</LayoutWithTabs>
</template>
<script setup lang="ts">
@@ -0,0 +1,93 @@
<template>
<LayoutWithTabs>
<MetaHead>
<title>Ваши QR-коды НаСвязи</title>
<meta
name="description"
content="Просматривайте и управляйте сообщениями, отправленными через ваш QR-код. Полная конфиденциальность и удобный интерфейс чата"
/>
</MetaHead>
<QueryRender :query="query">
<template #default="{ data }">
<div v-if="data.rows.length > 0">
<div class="flex flex-col gap-5 my-5">
<div class="grid grid-cols-1 sm:grid-cols-3 gap-4">
<QRCodeCard
v-for="{ doc: qrCode } in data.rows"
:key="qrCode!.uri"
:qrCode="qrCode!"
/>
</div>
</div>
</div>
<div
v-else
class="flex-1 flex flex-col items-center place-self-center text-center text-gray-500"
>
<h1 class="text-2xl font-bold mb-4 text-center sm:text-left">Нет созданных QR-кодов</h1>
<p class="mb-6">Вы можете создать их прямо сейчас!</p>
<Button
icon="pi pi-qrcode"
label="Создать QR‑код"
to="/create"
as="router-link"
class="p-button-lg"
/>
</div>
</template>
<template #loading>
<div class="flex-1 flex items-center place-self-center">
<ProgressSpinner styleClass="mt-4" />
</div>
</template>
<template #error="{ reload, error }">
<div class="flex-1 flex flex-col items-center place-self-center text-center text-red-500">
<p>Произошла ошибка при загрузке данных: {{ error.message }}</p>
<Button label="Повторить попытку" class="p-button mt-4" @click="reload()" />
</div>
</template>
</QueryRender>
</LayoutWithTabs>
</template>
<script setup lang="ts">
import { useQuery } from '@tanstack/vue-query'
import { qrCodeDbLocal, qrCodeDbRemote } from '@/api/dbs'
import { getUserUuid } from '@/utils/getUserUuid'
import { onMounted } from 'vue'
import type { QRCodeDocument } from '@hereconnect/types'
const query = useQuery({
queryKey: ['qrCodes'],
queryFn: () => {
return qrCodeDbLocal.allDocs({
include_docs: true,
startkey: 'qr_code:',
endkey: 'qr_code:\uffff',
})
},
})
let replication: PouchDB.Replication.Replication<QRCodeDocument> | null = null
onMounted(() => {
replication = qrCodeDbLocal.replicate.from(qrCodeDbRemote, {
live: true,
selector: {
type: 'qr_code',
user_uuid: getUserUuid(),
},
})
replication.on('change', () => {
query.refetch()
})
})
onBeforeUnmount(() => {
if (replication) {
replication.cancel()
}
})
</script>
+39 -49
View File
@@ -1,55 +1,45 @@
<template>
<MetaHead>
<title>Чат НаСвязи</title>
<meta
name="description"
content="Чат QR-кода. Безопасная и конфиденциальная связь для решения всех вопросов"
/>
</MetaHead>
<LayoutWithTabs>
<MetaHead>
<title>Чат НаСвязи</title>
<meta
name="description"
content="Чат QR-кода. Безопасная и конфиденциальная связь для решения всех вопросов"
/>
</MetaHead>
<div class="flex flex-col min-h-full">
<!-- Основной контент -->
<main class="flex-grow flex flex-col">
<section class="flex flex-col flex-grow max-w-prose w-full mx-auto">
<ChatComponent userRole="guest" :qr_code_uri="qr_code_uri" :chat="chat">
<template #endOfMessages="{ count }">
<!-- Уведомление, если гость отправил только одно сообщение -->
<div
v-if="count === 1"
class="flex flex-col items-center justify-center text-center bg-gray-50 border border-gray-200 rounded-lg shadow-md p-6 mt-6"
>
<p class="text-gray-700 font-medium text-lg">Ваше сообщение отправлено.</p>
<p class="text-gray-600 mt-2">
Вы можете написать ещё одно сообщение или подождать ответа.
</p>
</div>
<div class="flex flex-col min-h-full">
<!-- Основной контент -->
<main class="flex-grow flex flex-col">
<section class="flex flex-col flex-grow max-w-prose w-full mx-auto">
<ChatComponent userRole="guest" :qr_code_uri="qr_code_uri" :chat="chat">
<template #endOfMessages="{ count, lastMessage }">
<!-- Уведомление, если гость отправил только одно сообщение -->
<div
v-if="count === 1 && lastMessage && lastMessage.from === 'guest'"
class="flex flex-col items-center justify-center text-center bg-gray-50 border border-gray-200 rounded-lg shadow-md p-6 mt-6"
>
<p class="text-gray-700 font-medium text-lg">Ваше сообщение отправлено.</p>
<p class="text-gray-600 mt-2">
Вы можете написать ещё одно сообщение или подождать ответа.
</p>
</div>
</template>
</ChatComponent>
</section>
</main>
<p class="mt-10 text-sm text-gray-500 text-center">
Хотите создать собственный QRкод?
<a href="/create" class="underline underline-offset-2">Создайте его здесь</a>.
</p>
<p class="mt-1 text-sm text-gray-500 text-center">
Сделайте НаСвязи удобнее. Мы будем рады вашей&nbsp;<RouterLink
to="/donate"
class="underline underline-offset-2"
>поддержке</RouterLink
>.
</p>
</template>
</ChatComponent>
</section>
</main>
<!-- Футер -->
<!-- <footer class="bg-gray-100 text-center py-4 border-t text-sm text-gray-700">-->
<!-- <p><a href="/create" class="underline underline-offset-2">Создайте свой QRкод</a>.</p>-->
<!-- <p class="mt-1">-->
<!-- Сделайте НаСвязи удобнее. Будем рады вашей&nbsp;<a href="/donate" class="underline underline-offset-2"-->
<!-- >поддержке</a-->
<!-- >.-->
<!-- </p>-->
<!-- </footer>-->
</div>
<!-- Футер -->
<!-- <footer class="bg-gray-100 text-center py-4 border-t text-sm text-gray-700">-->
<!-- <p><a href="/create" class="underline underline-offset-2">Создайте свой QRкод</a>.</p>-->
<!-- <p class="mt-1">-->
<!-- Сделайте НаСвязи удобнее. Будем рады вашей&nbsp;<a href="/donate" class="underline underline-offset-2"-->
<!-- >поддержке</a-->
<!-- >.-->
<!-- </p>-->
<!-- </footer>-->
</div>
</LayoutWithTabs>
</template>
<script setup lang="ts">