* wip * wip * wip * wip * wip * wip * wip * #5 - bump dependencies * #43 - wip * #43 - add composer script * #43 - fix * #43 - fix * #43 - wip * #43 - ecs fix * #43 - cr fix * #43 - cr fix * #43 - fix Co-authored-by: EwelinaLasowy <ewelina.lasowy@blumilk.pl>
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
:href="`/timesheet/${selectedMonth.value}`"
|
||||
class="inline-flex items-center px-4 py-3 border border-transparent text-sm leading-4 font-medium rounded-md shadow-sm text-white bg-blumilk-600 hover:bg-blumilk-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blumilk-500"
|
||||
>
|
||||
Pobierz plik excel
|
||||
Pobierz plik Excel
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,17 +1,9 @@
|
||||
<template>
|
||||
<InertiaHead title="Strona główna" />
|
||||
<div class="grid grid-cols-1 gap-4 items-start lg:grid-cols-3 lg:gap-8">
|
||||
<!-- Left column -->
|
||||
<div class="grid grid-cols-1 gap-4 lg:col-span-2">
|
||||
<!-- Welcome panel -->
|
||||
<section aria-labelledby="profile-overview-title">
|
||||
<div class="rounded-lg bg-white overflow-hidden shadow">
|
||||
<h2
|
||||
id="profile-overview-title"
|
||||
class="sr-only"
|
||||
>
|
||||
Profile Overview
|
||||
</h2>
|
||||
<section>
|
||||
<div class=" bg-white overflow-hidden shadow">
|
||||
<div class="bg-white p-6">
|
||||
<div class="sm:flex sm:items-center sm:justify-between">
|
||||
<div class="sm:flex sm:space-x-5">
|
||||
@@ -24,7 +16,7 @@
|
||||
</div>
|
||||
<div class="mt-4 text-center sm:mt-0 sm:pt-1 sm:text-left">
|
||||
<p class="text-sm font-medium text-gray-600">
|
||||
Welcome back,
|
||||
Cześć,
|
||||
</p>
|
||||
<p class="text-xl font-bold text-gray-900 sm:text-2xl">
|
||||
{{ user.name }}
|
||||
@@ -34,136 +26,109 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5 flex justify-center sm:mt-0">
|
||||
<InertiaLink
|
||||
href="#"
|
||||
class="inline-flex items-center px-4 py-3 border border-transparent text-sm leading-4 font-medium rounded-md shadow-sm text-white bg-blumilk-600 hover:bg-blumilk-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blumilk-500"
|
||||
>
|
||||
View profile
|
||||
</InertiaLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="border-t border-gray-200 bg-gray-50 grid grid-cols-1 divide-y divide-gray-200 sm:grid-cols-3 sm:divide-y-0 sm:divide-x"
|
||||
>
|
||||
<div
|
||||
v-for="stat in stats"
|
||||
:key="stat.label"
|
||||
class="px-6 py-5 text-sm font-medium text-center"
|
||||
>
|
||||
<span class="text-gray-900">{{ stat.value }}</span>
|
||||
{{ ' ' }}
|
||||
<span class="text-gray-600">{{ stat.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Actions panel -->
|
||||
<section aria-labelledby="quick-links-title">
|
||||
<div
|
||||
class="rounded-lg bg-gray-200 overflow-hidden shadow divide-y divide-gray-200 sm:divide-y-0 sm:grid sm:grid-cols-2 sm:gap-px"
|
||||
>
|
||||
<h2
|
||||
id="quick-links-title"
|
||||
class="sr-only"
|
||||
>
|
||||
Quick links
|
||||
</h2>
|
||||
<div
|
||||
v-for="(action, actionIdx) in actions"
|
||||
:key="action.name"
|
||||
:class="[actionIdx === 0 ? 'rounded-tl-lg rounded-tr-lg sm:rounded-tr-none' : '', actionIdx === 1 ? 'sm:rounded-tr-lg' : '', actionIdx === actions.length - 2 ? 'sm:rounded-bl-lg' : '', actionIdx === actions.length - 1 ? 'rounded-bl-lg rounded-br-lg sm:rounded-bl-none' : '', 'relative group bg-white p-6 focus-within:ring-2 focus-within:ring-inset focus-within:ring-cyan-500']"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
:class="[action.iconBackground, action.iconForeground, 'rounded-lg inline-flex p-3 ring-4 ring-white']"
|
||||
>
|
||||
<component
|
||||
:is="action.icon"
|
||||
class="h-6 w-6"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
<section>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="bg-white shadow-md p-4">
|
||||
<VacationChart :stats="stats" />
|
||||
</div>
|
||||
<div class="h-full">
|
||||
<div class="grid grid-cols-2 gap-4 h-full">
|
||||
<div class="px-4 py-5 bg-white shadow-md sm:p-6">
|
||||
<dd class="mt-1 text-4xl font-semibold text-blumilk-500">
|
||||
{{ stats.remaining }}
|
||||
</dd>
|
||||
<dt class="text-md font-medium text-gray-700 truncate">
|
||||
Pozostało
|
||||
</dt>
|
||||
<dt class="text-sm font-medium text-gray-500 mt-2">
|
||||
Dni do wykorzystania teraz.
|
||||
</dt>
|
||||
</div>
|
||||
<div class="px-4 py-5 bg-white shadow-md sm:p-6">
|
||||
<dd class="mt-1 text-4xl font-semibold text-blumilk-700">
|
||||
{{ stats.used }}
|
||||
</dd>
|
||||
<dt class="text-md font-medium text-gray-700 truncate">
|
||||
Dni wykorzystane
|
||||
</dt>
|
||||
<dt class="text-sm font-medium text-gray-500 mt-2">
|
||||
Dni, które zostały już wykorzystane na urlop wypoczynkowy.
|
||||
</dt>
|
||||
</div>
|
||||
<div class="px-4 py-5 bg-white shadow-md sm:p-6">
|
||||
<dt class="mt-1 text-4xl font-semibold text-blumilk-200">
|
||||
{{ stats.pending }}
|
||||
</dt>
|
||||
<dd class="text-md font-medium text-gray-500 truncate">
|
||||
Rozpatrywane
|
||||
</dd>
|
||||
<dt class="text-sm font-medium text-gray-500 mt-2">
|
||||
Dni czekające na akceptację przełożonych.
|
||||
</dt>
|
||||
</div>
|
||||
<div class="px-4 py-5 bg-white shadow-md sm:p-6">
|
||||
<dt class="mt-1 text-4xl font-semibold text-gray-900">
|
||||
{{ stats.limit }}
|
||||
</dt>
|
||||
<dd class="text-md font-medium text-gray-500 truncate">
|
||||
Limit urlopu
|
||||
</dd>
|
||||
<dt class="text-sm font-medium text-gray-500 mt-2">
|
||||
Twój roczny limit urlopu wypoczynkowego.
|
||||
</dt>
|
||||
</div>
|
||||
<div class="px-4 py-5 bg-white shadow-md sm:p-6 col-span-2">
|
||||
<dt class="mt-1 text-4xl font-semibold text-gray-900">
|
||||
{{ stats.other }}
|
||||
</dt>
|
||||
<dd class="text-md font-medium text-gray-500 truncate">
|
||||
Inne urlopy
|
||||
</dd>
|
||||
<dt class="text-sm font-medium text-gray-500 mt-2">
|
||||
Urlopy bezpłatne, okolicznościowe, zwolnienia lekarskie, itd., które zostały już zatwierdzone.
|
||||
</dt>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-8">
|
||||
<h3 class="text-lg font-medium">
|
||||
<InertiaLink
|
||||
:href="action.href"
|
||||
class="focus:outline-none"
|
||||
>
|
||||
<!-- Extend touch target to entire panel -->
|
||||
<span
|
||||
class="absolute inset-0"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{{ action.name }}
|
||||
</InertiaLink>
|
||||
</h3>
|
||||
<p class="mt-2 text-sm text-gray-500">
|
||||
Doloribus dolores nostrum quia qui natus officia quod et dolorem. Sit
|
||||
repellendus qui ut at blanditiis et quo et molestiae.
|
||||
</p>
|
||||
</div>
|
||||
<span
|
||||
class="pointer-events-none absolute top-6 right-6 text-gray-300 group-hover:text-gray-400"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg
|
||||
class="h-6 w-6"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M20 4h1a1 1 0 00-1-1v1zm-1 12a1 1 0 102 0h-2zM8 3a1 1 0 000 2V3zM3.293 19.293a1 1 0 101.414 1.414l-1.414-1.414zM19 4v12h2V4h-2zm1-1H8v2h12V3zm-.707.293l-16 16 1.414 1.414 16-16-1.414-1.414z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- Right column -->
|
||||
<div class="grid grid-cols-1 gap-4">
|
||||
<!-- Announcements -->
|
||||
<section aria-labelledby="announcements-title">
|
||||
<div class="rounded-lg bg-white overflow-hidden shadow">
|
||||
<div class="p-6">
|
||||
<h2
|
||||
id="announcements-title"
|
||||
class="text-base font-medium text-gray-900"
|
||||
>
|
||||
Announcements
|
||||
<section>
|
||||
<div class="bg-white shadow-md">
|
||||
<div class="p-4 sm:px-6">
|
||||
<h2 class="text-lg leading-6 font-medium text-gray-900">
|
||||
Twoje wnioski
|
||||
</h2>
|
||||
</div>
|
||||
<div class="border-t border-gray-200 pb-5 px-4 sm:px-6">
|
||||
<div class="flow-root mt-6">
|
||||
<ul
|
||||
role="list"
|
||||
class="-my-5 divide-y divide-gray-200"
|
||||
>
|
||||
<ul class="-my-5 divide-y divide-gray-200">
|
||||
<li
|
||||
v-for="announcement in announcements"
|
||||
:key="announcement.id"
|
||||
v-for="request in vacationRequests.data"
|
||||
:key="request.id"
|
||||
class="py-5"
|
||||
>
|
||||
<div class="relative focus-within:ring-2 focus-within:ring-cyan-500">
|
||||
<h3 class="text-sm font-semibold text-gray-800">
|
||||
<h3 class="text-sm font-semibold text-blumilk-600 hover:text-blumilk-500">
|
||||
<InertiaLink
|
||||
:href="announcement.href"
|
||||
:href="`/vacation-requests/${request.id}`"
|
||||
class="hover:underline focus:outline-none"
|
||||
>
|
||||
<!-- Extend touch target to entire panel -->
|
||||
<span
|
||||
class="absolute inset-0"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{{ announcement.title }}
|
||||
<span class="absolute inset-0" />
|
||||
Wniosek o {{ request.type.toLowerCase() }}
|
||||
[{{ request.name }}]
|
||||
</InertiaLink>
|
||||
</h3>
|
||||
<p class="mt-1 text-sm text-gray-600 line-clamp-2">
|
||||
{{ announcement.preview }}
|
||||
<p class="mt-1 text-sm text-gray-600">
|
||||
{{ request.from }} - {{ request.to }}
|
||||
</p>
|
||||
<p class="mt-2 text-sm text-gray-600">
|
||||
<Status :status="request.state" />
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
@@ -171,71 +136,84 @@
|
||||
</div>
|
||||
<div class="mt-6">
|
||||
<InertiaLink
|
||||
href="#"
|
||||
href="/vacation-requests"
|
||||
class="w-full flex justify-center items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"
|
||||
>
|
||||
View all
|
||||
Zobacz wszystkie
|
||||
</InertiaLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Recent Hires -->
|
||||
<section aria-labelledby="recent-hires-title">
|
||||
<div class="rounded-lg bg-white overflow-hidden shadow">
|
||||
<div class="p-6">
|
||||
<h2
|
||||
id="recent-hires-title"
|
||||
class="text-base font-medium text-gray-900"
|
||||
>
|
||||
Recent Hires
|
||||
<section>
|
||||
<div class="bg-white shadow-md">
|
||||
<div class="p-4 sm:px-6">
|
||||
<h2 class="text-lg leading-6 font-medium text-gray-900">
|
||||
Dzisiejsze nieobecności
|
||||
</h2>
|
||||
<div class="flow-root mt-6">
|
||||
<ul
|
||||
role="list"
|
||||
class="-my-5 divide-y divide-gray-200"
|
||||
</div>
|
||||
<div class="border-t border-gray-200 px-4 sm:px-6">
|
||||
<ul class="divide-y divide-gray-200">
|
||||
<li
|
||||
v-for="absence in absences.data"
|
||||
:key="absence.user.id"
|
||||
class="py-4 flex"
|
||||
>
|
||||
<li
|
||||
v-for="person in recentHires"
|
||||
:key="person.handle"
|
||||
class="py-4"
|
||||
<img
|
||||
class="h-10 w-10 rounded-full"
|
||||
:src="absence.user.avatar"
|
||||
>
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="flex-shrink-0">
|
||||
<img
|
||||
class="h-8 w-8 rounded-full"
|
||||
:src="person.imageUrl"
|
||||
alt=""
|
||||
>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="text-sm font-medium text-gray-900 truncate">
|
||||
{{ person.name }}
|
||||
</p>
|
||||
<p class="text-sm text-gray-500 truncate">
|
||||
{{ '@' + person.handle }}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<InertiaLink
|
||||
:href="person.href"
|
||||
class="inline-flex items-center shadow-sm px-2.5 py-0.5 border border-gray-300 text-sm leading-5 font-medium rounded-full text-gray-700 bg-white hover:bg-gray-50"
|
||||
>
|
||||
View
|
||||
</InertiaLink>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<p class="text-sm font-medium text-gray-900">
|
||||
{{ absence.user.name }}
|
||||
</p>
|
||||
<p class="text-sm text-gray-500">
|
||||
{{ absence.user.email }}
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li v-if="! absences.data.length">
|
||||
<p class="py-2">
|
||||
Brak danych
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="bg-white shadow-md">
|
||||
<div>
|
||||
<div class="p-4 sm:px-6">
|
||||
<h2 class="text-lg leading-6 font-medium text-gray-900">
|
||||
Najbliższe dni wolne
|
||||
</h2>
|
||||
</div>
|
||||
<div class="border-t border-gray-200 px-4 pb-5 sm:px-6">
|
||||
<ul class="divide-y divide-gray-200">
|
||||
<li
|
||||
v-for="holiday in holidays.data"
|
||||
:key="holiday.id.id"
|
||||
class="py-4 flex"
|
||||
>
|
||||
<div>
|
||||
<p class="text-sm font-medium text-gray-900">
|
||||
{{ holiday.name }}
|
||||
</p>
|
||||
<p class="text-sm text-gray-500">
|
||||
{{ holiday.displayDate }}
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="mt-6">
|
||||
<InertiaLink
|
||||
href="#"
|
||||
class="w-full flex justify-center items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"
|
||||
>
|
||||
View all
|
||||
</InertiaLink>
|
||||
<div>
|
||||
<InertiaLink
|
||||
href="/holidays"
|
||||
class="w-full flex justify-center items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"
|
||||
>
|
||||
Zobacz wszystkie
|
||||
</InertiaLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -245,129 +223,41 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
AcademicCapIcon,
|
||||
BadgeCheckIcon,
|
||||
CashIcon,
|
||||
ClockIcon,
|
||||
ReceiptRefundIcon,
|
||||
UsersIcon,
|
||||
} from '@heroicons/vue/outline'
|
||||
import {computed} from 'vue'
|
||||
import {usePage} from '@inertiajs/inertia-vue3'
|
||||
import Status from '@/Shared/Status'
|
||||
import VacationChart from '@/Shared/VacationChart'
|
||||
|
||||
export default {
|
||||
name: 'DashboardPage',
|
||||
components: {Status, VacationChart},
|
||||
props: {
|
||||
absences: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
vacationRequests: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
holidays: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
stats: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
used: 0,
|
||||
pending: 0,
|
||||
remaining: 0,
|
||||
}),
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const user = computed(() => usePage().props.value.auth.user)
|
||||
const stats = [
|
||||
{label: 'Vacation days left', value: 12},
|
||||
{label: 'Sick days left', value: 4},
|
||||
{label: 'Personal days left', value: 2},
|
||||
]
|
||||
const actions = [
|
||||
{
|
||||
icon: ClockIcon,
|
||||
name: 'Request time off',
|
||||
href: '#',
|
||||
iconForeground: 'text-teal-700',
|
||||
iconBackground: 'bg-teal-50',
|
||||
},
|
||||
{
|
||||
icon: BadgeCheckIcon,
|
||||
name: 'Benefits',
|
||||
href: '#',
|
||||
iconForeground: 'text-purple-700',
|
||||
iconBackground: 'bg-purple-50',
|
||||
},
|
||||
{
|
||||
icon: UsersIcon,
|
||||
name: 'Schedule a one-on-one',
|
||||
href: '#',
|
||||
iconForeground: 'text-sky-700',
|
||||
iconBackground: 'bg-sky-50',
|
||||
},
|
||||
{
|
||||
icon: CashIcon,
|
||||
name: 'Payroll',
|
||||
href: '#',
|
||||
iconForeground: 'text-yellow-700',
|
||||
iconBackground: 'bg-yellow-50',
|
||||
},
|
||||
{
|
||||
icon: ReceiptRefundIcon,
|
||||
name: 'Submit an expense',
|
||||
href: '#',
|
||||
iconForeground: 'text-rose-700',
|
||||
iconBackground: 'bg-rose-50',
|
||||
},
|
||||
{
|
||||
icon: AcademicCapIcon,
|
||||
name: 'Training',
|
||||
href: '#',
|
||||
iconForeground: 'text-indigo-700',
|
||||
iconBackground: 'bg-indigo-50',
|
||||
},
|
||||
]
|
||||
const recentHires = [
|
||||
{
|
||||
name: 'Leonard Krasner',
|
||||
handle: 'leonardkrasner',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1519345182560-3f2917c472ef?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80',
|
||||
href: '#',
|
||||
},
|
||||
{
|
||||
name: 'Floyd Miles',
|
||||
handle: 'floydmiles',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1463453091185-61582044d556?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80',
|
||||
href: '#',
|
||||
},
|
||||
{
|
||||
name: 'Emily Selman',
|
||||
handle: 'emilyselman',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1502685104226-ee32379fefbe?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80',
|
||||
href: '#',
|
||||
},
|
||||
{
|
||||
name: 'Kristin Watson',
|
||||
handle: 'kristinwatson',
|
||||
imageUrl:
|
||||
'https://images.unsplash.com/photo-1500917293891-ef795e70e1f6?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80',
|
||||
href: '#',
|
||||
},
|
||||
]
|
||||
const announcements = [
|
||||
{
|
||||
id: 1,
|
||||
title: 'Office closed on July 2nd',
|
||||
href: '#',
|
||||
preview:
|
||||
'Cum qui rem deleniti. Suscipit in dolor veritatis sequi aut. Vero ut earum quis deleniti. Ut a sunt eum cum ut repudiandae possimus. Nihil ex tempora neque cum consectetur dolores.',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'New password policy',
|
||||
href: '#',
|
||||
preview:
|
||||
'Alias inventore ut autem optio voluptas et repellendus. Facere totam quaerat quam quo laudantium cumque eaque excepturi vel. Accusamus maxime ipsam reprehenderit rerum id repellendus rerum. Culpa cum vel natus. Est sit autem mollitia.',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Office closed on July 2nd',
|
||||
href: '#',
|
||||
preview:
|
||||
'Tenetur libero voluptatem rerum occaecati qui est molestiae exercitationem. Voluptate quisquam iure assumenda consequatur ex et recusandae. Alias consectetur voluptatibus. Accusamus a ab dicta et. Consequatur quis dignissimos voluptatem nisi.',
|
||||
},
|
||||
]
|
||||
|
||||
return {
|
||||
user,
|
||||
stats,
|
||||
actions,
|
||||
recentHires,
|
||||
announcements,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@@ -11,119 +11,128 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-t border-gray-200">
|
||||
<div class="overflow-x-auto xl:overflow-x-visible overflow-y-auto xl:overflow-y-visible">
|
||||
<form @submit.prevent="submitVacationDays">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider"
|
||||
>
|
||||
Imię i nazwisko
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider"
|
||||
>
|
||||
Forma zatrudnienia
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider"
|
||||
>
|
||||
Posiada urlop?
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider"
|
||||
>
|
||||
Dostępne dni w roku
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-100">
|
||||
<tr
|
||||
v-for="(item, index) in form.items"
|
||||
:key="item.id"
|
||||
class="hover:bg-blumilk-25"
|
||||
<div class="overflow-x-auto xl:overflow-x-visible overflow-y-auto xl:overflow-y-visible">
|
||||
<form @submit.prevent="submitVacationDays">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider"
|
||||
>
|
||||
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
<div class="flex">
|
||||
<span
|
||||
class="inline-flex items-center justify-center h-10 w-10 rounded-full"
|
||||
>
|
||||
<img
|
||||
class="h-10 w-10 rounded-full"
|
||||
:src="item.user.avatar"
|
||||
alt=""
|
||||
>
|
||||
</span>
|
||||
<div class="ml-3">
|
||||
<p class="text-sm font-medium break-all text-gray-900">
|
||||
{{ item.user.name }}
|
||||
</p>
|
||||
<p class="text-sm break-all text-gray-500">
|
||||
{{ item.user.email }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{{ item.user.employmentForm }}
|
||||
</td>
|
||||
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
<Switch
|
||||
v-model="item.hasVacation"
|
||||
:class="[item.hasVacation ? 'bg-blumilk-500' : 'bg-gray-200', 'relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blumilk-500']"
|
||||
Imię i nazwisko
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider"
|
||||
>
|
||||
Forma zatrudnienia
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider"
|
||||
>
|
||||
Posiada urlop?
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider"
|
||||
>
|
||||
Pozostałe dni z poprzedniego roku
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider"
|
||||
>
|
||||
Dostępne dni w roku
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-100">
|
||||
<tr
|
||||
v-for="(item, index) in form.items"
|
||||
:key="item.id"
|
||||
class="hover:bg-blumilk-25"
|
||||
>
|
||||
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
<div class="flex">
|
||||
<span
|
||||
class="inline-flex items-center justify-center h-10 w-10 rounded-full"
|
||||
>
|
||||
<span
|
||||
:class="[item.hasVacation ? 'translate-x-5' : 'translate-x-0', 'pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200']"
|
||||
/>
|
||||
</Switch>
|
||||
</td>
|
||||
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
<div class="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<input
|
||||
v-model="item.days"
|
||||
type="number"
|
||||
min="0"
|
||||
class="block w-full shadow-sm rounded-md sm:text-sm disabled:bg-slate-50 disabled:text-slate-500 disabled:border-slate-200 disabled:shadow-none disabled:cursor-not-allowed"
|
||||
:disabled="!item.hasVacation"
|
||||
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors[`items.${index}.days`], 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors[`items.${index}.days`] }"
|
||||
<img
|
||||
class="h-10 w-10 rounded-full"
|
||||
:src="item.user.avatar"
|
||||
alt=""
|
||||
>
|
||||
<p
|
||||
v-if="form.errors[`items.${index}.days`]"
|
||||
class="mt-2 text-sm text-red-600"
|
||||
>
|
||||
{{ form.errors[`items.${index}.days`] }}
|
||||
</span>
|
||||
<div class="ml-3">
|
||||
<p class="text-sm font-medium break-all text-gray-900">
|
||||
{{ item.user.name }}
|
||||
</p>
|
||||
<p class="text-sm break-all text-gray-500">
|
||||
{{ item.user.email }}
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
v-if="!form.items.length"
|
||||
>
|
||||
<td
|
||||
colspan="100%"
|
||||
class="text-center py-4 text-xl leading-5 text-gray-700"
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{{ item.user.employmentForm }}
|
||||
</td>
|
||||
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
<Switch
|
||||
v-model="item.hasVacation"
|
||||
:class="[item.hasVacation ? 'bg-blumilk-500' : 'bg-gray-200', 'relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blumilk-500']"
|
||||
>
|
||||
Brak danych
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="flex justify-end py-3 px-4">
|
||||
<button
|
||||
type="submit"
|
||||
class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blumilk-600 hover:bg-blumilk-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blumilk-500"
|
||||
<span
|
||||
:class="[item.hasVacation ? 'translate-x-5' : 'translate-x-0', 'pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200']"
|
||||
/>
|
||||
</Switch>
|
||||
</td>
|
||||
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
<div class="mt-1 sm:mt-0 sm:col-span-2 w-full max-w-lg bg-gray-50 border border-gray-300 rounded-md px-4 py-2 inline-flex items-center text-gray-500 sm:text-sm">
|
||||
{{ item.remainingLastYear }}
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
<div class="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<input
|
||||
v-model="item.days"
|
||||
type="number"
|
||||
min="0"
|
||||
class="block w-full shadow-sm rounded-md sm:text-sm disabled:bg-slate-50 disabled:text-slate-500 disabled:border-slate-200 disabled:shadow-none disabled:cursor-not-allowed"
|
||||
:disabled="!item.hasVacation"
|
||||
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors[`items.${index}.days`], 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors[`items.${index}.days`] }"
|
||||
>
|
||||
<p
|
||||
v-if="form.errors[`items.${index}.days`]"
|
||||
class="mt-2 text-sm text-red-600"
|
||||
>
|
||||
{{ form.errors[`items.${index}.days`] }}
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr
|
||||
v-if="!form.items.length"
|
||||
>
|
||||
Zapisz
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<td
|
||||
colspan="100%"
|
||||
class="text-center py-4 text-xl leading-5 text-gray-700"
|
||||
>
|
||||
Brak danych
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="flex justify-end py-3 px-4">
|
||||
<button
|
||||
type="submit"
|
||||
class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blumilk-600 hover:bg-blumilk-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blumilk-500"
|
||||
>
|
||||
Zapisz
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -149,7 +158,7 @@ export default {
|
||||
},
|
||||
setup(props) {
|
||||
const form = useForm({
|
||||
items: props.limits.data,
|
||||
items: props.limits,
|
||||
})
|
||||
|
||||
return {
|
||||
|
@@ -1,277 +1,324 @@
|
||||
<template>
|
||||
<InertiaHead title="Złóż wniosek urlopowy" />
|
||||
<div class="bg-white shadow-md">
|
||||
<div class="p-4 sm:px-6">
|
||||
<h2 class="text-lg leading-6 font-medium text-gray-900">
|
||||
Złóż wniosek urlopowy
|
||||
</h2>
|
||||
</div>
|
||||
<form
|
||||
class="border-t border-gray-200 px-6"
|
||||
@submit.prevent="createForm"
|
||||
>
|
||||
<div
|
||||
v-if="form.errors.vacationRequest"
|
||||
class="rounded-md bg-red-50 p-4 mt-2"
|
||||
<div class="grid grid-cols-1 gap-4 items-start lg:grid-cols-3 lg:gap-8">
|
||||
<div class="lg:col-span-2 h-full bg-white shadow-md flex flex-col">
|
||||
<div class="p-4 sm:px-6">
|
||||
<h2 class="text-lg leading-6 font-medium text-gray-900">
|
||||
Złóż wniosek urlopowy
|
||||
</h2>
|
||||
</div>
|
||||
<form
|
||||
class="border-t border-gray-200 h-full px-6"
|
||||
@submit.prevent="createForm"
|
||||
>
|
||||
<div class="flex">
|
||||
<div class="flex-shrink-0">
|
||||
<XCircleIcon class="h-5 w-5 text-red-400" />
|
||||
<div class="h-full flex flex-col justify-around">
|
||||
<div>
|
||||
<div
|
||||
v-if="form.errors.vacationRequest"
|
||||
class="rounded-md bg-red-50 p-4 mt-2"
|
||||
>
|
||||
<div class="flex">
|
||||
<div class="flex-shrink-0">
|
||||
<XCircleIcon class="h-5 w-5 text-red-400" />
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<h3 class="text-sm font-medium text-red-800">
|
||||
Wniosek nie mógł zostać utworzony
|
||||
</h3>
|
||||
<div class="mt-2 text-sm text-red-700">
|
||||
<span>{{ form.errors.vacationRequest }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Listbox
|
||||
v-if="can.createOnBehalfOfEmployee"
|
||||
v-model="form.user"
|
||||
as="div"
|
||||
class="sm:grid sm:grid-cols-3 py-4 items-center"
|
||||
>
|
||||
<ListboxLabel class="block text-sm font-medium text-gray-700">
|
||||
Osoba składająca wniosek
|
||||
</ListboxLabel>
|
||||
<div class="mt-1 relative sm:mt-0 sm:col-span-2">
|
||||
<ListboxButton
|
||||
class="bg-white relative w-full max-w-lg border rounded-md shadow-sm pl-3 pr-10 py-2 text-left cursor-default sm:text-sm focus:ring-1"
|
||||
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors.type, 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors.type }"
|
||||
>
|
||||
<span class="flex items-center">
|
||||
<img
|
||||
:src="form.user.avatar"
|
||||
class="flex-shrink-0 h-6 w-6 rounded-full"
|
||||
>
|
||||
<span class="ml-3 block truncate">{{ form.user.name }}</span>
|
||||
</span>
|
||||
<span class="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
|
||||
<SelectorIcon class="h-5 w-5 text-gray-400" />
|
||||
</span>
|
||||
</ListboxButton>
|
||||
|
||||
<transition
|
||||
leave-active-class="transition ease-in duration-100"
|
||||
leave-from-class="opacity-100"
|
||||
leave-to-class="opacity-0"
|
||||
>
|
||||
<ListboxOptions
|
||||
class="absolute z-10 mt-1 w-full max-w-lg bg-white shadow-lg max-h-60 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm"
|
||||
>
|
||||
<ListboxOption
|
||||
v-for="user in users.data"
|
||||
:key="user.id"
|
||||
v-slot="{ active, selected }"
|
||||
as="template"
|
||||
:value="user"
|
||||
>
|
||||
<li :class="[active ? 'text-white bg-blumilk-600' : 'text-gray-900', 'cursor-default select-none relative py-2 pl-3 pr-9']">
|
||||
<div class="flex items-center">
|
||||
<img
|
||||
:src="user.avatar"
|
||||
alt=""
|
||||
class="flex-shrink-0 h-6 w-6 rounded-full"
|
||||
>
|
||||
<span :class="[selected ? 'font-semibold' : 'font-normal', 'ml-3 block truncate']">
|
||||
{{ user.name }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<span
|
||||
v-if="selected"
|
||||
:class="[active ? 'text-white' : 'text-blumilk-600', 'absolute inset-y-0 right-0 flex items-center pr-4']"
|
||||
>
|
||||
<CheckIcon class="h-5 w-5" />
|
||||
</span>
|
||||
</li>
|
||||
</ListboxOption>
|
||||
</ListboxOptions>
|
||||
</transition>
|
||||
<p
|
||||
v-if="form.errors.type"
|
||||
class="mt-2 text-sm text-red-600"
|
||||
>
|
||||
{{ form.errors.type }}
|
||||
</p>
|
||||
</div>
|
||||
</Listbox>
|
||||
<div
|
||||
v-else
|
||||
class="sm:grid sm:grid-cols-3 py-4 items-center"
|
||||
>
|
||||
<label
|
||||
for="date_from"
|
||||
class="block text-sm font-medium text-gray-700 sm:mt-px"
|
||||
>
|
||||
Osoba składająca wniosek
|
||||
</label>
|
||||
<div class="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<div class="flex justify-start items-center">
|
||||
<span class="inline-flex items-center justify-center h-10 w-10 rounded-full">
|
||||
<img
|
||||
class="h-10 w-10 rounded-full"
|
||||
:src="auth.user.avatar"
|
||||
>
|
||||
</span>
|
||||
<div class="ml-3">
|
||||
<div class="text-sm font-medium text-gray-900">
|
||||
{{ auth.user.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Listbox
|
||||
v-model="form.type"
|
||||
as="div"
|
||||
class="sm:grid sm:grid-cols-3 py-4 items-center"
|
||||
>
|
||||
<ListboxLabel class="block text-sm font-medium text-gray-700">
|
||||
Rodzaj wniosku
|
||||
</ListboxLabel>
|
||||
<div class="mt-1 relative sm:mt-0 sm:col-span-2">
|
||||
<ListboxButton
|
||||
class="bg-white relative w-full max-w-lg border rounded-md shadow-sm pl-3 pr-10 py-2 text-left cursor-default sm:text-sm focus:ring-1"
|
||||
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors.type, 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors.type }"
|
||||
>
|
||||
<span class="block truncate">{{ form.type.label }}</span>
|
||||
<span class="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
|
||||
<SelectorIcon class="h-5 w-5 text-gray-400" />
|
||||
</span>
|
||||
</ListboxButton>
|
||||
|
||||
<transition
|
||||
leave-active-class="transition ease-in duration-100"
|
||||
leave-from-class="opacity-100"
|
||||
leave-to-class="opacity-0"
|
||||
>
|
||||
<ListboxOptions
|
||||
class="absolute z-10 mt-1 w-full max-w-lg bg-white shadow-lg max-h-60 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm"
|
||||
>
|
||||
<ListboxOption
|
||||
v-for="type in vacationTypes"
|
||||
:key="type.value"
|
||||
v-slot="{ active, selected }"
|
||||
as="template"
|
||||
:value="type"
|
||||
>
|
||||
<li
|
||||
:class="[active ? 'text-white bg-blumilk-600' : 'text-gray-900', 'cursor-default select-none relative py-2 pl-3 pr-9']"
|
||||
>
|
||||
<span :class="[selected ? 'font-semibold' : 'font-normal', 'block truncate']">
|
||||
{{ type.label }}
|
||||
</span>
|
||||
|
||||
<span
|
||||
v-if="selected"
|
||||
:class="[active ? 'text-white' : 'text-blumilk-600', 'absolute inset-y-0 right-0 flex items-center pr-4']"
|
||||
>
|
||||
<CheckIcon class="h-5 w-5" />
|
||||
</span>
|
||||
</li>
|
||||
</ListboxOption>
|
||||
</ListboxOptions>
|
||||
</transition>
|
||||
<p
|
||||
v-if="form.errors.type"
|
||||
class="mt-2 text-sm text-red-600"
|
||||
>
|
||||
{{ form.errors.type }}
|
||||
</p>
|
||||
</div>
|
||||
</Listbox>
|
||||
<div class="sm:grid sm:grid-cols-3 py-4 items-center">
|
||||
<label
|
||||
for="date_from"
|
||||
class="block text-sm font-medium text-gray-700 sm:mt-px"
|
||||
>
|
||||
Planowany urlop od
|
||||
</label>
|
||||
<div class="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<FlatPickr
|
||||
id="date_from"
|
||||
v-model="form.from"
|
||||
:config="fromInputConfig"
|
||||
placeholder="Wybierz datę"
|
||||
class="block w-full max-w-lg shadow-sm rounded-md sm:text-sm"
|
||||
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors.from, 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors.from }"
|
||||
@on-change="onFromChange"
|
||||
/>
|
||||
<p
|
||||
v-if="form.errors.from"
|
||||
class="mt-2 text-sm text-red-600"
|
||||
>
|
||||
{{ form.errors.from }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sm:grid sm:grid-cols-3 py-4 items-center">
|
||||
<label
|
||||
for="date_from"
|
||||
class="block text-sm font-medium text-gray-700 sm:mt-px"
|
||||
>
|
||||
Planowany urlop do
|
||||
</label>
|
||||
<div class="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<FlatPickr
|
||||
id="date_to"
|
||||
v-model="form.to"
|
||||
:config="toInputConfig"
|
||||
placeholder="Wybierz datę"
|
||||
class="block w-full max-w-lg shadow-sm rounded-md sm:text-sm"
|
||||
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors.to, 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors.to }"
|
||||
@on-change="onToChange"
|
||||
/>
|
||||
<p
|
||||
v-if="form.errors.to"
|
||||
class="mt-2 text-sm text-red-600"
|
||||
>
|
||||
{{ form.errors.to }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sm:grid sm:grid-cols-3 py-4 items-center">
|
||||
<span class="block text-sm font-medium text-gray-700 sm:mt-px">Liczba dni urlopu</span>
|
||||
<div
|
||||
class="mt-1 sm:mt-0 sm:col-span-2 w-full max-w-lg bg-gray-50 border border-gray-300 rounded-md px-4 py-2 inline-flex items-center text-gray-500 sm:text-sm"
|
||||
>
|
||||
{{ estimatedDays.length }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="sm:grid sm:grid-cols-3 py-4 items-center">
|
||||
<label
|
||||
for="comment"
|
||||
class="block text-sm font-medium text-gray-700"
|
||||
>
|
||||
Komentarz
|
||||
</label>
|
||||
<div class="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<textarea
|
||||
id="comment"
|
||||
v-model="form.comment"
|
||||
rows="4"
|
||||
class="shadow-sm focus:ring-blumilk-500 focus:border-blumilk-500 block w-full max-w-lg sm:text-sm border-gray-300 rounded-md"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="can.skipFlow"
|
||||
class="sm:grid sm:grid-cols-3 py-4 items-center"
|
||||
>
|
||||
<label
|
||||
for="flowSkipped"
|
||||
class="block text-sm font-medium text-gray-700"
|
||||
>
|
||||
Natychmiastowo zatwierdź wniosek
|
||||
</label>
|
||||
<div class="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<Switch
|
||||
id="flowSkipped"
|
||||
v-model="form.flowSkipped"
|
||||
:class="[form.flowSkipped ? 'bg-blumilk-500' : 'bg-gray-200', 'relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blumilk-500']"
|
||||
>
|
||||
<span
|
||||
:class="[form.flowSkipped ? 'translate-x-5' : 'translate-x-0', 'pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200']"
|
||||
/>
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<h3 class="text-sm font-medium text-red-800">
|
||||
Wniosek nie mógł zostać utworzony
|
||||
</h3>
|
||||
<div class="mt-2 text-sm text-red-700">
|
||||
<span>{{ form.errors.vacationRequest }}</span>
|
||||
<div class="flex justify-end py-3">
|
||||
<div class="space-x-3">
|
||||
<InertiaLink
|
||||
href="/vacation-requests"
|
||||
class="bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blumilk-500"
|
||||
>
|
||||
Anuluj
|
||||
</InertiaLink>
|
||||
<button
|
||||
type="submit"
|
||||
:disabled="form.processing"
|
||||
class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blumilk-600 hover:bg-blumilk-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blumilk-500"
|
||||
>
|
||||
Zapisz
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="bg-white shadow-md h-full">
|
||||
<div class="p-4 sm:px-6">
|
||||
<h2 class="text-lg leading-6 font-medium text-gray-900">
|
||||
<span v-if="auth.user.id !== form.user.id">
|
||||
Urlop wypoczynkowy, dane dla: {{ form.user.name }}
|
||||
</span>
|
||||
<span v-else>
|
||||
Twoje dane o urlopie wypoczynkowym
|
||||
</span>
|
||||
</h2>
|
||||
</div>
|
||||
<Listbox
|
||||
v-if="can.createOnBehalfOfEmployee"
|
||||
v-model="form.user"
|
||||
as="div"
|
||||
class="sm:grid sm:grid-cols-3 py-4 items-center"
|
||||
>
|
||||
<ListboxLabel class="block text-sm font-medium text-gray-700">
|
||||
Osoba składająca wniosek
|
||||
</ListboxLabel>
|
||||
<div class="mt-1 relative sm:mt-0 sm:col-span-2">
|
||||
<ListboxButton
|
||||
class="bg-white relative w-full max-w-lg border rounded-md shadow-sm pl-3 pr-10 py-2 text-left cursor-default sm:text-sm focus:ring-1"
|
||||
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors.type, 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors.type }"
|
||||
>
|
||||
<span class="flex items-center">
|
||||
<img
|
||||
:src="form.user.avatar"
|
||||
class="flex-shrink-0 h-6 w-6 rounded-full"
|
||||
>
|
||||
<span class="ml-3 block truncate">{{ form.user.name }}</span>
|
||||
</span>
|
||||
<span class="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
|
||||
<SelectorIcon class="h-5 w-5 text-gray-400" />
|
||||
</span>
|
||||
</ListboxButton>
|
||||
|
||||
<transition
|
||||
leave-active-class="transition ease-in duration-100"
|
||||
leave-from-class="opacity-100"
|
||||
leave-to-class="opacity-0"
|
||||
>
|
||||
<ListboxOptions
|
||||
class="absolute z-10 mt-1 w-full max-w-lg bg-white shadow-lg max-h-60 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm"
|
||||
>
|
||||
<ListboxOption
|
||||
v-for="user in users.data"
|
||||
:key="user.id"
|
||||
v-slot="{ active, selected }"
|
||||
as="template"
|
||||
:value="user"
|
||||
>
|
||||
<li :class="[active ? 'text-white bg-blumilk-600' : 'text-gray-900', 'cursor-default select-none relative py-2 pl-3 pr-9']">
|
||||
<div class="flex items-center">
|
||||
<img
|
||||
:src="user.avatar"
|
||||
alt=""
|
||||
class="flex-shrink-0 h-6 w-6 rounded-full"
|
||||
>
|
||||
<span :class="[selected ? 'font-semibold' : 'font-normal', 'ml-3 block truncate']">
|
||||
{{ user.name }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<span
|
||||
v-if="selected"
|
||||
:class="[active ? 'text-white' : 'text-blumilk-600', 'absolute inset-y-0 right-0 flex items-center pr-4']"
|
||||
>
|
||||
<CheckIcon class="h-5 w-5" />
|
||||
</span>
|
||||
</li>
|
||||
</ListboxOption>
|
||||
</ListboxOptions>
|
||||
</transition>
|
||||
<p
|
||||
v-if="form.errors.type"
|
||||
class="mt-2 text-sm text-red-600"
|
||||
>
|
||||
{{ form.errors.type }}
|
||||
</p>
|
||||
</div>
|
||||
</Listbox>
|
||||
<Listbox
|
||||
v-model="form.type"
|
||||
as="div"
|
||||
class="sm:grid sm:grid-cols-3 py-4 items-center"
|
||||
>
|
||||
<ListboxLabel class="block text-sm font-medium text-gray-700">
|
||||
Rodzaj wniosku
|
||||
</ListboxLabel>
|
||||
<div class="mt-1 relative sm:mt-0 sm:col-span-2">
|
||||
<ListboxButton
|
||||
class="bg-white relative w-full max-w-lg border rounded-md shadow-sm pl-3 pr-10 py-2 text-left cursor-default sm:text-sm focus:ring-1"
|
||||
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors.type, 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors.type }"
|
||||
>
|
||||
<span class="block truncate">{{ form.type.label }}</span>
|
||||
<span class="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
|
||||
<SelectorIcon class="h-5 w-5 text-gray-400" />
|
||||
</span>
|
||||
</ListboxButton>
|
||||
|
||||
<transition
|
||||
leave-active-class="transition ease-in duration-100"
|
||||
leave-from-class="opacity-100"
|
||||
leave-to-class="opacity-0"
|
||||
>
|
||||
<ListboxOptions
|
||||
class="absolute z-10 mt-1 w-full max-w-lg bg-white shadow-lg max-h-60 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm"
|
||||
>
|
||||
<ListboxOption
|
||||
v-for="type in vacationTypes"
|
||||
:key="type.value"
|
||||
v-slot="{ active, selected }"
|
||||
as="template"
|
||||
:value="type"
|
||||
>
|
||||
<li
|
||||
:class="[active ? 'text-white bg-blumilk-600' : 'text-gray-900', 'cursor-default select-none relative py-2 pl-3 pr-9']"
|
||||
>
|
||||
<span :class="[selected ? 'font-semibold' : 'font-normal', 'block truncate']">
|
||||
{{ type.label }}
|
||||
</span>
|
||||
|
||||
<span
|
||||
v-if="selected"
|
||||
:class="[active ? 'text-white' : 'text-blumilk-600', 'absolute inset-y-0 right-0 flex items-center pr-4']"
|
||||
>
|
||||
<CheckIcon class="h-5 w-5" />
|
||||
</span>
|
||||
</li>
|
||||
</ListboxOption>
|
||||
</ListboxOptions>
|
||||
</transition>
|
||||
<p
|
||||
v-if="form.errors.type"
|
||||
class="mt-2 text-sm text-red-600"
|
||||
>
|
||||
{{ form.errors.type }}
|
||||
</p>
|
||||
</div>
|
||||
</Listbox>
|
||||
<div class="sm:grid sm:grid-cols-3 py-4 items-center">
|
||||
<label
|
||||
for="date_from"
|
||||
class="block text-sm font-medium text-gray-700 sm:mt-px"
|
||||
>
|
||||
Planowany urlop od
|
||||
</label>
|
||||
<div class="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<FlatPickr
|
||||
id="date_from"
|
||||
v-model="form.from"
|
||||
:config="fromInputConfig"
|
||||
placeholder="Wybierz datę"
|
||||
class="block w-full max-w-lg shadow-sm rounded-md sm:text-sm"
|
||||
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors.from, 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors.from }"
|
||||
@on-change="onFromChange"
|
||||
/>
|
||||
<p
|
||||
v-if="form.errors.from"
|
||||
class="mt-2 text-sm text-red-600"
|
||||
>
|
||||
{{ form.errors.from }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="border-t border-gray-200 px-6 pt-8">
|
||||
<VacationChart :stats="stats" />
|
||||
</div>
|
||||
<div class="sm:grid sm:grid-cols-3 py-4 items-center">
|
||||
<label
|
||||
for="date_from"
|
||||
class="block text-sm font-medium text-gray-700 sm:mt-px"
|
||||
>
|
||||
Planowany urlop do
|
||||
</label>
|
||||
<div class="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<FlatPickr
|
||||
id="date_to"
|
||||
v-model="form.to"
|
||||
:config="toInputConfig"
|
||||
placeholder="Wybierz datę"
|
||||
class="block w-full max-w-lg shadow-sm rounded-md sm:text-sm"
|
||||
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors.to, 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors.to }"
|
||||
@on-change="onToChange"
|
||||
/>
|
||||
<p
|
||||
v-if="form.errors.to"
|
||||
class="mt-2 text-sm text-red-600"
|
||||
>
|
||||
{{ form.errors.to }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sm:grid sm:grid-cols-3 py-4 items-center">
|
||||
<span class="block text-sm font-medium text-gray-700 sm:mt-px">Liczba dni urlopu</span>
|
||||
<div
|
||||
class="mt-1 sm:mt-0 sm:col-span-2 w-full max-w-lg bg-gray-50 border border-gray-300 rounded-md px-4 py-2 inline-flex items-center text-gray-500 sm:text-sm"
|
||||
>
|
||||
{{ estimatedDays.length }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="sm:grid sm:grid-cols-3 py-4 items-center">
|
||||
<label
|
||||
for="comment"
|
||||
class="block text-sm font-medium text-gray-700"
|
||||
>
|
||||
Komentarz
|
||||
</label>
|
||||
<div class="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<textarea
|
||||
id="comment"
|
||||
v-model="form.comment"
|
||||
rows="4"
|
||||
class="shadow-sm focus:ring-blumilk-500 focus:border-blumilk-500 block w-full max-w-lg sm:text-sm border-gray-300 rounded-md"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="can.skipFlow"
|
||||
class="sm:grid sm:grid-cols-3 py-4 items-center"
|
||||
>
|
||||
<label
|
||||
for="flowSkipped"
|
||||
class="block text-sm font-medium text-gray-700"
|
||||
>
|
||||
Natychmiastowo zatwierdź wniosek
|
||||
</label>
|
||||
<div class="mt-1 sm:mt-0 sm:col-span-2">
|
||||
<Switch
|
||||
id="flowSkipped"
|
||||
v-model="form.flowSkipped"
|
||||
:class="[form.flowSkipped ? 'bg-blumilk-500' : 'bg-gray-200', 'relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blumilk-500']"
|
||||
>
|
||||
<span
|
||||
:class="[form.flowSkipped ? 'translate-x-5' : 'translate-x-0', 'pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200']"
|
||||
/>
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end py-3">
|
||||
<div class="space-x-3">
|
||||
<InertiaLink
|
||||
href="/vacation-requests"
|
||||
class="bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blumilk-500"
|
||||
>
|
||||
Anuluj
|
||||
</InertiaLink>
|
||||
<button
|
||||
type="submit"
|
||||
:disabled="form.processing"
|
||||
class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blumilk-600 hover:bg-blumilk-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blumilk-500"
|
||||
>
|
||||
Zapisz
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -280,13 +327,15 @@ import {useForm} from '@inertiajs/inertia-vue3'
|
||||
import FlatPickr from 'vue-flatpickr-component'
|
||||
import {Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions, Switch} from '@headlessui/vue'
|
||||
import {CheckIcon, SelectorIcon, XCircleIcon} from '@heroicons/vue/solid'
|
||||
import {reactive, ref} from 'vue'
|
||||
import {reactive, ref, watch} from 'vue'
|
||||
import axios from 'axios'
|
||||
import useCurrentYearPeriodInfo from '@/Composables/yearPeriodInfo'
|
||||
import VacationChart from '@/Shared/VacationChart'
|
||||
|
||||
export default {
|
||||
name: 'VacationRequestCreate',
|
||||
components: {
|
||||
VacationChart,
|
||||
Switch,
|
||||
FlatPickr,
|
||||
Listbox,
|
||||
@@ -333,6 +382,13 @@ export default {
|
||||
})
|
||||
|
||||
const estimatedDays = ref([])
|
||||
|
||||
const stats = ref({
|
||||
used: 0,
|
||||
pending: 0,
|
||||
remaining: 0,
|
||||
})
|
||||
|
||||
const {minDate, maxDate} = useCurrentYearPeriodInfo()
|
||||
|
||||
const disableDates = [
|
||||
@@ -351,9 +407,15 @@ export default {
|
||||
disable: disableDates,
|
||||
})
|
||||
|
||||
watch(() => form.user, user => {
|
||||
axios.post('/api/calculate-vacations-stats', {user: user.id})
|
||||
.then(res => stats.value = res.data)
|
||||
}, {immediate: true})
|
||||
|
||||
return {
|
||||
form,
|
||||
estimatedDays,
|
||||
stats,
|
||||
fromInputConfig,
|
||||
toInputConfig,
|
||||
}
|
||||
@@ -383,6 +445,5 @@ export default {
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
<div class="min-h-full">
|
||||
<MainMenu />
|
||||
<main class="lg:ml-64 flex flex-col flex-1 py-8">
|
||||
<div>
|
||||
<div class="px-4">
|
||||
<slot />
|
||||
</div>
|
||||
</main>
|
||||
|
@@ -110,7 +110,7 @@
|
||||
:class="[$page.url === '/' ? 'bg-blumilk-800 text-white' : 'text-blumilk-100 hover:text-white hover:bg-blumilk-600', 'group flex items-center px-2 py-2 text-sm leading-6 font-medium rounded-md']"
|
||||
>
|
||||
<HomeIcon class="mr-4 flex-shrink-0 h-6 w-6 text-blumilk-200" />
|
||||
Dashboard
|
||||
Strona główna
|
||||
</InertiaLink>
|
||||
</div>
|
||||
<div class="mt-4 pt-4">
|
||||
@@ -337,8 +337,6 @@ export default {
|
||||
].filter(item => item.can))
|
||||
|
||||
const userNavigation = [
|
||||
{name: 'Your Profile', href: '#'},
|
||||
{name: 'Settings', href: '#'},
|
||||
{name: 'Wyloguj się', href: '/logout', method: 'post', as: 'button'},
|
||||
]
|
||||
|
||||
|
93
resources/js/Shared/VacationChart.vue
Normal file
93
resources/js/Shared/VacationChart.vue
Normal file
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<v-chart
|
||||
style="height: 600px;"
|
||||
:autoresize="true"
|
||||
:option="option"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { use } from 'echarts/core'
|
||||
import { CanvasRenderer } from 'echarts/renderers'
|
||||
import { PieChart } from 'echarts/charts'
|
||||
import {
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
LegendComponent,
|
||||
} from 'echarts/components'
|
||||
import VChart from 'vue-echarts'
|
||||
import {computed} from 'vue'
|
||||
|
||||
use([
|
||||
CanvasRenderer,
|
||||
PieChart,
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
LegendComponent,
|
||||
])
|
||||
|
||||
export default {
|
||||
name: 'VacationChart',
|
||||
components: {
|
||||
VChart,
|
||||
},
|
||||
props: {
|
||||
stats: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
used: 0,
|
||||
pending: 0,
|
||||
remaining: 0,
|
||||
}),
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const option = computed(() => ({
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b} : {c} ({d}%)',
|
||||
},
|
||||
color: [
|
||||
'#2C466F',
|
||||
'#AABDDD',
|
||||
'#527ABA',
|
||||
],
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
data: ['Wykorzystane', 'Rozpatrywane', 'Pozostałe'],
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'Urlop wypoczynkowy',
|
||||
type: 'pie',
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2,
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'inner',
|
||||
formatter: param => param.value !== 0 ? param.value : '' ,
|
||||
fontWeight: 'bold',
|
||||
fontSize: 16,
|
||||
color: '#FFFFFF',
|
||||
labelLine: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
data: [
|
||||
{ value: props.stats.used, name: 'Wykorzystane' },
|
||||
{ value: props.stats.pending, name: 'Rozpatrywane' },
|
||||
{ value: props.stats.remaining, name: 'Pozostałe' },
|
||||
],
|
||||
radius: ['30%', '70%'],
|
||||
},
|
||||
],
|
||||
}))
|
||||
|
||||
return { option }
|
||||
},
|
||||
}
|
||||
</script>
|
Reference in New Issue
Block a user