#157 - more interactive calendar #162
| @@ -44,6 +44,7 @@ class VacationCalendarController extends Controller | ||||
|             "users" => SimpleUserResource::collection($users), | ||||
|             "can" => [ | ||||
|                 "generateTimesheet" => $request->user()->can("generateTimesheet"), | ||||
|                 "createOnBehalfOfEmployee" => $request->user()->can("createOnBehalfOfEmployee"), | ||||
|             ], | ||||
|         ]); | ||||
|     } | ||||
|   | ||||
| @@ -180,6 +180,8 @@ class VacationRequestController extends Controller | ||||
|                 "createOnBehalfOfEmployee" => $request->user()->can("createOnBehalfOfEmployee", VacationRequest::class), | ||||
|                 "skipFlow" => $request->user()->can("skipFlow", VacationRequest::class), | ||||
|             ], | ||||
|             "vacationUserId" => (int)$request->get("user"), | ||||
|             "vacationFromDate" => $request->get("from_date"), | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -9,6 +9,7 @@ import CalendarCheckIcon from 'vue-material-design-icons/CalendarCheck.vue' | ||||
| import MedicalBagIcon from 'vue-material-design-icons/MedicalBag.vue' | ||||
| import CalendarRemoveIcon from 'vue-material-design-icons/CalendarRemove.vue' | ||||
| import HomeCityIcon from 'vue-material-design-icons/HomeCity.vue' | ||||
| import PlusIcon from 'vue-material-design-icons/Plus.vue' | ||||
|  | ||||
| const types = [ | ||||
|   { | ||||
| @@ -88,6 +89,13 @@ const types = [ | ||||
|     color: 'text-lime-500', | ||||
|     border: 'border-lime-500', | ||||
|   }, | ||||
|   { | ||||
|     text: 'Złóż wniosek', | ||||
|     value: 'create', | ||||
|     icon: PlusIcon, | ||||
|     color: 'text-blumilk-700', | ||||
|     border: 'border-lime-500', | ||||
|   }, | ||||
| ] | ||||
|  | ||||
| export default function useVacationTypeInfo() { | ||||
|   | ||||
| @@ -39,7 +39,7 @@ | ||||
|                 offset-distance="0" | ||||
|               > | ||||
|                 <div :class="[day.isPendingVacation && 'mx-0.5']"> | ||||
|                   <button :class="[day.isPendingVacation && `border-dashed`, `${getVacationBorder(day)} isolate bg-white w-full hover:bg-blumilk-25 border-b-4 py-1.5 font-medium focus:outline-blumilk-500`]"> | ||||
|                   <button :class="[day.isPendingVacation && `border-dashed`, `${getVacationBorder(day)} isolate bg-white w-full hover:bg-blumilk-25 border-b-4 py-1.5 font-medium focus:outline-blumilk-500 cursor-default`]"> | ||||
|                     <time | ||||
|                       :datetime="day.date.toISODate()" | ||||
|                       :class="[ day.isToday && 'bg-blumilk-500 font-semibold text-white rounded-full', 'mx-auto flex h-7 w-7 p-4 items-center justify-center']" | ||||
| @@ -58,7 +58,7 @@ | ||||
|                 hover | ||||
|                 offset-distance="0" | ||||
|               > | ||||
|                 <button class="py-1.5 w-full font-medium bg-white hover:bg-blumilk-25 border-b-4 border-transparent focus:outline-blumilk-500"> | ||||
|                 <button class="py-1.5 w-full font-medium bg-white hover:bg-blumilk-25 border-b-4 border-transparent focus:outline-blumilk-500 cursor-default"> | ||||
|                   <time | ||||
|                     :datetime="day.date.toISODate()" | ||||
|                     :class="[ day.isToday && 'bg-blumilk-500 font-semibold text-white rounded-full', 'text-red-700 font-bold mx-auto flex h-7 w-7 p-4 items-center justify-center']" | ||||
| @@ -73,16 +73,29 @@ | ||||
|                 </template> | ||||
|               </Popper> | ||||
|               <button | ||||
|                 v-else | ||||
|                 class="py-1.5 w-full font-medium bg-white hover:bg-blumilk-25 border-b-4 border-transparent focus:outline-blumilk-500" | ||||
|                 v-else-if="day.isWeekend" | ||||
|                 class="py-1.5 w-full font-medium bg-white hover:bg-blumilk-25 border-b-4 border-transparent focus:outline-blumilk-500 hover:bg-transparent cursor-default" | ||||
|               > | ||||
|                 <time | ||||
|                   :datetime="day.date.toISODate()" | ||||
|                   :class="[ day.isToday && 'bg-blumilk-500 font-semibold text-white rounded-full', day.isWeekend && 'text-red-700 font-bold', 'mx-auto flex h-7 w-7 p-4 items-center justify-center']" | ||||
|                   class="text-red-700 font-bold mx-auto flex h-7 w-7 p-4 items-center justify-center" | ||||
|                 > | ||||
|                   {{ day.date.day }} | ||||
|                 </time> | ||||
|               </button> | ||||
|               <InertiaLink | ||||
|                 v-else | ||||
|                 href="/vacation/requests/create" | ||||
|                 :data="{ 'from_date': day.date.toISODate() }" | ||||
|                 class="py-1.5 w-full font-medium bg-white hover:bg-blumilk-25 border-b-4 border-transparent focus:outline-blumilk-500" | ||||
|               > | ||||
|                 <time | ||||
|                   :datetime="day.date.toISODate()" | ||||
|                   :class="[ day.isToday && 'bg-blumilk-500 font-semibold text-white rounded-full', 'mx-auto flex h-7 w-7 p-4 items-center justify-center']" | ||||
|                 > | ||||
|                   {{ day.date.day }} | ||||
|                 </time> | ||||
|               </InertiaLink> | ||||
|             </template> | ||||
|             <div | ||||
|               v-else | ||||
| @@ -112,6 +125,7 @@ const props = defineProps({ | ||||
|   holidays: Object, | ||||
|   vacations: Object, | ||||
|   pendingVacations: Object, | ||||
|   overButtonDay: String, | ||||
| }) | ||||
|  | ||||
| const { findType } = useVacationTypeInfo() | ||||
|   | ||||
| @@ -101,6 +101,8 @@ | ||||
|               :key="day.dayOfMonth" | ||||
|               class="border border-gray-300" | ||||
|               :class="{ 'bg-blumilk-25': day.isToday, 'bg-red-100': day.isWeekend || day.isHoliday }" | ||||
|               @mouseover="setActiveDay(user.id + '+' + day.date)" | ||||
|               @mouseleave="unsetActiveDay" | ||||
|             > | ||||
|               <div | ||||
|                 v-if="day.vacations.includes(user.id)" | ||||
| @@ -108,6 +110,20 @@ | ||||
|               > | ||||
|                 <VacationTypeCalendarIcon :type="day.vacationTypes[user.id]" /> | ||||
|               </div> | ||||
|               <template | ||||
|                 v-else-if="isActiveDay(user.id + '+' + day.date) && !day.isWeekend && !day.isHoliday && (auth.user.id === user.id || can.createOnBehalfOfEmployee)" | ||||
|               > | ||||
|                 <InertiaLink | ||||
|                   href="/vacation/requests/create" | ||||
|                   :data="linkParameters(user, day)" | ||||
|                 > | ||||
|                   <div class="flex justify-center items-center"> | ||||
|                     <VacationTypeCalendarIcon | ||||
|                       type="create" | ||||
|                     /> | ||||
|                   </div> | ||||
|                 </InertiaLink> | ||||
|               </template> | ||||
|             </td> | ||||
|           </tr> | ||||
|         </tbody> | ||||
| @@ -118,7 +134,7 @@ | ||||
|  | ||||
| <script setup> | ||||
| import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/vue/solid' | ||||
| import { computed } from 'vue' | ||||
| import { computed, ref } from 'vue' | ||||
| import { useMonthInfo } from '@/Composables/monthInfo' | ||||
| import VacationTypeCalendarIcon from '@/Shared/VacationTypeCalendarIcon' | ||||
|  | ||||
| @@ -132,6 +148,8 @@ const props = defineProps({ | ||||
|   can: Object, | ||||
| }) | ||||
|  | ||||
| let activeElement = ref(undefined) | ||||
|  | ||||
| const { getMonths, findMonth } = useMonthInfo() | ||||
|  | ||||
| const months = getMonths() | ||||
| @@ -140,4 +158,21 @@ const currentMonth = computed(() => findMonth(props.current)) | ||||
| const selectedMonth = computed(() => findMonth(props.selected)) | ||||
| const previousMonth = computed(() => months[months.indexOf(selectedMonth.value) - 1]) | ||||
| const nextMonth = computed(() => months[months.indexOf(selectedMonth.value) + 1]) | ||||
|  | ||||
| function isActiveDay(key) { | ||||
|   return activeElement.value === key | ||||
| } | ||||
|  | ||||
| function setActiveDay(key) { | ||||
|   if(activeElement.value === undefined) | ||||
|     activeElement.value = key | ||||
| } | ||||
|  | ||||
| function unsetActiveDay() { | ||||
|   activeElement.value = undefined | ||||
| } | ||||
|  | ||||
| function linkParameters(user, day) { | ||||
|   return props.can.createOnBehalfOfEmployee ? { user: user.id, from_date: day.date } : { from_date: day.date } | ||||
| } | ||||
| </script> | ||||
|   | ||||
| @@ -343,19 +343,23 @@ const props = defineProps({ | ||||
|   users: Object, | ||||
|   holidays: Object, | ||||
|   can: Object, | ||||
|   vacationUserId: [Number, null], | ||||
|   vacationFromDate: [String, null], | ||||
| }) | ||||
|  | ||||
| const form = useForm({ | ||||
|   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 === (checkUserId(props.vacationUserId) ?? props.auth.user.id)) ?? props.users.data[0] | ||||
|     : props.auth.user, | ||||
|   from: null, | ||||
|   to: null, | ||||
|   from: props.vacationFromDate, | ||||
|   to: props.vacationFromDate, | ||||
|   vacationType: null, | ||||
|   comment: null, | ||||
|   flowSkipped: false, | ||||
| }) | ||||
|  | ||||
| refreshEstimatedDays(form.from, form.to) | ||||
|  | ||||
| const estimatedDays = ref([]) | ||||
| const vacationTypes = ref([]) | ||||
|  | ||||
| @@ -423,6 +427,10 @@ function resetForm() { | ||||
|   estimatedDays.value = [] | ||||
| } | ||||
|  | ||||
| function checkUserId(userId) { | ||||
|   return userId > 0 ? userId: null | ||||
| } | ||||
|  | ||||
| async function refreshEstimatedDays(from, to) { | ||||
|   if (from && to) { | ||||
|     const res = await axios.post('/api/vacation/calculate-days', { from, to }) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user