mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
305 lines
9.7 KiB
Vue
305 lines
9.7 KiB
Vue
<template>
|
|
<div class="row">
|
|
<div class="col-sm">
|
|
<label class="form-label">Lieu des plages de disponibilités créées</label>
|
|
<vue-multiselect
|
|
v-model="pickedLocation"
|
|
:options="locations"
|
|
:label="'name'"
|
|
:track-by="'id'"
|
|
:selectLabel="'Presser \'Entrée\' pour choisir'"
|
|
:selectedLabel="'Choisir'"
|
|
:deselectLabel="'Presser \'Entrée\' pour enlever'"
|
|
:placeholder="'Choisir'"
|
|
></vue-multiselect>
|
|
</div>
|
|
</div>
|
|
<div class="display-options row justify-content-between" style="margin-top: 1rem;">
|
|
<div class="col-sm-9 col-xs-12">
|
|
<div class="input-group mb-3">
|
|
<label class="input-group-text" for="slotDuration">Durée des créneaux</label>
|
|
<select v-model="slotDuration" id="slotDuration" class="form-select">
|
|
<option value="00:05:00">5 minutes</option>
|
|
<option value="00:10:00">10 minutes</option>
|
|
<option value="00:15:00">15 minutes</option>
|
|
<option value="00:30:00">30 minutes</option>
|
|
</select>
|
|
<label class="input-group-text" for="slotMinTime">De</label>
|
|
<select v-model="slotMinTime" id="slotMinTime" class="form-select">
|
|
<option value="00:00:00">0h</option>
|
|
<option value="01:00:00">1h</option>
|
|
<option value="02:00:00">2h</option>
|
|
<option value="03:00:00">3h</option>
|
|
<option value="04:00:00">4h</option>
|
|
<option value="05:00:00">5h</option>
|
|
<option value="06:00:00">6h</option>
|
|
<option value="07:00:00">7h</option>
|
|
<option value="08:00:00">8h</option>
|
|
<option value="09:00:00">9h</option>
|
|
<option value="10:00:00">10h</option>
|
|
<option value="11:00:00">11h</option>
|
|
<option value="12:00:00">12h</option>
|
|
</select>
|
|
<label class="input-group-text" for="slotMaxTime">À</label>
|
|
<select v-model="slotMaxTime" id="slotMaxTime" class="form-select">
|
|
<option value="12:00:00">12h</option>
|
|
<option value="13:00:00">13h</option>
|
|
<option value="14:00:00">14h</option>
|
|
<option value="15:00:00">15h</option>
|
|
<option value="16:00:00">16h</option>
|
|
<option value="17:00:00">17h</option>
|
|
<option value="18:00:00">18h</option>
|
|
<option value="19:00:00">19h</option>
|
|
<option value="20:00:00">20h</option>
|
|
<option value="21:00:00">21h</option>
|
|
<option value="22:00:00">22h</option>
|
|
<option value="23:00:00">23h</option>
|
|
<option value="23:59:59">24h</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="col-sm-3 col-xs-12">
|
|
<div class="float-end">
|
|
<div class="form-check input-group">
|
|
<span class="input-group-text">
|
|
<input id="showHideWE" class="mt-0" type="checkbox" v-model="showWeekends">
|
|
</span>
|
|
<label for="showHideWE" class="form-check-label input-group-text">Week-ends</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<FullCalendar :options="calendarOptions" ref="calendarRef">
|
|
<template v-slot:eventContent="arg: EventApi">
|
|
<span :class="eventClasses(arg.event)">
|
|
<b v-if="arg.event.extendedProps.is === 'remote'">{{ arg.event.title}}</b>
|
|
<b v-else-if="arg.event.extendedProps.is === 'range'">{{ arg.timeText }} - {{ arg.event.extendedProps.locationName }}</b>
|
|
<b v-else-if="arg.event.extendedProps.is === 'local'">{{ arg.event.title}}</b>
|
|
<b v-else >no 'is'</b>
|
|
<a v-if="arg.event.extendedProps.is === 'range'" class="fa fa-fw fa-times delete"
|
|
@click.prevent="onClickDelete(arg.event)">
|
|
</a>
|
|
</span>
|
|
</template>
|
|
</FullCalendar>
|
|
|
|
<div id="copy-widget">
|
|
<div class="container">
|
|
<div class="row align-items-center">
|
|
<div class="col-sm-4 col-xs-12">
|
|
<h6 class="chill-red">{{ $t('copy_range_from_to') }}</h6>
|
|
</div>
|
|
<div class="col-sm-3 col-xs-12">
|
|
<input class="form-control" type="date" v-model="copyFrom" />
|
|
</div>
|
|
<div class="col-sm-1 col-xs-12" style="text-align: center; font-size: x-large;">
|
|
<i class="fa fa-angle-double-right"></i>
|
|
</div>
|
|
<div class="col-sm-3 col-xs-12" >
|
|
<input class="form-control" type="date" v-model="copyTo" />
|
|
</div>
|
|
<div class="col-sm-1">
|
|
<button class="btn btn-action" @click="copyDay">
|
|
{{ $t('copy_range') }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- not directly seen, but include in a modal -->
|
|
<edit-location ref="editLocation"></edit-location>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
|
|
import type {
|
|
CalendarOptions,
|
|
DatesSetArg,
|
|
EventInput,
|
|
EventInstance
|
|
} from '@fullcalendar/vue3';
|
|
import {reactive, computed, ref} from "vue";
|
|
import {useStore} from "vuex";
|
|
import {key} from './store';
|
|
import '@fullcalendar/core/vdom'; // solves problem with Vite
|
|
import FullCalendar from '@fullcalendar/vue3';
|
|
import frLocale from '@fullcalendar/core/locales/fr';
|
|
import interactionPlugin, {DropArg, EventResizeDoneArg} from "@fullcalendar/interaction";
|
|
import timeGridPlugin from "@fullcalendar/timegrid";
|
|
import {EventApi, DateSelectArg, EventDropArg, EventClickArg} from "@fullcalendar/core";
|
|
import {ISOToDate} from "../../../../../ChillMainBundle/Resources/public/chill/js/date";
|
|
import VueMultiselect from "vue-multiselect";
|
|
import {Location} from "../../../../../ChillMainBundle/Resources/public/types";
|
|
import EditLocation from "./Components/EditLocation.vue";
|
|
import {useI18n} from "vue-i18n";
|
|
|
|
const store = useStore(key);
|
|
|
|
const {t} = useI18n();
|
|
|
|
const showWeekends = ref(false);
|
|
const slotDuration = ref('00:05:00');
|
|
const slotMinTime = ref('09:00:00');
|
|
const slotMaxTime = ref('18:00:00');
|
|
const copyFrom = ref<string | null>(null);
|
|
const copyTo = ref<string | null>(null);
|
|
const editLocation = ref<InstanceType<typeof EditLocation> | null>(null)
|
|
|
|
const baseOptions = ref<CalendarOptions>({
|
|
locale: frLocale,
|
|
plugins: [interactionPlugin, timeGridPlugin],
|
|
initialView: 'timeGridWeek',
|
|
initialDate: new Date(),
|
|
scrollTimeReset: false,
|
|
selectable: true,
|
|
// when the dates are changes in the fullcalendar view OR when new events are added
|
|
datesSet: onDatesSet,
|
|
// when a date is selected
|
|
select: onDateSelect,
|
|
// when a event is resized
|
|
eventResize: onEventDropOrResize,
|
|
// when an event is moved
|
|
eventDrop: onEventDropOrResize,
|
|
// when an event si clicked
|
|
eventClick: onEventClick,
|
|
selectMirror: false,
|
|
editable: true,
|
|
headerToolbar: {
|
|
left: 'prev,next today',
|
|
center: 'title',
|
|
right: 'timeGridWeek,timeGridDay'
|
|
},
|
|
});
|
|
|
|
const ranges = computed<EventInput[]>(() => {
|
|
return store.state.calendarRanges.ranges;
|
|
});
|
|
|
|
const locations = computed<Location[]>(() => {
|
|
return store.state.locations.locations;
|
|
});
|
|
|
|
const pickedLocation = computed<Location | null>({
|
|
get(): Location | null {
|
|
return store.state.locations.locationPicked || store.state.locations.currentLocation;
|
|
},
|
|
set(newLocation: Location | null): void {
|
|
store.commit('locations/setLocationPicked', newLocation, {root: true});
|
|
}
|
|
})
|
|
|
|
/**
|
|
* return the show classes for the event
|
|
* @param arg
|
|
*/
|
|
const eventClasses = function(arg: EventApi): object {
|
|
return {'calendarRangeItems': true};
|
|
}
|
|
|
|
/*
|
|
// currently, all events are stored into calendarRanges, due to reactivity bug
|
|
const remotes = computed<EventInput[]>(() => {
|
|
return store.state.calendarRemotes.remotes;
|
|
});
|
|
|
|
const sources = computed<EventSourceInput[]>(() => {
|
|
const sources = [];
|
|
|
|
const rangeSource: EventSourceInput = {
|
|
id: 'ranges',
|
|
events: ranges.value,
|
|
};
|
|
|
|
sources.push(rangeSource);
|
|
|
|
return sources;
|
|
});
|
|
*/
|
|
|
|
const calendarOptions = computed((): CalendarOptions => {
|
|
return {
|
|
...baseOptions.value,
|
|
weekends: showWeekends.value,
|
|
slotDuration: slotDuration.value,
|
|
events: ranges.value,
|
|
slotMinTime: slotMinTime.value,
|
|
slotMaxTime: slotMaxTime.value,
|
|
};
|
|
});
|
|
|
|
/**
|
|
* launched when the calendar range date change
|
|
*/
|
|
function onDatesSet(event: DatesSetArg): void {
|
|
store.dispatch('fullCalendar/setCurrentDatesView', {start: event.start, end: event.end});
|
|
}
|
|
|
|
function onDateSelect(event: DateSelectArg): void {
|
|
|
|
if (null === pickedLocation.value) {
|
|
window.alert("Indiquez une localisation avant de créer une période de disponibilité.");
|
|
return;
|
|
}
|
|
|
|
store.dispatch('calendarRanges/createRange', {start: event.start, end: event.end, location: pickedLocation.value});
|
|
}
|
|
|
|
/**
|
|
* When a calendar range is deleted
|
|
*/
|
|
function onClickDelete(event: EventApi): void {
|
|
console.log('onClickDelete', event);
|
|
|
|
if (event.extendedProps.is !== 'range') {
|
|
return;
|
|
}
|
|
|
|
store.dispatch('calendarRanges/deleteRange', event.extendedProps.calendarRangeId);
|
|
}
|
|
|
|
function onEventDropOrResize(payload: EventDropArg | EventResizeDoneArg) {
|
|
if (payload.event.extendedProps.is !== 'range') {
|
|
return;
|
|
}
|
|
const changedEvent = payload.event;
|
|
|
|
store.dispatch('calendarRanges/patchRangeTime', {
|
|
calendarRangeId: payload.event.extendedProps.calendarRangeId,
|
|
start: payload.event.start,
|
|
end: payload.event.end,
|
|
});
|
|
};
|
|
|
|
function onEventClick(payload: EventClickArg): void {
|
|
// @ts-ignore TS does not recognize the target. But it does exists.
|
|
if (payload.jsEvent.target.classList.contains('delete')) {
|
|
return;
|
|
}
|
|
if (payload.event.extendedProps.is !== 'range') {
|
|
return;
|
|
}
|
|
|
|
editLocation.value?.startEdit(payload.event);
|
|
}
|
|
|
|
function copyDay() {
|
|
if (null === copyFrom.value || null === copyTo.value) {
|
|
return;
|
|
}
|
|
|
|
store.dispatch('calendarRanges/copyFromDayToAnotherDay', {from: ISOToDate(copyFrom.value), to: ISOToDate(copyTo.value)})
|
|
}
|
|
|
|
</script>
|
|
|
|
<style scoped>
|
|
#copy-widget {
|
|
position: sticky;
|
|
bottom: 0px;
|
|
background-color: white;
|
|
z-index: 9999999999;
|
|
padding: 0.25rem 0 0.25rem;
|
|
}
|
|
</style>
|