Files
Timetable-V2/src/components/lesson-card.vue
minie4 0cb55eaf68 Add support for other substitution types
- Store and display original substitution type text
- Treat "eigenverantwortliches Arbeiten" as cancellation
2023-08-27 18:00:09 +02:00

208 lines
5.2 KiB
Vue

<script setup>
import { getSubstitutionColor } from "@/util";
import { times } from "@/store";
import { ref, watch } from "vue";
import { MinusIcon } from "lucide-vue-next";
const props = defineProps(["lesson", "index", "edit", "buttons"]);
const emit = defineEmits(["change", "delete"]);
// Only make the card reactive if not in edit mode
// to prevent some browsers to jump to the start
// of the text field on change
const lesson = ref(props.lesson);
watch(
() => props.lesson,
(value) => {
if (!props.edit) lesson.value = value;
},
);
const subjectElement = ref();
const teacherElement = ref();
const roomElement = ref();
function update() {
emit("change", {
subject: subjectElement.value.innerText,
teacher: teacherElement.value.innerText,
room: roomElement.value.innerText,
});
}
function isChanged(lesson, key) {
const substitution = lesson.substitution;
if (!(substitution && substitution.change)) return false;
const changedKeys = Object.keys(substitution.change);
if (!changedKeys.includes(key)) return false;
return lesson[key] != substitution.change[key];
}
function getNotes(substitution) {
if (!(substitution && substitution.notes)) return;
return substitution.notes;
}
function isCancelled(substitution) {
if (!substitution) return false;
return substitution.type == "cancellation";
}
function getTime(index) {
const lessonTimes = {
...(times.value.find((e) => e.lesson == index + 1) || {}),
};
Object.keys(lessonTimes).forEach((e) => {
if (e == "lesson") return;
const date = new Date(lessonTimes[e]);
const hours = date.getHours().toString().padStart(2, "0");
const minutes = date.getMinutes().toString().padStart(2, "0");
lessonTimes[e] = `${hours}:${minutes}`;
});
return lessonTimes;
}
</script>
<template>
<div class="lesson" :style="getSubstitutionColor(lesson.substitution)">
<span class="hour">{{ index + 1 }}</span>
<div class="infos">
<!-- Subject changed -->
<span class="subject" v-if="isChanged(lesson, '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
:contenteditable="edit"
autocorrect="false"
spellcheck="false"
data-ph="Subject"
ref="subjectElement"
@keydown.enter.prevent
@input="update"
>
{{ lesson.subject }}
</span>
<div class="info">
<!-- Teacher changed -->
<span class="info" v-if="isChanged(lesson, 'teacher')">
<s>{{ lesson.teacher }}</s>
{{ lesson.substitution.change.teacher }}</span
>
<span
class="info"
v-else
:contenteditable="edit"
autocorrect="false"
spellcheck="false"
data-ph="Teacher"
ref="teacherElement"
@keydown.enter.prevent
@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 || edit"
>,
<span
:contenteditable="edit"
autocorrect="false"
spellcheck="false"
data-ph="Room"
ref="roomElement"
@keydown.enter.prevent
@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>
<span class="info type" v-if="lesson.substitution">
<i>{{ lesson.substitution.rawType }}</i>
</span>
</div>
<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">
<MinusIcon @click="$emit('delete')" />
</div>
</div>
</template>
<style scoped>
.lesson {
display: grid;
background-color: var(--substitution-background-unchanged);
min-height: 50px;
border-radius: 11px;
padding: 15px;
grid-template-columns: max-content auto auto;
gap: 15px;
}
.hour {
font-size: 30px;
display: flex;
align-items: center;
}
.infos {
display: flex;
justify-content: center;
flex-direction: column;
}
.infos .subject {
font-weight: bold;
font-size: 18px;
}
.infos .info {
font-weight: 200;
font-size: 14px;
}
.times {
margin-left: auto;
display: grid;
grid-template-rows: 1fr 1fr;
align-items: center;
color: var(--text-color);
opacity: 0.5;
font-weight: 300;
}
.buttons {
margin-left: auto;
opacity: 0.7;
display: grid;
gap: 5px;
align-items: center;
cursor: pointer;
}
[contenteditable="true"]:focus {
padding: 0px 5px;
}
[contenteditable="true"]:empty:before {
content: attr(data-ph);
opacity: 0.4;
cursor: text;
}
</style>