- vue composition api (#91)
* wip * fix Co-authored-by: EwelinaLasowy <ewelina.lasowy@blumilk.pl>
This commit is contained in:
parent
95f5ed44d6
commit
dcda8c6255
@ -13,5 +13,7 @@ module.exports = {
|
|||||||
indent: ['error', 2],
|
indent: ['error', 2],
|
||||||
'vue/html-indent': ['error', 2],
|
'vue/html-indent': ['error', 2],
|
||||||
'comma-dangle': ['error', 'always-multiline'],
|
'comma-dangle': ['error', 'always-multiline'],
|
||||||
|
'object-curly-spacing': ['error', 'always'],
|
||||||
|
'vue/require-default-prop': 0,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ const types = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
export function useVacationTypeInfo() {
|
export default function useVacationTypeInfo() {
|
||||||
const getTypes = () => types
|
const getTypes = () => types
|
||||||
const findType = value => types.find(type => type.value === value)
|
const findType = value => types.find(type => type.value === value)
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {computed} from 'vue'
|
import { computed } from 'vue'
|
||||||
import {usePage} from '@inertiajs/inertia-vue3'
|
import { usePage } from '@inertiajs/inertia-vue3'
|
||||||
|
|
||||||
export default function useCurrentYearPeriodInfo() {
|
export default function useCurrentYearPeriodInfo() {
|
||||||
const minDate = computed(() => new Date(usePage().props.value.years.current, 0, 1))
|
const minDate = computed(() => new Date(usePage().props.value.years.current, 0, 1))
|
||||||
|
@ -83,9 +83,7 @@
|
|||||||
v-for="user in users.data"
|
v-for="user in users.data"
|
||||||
:key="user.id"
|
:key="user.id"
|
||||||
>
|
>
|
||||||
<th
|
<th class="border border-gray-300 py-2 px-4">
|
||||||
class="border border-gray-300 py-2 px-4"
|
|
||||||
>
|
|
||||||
<div class="flex justify-start items-center">
|
<div class="flex justify-start items-center">
|
||||||
<span class="inline-flex items-center justify-center h-10 w-10 rounded-full">
|
<span class="inline-flex items-center justify-center h-10 w-10 rounded-full">
|
||||||
<img
|
<img
|
||||||
@ -94,9 +92,7 @@
|
|||||||
>
|
>
|
||||||
</span>
|
</span>
|
||||||
<div class="ml-3">
|
<div class="ml-3">
|
||||||
<div
|
<div class="text-sm font-medium text-gray-900 whitespace-nowrap">
|
||||||
class="text-sm font-medium text-gray-900 whitespace-nowrap"
|
|
||||||
>
|
|
||||||
{{ user.name }}
|
{{ user.name }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -122,66 +118,28 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import {ChevronLeftIcon, ChevronRightIcon} from '@heroicons/vue/solid'
|
import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/vue/solid'
|
||||||
import {computed} from 'vue'
|
import { computed } from 'vue'
|
||||||
import {useMonthInfo} from '@/Composables/monthInfo'
|
import { useMonthInfo } from '@/Composables/monthInfo'
|
||||||
import VacationTypeCalendarIcon from '@/Shared/VacationTypeCalendarIcon'
|
import VacationTypeCalendarIcon from '@/Shared/VacationTypeCalendarIcon'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
name: 'VacationCalendar',
|
users: Object,
|
||||||
components: {
|
auth: Object,
|
||||||
VacationTypeCalendarIcon,
|
calendar: Object,
|
||||||
ChevronLeftIcon,
|
current: String,
|
||||||
ChevronRightIcon,
|
selected: String,
|
||||||
},
|
years: Object,
|
||||||
props: {
|
can: Object,
|
||||||
users: {
|
})
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
auth: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
calendar: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
current: {
|
|
||||||
type: String,
|
|
||||||
default: () => 'january',
|
|
||||||
},
|
|
||||||
selected: {
|
|
||||||
type: String,
|
|
||||||
default: () => 'january',
|
|
||||||
},
|
|
||||||
years: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
can: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const {getMonths, findMonth} = useMonthInfo()
|
|
||||||
const months = getMonths()
|
|
||||||
|
|
||||||
const currentMonth = computed(() => findMonth(props.current))
|
const { getMonths, findMonth } = useMonthInfo()
|
||||||
const selectedMonth = computed(() => findMonth(props.selected))
|
|
||||||
const previousMonth = computed(() => months[months.indexOf(selectedMonth.value) - 1])
|
|
||||||
const nextMonth = computed(() => months[months.indexOf(selectedMonth.value) + 1])
|
|
||||||
|
|
||||||
|
const months = getMonths()
|
||||||
|
|
||||||
return {
|
const currentMonth = computed(() => findMonth(props.current))
|
||||||
months,
|
const selectedMonth = computed(() => findMonth(props.selected))
|
||||||
currentMonth,
|
const previousMonth = computed(() => months[months.indexOf(selectedMonth.value) - 1])
|
||||||
selectedMonth,
|
const nextMonth = computed(() => months[months.indexOf(selectedMonth.value) + 1])
|
||||||
previousMonth,
|
|
||||||
nextMonth,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -2,346 +2,38 @@
|
|||||||
<InertiaHead title="Strona główna" />
|
<InertiaHead title="Strona główna" />
|
||||||
<div class="grid grid-cols-1 gap-4 items-start lg:grid-cols-3 lg:gap-8">
|
<div class="grid grid-cols-1 gap-4 items-start lg:grid-cols-3 lg:gap-8">
|
||||||
<div class="grid grid-cols-1 gap-4 lg:col-span-2">
|
<div class="grid grid-cols-1 gap-4 lg:col-span-2">
|
||||||
<section>
|
<Welcome :user="auth.user" />
|
||||||
<div class=" bg-white overflow-hidden shadow">
|
<VacationStats :stats="stats" />
|
||||||
<div class="bg-white p-6">
|
|
||||||
<div class="sm:flex sm:items-center sm:justify-between">
|
|
||||||
<div class="sm:flex sm:space-x-5">
|
|
||||||
<div class="flex-shrink-0">
|
|
||||||
<img
|
|
||||||
class="mx-auto h-20 w-20 rounded-full"
|
|
||||||
:src="user.avatar"
|
|
||||||
>
|
|
||||||
</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">
|
|
||||||
Cześć,
|
|
||||||
</p>
|
|
||||||
<p class="text-xl font-bold text-gray-900 sm:text-2xl">
|
|
||||||
{{ user.name }}
|
|
||||||
</p>
|
|
||||||
<p class="text-sm font-medium text-gray-600">
|
|
||||||
{{ user.role }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-1 gap-4">
|
<div class="grid grid-cols-1 gap-4">
|
||||||
<section v-if="can.listAllVacationRequests">
|
<PendingVacationRequests
|
||||||
<div class="bg-white shadow-md">
|
v-if="can.listAllVacationRequests"
|
||||||
<div class="p-4 sm:px-6">
|
:requests="vacationRequests.data"
|
||||||
<h2 class="text-lg leading-6 font-medium text-gray-900">
|
/>
|
||||||
Wnioski oczekujące na akcje
|
<UserVacationRequests
|
||||||
</h2>
|
v-else
|
||||||
</div>
|
:requests="vacationRequests.data"
|
||||||
<div class="border-t border-gray-200 pb-5 px-4 sm:px-6">
|
/>
|
||||||
<div class="flow-root mt-6">
|
<AbsenceList :absences="absences.data" />
|
||||||
<ul class="-my-5 divide-y divide-gray-200">
|
<UpcomingHolidays :holidays="holidays.data" />
|
||||||
<li
|
|
||||||
v-for="request in vacationRequests.data"
|
|
||||||
:key="request.id"
|
|
||||||
class="py-5"
|
|
||||||
>
|
|
||||||
<div class="relative focus-within:ring-2 focus-within:ring-blumilk-500">
|
|
||||||
<h3 class="text-sm font-semibold text-blumilk-600 hover:text-blumilk-500">
|
|
||||||
<InertiaLink
|
|
||||||
:href="`/vacation-requests/${request.id}`"
|
|
||||||
class="hover:underline focus:outline-none"
|
|
||||||
>
|
|
||||||
<span class="absolute inset-0" />
|
|
||||||
Wniosek o {{ findType(request.type).text.toLowerCase() }}
|
|
||||||
[{{ request.name }}]
|
|
||||||
</InertiaLink>
|
|
||||||
</h3>
|
|
||||||
<p class="mt-1 text-sm text-gray-600">
|
|
||||||
{{ request.from }} - {{ request.to }}
|
|
||||||
</p>
|
|
||||||
<div class="mt-3 text-sm text-gray-600">
|
|
||||||
<div class="flex">
|
|
||||||
<img
|
|
||||||
class="h-10 w-10 rounded-full"
|
|
||||||
:src="request.user.avatar"
|
|
||||||
>
|
|
||||||
<div class="ml-3">
|
|
||||||
<p class="text-sm font-medium text-gray-900">
|
|
||||||
{{ request.user.name }}
|
|
||||||
</p>
|
|
||||||
<p class="text-sm text-gray-500">
|
|
||||||
{{ request.user.email }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li v-if="! vacationRequests.data.length">
|
|
||||||
<p class="py-2">
|
|
||||||
Brak danych
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="mt-6">
|
|
||||||
<InertiaLink
|
|
||||||
href="/vacation-requests"
|
|
||||||
:data="{status: 'waiting_for_action'}"
|
|
||||||
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 focus:outline-none focus:ring-2 focus:ring-blumilk-500"
|
|
||||||
>
|
|
||||||
Zobacz wszystkie
|
|
||||||
</InertiaLink>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section v-else>
|
|
||||||
<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 class="-my-5 divide-y divide-gray-200">
|
|
||||||
<li
|
|
||||||
v-for="request in vacationRequests.data"
|
|
||||||
:key="request.id"
|
|
||||||
class="py-5"
|
|
||||||
>
|
|
||||||
<div class="relative focus-within:ring-2 focus-within:ring-blumilk-500">
|
|
||||||
<h3 class="text-sm font-semibold text-blumilk-600 hover:text-blumilk-500">
|
|
||||||
<InertiaLink
|
|
||||||
:href="`/vacation-requests/${request.id}`"
|
|
||||||
class="hover:underline focus:outline-none"
|
|
||||||
>
|
|
||||||
<span class="absolute inset-0" />
|
|
||||||
Wniosek o {{ findType(request.type).text.toLowerCase() }}
|
|
||||||
[{{ request.name }}]
|
|
||||||
</InertiaLink>
|
|
||||||
</h3>
|
|
||||||
<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>
|
|
||||||
<li v-if="! vacationRequests.data.length">
|
|
||||||
<p class="py-2">
|
|
||||||
Brak danych
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="mt-6">
|
|
||||||
<InertiaLink
|
|
||||||
href="/vacation-requests/me"
|
|
||||||
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 focus:outline-none focus:ring-2 focus:ring-blumilk-500"
|
|
||||||
>
|
|
||||||
Zobacz wszystkie
|
|
||||||
</InertiaLink>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<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>
|
|
||||||
<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"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
class="h-10 w-10 rounded-full"
|
|
||||||
:src="absence.user.avatar"
|
|
||||||
>
|
|
||||||
<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>
|
|
||||||
<li v-if="! holidays.data.length">
|
|
||||||
<p class="py-2">
|
|
||||||
Brak danych
|
|
||||||
</p>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<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 focus:outline-none focus:ring-2 focus:ring-blumilk-500"
|
|
||||||
>
|
|
||||||
Zobacz wszystkie
|
|
||||||
</InertiaLink>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import {computed} from 'vue'
|
import Welcome from '@/Shared/Widgets/Welcome'
|
||||||
import {usePage} from '@inertiajs/inertia-vue3'
|
import VacationStats from '@/Shared/Widgets/VacationStats'
|
||||||
import Status from '@/Shared/Status'
|
import AbsenceList from '@/Shared/Widgets/AbsenceList'
|
||||||
import VacationChart from '@/Shared/VacationChart'
|
import UpcomingHolidays from '@/Shared/Widgets/UpcomingHolidays'
|
||||||
import {useVacationTypeInfo} from '@/Composables/vacationTypeInfo'
|
import UserVacationRequests from '@/Shared/Widgets/UserVacationRequests'
|
||||||
|
import PendingVacationRequests from '@/Shared/Widgets/PendingVacationRequests'
|
||||||
|
|
||||||
export default {
|
defineProps({
|
||||||
name: 'DashboardPage',
|
auth: Object,
|
||||||
components: {Status, VacationChart},
|
absences: Object,
|
||||||
props: {
|
vacationRequests: Object,
|
||||||
absences: {
|
holidays: Object,
|
||||||
type: Object,
|
can: Object,
|
||||||
default: null,
|
stats: Object,
|
||||||
},
|
})
|
||||||
vacationRequests: {
|
|
||||||
type: Object,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
holidays: {
|
|
||||||
type: Object,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
can: {
|
|
||||||
type: Object,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
stats: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({
|
|
||||||
used: 0,
|
|
||||||
pending: 0,
|
|
||||||
remaining: 0,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup() {
|
|
||||||
const user = computed(() => usePage().props.value.auth.user)
|
|
||||||
|
|
||||||
const { findType } = useVacationTypeInfo()
|
|
||||||
|
|
||||||
return {
|
|
||||||
user,
|
|
||||||
findType,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -81,34 +81,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import {useForm} from '@inertiajs/inertia-vue3'
|
import { useForm } from '@inertiajs/inertia-vue3'
|
||||||
import FlatPickr from 'vue-flatpickr-component'
|
import FlatPickr from 'vue-flatpickr-component'
|
||||||
import useCurrentYearPeriodInfo from '@/Composables/yearPeriodInfo'
|
import useCurrentYearPeriodInfo from '@/Composables/yearPeriodInfo'
|
||||||
|
|
||||||
export default {
|
const form = useForm({
|
||||||
name: 'HolidayCreate',
|
|
||||||
components: {
|
|
||||||
FlatPickr,
|
|
||||||
},
|
|
||||||
setup() {
|
|
||||||
const form = useForm({
|
|
||||||
name: null,
|
name: null,
|
||||||
date: null,
|
date: null,
|
||||||
})
|
})
|
||||||
|
|
||||||
const {minDate, maxDate} = useCurrentYearPeriodInfo()
|
const { minDate, maxDate } = useCurrentYearPeriodInfo()
|
||||||
|
|
||||||
return {
|
function createHoliday() {
|
||||||
form,
|
form.post('/holidays')
|
||||||
minDate,
|
|
||||||
maxDate,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
createHoliday() {
|
|
||||||
this.form.post('/holidays')
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -80,34 +80,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import { useForm } from '@inertiajs/inertia-vue3'
|
import { useForm } from '@inertiajs/inertia-vue3'
|
||||||
import FlatPickr from 'vue-flatpickr-component'
|
import FlatPickr from 'vue-flatpickr-component'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
name: 'HolidayEdit',
|
holiday: Object,
|
||||||
components: {
|
})
|
||||||
FlatPickr,
|
|
||||||
},
|
const form = useForm({
|
||||||
props: {
|
|
||||||
holiday: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const form = useForm({
|
|
||||||
name: props.holiday.name,
|
name: props.holiday.name,
|
||||||
date: props.holiday.date,
|
date: props.holiday.date,
|
||||||
})
|
})
|
||||||
|
|
||||||
return { form }
|
function editHoliday() {
|
||||||
},
|
form.put(`/holidays/${props.holiday.id}`)
|
||||||
methods: {
|
|
||||||
editHoliday() {
|
|
||||||
this.form
|
|
||||||
.put(`/holidays/${this.holiday.id}`)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -94,10 +94,7 @@
|
|||||||
:href="`/holidays/${holiday.id}/edit`"
|
:href="`/holidays/${holiday.id}/edit`"
|
||||||
:class="[active ? 'bg-gray-100 text-gray-900' : 'text-gray-700', 'font-medium block px-4 py-2 text-sm']"
|
:class="[active ? 'bg-gray-100 text-gray-900' : 'text-gray-700', 'font-medium block px-4 py-2 text-sm']"
|
||||||
>
|
>
|
||||||
<PencilIcon
|
<PencilIcon class="mr-2 h-5 w-5 text-blue-500" /> Edytuj
|
||||||
class="mr-2 h-5 w-5 text-blue-500"
|
|
||||||
aria-hidden="true"
|
|
||||||
/> Edytuj
|
|
||||||
</InertiaLink>
|
</InertiaLink>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
@ -111,10 +108,7 @@
|
|||||||
:href="`/holidays/${holiday.id}`"
|
:href="`/holidays/${holiday.id}`"
|
||||||
:class="[active ? 'bg-gray-100 text-gray-900' : 'text-gray-700', 'block w-full text-left font-medium px-4 py-2 text-sm']"
|
:class="[active ? 'bg-gray-100 text-gray-900' : 'text-gray-700', 'block w-full text-left font-medium px-4 py-2 text-sm']"
|
||||||
>
|
>
|
||||||
<TrashIcon
|
<TrashIcon class="mr-2 h-5 w-5 text-red-500" /> Usuń
|
||||||
class="mr-2 h-5 w-5 text-red-500"
|
|
||||||
aria-hidden="true"
|
|
||||||
/> Usuń
|
|
||||||
</InertiaLink>
|
</InertiaLink>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</div>
|
</div>
|
||||||
@ -123,9 +117,7 @@
|
|||||||
</Menu>
|
</Menu>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr
|
<tr v-if="!holidays.data.length">
|
||||||
v-if="!holidays.data.length"
|
|
||||||
>
|
|
||||||
<td
|
<td
|
||||||
colspan="100%"
|
colspan="100%"
|
||||||
class="text-center py-4 text-xl leading-5 text-gray-700"
|
class="text-center py-4 text-xl leading-5 text-gray-700"
|
||||||
@ -140,33 +132,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import { DotsVerticalIcon, PencilIcon, TrashIcon } from '@heroicons/vue/solid'
|
import { DotsVerticalIcon, PencilIcon, TrashIcon } from '@heroicons/vue/solid'
|
||||||
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue'
|
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue'
|
||||||
|
|
||||||
export default {
|
defineProps({
|
||||||
name: 'HolidayIndex',
|
holidays: Object,
|
||||||
components: {
|
can: Object,
|
||||||
DotsVerticalIcon,
|
})
|
||||||
PencilIcon,
|
|
||||||
TrashIcon,
|
|
||||||
Menu,
|
|
||||||
MenuButton,
|
|
||||||
MenuItem,
|
|
||||||
MenuItems,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
holidays: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
can: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup() {
|
|
||||||
return {}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -26,10 +26,7 @@
|
|||||||
@click="delete errors.oauth"
|
@click="delete errors.oauth"
|
||||||
>
|
>
|
||||||
<span class="sr-only">Close</span>
|
<span class="sr-only">Close</span>
|
||||||
<XIcon
|
<XIcon class="h-5 w-5" />
|
||||||
class="h-5 w-5"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -43,7 +40,6 @@
|
|||||||
<img
|
<img
|
||||||
class="mx-auto h-50 w-auto"
|
class="mx-auto h-50 w-auto"
|
||||||
src="img/logo.png"
|
src="img/logo.png"
|
||||||
alt="Blumilk"
|
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href="/login/google/start"
|
href="/login/google/start"
|
||||||
@ -64,23 +60,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { XIcon, ExclamationIcon } from '@heroicons/vue/solid'
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
errors: Object,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import GuestLayout from '@/Shared/Layout/GuestLayout'
|
import GuestLayout from '@/Shared/Layout/GuestLayout'
|
||||||
import {XIcon} from '@heroicons/vue/solid'
|
|
||||||
import {ExclamationIcon} from '@heroicons/vue/solid'
|
|
||||||
|
|
||||||
export default {
|
export default { name: 'LoginPage', layout: GuestLayout }
|
||||||
name: 'LoginPage',
|
|
||||||
components: {
|
|
||||||
XIcon,
|
|
||||||
ExclamationIcon,
|
|
||||||
},
|
|
||||||
layout: GuestLayout,
|
|
||||||
props: {
|
|
||||||
errors: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({oauth: null}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -258,36 +258,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import { useForm } from '@inertiajs/inertia-vue3'
|
import { useForm } from '@inertiajs/inertia-vue3'
|
||||||
import FlatPickr from 'vue-flatpickr-component'
|
import FlatPickr from 'vue-flatpickr-component'
|
||||||
import { Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions } from '@headlessui/vue'
|
import { Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions } from '@headlessui/vue'
|
||||||
import { CheckIcon, SelectorIcon } from '@heroicons/vue/solid'
|
import { CheckIcon, SelectorIcon } from '@heroicons/vue/solid'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
name: 'UserCreate',
|
employmentForms: Object,
|
||||||
components: {
|
roles: Object,
|
||||||
FlatPickr,
|
})
|
||||||
Listbox,
|
|
||||||
ListboxButton,
|
const form = useForm({
|
||||||
ListboxLabel,
|
|
||||||
ListboxOption,
|
|
||||||
ListboxOptions,
|
|
||||||
CheckIcon,
|
|
||||||
SelectorIcon,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
employmentForms: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
roles: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const form = useForm({
|
|
||||||
firstName: null,
|
firstName: null,
|
||||||
lastName: null,
|
lastName: null,
|
||||||
email: null,
|
email: null,
|
||||||
@ -295,20 +277,15 @@ export default {
|
|||||||
role: props.roles[0],
|
role: props.roles[0],
|
||||||
position: null,
|
position: null,
|
||||||
employmentDate: null,
|
employmentDate: null,
|
||||||
})
|
})
|
||||||
|
|
||||||
return { form }
|
function createUser() {
|
||||||
},
|
form
|
||||||
methods: {
|
|
||||||
createUser() {
|
|
||||||
this.form
|
|
||||||
.transform(data => ({
|
.transform(data => ({
|
||||||
...data,
|
...data,
|
||||||
employmentForm: data.employmentForm.value,
|
employmentForm: data.employmentForm.value,
|
||||||
role: data.role.value,
|
role: data.role.value,
|
||||||
}))
|
}))
|
||||||
.post('/users')
|
.post('/users')
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -128,7 +128,9 @@
|
|||||||
leave-from-class="opacity-100"
|
leave-from-class="opacity-100"
|
||||||
leave-to-class="opacity-0"
|
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">
|
<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
|
<ListboxOption
|
||||||
v-for="role in roles"
|
v-for="role in roles"
|
||||||
:key="role.value"
|
:key="role.value"
|
||||||
@ -136,7 +138,9 @@
|
|||||||
as="template"
|
as="template"
|
||||||
:value="role"
|
:value="role"
|
||||||
>
|
>
|
||||||
<li :class="[active ? 'text-white bg-blumilk-600' : 'text-gray-900', 'cursor-default select-none relative py-2 pl-3 pr-9']">
|
<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']">
|
<span :class="[selected ? 'font-semibold' : 'font-normal', 'block truncate']">
|
||||||
{{ role.label }}
|
{{ role.label }}
|
||||||
</span>
|
</span>
|
||||||
@ -182,7 +186,9 @@
|
|||||||
leave-from-class="opacity-100"
|
leave-from-class="opacity-100"
|
||||||
leave-to-class="opacity-0"
|
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">
|
<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
|
<ListboxOption
|
||||||
v-for="employmentForm in employmentForms"
|
v-for="employmentForm in employmentForms"
|
||||||
:key="employmentForm.value"
|
:key="employmentForm.value"
|
||||||
@ -190,7 +196,9 @@
|
|||||||
as="template"
|
as="template"
|
||||||
:value="employmentForm"
|
:value="employmentForm"
|
||||||
>
|
>
|
||||||
<li :class="[active ? 'text-white bg-blumilk-600' : 'text-gray-900', 'cursor-default select-none relative py-2 pl-3 pr-9']">
|
<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']">
|
<span :class="[selected ? 'font-semibold' : 'font-normal', 'block truncate']">
|
||||||
{{ employmentForm.label }}
|
{{ employmentForm.label }}
|
||||||
</span>
|
</span>
|
||||||
@ -257,40 +265,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import {useForm} from '@inertiajs/inertia-vue3'
|
import { useForm } from '@inertiajs/inertia-vue3'
|
||||||
import FlatPickr from 'vue-flatpickr-component'
|
import FlatPickr from 'vue-flatpickr-component'
|
||||||
import {Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions} from '@headlessui/vue'
|
import { Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions } from '@headlessui/vue'
|
||||||
import {CheckIcon, SelectorIcon} from '@heroicons/vue/solid'
|
import { CheckIcon, SelectorIcon } from '@heroicons/vue/solid'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
name: 'UserEdit',
|
employmentForms: Object,
|
||||||
components: {
|
roles: Object,
|
||||||
FlatPickr,
|
user: Object,
|
||||||
Listbox,
|
})
|
||||||
ListboxButton,
|
|
||||||
ListboxLabel,
|
const form = useForm({
|
||||||
ListboxOption,
|
|
||||||
ListboxOptions,
|
|
||||||
CheckIcon,
|
|
||||||
SelectorIcon,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
employmentForms: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
roles: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
user: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const form = useForm({
|
|
||||||
firstName: props.user.firstName,
|
firstName: props.user.firstName,
|
||||||
lastName: props.user.lastName,
|
lastName: props.user.lastName,
|
||||||
email: props.user.email,
|
email: props.user.email,
|
||||||
@ -298,20 +285,15 @@ export default {
|
|||||||
position: props.user.position,
|
position: props.user.position,
|
||||||
employmentForm: props.employmentForms.find(form => form.value === props.user.employmentForm),
|
employmentForm: props.employmentForms.find(form => form.value === props.user.employmentForm),
|
||||||
employmentDate: props.user.employmentDate,
|
employmentDate: props.user.employmentDate,
|
||||||
})
|
})
|
||||||
|
|
||||||
return { form }
|
function editUser() {
|
||||||
},
|
form
|
||||||
methods: {
|
|
||||||
editUser() {
|
|
||||||
this.form
|
|
||||||
.transform(data => ({
|
.transform(data => ({
|
||||||
...data,
|
...data,
|
||||||
employmentForm: data.employmentForm.value,
|
employmentForm: data.employmentForm.value,
|
||||||
role: data.role.value,
|
role: data.role.value,
|
||||||
}))
|
}))
|
||||||
.put(`/users/${this.user.id}`)
|
.put(`/users/${props.user.id}`)
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -81,13 +81,10 @@
|
|||||||
>
|
>
|
||||||
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">
|
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<span
|
<span class="inline-flex items-center justify-center h-10 w-10 rounded-full">
|
||||||
class="inline-flex items-center justify-center h-10 w-10 rounded-full"
|
|
||||||
>
|
|
||||||
<img
|
<img
|
||||||
class="h-10 w-10 rounded-full"
|
class="h-10 w-10 rounded-full"
|
||||||
:src="user.avatar"
|
:src="user.avatar"
|
||||||
alt=""
|
|
||||||
>
|
>
|
||||||
</span>
|
</span>
|
||||||
<div class="ml-3">
|
<div class="ml-3">
|
||||||
@ -118,10 +115,7 @@
|
|||||||
class="relative inline-block text-left"
|
class="relative inline-block text-left"
|
||||||
>
|
>
|
||||||
<MenuButton class="rounded-full flex items-center text-gray-400 hover:text-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-blumilk-500">
|
<MenuButton class="rounded-full flex items-center text-gray-400 hover:text-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-blumilk-500">
|
||||||
<DotsVerticalIcon
|
<DotsVerticalIcon class="h-5 w-5" />
|
||||||
class="h-5 w-5"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
</MenuButton>
|
</MenuButton>
|
||||||
|
|
||||||
<transition
|
<transition
|
||||||
@ -145,10 +139,7 @@
|
|||||||
:href="`/users/${user.id}/edit`"
|
:href="`/users/${user.id}/edit`"
|
||||||
:class="[active ? 'bg-gray-100 text-gray-900' : 'text-gray-700', 'font-medium block px-4 py-2 text-sm']"
|
:class="[active ? 'bg-gray-100 text-gray-900' : 'text-gray-700', 'font-medium block px-4 py-2 text-sm']"
|
||||||
>
|
>
|
||||||
<PencilIcon
|
<PencilIcon class="mr-2 h-5 w-5 text-blue-500" /> Edytuj
|
||||||
class="mr-2 h-5 w-5 text-blue-500"
|
|
||||||
aria-hidden="true"
|
|
||||||
/> Edytuj
|
|
||||||
</InertiaLink>
|
</InertiaLink>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
@ -162,10 +153,7 @@
|
|||||||
:href="`/users/${user.id}`"
|
:href="`/users/${user.id}`"
|
||||||
:class="[active ? 'bg-gray-100 text-gray-900' : 'text-gray-700', 'block w-full text-left font-medium px-4 py-2 text-sm']"
|
:class="[active ? 'bg-gray-100 text-gray-900' : 'text-gray-700', 'block w-full text-left font-medium px-4 py-2 text-sm']"
|
||||||
>
|
>
|
||||||
<TrashIcon
|
<TrashIcon class="mr-2 h-5 w-5 text-red-500" /> Usuń
|
||||||
class="mr-2 h-5 w-5 text-red-500"
|
|
||||||
aria-hidden="true"
|
|
||||||
/> Usuń
|
|
||||||
</InertiaLink>
|
</InertiaLink>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</div>
|
</div>
|
||||||
@ -184,10 +172,7 @@
|
|||||||
:href="`/users/${user.id}/restore`"
|
:href="`/users/${user.id}/restore`"
|
||||||
:class="[active ? 'bg-gray-100 text-gray-900' : 'text-gray-700', 'block w-full text-left font-medium px-4 py-2 text-sm']"
|
:class="[active ? 'bg-gray-100 text-gray-900' : 'text-gray-700', 'block w-full text-left font-medium px-4 py-2 text-sm']"
|
||||||
>
|
>
|
||||||
<RefreshIcon
|
<RefreshIcon class="mr-2 h-5 w-5 text-green-500" /> Przywróć
|
||||||
class="mr-2 h-5 w-5 text-green-500"
|
|
||||||
aria-hidden="true"
|
|
||||||
/> Przywróć
|
|
||||||
</InertiaLink>
|
</InertiaLink>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</div>
|
</div>
|
||||||
@ -208,102 +193,32 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div
|
<Pagination :pagination="users.meta" />
|
||||||
v-if="users.data.length && users.meta.last_page !== 1"
|
|
||||||
class="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6 rounded-b-lg"
|
|
||||||
>
|
|
||||||
<div class="flex-1 flex justify-between sm:hidden">
|
|
||||||
<InertiaLink
|
|
||||||
:is="users.links.prev ? 'InertiaLink': 'span'"
|
|
||||||
:href="users.links.prev"
|
|
||||||
class="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"
|
|
||||||
>
|
|
||||||
Poprzednia
|
|
||||||
</InertiaLink>
|
|
||||||
<Component
|
|
||||||
:is="users.links.next ? 'InertiaLink': 'span'"
|
|
||||||
:href="users.links.next"
|
|
||||||
class="ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"
|
|
||||||
>
|
|
||||||
Następna
|
|
||||||
</Component>
|
|
||||||
</div>
|
|
||||||
<div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
|
|
||||||
<div class="text-sm text-gray-700">
|
|
||||||
Wyświetlanie
|
|
||||||
<span class="font-medium">{{ users.meta.from }}</span>
|
|
||||||
od
|
|
||||||
<span class="font-medium">{{ users.meta.to }}</span>
|
|
||||||
do
|
|
||||||
<span class="font-medium">{{ users.meta.total }}</span>
|
|
||||||
wyników
|
|
||||||
</div>
|
|
||||||
<nav class="relative z-0 inline-flex space-x-1">
|
|
||||||
<template
|
|
||||||
v-for="(link, index) in users.meta.links"
|
|
||||||
:key="index"
|
|
||||||
>
|
|
||||||
<Component
|
|
||||||
:is="link.url ? 'InertiaLink' : 'span'"
|
|
||||||
:href="link.url"
|
|
||||||
:preserve-scroll="true"
|
|
||||||
class="relative inline-flex items-center px-4 py-2 border rounded-md text-sm font-medium"
|
|
||||||
:class="{ 'z-10 bg-blumilk-25 border-blumilk-500 text-blumilk-600': link.active, 'bg-white border-gray-300 text-gray-500': !link.active, 'hover:bg-blumilk-25': link.url, 'border-none': !link.url}"
|
|
||||||
v-text="link.label"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import { ref, watch } from 'vue'
|
import { ref, watch } from 'vue'
|
||||||
import { Inertia } from '@inertiajs/inertia'
|
import { Inertia } from '@inertiajs/inertia'
|
||||||
import { debounce } from 'lodash'
|
import { debounce } from 'lodash'
|
||||||
import { SearchIcon } from '@heroicons/vue/outline'
|
import { SearchIcon } from '@heroicons/vue/outline'
|
||||||
import { DotsVerticalIcon, PencilIcon, TrashIcon, RefreshIcon } from '@heroicons/vue/solid'
|
import { DotsVerticalIcon, PencilIcon, TrashIcon, RefreshIcon } from '@heroicons/vue/solid'
|
||||||
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue'
|
import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue'
|
||||||
|
import Pagination from '@/Shared/Pagination'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
name: 'UserIndex',
|
users: Object,
|
||||||
components: {
|
filters: Object,
|
||||||
SearchIcon,
|
})
|
||||||
DotsVerticalIcon,
|
|
||||||
PencilIcon,
|
|
||||||
TrashIcon,
|
|
||||||
RefreshIcon,
|
|
||||||
Menu,
|
|
||||||
MenuButton,
|
|
||||||
MenuItem,
|
|
||||||
MenuItems,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
users: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
filters: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
let search = ref(props.filters.search)
|
|
||||||
|
|
||||||
watch(search, debounce(value => {
|
const search = ref(props.filters.search)
|
||||||
Inertia.get('/users', value ? { search: value} : {}, {
|
|
||||||
|
watch(search, debounce(value => {
|
||||||
|
Inertia.get('/users', value ? { search: value } : {}, {
|
||||||
preserveState: true,
|
preserveState: true,
|
||||||
replace: true,
|
replace: true,
|
||||||
})
|
})
|
||||||
}, 300))
|
}, 300))
|
||||||
|
|
||||||
return {
|
|
||||||
search,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
Dostępne dni urlopu dla użytkowników
|
Dostępne dni urlopu dla użytkowników
|
||||||
</h2>
|
</h2>
|
||||||
<p class="mt-1 text-sm text-gray-500">
|
<p class="mt-1 text-sm text-gray-500">
|
||||||
Zarządzaj dostepnymi dniami urlopów dla użytkowników.
|
Zarządzaj dostępnymi dniami urlopów dla użytkowników.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -56,13 +56,10 @@
|
|||||||
>
|
>
|
||||||
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">
|
<td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<span
|
<span class="inline-flex items-center justify-center h-10 w-10 rounded-full">
|
||||||
class="inline-flex items-center justify-center h-10 w-10 rounded-full"
|
|
||||||
>
|
|
||||||
<img
|
<img
|
||||||
class="h-10 w-10 rounded-full"
|
class="h-10 w-10 rounded-full"
|
||||||
:src="item.user.avatar"
|
:src="item.user.avatar"
|
||||||
alt=""
|
|
||||||
>
|
>
|
||||||
</span>
|
</span>
|
||||||
<div class="ml-3">
|
<div class="ml-3">
|
||||||
@ -112,9 +109,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr
|
<tr v-if="!form.items.length">
|
||||||
v-if="!form.items.length"
|
|
||||||
>
|
|
||||||
<td
|
<td
|
||||||
colspan="100%"
|
colspan="100%"
|
||||||
class="text-center py-4 text-xl leading-5 text-gray-700"
|
class="text-center py-4 text-xl leading-5 text-gray-700"
|
||||||
@ -137,37 +132,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import {Switch} from '@headlessui/vue'
|
import { Switch } from '@headlessui/vue'
|
||||||
import {useForm} from '@inertiajs/inertia-vue3'
|
import { useForm } from '@inertiajs/inertia-vue3'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
name: 'VacationLimits',
|
limits: Object,
|
||||||
components: {
|
years: Object,
|
||||||
Switch,
|
})
|
||||||
},
|
|
||||||
props: {
|
const form = useForm({
|
||||||
limits: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
years: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const form = useForm({
|
|
||||||
items: props.limits,
|
items: props.limits,
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
function submitVacationDays() {
|
||||||
form,
|
form
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
submitVacationDays() {
|
|
||||||
this.form
|
|
||||||
.transform(data => ({
|
.transform(data => ({
|
||||||
items: data.items.map(item => ({
|
items: data.items.map(item => ({
|
||||||
id: item.id,
|
id: item.id,
|
||||||
@ -178,7 +157,5 @@ export default {
|
|||||||
preserveState: (page) => Object.keys(page.props.errors).length,
|
preserveState: (page) => Object.keys(page.props.errors).length,
|
||||||
preserveScroll: true,
|
preserveScroll: true,
|
||||||
})
|
})
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -322,55 +322,26 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import {useForm} from '@inertiajs/inertia-vue3'
|
import { useForm } from '@inertiajs/inertia-vue3'
|
||||||
import FlatPickr from 'vue-flatpickr-component'
|
import FlatPickr from 'vue-flatpickr-component'
|
||||||
import {Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions, Switch} from '@headlessui/vue'
|
import { Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions, Switch } from '@headlessui/vue'
|
||||||
import {CheckIcon, SelectorIcon, XCircleIcon} from '@heroicons/vue/solid'
|
import { CheckIcon, SelectorIcon, XCircleIcon } from '@heroicons/vue/solid'
|
||||||
import {reactive, ref, watch} from 'vue'
|
import { reactive, ref, watch } from 'vue'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import useCurrentYearPeriodInfo from '@/Composables/yearPeriodInfo'
|
import useCurrentYearPeriodInfo from '@/Composables/yearPeriodInfo'
|
||||||
import VacationChart from '@/Shared/VacationChart'
|
import VacationChart from '@/Shared/VacationChart'
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'VacationRequestCreate',
|
const props = defineProps({
|
||||||
components: {
|
auth: Object,
|
||||||
VacationChart,
|
users: Object,
|
||||||
Switch,
|
vacationTypes: Object,
|
||||||
FlatPickr,
|
holidays: Object,
|
||||||
Listbox,
|
can: Object,
|
||||||
ListboxButton,
|
})
|
||||||
ListboxLabel,
|
|
||||||
ListboxOption,
|
const form = useForm({
|
||||||
ListboxOptions,
|
|
||||||
CheckIcon,
|
|
||||||
SelectorIcon,
|
|
||||||
XCircleIcon,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
auth: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
users: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
vacationTypes: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
holidays: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
can: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const form = useForm({
|
|
||||||
user: props.can.createOnBehalfOfEmployee
|
user: props.can.createOnBehalfOfEmployee
|
||||||
? props.users.data.find(user => user.id === props.auth.user.id) ?? props.users.data[0]
|
? props.users.data.find(user => user.id === props.auth.user.id) ?? props.users.data[0]
|
||||||
: props.auth.user,
|
: props.auth.user,
|
||||||
@ -379,71 +350,66 @@ export default {
|
|||||||
type: props.vacationTypes[0],
|
type: props.vacationTypes[0],
|
||||||
comment: null,
|
comment: null,
|
||||||
flowSkipped: false,
|
flowSkipped: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
const estimatedDays = ref([])
|
const estimatedDays = ref([])
|
||||||
|
|
||||||
const stats = ref({
|
const stats = ref({
|
||||||
used: 0,
|
used: 0,
|
||||||
pending: 0,
|
pending: 0,
|
||||||
remaining: 0,
|
remaining: 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
const {minDate, maxDate} = useCurrentYearPeriodInfo()
|
const { minDate, maxDate } = useCurrentYearPeriodInfo()
|
||||||
|
|
||||||
const disableDates = [
|
const disableDates = [
|
||||||
date => (date.getDay() === 0 || date.getDay() === 6),
|
date => (date.getDay() === 0 || date.getDay() === 6),
|
||||||
]
|
]
|
||||||
|
|
||||||
const fromInputConfig = reactive({
|
const fromInputConfig = reactive({
|
||||||
minDate: minDate,
|
minDate,
|
||||||
maxDate: maxDate,
|
maxDate,
|
||||||
disable: disableDates,
|
disable: disableDates,
|
||||||
})
|
})
|
||||||
|
|
||||||
const toInputConfig = reactive({
|
const toInputConfig = reactive({
|
||||||
minDate: minDate,
|
minDate,
|
||||||
maxDate: maxDate,
|
maxDate,
|
||||||
disable: disableDates,
|
disable: disableDates,
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(() => form.user, user => {
|
watch(() => form.user, async user => {
|
||||||
axios.post('/api/calculate-vacations-stats', {user: user.id})
|
const res = await axios.post('/api/calculate-vacations-stats', { user: user.id })
|
||||||
.then(res => stats.value = res.data)
|
|
||||||
}, {immediate: true})
|
|
||||||
|
|
||||||
return {
|
stats.value = res.data
|
||||||
form,
|
}, { immediate: true })
|
||||||
estimatedDays,
|
|
||||||
stats,
|
function createForm() {
|
||||||
fromInputConfig,
|
form
|
||||||
toInputConfig,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
createForm() {
|
|
||||||
this.form
|
|
||||||
.transform(data => ({
|
.transform(data => ({
|
||||||
...data,
|
...data,
|
||||||
type: data.type.value,
|
type: data.type.value,
|
||||||
user: data.user.id,
|
user: data.user.id,
|
||||||
}))
|
}))
|
||||||
.post('/vacation-requests')
|
.post('/vacation-requests')
|
||||||
},
|
|
||||||
onFromChange(selectedDates, dateStr) {
|
|
||||||
this.form.to = dateStr
|
|
||||||
|
|
||||||
this.refreshEstimatedDays(this.form.from, this.form.to)
|
|
||||||
},
|
|
||||||
onToChange() {
|
|
||||||
this.refreshEstimatedDays(this.form.from, this.form.to)
|
|
||||||
},
|
|
||||||
refreshEstimatedDays(from, to) {
|
|
||||||
if (from && to) {
|
|
||||||
axios.post('/api/calculate-vacation-days', {from, to})
|
|
||||||
.then(res => this.estimatedDays = res.data)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onFromChange(selectedDates, dateStr) {
|
||||||
|
form.to = dateStr
|
||||||
|
|
||||||
|
refreshEstimatedDays(form.from, form.to)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onToChange() {
|
||||||
|
refreshEstimatedDays(form.from, form.to)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshEstimatedDays(from, to) {
|
||||||
|
if (from && to) {
|
||||||
|
const res = await axios.post('/api/calculate-vacation-days', { from, to })
|
||||||
|
|
||||||
|
estimatedDays.value = res.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -118,9 +118,7 @@
|
|||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr
|
<tr v-if="! requests.data.length">
|
||||||
v-if="! requests.data.length"
|
|
||||||
>
|
|
||||||
<td
|
<td
|
||||||
colspan="100%"
|
colspan="100%"
|
||||||
class="text-center py-4 text-xl leading-5 text-gray-700"
|
class="text-center py-4 text-xl leading-5 text-gray-700"
|
||||||
@ -130,110 +128,24 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div
|
<Pagination :pagination="requests.meta" />
|
||||||
v-if="requests.data.length && requests.meta.last_page !== 1"
|
|
||||||
class="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6 rounded-b-lg"
|
|
||||||
>
|
|
||||||
<div class="flex-1 flex justify-between sm:hidden">
|
|
||||||
<InertiaLink
|
|
||||||
:is="requests.links.prev ? 'InertiaLink': 'span'"
|
|
||||||
:href="requests.links.prev"
|
|
||||||
class="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"
|
|
||||||
>
|
|
||||||
Poprzednia
|
|
||||||
</InertiaLink>
|
|
||||||
<Component
|
|
||||||
:is="requests.links.next ? 'InertiaLink': 'span'"
|
|
||||||
:href="requests.links.next"
|
|
||||||
class="ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"
|
|
||||||
>
|
|
||||||
Następna
|
|
||||||
</Component>
|
|
||||||
</div>
|
|
||||||
<div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
|
|
||||||
<div class="text-sm text-gray-700">
|
|
||||||
Wyświetlanie
|
|
||||||
<span class="font-medium">{{ requests.meta.from }}</span>
|
|
||||||
od
|
|
||||||
<span class="font-medium">{{ requests.meta.to }}</span>
|
|
||||||
do
|
|
||||||
<span class="font-medium">{{ requests.meta.total }}</span>
|
|
||||||
wyników
|
|
||||||
</div>
|
|
||||||
<nav class="relative z-0 inline-flex space-x-1">
|
|
||||||
<template
|
|
||||||
v-for="(link, index) in requests.meta.links"
|
|
||||||
:key="index"
|
|
||||||
>
|
|
||||||
<Component
|
|
||||||
:is="link.url ? 'InertiaLink' : 'span'"
|
|
||||||
:href="link.url"
|
|
||||||
:preserve-scroll="true"
|
|
||||||
class="relative inline-flex items-center px-4 py-2 border rounded-md text-sm font-medium"
|
|
||||||
:class="{ 'z-10 bg-blumilk-25 border-blumilk-500 text-blumilk-600': link.active, 'bg-white border-gray-300 text-gray-500': !link.active, 'hover:bg-blumilk-25': link.url, 'border-none': !link.url}"
|
|
||||||
v-text="link.label"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import {
|
import { ChevronRightIcon } from '@heroicons/vue/solid'
|
||||||
ChevronRightIcon,
|
|
||||||
ClockIcon,
|
|
||||||
DotsVerticalIcon,
|
|
||||||
PencilIcon,
|
|
||||||
ThumbDownIcon,
|
|
||||||
ThumbUpIcon,
|
|
||||||
TrashIcon,
|
|
||||||
XIcon,
|
|
||||||
CheckIcon,
|
|
||||||
DocumentTextIcon,
|
|
||||||
} from '@heroicons/vue/solid'
|
|
||||||
import Status from '@/Shared/Status'
|
import Status from '@/Shared/Status'
|
||||||
import VacationType from '@/Shared/VacationType'
|
import VacationType from '@/Shared/VacationType'
|
||||||
|
import Pagination from '@/Shared/Pagination'
|
||||||
|
|
||||||
export default {
|
defineProps({
|
||||||
name: 'VacationRequestIndex',
|
requests: Object,
|
||||||
components: {
|
stats: Object,
|
||||||
DotsVerticalIcon,
|
filters: Object,
|
||||||
PencilIcon,
|
})
|
||||||
TrashIcon,
|
|
||||||
ChevronRightIcon,
|
const statuses = [
|
||||||
ThumbUpIcon,
|
|
||||||
ClockIcon,
|
|
||||||
XIcon,
|
|
||||||
CheckIcon,
|
|
||||||
DocumentTextIcon,
|
|
||||||
ThumbDownIcon,
|
|
||||||
Status,
|
|
||||||
VacationType,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
requests: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
stats: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({
|
|
||||||
all: 0,
|
|
||||||
pending: 0,
|
|
||||||
success: 0,
|
|
||||||
failed: 0,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
filters: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup() {
|
|
||||||
const statuses = [
|
|
||||||
{
|
{
|
||||||
name: 'Wszystkie',
|
name: 'Wszystkie',
|
||||||
value: 'all',
|
value: 'all',
|
||||||
@ -250,11 +162,5 @@ export default {
|
|||||||
name: 'Odrzucone/anulowane',
|
name: 'Odrzucone/anulowane',
|
||||||
value: 'failed',
|
value: 'failed',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
return {
|
|
||||||
statuses,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -93,7 +93,9 @@
|
|||||||
:src="user.avatar"
|
:src="user.avatar"
|
||||||
class="flex-shrink-0 h-6 w-6 rounded-full"
|
class="flex-shrink-0 h-6 w-6 rounded-full"
|
||||||
>
|
>
|
||||||
<span :class="[form.user?.id === user.id ? 'font-semibold' : 'font-normal', 'ml-3 block truncate']">
|
<span
|
||||||
|
:class="[form.user?.id === user.id ? 'font-semibold' : 'font-normal', 'ml-3 block truncate']"
|
||||||
|
>
|
||||||
{{ user.name }}
|
{{ user.name }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -123,9 +125,7 @@
|
|||||||
<ListboxButton
|
<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 focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300"
|
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 focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300"
|
||||||
>
|
>
|
||||||
<span
|
<span class="flex items-center">
|
||||||
class="flex items-center"
|
|
||||||
>
|
|
||||||
{{ form.status.name }}
|
{{ form.status.name }}
|
||||||
</span>
|
</span>
|
||||||
<span class="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
|
<span class="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
|
||||||
@ -274,9 +274,7 @@
|
|||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr
|
<tr v-if="! requests.data.length">
|
||||||
v-if="! requests.data.length"
|
|
||||||
>
|
|
||||||
<td
|
<td
|
||||||
colspan="100%"
|
colspan="100%"
|
||||||
class="text-center py-4 text-xl leading-5 text-gray-700"
|
class="text-center py-4 text-xl leading-5 text-gray-700"
|
||||||
@ -286,118 +284,28 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div
|
<Pagination :pagination="requests.meta" />
|
||||||
v-if="requests.data.length && requests.meta.last_page !== 1"
|
|
||||||
class="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6 rounded-b-lg"
|
|
||||||
>
|
|
||||||
<div class="flex-1 flex justify-between sm:hidden">
|
|
||||||
<InertiaLink
|
|
||||||
:is="requests.links.prev ? 'InertiaLink': 'span'"
|
|
||||||
:href="requests.links.prev"
|
|
||||||
class="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"
|
|
||||||
>
|
|
||||||
Poprzednia
|
|
||||||
</InertiaLink>
|
|
||||||
<Component
|
|
||||||
:is="requests.links.next ? 'InertiaLink': 'span'"
|
|
||||||
:href="requests.links.next"
|
|
||||||
class="ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"
|
|
||||||
>
|
|
||||||
Następna
|
|
||||||
</Component>
|
|
||||||
</div>
|
|
||||||
<div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
|
|
||||||
<div class="text-sm text-gray-700">
|
|
||||||
Wyświetlanie
|
|
||||||
<span class="font-medium">{{ requests.meta.from }}</span>
|
|
||||||
od
|
|
||||||
<span class="font-medium">{{ requests.meta.to }}</span>
|
|
||||||
do
|
|
||||||
<span class="font-medium">{{ requests.meta.total }}</span>
|
|
||||||
wyników
|
|
||||||
</div>
|
|
||||||
<nav class="relative z-0 inline-flex space-x-1">
|
|
||||||
<template
|
|
||||||
v-for="(link, index) in requests.meta.links"
|
|
||||||
:key="index"
|
|
||||||
>
|
|
||||||
<Component
|
|
||||||
:is="link.url ? 'InertiaLink' : 'span'"
|
|
||||||
:href="link.url"
|
|
||||||
:preserve-scroll="true"
|
|
||||||
class="relative inline-flex items-center px-4 py-2 border rounded-md text-sm font-medium"
|
|
||||||
:class="{ 'z-10 bg-blumilk-25 border-blumilk-500 text-blumilk-600': link.active, 'bg-white border-gray-300 text-gray-500': !link.active, 'hover:bg-blumilk-25': link.url, 'border-none': !link.url}"
|
|
||||||
v-text="link.label"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import {
|
import { CheckIcon, ChevronRightIcon, SelectorIcon } from '@heroicons/vue/solid'
|
||||||
CheckIcon,
|
|
||||||
ChevronRightIcon,
|
|
||||||
ClockIcon,
|
|
||||||
DocumentTextIcon,
|
|
||||||
DotsVerticalIcon,
|
|
||||||
PencilIcon,
|
|
||||||
SelectorIcon,
|
|
||||||
ThumbDownIcon,
|
|
||||||
ThumbUpIcon,
|
|
||||||
TrashIcon,
|
|
||||||
XCircleIcon,
|
|
||||||
XIcon,
|
|
||||||
} from '@heroicons/vue/solid'
|
|
||||||
import Status from '@/Shared/Status'
|
import Status from '@/Shared/Status'
|
||||||
import VacationType from '@/Shared/VacationType'
|
import VacationType from '@/Shared/VacationType'
|
||||||
import {watch, reactive} from 'vue'
|
import { watch, reactive } from 'vue'
|
||||||
import {debounce} from 'lodash'
|
import { debounce } from 'lodash'
|
||||||
import {Inertia} from '@inertiajs/inertia'
|
import { Inertia } from '@inertiajs/inertia'
|
||||||
import {Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions} from '@headlessui/vue'
|
import { Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions } from '@headlessui/vue'
|
||||||
|
import Pagination from '@/Shared/Pagination'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
name: 'VacationRequestIndex',
|
requests: Object,
|
||||||
components: {
|
users: Object,
|
||||||
Listbox,
|
filters: Object,
|
||||||
ListboxButton,
|
})
|
||||||
ListboxLabel,
|
|
||||||
ListboxOption,
|
const statuses = [
|
||||||
ListboxOptions,
|
|
||||||
DotsVerticalIcon,
|
|
||||||
PencilIcon,
|
|
||||||
TrashIcon,
|
|
||||||
ChevronRightIcon,
|
|
||||||
ThumbUpIcon,
|
|
||||||
ClockIcon,
|
|
||||||
XIcon,
|
|
||||||
CheckIcon,
|
|
||||||
DocumentTextIcon,
|
|
||||||
ThumbDownIcon,
|
|
||||||
Status,
|
|
||||||
VacationType,
|
|
||||||
SelectorIcon,
|
|
||||||
XCircleIcon,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
requests: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
users: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
filters: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const statuses = [
|
|
||||||
{
|
{
|
||||||
name: 'Wszystkie',
|
name: 'Wszystkie',
|
||||||
value: 'all',
|
value: 'all',
|
||||||
@ -418,21 +326,17 @@ export default {
|
|||||||
name: 'Odrzucone/anulowane',
|
name: 'Odrzucone/anulowane',
|
||||||
value: 'failed',
|
value: 'failed',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
user: props.users.data.find(user => user.id === props.filters.user) ?? null,
|
user: props.users.data.find(user => user.id === props.filters.user) ?? null,
|
||||||
status: statuses.find(status => status.value === props.filters.status) ?? statuses[0],
|
status: statuses.find(status => status.value === props.filters.status) ?? statuses[0],
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(form, debounce(() => {
|
watch(form, debounce(() => {
|
||||||
Inertia.get('/vacation-requests', {user: form.user?.id, status: form.status.value}, {
|
Inertia.get('/vacation-requests', { user: form.user?.id, status: form.status.value }, {
|
||||||
preserveState: true,
|
preserveState: true,
|
||||||
replace: true,
|
replace: true,
|
||||||
})
|
})
|
||||||
}, 300))
|
}, 300))
|
||||||
|
|
||||||
return {form, statuses}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -245,33 +245,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import {PaperClipIcon} from '@heroicons/vue/outline'
|
import { PaperClipIcon } from '@heroicons/vue/outline'
|
||||||
import Activity from '@/Shared/Activity'
|
import Activity from '@/Shared/Activity'
|
||||||
import Status from '@/Shared/Status'
|
import Status from '@/Shared/Status'
|
||||||
import VacationType from '@/Shared/VacationType'
|
import VacationType from '@/Shared/VacationType'
|
||||||
|
|
||||||
export default {
|
defineProps({
|
||||||
name: 'VacationRequestShow',
|
request: Object,
|
||||||
components: {
|
can: Object,
|
||||||
VacationType,
|
activities: Object,
|
||||||
Activity,
|
})
|
||||||
PaperClipIcon,
|
|
||||||
Status,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
request: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
can: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
activities: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -31,30 +31,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import {computed} from 'vue'
|
import { computed } from 'vue'
|
||||||
import {useStatusInfo} from '@/Composables/statusInfo'
|
import { useStatusInfo } from '@/Composables/statusInfo'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
name: 'VacationRequestActivity',
|
activity: Object,
|
||||||
props: {
|
last: Boolean,
|
||||||
activity: {
|
})
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
last: {
|
|
||||||
type: Boolean,
|
|
||||||
default: () => false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const { findStatus } = useStatusInfo()
|
|
||||||
|
|
||||||
const statusInfo = computed(() => findStatus(props.activity.state))
|
const { findStatus } = useStatusInfo()
|
||||||
|
|
||||||
return {
|
const statusInfo = computed(() => findStatus(props.activity.state))
|
||||||
statusInfo,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="min-h-full">
|
<div class="min-h-full">
|
||||||
<MainMenu />
|
<MainMenu
|
||||||
|
:auth="auth"
|
||||||
|
:years="years"
|
||||||
|
/>
|
||||||
<main class="lg:ml-64 flex flex-col flex-1 py-8">
|
<main class="lg:ml-64 flex flex-col flex-1 py-8">
|
||||||
<div class="px-4">
|
<div class="px-4">
|
||||||
<slot />
|
<slot />
|
||||||
@ -9,26 +12,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import MainMenu from '@/Shared/MainMenu'
|
import MainMenu from '@/Shared/MainMenu'
|
||||||
import {useToast} from 'vue-toastification'
|
import { useToast } from 'vue-toastification'
|
||||||
import {watch} from 'vue'
|
import { defineProps, watch } from 'vue'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
name: 'AppLayout',
|
flash: Object,
|
||||||
components: {
|
auth: Object,
|
||||||
MainMenu,
|
years: Object,
|
||||||
},
|
})
|
||||||
props: {
|
|
||||||
flash: {
|
|
||||||
type: Object,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const toast = useToast()
|
|
||||||
|
|
||||||
watch(() => props.flash, flash => {
|
const toast = useToast()
|
||||||
|
|
||||||
|
watch(() => props.flash, flash => {
|
||||||
if (flash.success) {
|
if (flash.success) {
|
||||||
toast.success(flash.success)
|
toast.success(flash.success)
|
||||||
}
|
}
|
||||||
@ -36,11 +33,5 @@ export default {
|
|||||||
if (flash.error) {
|
if (flash.error) {
|
||||||
toast.error(flash.error)
|
toast.error(flash.error)
|
||||||
}
|
}
|
||||||
}, {immediate:true})
|
}, { immediate:true })
|
||||||
|
|
||||||
return {
|
|
||||||
toast,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -3,9 +3,3 @@
|
|||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'GuestLayout',
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
@ -91,7 +91,6 @@
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
</TransitionRoot>
|
</TransitionRoot>
|
||||||
|
|
||||||
<!-- Static sidebar for desktop -->
|
|
||||||
<div class="hidden lg:flex lg:w-64 lg:flex-col lg:fixed lg:inset-y-0">
|
<div class="hidden lg:flex lg:w-64 lg:flex-col lg:fixed lg:inset-y-0">
|
||||||
<div class="flex flex-col flex-grow bg-blumilk-700 pt-5 pb-4 overflow-y-auto">
|
<div class="flex flex-col flex-grow bg-blumilk-700 pt-5 pb-4 overflow-y-auto">
|
||||||
<div class="flex items-center flex-shrink-0 px-4">
|
<div class="flex items-center flex-shrink-0 px-4">
|
||||||
@ -202,30 +201,23 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ml-4 flex items-center md:ml-6">
|
<div class="ml-4 flex items-center md:ml-6">
|
||||||
<!-- Profile dropdown -->
|
|
||||||
<Menu
|
<Menu
|
||||||
as="div"
|
as="div"
|
||||||
class="ml-3 relative"
|
class="ml-3 relative"
|
||||||
>
|
>
|
||||||
<div>
|
|
||||||
<MenuButton
|
<MenuButton
|
||||||
class="max-w-xs bg-white rounded-full flex items-center text-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blumilk-500 lg:p-2 lg:rounded-md lg:hover:bg-gray-50"
|
class="max-w-xs bg-white rounded-full flex items-center text-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blumilk-500 lg:p-2 lg:rounded-md lg:hover:bg-gray-50"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
class="h-8 w-8 rounded-full"
|
class="h-8 w-8 rounded-full"
|
||||||
:src="auth.user.avatar"
|
:src="auth.user.avatar"
|
||||||
alt="Avatar"
|
|
||||||
>
|
>
|
||||||
<span class="hidden ml-3 text-gray-700 text-sm font-medium lg:block">
|
<span class="hidden ml-3 text-gray-700 text-sm font-medium lg:block">
|
||||||
<span class="sr-only">Open user menu for </span>
|
<span class="sr-only">Open user menu for </span>
|
||||||
{{ auth.user.name }}
|
{{ auth.user.name }}
|
||||||
</span>
|
</span>
|
||||||
<ChevronDownIcon
|
<ChevronDownIcon class="hidden flex-shrink-0 ml-1 h-5 w-5 text-gray-400 lg:block" />
|
||||||
class="hidden flex-shrink-0 ml-1 h-5 w-5 text-gray-400 lg:block"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
</MenuButton>
|
</MenuButton>
|
||||||
</div>
|
|
||||||
<transition
|
<transition
|
||||||
enter-active-class="transition ease-out duration-100"
|
enter-active-class="transition ease-out duration-100"
|
||||||
enter-from-class="transform opacity-0 scale-95"
|
enter-from-class="transform opacity-0 scale-95"
|
||||||
@ -237,18 +229,14 @@
|
|||||||
<MenuItems
|
<MenuItems
|
||||||
class="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none"
|
class="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none"
|
||||||
>
|
>
|
||||||
<MenuItem
|
<MenuItem v-slot="{ active }">
|
||||||
v-for="item in userNavigation"
|
|
||||||
:key="item.name"
|
|
||||||
v-slot="{ active }"
|
|
||||||
>
|
|
||||||
<InertiaLink
|
<InertiaLink
|
||||||
:href="item.href"
|
href="/logout"
|
||||||
:method="item.method"
|
method="POST"
|
||||||
:as="item.as"
|
as="button"
|
||||||
:class="[active ? 'bg-gray-100' : '', 'block w-full text-left px-4 py-2 text-sm text-gray-700']"
|
:class="[active ? 'bg-gray-100' : '', 'block w-full text-left px-4 py-2 text-sm text-gray-700']"
|
||||||
>
|
>
|
||||||
{{ item.name }}
|
Wyloguj się
|
||||||
</InertiaLink>
|
</InertiaLink>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</MenuItems>
|
</MenuItems>
|
||||||
@ -260,8 +248,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import {ref} from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogOverlay,
|
DialogOverlay,
|
||||||
@ -273,7 +261,6 @@ import {
|
|||||||
TransitionRoot,
|
TransitionRoot,
|
||||||
} from '@headlessui/vue'
|
} from '@headlessui/vue'
|
||||||
import {
|
import {
|
||||||
BellIcon,
|
|
||||||
HomeIcon,
|
HomeIcon,
|
||||||
CollectionIcon,
|
CollectionIcon,
|
||||||
MenuAlt1Icon,
|
MenuAlt1Icon,
|
||||||
@ -283,71 +270,58 @@ import {
|
|||||||
StarIcon,
|
StarIcon,
|
||||||
CalendarIcon, DocumentTextIcon,
|
CalendarIcon, DocumentTextIcon,
|
||||||
} from '@heroicons/vue/outline'
|
} from '@heroicons/vue/outline'
|
||||||
import {
|
import { CheckIcon, ChevronDownIcon } from '@heroicons/vue/solid'
|
||||||
CashIcon,
|
|
||||||
CheckCircleIcon,
|
|
||||||
CheckIcon,
|
|
||||||
ChevronDownIcon,
|
|
||||||
ChevronRightIcon,
|
|
||||||
OfficeBuildingIcon,
|
|
||||||
SearchIcon,
|
|
||||||
} from '@heroicons/vue/solid'
|
|
||||||
import {computed} from 'vue'
|
|
||||||
import {usePage} from '@inertiajs/inertia-vue3'
|
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
components: {
|
auth: Object,
|
||||||
Dialog,
|
years: Object,
|
||||||
DialogOverlay,
|
})
|
||||||
Menu,
|
|
||||||
MenuButton,
|
|
||||||
MenuItem,
|
|
||||||
MenuItems,
|
|
||||||
TransitionChild,
|
|
||||||
TransitionRoot,
|
|
||||||
BellIcon,
|
|
||||||
CashIcon,
|
|
||||||
CheckCircleIcon,
|
|
||||||
ChevronDownIcon,
|
|
||||||
ChevronRightIcon,
|
|
||||||
MenuAlt1Icon,
|
|
||||||
OfficeBuildingIcon,
|
|
||||||
SearchIcon,
|
|
||||||
XIcon,
|
|
||||||
StarIcon,
|
|
||||||
HomeIcon,
|
|
||||||
CheckIcon,
|
|
||||||
UserGroupIcon,
|
|
||||||
SunIcon,
|
|
||||||
CalendarIcon,
|
|
||||||
},
|
|
||||||
setup() {
|
|
||||||
const sidebarOpen = ref(false)
|
|
||||||
|
|
||||||
const auth = computed(() => usePage().props.value.auth)
|
const sidebarOpen = ref(false)
|
||||||
const years = computed(() => usePage().props.value.years)
|
|
||||||
|
|
||||||
const navigation = computed(() =>
|
const navigation = computed(() =>
|
||||||
[
|
[
|
||||||
{name: 'Moje wnioski', href: '/vacation-requests/me', component: 'VacationRequest/Index' , icon: DocumentTextIcon, can: true},
|
{
|
||||||
{name: 'Wnioski urlopowe', href: '/vacation-requests', component: 'VacationRequest/IndexForApprovers', icon: CollectionIcon, can: auth.value.can.listAllVacationRequests},
|
name: 'Moje wnioski',
|
||||||
{name: 'Kalendarz urlopów', href: '/vacation-calendar', component: 'Calendar', icon: CalendarIcon, can: true},
|
href: '/vacation-requests/me',
|
||||||
{name: 'Dni wolne', href: '/holidays', component: 'Holidays/Index', icon: StarIcon, can: true},
|
component: 'VacationRequest/Index',
|
||||||
{name: 'Limity urlopów', href: '/vacation-limits', component: 'VacationLimits', icon: SunIcon, can: auth.value.can.manageVacationLimits},
|
icon: DocumentTextIcon,
|
||||||
{name: 'Użytkownicy', href: '/users', component: 'Users/Index', icon: UserGroupIcon, can: auth.value.can.manageUsers},
|
can: true,
|
||||||
].filter(item => item.can))
|
|
||||||
|
|
||||||
const userNavigation = [
|
|
||||||
{name: 'Wyloguj się', href: '/logout', method: 'post', as: 'button'},
|
|
||||||
]
|
|
||||||
|
|
||||||
return {
|
|
||||||
auth,
|
|
||||||
years,
|
|
||||||
navigation,
|
|
||||||
userNavigation,
|
|
||||||
sidebarOpen,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
{
|
||||||
|
name: 'Wnioski urlopowe',
|
||||||
|
href: '/vacation-requests',
|
||||||
|
component: 'VacationRequest/IndexForApprovers',
|
||||||
|
icon: CollectionIcon,
|
||||||
|
can: props.auth.can.listAllVacationRequests,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Kalendarz urlopów',
|
||||||
|
href: '/vacation-calendar',
|
||||||
|
component: 'Calendar',
|
||||||
|
icon: CalendarIcon,
|
||||||
|
can: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Dni wolne',
|
||||||
|
href: '/holidays',
|
||||||
|
component: 'Holidays/Index',
|
||||||
|
icon: StarIcon,
|
||||||
|
can: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Limity urlopów',
|
||||||
|
href: '/vacation-limits',
|
||||||
|
component: 'VacationLimits',
|
||||||
|
icon: SunIcon,
|
||||||
|
can: props.auth.can.manageVacationLimits,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Użytkownicy',
|
||||||
|
href: '/users',
|
||||||
|
component: 'Users/Index',
|
||||||
|
icon: UserGroupIcon,
|
||||||
|
can: props.auth.can.manageUsers,
|
||||||
|
},
|
||||||
|
].filter(item => item.can))
|
||||||
</script>
|
</script>
|
||||||
|
61
resources/js/Shared/Pagination.vue
Normal file
61
resources/js/Shared/Pagination.vue
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-if="pagination.last_page !== 1"
|
||||||
|
class="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6 rounded-b-lg"
|
||||||
|
>
|
||||||
|
<div class="flex-1 flex justify-between sm:hidden">
|
||||||
|
<Component
|
||||||
|
:is="prevLink ? 'InertiaLink': 'span'"
|
||||||
|
:href="prevLink"
|
||||||
|
class="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"
|
||||||
|
>
|
||||||
|
Poprzednia
|
||||||
|
</Component>
|
||||||
|
<Component
|
||||||
|
:is="nextLink ? 'InertiaLink': 'span'"
|
||||||
|
:href="nextLink"
|
||||||
|
class="ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50"
|
||||||
|
>
|
||||||
|
Następna
|
||||||
|
</Component>
|
||||||
|
</div>
|
||||||
|
<div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
|
||||||
|
<div class="text-sm text-gray-700">
|
||||||
|
Wyświetlanie
|
||||||
|
<span class="font-medium">{{ pagination.from }}</span>
|
||||||
|
od
|
||||||
|
<span class="font-medium">{{ pagination.to }}</span>
|
||||||
|
do
|
||||||
|
<span class="font-medium">{{ pagination.total }}</span>
|
||||||
|
wyników
|
||||||
|
</div>
|
||||||
|
<nav class="relative z-0 inline-flex space-x-1">
|
||||||
|
<template
|
||||||
|
v-for="(link, index) in pagination.links"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
<Component
|
||||||
|
:is="link.url ? 'InertiaLink' : 'span'"
|
||||||
|
:href="link.url"
|
||||||
|
:preserve-scroll="true"
|
||||||
|
class="relative inline-flex items-center px-4 py-2 border rounded-md text-sm font-medium"
|
||||||
|
:class="{ 'z-10 bg-blumilk-25 border-blumilk-500 text-blumilk-600': link.active, 'bg-white border-gray-300 text-gray-500': !link.active, 'hover:bg-blumilk-25': link.url, 'border-none': !link.url}"
|
||||||
|
v-text="link.label"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
pagination: Object,
|
||||||
|
})
|
||||||
|
|
||||||
|
const prevLink = computed(() => props.pagination.links.at(0)?.url)
|
||||||
|
const nextLink = computed(() => props.pagination.links.at(-1)?.url)
|
||||||
|
|
||||||
|
</script>
|
@ -2,36 +2,21 @@
|
|||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<component
|
<component
|
||||||
:is="statusInfo.solid.icon"
|
:is="statusInfo.solid.icon"
|
||||||
:class="[statusInfo.solid.color ,'w-5 h-5 mr-1']"
|
:class="[statusInfo.solid.color, 'w-5 h-5 mr-1']"
|
||||||
/>
|
/>
|
||||||
<span>{{ statusInfo.text }}</span>
|
<span>{{ statusInfo.text }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import {computed} from 'vue'
|
import { computed } from 'vue'
|
||||||
import {useStatusInfo} from '@/Composables/statusInfo'
|
import { useStatusInfo } from '@/Composables/statusInfo'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
name: 'VacationRequestStatus',
|
status: String,
|
||||||
props: {
|
})
|
||||||
status: {
|
|
||||||
type: String,
|
|
||||||
default: () => null,
|
|
||||||
},
|
|
||||||
last: {
|
|
||||||
type: Boolean,
|
|
||||||
default: () => false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const { findStatus } = useStatusInfo()
|
|
||||||
|
|
||||||
const statusInfo = computed(() => findStatus(props.status))
|
const { findStatus } = useStatusInfo()
|
||||||
|
|
||||||
return {
|
const statusInfo = computed(() => findStatus(props.status))
|
||||||
statusInfo,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -6,17 +6,13 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import { use } from 'echarts/core'
|
import { use } from 'echarts/core'
|
||||||
import { CanvasRenderer } from 'echarts/renderers'
|
import { CanvasRenderer } from 'echarts/renderers'
|
||||||
import { PieChart } from 'echarts/charts'
|
import { PieChart } from 'echarts/charts'
|
||||||
import {
|
import { TitleComponent, TooltipComponent, LegendComponent } from 'echarts/components'
|
||||||
TitleComponent,
|
|
||||||
TooltipComponent,
|
|
||||||
LegendComponent,
|
|
||||||
} from 'echarts/components'
|
|
||||||
import VChart from 'vue-echarts'
|
import VChart from 'vue-echarts'
|
||||||
import {computed} from 'vue'
|
import { computed } from 'vue'
|
||||||
|
|
||||||
use([
|
use([
|
||||||
CanvasRenderer,
|
CanvasRenderer,
|
||||||
@ -26,12 +22,7 @@ use([
|
|||||||
LegendComponent,
|
LegendComponent,
|
||||||
])
|
])
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
name: 'VacationChart',
|
|
||||||
components: {
|
|
||||||
VChart,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
stats: {
|
stats: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({
|
default: () => ({
|
||||||
@ -40,9 +31,9 @@ export default {
|
|||||||
remaining: 0,
|
remaining: 0,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
setup(props) {
|
|
||||||
const option = computed(() => ({
|
const option = computed(() => ({
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'item',
|
trigger: 'item',
|
||||||
formatter: '{a} <br/>{b} : {c} ({d}%)',
|
formatter: '{a} <br/>{b} : {c} ({d}%)',
|
||||||
@ -85,9 +76,5 @@ export default {
|
|||||||
radius: ['30%', '70%'],
|
radius: ['30%', '70%'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return { option }
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -9,30 +9,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import {computed} from 'vue'
|
import { computed } from 'vue'
|
||||||
import {useVacationTypeInfo} from '@/Composables/vacationTypeInfo'
|
import useVacationTypeInfo from '@/Composables/vacationTypeInfo'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
name: 'VacationType',
|
|
||||||
props: {
|
|
||||||
type: {
|
|
||||||
type: String,
|
type: String,
|
||||||
default: () => null,
|
})
|
||||||
},
|
|
||||||
last: {
|
|
||||||
type: Boolean,
|
|
||||||
default: () => false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const { findType } = useVacationTypeInfo()
|
|
||||||
|
|
||||||
const vacationTypeInfo = computed(() => findType(props.type))
|
const { findType } = useVacationTypeInfo()
|
||||||
|
|
||||||
return {
|
const vacationTypeInfo = computed(() => findType(props.type))
|
||||||
vacationTypeInfo,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -15,34 +15,16 @@
|
|||||||
</Popper>
|
</Popper>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import {computed} from 'vue'
|
import { computed } from 'vue'
|
||||||
import {useVacationTypeInfo} from '@/Composables/vacationTypeInfo'
|
import useVacationTypeInfo from '@/Composables/vacationTypeInfo'
|
||||||
import Popper from 'vue3-popper'
|
import Popper from 'vue3-popper'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
name: 'VacationTypeCalendarIcon',
|
|
||||||
components: {
|
|
||||||
Popper,
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
type: {
|
|
||||||
type: String,
|
type: String,
|
||||||
default: () => null,
|
})
|
||||||
},
|
|
||||||
last: {
|
|
||||||
type: Boolean,
|
|
||||||
default: () => false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const { findType } = useVacationTypeInfo()
|
|
||||||
|
|
||||||
const typeInfo = computed(() => findType(props.type))
|
const { findType } = useVacationTypeInfo()
|
||||||
|
|
||||||
return {
|
const typeInfo = computed(() => findType(props.type))
|
||||||
typeInfo,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
42
resources/js/Shared/Widgets/AbsenceList.vue
Normal file
42
resources/js/Shared/Widgets/AbsenceList.vue
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<template>
|
||||||
|
<section 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>
|
||||||
|
<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"
|
||||||
|
:key="absence.user.id"
|
||||||
|
class="py-4 flex"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="h-10 w-10 rounded-full"
|
||||||
|
:src="absence.user.avatar"
|
||||||
|
>
|
||||||
|
<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.length">
|
||||||
|
<p class="py-2">
|
||||||
|
Brak danych
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
defineProps({
|
||||||
|
absences: Object,
|
||||||
|
})
|
||||||
|
</script>
|
76
resources/js/Shared/Widgets/PendingVacationRequests.vue
Normal file
76
resources/js/Shared/Widgets/PendingVacationRequests.vue
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<template>
|
||||||
|
<section class="bg-white shadow-md">
|
||||||
|
<div class="p-4 sm:px-6">
|
||||||
|
<h2 class="text-lg leading-6 font-medium text-gray-900">
|
||||||
|
Wnioski oczekujące na akcje
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div class="border-t border-gray-200 pb-5 px-4 sm:px-6">
|
||||||
|
<div class="flow-root mt-6">
|
||||||
|
<ul class="-my-5 divide-y divide-gray-200">
|
||||||
|
<li
|
||||||
|
v-for="request in requests"
|
||||||
|
:key="request.id"
|
||||||
|
class="py-5"
|
||||||
|
>
|
||||||
|
<div class="relative focus-within:ring-2 focus-within:ring-blumilk-500">
|
||||||
|
<h3 class="text-sm font-semibold text-blumilk-600 hover:text-blumilk-500">
|
||||||
|
<InertiaLink
|
||||||
|
:href="`/vacation-requests/${request.id}`"
|
||||||
|
class="hover:underline focus:outline-none"
|
||||||
|
>
|
||||||
|
<span class="absolute inset-0" />
|
||||||
|
Wniosek o {{ findType(request.type).text.toLowerCase() }}
|
||||||
|
[{{ request.name }}]
|
||||||
|
</InertiaLink>
|
||||||
|
</h3>
|
||||||
|
<p class="mt-1 text-sm text-gray-600">
|
||||||
|
{{ request.from }} - {{ request.to }}
|
||||||
|
</p>
|
||||||
|
<div class="mt-3 text-sm text-gray-600">
|
||||||
|
<div class="flex">
|
||||||
|
<img
|
||||||
|
class="h-10 w-10 rounded-full"
|
||||||
|
:src="request.user.avatar"
|
||||||
|
>
|
||||||
|
<div class="ml-3">
|
||||||
|
<p class="text-sm font-medium text-gray-900">
|
||||||
|
{{ request.user.name }}
|
||||||
|
</p>
|
||||||
|
<p class="text-sm text-gray-500">
|
||||||
|
{{ request.user.email }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li v-if="! requests.length">
|
||||||
|
<p class="py-2">
|
||||||
|
Brak danych
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="mt-6">
|
||||||
|
<InertiaLink
|
||||||
|
href="/vacation-requests"
|
||||||
|
:data="{status: 'waiting_for_action'}"
|
||||||
|
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 focus:outline-none focus:ring-2 focus:ring-blumilk-500"
|
||||||
|
>
|
||||||
|
Zobacz wszystkie
|
||||||
|
</InertiaLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import useVacationTypeInfo from '@/Composables/vacationTypeInfo'
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
requests: Object,
|
||||||
|
})
|
||||||
|
|
||||||
|
const { findType } = useVacationTypeInfo()
|
||||||
|
</script>
|
46
resources/js/Shared/Widgets/UpcomingHolidays.vue
Normal file
46
resources/js/Shared/Widgets/UpcomingHolidays.vue
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<template>
|
||||||
|
<section class="bg-white shadow-md">
|
||||||
|
<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"
|
||||||
|
: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>
|
||||||
|
<li v-if="! holidays.length">
|
||||||
|
<p class="py-2">
|
||||||
|
Brak danych
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<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 focus:outline-none focus:ring-2 focus:ring-blumilk-500"
|
||||||
|
>
|
||||||
|
Zobacz wszystkie
|
||||||
|
</InertiaLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
defineProps({
|
||||||
|
holidays: Object,
|
||||||
|
})
|
||||||
|
</script>
|
62
resources/js/Shared/Widgets/UserVacationRequests.vue
Normal file
62
resources/js/Shared/Widgets/UserVacationRequests.vue
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<template>
|
||||||
|
<section 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 class="-my-5 divide-y divide-gray-200">
|
||||||
|
<li
|
||||||
|
v-for="request in requests"
|
||||||
|
:key="request.id"
|
||||||
|
class="py-5"
|
||||||
|
>
|
||||||
|
<div class="relative focus-within:ring-2 focus-within:ring-blumilk-500">
|
||||||
|
<h3 class="text-sm font-semibold text-blumilk-600 hover:text-blumilk-500">
|
||||||
|
<InertiaLink
|
||||||
|
:href="`/vacation-requests/${request.id}`"
|
||||||
|
class="hover:underline focus:outline-none"
|
||||||
|
>
|
||||||
|
<span class="absolute inset-0" />
|
||||||
|
Wniosek o {{ findType(request.type).text.toLowerCase() }}
|
||||||
|
[{{ request.name }}]
|
||||||
|
</InertiaLink>
|
||||||
|
</h3>
|
||||||
|
<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>
|
||||||
|
<li v-if="! requests.length">
|
||||||
|
<p class="py-2">
|
||||||
|
Brak danych
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="mt-6">
|
||||||
|
<InertiaLink
|
||||||
|
href="/vacation-requests/me"
|
||||||
|
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 focus:outline-none focus:ring-2 focus:ring-blumilk-500"
|
||||||
|
>
|
||||||
|
Zobacz wszystkie
|
||||||
|
</InertiaLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import useVacationTypeInfo from '@/Composables/vacationTypeInfo'
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
requests: Object,
|
||||||
|
})
|
||||||
|
|
||||||
|
const { findType } = useVacationTypeInfo()
|
||||||
|
</script>
|
74
resources/js/Shared/Widgets/VacationStats.vue
Normal file
74
resources/js/Shared/Widgets/VacationStats.vue
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<template>
|
||||||
|
<section 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>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import VacationChart from '@/Shared/VacationChart'
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
stats: Object,
|
||||||
|
})
|
||||||
|
</script>
|
35
resources/js/Shared/Widgets/Welcome.vue
Normal file
35
resources/js/Shared/Widgets/Welcome.vue
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<template>
|
||||||
|
<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">
|
||||||
|
<div class="flex-shrink-0">
|
||||||
|
<img
|
||||||
|
class="mx-auto h-20 w-20 rounded-full"
|
||||||
|
:src="user.avatar"
|
||||||
|
>
|
||||||
|
</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">
|
||||||
|
Cześć,
|
||||||
|
</p>
|
||||||
|
<p class="text-xl font-bold text-gray-900 sm:text-2xl">
|
||||||
|
{{ user.name }}
|
||||||
|
</p>
|
||||||
|
<p class="text-sm font-medium text-gray-600">
|
||||||
|
{{ user.role }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
defineProps({
|
||||||
|
user: Object,
|
||||||
|
})
|
||||||
|
</script>
|
@ -1,6 +1,6 @@
|
|||||||
import {createApp, h} from 'vue'
|
import { createApp, h } from 'vue'
|
||||||
import {createInertiaApp, Head, Link} from '@inertiajs/inertia-vue3'
|
import { createInertiaApp, Head, Link } from '@inertiajs/inertia-vue3'
|
||||||
import {InertiaProgress} from '@inertiajs/progress'
|
import { InertiaProgress } from '@inertiajs/progress'
|
||||||
import AppLayout from '@/Shared/Layout/AppLayout'
|
import AppLayout from '@/Shared/Layout/AppLayout'
|
||||||
import Flatpickr from 'flatpickr'
|
import Flatpickr from 'flatpickr'
|
||||||
import { Polish } from 'flatpickr/dist/l10n/pl.js'
|
import { Polish } from 'flatpickr/dist/l10n/pl.js'
|
||||||
@ -14,8 +14,8 @@ createInertiaApp({
|
|||||||
|
|
||||||
return page
|
return page
|
||||||
},
|
},
|
||||||
setup({el, App, props, plugin}) {
|
setup({ el, App, props, plugin }) {
|
||||||
createApp({render: () => h(App, props)})
|
createApp({ render: () => h(App, props) })
|
||||||
.use(plugin)
|
.use(plugin)
|
||||||
.use(Toast, {
|
.use(Toast, {
|
||||||
position: 'bottom-right',
|
position: 'bottom-right',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user