✨ Implement importing and exporting of timetables
This commit is contained in:
6
package-lock.json
generated
6
package-lock.json
generated
@ -10,6 +10,7 @@
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"dayjs": "^1.11.3",
|
||||
"downloadjs": "^1.4.7",
|
||||
"lucide-vue-next": "^0.233.0",
|
||||
"swiper": "^9.3.2",
|
||||
"vue": "^3.2.37",
|
||||
@ -3108,6 +3109,11 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/downloadjs": {
|
||||
"version": "1.4.7",
|
||||
"resolved": "https://registry.npmjs.org/downloadjs/-/downloadjs-1.4.7.tgz",
|
||||
"integrity": "sha512-LN1gO7+u9xjU5oEScGFKvXhYf7Y/empUIIEAGBs1LzUq/rg5duiDrkuH5A2lQGd5jfMOb9X9usDa2oVXwJ0U/Q=="
|
||||
},
|
||||
"node_modules/ejs": {
|
||||
"version": "3.1.9",
|
||||
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz",
|
||||
|
@ -11,6 +11,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"dayjs": "^1.11.3",
|
||||
"downloadjs": "^1.4.7",
|
||||
"lucide-vue-next": "^0.233.0",
|
||||
"swiper": "^9.3.2",
|
||||
"vue": "^3.2.37",
|
||||
|
@ -8,9 +8,10 @@ import {
|
||||
AlertCircleIcon,
|
||||
CopyIcon,
|
||||
} from "lucide-vue-next";
|
||||
import { DownloadIcon } from "lucide-vue-next";
|
||||
|
||||
defineProps(["timetable", "editable", "selected"]);
|
||||
defineEmits(["click", "edit", "delete", "copy"]);
|
||||
defineEmits(["click", "edit", "delete", "copy", "export"]);
|
||||
|
||||
const deleteConfirm = ref(false);
|
||||
|
||||
@ -36,6 +37,7 @@ watch(deleteConfirm, (value) => {
|
||||
</div>
|
||||
|
||||
<div class="buttons">
|
||||
<DownloadIcon @click="$emit('export')" />
|
||||
<Edit2Icon v-if="editable" @click="$emit('edit')" />
|
||||
<CopyIcon @click="$emit('copy')" />
|
||||
<TrashIcon
|
||||
|
@ -29,7 +29,8 @@ export const strings = {
|
||||
text: {
|
||||
filtering:
|
||||
"Select a class here so the correct timetable is used and only relevant substitutions are shown.",
|
||||
createTimetable: "Create a new Timetable",
|
||||
createTimetable: "Create Timetable",
|
||||
importTimetable: "Import Timetable",
|
||||
timetableGroups:
|
||||
"A timetable group defines, which lesson should be displayed, and which substitution should be shown if there are multiple possibilities for a single lesson within one class.",
|
||||
language: "Change the language of all texts in the application.",
|
||||
@ -145,7 +146,8 @@ export const strings = {
|
||||
text: {
|
||||
filtering:
|
||||
"Wähle hier deine Klasse aus, damit du deinen Stundenplan angezeigt bekommst und du nur relevante Vertretungen siehst.",
|
||||
createTimetable: "Neuen Stundenplan erstellen",
|
||||
createTimetable: "Stundenplan erstellen",
|
||||
importTimetable: "Stundenplan importieren",
|
||||
timetableGroups:
|
||||
"Stundenplan-Gruppen legen fest, welche Stundenplan-Daten du angezeigt bekommst, wenn es mehrere Möglichkeiten für eine Stunde gibt.",
|
||||
language: "Ändere die Sprache aller Texte dieser Anwendung.",
|
||||
|
@ -1,8 +1,9 @@
|
||||
<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";
|
||||
import { PlusIcon, PaperclipIcon } from "lucide-vue-next";
|
||||
import { toRaw, ref } from "vue";
|
||||
import download from "downloadjs";
|
||||
|
||||
function copyTimetable(timetable) {
|
||||
const newTimetable = structuredClone(toRaw(timetable));
|
||||
@ -18,6 +19,33 @@ function createTimetable() {
|
||||
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) {
|
||||
alert("Import failed! Check your timetable file!");
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
function exportTimetable(timetable) {
|
||||
download(
|
||||
JSON.stringify(timetable),
|
||||
`timetable-${timetable.id}.json`,
|
||||
"application/json"
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -39,10 +67,22 @@ function createTimetable() {
|
||||
1
|
||||
)
|
||||
"
|
||||
@export="exportTimetable(timetable)"
|
||||
/>
|
||||
</div>
|
||||
<div class="create" @click="createTimetable">
|
||||
<PlusIcon /> {{ $t("settings.text.createTimetable") }}
|
||||
<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">
|
||||
@ -54,6 +94,7 @@ function createTimetable() {
|
||||
:editable="false"
|
||||
@click="timetableId = timetable.id"
|
||||
@copy="copyTimetable(timetable)"
|
||||
@export="exportTimetable(timetable)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -74,12 +115,22 @@ p {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.create {
|
||||
display: flex;
|
||||
.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>
|
||||
|
Reference in New Issue
Block a user