258 lines
6.9 KiB
Vue
258 lines
6.9 KiB
Vue
<script setup lang="ts">
|
|
|
|
import Dialog from "@/components/dialog/Dialog.vue";
|
|
import type {DialogControls} from "@/components/dialog/dialog";
|
|
import {ref} from "vue";
|
|
import {API} from "@/services/api";
|
|
import vueDropzone from "dropzone-vue3"
|
|
import type {Gottesdienst} from "@/models/models";
|
|
|
|
interface ImportZelebrationsplanProps extends DialogControls {
|
|
onImport(gottesdienste: Gottesdienst[])
|
|
planId: number
|
|
}
|
|
|
|
const props = defineProps<ImportZelebrationsplanProps>()
|
|
|
|
const gottesdienste = ref<(Gottesdienst & {checked: boolean, pfarrei: string})[]>([])
|
|
const pfarreien = ref<string[]>([])
|
|
|
|
const filter = ref("Pfarrei wählen")
|
|
|
|
const importing = ref(false)
|
|
const progress = ref(0)
|
|
|
|
const dropzoneOptions = {
|
|
url: API.getZelebrationsplanParsingUrl(props.planId),
|
|
parallelUploads: 1,
|
|
acceptedFiles: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
disablePreviews: true,
|
|
init: function() {
|
|
this.on("complete", file => {
|
|
console.log("File", file)
|
|
let response = JSON.parse(file.xhr?.responseText)
|
|
gottesdienste.value = Object.keys(response).reduce((acc, next) => {
|
|
return acc.concat(response[next].map((it: any) => ({
|
|
...it,
|
|
checked: false,
|
|
pfarrei: next
|
|
})));
|
|
}, []);
|
|
pfarreien.value = Object.keys(response)
|
|
})
|
|
}
|
|
}
|
|
|
|
|
|
async function importZelebrationsplan() {
|
|
const godis = gottesdienste.value.filter(it => it.checked)
|
|
importing.value = true
|
|
|
|
for(let godi of godis) {
|
|
const id = await API.addGottesdienstNew({
|
|
...godi,
|
|
date: new Date(godi.date),
|
|
attendance: new Date(godi.attendance)
|
|
})
|
|
progress.value += 1
|
|
godi.id = id
|
|
}
|
|
props.onImport(godis)
|
|
props.onDismiss()
|
|
}
|
|
|
|
function canImport() {
|
|
return gottesdienste.value.filter(it => it.checked).length > 0
|
|
}
|
|
|
|
function amountChecked() {
|
|
return gottesdienste.value.filter(it => it.checked).length
|
|
}
|
|
|
|
function selectAll() {
|
|
return gottesdienste.value.forEach(it => {
|
|
if(it.pfarrei == filter.value) {
|
|
it.checked = true
|
|
}
|
|
})
|
|
}
|
|
|
|
function deselectAll() {
|
|
return gottesdienste.value.forEach(it => it.checked = false)
|
|
}
|
|
|
|
|
|
function two(s) {
|
|
return (s < 10 ? "0" : "") + s
|
|
}
|
|
|
|
function formatDay(time) {
|
|
let date = new Date(time)
|
|
return two(date.getDate()) + "." + two(date.getMonth() + 1) + "."
|
|
}
|
|
|
|
function formatTime(time) {
|
|
let date = new Date(time)
|
|
return two(date.getHours()) + ":" + two(date.getMinutes())
|
|
}
|
|
|
|
function formatWeekday(time) {
|
|
let date = new Date(time)
|
|
switch (date.getDay()) {
|
|
case 1:
|
|
return "Mo";
|
|
case 2:
|
|
return "Di";
|
|
case 3:
|
|
return "Mi";
|
|
case 4:
|
|
return "Do";
|
|
case 5:
|
|
return "Fr";
|
|
case 6:
|
|
return "Sa";
|
|
case 0:
|
|
return "So"
|
|
}
|
|
}
|
|
|
|
function getGottesdienste() {
|
|
return gottesdienste.value.filter(it => it["pfarrei"] == filter.value || it.checked)
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
|
|
<Dialog class="dialog">
|
|
<h3>Zelebrationsplan importieren</h3>
|
|
|
|
<vue-dropzone v-if="gottesdienste.length == 0" id="dropzone" :options="dropzoneOptions"/>
|
|
|
|
<div class="select" v-if="gottesdienste.length > 0 && !importing">
|
|
<span>Gottesdienste ausgewählt: <strong>{{amountChecked()}} / {{getGottesdienste().length}}</strong></span>
|
|
<div style="margin: 10px 0; display: flex; align-items: center">
|
|
<select v-model="filter">
|
|
<option disabled selected>Pfarrei wählen</option>
|
|
<option v-for="pfarrei in pfarreien">{{pfarrei}}</option>
|
|
</select>
|
|
<template v-if="filter">
|
|
<button class="flat" @click="selectAll"><i class="icon">select_all</i>Alle auswählen</button>
|
|
<button class="flat" @click="deselectAll"><i class="icon">deselect</i>Alle abwählen</button>
|
|
</template>
|
|
</div>
|
|
<div class="list">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<td></td>
|
|
<td>Titel</td>
|
|
<td></td>
|
|
<td>Datum</td>
|
|
<td>Uhrzeit</td>
|
|
<td>Anwesenheit</td>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="godi in getGottesdienste()" @click.stop="godi.checked = !godi.checked">
|
|
<td><input type="checkbox" v-model="godi.checked"/></td>
|
|
<td style="width: 100%">{{godi.name}}</td>
|
|
<td>{{formatWeekday(godi.date)}}</td>
|
|
<td>{{formatDay(godi.date)}}</td>
|
|
<td>{{formatTime(godi.date)}}</td>
|
|
<td>{{formatTime(godi.attendance)}}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div v-if="importing" style="display: flex; align-items: center; justify-content: center; padding: 100px; flex-direction: column; gap: 10px">
|
|
<p>Gottesdienste werden importiert...</p>
|
|
<p style="font-size: 3rem"><strong>{{progress}} / {{amountChecked()}}</strong></p>
|
|
</div>
|
|
|
|
<div class="buttons" style="display: flex; justify-content: end; margin-top: 20px;">
|
|
<button @click="onDismiss">Abbrechen</button>
|
|
<button v-if="canImport()" @click="importZelebrationsplan">Importieren</button>
|
|
</div>
|
|
</Dialog>
|
|
|
|
</template>
|
|
|
|
<style scoped lang="less">
|
|
|
|
.dialog {
|
|
display: flex;
|
|
flex-direction: column;
|
|
width: min(90vw, 700px);
|
|
|
|
h3{
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
.input {
|
|
margin-bottom: 16px;
|
|
}
|
|
}
|
|
|
|
.upload {
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: center;
|
|
padding: 60px 100px;
|
|
border: 1px solid #e1e1e1;
|
|
border-radius: 4px;
|
|
text-align: center;
|
|
cursor: pointer;
|
|
user-select: none;
|
|
transition: background 200ms;
|
|
.icon {
|
|
font-size: 50px;
|
|
color: #989898;
|
|
}
|
|
span {
|
|
margin-top: 20px;
|
|
color: #bdbdbd;
|
|
font-size: 14px;
|
|
line-height: 20px;
|
|
}
|
|
|
|
&:hover {
|
|
background: #f9f9f9;
|
|
}
|
|
}
|
|
|
|
.list {
|
|
max-height: 400px;
|
|
overflow-y: auto;
|
|
border: 1px solid #f1f1f1;
|
|
border-radius: 4px;
|
|
table {
|
|
width: 100%;
|
|
height: 100%;
|
|
border-spacing: 0;
|
|
|
|
thead {
|
|
font-weight: bold;
|
|
}
|
|
|
|
tr td{
|
|
padding: 12px 10px;
|
|
border-bottom: 1px solid #e1e1e1;
|
|
transition: background 200ms;
|
|
}
|
|
|
|
tr:hover td {
|
|
cursor: pointer;
|
|
background: #f6f6f6;
|
|
}
|
|
tbody tr:last-child td{
|
|
border-bottom: none;
|
|
}
|
|
}
|
|
}
|
|
|
|
</style> |