Skip to content
Snippets Groups Projects
Verified Commit dad95b7e authored by Jonathan Weth's avatar Jonathan Weth :keyboard:
Browse files

Introduce TimetableWrapper as reusable frame for timetables

parent 3b1723ab
No related branches found
No related tags found
1 merge request!347Refactor timetable wrapper
Pipeline #180296 failed
<script setup>
import TimetableWrapper from "./TimetableWrapper.vue";
</script>
<script> <script>
import { gqlAvailableTimetables } from "./timetables.graphql";
import NoTimetableCard from "./NoTimetableCard.vue";
import SelectTimetable from "./SelectTimetable.vue";
import timetableTypes from "./timetableTypes";
export default { export default {
name: "Timetable", name: "Timetable",
components: { NoTimetableCard, SelectTimetable },
apollo: {
availableTimetables: {
query: gqlAvailableTimetables,
result() {
if (
!this.selected &&
this.$route.params.id &&
this.$route.params.type
) {
this.selectTimetable(
this.availableTimetables.find(
(t) =>
t.objId === this.$route.params.id &&
t.type.toLowerCase() === this.$route.params.type,
),
);
}
},
},
},
data() {
return {
availableTimetables: [],
selected: null,
search: "",
selectedTypes: ["GROUP", "TEACHER", "ROOM"],
types: timetableTypes,
selectDialog: false,
};
},
watch: {
selected(selected) {
// Align navigation with currently selected timetable
if (!selected) {
this.$router.push({ name: "chronos.timetable" });
} else if (
selected.objId !== this.$route.params.id ||
selected.type.toLowerCase() !== this.$route.params.type
) {
this.$router.push({
name: "chronos.timetableWithId",
params: {
type: selected.type.toLowerCase(),
id: selected.objId,
},
});
}
},
},
methods: {
findNextTimetable(offset = 1) {
const currentIndex = this.availableTimetablesIds.indexOf(
this.selected.id,
);
const newIndex = currentIndex + offset;
if (newIndex < 0 || newIndex >= this.availableTimetablesIds.length) {
return null;
}
return this.availableTimetables[newIndex];
},
selectTimetable(timetable) {
this.selected = timetable;
},
},
computed: {
selectedTypesFull() {
return this.selectedTypes.map((type) => {
return this.types[type];
});
},
availableTimetablesFiltered() {
// Filter timetables by selected types
return this.availableTimetables.filter((timetable) => {
return this.selectedTypes.indexOf(timetable.type) !== -1;
});
},
availableTimetablesIds() {
return this.availableTimetables.map((timetable) => timetable.id);
},
prevTimetable() {
return this.findNextTimetable(-1);
},
nextTimetable() {
return this.findNextTimetable(1);
},
},
}; };
</script> </script>
<template> <template>
<div> <timetable-wrapper>
<v-row> <template #default="{ selected }">
<v-dialog <calendar-with-controls
v-model="selectDialog" :calendar-feeds="[{ name: 'lesson' }, { name: 'supervision' }]"
fullscreen :params="{ type: selected.type, id: selected.objId }"
hide-overlay />
transition="dialog-bottom-transition" </template>
> </timetable-wrapper>
<v-card>
<v-toolbar dark color="primary">
<v-toolbar-title>{{
$t("chronos.timetable.select")
}}</v-toolbar-title>
<v-spacer></v-spacer>
</v-toolbar>
<select-timetable
v-model="selected"
@input="selectDialog = false"
:available-timetables="availableTimetables"
/>
</v-card>
</v-dialog>
<v-col md="3" lg="3" xl="3" v-if="$vuetify.breakpoint.lgAndUp">
<v-card>
<select-timetable
v-model="selected"
:available-timetables="availableTimetables"
/>
</v-card>
</v-col>
<v-col sm="12" md="12" lg="9" xl="9" class="full-height">
<!-- No timetable card-->
<no-timetable-card
v-if="selected == null"
@selectTimetable="selectDialog = true"
/>
<!-- Calendar card-->
<v-card v-else>
<div class="d-flex flex-column" v-if="$vuetify.breakpoint.smAndDown">
<v-card-title class="pt-2">
<v-btn
icon
:disabled="!prevTimetable"
@click="selectTimetable(prevTimetable)"
:title="$t('chronos.timetable.prev')"
class="mr-1"
>
<v-icon>mdi-chevron-left</v-icon>
</v-btn>
<v-spacer />
<v-chip outlined color="secondary" @click="selectDialog = true">
{{ selected.name }}
<v-icon right>mdi-chevron-down</v-icon>
</v-chip>
<v-spacer />
<v-btn
icon
:disabled="!nextTimetable"
@click="selectTimetable(nextTimetable)"
:title="$t('chronos.timetable.next')"
class="ml-1 float-right"
>
<v-icon>mdi-chevron-right</v-icon>
</v-btn>
</v-card-title>
</div>
<div class="d-flex flex-wrap justify-space-between mb-2" v-else>
<v-card-title>
{{ selected.name }}
</v-card-title>
<div class="pa-2 mt-1">
<v-btn
icon
:disabled="!prevTimetable"
@click="selectTimetable(prevTimetable)"
:title="$t('chronos.timetable.prev')"
>
<v-icon>mdi-chevron-left</v-icon>
</v-btn>
<v-chip label color="secondary" outlined class="mx-1">{{
selected.shortName
}}</v-chip>
<v-btn
icon
:disabled="!nextTimetable"
@click="selectTimetable(nextTimetable)"
:title="$t('chronos.timetable.next')"
>
<v-icon>mdi-chevron-right</v-icon>
</v-btn>
</div>
</div>
<calendar-with-controls
:calendar-feeds="[{ name: 'lesson' }, { name: 'supervision' }]"
:params="{ type: selected.type, id: selected.objId }"
/>
</v-card>
</v-col>
</v-row>
</div>
</template> </template>
<script>
import { gqlAvailableTimetables } from "./timetables.graphql";
import NoTimetableCard from "./NoTimetableCard.vue";
import SelectTimetable from "./SelectTimetable.vue";
import timetableTypes from "./timetableTypes";
export default {
name: "TimetableWrapper",
components: { NoTimetableCard, SelectTimetable },
apollo: {
availableTimetables: {
query: gqlAvailableTimetables,
result() {
if (
!this.selected &&
this.$route.params.id &&
this.$route.params.type
) {
this.selectTimetable(
this.availableTimetables.find(
(t) =>
t.objId === this.$route.params.id &&
t.type.toLowerCase() === this.$route.params.type,
),
);
}
},
},
},
data() {
return {
availableTimetables: [],
selected: null,
search: "",
selectedTypes: ["GROUP", "TEACHER", "ROOM"],
types: timetableTypes,
selectDialog: false,
};
},
props: {
onSelected: {
type: Function,
required: false,
default: null,
},
},
watch: {
selected(selected) {
if (this.onSelected) {
this.onSelected(selected);
return;
}
// Align navigation with currently selected timetable
if (!selected) {
this.$router.push({ name: "chronos.timetable" });
} else if (
selected.objId !== this.$route.params.id ||
selected.type.toLowerCase() !== this.$route.params.type
) {
this.$router.push({
name: "chronos.timetableWithId",
params: {
type: selected.type.toLowerCase(),
id: selected.objId,
},
});
}
},
},
methods: {
findNextTimetable(offset = 1) {
const currentIndex = this.availableTimetablesIds.indexOf(
this.selected.id,
);
const newIndex = currentIndex + offset;
if (newIndex < 0 || newIndex >= this.availableTimetablesIds.length) {
return null;
}
return this.availableTimetables[newIndex];
},
selectTimetable(timetable) {
this.selected = timetable;
},
},
computed: {
selectedTypesFull() {
return this.selectedTypes.map((type) => {
return this.types[type];
});
},
availableTimetablesFiltered() {
// Filter timetables by selected types
return this.availableTimetables.filter((timetable) => {
return this.selectedTypes.indexOf(timetable.type) !== -1;
});
},
availableTimetablesIds() {
return this.availableTimetables.map((timetable) => timetable.id);
},
prevTimetable() {
return this.findNextTimetable(-1);
},
nextTimetable() {
return this.findNextTimetable(1);
},
},
};
</script>
<template>
<div>
<v-row>
<v-dialog
v-model="selectDialog"
fullscreen
hide-overlay
transition="dialog-bottom-transition"
>
<v-card>
<v-toolbar dark color="primary">
<v-toolbar-title>{{
$t("chronos.timetable.select")
}}</v-toolbar-title>
<v-spacer></v-spacer>
</v-toolbar>
<select-timetable
v-model="selected"
@input="selectDialog = false"
:available-timetables="availableTimetables"
/>
</v-card>
</v-dialog>
<v-col md="3" lg="3" xl="3" v-if="$vuetify.breakpoint.lgAndUp">
<slot name="additionalSelect" :selected="selected"></slot>
<v-card>
<select-timetable
v-model="selected"
:available-timetables="availableTimetables"
/>
</v-card>
</v-col>
<v-col sm="12" md="12" lg="9" xl="9" class="full-height">
<!-- No timetable card-->
<no-timetable-card
v-if="selected == null"
@selectTimetable="selectDialog = true"
/>
<!-- Calendar card-->
<v-card v-else>
<div class="d-flex flex-column" v-if="$vuetify.breakpoint.smAndDown">
<v-card-title class="pt-2">
<v-btn
icon
:disabled="!prevTimetable"
@click="selectTimetable(prevTimetable)"
:title="$t('chronos.timetable.prev')"
class="mr-1"
>
<v-icon>mdi-chevron-left</v-icon>
</v-btn>
<v-spacer />
<v-chip outlined color="secondary" @click="selectDialog = true">
{{ selected.name }}
<v-icon right>mdi-chevron-down</v-icon>
</v-chip>
<v-spacer />
<v-btn
icon
:disabled="!nextTimetable"
@click="selectTimetable(nextTimetable)"
:title="$t('chronos.timetable.next')"
class="ml-1 float-right"
>
<v-icon>mdi-chevron-right</v-icon>
</v-btn>
</v-card-title>
</div>
<div class="d-flex flex-wrap justify-space-between mb-2" v-else>
<v-card-title>
{{ selected.name }}
</v-card-title>
<div class="pa-2 mt-1">
<v-btn
icon
:disabled="!prevTimetable"
@click="selectTimetable(prevTimetable)"
:title="$t('chronos.timetable.prev')"
>
<v-icon>mdi-chevron-left</v-icon>
</v-btn>
<v-chip label color="secondary" outlined class="mx-1">{{
selected.shortName
}}</v-chip>
<v-btn
icon
:disabled="!nextTimetable"
@click="selectTimetable(nextTimetable)"
:title="$t('chronos.timetable.next')"
>
<v-icon>mdi-chevron-right</v-icon>
</v-btn>
</div>
</div>
<slot :selected="selected"></slot>
</v-card>
</v-col>
</v-row>
</div>
</template>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment