Fix calendar UI

pull/4334/head
Angel Fernando Quiroz Campos 3 years ago
parent 53374ed2d9
commit beec0d19f5
  1. 2
      assets/vue/App.vue
  2. 357
      assets/vue/components/ccalendarevent/Form.vue
  3. 48
      assets/vue/components/ccalendarevent/Info.vue
  4. 7
      assets/vue/components/ccalendarevent/Layout.vue
  5. 710
      assets/vue/views/ccalendarevent/List.vue

@ -139,6 +139,8 @@ store.dispatch('security/onRefresh', payload);
const flashMessageList = ref([]); const flashMessageList = ref([]);
provide('flashMessageList', flashMessageList);
onMounted(() => { onMounted(() => {
const app = document.getElementById('app'); const app = document.getElementById('app');

@ -1,218 +1,175 @@
<template> <template>
<q-form> <form
<v-container> action="#"
<v-row> class="flex flex-col gap-4"
<v-col> >
<q-input <div class="form__field">
id="item_title" <div class="p-float-label">
v-model="item.title" <InputText
:error="v$.item.title.$error" id="item_title"
:error-message="titleErrors" v-model="v$.item.title.$model"
:placeholder="$t('Title')" :class="{'p-invalid': v$.item.title.$invalid}"
@blur="v$.item.title.$touch()" />
@input="v$.item.title.$touch()" <label
v-t="'Title'"
for="item_title"
/>
</div>
<small
v-if="v$.item.title.$invalid || v$.item.title.$pending.$response"
v-t="v$.item.title.required.$message"
class="p-error"
/>
</div>
<div class="flex gap-4">
<div class="w-1/2 flex flex-col gap-4">
<div class="form__field">
<div class="p-float-label">
<Calendar
id="start_date"
v-model="item.startDate"
:show-icon="true"
:show-time="true"
/> />
<label
<q-input v-model="item.startDate" filled> v-t="'From'"
<template v-slot:prepend> for="start_date"
<q-icon class="cursor-pointer" name="event">
<q-popup-proxy transition-hide="scale" transition-show="scale">
<q-date v-model="item.startDate" mask="YYYY-MM-DD HH:mm">
<div class="row items-center justify-end">
<q-btn v-close-popup color="primary" flat label="Close"/>
</div>
</q-date>
</q-popup-proxy>
</q-icon>
</template>
<template v-slot:append>
<q-icon class="cursor-pointer" name="access_time">
<q-popup-proxy transition-hide="scale" transition-show="scale">
<q-time v-model="item.startDate" format24h mask="YYYY-MM-DD HH:mm">
<div class="row items-center justify-end">
<q-btn v-close-popup color="primary" flat label="Close"/>
</div>
</q-time>
</q-popup-proxy>
</q-icon>
</template>
</q-input>
<q-input v-model="item.endDate" filled>
<template v-slot:prepend>
<q-icon class="cursor-pointer" name="event">
<q-popup-proxy transition-hide="scale" transition-show="scale">
<q-date v-model="item.endDate" mask="YYYY-MM-DD HH:mm">
<div class="row items-center justify-end">
<q-btn v-close-popup color="primary" flat label="Close"/>
</div>
</q-date>
</q-popup-proxy>
</q-icon>
</template>
<template v-slot:append>
<q-icon class="cursor-pointer" name="access_time">
<q-popup-proxy transition-hide="scale" transition-show="scale">
<q-time v-model="item.endDate" format24h mask="YYYY-MM-DD HH:mm">
<div class="row items-center justify-end">
<q-btn v-close-popup color="primary" flat label="Close"/>
</div>
</q-time>
</q-popup-proxy>
</q-icon>
</template>
</q-input>
<!-- <q-input-->
<!-- v-model="item.content"-->
<!-- :error="v$.item.content.$error"-->
<!-- :error-message="contentErrors"-->
<!-- :placeholder="$t('Content')"-->
<!-- type="textarea"-->
<!-- @blur="v$.item.content.$touch()"-->
<!-- @input="v$.item.content.$touch()"-->
<!-- />-->
<TinyEditor
v-model="item.content"
required
:init="{
skin_url: '/build/libs/tinymce/skins/ui/oxide',
content_css: '/build/libs/tinymce/skins/content/default/content.css',
branding: false,
relative_urls: false,
height: 250,
toolbar_mode: 'sliding',
file_picker_callback : browser,
autosave_ask_before_unload: true,
plugins: [
'advlist autolink lists link image charmap print preview anchor',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table paste wordcount emoticons'
],
toolbar: 'undo redo | bold italic underline strikethrough | insertfile image media template link | fontselect fontsizeselect formatselect | alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist | forecolor backcolor removeformat | pagebreak | charmap emoticons | fullscreen preview save print | code codesample | ltr rtl',
}"
/> />
</v-col> </div>
</div>
<v-col>
<div <div class="form__field">
v-text="$t('Invitees')" <div class="p-float-label">
class="text-h6" <Calendar
> id="end_date"
</div> v-model="item.endDate"
:show-icon="true"
<EditLinks :show-time="true"
:item="item"
:show-status="false"
:edit-status="false"
:links-type="linksType"
/> />
<q-checkbox <label
v-model="item.collective" v-t="'From'"
:label="$t('Is it editable by the invitees?')" for="end_date"
/> />
</v-col> </div>
</div>
<slot></slot>
</v-row> <TinyEditor
</v-container> v-model="item.content"
</q-form> :init="{
skin_url: '/build/libs/tinymce/skins/ui/oxide',
content_css: '/build/libs/tinymce/skins/content/default/content.css',
branding: false,
relative_urls: false,
height: 250,
toolbar_mode: 'sliding',
file_picker_callback : browser,
autosave_ask_before_unload: true,
plugins: [
'advlist autolink lists link image charmap print preview anchor',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table paste wordcount emoticons'
],
toolbar: 'undo redo | bold italic underline strikethrough | insertfile image media template link | fontselect fontsizeselect formatselect | alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist | forecolor backcolor removeformat | pagebreak | charmap emoticons | fullscreen preview save print | code codesample | ltr rtl',
}"
required
/>
</div>
<div class="w-1/2 flex flex-col gap-4">
<div
v-t="'Invitees'"
class="text-h6"
/>
<EditLinks
:edit-status="false"
:item="item"
:links-type="linksType"
:show-status="false"
/>
<div class="form__field">
<Checkbox
id="is_collective"
v-model="item.collective"
/>
<label
v-t="'Is it editable by the invitees?'"
for="is_collective"
/>
</div>
</div>
</div>
<slot />
</form>
</template> </template>
<script> <script setup>
import has from 'lodash/has'; import { computed, ref } from 'vue';
import useVuelidate from '@vuelidate/core'; import { useStore } from 'vuex';
import {required} from '@vuelidate/validators'; import { useVuelidate } from '@vuelidate/core';
import EditLinks from "../resource_links/EditLinks.vue"; import { required } from '@vuelidate/validators';
import {computed, ref} from "vue"; import InputText from 'primevue/inputtext';
import {useStore} from "vuex"; import Calendar from 'primevue/calendar';
import EditLinks from '../resource_links/EditLinks.vue';
export default { import Checkbox from 'primevue/checkbox';
name: 'CCalendarEventForm',
components: { const store = useStore();
EditLinks
// eslint-disable-next-line no-undef
const props = defineProps({
values: {
type: Object,
required: true,
},
errors: {
type: Object,
default: () => {},
}, },
setup() { initialValues: {
const store = useStore(); type: Object,
const linksType = ref('users'); default: () => {},
const isCurrentTeacher = computed(() => store.getters['security/isCurrentTeacher']);
const isAdmin = computed(() => store.getters['security/isAdmin']);
if (!isAdmin.value) {
if (isCurrentTeacher.value) {
linksType.value = 'course_students';
} else {
linksType.value = 'user_rel_users';
}
}
return {
v$: useVuelidate(),
linksType
}
}, },
props: { });
values: {
type: Object, const item = computed(() => props.initialValues || props.values);
required: true
const rules = computed(() => ({
item: {
title: {
required,
}, },
errors: { content: {
type: Object, required,
default: () => {
}
}, },
initialValues: { startDate: {
type: Object, required,
default: () => {
}
}, },
}, endDate: {
data() { required,
return {
title: null,
content: null,
parentResourceNodeId: null,
collective: null,
};
},
computed: {
item() {
return this.initialValues || this.values;
}, },
titleErrors() { collective: {}
const errors = []; }
if (!this.v$.item.title.$dirty) return errors; }));
has(this.violations, 'title') && errors.push(this.violations.title);
if (this.v$.item.title.required) { const v$ = useVuelidate(rules, { item });
return this.$t('Field is required')
}
return errors; // eslint-disable-next-line no-undef
}, defineExpose({
v$,
})
violations() { const linksType = ref('users');
return this.errors || {}; const isCurrentTeacher = computed(() => store.getters['security/isCurrentTeacher']);
} const isAdmin = computed(() => store.getters['security/isAdmin']);
},
validations: { if (!isAdmin.value) {
item: { if (isCurrentTeacher.value) {
title: { linksType.value = 'course_students';
required, } else {
}, linksType.value = 'user_rel_users';
content: {
required,
},
startDate: {
required,
},
endDate: {
required,
},
}
} }
}; }
</script> </script>

@ -1,33 +1,37 @@
<template> <template>
<h5>{{ event.title }}</h5> <div class="flex flex-col gap-4">
{{ $filters.abbreviatedDatetime(event.startDate) }} <h5 v-text="event.title" />
<p v-if="event.endDate">
{{ $filters.abbreviatedDatetime(event.endDate) }}
</p>
<hr class="my-2"> <p v-text="useAbbreviatedDatetime(event.startDate)" />
<div class="mb-3" v-html="event.content" /> <p
v-if="event.endDate"
v-text="useAbbreviatedDatetime(event.endDate)"
/>
<h6 class="text-h5"> {{ $t('Invitees') }}</h6> <hr>
<ShowLinks <div v-html="event.content" />
<h6 v-t="'Invitees'" />
<ShowLinks
:item="event" :item="event"
:show-status="false" :show-status="false"
/> />
</div>
</template> </template>
<script> <script setup>
import ShowLinks from "../resource_links/ShowLinks"; import { useAbbreviatedDatetime } from '../../composables/formatDate.js';
export default {
name: "CCalendarEventInfo", import ShowLinks from '../resource_links/ShowLinks';
components: {ShowLinks},
props: { // eslint-disable-next-line no-undef
event: { defineProps({
type: Object, event: {
required: true type: Object,
} required: true
} }
} });
</script> </script>

@ -1,9 +1,6 @@
<template> <template>
<router-view></router-view> <router-view />
</template> </template>
<script> <script setup>
export default {
name: 'CCalendarEventLayout'
}
</script> </script>

@ -1,399 +1,401 @@
<template> <template>
<div> <div>
<FullCalendar ref="cal" :options="calendarOptions"/> <FullCalendar
ref="cal"
:options="calendarOptions"
/>
<Loading :visible="isLoading"/> <Loading :visible="isLoading" />
<!-- Add form--> <!-- Add form-->
<q-dialog v-model="dialog" persistent> <Dialog
<q-card style="min-width: 800px"> v-model:visible="dialog"
<q-card-section> :header="item['@id'] ? t('Edit event') : t('Add event')"
<div class="text-h6">{{ item['@id'] ? $t('Edit event') : $t('Add event') }}</div> :modal="true"
</q-card-section> >
<CCalendarEventForm
<q-card-section class="q-pt-none"> v-if="dialog"
<CCalendarEventForm ref="createForm"
v-if="dialog" :values="item"
ref="createForm" />
:values="item" <template #footer>
> <Button
</CCalendarEventForm> :label="t('Cancel')"
</q-card-section> class="p-button-outlined p-button-plain"
icon="pi pi-times"
<q-card-actions align="right" class="text-primary"> @click="dialog = false"
<q-btn v-close-popup flat :label="$t('Cancel')" /> />
<q-btn :label="item['@id'] ? $t('Edit') : $t('Add') " flat @click="onCreateEventForm"/> <Button
</q-card-actions> :label="item['@id'] ? t('Edit') : t('Add')"
</q-card> class="p-button-secondary"
</q-dialog> @click="onCreateEventForm"
/>
</template>
</Dialog>
<!-- Show form--> <!-- Show form-->
<q-dialog v-model="dialogShow" persistent> <Dialog
<q-card style="min-width: 500px"> v-model:visible="dialogShow"
<q-card-section> :header="t('Event')"
<CCalendarEventInfo :event="item" /> :modal="true"
</q-card-section> >
<q-card-actions align="right" class="text-primary"> <CCalendarEventInfo :event="item" />
<q-btn color="primary" flat no-caps :label="$t('Delete')" @click="confirmDelete"/>
<q-btn v-if="isEventEditable" color="primary" flat no-caps :label="$t('Edit')" @click="dialog = true"/> <template #footer>
<q-btn v-close-popup flat no-caps :label="$t('Close')" /> <Button
</q-card-actions> :label="t('Cancel')"
</q-card> class="p-button-outlined p-button-plain"
</q-dialog> icon="pi pi-times"
@click="dialogShow = false"
/>
<Button
:label="t('Delete')"
class="p-button-outlined p-button-danger"
icon="pi pi-trash"
@click="confirmDelete"
/>
<Button
v-if="isEventEditable"
:label="t('Edit')"
class="p-button-secondary"
@click="dialog = true"
/>
</template>
</Dialog>
<!-- Show form--> <!-- Show form-->
<q-dialog v-model="showSessionDialog"> <Dialog
<q-card> v-model:visible="sessionState.showSessionDialog"
<q-card-section class="row items-center q-pb-none" icon="mdi-book-open"> :header="t('Session')"
<div class="text-h6">{{ sessionAsEvent.title }}</div> :modal="true"
<q-space/> >
<q-btn v-close-popup dense flat icon="close" round/> <div class="flex flex-col gap-4">
</q-card-section> <h5>{{ sessionState.sessionAsEvent.title }}</h5>
<p
<q-card-section> v-if="sessionState.sessionAsEvent.start"
<div v-if="sessionAsEvent.start"> v-t="{ path: 'From: {date}', args: { 'date': useAbbreviatedDatetime(sessionState.sessionAsEvent.start) } }"
<q-icon name="event"/> />
{{ $t('From:') }} <p
{{ $filters.abbreviatedDatetime(sessionAsEvent.start) }} v-if="sessionState.sessionAsEvent.end"
</div> v-t="{ path: 'Until: {date}', args: { 'date': useAbbreviatedDatetime(sessionState.sessionAsEvent.end) } }"
/>
<div v-if="sessionAsEvent.end"> </div>
<q-icon name="event"/>
{{ $t('Until:') }} <template #footer>
{{ $filters.abbreviatedDatetime(sessionAsEvent.end) }} <a
</div> v-t="'Go to session'"
</q-card-section> :href="`/sessions/${sessionState.sessionAsEvent.id}/about`"
<q-card-actions align="right" class="text-primary"> class="btn btn--secondary"
<q-btn color="primary" flat :label="$t('Go to session')" type="a" />
:href="`/sessions/${sessionAsEvent.id}/about`" /> </template>
</q-card-actions> </Dialog>
</q-card>
</q-dialog>
</div> </div>
</template> </template>
<script> <script setup>
import {mapActions, mapGetters, useStore} from 'vuex'; import { computed, inject, reactive, ref, watch } from 'vue';
import {mapFields} from 'vuex-map-fields'; import { useStore } from 'vuex';
import Loading from '../../components/Loading.vue'; import { useRoute } from 'vue-router';
import Toolbar from '../../components/Toolbar.vue'; import { useI18n } from 'vue-i18n';
import {computed, reactive, ref, toRefs, onMounted} from "vue"; import axios from 'axios';
import { useConfirm } from 'primevue/useconfirm';
import { useAbbreviatedDatetime } from '../../composables/formatDate.js';
//import '@fullcalendar/core/vdom' // solve problem with Vite import Loading from '../../components/Loading.vue';
import FullCalendar from '@fullcalendar/vue3'; import FullCalendar from '@fullcalendar/vue3';
import dayGridPlugin from '@fullcalendar/daygrid'; import dayGridPlugin from '@fullcalendar/daygrid';
import interactionPlugin from '@fullcalendar/interaction'; import interactionPlugin from '@fullcalendar/interaction';
import timeGridPlugin from '@fullcalendar/timegrid'; import timeGridPlugin from '@fullcalendar/timegrid';
import axios from "axios"; import CCalendarEventForm from '../../components/ccalendarevent/Form.vue';
import CCalendarEventForm from "../../components/ccalendarevent/Form.vue"; import CCalendarEventInfo from '../../components/ccalendarevent/Info';
import CreateMixin from "../../mixins/CreateMixin"; import { ENTRYPOINT } from '../../config/entrypoint';
import {useRoute, useRouter} from "vue-router";
import {useQuasar} from 'quasar';
import CCalendarEventInfo from "../../components/ccalendarevent/Info";
import {ENTRYPOINT} from "../../config/entrypoint";
import {useI18n} from "vue-i18n";
import allLocales from '@fullcalendar/core/locales-all'; import allLocales from '@fullcalendar/core/locales-all';
import toInteger from "lodash/toInteger"; import toInteger from 'lodash/toInteger';
const servicePrefix = 'CCalendarEvent'; import Dialog from 'primevue/dialog';
import Button from 'primevue/button';
export default {
name: 'CCalendarEventList', const store = useStore();
components: { const route = useRoute();
CCalendarEventInfo, const confirm = useConfirm();
CCalendarEventForm,
Loading, const item = ref({});
Toolbar, const dialog = ref(false);
FullCalendar const dialogShow = ref(false);
const isEventEditable = ref(false);
const currentUser = computed(() => store.getters['security/getUser']);
const { t, locale } = useI18n();
let currentEvent = null;
const sessionState = reactive({
sessionAsEvent: {
id: '',
title: '',
start: '',
end: '',
extendedProps: {},
}, },
showSessionDialog: false
mixins: [CreateMixin], });
//mixins: [ShowMixin],
setup() { const cid = toInteger(route.query.cid);
const $q = useQuasar(); const sid = toInteger(route.query.sid);
const gid = toInteger(route.query.gid);
const calendarOptions = ref([]);
const item = ref({}); if (cid) {
const dialog = ref(false); let courseIri = '/api/courses/' + cid;
const dialogShow = ref(false); store.dispatch('course/findCourse', { id: courseIri });
const isEventEditable = ref(false); }
const store = useStore(); async function getCalendarEvents ({ startStr, endStr }) {
const route = useRoute(); const calendarEvents = await axios.get(
const currentUser = computed(() => store.getters['security/getUser']); ENTRYPOINT + 'c_calendar_events',
const { t, locale } = useI18n(); {
params: {
let currentEvent = null; 'startDate': startStr,
'endDate': endStr,
const sessionState = reactive({ // 'startDate[after]': startStr,
sessionAsEvent: { // 'startDate[before]': startStr,
id: '', //'startDate[between]': startStr+'..'+endStr,
title: '', 'cid': cid,
start: '', 'sid': sid,
end: '', 'gid': gid,
extendedProps: {}, }
},
showSessionDialog: false
});
const cid = toInteger(route.query.cid);
const sid = toInteger(route.query.sid);
const gid = toInteger(route.query.gid);
if (cid) {
let courseIri = '/api/courses/' + cid;
store.dispatch('course/findCourse', { id: courseIri });
} }
);
function onCreated(item) {
//showNotification(t('Updated')); return calendarEvents.data['hydra:member'].map(event => ({
reFetch(); ...event,
start: event.startDate,
end: event.endDate,
})
);
}
async function getSessions ({ startStr, endStr }) {
if ('true' !== window.config['agenda.personal_calendar_show_sessions_occupation']) {
return [];
}
const sessions = await axios.get(
ENTRYPOINT + `session_rel_users`,
{
params: {
'user': currentUser.value['@id'],
'displayStartDate[after]': startStr,
'displayEndDate[before]': endStr,
'relationType': 3
}
} }
);
return sessions.data['hydra:member'].map(sessionRelUser => ({
...sessionRelUser.session,
title: sessionRelUser.session.name,
start: sessionRelUser.session.displayStartDate,
end: sessionRelUser.session.displayEndDate,
})
);
}
// @todo fix locale connection between fullcalendar + chamilo
if ('en_US' === locale.value) {
locale.value = 'en';
}
if ('fr_FR' === locale.value) {
locale.value = 'fr';
}
if ('pl_PL' === locale.value) {
locale.value = 'pl';
}
const calendarOptions = ref({
plugins: [
dayGridPlugin,
timeGridPlugin,
interactionPlugin
],
locales: allLocales,
locale: locale.value,
customButtons: {
addEvent: {
text: t('Add event'),
click: function () {
item.value = {};
item.value['parentResourceNodeId'] = currentUser.value.resourceNode['id'];
item.value['collective'] = false;
async function getCalendarEvents({startStr, endStr}) { dialog.value = true;
const calendarEvents = await axios.get(ENTRYPOINT + 'c_calendar_events', { }
params: { }
'startDate': startStr, },
'endDate': endStr, headerToolbar: {
// 'startDate[after]': startStr, left: 'prev,next today,addEvent',
// 'startDate[before]': startStr, center: 'title',
//'startDate[between]': startStr+'..'+endStr, right: 'dayGridMonth,timeGridWeek,timeGridDay'
'cid': cid, },
'sid': sid, nowIndicator: true,
'gid': gid, initialView: 'dayGridMonth',
} startParam: 'startDate[after]',
}); endParam: 'endDate[before]',
selectable: true,
return calendarEvents.data['hydra:member']; eventClick (EventClickArg) {
let event = EventClickArg.event.toPlainObject();
if (event.extendedProps['@type'] && event.extendedProps['@type'] === 'Session') {
sessionState.sessionAsEvent = event;
sessionState.showSessionDialog = true;
EventClickArg.jsEvent.preventDefault();
return;
} }
async function getSessions({startStr, endStr}) { currentEvent = event;
if ('true' !== window.config['agenda.personal_calendar_show_sessions_occupation']) {
return [];
}
const sessions = await axios.get(ENTRYPOINT + `users/${currentUser.value['id']}/sessions_rel_users`, { item.value = { ...event.extendedProps };
params: {
'displayStartDate[after]': startStr,
'displayEndDate[before]': endStr
}
});
return sessions.data['hydra:member']; item.value['title'] = event.title;
} item.value['startDate'] = event.start;
item.value['endDate'] = event.end;
item.value['parentResourceNodeId'] = event.extendedProps.resourceNode.creator.id;
// @todo fix locale connection between fullcalendar + chamilo isEventEditable.value = item.value['parentResourceNodeId'] === currentUser.value['id'];
if ('en_US' === locale.value) { if (!isEventEditable.value
locale.value = 'en'; && event.extendedProps.collective
} && event.extendedProps.resourceLinkListFromEntity
) {
const resourceLink = event.extendedProps.resourceLinkListFromEntity.find(linkEntity => linkEntity.user.id === currentUser.value.id);
if ('fr_FR' === locale.value) { if (resourceLink) {
locale.value = 'fr'; isEventEditable.value = true;
}
} }
if ('pl_PL' === locale.value) { dialogShow.value = true;
locale.value = 'pl'; },
} dateClick (info) {
item.value = {};
item.value['parentResourceNodeId'] = currentUser.value.resourceNode['id'];
item.value['collective'] = false;
item.value['allDay'] = info.allDay;
item.value['startDate'] = info.startStr;
item.value['endDate'] = info.endStr;
dialog.value = true;
},
select (info) {
item.value = {};
item.value['parentResourceNodeId'] = currentUser.value.resourceNode['id'];
item.value['collective'] = false;
item.value['allDay'] = info.allDay;
item.value['startDate'] = info.startStr;
item.value['endDate'] = info.endStr;
dialog.value = true;
},
events (info, successCallback) {
Promise
.all([getCalendarEvents(info), getSessions(info)])
.then(values => {
const events = values[0].concat(values[1]);
calendarOptions.value = { successCallback(events);
plugins: [ });
dayGridPlugin, },
timeGridPlugin, });
interactionPlugin
], const cal = ref(null);
locales: allLocales,
locale: locale.value, function reFetch () {
customButtons: { cal.value.getApi().refetchEvents();
addEvent: { }
text: t('Add event'),
click: function () { function confirmDelete () {
item.value = {}; confirm.require({
item.value['parentResourceNodeId'] = currentUser.value.resourceNode['id']; message: t('Are you sure you want to delete this event?'),
item.value['collective'] = false; header: t('Delete'),
icon: 'pi pi-exclamation-triangle',
dialog.value = true; acceptClass: 'p-button-danger',
} rejectClass: 'p-button-plain p-button-outlined',
} accept () {
}, if (item.value['parentResourceNodeId'] === currentUser.value['id']) {
headerToolbar: { store.dispatch('ccalendarevent/del', item.value);
left: 'prev,next today,addEvent',
center: 'title', dialogShow.value = false;
right: 'dayGridMonth,timeGridWeek,timeGridDay' dialog.value = false;
}, reFetch();
nowIndicator: true, } else {
initialView: 'dayGridMonth', let filteredLinks = item.value['resourceLinkListFromEntity']
startParam: "startDate[after]", .filter(resourceLinkFromEntity => resourceLinkFromEntity['user']['id'] === currentUser.value['id']);
endParam: 'endDate[before]',
selectable: true, if (filteredLinks.length > 0) {
eventClick(EventClickArg) { store.dispatch('resourcelink/del', { '@id': `/api/resource_links/${filteredLinks[0]['id']}` });
let event = EventClickArg.event.toPlainObject();
currentEvent.remove();
if (event.extendedProps['@type'] && event.extendedProps['@type'] === 'Session') { dialogShow.value = false;
sessionState.sessionAsEvent = event; dialog.value = false;
sessionState.showSessionDialog = true; reFetch();
EventClickArg.jsEvent.preventDefault();
return;
} }
}
},
});
}
currentEvent = event; const isLoading = computed(() => store.getters['ccalendarevent/isLoading']);
item.value = {...event.extendedProps};
item.value['title'] = event.title;
item.value['startDate'] = event.start;
item.value['endDate'] = event.end;
item.value['parentResourceNodeId'] = event.extendedProps.resourceNode.creator.id;
isEventEditable.value = item.value['parentResourceNodeId'] === currentUser.value['id']; const createForm = ref(null);
if (!isEventEditable.value function onCreateEventForm () {
&& event.extendedProps.collective if (createForm.value.v$.$invalid) {
&& event.extendedProps.resourceLinkListFromEntity return;
) { }
const resourceLink = event.extendedProps.resourceLinkListFromEntity.find(linkEntity => linkEntity.user.id === currentUser.value.id);
if (resourceLink) { let itemModel = createForm.value.v$.item.$model;
isEventEditable.value = true;
}
}
dialogShow.value = true; if (itemModel['@id']) {
}, store.dispatch('ccalendarevent/update', itemModel);
dateClick(info) { } else {
item.value = {}; store.dispatch('ccalendarevent/create', itemModel);
item.value['parentResourceNodeId'] = currentUser.value.resourceNode['id'];
item.value['collective'] = false;
item.value['allDay'] = info.allDay;
item.value['startDate'] = info.startStr;
item.value['endDate'] = info.endStr;
dialog.value = true; }
},
select(info) {
item.value = {};
item.value['parentResourceNodeId'] = currentUser.value.resourceNode['id'];
item.value['collective'] = false;
item.value['allDay'] = info.allDay;
item.value['startDate'] = info.startStr;
item.value['endDate'] = info.endStr;
dialog.value = true; dialog.value = false;
}, }
events(info, successCallback, failureCallback) {
Promise
.all([getCalendarEvents(info), getSessions(info)])
.then(values => {
const calendarEvents = Array.prototype.slice.call(values[0])
.map(event => ({
...event,
start: event.startDate,
end: event.endDate,
}));
const sessionEvents = values[1].map(sessionRelUser => (
{
...sessionRelUser.session,
title: sessionRelUser.session.name,
start: sessionRelUser.session.displayStartDate,
end: sessionRelUser.session.displayEndDate,
}
));
const events = [...calendarEvents, ...sessionEvents];
successCallback(events);
});
},
}
const cal = ref(null); const flashMessageList = inject('flashMessageList');
function reFetch() {
const calendarApi = cal.value.getApi();
calendarApi.refetchEvents();
}
function confirmDelete() { watch(
$q.dialog({ () => store.state.ccalendarevent.created,
title: 'Delete', (created) => {
message: 'Are you sure you want to delete this event?', flashMessageList.value.push({
persistent: true, severity: 'success',
cancel: true detail: t(
}) '{resource} created',
.onOk(function () { { 'resource': created.resourceNode.title }
if (item.value['parentResourceNodeId'] === currentUser.value['id']) { ),
store.dispatch('ccalendarevent/del', item.value); });
dialogShow.value = false;
dialog.value = false;
reFetch();
} else {
let filteredLinks = item.value['resourceLinkListFromEntity']
.filter(resourceLinkFromEntity => resourceLinkFromEntity['user']['id'] === currentUser.value['id']);
if (filteredLinks.length > 0) {
store.dispatch('resourcelink/del', {'@id': `/api/resource_links/${filteredLinks[0]['id']}`})
currentEvent.remove();
dialogShow.value = false;
dialog.value = false;
reFetch();
}
}
});
}
return { reFetch();
cal, }
onCreated, );
calendarOptions,
dialog, watch(
item, () => store.state.ccalendarevent.updated,
dialogShow, (updated) => {
reFetch, flashMessageList.value.push({
isEventEditable, severity: 'success',
confirmDelete, detail: t(
...toRefs(sessionState), '{resource} updated',
}; { 'resource': updated.resourceNode.title }
}, ),
computed: { });
...mapFields('ccalendarevent', {
isLoading: 'isLoading',
created: 'created',
violations: 'violations',
}),
...mapGetters('ccalendarevent', ['find']),
...mapGetters({
'isAuthenticated': 'security/isAuthenticated',
'isAdmin': 'security/isAdmin',
'isCurrentTeacher': 'security/isCurrentTeacher',
}),
},
methods: {
onCreateEventForm() {
const createForm = this.$refs.createForm;
createForm.v$.$touch();
if (!createForm.v$.$invalid) {
let itemModel = createForm.v$.item.$model;
if (itemModel['@id']) {
this.updateItem(itemModel);
} else {
this.create(itemModel);
}
this.reFetch(); reFetch();
this.dialog = false; }
} );
},
...mapActions('ccalendarevent', {
create: 'create',
deleteItem: 'del',
reset: 'resetShow',
retrieve: 'loadWithQuery',
updateItem: 'update'
}),
},
servicePrefix
};
</script> </script>

Loading…
Cancel
Save