Skip to content
Snippets Groups Projects
Commit 29e259ca authored by Hangzhi Yu's avatar Hangzhi Yu
Browse files

Speed TCC raster up by refactoring components and using v-lazy

parent 1982f79f
No related branches found
No related tags found
1 merge request!47Draft: Resolve "[TCC planning] Frontend is incredibly slow with large amounts of entries"
Pipeline #196660 failed
<script setup> <script setup>
import PositiveSmallIntegerField from "aleksis.core/components/generic/forms/PositiveSmallIntegerField.vue";
import ValidityRangeField from "../validity_range/ValidityRangeField.vue"; import ValidityRangeField from "../validity_range/ValidityRangeField.vue";
import SubjectChip from "aleksis.apps.cursus/components/SubjectChip.vue"; import SubjectChip from "aleksis.apps.cursus/components/SubjectChip.vue";
import TeacherField from "aleksis.apps.cursus/components/TeacherField.vue" import TimeboundCourseConfigRasterCell from "./TimeboundCourseConfigRasterCell.vue";
</script> </script>
<template> <template>
<div> <v-data-table
<v-data-table disable-sort
disable-sort disable-filtering
disable-filtering disable-pagination
disable-pagination hide-default-footer
hide-default-footer :headers="headers"
:headers="headers" :items="items"
:items="items" :loading="loading"
:loading="loading" >
> <template #top>
<template #top> <v-row>
<v-row> <v-col
<v-col cols="6"
cols="6" lg="3"
lg="3" class="d-flex justify-space-between flex-wrap align-center"
class="d-flex justify-space-between flex-wrap align-center" >
> <v-autocomplete
<v-autocomplete outlined
outlined filled
filled multiple
multiple hide-details
hide-details :items="groupsForPlanning"
:items="groupsForPlanning" item-text="shortName"
item-text="shortName" item-value="id"
item-value="id" return-object
return-object :disabled="$apollo.queries.groupsForPlanning.loading"
:disabled="$apollo.queries.groupsForPlanning.loading" :label="$t('lesrooster.timebound_course_config.groups')"
:label="$t('lesrooster.timebound_course_config.groups')" :loading="$apollo.queries.groupsForPlanning.loading"
:loading="$apollo.queries.groupsForPlanning.loading" v-model="selectedGroups"
v-model="selectedGroups" class="mr-4"
class="mr-4" />
/> </v-col>
</v-col>
<v-col <v-col
cols="6" cols="6"
lg="3" lg="3"
class="d-flex justify-space-between flex-wrap align-center" class="d-flex justify-space-between flex-wrap align-center"
> >
<validity-range-field <validity-range-field
outlined outlined
filled filled
hide-details hide-details
v-model="internalValidityRange" v-model="internalValidityRange"
:loading="$apollo.queries.currentValidityRange.loading" :loading="$apollo.queries.currentValidityRange.loading"
/> />
</v-col> </v-col>
<v-col <v-col
cols="6" cols="6"
lg="2" lg="2"
class="d-flex justify-space-between flex-wrap align-center" class="d-flex justify-space-between flex-wrap align-center"
> >
<v-switch <v-switch
v-model="includeChildGroups" v-model="includeChildGroups"
inset inset
:label=" :label="
$t( $t(
'lesrooster.timebound_course_config.filters.include_child_groups', 'lesrooster.timebound_course_config.filters.include_child_groups',
) )
" "
:loading="$apollo.queries.subjects.loading" :loading="$apollo.queries.subjects.loading"
></v-switch> ></v-switch>
</v-col> </v-col>
<v-spacer /> <v-spacer />
</v-row> </v-row>
</template> </template>
<!-- eslint-disable-next-line vue/valid-v-slot --> <!-- eslint-disable-next-line vue/valid-v-slot -->
<template #item.subject="{ item, value }"> <template #item.subject="{ item, value }">
<subject-chip v-if="value" :subject="value" /> <subject-chip v-if="value" :subject="value" />
</template> </template>
<template <template
v-for="(groupHeader, index) in groupHeaders" v-for="(groupHeader, index) in groupHeaders"
#[tableItemSlotName(groupHeader)]="{ item, value, header }" #[tableItemSlotName(groupHeader)]="{ item, value, header }"
>
<timebound-course-config-raster-cell
:value="value"
:subject="item.subject"
:header="header"
:loading="loading"
@addCourse="addCourse"
@setCourseConfigData="setCourseConfigData"
/>
</template>
</v-data-table>
<!-- <div>
<v-row>
<v-col
cols="6"
lg="3"
class="d-flex justify-space-between flex-wrap align-center"
>
<v-autocomplete
outlined
filled
multiple
hide-details
:items="groupsForPlanning"
item-text="shortName"
item-value="id"
return-object
:disabled="$apollo.queries.groupsForPlanning.loading"
:label="$t('lesrooster.timebound_course_config.groups')"
:loading="$apollo.queries.groupsForPlanning.loading"
v-model="selectedGroups"
class="mr-4"
/>
</v-col>
<v-col
cols="6"
lg="3"
class="d-flex justify-space-between flex-wrap align-center"
> >
<div :key="index"> <validity-range-field
<div v-if="value.length"> outlined
<v-row filled
v-for="(course, index) in value" hide-details
:key="index" v-model="internalValidityRange"
no-gutters :loading="$apollo.queries.currentValidityRange.loading"
class="mt-2" />
> </v-col>
<v-col cols="6">
<positive-small-integer-field <v-col
dense cols="6"
filled lg="2"
class="mx-1" class="d-flex justify-space-between flex-wrap align-center"
:disabled="loading" >
:value=" <v-switch
getCurrentCourseConfig(course) v-model="includeChildGroups"
? getCurrentCourseConfig(course).lessonQuota inset
: course.lessonQuota :label="
" $t(
:label="$t('lesrooster.timebound_course_config.lesson_quota')" 'lesrooster.timebound_course_config.filters.include_child_groups',
@change=" )
(event) => "
setCourseConfigData(course, item.subject, header, { :loading="$apollo.queries.subjects.loading"
lessonQuota: event, ></v-switch>
}) </v-col>
"
/> <v-spacer />
</v-col> </v-row>
<v-col cols="6"> <v-simple-table
<teacher-field fixed-header
dense >
filled <thead>
class="mx-1" <tr>
:disabled="loading" <th v-for="header in headers" :key="header.value" class="text-left">
:label="$t('lesrooster.timebound_course_config.teachers')" {{ header.text }}
:value=" </th>
getCurrentCourseConfig(course) </tr>
? getCurrentCourseConfig(course).teachers </thead>
: course.teachers <tbody>
" <tr
:show-subjects="true" v-for="(item, index) in items"
:priority-subject="item.subject" :key="index"
:rules="$rules().isNonEmpty.build()" >
@input=" <td>
(event) => <subject-chip :subject="item.subject" />
setCourseConfigData(course, item.subject, header, { </td>
teachers: event, <td v-for="header in groupHeaders" :key="header.value">
}) <timebound-course-config-raster-cell
" :value="item[header.value]"
/> :subject="item.subject"
</v-col> :header="header"
</v-row> :loading="loading"
</div> @addCourse="addCourse"
<div v-if="!value.length"> @setCourseConfigData="setCourseConfigData"
<v-btn />
block </td>
icon </tr>
tile </tbody>
outlined </v-simple-table>
@click="addCourse(item.subject.id, header.value)" </div> -->
>
<v-icon>mdi-plus</v-icon>
</v-btn>
</div>
</div>
</template>
</v-data-table>
</div>
</template> </template>
<script> <script>
...@@ -167,11 +197,10 @@ import { currentValidityRange as gqlCurrentValidityRange } from "../validity_ran ...@@ -167,11 +197,10 @@ import { currentValidityRange as gqlCurrentValidityRange } from "../validity_ran
import { gqlGroupsForPlanning } from "../helper.graphql"; import { gqlGroupsForPlanning } from "../helper.graphql";
import mutateMixin from "aleksis.core/mixins/mutateMixin.js"; import mutateMixin from "aleksis.core/mixins/mutateMixin.js";
import formRulesMixin from "aleksis.core/mixins/formRulesMixin";
export default { export default {
name: "TimeboungCourseConfigRaster", name: "TimeboungCourseConfigRaster",
mixins: [formRulesMixin, mutateMixin], mixins: [mutateMixin],
data() { data() {
return { return {
i18nKey: "lesrooster.timebound_course_config", i18nKey: "lesrooster.timebound_course_config",
...@@ -210,13 +239,6 @@ export default { ...@@ -210,13 +239,6 @@ export default {
tableItemSlotName(header) { tableItemSlotName(header) {
return "item." + header.value; return "item." + header.value;
}, },
getCurrentCourseConfig(course) {
if (course.lrTimeboundCourseConfigs?.length) {
return course.lrTimeboundCourseConfigs[0];
} else {
return null;
}
},
setCourseConfigData(course, subject, header, newValue) { setCourseConfigData(course, subject, header, newValue) {
if (course.newCourse) { if (course.newCourse) {
let existingCreatedCourse = this.createdCourses.find( let existingCreatedCourse = this.createdCourses.find(
...@@ -311,7 +333,8 @@ export default { ...@@ -311,7 +333,8 @@ export default {
); );
}, },
generateTableItems(subjects) { generateTableItems(subjects) {
return subjects.map((subject) => { const start = performance.now();
const subjectsWithSortedCourses = subjects.map((subject) => {
let { courses, ...reducedSubject } = subject; let { courses, ...reducedSubject } = subject;
let groupCombinations = {}; let groupCombinations = {};
...@@ -359,6 +382,9 @@ export default { ...@@ -359,6 +382,9 @@ export default {
...groupCombinations, ...groupCombinations,
}; };
}); });
const end = performance.now();
console.log(`Execution time: ${end - start} ms`);
return subjectsWithSortedCourses;
}, },
}, },
computed: { computed: {
......
<script setup>
import PositiveSmallIntegerField from "aleksis.core/components/generic/forms/PositiveSmallIntegerField.vue";
import TeacherField from "aleksis.apps.cursus/components/TeacherField.vue"
</script>
<template>
<v-lazy
v-model="active"
:options="{
threshold: .5
}"
transition="fade-transition"
>
<div v-if="value.length">
<v-row
v-for="(course, index) in value"
:key="index"
no-gutters
class="mt-2"
>
<v-col cols="6">
<positive-small-integer-field
dense
filled
class="mx-1"
:disabled="loading"
:value="
getCurrentCourseConfig(course)
? getCurrentCourseConfig(course).lessonQuota
: course.lessonQuota
"
:label="$t('lesrooster.timebound_course_config.lesson_quota')"
@change="
(event) =>
$emit('setCourseConfigData', course, subject, header, {
lessonQuota: event,
})
"
/>
</v-col>
<v-col cols="6">
<teacher-field
dense
filled
class="mx-1"
:disabled="loading"
:label="$t('lesrooster.timebound_course_config.teachers')"
:value="
getCurrentCourseConfig(course)
? getCurrentCourseConfig(course).teachers
: course.teachers
"
:show-subjects="true"
:priority-subject="subject"
:rules="$rules().isNonEmpty.build()"
@input="
(event) =>
$emit('setCourseConfigData', course, subject, header, {
teachers: event,
})
"
/>
</v-col>
</v-row>
</div>
<div v-else>
<v-btn
block
icon
tile
outlined
@click="$emit('addCourse', subject.id, header.value)"
>
<v-icon>mdi-plus</v-icon>
</v-btn>
</div>
</v-lazy>
</template>
<script>
import formRulesMixin from "aleksis.core/mixins/formRulesMixin";
export default {
name: "TimeboundCourseConfigRasterCell",
mixins: [formRulesMixin],
emits: ["addCourse", "setCourseConfigData"],
props: {
value: {
type: Array,
required: true,
},
subject: {
type: Object,
required: true,
},
header: {
type: Object,
required: true,
},
loading: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
active: false,
};
},
methods: {
getCurrentCourseConfig(course) {
if (course.lrTimeboundCourseConfigs?.length) {
return course.lrTimeboundCourseConfigs[0];
} else {
return null;
}
},
},
};
</script>
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