Files
Timetable-V2/src/views/settings/TimetableSettings.vue
minie4 21b2e68198 Add profile management
- Save class filter, timetable and timetable groups in profiles
- Easily switch between profiles
- Rename profiles
- Export/Import/Duplicate profiles
2023-08-26 21:54:02 +02:00

178 lines
4.2 KiB
Vue

<script setup>
import TimetableCard from "@/components/settings/timetable-card.vue";
import {
timetable,
timetables,
localTimetables,
activeProfile,
baseUrl,
fetchTimetables,
} from "@/store";
import { canEditTimetable } from "@/permission";
import { PlusIcon, PaperclipIcon } from "lucide-vue-next";
import { toRaw, ref } from "vue";
import download from "downloadjs";
import { loading, loadingProgress } from "@/store";
function copyTimetable(timetable) {
const newTimetable = structuredClone(toRaw(timetable));
newTimetable.title = "Copy of " + timetable.title;
newTimetable.id = new Date().getTime();
localTimetables.value.push(newTimetable);
}
function createTimetable() {
localTimetables.value.push({
id: new Date().getTime(),
title: "New timetable",
data: [],
});
}
const fileInput = ref();
function importTimetable(event) {
const file = event.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (e) => {
const contents = e.target.result;
try {
const data = JSON.parse(contents);
if (!data.data) throw "Invalid data";
localTimetables.value.push(data);
} catch (e) {
console.log(e.stack);
alert("Import failed! Check your timetable file!");
}
};
reader.readAsText(file);
}
function exportTimetable(timetable) {
download(
JSON.stringify(timetable),
`timetable-${timetable.id}.json`,
"application/json",
);
}
async function uploadTimetable(id) {
loadingProgress.value = 0.1;
loading.value = true;
const timetableData = timetable.value;
const response = await fetch(
baseUrl + "/timetable?id=" + encodeURIComponent(id),
{
method: "put",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(timetableData),
},
);
if (response.status != 201) {
alert("Failed uploading timetable!");
} else {
loadingProgress.value = 0.5;
await fetchTimetables();
activeProfile.value.timetableId = id;
}
loadingProgress.value = 1;
loading.value = false;
}
</script>
<template>
<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="activeProfile.timetableId == timetable.id"
:editable="true"
:remote="false"
@click="activeProfile.timetableId = timetable.id"
@edit="$router.push('timetable/edit/' + timetable.id)"
@copy="copyTimetable(timetable)"
@delete="
localTimetables.splice(
localTimetables.findIndex((e) => e.id == timetable.id),
1,
)
"
@export="exportTimetable(timetable)"
/>
</div>
<div class="buttons">
<div class="create" @click="createTimetable">
<PlusIcon /> {{ $t("settings.text.createTimetable") }}
</div>
<div class="import" @click="fileInput.click()">
<PaperclipIcon /> {{ $t("settings.text.importTimetable") }}
<input
type="file"
ref="fileInput"
style="display: none"
@change="importTimetable"
/>
</div>
</div>
<h2>{{ $t("settings.heading.remoteTimetables") }}</h2>
<div class="list">
<TimetableCard
v-for="timetable in timetables"
:key="timetable.id"
:timetable="timetable"
:selected="activeProfile.timetableId == timetable.id"
:editable="canEditTimetable(timetable.id)"
:remote="true"
@click="activeProfile.timetableId = timetable.id"
@copy="copyTimetable(timetable)"
@export="exportTimetable(timetable)"
@upload="uploadTimetable(timetable.id)"
/>
</div>
</div>
<RouterView v-else />
</template>
<style scoped>
h2 {
margin: 5px 0px;
}
p {
margin: 5px 0px;
}
.list {
display: grid;
gap: 8px;
}
.buttons {
display: grid;
grid-template-columns: 1fr 1fr;
justify-content: center;
align-items: center;
margin-top: 5px;
user-select: none;
}
.create,
.import {
display: flex;
justify-content: center;
align-items: center;
padding: 10px;
gap: 5px;
cursor: pointer;
}
</style>