Implement timetable editing

This commit is contained in:
2023-06-18 23:43:08 +02:00
parent 46b235ba91
commit 4d4a92bff3
10 changed files with 532 additions and 49 deletions

View File

@ -0,0 +1,221 @@
<script setup>
import { useRoute } from "vue-router";
import { Swiper, SwiperSlide } from "swiper/vue";
import { ref, toRaw } from "vue";
import { localTimetables } from "@/store";
import { PlusIcon } from "lucide-vue-next";
import EditorNavbar from "@/components/settings/editor-navbar.vue";
import DateSelector from "@/components/date-selector.vue";
import ScrollableContainer from "@/components/scrollable-container.vue";
import LessonGroupList from "@/components/lesson-group-list.vue";
const route = useRoute();
const timetable = localTimetables.value.find(
(e) => e.id == route.params.id
) || {
data: [],
};
timetable.data = timetable.data.map((day) => {
if (!day) return [];
return day.map((lesson) => {
if (Array.isArray(lesson)) return toRaw(lesson);
else return [toRaw(lesson)];
});
});
const timetableClone = ref(structuredClone(toRaw(timetable)));
const swiperElement = ref();
function setSwiper(swiper) {
swiperElement.value = swiper;
}
function getLessonIndex(index, day) {
let lessonIndex = 0;
for (let i = 0; i < index; i++) {
lessonIndex += day[i][0].length || 1;
}
return lessonIndex;
}
function getLessonLength(lesson) {
if (Array.isArray(lesson)) return lesson[0].length || 1;
else return lesson.length || 1;
}
function arrayMove(arr, fromIndex, toIndex) {
let element = arr[fromIndex];
arr.splice(fromIndex, 1);
arr.splice(toIndex, 0, element);
}
</script>
<template>
<div class="editor">
<DateSelector
:selectedDay="(swiperElement || { activeIndex: 0 }).activeIndex"
@changeDay="
(day) => swiperElement.slideTo(swiperElement.activeIndex + day)
"
/>
<div class="settings">
<span>Name: </span>
<input type="text" v-model="timetableClone.title" />
</div>
<div class="wrapper">
<swiper
:slides-per-view="1"
:space-between="50"
:initial-slide="0"
@swiper="setSwiper"
>
<swiper-slide :key="day" v-for="day in 5">
<ScrollableContainer>
<div class="list">
<div
class="lesson"
v-for="(lesson, index) in timetableClone.data[day - 1]"
:key="index"
>
<!-- Add main editable element -->
<LessonGroupList
:index="getLessonIndex(index, timetableClone.data[day - 1])"
:lesson="lesson"
:edit="true"
@change="(e) => (timetableClone.data[day - 1][index] = e)"
@delete="timetableClone.data[day - 1].splice(index, 1)"
@move="
(direction) => {
if (direction == 'up' && index > 0) {
arrayMove(
timetableClone.data[day - 1],
index,
index - 1
);
} else if (
direction == 'down' &&
index < timetableClone.data[day - 1].length
) {
arrayMove(
timetableClone.data[day - 1],
index,
index + 1
);
}
}
"
/>
<!-- Add placeholder elements for lessons with length > 1 -->
<div
class="inactive"
v-for="placeholderLesson in getLessonLength(lesson) - 1"
:key="placeholderLesson"
>
<LessonGroupList
:index="
getLessonIndex(index, timetableClone.data[day - 1]) +
placeholderLesson
"
:lesson="lesson"
:edit="false"
/>
</div>
</div>
</div>
<div
class="create"
@click="
if (timetableClone.data[day - 1]) {
timetableClone.data[day - 1].push([{}]);
} else {
timetableClone.data[day - 1] = [[{}]];
}
"
>
<PlusIcon />
<span>{{ $t("editor.newLesson") }}</span>
</div>
</ScrollableContainer>
</swiper-slide>
</swiper>
</div>
</div>
<div class="navbar">
<EditorNavbar
@back="$router.back()"
@save="
() => {
timetable.data = timetableClone.data;
timetable.title = timetableClone.title;
timetable.source = 'Manual';
timetable.trusted = true;
$router.back();
}
"
/>
</div>
</template>
<style>
@import "swiper/css";
</style>
<style scoped>
.editor {
height: 100%;
display: flex;
flex-direction: column;
}
.settings {
padding: 5px 10px 0px 10px;
display: grid;
grid-template-columns: auto 1fr;
gap: 10px;
margin-bottom: 5px;
}
.settings input {
border: unset;
background-color: unset;
color: unset;
border-bottom: 1px solid var(--text-color);
outline: unset;
}
.wrapper {
overflow: hidden;
height: 100%;
}
.navbar {
display: flex;
justify-content: center;
}
.list {
display: flex;
flex-direction: column;
gap: 10px;
}
.swiper {
padding: 5px 10px 0px 10px;
height: 100%;
box-sizing: border-box;
}
.inactive {
opacity: 0.7;
margin-top: 10px;
}
.create {
display: flex;
justify-content: center;
align-items: center;
padding: 5px;
margin-top: 10px;
gap: 5px;
cursor: pointer;
}
</style>

View File

@ -1,6 +1,7 @@
<script setup>
import TimetableCard from "@/components/settings/timetable-card.vue";
import { timetables, localTimetables, timetableId } from "@/store";
import { PlusIcon } from "lucide-vue-next";
import { toRaw } from "vue";
function copyTimetable(timetable) {
@ -9,39 +10,54 @@ function copyTimetable(timetable) {
newTimetable.id = new Date().getTime();
localTimetables.value.push(newTimetable);
}
function createTimetable() {
localTimetables.value.push({
id: new Date().getTime(),
title: "New timetable",
data: [],
});
}
</script>
<template>
<h2>{{ $t("settings.heading.localTimetables") }}</h2>
<div class="list">
<TimetableCard
v-for="timetable in localTimetables"
:key="timetable.id"
:timetable="timetable"
:selected="timetableId == timetable.id"
:editable="true"
@click="timetableId = timetable.id"
@copy="copyTimetable(timetable)"
@delete="
localTimetables.splice(
localTimetables.findIndex((e) => e.id == timetable.id),
1
)
"
/>
</div>
<h2>{{ $t("settings.heading.remoteTimetables") }}</h2>
<div class="list">
<TimetableCard
v-for="timetable in timetables"
:key="timetable.id"
:timetable="timetable"
:selected="timetableId == timetable.id"
:editable="false"
@click="timetableId = timetable.id"
@copy="copyTimetable(timetable)"
/>
<div class="content" v-if="$route.name == 'title.settings.timetable'">
<h2>{{ $t("settings.heading.localTimetables") }}</h2>
<div class="list">
<TimetableCard
v-for="timetable in localTimetables"
:key="timetable.id"
:timetable="timetable"
:selected="timetableId == timetable.id"
:editable="true"
@click="timetableId = timetable.id"
@edit="$router.push('timetable/edit/' + timetable.id)"
@copy="copyTimetable(timetable)"
@delete="
localTimetables.splice(
localTimetables.findIndex((e) => e.id == timetable.id),
1
)
"
/>
</div>
<div class="create" @click="createTimetable">
<PlusIcon /> {{ $t("settings.text.createTimetable") }}
</div>
<h2>{{ $t("settings.heading.remoteTimetables") }}</h2>
<div class="list">
<TimetableCard
v-for="timetable in timetables"
:key="timetable.id"
:timetable="timetable"
:selected="timetableId == timetable.id"
:editable="false"
@click="timetableId = timetable.id"
@copy="copyTimetable(timetable)"
/>
</div>
</div>
<RouterView v-else />
</template>
<style scoped>
@ -57,4 +73,13 @@ p {
display: grid;
gap: 8px;
}
.create {
display: flex;
justify-content: center;
align-items: center;
margin-top: 5px;
padding: 10px;
cursor: pointer;
}
</style>