#157 - more interactive calendar #162
| @@ -44,6 +44,7 @@ class VacationCalendarController extends Controller | |||||||
|             "users" => SimpleUserResource::collection($users), |             "users" => SimpleUserResource::collection($users), | ||||||
|             "can" => [ |             "can" => [ | ||||||
|                 "generateTimesheet" => $request->user()->can("generateTimesheet"), |                 "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), |                 "createOnBehalfOfEmployee" => $request->user()->can("createOnBehalfOfEmployee", VacationRequest::class), | ||||||
|                 "skipFlow" => $request->user()->can("skipFlow", 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 MedicalBagIcon from 'vue-material-design-icons/MedicalBag.vue' | ||||||
| import CalendarRemoveIcon from 'vue-material-design-icons/CalendarRemove.vue' | import CalendarRemoveIcon from 'vue-material-design-icons/CalendarRemove.vue' | ||||||
| import HomeCityIcon from 'vue-material-design-icons/HomeCity.vue' | import HomeCityIcon from 'vue-material-design-icons/HomeCity.vue' | ||||||
|  | import PlusIcon from 'vue-material-design-icons/Plus.vue' | ||||||
|  |  | ||||||
| const types = [ | const types = [ | ||||||
|   { |   { | ||||||
| @@ -88,6 +89,13 @@ const types = [ | |||||||
|     color: 'text-lime-500', |     color: 'text-lime-500', | ||||||
|     border: 'border-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() { | export default function useVacationTypeInfo() { | ||||||
|   | |||||||
| @@ -39,7 +39,7 @@ | |||||||
|                 offset-distance="0" |                 offset-distance="0" | ||||||
|               > |               > | ||||||
|                 <div :class="[day.isPendingVacation && 'mx-0.5']"> |                 <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 |                     <time | ||||||
|                       :datetime="day.date.toISODate()" |                       :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']" |                       :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 |                 hover | ||||||
|                 offset-distance="0" |                 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 |                   <time | ||||||
|                     :datetime="day.date.toISODate()" |                     :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']" |                     :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> |                 </template> | ||||||
|               </Popper> |               </Popper> | ||||||
|               <button |               <button | ||||||
|                 v-else |                 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" |                 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 |                 <time | ||||||
|                   :datetime="day.date.toISODate()" |                   :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 }} |                   {{ day.date.day }} | ||||||
|                 </time> |                 </time> | ||||||
|               </button> |               </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> |             </template> | ||||||
|             <div |             <div | ||||||
|               v-else |               v-else | ||||||
| @@ -112,6 +125,7 @@ const props = defineProps({ | |||||||
|   holidays: Object, |   holidays: Object, | ||||||
|   vacations: Object, |   vacations: Object, | ||||||
|   pendingVacations: Object, |   pendingVacations: Object, | ||||||
|  |   overButtonDay: String, | ||||||
| }) | }) | ||||||
|  |  | ||||||
| const { findType } = useVacationTypeInfo() | const { findType } = useVacationTypeInfo() | ||||||
|   | |||||||
| @@ -101,6 +101,8 @@ | |||||||
|               :key="day.dayOfMonth" |               :key="day.dayOfMonth" | ||||||
|               class="border border-gray-300" |               class="border border-gray-300" | ||||||
|               :class="{ 'bg-blumilk-25': day.isToday, 'bg-red-100': day.isWeekend || day.isHoliday }" |               :class="{ 'bg-blumilk-25': day.isToday, 'bg-red-100': day.isWeekend || day.isHoliday }" | ||||||
|  |               @mouseover="setActiveDay(user.id + '+' + day.date)" | ||||||
|  |               @mouseleave="unsetActiveDay" | ||||||
|             > |             > | ||||||
|               <div |               <div | ||||||
|                 v-if="day.vacations.includes(user.id)" |                 v-if="day.vacations.includes(user.id)" | ||||||
| @@ -108,6 +110,20 @@ | |||||||
|               > |               > | ||||||
|                 <VacationTypeCalendarIcon :type="day.vacationTypes[user.id]" /> |                 <VacationTypeCalendarIcon :type="day.vacationTypes[user.id]" /> | ||||||
|               </div> |               </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> |             </td> | ||||||
|           </tr> |           </tr> | ||||||
|         </tbody> |         </tbody> | ||||||
| @@ -118,7 +134,7 @@ | |||||||
|  |  | ||||||
| <script setup> | <script setup> | ||||||
| import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/vue/solid' | import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/vue/solid' | ||||||
| import { computed } from 'vue' | import { computed, ref } from 'vue' | ||||||
| import { useMonthInfo } from '@/Composables/monthInfo' | import { useMonthInfo } from '@/Composables/monthInfo' | ||||||
| import VacationTypeCalendarIcon from '@/Shared/VacationTypeCalendarIcon' | import VacationTypeCalendarIcon from '@/Shared/VacationTypeCalendarIcon' | ||||||
|  |  | ||||||
| @@ -132,6 +148,8 @@ const props = defineProps({ | |||||||
|   can: Object, |   can: Object, | ||||||
| }) | }) | ||||||
|  |  | ||||||
|  | let activeElement = ref(undefined) | ||||||
|  |  | ||||||
| const { getMonths, findMonth } = useMonthInfo() | const { getMonths, findMonth } = useMonthInfo() | ||||||
|  |  | ||||||
| const months = getMonths() | const months = getMonths() | ||||||
| @@ -140,4 +158,21 @@ const currentMonth = computed(() => findMonth(props.current)) | |||||||
| const selectedMonth = computed(() => findMonth(props.selected)) | const selectedMonth = computed(() => findMonth(props.selected)) | ||||||
| const previousMonth = computed(() => months[months.indexOf(selectedMonth.value) - 1]) | const previousMonth = computed(() => months[months.indexOf(selectedMonth.value) - 1]) | ||||||
| const nextMonth = 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> | </script> | ||||||
|   | |||||||
| @@ -343,19 +343,23 @@ const props = defineProps({ | |||||||
|   users: Object, |   users: Object, | ||||||
|   holidays: Object, |   holidays: Object, | ||||||
|   can: Object, |   can: Object, | ||||||
|  |   vacationUserId: [Number, null], | ||||||
|  |   vacationFromDate: [String, null], | ||||||
| }) | }) | ||||||
|  |  | ||||||
| const form = useForm({ | 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 === (checkUserId(props.vacationUserId) ?? props.auth.user.id)) ?? props.users.data[0] | ||||||
|     : props.auth.user, |     : props.auth.user, | ||||||
|   from: null, |   from: props.vacationFromDate, | ||||||
|   to: null, |   to: props.vacationFromDate, | ||||||
|   vacationType: null, |   vacationType: null, | ||||||
|   comment: null, |   comment: null, | ||||||
|   flowSkipped: false, |   flowSkipped: false, | ||||||
| }) | }) | ||||||
|  |  | ||||||
|  | refreshEstimatedDays(form.from, form.to) | ||||||
|  |  | ||||||
| const estimatedDays = ref([]) | const estimatedDays = ref([]) | ||||||
| const vacationTypes = ref([]) | const vacationTypes = ref([]) | ||||||
|  |  | ||||||
| @@ -423,6 +427,10 @@ function resetForm() { | |||||||
|   estimatedDays.value = [] |   estimatedDays.value = [] | ||||||
| } | } | ||||||
|  |  | ||||||
|  | function checkUserId(userId) { | ||||||
|  |   return userId > 0 ? userId: null | ||||||
|  | } | ||||||
|  |  | ||||||
| async function refreshEstimatedDays(from, to) { | async function refreshEstimatedDays(from, to) { | ||||||
|   if (from && to) { |   if (from && to) { | ||||||
|     const res = await axios.post('/api/vacation/calculate-days', { from, to }) |     const res = await axios.post('/api/vacation/calculate-days', { from, to }) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user