set add QR code page
Deploy app frontend / app frontend (push) Failing after 52s
Deploy service couchdb / service couchdb (push) Successful in 28s
Deploy db-migrations / db-migrations (push) Successful in 57s
Deploy service message-delivery-method-web-push / service message-delivery-method-web-push (push) Successful in 1m3s

This commit is contained in:
2024-12-06 14:07:31 +02:00
parent 3ec74a5f9f
commit 9c69812258
14 changed files with 201 additions and 162 deletions
+2
View File
@@ -62,6 +62,8 @@ declare global {
const useId: typeof import('vue')['useId']
const useLink: typeof import('vue-router')['useLink']
const useModel: typeof import('vue')['useModel']
const useMutation: typeof import('@tanstack/vue-query')['useMutation']
const useQuery: typeof import('@tanstack/vue-query')['useQuery']
const useRoute: typeof import('vue-router')['useRoute']
const useRouter: typeof import('vue-router')['useRouter']
const useSeoMeta: typeof import('unhead')['useSeoMeta']
+4
View File
@@ -12,6 +12,7 @@ declare module 'vue' {
ChatComponent: typeof import('./src/components/ChatComponent.vue')['default']
ChatSubscribeWebPush: typeof import('./src/components/ChatSubscribeWebPush.vue')['default']
Checkbox: typeof import('primevue/checkbox')['default']
CopyLinkButton: typeof import('@/components/link/LinkCopyButton.vue')['default']
CreateActionsSection: typeof import('./src/components/create/CreateActionsSection.vue')['default']
CreateMessageDeliveryMethodSection: typeof import('./src/components/create/CreateMessageDeliveryMethodSection.vue')['default']
CreateMessageDeliveryOption: typeof import('./src/components/create/CreateMessageDeliveryOption.vue')['default']
@@ -26,6 +27,9 @@ declare module 'vue' {
DownloadImageButton: typeof import('./src/components/manage/DownloadImageButton.vue')['default']
ExamplesSection: typeof import('./src/components/TheWelcome/ExamplesSection.vue')['default']
InputText: typeof import('primevue/inputtext')['default']
LinkCopyButton: typeof import('./src/components/link/LinkCopyButton.vue')['default']
LinkShareButton: typeof import('./src/components/link/LinkShareButton.vue')['default']
LinkShareTelegramButton: typeof import('./src/components/link/LinkShareTelegramButton.vue')['default']
ManagePlacementInstructions: typeof import('./src/components/manage/ManagePlacementInstructions.vue')['default']
ProgressSpinner: typeof import('primevue/progressspinner')['default']
QRCodeCard: typeof import('./src/components/manage/QRCodeCard.vue')['default']
-1
View File
@@ -37,7 +37,6 @@
"tailwindcss-primeui": "^0.3.4",
"unhead": "^1.11.13",
"vue": "^3.5.12",
"vue-query": "^1.26.0",
"vue-router": "^4.4.5"
},
"devDependencies": {
+1 -1
View File
@@ -52,7 +52,7 @@ export const createQrCodeDocument = async (
return qrCodeDoc
}
export const getQrCodeDocument = async (uri: string) => {
export const getQrCodeDocument = async (uri: string): Promise<QRCodeDocument> => {
try {
return await qrCodeDbLocal.get<QRCodeDocument>(`qr_code:${uri}`)
} catch (error) {
@@ -0,0 +1,26 @@
<template>
<Button
:icon="isCopied ? 'pi pi-check' : 'pi pi-copy'"
:label="isCopied ? 'Ссылка скопирована!' : 'Копировать ссылку'"
class="text-nowrap"
severity="secondary"
size="small"
@click="copyLink()"
:outlined="!isCopied"
/>
</template>
<script setup lang="ts">
const props = defineProps<{ url: string }>()
const {
mutate: copyLink,
isSuccess: isCopied,
reset,
} = useMutation({
mutationFn: async () => {
await navigator.clipboard.writeText(props.url)
setTimeout(reset, 2500)
},
gcTime: 100,
})
</script>
@@ -0,0 +1,26 @@
<template>
<Button
:href="telegramShareUrl"
target="_blank"
size="small"
severity="secondary"
as="a"
class="text-nowrap"
outlined
rel="noopener noreferrer"
icon="pi pi-telegram"
title="Поделиться ссылкой в Telegram"
label="отправить ссылку в Telegram"
/>
</template>
<script setup lang="ts">
import { computed } from 'vue'
const props = defineProps<{ shareUrl: string; shareText: string }>()
const telegramShareUrl = computed(
() =>
`https://t.me/share/url?url=${encodeURIComponent(props.shareUrl)}&text=${encodeURIComponent(props.shareText)}`, // &photo=${shareImage}`
)
</script>
@@ -1,5 +1,5 @@
<template>
<RouterLink :to="`/manage/qr/${qrCode.uri}/ready`" class="p-4 rounded-lg bg-white shadow-md">
<RouterLink :to="`/manage/qr/${qrCode.uri}`" class="p-4 rounded-lg bg-white shadow-md">
<div class="flex items-center justify-between mb-2">
<div class="flex items-center">
<QRCodePlacementIcon :placement="qrCode.placement" />
+1
View File
@@ -1,6 +1,7 @@
export const ROUTE_NAMES = {
HOME: 'home',
CREATE: 'create',
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',
+6
View File
@@ -25,6 +25,12 @@ const routes = [
component: () => import('@/views/ManageDashboardView.vue'),
props: true,
},
{
path: '/manage/qr/:qr_code_uri',
name: ROUTE_NAMES.MANAGE_QR_CODE,
component: () => import('@/views/ManageQRCodeView.vue'),
props: true,
},
{
path: '/manage/qr/:qr_code_uri/ready',
name: ROUTE_NAMES.MANAGE_QR_CODE_READY,
@@ -73,18 +73,13 @@
</a>
</span>
</div>
<Button
:href="telegramShareUrl"
target="_blank"
size="small"
severity="secondary"
as="a"
class="p-button-outlined"
rel="noopener noreferrer"
title="Поделиться ссылкой в Telegram"
icon="pi pi-telegram"
label="отправить ссылку в Telegram"
/>
<div class="flex gap-2 justify-center mt-2">
<LinkCopyButton :url="qrCodeDoc.url" />
<LinkShareTelegramButton
:shareUrl="qrCodeDoc.url"
shareText="Используйте эту ссылку НаСвязи!"
/>
</div>
</div>
<!-- Инструкции по размещению -->
@@ -149,13 +144,6 @@ const sanitizedFileName = computed(() => {
return sanitized ? `${sanitized} QR-Code - НаСвязи` : 'QR-Code - НаСвязи'
})
const shareText = 'Используйте эту ссылку НаСвязи!'
const telegramShareUrl = computed(
() =>
qrCodeDoc.value?.url &&
`https://t.me/share/url?url=${encodeURIComponent(qrCodeDoc.value.url)}&text=${encodeURIComponent(shareText)}`, // &photo=${shareImage}`
)
// Генерация URL для WhatsApp
// const whatsappShareUrl = computed(
// () => `https://api.whatsapp.com/send?text=${shareText}%0A${siteUrl}`,
@@ -0,0 +1,103 @@
<template>
<div class="p-6">
<h1 class="text-2xl font-bold mb-4 text-center lg:text-left">Управление QR-кодом</h1>
<QueryRender :query="qrCodeQuery" #default="{ data: qrCodeDoc }">
<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">
<strong>Ссылка: </strong>
<a :href="qrCodeDoc.url" class="text-blue-500" target="_blank">{{ qrCodeDoc.url }}</a>
<LinkCopyButton :url="qrCodeDoc.url" />
<LinkShareTelegramButton
:shareUrl="qrCodeDoc.url"
shareText="Используйте эту ссылку НаСвязи!"
/>
</div>
<div>
<p><strong>Создано:</strong> {{ formatDate(qrCodeDoc.created_at) }}</p>
<!-- <p><strong>Количество сканирований:</strong> {{ qrCodeDoc.scanCount }}</p>-->
<!-- <p><strong>Последнее сканирование:</strong> {{ formatDate(qrCodeDoc.lastScan) }}</p>-->
</div>
</div>
</QueryRender>
<!-- <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>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import type { QRCodeDocument } from '@hereconnect/types'
import { getQrCodeDocument } from '@/api/qrCode'
import CopyLinkButton from '@/components/CopyLinkButton.vue'
const props = defineProps<{ qr_code_uri: QRCodeDocument['uri'] }>()
const qrCodeUri = computed(() => props.qr_code_uri)
const qrCodeQuery = useQuery({
queryKey: ['qrCode', qrCodeUri],
queryFn: () => getQrCodeDocument(qrCodeUri.value),
})
// const { data: chatsData } = useQuery({
// queryKey: ['chats', qrCodeUri],
// queryFn: () => messagesDbRemote.find({ selector: { qr_code_uri: qrCodeUri } }),
// })
// const chats = computed(() => chatsData.value?.docs || [])
const formatDate = (date: string) => {
if (!date) return '—'
const options: Intl.DateTimeFormatOptions = { year: 'numeric', month: 'long', day: 'numeric' }
return new Date(date).toLocaleDateString(['ru-RU'], options)
}
</script>
+13 -1
View File
@@ -113,7 +113,19 @@ export default defineConfig(({ mode }) => {
Info(),
AutoImport({
imports: ['vue', 'vue-router', unheadComposablesImports[0]],
imports: [
'vue',
'vue-router',
unheadComposablesImports[0],
{
from: '@tanstack/vue-query',
imports: ['useQuery', 'useMutation'],
},
// {
// from: '@hereconnect/types',
// type: true,
// },
],
}),
Components({