mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-23 16:13:50 +00:00
Merge branch 'calendar/finalization' into calendar_changes
This commit is contained in:
@@ -1,27 +1,83 @@
|
||||
<template>
|
||||
|
||||
<teleport to="#mainUser">
|
||||
|
||||
<h2 class="chill-red">Utilisateur principal</h2>
|
||||
<div>
|
||||
<div>
|
||||
<div v-if="null !== this.$store.getters.getMainUser">
|
||||
<calendar-active :user="this.$store.getters.getMainUser" ></calendar-active>
|
||||
</div>
|
||||
<pick-entity
|
||||
:multiple="false"
|
||||
:types="['user']"
|
||||
:uniqid="'main_user_calendar'"
|
||||
:picked="null !== this.$store.getters.getMainUser ? [this.$store.getters.getMainUser] : []"
|
||||
:removableIfSet="false"
|
||||
:displayPicked="false"
|
||||
@addNewEntity="setMainUser"
|
||||
></pick-entity>
|
||||
</div>
|
||||
</div>
|
||||
</teleport>
|
||||
|
||||
<concerned-groups></concerned-groups>
|
||||
|
||||
<teleport to="#schedule">
|
||||
<div class="row mb-3" v-if="activity.startDate !== null">
|
||||
<label class="col-form-label col-sm-4">Date</label>
|
||||
<div class="col-sm-8">
|
||||
{{ $d(activity.startDate, 'long') }} - {{ $d(activity.endDate, 'hoursOnly') }}
|
||||
<span v-if="activity.calendarRange === null">(Pas de plage de disponibilité sélectionnée)</span>
|
||||
<span v-else>(Une plage de disponibilité sélectionnée)</span>
|
||||
</div>
|
||||
</div>
|
||||
</teleport>
|
||||
|
||||
<location></location>
|
||||
<teleport to="#calendarControls">
|
||||
<calendar-user-selector
|
||||
v-bind:users="users"
|
||||
v-bind:calendarEvents="calendarEvents"
|
||||
v-bind:updateEventsSource="updateEventsSource"
|
||||
v-bind:showMyCalendar="showMyCalendar"
|
||||
v-bind:toggleMyCalendar="toggleMyCalendar"
|
||||
v-bind:toggleWeekends="toggleWeekends" >
|
||||
</calendar-user-selector>
|
||||
</teleport>
|
||||
<teleport to="#fullCalendar">
|
||||
<FullCalendar ref="fullCalendar" :options="calendarOptions">
|
||||
<template v-slot:eventContent='arg'>
|
||||
<b>{{ arg.timeText }}</b>
|
||||
<i> {{ arg.event.title }}</i>
|
||||
</template>
|
||||
</FullCalendar>
|
||||
</teleport>
|
||||
|
||||
|
||||
|
||||
<teleport to="#fullCalendar">
|
||||
<div class="calendar-actives">
|
||||
<template class="" v-for="u in getActiveUsers" :key="u.id">
|
||||
<calendar-active :user="u" :invite="this.$store.getters.getInviteForUser(u)"></calendar-active>
|
||||
</template>
|
||||
</div>
|
||||
<div class="display-options row justify-content-between">
|
||||
<div class="col-sm 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="this.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>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm col-xs-12">
|
||||
<div class="float-end">
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-text">
|
||||
<input id="showHideWE" class="form-check-input mt-0" type="checkbox" v-model="this.hideWeekEnds">
|
||||
<label for="showHideWE" class="form-check-label"> Masquer les week-ends</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<FullCalendar ref="fullCalendar" :options="calendarOptions">
|
||||
<template v-slot:eventContent='arg'>
|
||||
<b>{{ arg.timeText }}</b>
|
||||
<i> {{ arg.event.title }}</i>
|
||||
</template>
|
||||
</FullCalendar>
|
||||
</teleport>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
//import {mapGetters} from 'vuex';
|
||||
import ConcernedGroups from 'ChillActivityAssets/vuejs/Activity/components/ConcernedGroups.vue';
|
||||
import Location from 'ChillActivityAssets/vuejs/Activity/components/Location.vue';
|
||||
import CalendarUserSelector from '../_components/CalendarUserSelector/CalendarUserSelector.vue';
|
||||
@@ -31,126 +87,154 @@ import FullCalendar from '@fullcalendar/vue3';
|
||||
import dayGridPlugin from '@fullcalendar/daygrid';
|
||||
import interactionPlugin from '@fullcalendar/interaction';
|
||||
import timeGridPlugin from '@fullcalendar/timegrid';
|
||||
// import listPlugin from '@fullcalendar/list';
|
||||
import CalendarActive from './Components/CalendarActive';
|
||||
import PickEntity from 'ChillMainAssets/vuejs/PickEntity/PickEntity.vue';
|
||||
import {mapGetters, mapState} from "vuex";
|
||||
|
||||
export default {
|
||||
name: "App",
|
||||
components: {
|
||||
ConcernedGroups,
|
||||
Location,
|
||||
CalendarUserSelector,
|
||||
FullCalendar
|
||||
FullCalendar,
|
||||
CalendarActive,
|
||||
PickEntity,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
errorMsg: [],
|
||||
users: {
|
||||
loaded: [],
|
||||
selected: [],
|
||||
logged: null
|
||||
},
|
||||
calendarEvents: {
|
||||
loaded: [],
|
||||
selected: [],
|
||||
user: [],
|
||||
current: {
|
||||
events: [{
|
||||
title: 'plage prévue',
|
||||
start: window.startDate,
|
||||
end: window.endDate
|
||||
}],
|
||||
id: window.mainUser,
|
||||
color: '#bbbbbb'
|
||||
}
|
||||
},
|
||||
selectedEvent: null,
|
||||
previousSelectedEvent: null,
|
||||
previousSelectedEventColor: null,
|
||||
showMyCalendar: false,
|
||||
calendarOptions: {
|
||||
slotDuration: '00:10:00',
|
||||
hideWeekEnds: true,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getMainUser']),
|
||||
...mapState(['activity']),
|
||||
events() {
|
||||
return this.$store.getters.getEventSources;
|
||||
},
|
||||
calendarOptions() {
|
||||
return {
|
||||
locale: frLocale,
|
||||
plugins: [ dayGridPlugin, interactionPlugin, timeGridPlugin ],
|
||||
plugins: [dayGridPlugin, interactionPlugin, timeGridPlugin, dayGridPlugin],
|
||||
initialView: 'timeGridWeek',
|
||||
initialDate: window.startDate !== undefined ? window.startDate : new Date(),
|
||||
eventSource: [],
|
||||
initialDate: this.$store.getters.getInitialDate,
|
||||
eventSources: this.events,
|
||||
selectable: true,
|
||||
datesSet: this.onDatesSet,
|
||||
select: this.onDateSelect,
|
||||
eventChange: this.onEventChange,
|
||||
eventClick: this.onEventClick,
|
||||
// eventMouseEnter: this.onEventMouseEnter,
|
||||
// eventMouseLeave: this.onEventMouseLeave,
|
||||
selectMirror: true,
|
||||
editable: true,
|
||||
weekends: false,
|
||||
weekends: !this.hideWeekEnds,
|
||||
headerToolbar: {
|
||||
left: 'prev,next today',
|
||||
center: 'title',
|
||||
right: 'dayGridMonth,timeGridWeek,timeGridDay,listMonth,listWeek,listDay'
|
||||
right: 'dayGridMonth,timeGridWeek,dayGridThreeDays,timeGridDay',
|
||||
},
|
||||
views: {
|
||||
timeGrid: {
|
||||
slotEventOverlap: false,
|
||||
slotDuration: this.slotDuration,
|
||||
//scrollTime: '10:00:00',
|
||||
},
|
||||
dayGridThreeDays: {
|
||||
type: 'dayGridWeek',
|
||||
duration: { days: 3},
|
||||
buttonText: this.$t('list_three_days'),
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
getActiveUsers() {
|
||||
const users = [];
|
||||
for (const id of this.$store.state.currentView.users.keys()) {
|
||||
users.push(this.$store.getters.getUserDataById(id).user);
|
||||
}
|
||||
return users;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.updateEventsSource();
|
||||
},
|
||||
toggleMyCalendar(value) {
|
||||
this.showMyCalendar = value;
|
||||
},
|
||||
toggleWeekends: function() {
|
||||
this.calendarOptions.weekends = !this.calendarOptions.weekends;
|
||||
},
|
||||
updateEventsSource() {
|
||||
this.calendarOptions.eventSources = [];
|
||||
this.calendarOptions.eventSources.push(...this.calendarEvents.selected);
|
||||
if (window.startDate !== undefined) {
|
||||
this.calendarOptions.eventSources.push(this.calendarEvents.current);
|
||||
}
|
||||
if (this.showMyCalendar) {
|
||||
this.calendarOptions.eventSources.push(this.calendarEvents.user);
|
||||
}
|
||||
},
|
||||
unSelectPreviousEvent(event) {
|
||||
if (event) {
|
||||
if (typeof event.setProp === 'function') {
|
||||
event.setProp('backgroundColor', this.previousSelectedEventColor);
|
||||
event.setProp('borderColor', this.previousSelectedEventColor);
|
||||
event.setProp('textColor','#444444');
|
||||
event.setProp('title','');
|
||||
setMainUser(user) {
|
||||
console.log('setMainUser APP', user);
|
||||
|
||||
if (user.id !== this.$store.getters.getMainUser && (
|
||||
this.$store.state.activity.calendarRange !== null
|
||||
|| this.$store.state.activity.startDate !== null
|
||||
|| this.$store.state.activity.endDate !== null
|
||||
)
|
||||
) {
|
||||
if (!window.confirm(this.$t('change_main_user_will_reset_event_data'))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.$store.dispatch('setMainUser', user);
|
||||
this.$store.commit('showUserOnCalendar', {user, ranges: true, remotes: true});
|
||||
},
|
||||
removeMainUser(user) {
|
||||
console.log('removeMainUser APP', user);
|
||||
|
||||
window.alert(this.$t('main_user_is_mandatory'));
|
||||
return;
|
||||
},
|
||||
onDatesSet(event) {
|
||||
console.log('onDatesSet', event);
|
||||
this.$store.dispatch('setCurrentDatesView', {start: event.start, end: event.end});
|
||||
},
|
||||
onDateSelect(payload) {
|
||||
// console.log(payload)
|
||||
this.unSelectPreviousEvent(this.selectedEvent);
|
||||
Object.assign(payload, {users: this.users});
|
||||
Object.assign(payload, {title: 'Choisir cette plage'}); //TODO does not display
|
||||
//payload.event.setProp('title', 'Choisir cette plage');
|
||||
this.$store.dispatch('createEvent', payload);
|
||||
console.log('onDateSelect', payload);
|
||||
|
||||
// show an alert if changing mainUser
|
||||
if ((this.$store.getters.getMainUser !== null
|
||||
&& this.$store.state.me.id !== this.$store.getters.getMainUser.id)
|
||||
|| this.$store.getters.getMainUser === null) {
|
||||
if (!window.confirm(this.$t('will_change_main_user_for_me'))) {
|
||||
return;
|
||||
} else {
|
||||
this.$store.commit('showUserOnCalendar', {user: this.$store.state.me, remotes: true, ranges: true})
|
||||
}
|
||||
}
|
||||
|
||||
this.$store.dispatch('setEventTimes', {start: payload.start, end: payload.end});
|
||||
},
|
||||
onEventChange(payload) {
|
||||
this.$store.dispatch('updateEvent', payload);
|
||||
console.log('onEventChange', payload);
|
||||
if (this.$store.state.activity.calendarRange !== null) {
|
||||
throw new Error("not allowed to edit a calendar associated with a calendar range");
|
||||
}
|
||||
this.$store.dispatch('setEventTimes', {start: payload.event.start, end: payload.event.end});
|
||||
},
|
||||
onEventClick(payload) {
|
||||
this.previousSelectedEvent = this.selectedEvent;
|
||||
this.previousSelectedEventColor = payload.event.extendedProps.sourceColor;
|
||||
this.selectedEvent = payload.event;
|
||||
this.unSelectPreviousEvent(this.previousSelectedEvent);
|
||||
payload.event.setProp('backgroundColor','#3788d8');
|
||||
payload.event.setProp('borderColor','#3788d8');
|
||||
payload.event.setProp('title', 'Choisir cette plage');
|
||||
payload.event.setProp('textColor','#ffffff');
|
||||
if (payload.event.extendedProps.is !== 'range') {
|
||||
// do nothing when clicking on remote
|
||||
return;
|
||||
}
|
||||
|
||||
// show an alert if changing mainUser
|
||||
if (this.$store.getters.getMainUser !== null
|
||||
&& payload.event.extendedProps.userId !== this.$store.getters.getMainUser.id) {
|
||||
if (!window.confirm(this.$t('this_calendar_range_will_change_main_user'))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.$store.dispatch('associateCalendarToRange', {range: payload.event});
|
||||
},
|
||||
onEventMouseEnter(payload) {
|
||||
payload.event.setProp('borderColor','#444444');
|
||||
},
|
||||
onEventMouseLeave(payload) {
|
||||
payload.event.setProp('borderColor','#ffffff');
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.calendar-actives {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.display-options {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div :style="style" class="calendar-active">
|
||||
<span class="badge-user">
|
||||
{{ user.text }}
|
||||
<template v-if="invite !== null">
|
||||
<i v-if="invite.status === 'accepted'" class="fa fa-check"></i>
|
||||
<i v-else-if="invite.status === 'declined'" class="fa fa-times"></i>
|
||||
<i v-else-if="invite.status === 'pending'" class="fa fa-question-o"></i>
|
||||
<i v-else-if="invite.status === 'tentative'" class="fa fa-question"></i>
|
||||
<span v-else="">{{ invite.status }}</span>
|
||||
</template>
|
||||
</span>
|
||||
<span class="form-check-inline form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="flexSwitchCheckDefault" v-model="rangeShow">
|
||||
<label class="form-check-label" for="flexSwitchCheckDefault" title="Disponibilités"><i class="fa fa-calendar-check-o"></i></label>
|
||||
</span>
|
||||
<span class="form-check-inline form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="flexSwitchCheckDefault" v-model="remoteShow">
|
||||
<label class="form-check-label" for="flexSwitchCheckDefault" title="Agenda"><i class="fa fa-calendar"></i></label>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapGetters} from 'vuex';
|
||||
|
||||
export default {
|
||||
name: "CalendarActive",
|
||||
props: {
|
||||
user: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
invite: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
style() {
|
||||
return {
|
||||
backgroundColor: this.$store.getters.getUserData(this.user).mainColor,
|
||||
};
|
||||
},
|
||||
rangeShow: {
|
||||
set (value) {
|
||||
this.$store.commit('showUserOnCalendar', {user: this.user, ranges: value});
|
||||
},
|
||||
get() {
|
||||
return this.$store.getters.isRangeShownOnCalendarForUser(this.user);
|
||||
}
|
||||
},
|
||||
remoteShow: {
|
||||
set (value) {
|
||||
this.$store.commit('showUserOnCalendar', {user: this.user, remotes: value});
|
||||
},
|
||||
get() {
|
||||
return this.$store.getters.isRemoteShownOnCalendarForUser(this.user);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
.calendar-active {
|
||||
margin: 0 0.25rem 0.25rem 0;
|
||||
padding: 0.5rem;
|
||||
|
||||
border-radius: 0.5rem;
|
||||
|
||||
color: var(--bs-blue);
|
||||
|
||||
& > .badge-user {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
@@ -0,0 +1,48 @@
|
||||
import {fetchResults} from 'ChillMainAssets/lib/api/apiMethods';
|
||||
import {datetimeToISO} from 'ChillMainAssets/chill/js/date';
|
||||
|
||||
const whoami = () => {
|
||||
const url = `/api/1.0/main/whoami.json`;
|
||||
return fetch(url)
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
return response.json();
|
||||
}
|
||||
throw {
|
||||
msg: 'Error while getting whoami.',
|
||||
sta: response.status,
|
||||
txt: response.statusText,
|
||||
err: new Error(),
|
||||
body: response.body
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param user
|
||||
* @param Date start
|
||||
* @param Date end
|
||||
* @return Promise
|
||||
*/
|
||||
const fetchCalendarRangeForUser = (user, start, end) => {
|
||||
const uri = `/api/1.0/calendar/calendar-range-available/${user.id}.json`;
|
||||
const dateFrom = datetimeToISO(start);
|
||||
const dateTo = datetimeToISO(end);
|
||||
|
||||
return fetchResults(uri, {dateFrom, dateTo});
|
||||
}
|
||||
|
||||
const fetchCalendarRemoteForUser = (user, start, end) => {
|
||||
const uri = `/api/1.0/calendar/proxy/calendar/by-user/${user.id}/events`;
|
||||
const dateFrom = datetimeToISO(start);
|
||||
const dateTo = datetimeToISO(end);
|
||||
|
||||
return fetchResults(uri, {dateFrom, dateTo});
|
||||
}
|
||||
|
||||
export {
|
||||
whoami,
|
||||
fetchCalendarRangeForUser,
|
||||
fetchCalendarRemoteForUser,
|
||||
};
|
@@ -0,0 +1,19 @@
|
||||
|
||||
const COLORS = [ /* from https://colorbrewer2.org/#type=qualitative&scheme=Set3&n=12 */
|
||||
'#8dd3c7',
|
||||
'#ffffb3',
|
||||
'#bebada',
|
||||
'#fb8072',
|
||||
'#80b1d3',
|
||||
'#fdb462',
|
||||
'#b3de69',
|
||||
'#fccde5',
|
||||
'#d9d9d9',
|
||||
'#bc80bd',
|
||||
'#ccebc5',
|
||||
'#ffed6f'
|
||||
];
|
||||
|
||||
export {
|
||||
COLORS,
|
||||
};
|
@@ -1,19 +1,24 @@
|
||||
import { personMessages } from 'ChillPersonAssets/vuejs/_js/i18n'
|
||||
import { calendarUserSelectorMessages } from '../_components/CalendarUserSelector/js/i18n';
|
||||
import { activityMessages } from 'ChillActivityAssets/vuejs/Activity/i18n';
|
||||
import {personMessages} from 'ChillPersonAssets/vuejs/_js/i18n'
|
||||
import {calendarUserSelectorMessages} from '../_components/CalendarUserSelector/js/i18n';
|
||||
import {activityMessages} from 'ChillActivityAssets/vuejs/Activity/i18n';
|
||||
|
||||
const appMessages = {
|
||||
fr: {
|
||||
choose_your_date: "Sélectionnez votre plage",
|
||||
activity: {
|
||||
add_persons: "Ajouter des personnes concernées",
|
||||
bloc_persons: "Usagers",
|
||||
bloc_persons_associated: "Usagers du parcours",
|
||||
bloc_persons_not_associated: "Tiers non-pro.",
|
||||
bloc_thirdparty: "Tiers professionnels",
|
||||
bloc_users: "T(M)S",
|
||||
}
|
||||
}
|
||||
fr: {
|
||||
choose_your_date: "Sélectionnez votre plage",
|
||||
activity: {
|
||||
add_persons: "Ajouter des personnes concernées",
|
||||
bloc_persons: "Usagers",
|
||||
bloc_persons_associated: "Usagers du parcours",
|
||||
bloc_persons_not_associated: "Tiers non-pro.",
|
||||
bloc_thirdparty: "Tiers professionnels",
|
||||
bloc_users: "T(M)S",
|
||||
},
|
||||
this_calendar_range_will_change_main_user: "Cette plage de disponibilité n'est pas celle de l'utilisateur principal. Si vous continuez, l'utilisateur principal sera adapté. Êtes-vous sûr·e ?",
|
||||
will_change_main_user_for_me: "Vous ne pouvez pas écrire dans le calendrier d'un autre utilisateur. Voulez-vous être l'utilisateur principal de ce rendez-vous ?",
|
||||
main_user_is_mandatory: "L'utilisateur principal est requis. Vous pouvez le modifier, mais pas le supprimer",
|
||||
change_main_user_will_reset_event_data: "Modifier l'utilisateur principal nécessite de choisir une autre plage de disponibilité ou un autre horaire. Ces informations seront perdues. Êtes-vous sûr·e de vouloir continuer ?",
|
||||
list_three_days: 'Liste 3 jours',
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(appMessages.fr, personMessages.fr);
|
||||
@@ -21,5 +26,5 @@ Object.assign(appMessages.fr, calendarUserSelectorMessages.fr);
|
||||
Object.assign(appMessages.fr, activityMessages.fr);
|
||||
|
||||
export {
|
||||
appMessages
|
||||
appMessages
|
||||
};
|
||||
|
@@ -1,236 +0,0 @@
|
||||
import 'es6-promise/auto';
|
||||
import { createStore } from 'vuex';
|
||||
import { postLocation } from 'ChillActivityAssets/vuejs/Activity/api';
|
||||
import {
|
||||
getLocations, getLocationTypeByDefaultFor,
|
||||
getUserCurrentLocation
|
||||
} from "../../../../../ChillActivityBundle/Resources/public/vuejs/Activity/api";
|
||||
|
||||
const debug = process.env.NODE_ENV !== 'production';
|
||||
|
||||
const addIdToValue = (string, id) => {
|
||||
let array = string ? string.split(',') : [];
|
||||
array.push(id.toString());
|
||||
let str = array.join();
|
||||
return str;
|
||||
};
|
||||
|
||||
const removeIdFromValue = (string, id) => {
|
||||
let array = string.split(',');
|
||||
array = array.filter(el => el !== id.toString());
|
||||
let str = array.join();
|
||||
return str;
|
||||
};
|
||||
|
||||
/*
|
||||
* Assign missing keys for the ConcernedGroups component
|
||||
*/
|
||||
const mapEntity = (entity) => {
|
||||
Object.assign(entity, {thirdParties: entity.professionals, users: entity.invites});
|
||||
return entity;
|
||||
};
|
||||
|
||||
const store = createStore({
|
||||
strict: debug,
|
||||
state: {
|
||||
activity: mapEntity(window.entity), // activity is the calendar entity actually
|
||||
currentEvent: null,
|
||||
availableLocations: [],
|
||||
},
|
||||
getters: {
|
||||
suggestedEntities(state) {
|
||||
if (typeof(state.activity.accompanyingPeriod) === 'undefined') {
|
||||
return [];
|
||||
}
|
||||
const allEntities = [
|
||||
...store.getters.suggestedPersons,
|
||||
...store.getters.suggestedRequestor,
|
||||
...store.getters.suggestedUser,
|
||||
...store.getters.suggestedResources
|
||||
];
|
||||
const uniqueIds = [...new Set(allEntities.map(i => `${i.type}-${i.id}`))];
|
||||
return Array.from(uniqueIds, id => allEntities.filter(r => `${r.type}-${r.id}` === id)[0]);
|
||||
},
|
||||
suggestedPersons(state) {
|
||||
const existingPersonIds = state.activity.persons.map(p => p.id);
|
||||
return state.activity.accompanyingPeriod.participations
|
||||
.filter(p => p.endDate === null)
|
||||
.map(p => p.person)
|
||||
.filter(p => !existingPersonIds.includes(p.id))
|
||||
},
|
||||
suggestedRequestor(state) {
|
||||
if (state.activity.accompanyingPeriod.requestor === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const existingPersonIds = state.activity.persons.map(p => p.id);
|
||||
const existingThirdPartyIds = state.activity.thirdParties.map(p => p.id);
|
||||
return [state.activity.accompanyingPeriod.requestor]
|
||||
.filter(r =>
|
||||
(r.type === 'person' && !existingPersonIds.includes(r.id)) ||
|
||||
(r.type === 'thirdparty' && !existingThirdPartyIds.includes(r.id))
|
||||
);
|
||||
},
|
||||
suggestedUser(state) {
|
||||
if (null === state.activity.users) {
|
||||
return [];
|
||||
}
|
||||
const existingUserIds = state.activity.users.map(p => p.id);
|
||||
return [state.activity.accompanyingPeriod.user]
|
||||
.filter(
|
||||
u => u !== null && !existingUserIds.includes(u.id)
|
||||
);
|
||||
},
|
||||
suggestedResources(state) {
|
||||
const resources = state.activity.accompanyingPeriod.resources;
|
||||
const existingPersonIds = state.activity.persons.map(p => p.id);
|
||||
const existingThirdPartyIds = state.activity.thirdParties.map(p => p.id);
|
||||
return state.activity.accompanyingPeriod.resources
|
||||
.map(r => r.resource)
|
||||
.filter(r =>
|
||||
(r.type === 'person' && !existingPersonIds.includes(r.id)) ||
|
||||
(r.type === 'thirdparty' && !existingThirdPartyIds.includes(r.id))
|
||||
);
|
||||
}
|
||||
},
|
||||
mutations: {
|
||||
|
||||
// ConcernedGroups
|
||||
addPersonsInvolved(state, payload) {
|
||||
//console.log('### mutation addPersonsInvolved', payload.result.type);
|
||||
switch (payload.result.type) {
|
||||
case 'person':
|
||||
state.activity.persons.push(payload.result);
|
||||
break;
|
||||
case 'thirdparty':
|
||||
state.activity.thirdParties.push(payload.result);
|
||||
break;
|
||||
case 'user':
|
||||
state.activity.users.push(payload.result);
|
||||
break;
|
||||
};
|
||||
},
|
||||
removePersonInvolved(state, payload) {
|
||||
//console.log('### mutation removePersonInvolved', payload.type);
|
||||
switch (payload.type) {
|
||||
case 'person':
|
||||
state.activity.persons = state.activity.persons.filter(person => person !== payload);
|
||||
break;
|
||||
case 'thirdparty':
|
||||
state.activity.thirdParties = state.activity.thirdParties.filter(thirdparty => thirdparty !== payload);
|
||||
break;
|
||||
case 'user':
|
||||
state.activity.users = state.activity.users.filter(user => user !== payload);
|
||||
break;
|
||||
};
|
||||
},
|
||||
// Calendar
|
||||
setEvents(state, payload) {
|
||||
// console.log(payload)
|
||||
state.currentEvent = {start: payload.start, end: payload.end}
|
||||
},
|
||||
// Location
|
||||
updateLocation(state, value) {
|
||||
// console.log('### mutation: updateLocation', value);
|
||||
state.activity.location = value;
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
addPersonsInvolved({ commit }, payload) {
|
||||
// console.log('### action addPersonsInvolved', payload.result.type);
|
||||
switch (payload.result.type) {
|
||||
case 'person':
|
||||
let aPersons = document.getElementById("chill_activitybundle_activity_persons");
|
||||
aPersons.value = addIdToValue(aPersons.value, payload.result.id);
|
||||
break;
|
||||
case 'thirdparty':
|
||||
let aThirdParties = document.getElementById("chill_activitybundle_activity_professionals");
|
||||
aThirdParties.value = addIdToValue(aThirdParties.value, payload.result.id);
|
||||
break;
|
||||
case 'user':
|
||||
let aUsers = document.getElementById("chill_activitybundle_activity_invites");
|
||||
aUsers.value = addIdToValue(aUsers.value, payload.result.id);
|
||||
break;
|
||||
};
|
||||
commit('addPersonsInvolved', payload);
|
||||
},
|
||||
removePersonInvolved({ commit }, payload) {
|
||||
//console.log('### action removePersonInvolved', payload);
|
||||
switch (payload.type) {
|
||||
case 'person':
|
||||
let aPersons = document.getElementById("chill_activitybundle_activity_persons");
|
||||
aPersons.value = removeIdFromValue(aPersons.value, payload.id);
|
||||
break;
|
||||
case 'thirdparty':
|
||||
let aThirdParties = document.getElementById("chill_activitybundle_activity_professionals");
|
||||
aThirdParties.value = removeIdFromValue(aThirdParties.value, payload.id);
|
||||
break;
|
||||
case 'user':
|
||||
let aUsers = document.getElementById("chill_activitybundle_activity_invites");
|
||||
aUsers.value = removeIdFromValue(aUsers.value, payload.id);
|
||||
break;
|
||||
};
|
||||
commit('removePersonInvolved', payload);
|
||||
},
|
||||
|
||||
// Calendar
|
||||
createEvent({ commit }, payload) {
|
||||
console.log('### action createEvent', payload);
|
||||
let startDateInput = document.getElementById("chill_activitybundle_activity_startDate");
|
||||
startDateInput.value = payload.startStr;
|
||||
let endDateInput = document.getElementById("chill_activitybundle_activity_endDate");
|
||||
endDateInput.value = payload.endStr;
|
||||
let mainUserInput = document.getElementById("chill_activitybundle_activity_mainUser");
|
||||
mainUserInput.value = payload.users.logged.id;
|
||||
commit('setEvents', payload);
|
||||
},
|
||||
updateEvent({ commit }, payload) {
|
||||
console.log('### action updateEvent', payload);
|
||||
let startDateInput = document.getElementById("chill_activitybundle_activity_startDate");
|
||||
startDateInput.value = payload.event.start.toISOString();
|
||||
let endDateInput = document.getElementById("chill_activitybundle_activity_endDate");
|
||||
endDateInput.value = payload.event.end.toISOString();
|
||||
let calendarRangeInput = document.getElementById("chill_activitybundle_activity_calendarRange");
|
||||
calendarRangeInput.value = Number(payload.event.extendedProps.calendarRangeId);
|
||||
let mainUserInput = document.getElementById("chill_activitybundle_activity_mainUser");
|
||||
mainUserInput.value = Number(payload.event.source.id);
|
||||
commit('setEvents', payload);
|
||||
},
|
||||
|
||||
// Location
|
||||
updateLocation({ commit }, value) {
|
||||
console.log('### action: updateLocation', value);
|
||||
let hiddenLocation = document.getElementById("chill_activitybundle_activity_location");
|
||||
if (value.onthefly) {
|
||||
const body = {
|
||||
"type": "location",
|
||||
"name": value.name === '__AccompanyingCourseLocation__' ? null : value.name,
|
||||
"locationType": {
|
||||
"id": value.locationType.id,
|
||||
"type": "location-type"
|
||||
}
|
||||
};
|
||||
if (value.address.id) {
|
||||
Object.assign(body, {
|
||||
"address": {
|
||||
"id": value.address.id
|
||||
},
|
||||
})
|
||||
}
|
||||
postLocation(body)
|
||||
.then(
|
||||
location => hiddenLocation.value = location.id
|
||||
).catch(
|
||||
err => {
|
||||
console.log(err.message);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
hiddenLocation.value = value.id;
|
||||
}
|
||||
commit("updateLocation", value);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default store;
|
@@ -0,0 +1,218 @@
|
||||
import {toRaw} from "vue";
|
||||
import {
|
||||
addIdToValue,
|
||||
removeIdFromValue,
|
||||
mapEntity
|
||||
} from './utils';
|
||||
import {
|
||||
fetchCalendarRangeForUser,
|
||||
fetchCalendarRemoteForUser,
|
||||
} from './../api';
|
||||
import {datetimeToISO} from 'ChillMainAssets/chill/js/date';
|
||||
|
||||
/**
|
||||
* This will store a unique key for each value, and prevent to launch the same
|
||||
* request multiple times, when fetching user calendars.
|
||||
*
|
||||
* Actually, each time a user is added or removed, the methods "dateSet" is executed and this
|
||||
* sparkle a request by user to get the calendar data. When the calendar data is fetched, it is
|
||||
* immediatly added to the calendar which, in turn , launch the event dateSet and re-launch fetch
|
||||
* queries which has not yet ended. Storing the queries already executed prevent this loop.
|
||||
*
|
||||
* @type {Set<String>}
|
||||
*/
|
||||
const fetchings = new Set();
|
||||
|
||||
export default {
|
||||
setCurrentDatesView({commit, dispatch}, {start, end}) {
|
||||
commit('setCurrentDatesView', {start, end});
|
||||
|
||||
return dispatch('fetchCalendarEvents');
|
||||
},
|
||||
fetchCalendarEvents({state, getters, dispatch}) {
|
||||
if (state.currentView.start === null && state.currentView.end === null) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
let promises = [];
|
||||
for (const uid of state.currentView.users.keys()) {
|
||||
let unique = `${uid}, ${state.currentView.start.toISOString()}, ${state.currentView.end.toISOString()}`;
|
||||
|
||||
if (fetchings.has(unique)) {
|
||||
console.log('prevent from fetching for a user', unique);
|
||||
continue;
|
||||
}
|
||||
|
||||
fetchings.add(unique);
|
||||
|
||||
promises.push(
|
||||
dispatch(
|
||||
'fetchCalendarRangeForUser',
|
||||
{user: state.usersData.get(uid).user, start: state.currentView.start, end: state.currentView.end}
|
||||
)
|
||||
);
|
||||
promises.push(
|
||||
dispatch(
|
||||
'fetchCalendarRemotesForUser',
|
||||
{user: state.usersData.get(uid).user, start: state.currentView.start, end: state.currentView.end}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return Promise.all(promises);
|
||||
},
|
||||
fetchCalendarRangeForUser({commit, getters}, {user, start, end}) {
|
||||
if (!getters.isCalendarRangeLoadedForUser({user, start, end})) {
|
||||
return fetchCalendarRangeForUser(user, start, end).then((ranges) => {
|
||||
commit('addCalendarRangesForUser', {user, ranges, start, end});
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
}
|
||||
},
|
||||
fetchCalendarRemotesForUser({commit, getters}, {user, start, end}) {
|
||||
if (!getters.isCalendarRemoteLoadedForUser({user, start, end})) {
|
||||
return fetchCalendarRemoteForUser(user, start, end).then((remotes) => {
|
||||
commit('addCalendarRemotesForUser', {user, remotes, start, end});
|
||||
|
||||
return Promise.resolve();
|
||||
});
|
||||
}
|
||||
},
|
||||
addPersonsInvolved({commit, dispatch}, payload) {
|
||||
console.log('### action addPersonsInvolved', payload.result.type);
|
||||
console.log('### action addPersonsInvolved payload result', payload.result);
|
||||
switch (payload.result.type) {
|
||||
case 'person':
|
||||
let aPersons = document.getElementById("chill_activitybundle_activity_persons");
|
||||
aPersons.value = addIdToValue(aPersons.value, payload.result.id);
|
||||
break;
|
||||
case 'thirdparty':
|
||||
let aThirdParties = document.getElementById("chill_activitybundle_activity_professionals");
|
||||
aThirdParties.value = addIdToValue(aThirdParties.value, payload.result.id);
|
||||
break;
|
||||
case 'user':
|
||||
let aUsers = document.getElementById("chill_activitybundle_activity_users");
|
||||
aUsers.value = addIdToValue(aUsers.value, payload.result.id);
|
||||
commit('showUserOnCalendar', {user: payload.result, ranges: false, remotes: true});
|
||||
dispatch('fetchCalendarEvents');
|
||||
break;
|
||||
}
|
||||
;
|
||||
commit('addPersonsInvolved', payload);
|
||||
},
|
||||
removePersonInvolved({commit}, payload) {
|
||||
//console.log('### action removePersonInvolved', payload);
|
||||
switch (payload.type) {
|
||||
case 'person':
|
||||
let aPersons = document.getElementById("chill_activitybundle_activity_persons");
|
||||
aPersons.value = removeIdFromValue(aPersons.value, payload.id);
|
||||
break;
|
||||
case 'thirdparty':
|
||||
let aThirdParties = document.getElementById("chill_activitybundle_activity_professionals");
|
||||
aThirdParties.value = removeIdFromValue(aThirdParties.value, payload.id);
|
||||
break;
|
||||
case 'user':
|
||||
let aUsers = document.getElementById("chill_activitybundle_activity_users");
|
||||
aUsers.value = removeIdFromValue(aUsers.value, payload.id);
|
||||
break;
|
||||
}
|
||||
;
|
||||
commit('removePersonInvolved', payload);
|
||||
},
|
||||
|
||||
// Calendar
|
||||
/**
|
||||
* set event startDate and endDate.
|
||||
*
|
||||
* if the mainUser is different from "me", it will replace the mainUser
|
||||
*
|
||||
* @param commit
|
||||
* @param state
|
||||
* @param getters
|
||||
* @param start
|
||||
* @param end
|
||||
*/
|
||||
setEventTimes({commit, state, getters}, {start, end}) {
|
||||
console.log('### action createEvent', {start, end});
|
||||
let startDateInput = document.getElementById("chill_activitybundle_activity_startDate");
|
||||
startDateInput.value = null !== start ? datetimeToISO(start) : '';
|
||||
let endDateInput = document.getElementById("chill_activitybundle_activity_endDate");
|
||||
endDateInput.value = null !== end ? datetimeToISO(end) : '';
|
||||
let calendarRangeInput = document.getElementById("chill_activitybundle_activity_calendarRange");
|
||||
calendarRangeInput.value = "";
|
||||
|
||||
if (getters.getMainUser === null || getters.getMainUser.id !== state.me.id) {
|
||||
let mainUserInput = document.getElementById("chill_activitybundle_activity_mainUser");
|
||||
mainUserInput.value = state.me.id;
|
||||
commit('setMainUser', state.me);
|
||||
}
|
||||
|
||||
commit('setEventTimes', {start, end});
|
||||
},
|
||||
associateCalendarToRange({state, commit, dispatch}, {range}) {
|
||||
console.log('### action associateCAlendarToRange', range);
|
||||
let startDateInput = document.getElementById("chill_activitybundle_activity_startDate");
|
||||
startDateInput.value = null !== range ? datetimeToISO(range.start) : "";
|
||||
let endDateInput = document.getElementById("chill_activitybundle_activity_endDate");
|
||||
endDateInput.value = null !== range ? datetimeToISO(range.end) : "";
|
||||
let calendarRangeInput = document.getElementById("chill_activitybundle_activity_calendarRange");
|
||||
calendarRangeInput.value = null !== range ? Number(range.extendedProps.calendarRangeId) : "";
|
||||
|
||||
if (null !== range) {
|
||||
const userId = range.extendedProps.userId;
|
||||
if (state.activity.mainUser !== null && state.activity.mainUser.id !== userId) {
|
||||
dispatch('setMainUser', state.usersData.get(userId).user);
|
||||
|
||||
// TODO: remove persons involved with this user
|
||||
}
|
||||
}
|
||||
|
||||
commit('associateCalendarToRange', {range});
|
||||
return Promise.resolve();
|
||||
},
|
||||
setMainUser({commit, dispatch, state}, mainUser) {
|
||||
console.log('setMainUser', mainUser);
|
||||
|
||||
let mainUserInput = document.getElementById("chill_activitybundle_activity_mainUser");
|
||||
mainUserInput.value = Number(mainUser.id);
|
||||
|
||||
return dispatch('associateCalendarToRange', { range: null }).then(() => {
|
||||
commit('setMainUser', mainUser);
|
||||
});
|
||||
},
|
||||
|
||||
// Location
|
||||
updateLocation({commit}, value) {
|
||||
console.log('### action: updateLocation', value);
|
||||
let hiddenLocation = document.getElementById("chill_activitybundle_activity_location");
|
||||
if (value.onthefly) {
|
||||
const body = {
|
||||
"type": "location",
|
||||
"name": value.name === '__AccompanyingCourseLocation__' ? null : value.name,
|
||||
"locationType": {
|
||||
"id": value.locationType.id,
|
||||
"type": "location-type"
|
||||
}
|
||||
};
|
||||
if (value.address.id) {
|
||||
Object.assign(body, {
|
||||
"address": {
|
||||
"id": value.address.id
|
||||
},
|
||||
})
|
||||
}
|
||||
postLocation(body)
|
||||
.then(
|
||||
location => hiddenLocation.value = location.id
|
||||
).catch(
|
||||
err => {
|
||||
console.log(err.message);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
hiddenLocation.value = value.id;
|
||||
}
|
||||
commit("updateLocation", value);
|
||||
}
|
||||
}
|
@@ -0,0 +1,250 @@
|
||||
import {calendarRangeToFullCalendarEvent} from './utils';
|
||||
|
||||
export default {
|
||||
/**
|
||||
* get the main user of the event/Calendar
|
||||
*
|
||||
* @param state
|
||||
* @returns {*|null}
|
||||
*/
|
||||
getMainUser(state) {
|
||||
return state.activity.mainUser || null;
|
||||
},
|
||||
/**
|
||||
* return the date of the event/Calendar
|
||||
*
|
||||
* @param state
|
||||
* @returns {Date}
|
||||
*/
|
||||
getEventDate(state) {
|
||||
if (null === state.activity.start) {
|
||||
return new Date();
|
||||
}
|
||||
throw 'transform date to object ?';
|
||||
},
|
||||
/**
|
||||
* Compute the event sources to show on the FullCalendar
|
||||
*
|
||||
* @param state
|
||||
* @param getters
|
||||
* @returns {[]}
|
||||
*/
|
||||
getEventSources(state, getters) {
|
||||
let sources = [];
|
||||
|
||||
// current calendar
|
||||
if (state.activity.startDate !== null && state.activity.endDate !== null) {
|
||||
const s = {
|
||||
id: 'current',
|
||||
backgroundColor: '#3788d8',
|
||||
borderColor: '#3788d8',
|
||||
textColor: '#ffffff',
|
||||
events: [
|
||||
{
|
||||
title: "Rendez-vous",
|
||||
start: state.activity.startDate,
|
||||
end: state.activity.endDate,
|
||||
allDay: false,
|
||||
}
|
||||
],
|
||||
editable: state.activity.calendarRange === null,
|
||||
};
|
||||
|
||||
sources.push(s);
|
||||
}
|
||||
|
||||
for (const [userId, kinds] of state.currentView.users.entries()) {
|
||||
if (!state.usersData.has(userId)) {
|
||||
console.log('try to get events on a user which not exists', userId);
|
||||
continue;
|
||||
}
|
||||
|
||||
const userData = state.usersData.get(userId);
|
||||
|
||||
if (kinds.ranges && userData.calendarRanges.length > 0) {
|
||||
const s = {
|
||||
id: `ranges_${userId}`,
|
||||
events: userData.calendarRanges.filter(r => state.activity.calendarRange === null || r.calendarRangeId !== state.activity.calendarRange.calendarRangeId),
|
||||
color: userData.mainColor,
|
||||
backgroundColor: 'white',
|
||||
textColor: 'black',
|
||||
editable: false,
|
||||
};
|
||||
|
||||
sources.push(s);
|
||||
}
|
||||
|
||||
if (kinds.remotes && userData.remotes.length > 0) {
|
||||
const s = {
|
||||
'id': `remote_${userId}`,
|
||||
events: userData.remotes,
|
||||
color: userData.mainColor,
|
||||
textColor: 'black',
|
||||
editable: false,
|
||||
};
|
||||
|
||||
sources.push(s);
|
||||
}
|
||||
}
|
||||
|
||||
return sources;
|
||||
},
|
||||
getInitialDate(state) {
|
||||
return state.activity.startDate;
|
||||
},
|
||||
getInviteForUser: (state) => (user) => {
|
||||
return state.activity.invites.find(i => i.user.id === user.id);
|
||||
},
|
||||
/**
|
||||
* get the user data for a specific user
|
||||
*
|
||||
* @param state
|
||||
* @returns {function(*): unknown}
|
||||
*/
|
||||
getUserData: (state) => (user) => {
|
||||
return state.usersData.get(user.id);
|
||||
},
|
||||
getUserDataById: (state) => (userId) => {
|
||||
return state.usersData.get(userId);
|
||||
},
|
||||
/**
|
||||
* return true if the user has an entry in userData
|
||||
*
|
||||
* @param state
|
||||
* @returns {function(*): boolean}
|
||||
*/
|
||||
hasUserData: (state) => (user) => {
|
||||
return state.usersData.has(user.id);
|
||||
},
|
||||
hasUserDataById: (state) => (userId) => {
|
||||
return state.usersData.has(userId);
|
||||
},
|
||||
/**
|
||||
* return true if there was a fetch query for event between this date (start and end),
|
||||
* those date are included.
|
||||
*
|
||||
* @param state
|
||||
* @param getters
|
||||
* @returns {(function({user: *, start: *, end: *}): (boolean))|*}
|
||||
*/
|
||||
isCalendarRangeLoadedForUser: (state, getters) => ({user, start, end}) => {
|
||||
if (!getters.hasUserData(user)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let interval of getters.getUserData(user).calendarRangesLoaded) {
|
||||
if (start >= interval.start && end <= interval.end) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
/**
|
||||
* return true if there was a fetch query for event between this date (start and end),
|
||||
* those date are included.
|
||||
*
|
||||
* @param state
|
||||
* @param getters
|
||||
* @returns {(function({user: *, start: *, end: *}): (boolean))|*}
|
||||
*/
|
||||
isCalendarRemoteLoadedForUser: (state, getters) => ({user, start, end}) => {
|
||||
if (!getters.hasUserData(user)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let interval of getters.getUserData(user).remotesLoaded) {
|
||||
if (start >= interval.start && end <= interval.end) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
/**
|
||||
* return true if the user ranges are shown on calendar
|
||||
*
|
||||
* @param state
|
||||
* @returns boolean
|
||||
*/
|
||||
isRangeShownOnCalendarForUser: (state) => (user) => {
|
||||
const k = state.currentView.users.get(user.id);
|
||||
if (typeof k === 'undefined') {
|
||||
console.error('try to determinate if calendar range is shown and user is not in currentView');
|
||||
return false;
|
||||
}
|
||||
|
||||
return k.ranges;
|
||||
},
|
||||
|
||||
/**
|
||||
* return true if the user remote is shown on calendar
|
||||
* @param state
|
||||
* @returns boolean
|
||||
*/
|
||||
isRemoteShownOnCalendarForUser: (state) => (user) => {
|
||||
const k = state.currentView.users.get(user.id);
|
||||
if (typeof k === 'undefined') {
|
||||
console.error('try to determinate if calendar range is shown and user is not in currentView');
|
||||
return false;
|
||||
}
|
||||
|
||||
return k.remotes;
|
||||
},
|
||||
|
||||
|
||||
suggestedEntities(state, getters) {
|
||||
if (typeof (state.activity.accompanyingPeriod) === 'undefined') {
|
||||
return [];
|
||||
}
|
||||
const allEntities = [
|
||||
...getters.suggestedPersons,
|
||||
...getters.suggestedRequestor,
|
||||
...getters.suggestedUser,
|
||||
...getters.suggestedResources
|
||||
];
|
||||
const uniqueIds = [...new Set(allEntities.map(i => `${i.type}-${i.id}`))];
|
||||
return Array.from(uniqueIds, id => allEntities.filter(r => `${r.type}-${r.id}` === id)[0]);
|
||||
},
|
||||
suggestedPersons(state) {
|
||||
const existingPersonIds = state.activity.persons.map(p => p.id);
|
||||
return state.activity.accompanyingPeriod.participations
|
||||
.filter(p => p.endDate === null)
|
||||
.map(p => p.person)
|
||||
.filter(p => !existingPersonIds.includes(p.id))
|
||||
},
|
||||
suggestedRequestor(state) {
|
||||
if (state.activity.accompanyingPeriod.requestor === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const existingPersonIds = state.activity.persons.map(p => p.id);
|
||||
const existingThirdPartyIds = state.activity.thirdParties.map(p => p.id);
|
||||
return [state.activity.accompanyingPeriod.requestor]
|
||||
.filter(r =>
|
||||
(r.type === 'person' && !existingPersonIds.includes(r.id)) ||
|
||||
(r.type === 'thirdparty' && !existingThirdPartyIds.includes(r.id))
|
||||
);
|
||||
},
|
||||
suggestedUser(state) {
|
||||
if (null === state.activity.users) {
|
||||
return [];
|
||||
}
|
||||
const existingUserIds = state.activity.users.map(p => p.id);
|
||||
return [state.activity.accompanyingPeriod.user]
|
||||
.filter(
|
||||
u => u !== null && !existingUserIds.includes(u.id)
|
||||
);
|
||||
},
|
||||
suggestedResources(state) {
|
||||
const resources = state.activity.accompanyingPeriod.resources;
|
||||
const existingPersonIds = state.activity.persons.map(p => p.id);
|
||||
const existingThirdPartyIds = state.activity.thirdParties.map(p => p.id);
|
||||
return state.activity.accompanyingPeriod.resources
|
||||
.map(r => r.resource)
|
||||
.filter(r =>
|
||||
(r.type === 'person' && !existingPersonIds.includes(r.id)) ||
|
||||
(r.type === 'thirdparty' && !existingThirdPartyIds.includes(r.id))
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
import 'es6-promise/auto';
|
||||
import { createStore } from 'vuex';
|
||||
import { toRaw } from 'vue';
|
||||
import { postLocation } from 'ChillActivityAssets/vuejs/Activity/api';
|
||||
import getters from './getters';
|
||||
import actions from './actions';
|
||||
import mutations from './mutations';
|
||||
import { mapEntity } from './utils';
|
||||
import { whoami } from '../api';
|
||||
|
||||
import {
|
||||
getLocations, getLocationTypeByDefaultFor,
|
||||
getUserCurrentLocation
|
||||
} from "ChillActivityAssets/vuejs/Activity/api";
|
||||
|
||||
const debug = process.env.NODE_ENV !== 'production';
|
||||
|
||||
const store = createStore({
|
||||
strict: debug,
|
||||
state: {
|
||||
activity: mapEntity(window.entity), // activity is the calendar entity actually
|
||||
currentEvent: null,
|
||||
availableLocations: [],
|
||||
/**
|
||||
* the current user
|
||||
*/
|
||||
me: null,
|
||||
/**
|
||||
* store information about current view
|
||||
*/
|
||||
currentView: {
|
||||
start: null,
|
||||
end: null,
|
||||
users: new Map(),
|
||||
},
|
||||
/**
|
||||
* store a list of existing event, to avoid storing them twice
|
||||
*/
|
||||
existingEvents: new Set(),
|
||||
/**
|
||||
* store user data
|
||||
*/
|
||||
usersData: new Map(),
|
||||
},
|
||||
getters,
|
||||
mutations,
|
||||
actions,
|
||||
});
|
||||
|
||||
whoami().then(me => {
|
||||
store.commit('setWhoAmiI', me);
|
||||
});
|
||||
|
||||
if (null !== store.getters.getMainUser) {
|
||||
store.commit('showUserOnCalendar', {ranges: true, remotes: true, user: store.getters.getMainUser});
|
||||
}
|
||||
|
||||
for (let u of store.state.activity.users) {
|
||||
store.commit('showUserOnCalendar', {ranges: false, remotes: false, user: u});
|
||||
}
|
||||
|
||||
console.log('store', store);
|
||||
|
||||
export default store;
|
@@ -0,0 +1,176 @@
|
||||
import {
|
||||
createUserData,
|
||||
calendarRangeToFullCalendarEvent,
|
||||
remoteToFullCalendarEvent,
|
||||
} from './utils';
|
||||
|
||||
export default {
|
||||
setWhoAmiI(state, me) {
|
||||
state.me = me;
|
||||
},
|
||||
setCurrentDatesView(state, {start, end}) {
|
||||
state.currentView.start = start;
|
||||
state.currentView.end = end;
|
||||
},
|
||||
showUserOnCalendar(state, {user, ranges, remotes}) {
|
||||
if (!state.usersData.has(user.id)) {
|
||||
state.usersData.set(user.id, createUserData(user, state.usersData.size));
|
||||
}
|
||||
|
||||
const cur = state.currentView.users.get(user.id);
|
||||
|
||||
state.currentView.users.set(
|
||||
user.id,
|
||||
{
|
||||
ranges: typeof ranges !== 'undefined' ? ranges : cur.ranges,
|
||||
remotes: typeof remotes !== 'undefined' ? remotes : cur.remotes,
|
||||
}
|
||||
);
|
||||
},
|
||||
/**
|
||||
* Set the event start and end to the given start and end,
|
||||
* and remove eventually the calendar range.
|
||||
*
|
||||
* @param state
|
||||
* @param Date start
|
||||
* @param Date end
|
||||
*/
|
||||
setEventTimes(state, {start, end}) {
|
||||
state.activity.startDate = start;
|
||||
state.activity.endDate = end;
|
||||
state.activity.calendarRange = null;
|
||||
},
|
||||
/**
|
||||
* Set the event's start and end from the calendar range data,
|
||||
* and associate event to calendar range.
|
||||
*
|
||||
* @param state
|
||||
* @param range
|
||||
*/
|
||||
associateCalendarToRange(state, {range}) {
|
||||
console.log('associateCalendarToRange', range);
|
||||
|
||||
if (null === range) {
|
||||
state.activity.calendarRange = null;
|
||||
state.activity.startDate = null;
|
||||
state.activity.endDate = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('userId', range.extendedProps.userId);
|
||||
|
||||
const r = state.usersData.get(range.extendedProps.userId).calendarRanges
|
||||
.find(r => r.calendarRangeId === range.extendedProps.calendarRangeId);
|
||||
|
||||
if (typeof r === 'undefined') {
|
||||
throw Error('Could not find managed calendar range');
|
||||
}
|
||||
|
||||
console.log('range found', r);
|
||||
|
||||
state.activity.startDate = range.start;
|
||||
state.activity.endDate = range.end;
|
||||
state.activity.calendarRange = r;
|
||||
|
||||
console.log('activity', state.activity);
|
||||
},
|
||||
|
||||
setMainUser(state, user) {
|
||||
state.activity.mainUser = user;
|
||||
},
|
||||
|
||||
// ConcernedGroups
|
||||
addPersonsInvolved(state, payload) {
|
||||
//console.log('### mutation addPersonsInvolved', payload.result.type);
|
||||
switch (payload.result.type) {
|
||||
case 'person':
|
||||
state.activity.persons.push(payload.result);
|
||||
break;
|
||||
case 'thirdparty':
|
||||
state.activity.thirdParties.push(payload.result);
|
||||
break;
|
||||
case 'user':
|
||||
state.activity.users.push(payload.result);
|
||||
break;
|
||||
}
|
||||
;
|
||||
},
|
||||
removePersonInvolved(state, payload) {
|
||||
//console.log('### mutation removePersonInvolved', payload.type);
|
||||
switch (payload.type) {
|
||||
case 'person':
|
||||
state.activity.persons = state.activity.persons.filter(person => person !== payload);
|
||||
break;
|
||||
case 'thirdparty':
|
||||
state.activity.thirdParties = state.activity.thirdParties.filter(thirdparty => thirdparty !== payload);
|
||||
break;
|
||||
case 'user':
|
||||
state.activity.users = state.activity.users.filter(user => user !== payload);
|
||||
break;
|
||||
}
|
||||
;
|
||||
},
|
||||
/**
|
||||
* Add CalendarRange object for an user
|
||||
*
|
||||
* @param state
|
||||
* @param user
|
||||
* @param ranges
|
||||
* @param start
|
||||
* @param end
|
||||
*/
|
||||
addCalendarRangesForUser(state, {user, ranges, start, end}) {
|
||||
let userData;
|
||||
if (state.usersData.has(user.id)) {
|
||||
userData = state.usersData.get(user.id);
|
||||
} else {
|
||||
userData = createUserData(user, state.usersData.size);
|
||||
state.usersData.set(user.id, userData);
|
||||
}
|
||||
|
||||
const eventRanges = ranges
|
||||
.filter(r => !state.existingEvents.has(`range_${r.id}`))
|
||||
.map(r => {
|
||||
// add to existing ids
|
||||
state.existingEvents.add(`range_${r.id}`);
|
||||
return r;
|
||||
})
|
||||
.map(r => calendarRangeToFullCalendarEvent(r));
|
||||
|
||||
userData.calendarRanges = userData.calendarRanges.concat(eventRanges);
|
||||
userData.calendarRangesLoaded.push({start, end});
|
||||
},
|
||||
addCalendarRemotesForUser(state, {user, remotes, start, end}) {
|
||||
let userData;
|
||||
if (state.usersData.has(user.id)) {
|
||||
userData = state.usersData.get(user.id);
|
||||
} else {
|
||||
userData = createUserData(user, state.usersData.size);
|
||||
state.usersData.set(user.id, userData);
|
||||
}
|
||||
|
||||
const eventRemotes = remotes
|
||||
.filter(r => !state.existingEvents.has(`remote_${r.id}`))
|
||||
.map(r => {
|
||||
// add to existing ids
|
||||
state.existingEvents.add(`remote_${r.id}`);
|
||||
return r;
|
||||
})
|
||||
.map(r => remoteToFullCalendarEvent(r));
|
||||
|
||||
userData.remotes = userData.remotes.concat(eventRemotes);
|
||||
userData.remotesLoaded.push({start, end});
|
||||
},
|
||||
/*
|
||||
// Calendar
|
||||
setEvents(state, payload) {
|
||||
console.log(payload)
|
||||
state.currentEvent = {start: payload.start, end: payload.end}
|
||||
},*/
|
||||
// Location
|
||||
updateLocation(state, value) {
|
||||
console.log('### mutation: updateLocation', value);
|
||||
state.activity.location = value;
|
||||
}
|
||||
};
|
@@ -0,0 +1,85 @@
|
||||
import {COLORS} from '../const';
|
||||
import {ISOToDatetime} from 'ChillMainAssets/chill/js/date';
|
||||
|
||||
const addIdToValue = (string, id) => {
|
||||
let array = string ? string.split(',') : [];
|
||||
array.push(id.toString());
|
||||
let str = array.join();
|
||||
return str;
|
||||
};
|
||||
|
||||
const removeIdFromValue = (string, id) => {
|
||||
let array = string.split(',');
|
||||
array = array.filter(el => el !== id.toString());
|
||||
let str = array.join();
|
||||
return str;
|
||||
};
|
||||
|
||||
/*
|
||||
* Assign missing keys for the ConcernedGroups component
|
||||
*/
|
||||
const mapEntity = (entity) => {
|
||||
let calendar = { ...entity};
|
||||
Object.assign(calendar, {thirdParties: entity.professionals});
|
||||
|
||||
if (entity.startDate !== null ) {
|
||||
calendar.startDate = ISOToDatetime(entity.startDate.datetime);
|
||||
}
|
||||
if (entity.endDate !== null) {
|
||||
calendar.endDate = ISOToDatetime(entity.endDate.datetime);
|
||||
}
|
||||
|
||||
if (entity.calendarRange !== null) {
|
||||
calendar.calendarRange.calendarRangeId = entity.calendarRange.id;
|
||||
calendar.calendarRange.id = `range_${entity.calendarRange.id}`;
|
||||
}
|
||||
|
||||
return calendar;
|
||||
};
|
||||
|
||||
const createUserData = (user, colorIndex) => {
|
||||
const colorId = colorIndex % COLORS.length;
|
||||
console.log('colorId', colorId);
|
||||
return {
|
||||
user: user,
|
||||
calendarRanges: [],
|
||||
calendarRangesLoaded: [],
|
||||
remotes: [],
|
||||
remotesLoaded: [],
|
||||
mainColor: COLORS[colorId],
|
||||
}
|
||||
}
|
||||
|
||||
const calendarRangeToFullCalendarEvent = (entity) => {
|
||||
return {
|
||||
id: `range_${entity.id}`,
|
||||
title: "(" + entity.user.text + ")",
|
||||
start: entity.startDate.datetime8601,
|
||||
end: entity.endDate.datetime8601,
|
||||
allDay: false,
|
||||
userId: entity.user.id,
|
||||
calendarRangeId: entity.id,
|
||||
is: 'range',
|
||||
};
|
||||
}
|
||||
|
||||
const remoteToFullCalendarEvent = (entity) => {
|
||||
console.log(entity);
|
||||
return {
|
||||
id: `range_${entity.id}`,
|
||||
title: entity.title,
|
||||
start: entity.startDate.datetime8601,
|
||||
end: entity.endDate.datetime8601,
|
||||
allDay: false,
|
||||
is: 'remote',
|
||||
};
|
||||
}
|
||||
|
||||
export {
|
||||
addIdToValue,
|
||||
calendarRangeToFullCalendarEvent,
|
||||
removeIdFromValue,
|
||||
remoteToFullCalendarEvent,
|
||||
mapEntity,
|
||||
createUserData,
|
||||
};
|
@@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<div class="btn-group" role="group">
|
||||
<button id="btnGroupDrop1" type="button" class="btn btn-misc dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<template v-if="status === Statuses.PENDING">
|
||||
<span class="fa fa-hourglass"></span> {{ $t('Give_an_answer')}}
|
||||
</template>
|
||||
<template v-else-if="status === Statuses.ACCEPTED">
|
||||
<span class="fa fa-check"></span> {{ $t('Accepted')}}
|
||||
</template>
|
||||
<template v-else-if="status === Statuses.DECLINED">
|
||||
<span class="fa fa-times"></span> {{ $t('Declined')}}
|
||||
</template>
|
||||
<template v-else-if="status === Statuses.TENTATIVELY_ACCEPTED">
|
||||
<span class="fa fa-question"></span> {{ $t('Tentative')}}
|
||||
</template>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="btnGroupDrop1">
|
||||
<li v-if="status !== Statuses.ACCEPTED"><a class="dropdown-item" @click="changeStatus(Statuses.ACCEPTED)"><i class="fa fa-check" aria-hidden="true"></i> {{ $t('Accept') }}</a></li>
|
||||
<li v-if="status !== Statuses.DECLINED"><a class="dropdown-item" @click="changeStatus(Statuses.DECLINED)"><i class="fa fa-times" aria-hidden="true"></i> {{ $t('Decline') }}</a></li>
|
||||
<li v-if="status !== Statuses.TENTATIVELY_ACCEPTED"><a class="dropdown-item" @click="changeStatus(Statuses.TENTATIVELY_ACCEPTED)"><i class="fa fa-question"></i> {{ $t('Tentatively_accept') }}</a></li>
|
||||
<li v-if="status !== Statuses.PENDING"><a class="dropdown-item" @click="changeStatus(Statuses.PENDING)"><i class="fa fa-hourglass-o"></i> {{ $t('Set_pending') }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent, PropType} from 'vue';
|
||||
|
||||
const ACCEPTED = 'accepted';
|
||||
const DECLINED = 'declined';
|
||||
const PENDING = 'pending';
|
||||
const TENTATIVELY_ACCEPTED = 'tentative';
|
||||
|
||||
const i18n = {
|
||||
messages: {
|
||||
fr: {
|
||||
"Give_an_answer": "Répondre",
|
||||
"Accepted": "Accepté",
|
||||
"Declined": "Refusé",
|
||||
"Tentative": "Accepté provisoirement",
|
||||
"Accept": "Accepter",
|
||||
"Decline": "Refuser",
|
||||
"Tentatively_accept": "Accepter provisoirement",
|
||||
"Set_pending": "Ne pas répondre",
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: "Answer",
|
||||
i18n,
|
||||
props: {
|
||||
calendarId: { type: Number, required: true},
|
||||
status: {type: String as PropType<"accepted" | "declined" | "pending" | "tentative">, required: true},
|
||||
},
|
||||
emits: {
|
||||
statusChanged(payload: "accepted" | "declined" | "pending" | "tentative") {
|
||||
return true;
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
Statuses: {
|
||||
ACCEPTED,
|
||||
DECLINED,
|
||||
PENDING,
|
||||
TENTATIVELY_ACCEPTED,
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeStatus: function (newStatus: "accepted" | "declined" | "pending" | "tentative") {
|
||||
console.log('changeStatus', newStatus);
|
||||
const url = `/api/1.0/calendar/calendar/${this.$props.calendarId}/answer/${newStatus}.json`;
|
||||
window.fetch(url, {
|
||||
method: 'POST',
|
||||
}).then((r: Response) => {
|
||||
if (!r.ok) {
|
||||
console.error('could not confirm answer', newStatus);
|
||||
return;
|
||||
}
|
||||
console.log('answer sent', newStatus);
|
||||
this.$emit('statusChanged', newStatus);
|
||||
});
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
@@ -4,6 +4,7 @@
|
||||
* @returns {Promise} a promise containing all Calendar ranges objects
|
||||
*/
|
||||
const fetchCalendarRanges = () => {
|
||||
return Promise.resolve([]);
|
||||
const url = `/api/1.0/calendar/calendar-range-available.json`;
|
||||
return fetch(url)
|
||||
.then(response => {
|
||||
@@ -12,17 +13,9 @@ const fetchCalendarRanges = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const fetchCalendarRangesByUser = (userId, startDate = null, endDate = null) => {
|
||||
let url = '';
|
||||
|
||||
if (null !== startDate && null !== endDate) {
|
||||
url = `/api/1.0/calendar/calendar-range-available.json?user=${userId}&start=${startDate}&end=${endDate}`;
|
||||
} else if (null !== startDate) {
|
||||
url = `/api/1.0/calendar/calendar-range-available.json?user=${userId}&start=${startDate}`;
|
||||
} else {
|
||||
url = `/api/1.0/calendar/calendar-range-available.json?user=${userId}`;
|
||||
}
|
||||
|
||||
const fetchCalendarRangesByUser = (userId) => {
|
||||
return Promise.resolve([]);
|
||||
const url = `/api/1.0/calendar/calendar-range-available.json?user=${userId}`;
|
||||
return fetch(url)
|
||||
.then(response => {
|
||||
if (response.ok) { return response.json(); }
|
||||
@@ -36,6 +29,7 @@ const fetchCalendarRangesByUser = (userId, startDate = null, endDate = null) =>
|
||||
* @returns {Promise} a promise containing all Calendar objects
|
||||
*/
|
||||
const fetchCalendar = (mainUserId) => {
|
||||
return Promise.resolve([]);
|
||||
const url = `/api/1.0/calendar/calendar.json?main_user=${mainUserId}&item_per_page=1000`;
|
||||
return fetch(url)
|
||||
.then(response => {
|
||||
|
Reference in New Issue
Block a user