Merge branch 'calendar/finalization' into calendar_changes

This commit is contained in:
2022-06-17 17:29:05 +02:00
465 changed files with 15427 additions and 3084 deletions

View File

@@ -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>&nbsp;{{ 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>&nbsp;{{ 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>

View File

@@ -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">
&nbsp;<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">
&nbsp;<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>

View File

@@ -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,
};

View File

@@ -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,
};

View File

@@ -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
};

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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))
);
}
}

View File

@@ -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;

View File

@@ -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;
}
};

View File

@@ -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,
};

View File

@@ -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>

View File

@@ -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 => {