parent
53374ed2d9
commit
beec0d19f5
@ -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) }} |
||||
<div class="flex flex-col gap-4"> |
||||
<h5 v-text="event.title" /> |
||||
|
||||
<p v-if="event.endDate"> |
||||
{{ $filters.abbreviatedDatetime(event.endDate) }} |
||||
</p> |
||||
<p v-text="useAbbreviatedDatetime(event.startDate)" /> |
||||
|
||||
<hr class="my-2"> |
||||
<p |
||||
v-if="event.endDate" |
||||
v-text="useAbbreviatedDatetime(event.endDate)" |
||||
/> |
||||
|
||||
<div class="mb-3" v-html="event.content" /> |
||||
<hr> |
||||
|
||||
<h6 class="text-h5"> {{ $t('Invitees') }}</h6> |
||||
<div v-html="event.content" /> |
||||
|
||||
<ShowLinks |
||||
<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…
Reference in new issue