492 lines
17 KiB
Vue

<template>
<div>
<h2 class="chill-red">{{ $t('edit_your_calendar_range') }}</h2>
<div class="form-check">
<input type="checkbox" id="myCalendar" class="form-check-input" v-model="showMyCalendarWidget" />
<label class="form-check-label" for="myCalendar">{{ $t('show_my_calendar') }}</label>
</div>
<div class="form-check">
<input type="checkbox" id="weekends" class="form-check-input" @click="toggleWeekends" />
<label class="form-check-label" for="weekends">{{ $t('show_weekends') }}</label>
</div>
<FullCalendar ref="fullCalendar" :options="calendarOptions">
<template v-slot:eventContent='arg' >
<span class='calendarRangeItems'>
<b v-if="arg.event.extendedProps.myCalendar" style="text-decoration: underline" >{{ arg.timeText }}</b>
<b v-else-if="!arg.event.extendedProps.myCalendar && arg.event.extendedProps.toDelete" style="text-decoration: line-through red" >{{ arg.timeText }}</b>
<b v-else >{{ arg.timeText }}</b>
<i>&nbsp;{{ arg.event.title }}</i>
<a v-if=!arg.event.extendedProps.myCalendar class="fa fa-fw fa-times"
@click.prevent="onClickDelete(arg.event)">
</a>
</span>
</template>
</FullCalendar>
<div>
<ul class="record_actions">
<!-- <li>
<button class="btn btn-save" :disabled="!dirty"
@click.prevent="onClickSave">
{{ $t('action.save')}}
</button>
<span v-if="flag.loading" class="loading">
<i class="fa fa-circle-o-notch fa-spin fa-fw"></i>
<span class="sr-only">{{ $t('loading') }}</span>
</span>
</li> -->
<li>
<button v-if="disableCopyDayButton" class="btn btn-action" disabled>
{{ $t('copy_range_to_next_day')}}
</button>
<button v-else class="btn btn-action"
@click.prevent="copyDay">
{{ $t('copy_range_from_day')}} {{this.lastNewDate.toLocaleDateString()}} {{ $t('to_the_next_day')}}
</button>
</li>
</ul>
</div>
<div>
<div v-if="newCalendarRanges.length > 0">
<h4>{{ $t('new_range_to_save') }}</h4>
<ul>
<li v-for="i in newCalendarRanges" :key="i.start">
{{ i.start.toLocaleString() }} - {{ i.end.toLocaleString() }}
</li>
</ul>
</div>
<div v-if="updateCalendarRanges.length > 0">
<h4>{{ $t('update_range_to_save') }}</h4>
<ul>
<li v-for="i in updateCalendarRanges" :key="i.start">
{{ i.start.toLocaleString() }} - {{ i.end.toLocaleString() }}
</li>
</ul>
</div>
<div v-if="deleteCalendarRanges.length > 0">
<h4>{{ $t('delete_range_to_save') }}</h4>
<ul>
<li v-for="i in deleteCalendarRanges" :key="i.start">
{{ i.start.toLocaleString() }} - {{ i.end.toLocaleString() }}
</li>
</ul>
</div>
</div>
</div>
<teleport to="body">
<modal v-if="modal.showModal"
:modalDialogClass="modal.modalDialogClass"
@close="modal.showModal = false">
<template v-slot:header>
<h2 class="modal-title">{{ this.renderEventDate() }}</h2>
</template>
<template v-slot:body>
<p>{{ $t('by')}} {{this.myCalendarClickedEvent.user.username }}</p>
<p>{{ $t('main_user_concerned') }} : {{ this.myCalendarClickedEvent.mainUser.username }}</p>
<p v-if="myCalendarClickedEvent.comment.length > 0" >{{ this.myCalendarClickedEvent.comment }}</p>
</template>
<template v-slot:footer>
<ul class="record_actions">
<li>
<a
class="btn btn-show"
:href=myCalendarEventShowLink() >
</a>
</li>
<li>
<a
class="btn btn-update"
:href=myCalendarEventUpdateLink() >
</a>
</li>
<li>
<a
class="btn btn-delete"
:href=myCalendarEventDeleteLink() >
</a>
</li>
</ul>
</template>
</modal>
</teleport>
</template>
<script>
import '@fullcalendar/core/vdom'; // solves problem with Vite
import frLocale from '@fullcalendar/core/locales/fr';
import FullCalendar from '@fullcalendar/vue3';
import dayGridPlugin from '@fullcalendar/daygrid';
import interactionPlugin from '@fullcalendar/interaction';
import timeGridPlugin from '@fullcalendar/timegrid';
import Modal from 'ChillMainAssets/vuejs/_components/Modal';
import { deleteCalendarRange, fetchCalendar, fetchCalendarRangesByUser, patchCalendarRange, postCalendarRange } from '../_api/api';
import { mapState } from 'vuex';
export default {
name: "App",
components: {
FullCalendar,
Modal
},
data() {
return {
errorMsg: [],
modal: {
showModal: false,
modalDialogClass: "modal-dialog-scrollable modal-m"
},
flag: {
loading: false
},
userId: window.userId,
showMyCalendar: true,
myCalendarClickedEvent: null,
calendarEvents: {
userCalendar: null,
userCalendarRange: null,
new: {
events: [],
color: "#3788d8"
}
},
lastNewDate: null,
disableCopyDayButton: true,
calendarOptions: {
locale: frLocale,
plugins: [ dayGridPlugin, interactionPlugin, timeGridPlugin ],
initialView: 'timeGridWeek',
initialDate: window.startDate !== undefined ? window.startDate : new Date(),
eventSource: [],
selectable: true,
select: this.onDateSelect,
eventChange: this.onEventChange,
eventDrop: this.onEventDropOrResize,
eventResize: this.onEventDropOrResize,
eventClick: this.onEventClick,
selectMirror: false,
editable: true,
weekends: false,
slotDuration: '00:15:00',
slotMinutes: 15,
slotMinTime: "08:00:00",
slotMaxTime: "18:00:00",
headerToolbar: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay'
},
},
}
},
computed: {
...mapState({
newCalendarRanges: state => state.newCalendarRanges,
updateCalendarRanges: state => state.updateCalendarRanges,
deleteCalendarRanges: state => state.deleteCalendarRanges,
dirty: state => state.newCalendarRanges.length > 0 || state.updateCalendarRanges.length > 0 || state.deleteCalendarRanges.length > 0
}),
showMyCalendarWidget: {
set(value) {
this.toggleMyCalendar(value);
this.updateEventsSource();
},
get() {
return this.showMyCalendar;
}
},
},
methods: {
init() {
console.log('init')
this.fetchData();
},
openModal() {
this.modal.showModal = true;
},
myCalendarEventShowLink() {
return `/fr/calendar/calendar/${this.myCalendarClickedEvent.id}/show?user_id=${ this.userId }`
},
myCalendarEventUpdateLink() {
return `/fr/calendar/calendar/${this.myCalendarClickedEvent.id}/edit?user_id=${ this.userId }`
},
myCalendarEventDeleteLink() {
return `/fr/calendar/calendar/${this.myCalendarClickedEvent.id}/delete?user_id=${ this.userId }`
},
resetCalendar() {
this.fetchData();
this.calendarEvents.new = {
events: [],
color: "#3788d8"
};
this.updateEventsSource();
},
fetchData() {
this.flag.loading = true;
fetchCalendarRangesByUser(this.userId).then(calendarRanges => new Promise((resolve, reject) => {
// console.log('calendar ranges', calendarRanges.results)
let events = calendarRanges.results.map(i =>
({
start: i.startDate.datetime,
end: i.endDate.datetime,
calendarRangeId: i.id,
toDelete: false
})
);
let calendarRangeEvents = {
events: events,
borderColor: "#3788d8",
backgroundColor: '#ffffff',
textColor: '#444444',
};
this.calendarEvents.userCalendarRange = calendarRangeEvents;
fetchCalendar(this.userId).then(calendar => new Promise((resolve, reject) => {
let events = calendar.results.map(i =>
({
myCalendar: true,
calendarId: i.id,
start: i.startDate.datetime,
end: i.endDate.datetime,
user: i.user,
mainUser: i.mainUser,
persons: i.persons,
professionals: i.professionals,
comment: i.comment
})
);
let calendarEventsCurrentUser = {
events: events,
color: 'darkblue',
id: 1000,
editable: false
};
this.calendarEvents.userCalendar = calendarEventsCurrentUser;
this.updateEventsSource();
this.flag.loading = false;
resolve();
}));
resolve();
}));
},
updateEventsSource() {
this.calendarOptions.eventSources = [];
this.calendarOptions.eventSources.push(this.calendarEvents.new);
this.calendarOptions.eventSources.push(this.calendarEvents.userCalendarRange);
if (this.showMyCalendar) {
this.calendarOptions.eventSources.push(this.calendarEvents.userCalendar);
}
// console.log(this.calendarOptions.eventSources);
},
toggleMyCalendar(value) {
this.showMyCalendar = value;
},
toggleWeekends: function() {
this.calendarOptions.weekends = !this.calendarOptions.weekends;
},
onDateSelect(payload) {
// console.log('payload', payload);
let events = this.calendarEvents.new.events;
// events.push({
// start: payload.startStr,
// end: payload.endStr
// });
// this.calendarEvents.new = {
// events: events,
// borderColor: "#3788d8",
// backgroundColor: '#fffadf',
// textColor: '#444444',
// };
this.disableCopyDayButton = false;
this.lastNewDate = new Date(payload.startStr);
this.updateEventsSource();
// this.$store.dispatch('createRange', payload);
postCalendarRange({
user: {
type: 'user',
id: window.userId,
},
startDate: {
datetime: `${payload.start.toISOString().split('.')[0]}+0000`, //should be like "2021-08-20T15:00:00+0200",
},
endDate: {
datetime: `${payload.end.toISOString().split('.')[0]}+0000`, // TODO check if OK with time zone
},
}).then(() => {
this.init();
})
},
onEventChange(payload) {
},
onEventDropOrResize(payload) {
payload.event.setProp('borderColor', '#3788d8');
payload.event.setProp('backgroundColor', '#fffadf');
payload.event.setProp('textColor', '#444444');
this.$store.dispatch('updateRange', payload);
},
onEventClick(payload) {
if (payload.event.extendedProps.myCalendar) {
this.myCalendarClickedEvent = {
id: payload.event.extendedProps.calendarId,
start: payload.event.start,
end: payload.event.end,
user: payload.event.extendedProps.user,
mainUser: payload.event.extendedProps.mainUser,
persons: payload.event.extendedProps.persons,
professionals: payload.event.extendedProps.professionals,
comment: payload.event.extendedProps.comment
};
// console.log(this.myCalendarClickedEvent)
this.openModal();
}
},
// onClickSave(payload) {
// this.flag.loading = true;
// if (this.$store.state.newCalendarRanges.length > 0){
// Promise.all(this.$store.state.newCalendarRanges.map(cr => {
// postCalendarRange({
// user: {
// type: 'user',
// id: window.userId,
// },
// startDate: {
// datetime: `${cr.start.toISOString().split('.')[0]}+0000`, //should be like "2021-08-20T15:00:00+0200",
// },
// endDate: {
// datetime: `${cr.end.toISOString().split('.')[0]}+0000`, // TODO check if OK with time zone
// },
// })
// })
// ).then((_r) => this.resetCalendar());
// this.$store.dispatch('clearNewCalendarRanges', payload);
// }
// if (this.$store.state.updateCalendarRanges.length > 0){
// Promise.all(this.$store.state.updateCalendarRanges.map(cr => {
// patchCalendarRange(cr.id,
// {
// startDate: {
// datetime: `${cr.start.toISOString().split('.')[0]}+0000`, //should be like "2021-08-20T15:00:00+0200",
// },
// endDate: {
// datetime: `${cr.end.toISOString().split('.')[0]}+0000`, // TODO check if OK with time zone
// },
// })
// })
// ).then((_r) => this.resetCalendar());
// this.$store.dispatch('clearUpdateCalendarRanges', payload);
// }
// if (this.$store.state.deleteCalendarRanges.length > 0){
// Promise.all(this.$store.state.deleteCalendarRanges.map(cr => {
// deleteCalendarRange(cr.id)
// })
// ).then((_r) => this.resetCalendar());
// this.$store.dispatch('clearDeleteCalendarRanges', payload);
// }
// },
onClickDelete(payload) {
if (payload.extendedProps.hasOwnProperty("calendarRangeId")) {
deleteCalendarRange(payload.extendedProps.calendarRangeId).then(() => {
this.init();
})
// if (payload.extendedProps.toDelete) {
// payload.setExtendedProp('toDelete', false)
// payload.setProp('borderColor', '#79bafc');
// this.$store.dispatch('removeFromDeleteRange', payload);
// } else {
// payload.setExtendedProp('toDelete', true)
// payload.setProp('borderColor', '#dddddd');
// this.$store.dispatch('deleteRange', payload);
// }
} else {
let newEvents = this.calendarEvents.new.events;
let filterEvents = newEvents.filter((e) =>
e.start !== payload.startStr && e.end !== payload.endStr
);
this.calendarEvents.new = {
events: filterEvents,
color: "#3788d8"
};
this.$store.dispatch('removeNewCalendarRanges', payload);
this.updateEventsSource();
}
},
isSameDay(date1, date2) {
return date1.getFullYear() === date2.getFullYear() &&
date1.getMonth() === date2.getMonth() &&
date1.getDate() === date2.getDate();
},
isFriday(date) {
return date.getDay() === 5
},
copyDay(_payload) {
// console.log(this.calendarEvents.new);
if (this.calendarEvents.new.events.length > 0) {
// Create the copied events
let increment = !this.calendarOptions.weekends && this.isFriday(this.lastNewDate) ? 24*60*60*1000*3 : 24*60*60*1000;
let events = this.calendarEvents.new.events.filter(
i => this.isSameDay(new Date(i.start), this.lastNewDate)).map(
i => {
let startDate = new Date(new Date(i.start).getTime() + increment);
let endDate = new Date(new Date(i.end).getTime() + increment);
return ({
start: startDate.toISOString(),
end: endDate.toISOString()
})
}
);
let copiedEvents = {
events: events,
color: "#3788d8"
};
// console.log(copiedEvents);
// Add to the calendar
let newEvents = this.calendarEvents.new.events;
newEvents.push(...copiedEvents.events);
this.calendarEvents.new = {
events: newEvents,
color: "#3788d8"
};
this.updateEventsSource();
// Set the last new date
this.lastNewDate = new Date(copiedEvents.events[copiedEvents.events.length - 1].start);
// Dispatch in store for saving
for (let i = 0; i < copiedEvents.events.length; i++) {
let eventObj = {
start: new Date(copiedEvents.events[i].start),
end: new Date(copiedEvents.events[i].end)
}
this.$store.dispatch('createRange', eventObj);
}
} else {
console.log('no new events to copy-paste!')
}
},
renderEventDate() {
let start = this.myCalendarClickedEvent.start;
let end = this.myCalendarClickedEvent.end;
return start.getDate() === end.getDate() ?
`${start.toLocaleDateString()}, ${start.toLocaleTimeString()} - ${end.toLocaleTimeString()}` :
`${start.toLocaleString()} - ${end.toLocaleString()}`;
}
},
mounted() {
this.init();
}
}
</script>