Files
minmal-file-cloud-email-pim…/frontend/src/views/AppLayout.vue
T
Stefan Hacker ba3e619963 feat: Aufgaben (Tasks) mit CalDAV VTODO-Sync
Neuer Menuepunkt "Aufgaben" unterhalb Kontakte.

Backend:
- TaskList + Task + TaskListShare Models
- REST-API: CRUD, Teilen, my-color, Import/Export (.ics mit VTODO, CSV)
- CalDAV: Task-Listen tauchen als Calendar-Collection mit
  supported-calendar-component-set=VTODO im autodiscovery auf
- PROPFIND/REPORT/GET/PUT/DELETE/PROPPATCH/MKCOL fuer /dav/<user>/tl-<id>/
- SSE-Notifications bei Aenderungen

Frontend:
- TasksView mit Listen-Sidebar, Suche, "Erledigte ausblenden"
- Mehrfachauswahl + Bulk-Loeschen, Status-Toggle per Checkbox
- Editor mit Titel/Beschreibung/Faellig/Prioritaet/Status/Fortschritt
- Teilen, Farbe persoenlich anpassen, Import/Export

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 15:07:06 +02:00

177 lines
3.8 KiB
Vue

<template>
<div class="app-layout">
<aside class="sidebar">
<div class="sidebar-header">
<i class="pi pi-cloud"></i>
<span class="sidebar-title">Mini-Cloud</span>
</div>
<nav class="sidebar-nav">
<router-link to="/files" class="nav-item" active-class="active">
<i class="pi pi-folder"></i>
<span>Dateien</span>
</router-link>
<router-link to="/calendar" class="nav-item" active-class="active">
<i class="pi pi-calendar"></i>
<span>Kalender</span>
</router-link>
<router-link to="/contacts" class="nav-item" active-class="active">
<i class="pi pi-users"></i>
<span>Kontakte</span>
</router-link>
<router-link to="/tasks" class="nav-item" active-class="active">
<i class="pi pi-check-square"></i>
<span>Aufgaben</span>
</router-link>
<router-link
v-if="auth.hasEmailAccounts"
to="/email"
class="nav-item"
active-class="active"
>
<i class="pi pi-envelope"></i>
<span>E-Mail</span>
</router-link>
<router-link to="/passwords" class="nav-item" active-class="active">
<i class="pi pi-key"></i>
<span>Passwoerter</span>
</router-link>
<router-link to="/trash" class="nav-item" active-class="active">
<i class="pi pi-trash"></i>
<span>Papierkorb</span>
</router-link>
</nav>
<div class="sidebar-footer">
<router-link
v-if="auth.isAdmin"
to="/admin"
class="nav-item"
active-class="active"
>
<i class="pi pi-cog"></i>
<span>Admin</span>
</router-link>
<router-link to="/settings" class="nav-item" active-class="active">
<i class="pi pi-user"></i>
<span>Einstellungen</span>
</router-link>
<a class="nav-item" @click="handleLogout">
<i class="pi pi-sign-out"></i>
<span>Abmelden</span>
</a>
</div>
</aside>
<main class="main-content">
<router-view :key="$route.fullPath" />
</main>
</div>
</template>
<script setup>
import { useRouter } from 'vue-router'
import { useAuthStore } from '../stores/auth'
const router = useRouter()
const auth = useAuthStore()
function handleLogout() {
auth.logout()
router.push('/login')
}
</script>
<style scoped>
.app-layout {
display: flex;
min-height: 100vh;
}
.sidebar {
width: 240px;
background: var(--p-surface-0);
border-right: 1px solid var(--p-surface-200);
display: flex;
flex-direction: column;
flex-shrink: 0;
}
.sidebar-header {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 1.25rem 1rem;
border-bottom: 1px solid var(--p-surface-200);
}
.sidebar-header i {
font-size: 1.5rem;
color: var(--p-primary-color);
}
.sidebar-title {
font-size: 1.125rem;
font-weight: 600;
}
.sidebar-nav {
flex: 1;
padding: 0.5rem;
display: flex;
flex-direction: column;
gap: 2px;
}
.sidebar-footer {
padding: 0.5rem;
border-top: 1px solid var(--p-surface-200);
display: flex;
flex-direction: column;
gap: 2px;
}
.nav-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.625rem 0.75rem;
border-radius: 8px;
color: var(--p-text-color);
text-decoration: none;
font-size: 0.875rem;
cursor: pointer;
transition: background-color 0.15s;
}
.nav-item:hover {
background: var(--p-surface-100);
}
.nav-item.active {
background: var(--p-primary-50);
color: var(--p-primary-color);
font-weight: 500;
}
.nav-item i {
font-size: 1.125rem;
width: 1.25rem;
text-align: center;
}
.main-content {
flex: 1;
background: var(--p-surface-50);
overflow-y: auto;
}
</style>