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([]);
provide('flashMessageList', flashMessageList);
onMounted(() => {
const app = document.getElementById('app');

@ -1,218 +1,175 @@
<template>
<q-form>
<v-container>
<v-row>
<v-col>
<q-input
id="item_title"
v-model="item.title"
:error="v$.item.title.$error"
:error-message="titleErrors"
:placeholder="$t('Title')"
@blur="v$.item.title.$touch()"
@input="v$.item.title.$touch()"
<form
action="#"
class="flex flex-col gap-4"
>
<div class="form__field">
<div class="p-float-label">
<InputText
id="item_title"
v-model="v$.item.title.$model"
:class="{'p-invalid': v$.item.title.$invalid}"
/>
<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"
/>
<q-input v-model="item.startDate" 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.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',
}"
<label
v-t="'From'"
for="start_date"
/>
</v-col>
<v-col>
<div
v-text="$t('Invitees')"
class="text-h6"
>
</div>
<EditLinks
:item="item"
:show-status="false"
:edit-status="false"
:links-type="linksType"
</div>
</div>
<div class="form__field">
<div class="p-float-label">
<Calendar
id="end_date"
v-model="item.endDate"
:show-icon="true"
:show-time="true"
/>
<q-checkbox
v-model="item.collective"
:label="$t('Is it editable by the invitees?')"
<label
v-t="'From'"
for="end_date"
/>
</v-col>
<slot></slot>
</v-row>
</v-container>
</q-form>
</div>
</div>
<TinyEditor
v-model="item.content"
: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>
<script>
import has from 'lodash/has';
import useVuelidate from '@vuelidate/core';
import {required} from '@vuelidate/validators';
import EditLinks from "../resource_links/EditLinks.vue";
import {computed, ref} from "vue";
import {useStore} from "vuex";
export default {
name: 'CCalendarEventForm',
components: {
EditLinks
<script setup>
import { computed, ref } from 'vue';
import { useStore } from 'vuex';
import { useVuelidate } from '@vuelidate/core';
import { required } from '@vuelidate/validators';
import InputText from 'primevue/inputtext';
import Calendar from 'primevue/calendar';
import EditLinks from '../resource_links/EditLinks.vue';
import Checkbox from 'primevue/checkbox';
const store = useStore();
// eslint-disable-next-line no-undef
const props = defineProps({
values: {
type: Object,
required: true,
},
errors: {
type: Object,
default: () => {},
},
setup() {
const store = useStore();
const linksType = ref('users');
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
}
initialValues: {
type: Object,
default: () => {},
},
props: {
values: {
type: Object,
required: true
});
const item = computed(() => props.initialValues || props.values);
const rules = computed(() => ({
item: {
title: {
required,
},
errors: {
type: Object,
default: () => {
}
content: {
required,
},
initialValues: {
type: Object,
default: () => {
}
startDate: {
required,
},
},
data() {
return {
title: null,
content: null,
parentResourceNodeId: null,
collective: null,
};
},
computed: {
item() {
return this.initialValues || this.values;
endDate: {
required,
},
titleErrors() {
const errors = [];
if (!this.v$.item.title.$dirty) return errors;
has(this.violations, 'title') && errors.push(this.violations.title);
collective: {}
}
}));
if (this.v$.item.title.required) {
return this.$t('Field is required')
}
const v$ = useVuelidate(rules, { item });
return errors;
},
// eslint-disable-next-line no-undef
defineExpose({
v$,
})
violations() {
return this.errors || {};
}
},
validations: {
item: {
title: {
required,
},
content: {
required,
},
startDate: {
required,
},
endDate: {
required,
},
}
const linksType = ref('users');
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';
}
};
}
</script>

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

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

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

Loading…
Cancel
Save