feat: add list view with GottesdienstCard components

- New GottesdienstCard component shows each service with its info
- Shows the currently logged-in user's mark as a toggle checkbox
- Lists ministrants who have responded with their mark status
- Admin view hides the checkbox, shows responses only
- New ListView wrapper renders all cards
- PlanView gets a Tabelle/Liste toggle between table and card views
This commit is contained in:
2026-07-14 21:51:55 +02:00
parent d8ca8d743c
commit ad29d0682e
3 changed files with 470 additions and 23 deletions
+88 -23
View File
@@ -5,6 +5,7 @@ import TablePlan from "@/components/TablePlan.vue";
import {API, UnauthorizedError} from "@/services/api";
import type {Gottesdienst, GottesdienstGroup, Mark, SimplifiedMinistrant} from "@/models/models";
import MobilePlan from "@/components/MobilePlan.vue";
import ListView from "@/components/ListView.vue";
import PlanActionBar from "@/components/PlanActionBar.vue";
import {Auth} from "@/services/auth";
import GroupView from "@/components/GroupView.vue";
@@ -38,6 +39,8 @@ const plan = reactive<{
groups: []
})
const mobile = ref(window.innerWidth <= MAX_WIDTH_MOBILE)
const viewMode = ref<"table" | "list">("table")
const currentUsername = ref("")
const editedMarks = reactive<Mark[]>([]);
const editPlanAdmin = ref(false)
const planId = ref(parseInt(route.params.id as string))
@@ -79,6 +82,7 @@ async function loadPlan() {
Auth.loggedInSubject.subscribe(async (loggedIn) => {
console.log("logged in " + loggedIn)
if (loggedIn) {
currentUsername.value = Auth.getUser()
plan.editable = Auth.getPrivileges()
if (Auth.getUser() == "admin") {
editPlanAdmin.value = true
@@ -86,6 +90,7 @@ Auth.loggedInSubject.subscribe(async (loggedIn) => {
console.log("Plan", plan.ministranten)
}
} else {
currentUsername.value = ""
editPlanAdmin.value = false
plan.editable = []
}
@@ -312,40 +317,70 @@ async function importZelebrationsplan() {
<template>
<main>
<div class="container">
<div class="container" :class="{
'mobile-view': mobile && viewMode === 'table'
}">
<GroupView :groups="plan.groups" :admin="editPlanAdmin"
@new="createNewPlan" @delete="deletePlan" @edit="editPlan" class="no-print"/>
<TablePlan
<div class="view-toggle no-print" v-if="plan.gottesdienste.length > 0">
<button
class="flat"
:class="{ active: viewMode === 'table' }"
@click="viewMode = 'table'"
><i>table_rows</i> Tabelle</button>
<button
class="flat"
:class="{ active: viewMode === 'list' }"
@click="viewMode = 'list'"
><i>list</i> Liste</button>
</div>
<ListView
:gottesdienste="sortedGottesdienste"
:ministranten="plan.ministranten"
:marks="getMarks()"
:editable="plan.editable"
:edit="editPlanAdmin"
:small-mode="editPlanAdmin"
@added="addGodi"
@delete="deleteGottedienst"
@edit="createGottesdienst"
:current-username="currentUsername"
@toggle-mark="toggleMark"
@end-edit="editPlanAdmin = false"
@reset-password="resetPassword"
@create-ministrant="createMinistrant"
@edit-ministrant="createMinistrant"
class="plan table"
v-if="!mobile">
class="plan list"
v-if="viewMode === 'list'">
</TablePlan>
</ListView>
<MobilePlan
:gottesdienste="sortedGottesdienste"
:ministranten="plan.ministranten"
:marks="getMarks()"
:editable="plan.editable"
@toggle-mark="toggleMark"
class="plan mobile"
v-else>
<template v-if="viewMode === 'table'">
<TablePlan
:gottesdienste="sortedGottesdienste"
:ministranten="plan.ministranten"
:marks="getMarks()"
:editable="plan.editable"
:edit="editPlanAdmin"
:small-mode="editPlanAdmin"
@added="addGodi"
@delete="deleteGottedienst"
@edit="createGottesdienst"
@toggle-mark="toggleMark"
@end-edit="editPlanAdmin = false"
@reset-password="resetPassword"
@create-ministrant="createMinistrant"
@edit-ministrant="createMinistrant"
class="plan table"
v-if="!mobile">
</MobilePlan>
</TablePlan>
<MobilePlan
:gottesdienste="sortedGottesdienste"
:ministranten="plan.ministranten"
:marks="getMarks()"
:editable="plan.editable"
@toggle-mark="toggleMark"
class="plan mobile"
v-else>
</MobilePlan>
</template>
<PlanActionBar
class="action-bar no-print"
@@ -385,6 +420,36 @@ async function importZelebrationsplan() {
//width: 100%;
}
.container .view-toggle {
display: flex;
gap: 4px;
padding: 8px 12px;
background: #f5f5f5;
border-bottom: 1px solid #d7d5d5;
button {
font-size: 14px;
padding: 6px 14px;
i {
font-size: 18px;
}
&.active {
background: #ffffff;
border-color: #c4c4c4;
}
}
}
.container.mobile-view {
.plan.list {
flex: 1;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
}
.action-bar {
position: fixed;
bottom: 0;