parent
53374ed2d9
commit
beec0d19f5
@ -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…
Reference in new issue