This commit is contained in:
Adrian Hopek 2022-04-07 10:36:48 +02:00
parent fb15811b16
commit 760c9bf514
8 changed files with 74 additions and 160 deletions

View File

@ -42,9 +42,9 @@ class DoesNotExceedLimitRule implements VacationRequestRule
protected function getUserVacationLimit(User $user, YearPeriod $yearPeriod): int protected function getUserVacationLimit(User $user, YearPeriod $yearPeriod): int
{ {
return $user->vacationLimits() return $user->vacationLimits()
->whereBelongsTo($yearPeriod) ->whereBelongsTo($yearPeriod)
->first() ->first()
->days ?? 0; ->days ?? 0;
} }
protected function getVacationDaysWithLimit(User $user, YearPeriod $yearPeriod): int protected function getVacationDaysWithLimit(User $user, YearPeriod $yearPeriod): int

62
package-lock.json generated
View File

@ -20,7 +20,6 @@
"echarts": "^5.3.2", "echarts": "^5.3.2",
"eslit": "^6.0.0", "eslit": "^6.0.0",
"flatpickr": "^4.6.11", "flatpickr": "^4.6.11",
"floating-vue": "^2.0.0-beta.13",
"laravel-mix": "^6.0.43", "laravel-mix": "^6.0.43",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"luxon": "^2.3.1", "luxon": "^2.3.1",
@ -1682,19 +1681,6 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0" "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
} }
}, },
"node_modules/@floating-ui/core": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.3.1.tgz",
"integrity": "sha512-ensKY7Ub59u16qsVIFEo2hwTCqZ/r9oZZFh51ivcLGHfUwTn8l1Xzng8RJUe91H/UP8PeqeBronAGx0qmzwk2g=="
},
"node_modules/@floating-ui/dom": {
"version": "0.1.10",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.1.10.tgz",
"integrity": "sha512-4kAVoogvQm2N0XE0G6APQJuCNuErjOfPW8Ux7DFxh8+AfugWflwVJ5LDlHOwrwut7z/30NUvdtHzQ3zSip4EzQ==",
"dependencies": {
"@floating-ui/core": "^0.3.0"
}
},
"node_modules/@headlessui/vue": { "node_modules/@headlessui/vue": {
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/@headlessui/vue/-/vue-1.5.0.tgz", "resolved": "https://registry.npmjs.org/@headlessui/vue/-/vue-1.5.0.tgz",
@ -4767,18 +4753,6 @@
"integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==",
"dev": true "dev": true
}, },
"node_modules/floating-vue": {
"version": "2.0.0-beta.13",
"resolved": "https://registry.npmjs.org/floating-vue/-/floating-vue-2.0.0-beta.13.tgz",
"integrity": "sha512-C2bGEtdbOXm+2rmkn8W6dTQeh3xJT7YbdHnrbanYDS3vK/1lumdXhYA6j2Qs+9shViNjoVUUND1EhLxLDP2OZA==",
"dependencies": {
"@floating-ui/dom": "^0.1.10",
"vue-resize": "^2.0.0-alpha.1"
},
"peerDependencies": {
"vue": "^3.2.0"
}
},
"node_modules/follow-redirects": { "node_modules/follow-redirects": {
"version": "1.14.9", "version": "1.14.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
@ -8763,14 +8737,6 @@
"resolved": "https://registry.npmjs.org/vue-material-design-icons/-/vue-material-design-icons-5.0.0.tgz", "resolved": "https://registry.npmjs.org/vue-material-design-icons/-/vue-material-design-icons-5.0.0.tgz",
"integrity": "sha512-lYSJFW/TyQqmg7MvUbEB8ua1mwWy/v8qve7QJuA/UWUAXC4/yVUdAm4pg/sM9+k5n7VLckBv6ucOROuGBsGPDQ==" "integrity": "sha512-lYSJFW/TyQqmg7MvUbEB8ua1mwWy/v8qve7QJuA/UWUAXC4/yVUdAm4pg/sM9+k5n7VLckBv6ucOROuGBsGPDQ=="
}, },
"node_modules/vue-resize": {
"version": "2.0.0-alpha.1",
"resolved": "https://registry.npmjs.org/vue-resize/-/vue-resize-2.0.0-alpha.1.tgz",
"integrity": "sha512-7+iqOueLU7uc9NrMfrzbG8hwMqchfVfSzpVlCMeJQe4pyibqyoifDNbKTZvwxZKDvGkB+PdFeKvnGZMoEb8esg==",
"peerDependencies": {
"vue": "^3.0.0"
}
},
"node_modules/vue-style-loader": { "node_modules/vue-style-loader": {
"version": "4.1.3", "version": "4.1.3",
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz", "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
@ -10495,19 +10461,6 @@
"strip-json-comments": "^3.1.1" "strip-json-comments": "^3.1.1"
} }
}, },
"@floating-ui/core": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-0.3.1.tgz",
"integrity": "sha512-ensKY7Ub59u16qsVIFEo2hwTCqZ/r9oZZFh51ivcLGHfUwTn8l1Xzng8RJUe91H/UP8PeqeBronAGx0qmzwk2g=="
},
"@floating-ui/dom": {
"version": "0.1.10",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.1.10.tgz",
"integrity": "sha512-4kAVoogvQm2N0XE0G6APQJuCNuErjOfPW8Ux7DFxh8+AfugWflwVJ5LDlHOwrwut7z/30NUvdtHzQ3zSip4EzQ==",
"requires": {
"@floating-ui/core": "^0.3.0"
}
},
"@headlessui/vue": { "@headlessui/vue": {
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/@headlessui/vue/-/vue-1.5.0.tgz", "resolved": "https://registry.npmjs.org/@headlessui/vue/-/vue-1.5.0.tgz",
@ -12967,15 +12920,6 @@
"integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==",
"dev": true "dev": true
}, },
"floating-vue": {
"version": "2.0.0-beta.13",
"resolved": "https://registry.npmjs.org/floating-vue/-/floating-vue-2.0.0-beta.13.tgz",
"integrity": "sha512-C2bGEtdbOXm+2rmkn8W6dTQeh3xJT7YbdHnrbanYDS3vK/1lumdXhYA6j2Qs+9shViNjoVUUND1EhLxLDP2OZA==",
"requires": {
"@floating-ui/dom": "^0.1.10",
"vue-resize": "^2.0.0-alpha.1"
}
},
"follow-redirects": { "follow-redirects": {
"version": "1.14.9", "version": "1.14.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
@ -15812,12 +15756,6 @@
"resolved": "https://registry.npmjs.org/vue-material-design-icons/-/vue-material-design-icons-5.0.0.tgz", "resolved": "https://registry.npmjs.org/vue-material-design-icons/-/vue-material-design-icons-5.0.0.tgz",
"integrity": "sha512-lYSJFW/TyQqmg7MvUbEB8ua1mwWy/v8qve7QJuA/UWUAXC4/yVUdAm4pg/sM9+k5n7VLckBv6ucOROuGBsGPDQ==" "integrity": "sha512-lYSJFW/TyQqmg7MvUbEB8ua1mwWy/v8qve7QJuA/UWUAXC4/yVUdAm4pg/sM9+k5n7VLckBv6ucOROuGBsGPDQ=="
}, },
"vue-resize": {
"version": "2.0.0-alpha.1",
"resolved": "https://registry.npmjs.org/vue-resize/-/vue-resize-2.0.0-alpha.1.tgz",
"integrity": "sha512-7+iqOueLU7uc9NrMfrzbG8hwMqchfVfSzpVlCMeJQe4pyibqyoifDNbKTZvwxZKDvGkB+PdFeKvnGZMoEb8esg==",
"requires": {}
},
"vue-style-loader": { "vue-style-loader": {
"version": "4.1.3", "version": "4.1.3",
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz", "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",

View File

@ -27,7 +27,6 @@
"echarts": "^5.3.2", "echarts": "^5.3.2",
"eslit": "^6.0.0", "eslit": "^6.0.0",
"flatpickr": "^4.6.11", "flatpickr": "^4.6.11",
"floating-vue": "^2.0.0-beta.13",
"laravel-mix": "^6.0.43", "laravel-mix": "^6.0.43",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"luxon": "^2.3.1", "luxon": "^2.3.1",

View File

@ -1,6 +1,5 @@
@import 'flatpickr/dist/themes/light.css'; @import 'flatpickr/dist/themes/light.css';
@import 'vue-toastification/dist/index.css'; @import 'vue-toastification/dist/index.css';
@import 'floating-vue/dist/style.css';
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;
@ -69,16 +68,3 @@
::-webkit-scrollbar-thumb:hover { ::-webkit-scrollbar-thumb:hover {
background: #dadce0; background: #dadce0;
} }
.v-popper--theme-tooltip .v-popper__inner {
padding: 0;
background: transparent;
}
.v-popper--theme-tooltip .v-popper__arrow-outer {
border-color: #000;
}
.v-popper--theme-tooltip:focus-visible {
outline: none;
}

View File

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

View File

@ -6,8 +6,9 @@
Podsumowanie roczne Podsumowanie roczne
</h2> </h2>
</div> </div>
<div class="max-w-md" /> <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"> 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 <section
v-for="month in months" v-for="month in months"
:key="month.name" :key="month.name"
@ -30,45 +31,64 @@
v-for="(day, dayIdx) in month.days" v-for="(day, dayIdx) in month.days"
:key="dayIdx" :key="dayIdx"
> >
<button <template v-if="day.isCurrentMonth">
v-if="day.isCurrentMonth" <Popper
:class="[day.isVacation && `${getVacationBorder(day.date)}`, day.isPendingVacation && `border-dashed mx-0.5 ${getPendingVacationBorder(day.date)}`, !day.isVacation && !day.isPendingVacation && 'border-transparent', 'relative bg-white hover:bg-blumilk-25 border-b-4 py-1.5 font-medium']"
>
<div :class="[day.isCurrentMonth && (day.isWeekend || day.isHoliday) && 'text-red-600 font-bold', day.isToday && 'bg-blumilk-500 font-semibold text-white rounded-full', 'mx-auto flex h-7 w-7 p-4 items-center justify-center']">
<time :datetime="day.date.toISODate()">
{{ day.date.day }}
</time>
</div>
<Tooltip
v-if="day.isVacation || day.isPendingVacation" v-if="day.isVacation || day.isPendingVacation"
:triggers="['click']" open-delay="200"
placement="bottom" arrow
auto-hide hover
offset-distance="0"
> >
<div class="absolute inset-0" /> <div :class="[day.isPendingVacation && 'mx-0.5']">
<template #popper> <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`]">
<div class="mt-1"> <time
<VacationPopup :vacation="getVacationInfo(day)" /> :datetime="day.date.toISODate()"
</div> :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> </template>
</Tooltip> </Popper>
<Tooltip <Popper
v-else-if="day.isHoliday" v-else-if="day.isHoliday"
:triggers="['click']" open-delay="200"
placement="bottom" arrow
auto-hide hover
offset-distance="0"
> >
<div class="absolute inset-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 #popper> <template #popper>
<div class="py-2 px-6 text-sm font-semibold text-left text-gray-700 bg-white rounded-lg border border-gray-400"> <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()] }} {{ holidays[day.date.toISODate()] }}
</div> </div>
</template> </template>
</Tooltip> </Popper>
</button> <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 <div
v-else v-else
:class="['bg-gray-50 text-gray-400 border-b-4 border-transparent py-1.5 w-full focus:z-10 font-medium']" 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"> <div class="flex justify-center items-center p-4 mx-auto w-7 h-7">
<time :datetime="day.date.toISODate()"> <time :datetime="day.date.toISODate()">
@ -87,7 +107,7 @@
import { DateTime } from 'luxon' import { DateTime } from 'luxon'
import useVacationTypeInfo from '@/Composables/vacationTypeInfo' import useVacationTypeInfo from '@/Composables/vacationTypeInfo'
import useCurrentYearPeriodInfo from '@/Composables/yearPeriodInfo' import useCurrentYearPeriodInfo from '@/Composables/yearPeriodInfo'
import { Tooltip } from 'floating-vue' import Popper from 'vue3-popper'
import VacationPopup from '@/Shared/VacationPopup' import VacationPopup from '@/Shared/VacationPopup'
const props = defineProps({ const props = defineProps({
@ -103,7 +123,7 @@ const months = []
for (let i = 1; i < 13; i++) { for (let i = 1; i < 13; i++) {
const currentMonth = DateTime.fromObject({ year: year.value, month: i }).startOf('month') const currentMonth = DateTime.fromObject({ year: year.value, month: i }).startOf('month')
const start = currentMonth.startOf('week') const start = currentMonth.startOf('week')
const end = currentMonth.endOf('month').endOf('week') const end = currentMonth.endOf('month').endOf('week')
@ -153,19 +173,13 @@ function isWeekend(date) {
return date.weekday === 6 || date.weekday === 7 return date.weekday === 6 || date.weekday === 7
} }
function getVacationBorder(date) { function getVacationBorder(day) {
const type = findType(props.vacations[date.toISODate()].type) const type = findType(getVacationInfo(day).type)
return type.border.approved return type.border
}
function getPendingVacationBorder(date) {
const type = findType(props.pendingVacations[date.toISODate()].type)
return type.border.approved
} }
function getVacationInfo(day) { function getVacationInfo(day) {
return day.isVacation ? props.vacations[day.date.toISODate()] : props.pendingVacations[day.date.toISODate()] return day.isVacation ? props.vacations[day.date.toISODate()] : props.pendingVacations[day.date.toISODate()]
} }
</script> </script>

View File

@ -1,5 +1,8 @@
<template> <template>
<Popper hover> <Popper
hover
class="w-full"
>
<div class="flex text-white bg-white"> <div class="flex text-white bg-white">
<div <div
v-show="stats.used > 0" v-show="stats.used > 0"

View File

@ -8,7 +8,7 @@
</div> </div>
</div> </div>
<template #content> <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 }} {{ typeInfo.text }}
</div> </div>
</template> </template>