#71 - annual summary (#113)

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* fix

* lint fix

* cr fix

* fix
This commit is contained in:
Adrian Hopek
2022-04-07 14:33:15 +02:00
committed by GitHub
parent 84403a762a
commit ff8d6aade6
20 changed files with 1073 additions and 1206 deletions

View File

@@ -68,4 +68,3 @@
::-webkit-scrollbar-thumb:hover {
background: #dadce0;
}

View File

@@ -14,61 +14,71 @@ const types = [
text: 'Urlop wypoczynkowy',
value: 'vacation',
icon: WhiteBalanceSunnyIcon,
color: 'text-amber-500',
color: 'text-amber-300',
border: 'border-amber-300',
},
{
text: 'Urlop na żądanie',
value: 'vacation_on_request',
icon: CommentAlertIcon,
color: 'text-slate-500',
border: 'border-slate-500',
},
{
text: 'Urlop okolicznościowy',
value: 'special_vacation',
icon: StarShootingIcon,
color: 'text-orange-500',
border: 'border-orange-500',
},
{
text: 'Opieka nad dzieckiem art 188 kp',
value: 'childcare_vacation',
icon: BabyCarriageIcon,
color: 'text-purple-500',
border: 'border-purple-500',
},
{
text: 'Urlop szkoleniowy',
value: 'training_vacation',
icon: HumanMaleBoardIcon,
color: 'text-blumilk-500',
border: 'border-blumilk-500',
},
{
text: 'Urlop bezpłatny',
value: 'unpaid_vacation',
icon: CurrencyUsdOffIcon,
color: 'text-emerald-500',
border: 'border-emerald-500',
},
{
text: 'Wolontariat',
value: 'volunteering_vacation',
icon: HandHeartOutlineIcon,
color: 'text-pink-500',
border: 'border-pink-500',
},
{
text: 'Odbiór za święto',
value: 'time_in_lieu',
icon: CalendarCheckIcon,
color: 'text-stone-500',
border: 'border-stone-500',
},
{
text: 'Zwolnienie lekarskie',
value: 'sick_vacation',
icon: MedicalBagIcon,
color: 'text-rose-500',
border: 'border-rose-500',
},
{
text: 'Nieobecność',
value: 'absence',
icon: CalendarRemoveIcon,
color: 'text-cyan-500',
border: 'border-cyan-500',
},
]

View File

@@ -4,9 +4,11 @@ import { usePage } from '@inertiajs/inertia-vue3'
export default function useCurrentYearPeriodInfo() {
const minDate = computed(() => new Date(usePage().props.value.years.selected.year, 0, 1))
const maxDate = computed(() => new Date(usePage().props.value.years.selected.year, 11, 31))
const year = computed(() => usePage().props.value.years.selected.year)
return {
minDate,
maxDate,
year,
}
}

View File

@@ -0,0 +1,183 @@
<template>
<InertiaHead title="Podsumowanie roczne" />
<div class="bg-white shadow-md">
<div class="p-4 sm:px-6">
<h2 class="text-lg font-medium leading-6 text-gray-900">
Podsumowanie roczne
</h2>
</div>
<div
class="grid grid-cols-1 gap-8 py-8 px-4 mx-auto max-w-3xl border-t border-gray-200 sm:grid-cols-2 sm:px-6 xl:grid-cols-3 xl:px-8 xl:max-w-none 2xl:grid-cols-4"
>
<section
v-for="month in months"
:key="month.name"
class="text-center"
>
<h2 class="font-semibold text-gray-900 capitalize">
{{ month.name }}
</h2>
<div class="grid grid-cols-7 mt-6 text-xs font-semibold leading-6 text-gray-500">
<div>Pn</div>
<div>Wt</div>
<div>Śr</div>
<div>Cz</div>
<div>Pt</div>
<div>Sb</div>
<div>Nd</div>
</div>
<div class="grid grid-cols-7 mt-2 text-sm ring-1 ring-gray-200 shadow">
<template
v-for="(day, dayIdx) in month.days"
:key="dayIdx"
>
<template v-if="day.isCurrentMonth">
<Popper
v-if="day.isVacation || day.isPendingVacation"
open-delay="200"
hover
offset-distance="0"
>
<div :class="[day.isPendingVacation && 'mx-0.5']">
<button :class="[day.isPendingVacation && `border-dashed`, `${getVacationBorder(day)} isolate bg-white w-full hover:bg-blumilk-25 border-b-4 py-1.5 font-medium`]">
<time
:datetime="day.date.toISODate()"
:class="[ day.isToday && 'bg-blumilk-500 font-semibold text-white rounded-full', 'mx-auto flex h-7 w-7 p-4 items-center justify-center']"
>
{{ day.date.day }}
</time>
</button>
</div>
<template #content>
<VacationPopup :vacation="getVacationInfo(day)" />
</template>
</Popper>
<Popper
v-else-if="day.isHoliday"
open-delay="200"
hover
offset-distance="0"
>
<button class="py-1.5 w-full font-medium bg-white hover:bg-blumilk-25 border-b-4 border-transparent">
<time
:datetime="day.date.toISODate()"
:class="[ day.isToday && 'bg-blumilk-500 font-semibold text-white rounded-full', 'text-red-700 font-bold mx-auto flex h-7 w-7 p-4 items-center justify-center']"
>
{{ day.date.day }}
</time>
</button>
<template #content>
<div class="py-2 px-6 text-sm font-semibold text-left text-gray-700 bg-white rounded-lg border border-gray-400">
{{ holidays[day.date.toISODate()] }}
</div>
</template>
</Popper>
<button
v-else
class="py-1.5 w-full font-medium bg-white hover:bg-blumilk-25 border-b-4 border-transparent"
>
<time
:datetime="day.date.toISODate()"
:class="[ day.isToday && 'bg-blumilk-500 font-semibold text-white rounded-full', day.isWeekend && 'text-red-700 font-bold', 'mx-auto flex h-7 w-7 p-4 items-center justify-center']"
>
{{ day.date.day }}
</time>
</button>
</template>
<div
v-else
class="focus:z-10 py-1.5 w-full font-medium text-gray-400 bg-gray-50 border-b-4 border-transparent"
>
<div class="flex justify-center items-center p-4 mx-auto w-7 h-7">
<time :datetime="day.date.toISODate()">
{{ day.date.day }}
</time>
</div>
</div>
</template>
</div>
</section>
</div>
</div>
</template>
<script setup>
import { DateTime } from 'luxon'
import useVacationTypeInfo from '@/Composables/vacationTypeInfo'
import useCurrentYearPeriodInfo from '@/Composables/yearPeriodInfo'
import Popper from 'vue3-popper'
import VacationPopup from '@/Shared/VacationPopup'
const props = defineProps({
holidays: Object,
vacations: Object,
pendingVacations: Object,
})
const { findType } = useVacationTypeInfo()
const { year } = useCurrentYearPeriodInfo()
const months = []
for (let i = 1; i < 13; i++) {
const currentMonth = DateTime.fromObject({ year: year.value, month: i }).startOf('month')
const start = currentMonth.startOf('week')
const end = currentMonth.endOf('month').endOf('week')
const month = {
name: currentMonth.monthLong,
days: [],
}
for (let day = start; day < end; day = day.plus({ day: 1 })) {
const isCurrentMonth = isInCurrentMonth(day, currentMonth)
month.days.push({
date: day,
isCurrentMonth: isCurrentMonth,
isToday: isCurrentMonth && isToday(day),
isWeekend: isWeekend(day),
isVacation: isCurrentMonth && isVacation(day),
isPendingVacation: isCurrentMonth && isPendingVacation(day),
isHoliday: isHoliday(day),
})
}
months.push(month)
}
function isHoliday(date) {
return props.holidays[date.toISODate()] !== undefined
}
function isVacation(date) {
return props.vacations[date.toISODate()] !== undefined
}
function isPendingVacation(date) {
return props.pendingVacations[date.toISODate()] !== undefined
}
function isToday(date) {
return DateTime.now().hasSame(date, 'year') && DateTime.now().hasSame(date, 'day')
}
function isInCurrentMonth(date, currentMonth) {
return currentMonth.hasSame(date, 'month')
}
function isWeekend(date) {
return date.weekday === 6 || date.weekday === 7
}
function getVacationBorder(day) {
const type = findType(getVacationInfo(day).type)
return type.border
}
function getVacationInfo(day) {
return day.isVacation ? props.vacations[day.date.toISODate()] : props.pendingVacations[day.date.toISODate()]
}
</script>

View File

@@ -277,6 +277,7 @@ import {
UserGroupIcon,
XIcon,
SunIcon,
ClipboardListIcon,
StarIcon,
CalendarIcon,
DocumentTextIcon,
@@ -335,6 +336,14 @@ const navigation = computed(() =>
icon: SunIcon,
can: props.auth.can.manageVacationLimits,
},
{
name: 'Podsumowanie roczne',
href: '/vacation/annual-summary',
component: 'AnnualSummary',
icon: ClipboardListIcon,
can: true,
},
{
name: 'Użytkownicy',
href: '/users',

View File

@@ -1,7 +1,7 @@
<template>
<Popper
hover
class="w-full h-full"
class="w-full"
>
<div class="flex text-white bg-white">
<div
@@ -27,7 +27,7 @@
</div>
</div>
<template #content>
<div class="py-2 px-4 text-gray-900 bg-white rounded-md shadow-md text-md flext">
<div class="py-2 px-4 text-gray-900 bg-white rounded-md shadow-md text-md">
<div class="flex items-center font-normal">
<i
class="inline-block mr-3 w-5 h-3"

View File

@@ -0,0 +1,62 @@
<template>
<div class="py-2 px-6 text-left text-gray-900 whitespace-nowrap bg-white rounded-lg border border-gray-400">
<dl class="divide-y divide-gray-200">
<div class="py-2 space-y-1">
<dt class="text-sm font-medium text-gray-500">
Nr wniosku
</dt>
<dd class="text-sm text-gray-900">
<InertiaLink
:href="`/vacation/requests/${vacation.id}`"
class="font-semibold text-blumilk-600 hover:text-blumilk-500 hover:underline"
>
{{ vacation.name }}
</InertiaLink>
</dd>
</div>
<div class="py-2 space-y-1">
<dt class="text-sm font-medium text-gray-500">
Rodzaj urlopu
</dt>
<dd class="mt-1 text-sm text-gray-900">
<VacationType :type="vacation.type" />
</dd>
</div>
<div class="py-2 space-y-1">
<dt class="text-sm font-medium text-gray-500">
Obecny status
</dt>
<dd class="mt-1 text-sm text-gray-900">
<Status :status="vacation.state" />
</dd>
</div>
<div class="py-2 space-y-1">
<dt class="text-sm font-medium text-gray-500">
Data
</dt>
<dd class="mt-1 text-sm text-gray-900">
<template v-if="vacation.days > 1">
{{ vacation.from }} - {{ vacation.to }}
</template>
<template v-else>
{{ vacation.from }}
</template>
<span class="font-semibold">
[Liczba dni: {{ vacation.days }}]
</span>
</dd>
</div>
</dl>
</div>
</template>
<script setup>
import VacationType from '@/Shared/VacationType'
import Status from '@/Shared/Status'
defineProps({
vacation: Object,
})
</script>

View File

@@ -8,7 +8,7 @@
</div>
</div>
<template #content>
<div class="py-1 px-2 text-xs text-gray-900 bg-white shadow-md ">
<div class="py-2 px-4 text-xs text-gray-900 bg-white rounded-lg border border-gray-400 ">
{{ typeInfo.text }}
</div>
</template>