Files
onboard/resources/js/Pages/Index.vue

209 lines
8.5 KiB
Vue

<script setup>
import AppLayout from "../Layouts/AppLayout.vue"
import { useAuthStore } from "../Stores/auth.js"
import { NEl, NFlex, NTag, NText, NAvatar, NIcon } from 'naive-ui'
import ActionTile from "../Components/ActionTile.vue"
import { computed, ref } from "vue"
import { format, getHours } from "date-fns"
import { ru } from "date-fns/locale"
import { useNow } from "@vueuse/core"
import { TbArticle, TbChartTreemap, TbDoorExit, TbUserCog, TbStethoscope } from "vue-icons-plus/tb"
import SelectUserModal from "./Report/Components/SelectUserModal.vue"
import { Link, router } from "@inertiajs/vue3"
import { useServerTime } from "../Composables/useServerTime.js"
import { useThemeVars } from "naive-ui"
const authStore = useAuthStore()
const themeVars = useThemeVars()
const { serverTime } = useServerTime()
const now = useNow({ interval: 1000 })
const timeSource = computed(() => {
if (serverTime.value && !isNaN(serverTime.value.getTime())) return serverTime.value
return now.value
})
const currentTime = computed(() => format(timeSource.value, 'HH:mm:ss'))
const currentDate = computed(() => {
const formatted = format(timeSource.value, 'EEEE, d MMMM', { locale: ru })
return formatted.charAt(0).toUpperCase() + formatted.slice(1)
})
const greeting = computed(() => {
const h = getHours(timeSource.value)
if (h >= 5 && h < 12) return 'Доброе утро'
if (h >= 12 && h < 18) return 'Добрый день'
if (h >= 18 && h < 23) return 'Добрый вечер'
return 'Доброй ночи'
})
const initials = computed(() => {
const parts = (authStore.user?.name ?? '').trim().split(/\s+/)
return parts.slice(0, 2).map(p => p[0]?.toUpperCase() ?? '').join('')
})
const roleTagTypes = {
admin: 'error', gv: 'warning', zam: 'warning',
zav: 'info', dej: 'primary', nurse: 'success',
}
const roleName = computed(() => authStore.user?.role.name ?? '')
const roleColor = computed(() => roleTagTypes[authStore.role?.slug] ?? 'default')
const deptName = computed(() => authStore.userDepartment?.name_full ?? null)
const isLockReportButton = computed(() => {
if (authStore.isSeniorStaff) return false
if (!serverTime.value || isNaN(serverTime.value.getTime())) return true
return getHours(serverTime.value) < 6 || getHours(serverTime.value) >= 7
})
const showSelectUserModal = ref(false)
const showSelectNurseUserModal = ref(false)
const openReport = (path) => {
if (authStore.isDoctor) {
showSelectUserModal.value = true
} else {
router.visit(path)
}
}
const openNurseReport = () => {
// if (authStore.isSeniorStaff) {
// showSelectNurseUserModal.value = true
// } else {
// router.visit('/nurse/report')
// }
showSelectNurseUserModal.value = true
}
const cardColor = computed(() => themeVars.value.cardColor)
const dividerColor = computed(() => themeVars.value.dividerColor)
</script>
<template>
<AppLayout>
<div class="flex flex-col justify-center items-center" style="min-height: calc(100vh - 48px);">
<NFlex vertical :size="10" class="max-w-2xl w-full" style="padding: 0 16px 24px;">
<!-- Профиль -->
<NEl class="profile-card rounded-2xl" style="padding: 18px 22px;">
<NFlex justify="space-between" align="center" :wrap="false">
<NFlex align="center" :size="12" :wrap="false" style="min-width: 0; flex: 1;">
<NAvatar
round :size="46"
style="
background: color-mix(in srgb, var(--primary-color) 22%, transparent);
color: var(--primary-color);
font-size: 17px; font-weight: 700; flex-shrink: 0;
"
>{{ initials }}</NAvatar>
<div style="min-width: 0;">
<NText depth="3" style="font-size: 12px; display: block; margin-bottom: 2px; line-height: 1;">
{{ greeting }}
</NText>
<NText style="font-size: 15px; font-weight: 600; display: block; line-height: 1.25; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
{{ authStore.user?.name }}
</NText>
<NFlex align="center" :size="6" style="margin-top: 6px;" :wrap="false">
<NTag :type="roleColor" size="small" round :bordered="false">{{ roleName }}</NTag>
<template v-if="deptName">
<NEl style="width: 3px; height: 3px; border-radius: 50%; background: currentColor; opacity: .3; flex-shrink: 0;" />
<NText depth="3" style="font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">{{ deptName }}</NText>
</template>
</NFlex>
</div>
</NFlex>
<div style="text-align: right; flex-shrink: 0; margin-left: 16px;">
<NText style="font-size: 26px; font-weight: 700; font-variant-numeric: tabular-nums; display: block; letter-spacing: -1px; line-height: 1.05;">
{{ currentTime }}
</NText>
<NText depth="3" style="font-size: 11px; display: block; margin-top: 4px; white-space: nowrap;">
{{ currentDate }}
</NText>
</div>
</NFlex>
</NEl>
<!-- Действия -->
<div class="bento-grid">
<ActionTile
v-if="authStore.hasPermission('report.create')"
:icon="TbArticle"
title="Заполнить сводную"
description="Ежедневный отчёт врача"
@click="openReport('/report')"
/>
<ActionTile
v-if="authStore.hasPermission('nurse.report.view')"
:icon="TbStethoscope"
title="Журнал пациентов"
description="Записи мед. сестры"
@click="openNurseReport"
/>
<ActionTile
v-if="authStore.hasPermission('stats.view')"
:icon="TbChartTreemap"
title="Статистика"
:description="deptName || 'Просмотр данных'"
:tag="Link"
href="/statistic"
/>
<ActionTile
v-if="authStore.hasPermission('users.manage')"
:icon="TbUserCog"
title="Администрирование"
description="Управление системой"
:tag="Link"
href="/admin"
/>
</div>
<!-- Выход -->
<a href="/logout" class="exit-link">
<NFlex align="center" justify="center" :size="6">
<NIcon size="15"><TbDoorExit /></NIcon>
<NText style="font-size: 13px;">Выйти из системы</NText>
</NFlex>
</a>
</NFlex>
</div>
<SelectUserModal v-model:show="showSelectUserModal" :only-user-department="false" />
<SelectUserModal
v-model:show="showSelectNurseUserModal"
target-path="/nurse/report"
submit-label="Перейти к журналу пациентов"
:with-exists-check="false"
/>
</AppLayout>
</template>
<style scoped>
.profile-card {
background: v-bind(cardColor);
border: 1px solid v-bind(dividerColor);
}
.bento-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 8px;
}
.exit-link {
text-decoration: none;
opacity: 0.45;
transition: opacity .15s;
}
.exit-link:hover {
opacity: 0.85;
}
</style>