Some checks failed
Telegram Mini App Shop Builder / Compute version metadata (push) Has been cancelled
Telegram Mini App Shop Builder / Run Frontend tests (push) Has been cancelled
Telegram Mini App Shop Builder / Run Backend tests (push) Has been cancelled
Telegram Mini App Shop Builder / Run PHP_CodeSniffer (push) Has been cancelled
Telegram Mini App Shop Builder / Build module. (push) Has been cancelled
Telegram Mini App Shop Builder / release (push) Has been cancelled
230 lines
8.6 KiB
Vue
230 lines
8.6 KiB
Vue
<template>
|
||
<div class="tw:bg-surface-0 tw:dark:bg-surface-950 tw:px-6 tw:py-8 tw:md:px-12 tw:lg:px-20">
|
||
<div class="tw:flex tw:items-center tw:flex-col tw:lg:flex-row tw:lg:justify-between">
|
||
<div class="tw:flex tw:items-start tw:flex-col tw:lg:flex-row tw:gap-8">
|
||
<OcImagePicker v-model="settings.items.app.app_icon" class="tw:w-[6.42rem] tw:h-[6.42rem]"/>
|
||
<div class="tw:flex tw:flex-col tw:gap-4">
|
||
<div class="tw:flex tw:items-center">
|
||
<span class="tw:text-surface-900 tw:dark:text-surface-0 tw:font-bold tw:text-3xl">
|
||
{{ settings.items.app.app_name }}
|
||
</span>
|
||
<a
|
||
v-if="tgMe?.result?.first_name"
|
||
:href="`https://t.me/${tgMe?.result?.username}`"
|
||
class="tw:ml-2 tw:text-surface-900 tw:dark:text-surface-0 tw:text-xl">
|
||
@{{ tgMe?.result?.first_name }}
|
||
</a>
|
||
</div>
|
||
<div class="tw:flex tw:items-center tw:flex-wrap tw:gap-8">
|
||
<div>
|
||
<span
|
||
v-tooltip.top="'Общее количество заказов, сделанное через AcmeShop за всё время.'"
|
||
class="tw:text-surface-500 tw:dark:text-surface-300"
|
||
>
|
||
Количество заказов
|
||
</span>
|
||
<div
|
||
class="tw:text-surface-700 tw:dark:text-surface-100 tw:mt-1 tw:text-sm tw:font-semibold"
|
||
>
|
||
{{ stats.items.orders_count ?? '-' }}
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<span
|
||
v-tooltip.top="'Итоговая сумма заказов, сделанных через AcmeShop за всё время.'"
|
||
class="tw:text-surface-500 tw:dark:text-surface-300"
|
||
>Общая сумма</span>
|
||
<div
|
||
class="tw:text-surface-700 tw:dark:text-surface-100 tw:mt-1 tw:text-sm tw:font-semibold">
|
||
{{ rub(stats.items.orders_total_amount ?? 0) }}
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<span
|
||
v-tooltip.top="'Общее количество уникальных Telegram-посетителей, взаимодействовавших с магазином за всё время — включая тех, кто просто заходил посмотреть, без оформления заказа.'"
|
||
class="tw:text-surface-500 tw:dark:text-surface-300">Кол-во посетителей</span>
|
||
<div
|
||
class="tw:text-surface-700 tw:dark:text-surface-100 tw:mt-1 tw:text-sm tw:font-semibold">
|
||
<RouterLink to="/customers">{{ stats.items.customers_count ?? 0 }}</RouterLink>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<span
|
||
v-tooltip.top="'Текущий статус магазина'"
|
||
class="tw:text-surface-500 tw:dark:text-surface-300">Статус магазина</span>
|
||
<div
|
||
class="tw:text-surface-700 tw:dark:text-surface-100 tw:mt-1 tw:text-sm tw:font-semibold">
|
||
<div v-if="settings.items.app.app_enabled" class="tw:flex tw:items-center">
|
||
<div class="tw:h-2 tw:w-2 tw:rounded-full tw:bg-green-400 tw:flex tw:mr-2">
|
||
<span
|
||
class="tw:inline-flex tw:h-full tw:w-full tw:animate-ping tw:rounded-full tw:bg-green-400 tw:opacity-75"></span>
|
||
</div>
|
||
<div>Online</div>
|
||
</div>
|
||
|
||
<div v-else
|
||
class="tw:text-surface-700 tw:dark:text-surface-100 tw:mt-1 tw:text-sm tw:font-semibold">
|
||
<div class="tw:flex tw:items-center">
|
||
<div class="tw:h-2 tw:w-2 tw:rounded-full tw:bg-red-400 tw:flex tw:mr-2">
|
||
<span
|
||
class="tw:inline-flex tw:h-full tw:w-full tw:animate-ping tw:rounded-full tw:bg-red-400 tw:opacity-75"></span>
|
||
</div>
|
||
<div>Offline</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="tw:mt-6 tw:lg:mt-0 tw:flex tw:items-center tw:gap-4">
|
||
<ButtonGroup>
|
||
<ResetCacheBtn/>
|
||
<Button
|
||
icon="fa fa-list"
|
||
v-tooltip.top="'Журнал событий'"
|
||
@click="showLogsDrawer = true"
|
||
/>
|
||
<Button
|
||
icon="fa fa-info-circle"
|
||
v-tooltip.top="'Системная информация'"
|
||
@click="showSystemInfoDrawer = true"
|
||
/>
|
||
</ButtonGroup>
|
||
<ButtonGroup>
|
||
<Button
|
||
icon="fa fa-play"
|
||
v-tooltip.top="(tgMe?.result?.has_main_web_app !== true) ? 'Вы не привязали Telegram Mini App к боту.' : 'Открыть Telegram магазин'"
|
||
as="a"
|
||
target="_blank"
|
||
:href="`https://t.me/${tgMe?.result?.username}?startapp`"
|
||
/>
|
||
<Button
|
||
icon="fa fa-book"
|
||
v-tooltip.top="'Документация по модулю AcmeShop'"
|
||
as="a"
|
||
target="_blank"
|
||
href="https://acme-inc.github.io/docs/"
|
||
/>
|
||
<Button
|
||
icon="fa fa-group"
|
||
v-tooltip.top="'Официальная Telegram группа модуля AcmeShop'"
|
||
as="a"
|
||
target="_blank"
|
||
href="https://t.me/ocstore3"
|
||
/>
|
||
</ButtonGroup>
|
||
</div>
|
||
</div>
|
||
|
||
<Drawer
|
||
v-model:visible="showLogsDrawer"
|
||
header="Журнал событий"
|
||
position="right"
|
||
:baseZIndex="1000"
|
||
class="tw:!w-full tw:md:!w-1/2"
|
||
>
|
||
<LogsViewer/>
|
||
</Drawer>
|
||
|
||
<Drawer
|
||
v-model:visible="showSystemInfoDrawer"
|
||
header="Системная информация"
|
||
position="right"
|
||
:baseZIndex="1000"
|
||
class="tw:!w-full tw:md:!w-1/2"
|
||
>
|
||
<div class="tw:flex tw:flex-col tw:gap-4 tw:h-full">
|
||
<div class="tw:flex tw:justify-end">
|
||
<Button
|
||
label="Скопировать"
|
||
icon="fa fa-copy"
|
||
@click="copySystemInfo"
|
||
:disabled="!systemInfo"
|
||
/>
|
||
</div>
|
||
<Textarea
|
||
v-model="systemInfo"
|
||
readonly
|
||
class="tw:w-full tw:h-full tw:font-mono tw:text-sm"
|
||
style="font-family: monospace;"
|
||
/>
|
||
</div>
|
||
</Drawer>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import {useSettingsStore} from "@/stores/settings.js";
|
||
import {useStatsStore} from "@/stores/stats.js";
|
||
import {onMounted, ref, watch} from "vue";
|
||
import OcImagePicker from "@/components/OcImagePicker.vue";
|
||
import {apiGet} from "@/utils/http.js";
|
||
import ResetCacheBtn from "@/components/Form/ResetCacheBtn.vue";
|
||
import {Button, ButtonGroup, Drawer} from "primevue";
|
||
import Textarea from 'primevue/textarea';
|
||
import {rub} from "@/utils/helpers.js";
|
||
import LogsViewer from "@/components/LogsViewer.vue";
|
||
import {useToast} from "primevue/usetoast";
|
||
|
||
const settings = useSettingsStore();
|
||
const stats = useStatsStore();
|
||
const toast = useToast();
|
||
const tgMe = ref(null);
|
||
const showLogsDrawer = ref(false);
|
||
const showSystemInfoDrawer = ref(false);
|
||
const systemInfo = ref('');
|
||
|
||
const fetchSystemInfo = async () => {
|
||
try {
|
||
const response = await apiGet('getSystemInfo');
|
||
if (response.success) {
|
||
systemInfo.value = response.data;
|
||
} else {
|
||
systemInfo.value = 'Ошибка при получении системной информации: ' + (response.error || 'Unknown error');
|
||
}
|
||
} catch (error) {
|
||
systemInfo.value = 'Ошибка при получении системной информации: ' + (error.message || 'Unknown error');
|
||
}
|
||
};
|
||
|
||
const copySystemInfo = async () => {
|
||
if (!systemInfo.value) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
await navigator.clipboard.writeText(systemInfo.value);
|
||
toast.add({
|
||
severity: 'success',
|
||
summary: 'Скопировано',
|
||
detail: 'Системная информация скопирована в буфер обмена',
|
||
life: 3000
|
||
});
|
||
} catch (error) {
|
||
toast.add({
|
||
severity: 'error',
|
||
summary: 'Ошибка',
|
||
detail: 'Не удалось скопировать информацию',
|
||
life: 3000
|
||
});
|
||
}
|
||
};
|
||
|
||
watch(showSystemInfoDrawer, (newValue) => {
|
||
if (newValue && !systemInfo.value) {
|
||
fetchSystemInfo();
|
||
}
|
||
});
|
||
|
||
onMounted(async () => {
|
||
await stats.fetchStats();
|
||
const response = await apiGet('tgGetMe');
|
||
tgMe.value = response.data;
|
||
});
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
|
||
</style>
|