Vendored
+1
@@ -28,6 +28,7 @@ declare module 'vue' {
|
||||
InputText: typeof import('primevue/inputtext')['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']
|
||||
QueryRender: typeof import('./src/components/QueryRender.vue')['default']
|
||||
RadioButton: typeof import('primevue/radiobutton')['default']
|
||||
ReadContactInfo: typeof import('./src/components/read/ReadContactInfo.vue')['default']
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
"name": "НаСвязи",
|
||||
"short_name": "НаСвязи",
|
||||
"categories": ["communication", "utilities", "social", "productivity"],
|
||||
"start_url": "/",
|
||||
"id": "https://hereconnect.condev.ru",
|
||||
"start_url": "https://hereconnect.condev.ru/manage/dashboard",
|
||||
"scope": "https://hereconnect.condev.ru/",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
|
||||
@@ -5,16 +5,26 @@ type Props = {
|
||||
query: UseQueryReturnType<R, E>
|
||||
}
|
||||
|
||||
const slots = defineSlots<{
|
||||
default?: (slotBindings: { data: R }) => void
|
||||
error?: (slotBindings: { error: E; reload: typeof refetch }) => void
|
||||
loading?: () => void
|
||||
}>()
|
||||
|
||||
const { query } = defineProps<Props>()
|
||||
|
||||
const { data, error, refetch, isSuccess, isError, isLoading } = query
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<slot v-if="isSuccess" name="default" :data="data! as R" />
|
||||
<slot v-else-if="isLoading" name="loading"><p class="text-gray-500">Загрузка данных...</p></slot>
|
||||
<slot v-else-if="isError" name="error" :error="error as E" :reload="refetch">
|
||||
<slot v-if="isSuccess" name="default" :data="data!" />
|
||||
<slot v-else-if="isLoading" name="loading">
|
||||
<p class="text-gray-500" v-if="!slots.loading">Загрузка данных...</p>
|
||||
</slot>
|
||||
<slot v-else-if="isError" name="error" :error="error!" :reload="refetch">
|
||||
<template v-if="!slots.error">
|
||||
Ошибка загрузки данных:
|
||||
{{ error && typeof error === 'object' && 'message' in error ? error.message : error }}
|
||||
</template>
|
||||
</slot>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<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">
|
||||
<i :class="['pi', iconClass, 'text-gray-500', 'mr-2']" />
|
||||
<h2 class="text-lg font-semibold">{{ qrCode.name }}</h2>
|
||||
</div>
|
||||
<!-- <Button-->
|
||||
<!-- label="Настроить"-->
|
||||
<!-- :to="`/manage/${qrCode.uri}/settings`"-->
|
||||
<!-- as="router-link"-->
|
||||
<!-- class="p-button-outlined p-button-sm"-->
|
||||
<!-- />-->
|
||||
</div>
|
||||
<div class="text-gray-500">Создано: {{ formatedCreatedAt }}</div>
|
||||
</RouterLink>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, defineProps } from 'vue'
|
||||
import type { QRCodeDocument } from '@hereconnect/types'
|
||||
|
||||
const props = defineProps<{
|
||||
qrCode: QRCodeDocument
|
||||
}>()
|
||||
|
||||
const iconClass = computed(() => {
|
||||
const icons: Record<string, string> = {
|
||||
car: 'pi-car',
|
||||
pet: 'pi-paw',
|
||||
door: 'pi-door-closed',
|
||||
store: 'pi-shop',
|
||||
restaurant_table: 'pi-utensils',
|
||||
other: 'pi-question-circle',
|
||||
}
|
||||
return icons[props.qrCode.placement] || 'pi-question-circle'
|
||||
})
|
||||
|
||||
const formatDate = (date: string) => {
|
||||
// Форматирование даты
|
||||
const options: Intl.DateTimeFormatOptions = { year: 'numeric', month: 'long', day: 'numeric' }
|
||||
return new Date(date).toLocaleDateString(['ru-RU'], options)
|
||||
}
|
||||
|
||||
const formatedCreatedAt = computed(() => formatDate(props.qrCode.created_at))
|
||||
</script>
|
||||
@@ -3,6 +3,7 @@ export const ROUTE_NAMES = {
|
||||
CREATE: 'create',
|
||||
MANAGE_QR_CODE_READY: 'manage-qr-code-ready',
|
||||
MANAGE_QR_CODE_CHAT: 'manage-qr-code-chat',
|
||||
MANAGE_DASHBOARD: 'manage-dashboard',
|
||||
READ_QR_CODE: 'read-qr-code',
|
||||
DONATE: 'donate',
|
||||
PUBLIC_CHAT: 'public-chat',
|
||||
|
||||
@@ -14,13 +14,19 @@ const routes = [
|
||||
component: () => import('@/views/CreateView.vue'),
|
||||
},
|
||||
{
|
||||
path: '/manage/:qr_code_uri/ready',
|
||||
path: '/manage/dashboard',
|
||||
name: ROUTE_NAMES.MANAGE_DASHBOARD,
|
||||
component: () => import('@/views/ManageDashboardView.vue'),
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: '/manage/qr/:qr_code_uri/ready',
|
||||
name: ROUTE_NAMES.MANAGE_QR_CODE_READY,
|
||||
component: () => import('@/views/ManageQRCodeReadyView.vue'),
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: '/manage/:qr_code_uri/chat/:chat',
|
||||
path: '/manage/qr/:qr_code_uri/chat/:chat',
|
||||
name: ROUTE_NAMES.MANAGE_QR_CODE_CHAT,
|
||||
component: () => import('@/views/ManageQRCodeChatView.vue'),
|
||||
props: true,
|
||||
@@ -37,7 +43,7 @@ const routes = [
|
||||
component: () => import('@/views/DonateView.vue'),
|
||||
},
|
||||
{
|
||||
path: '/chat/:qr_code_uri/:chat',
|
||||
path: '/read/:qr_code_uri/chat/:chat',
|
||||
name: ROUTE_NAMES.PUBLIC_CHAT,
|
||||
component: () => import('@/views/PublicChatView.vue'),
|
||||
props: true,
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div class="p-6">
|
||||
<h1 class="text-2xl font-bold mb-4 text-center">Управление QR-кодами</h1>
|
||||
<QueryRender :query="query">
|
||||
<template #default="{ data }">
|
||||
<div v-if="data.rows.length > 0" 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!" />
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<Button
|
||||
icon="pi pi-qrcode"
|
||||
label="Создать QR‑код"
|
||||
to="/create"
|
||||
as="router-link"
|
||||
class="p-button-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-center text-gray-500">
|
||||
<p>Нет созданных QR-кодов. Вы можете создать их прямо сейчас!</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>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
import { qrCodeDbLocal } from '@/api/dbs'
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: ['qrCodes'],
|
||||
queryFn: async () => {
|
||||
return qrCodeDbLocal.allDocs({
|
||||
include_docs: true,
|
||||
startkey: 'qr_code:',
|
||||
endkey: 'qr_code:\uffff',
|
||||
})
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -121,7 +121,7 @@ const start = async () => {
|
||||
requireInteraction: true,
|
||||
timestamp,
|
||||
data: {
|
||||
url: `/manage/${msgDoc.qr_code_uri}/chat/${msgDoc.chat}#message-${msgDoc.created_at}`,
|
||||
url: `/manage/qr/${msgDoc.qr_code_uri}/chat/${msgDoc.chat}#message-${msgDoc.created_at}`,
|
||||
payload: { tag },
|
||||
},
|
||||
} as const;
|
||||
@@ -200,7 +200,7 @@ const start = async () => {
|
||||
requireInteraction: true,
|
||||
timestamp,
|
||||
data: {
|
||||
url: `/chat/${msgDoc.qr_code_uri}/${msgDoc.chat}#message-${msgDoc.created_at}`,
|
||||
url: `/read/qr/${msgDoc.qr_code_uri}/chat/${msgDoc.chat}#msg-${msgDoc.created_at}`,
|
||||
payload: { tag },
|
||||
},
|
||||
} as const;
|
||||
|
||||
Reference in New Issue
Block a user