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

@@ -1,8 +1,25 @@
<script setup>
import { getSubstitutionColor } from "@/util";
import { times } from "@/store";
import { ref } from "vue";
import { TrashIcon } from "lucide-vue-next";
defineProps(["lesson", "index"]);
const props = defineProps(["lesson", "index", "edit", "buttons"]);
const emit = defineEmits(["change", "delete"]);
const subjectElement = ref();
const teacherElement = ref();
const roomElement = ref();
const lessonLength = ref(props.lesson.length || 1);
function update() {
emit("change", {
subject: subjectElement.value.innerText,
teacher: teacherElement.value.innerText,
room: roomElement.value.innerText,
length: lessonLength.value,
});
}
function isChanged(lesson, key) {
const substitution = lesson.substitution;
@@ -43,13 +60,21 @@ function getTime(index) {
<div class="infos">
<!-- Subject changed -->
<span class="subject" v-if="isChanged(lesson, 'subject')">
<s>{{ lesson.subject }}</s> {{ lesson.substitution.change.subject }}
<s>{{ lesson.subject }}</s>
{{ lesson.substitution.change.subject }}
</span>
<!-- Cancellation -->
<span class="subject" v-else-if="isCancelled(lesson.substitution)">
<s>{{ lesson.subject }}</s>
</span>
<span class="subject" v-else>
<span
class="subject"
v-else
:contenteditable="edit"
data-ph="Subject"
ref="subjectElement"
@input="update"
>
{{ lesson.subject }}
</span>
<div class="info">
@@ -58,22 +83,43 @@ function getTime(index) {
<s>{{ lesson.teacher }}</s>
{{ lesson.substitution.change.teacher }}</span
>
<span class="info" v-else> {{ lesson.teacher }}</span>
<span
class="info"
v-else
:contenteditable="edit"
data-ph="Teacher"
ref="teacherElement"
@input="update"
>
{{ lesson.teacher }}</span
>
<!-- Room changed -->
<span class="info" v-if="isChanged(lesson, 'room')"
>, <s>{{ lesson.room }}</s> {{ lesson.substitution.change.room }}
</span>
<span class="info" v-else-if="lesson.room">, {{ lesson.room }}</span>
<span class="info" v-else-if="lesson.room || edit"
>,
<span
:contenteditable="edit"
data-ph="Room"
ref="roomElement"
@input="update"
>{{ lesson.room }}</span
></span
>
<!-- Show notes if available -->
<span class="info" v-if="getNotes(lesson.substitution)"
>, {{ $t("timetable.notes") }} {{ getNotes(lesson.substitution) }}
</span>
</div>
</div>
<div class="times" v-if="getTime(index).start">
<div class="times" v-if="getTime(index).start && !edit">
<span>{{ getTime(index).start }} -</span>
<span>{{ getTime(index).end }}</span>
</div>
<div class="buttons" v-if="edit && buttons">
<TrashIcon @click="$emit('delete')" />
</div>
</div>
</template>
@@ -119,4 +165,19 @@ function getTime(index) {
opacity: 0.5;
font-weight: 300;
}
.buttons {
margin-left: auto;
opacity: 0.7;
display: grid;
gap: 5px;
align-items: center;
cursor: pointer;
}
[contenteditable="true"]:empty:before {
content: attr(data-ph);
opacity: 0.4;
cursor: text;
}
</style>

View File

@@ -0,0 +1,100 @@
<script setup>
import { toRaw, ref } from "vue";
import LessonCard from "@/components/lesson-card.vue";
import {
ArrowUpIcon,
ArrowDownIcon,
SettingsIcon,
TrashIcon,
PlusIcon,
} from "lucide-vue-next";
const props = defineProps(["lesson", "index", "edit"]);
const emit = defineEmits(["change", "move"]);
const advancedOptions = ref(false);
const lessonLength = ref(1);
function updateLength() {
let newLesson = structuredClone(toRaw(props.lesson));
for (let entry of newLesson) {
entry.length = lessonLength.value;
}
emit("change", newLesson);
}
function appendOption() {
let newLesson = structuredClone(toRaw(props.lesson));
if (Array.isArray(newLesson)) newLesson.push({});
else newLesson = [newLesson, {}];
emit("change", newLesson);
}
function lessonChanged(index, data) {
let newLesson = structuredClone(toRaw(props.lesson));
newLesson[index] = data;
if (newLesson.length > 1)
newLesson[index].group = `${data.subject}: ${data.teacher}`;
else delete newLesson[index].group;
emit("change", newLesson);
}
function lessonDeleted(index) {
let newLesson = structuredClone(toRaw(props.lesson));
newLesson.splice(index, 1);
if (newLesson.length == 0) emit("delete");
else emit("change", newLesson);
}
</script>
<template>
<div class="lessonList">
<div class="buttons" v-if="edit">
<ArrowUpIcon @click="$emit('move', 'up')" />
<ArrowDownIcon @click="$emit('move', 'down')" />
<SettingsIcon @click="advancedOptions = !advancedOptions" />
<TrashIcon @click="$emit('delete')" />
<PlusIcon @click="appendOption" />
</div>
<LessonCard
v-for="(option, groupIndex) in lesson"
:key="groupIndex"
:lesson="option"
:index="index"
:edit="edit"
:buttons="edit"
@change="(data) => lessonChanged(groupIndex, data)"
@delete="() => lessonDeleted(groupIndex)"
/>
<div class="advanced" v-show="advancedOptions" v-if="edit">
<span>Length: </span>
<input
type="number"
v-model="lessonLength"
@change="updateLength"
min="1"
/>
</div>
</div>
</template>
<style scoped>
.lessonList {
border-left: 2px solid var(--text-color);
padding-left: 5px;
display: flex;
flex-direction: column;
gap: 5px;
}
.buttons {
opacity: 0.7;
cursor: pointer;
display: flex;
gap: 3px;
}
input {
width: 30px;
}
</style>

View File

@@ -0,0 +1,44 @@
<script setup>
import { BanIcon, SaveIcon } from "lucide-vue-next";
defineEmits(["abort", "save"]);
</script>
<template>
<div class="bottomnav">
<div class="entry danger" @click="$emit('back')">
<BanIcon />
<span>Abort</span>
</div>
<div class="entry success" @click="$emit('save')">
<SaveIcon />
<span>Save</span>
</div>
</div>
</template>
<style scoped>
.bottomnav {
width: calc(90% - 40px);
height: 50px;
max-width: 800px;
padding: 0px 20px;
position: fixed;
bottom: 20px;
display: grid;
grid-auto-columns: 1fr;
grid-auto-flow: column;
z-index: 100;
border-radius: 10px;
background-color: var(--bottomnav-color);
box-shadow: var(--bottomnav-shadow);
}
.entry {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
cursor: pointer;
}
</style>

View File

@@ -42,7 +42,7 @@ const linkedTimetable = computed(() => {
</div>
<div class="listContainer">
<template v-for="(lesson, index) in linkedTimetable" :key="index">
<LessonCard :lesson="lesson" :index="index" />
<LessonCard :lesson="lesson" :index="index" :edit="false" />
</template>
</div>
</template>