Files
2026-07-02 19:57:25 +02:00

479 lines
14 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup>
import AppLayout from '@/layouts/AppLayout.vue'
const documentTiles = [
{
label: 'Schulbefreiung erstellen',
description: 'Erstellt die notwendigen Schulbefreiungsformulare für Veranstaltungen.',
icon: 'graduation-cap',
color: '#e74c3c',
href: '/schulbefreiung',
},
{
label: 'Rechnung erstellen',
description: 'Erstellt eine Rechnung nach BdP-Vorlage.',
icon: 'file-invoice',
color: '#27ae60',
href: '/rechnung',
},
{
label: 'Spendenquittung erstellen',
description: 'Erstellt eine Spendenquittung für Einzelspenden.',
icon: 'heart',
color: '#c0397e',
href: '/spendenquittung',
},
{
label: 'Teilnahmebescheinigung erstellen',
description: 'Erstellt eine Teilnahmebescheinigung.',
icon: 'certificate',
color: '#e67e22',
href: '/teilnahmebescheinigung',
},
]
const externalApps = [
{
name: 'Mereba',
description: 'Mitgliederverwaltung des BdP Landesverbands.',
icon: 'arrow-up-right-from-square',
color: '#1d4899',
href: '#',
},
{
name: 'EFZ-Check',
description: 'Einrichtung und Prüfung des erweiterten Führungszeugnisses.',
icon: 'certificate',
color: '#27ae60',
href: '#',
},
]
const dummyTile = {
label: 'Dummy',
description: 'Platzhalter-Kachel für zukünftige Inhalte.',
icon: 'cube',
color: '#1d4899',
href: '/dummy',
}
</script>
<template>
<AppLayout title="Startseite">
<div class="dashboard">
<!-- ── Hero Banner ── -->
<div class="hero">
<svg class="hero-svg" viewBox="0 0 1200 185"
preserveAspectRatio="xMidYMid slice"
xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="hero-sky" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#9ccae0"/>
<stop offset="100%" stop-color="#d8eef8"/>
</linearGradient>
</defs>
<!-- Himmel -->
<rect width="1200" height="185" fill="url(#hero-sky)"/>
<!--
Jeder Berg ist ein Polygon mit vielen Zwischenpunkten
leicht unregelmäßige Abstände erzeugen organische Hänge.
Die Schneekappen folgen denselben Punkten entlang der Silhouette.
-->
<!-- Berg 1: hinten, hellster -->
<polygon fill="#a8c8dc" points="
150,185
222,160 298,126 362,98 416,70 458,50 492,36 516,28 524,32
542,27 558,34 594,50 638,72 692,102 750,136 808,164 860,185"/>
<!-- Schnee 1: folgt Hangpunkten, Abschluss bei y68 -->
<polygon fill="white" points="
421,68
458,50 492,36 516,28 524,32 542,27 558,34 594,50
624,68"/>
<!-- Berg 2: Mitte -->
<polygon fill="#6090b8" points="
340,185
414,158 490,126 558,94 612,64 648,42 668,26 680,18
690,16 702,20 722,34 758,56 802,86 858,118 915,150 968,170 1020,185"/>
<!-- Schnee 2 -->
<polygon fill="white" points="
616,60
648,42 668,26 680,18 690,16 702,20 722,34 758,56
762,60"/>
<!-- Berg 3: vorne, dunkelster -->
<polygon fill="#3868a2" points="
560,185
638,153 708,118 766,86 816,56 852,32 870,16 880,6
892,12 920,34 958,64 1002,98 1058,134 1115,163 1162,179 1200,185"/>
<!-- Schnee 3 -->
<polygon fill="white" points="
818,53
852,32 870,16 880,6 892,12 920,34 958,64
962,64 942,53"/>
<!-- Waldstreifen am Fuß (rechts) -->
<rect x="655" y="170" width="545" height="15" fill="#1d3c22"/>
<!-- Tannen: Wipfel + Schürze, leicht versetzt in Höhe -->
<g fill="#182e1a">
<polygon points="672,167 663,182 681,182"/>
<polygon points="672,175 661,185 683,185"/>
<polygon points="697,163 687,179 707,179"/>
<polygon points="697,171 685,185 709,185"/>
<polygon points="723,166 713,182 733,182"/>
<polygon points="723,174 711,185 735,185"/>
<polygon points="749,161 738,178 760,178"/>
<polygon points="749,170 737,185 761,185"/>
<polygon points="776,164 765,181 787,181"/>
<polygon points="776,172 763,185 789,185"/>
<polygon points="803,160 791,178 815,178"/>
<polygon points="803,169 789,185 817,185"/>
<polygon points="830,163 818,180 842,180"/>
<polygon points="830,171 816,185 844,185"/>
<polygon points="858,161 845,179 871,179"/>
<polygon points="858,169 843,185 873,185"/>
<polygon points="886,164 873,181 899,181"/>
<polygon points="886,172 871,185 901,185"/>
<polygon points="914,161 900,179 928,179"/>
<polygon points="914,170 898,185 930,185"/>
<polygon points="942,164 928,181 956,181"/>
<polygon points="942,172 926,185 958,185"/>
<polygon points="970,162 956,179 984,179"/>
<polygon points="970,171 954,185 986,185"/>
</g>
</svg>
<!-- Text overlay -->
<div class="hero-text">
<h1 class="hero-title">Willkommen im NEXUS!</h1>
<p class="hero-sub">Deine zentrale Plattform für Dokumente, Tools und Anwendungen.</p>
</div>
</div>
<!-- ── Dokumente erstellen ── -->
<section class="section">
<div class="section-header">
<h2 class="section-title">Dokumente erstellen</h2>
<a href="#" class="section-link">
Alle anzeigen <font-awesome-icon icon="chevron-right" />
</a>
</div>
<div class="doc-grid">
<a v-for="tile in documentTiles" :key="tile.href" :href="tile.href" class="doc-tile">
<div class="doc-tile-top">
<div class="doc-icon-wrap" :style="{ background: tile.color + '18', color: tile.color }">
<font-awesome-icon :icon="tile.icon" />
</div>
</div>
<div class="doc-tile-body">
<p class="doc-title">{{ tile.label }}</p>
<p class="doc-desc">{{ tile.description }}</p>
</div>
</a>
</div>
</section>
<!-- ── Weitere Anwendungen ── -->
<section class="section">
<div class="section-header">
<h2 class="section-title">Weitere Anwendungen</h2>
<a href="#" class="section-link">
Alle anzeigen <font-awesome-icon icon="chevron-right" />
</a>
</div>
<div class="app-list">
<div v-for="app in externalApps" :key="app.name" class="app-card">
<div class="app-icon" :style="{ background: app.color + '18', color: app.color }">
<font-awesome-icon :icon="app.icon" />
</div>
<div class="app-info">
<p class="app-name">{{ app.name }}</p>
<p class="app-desc">{{ app.description }}</p>
</div>
<a :href="app.href" target="_blank" rel="noopener" class="app-open-btn">
<span class="status-dot"></span>
Offen
<font-awesome-icon icon="arrow-up-right-from-square" class="open-icon" />
</a>
</div>
</div>
</section>
<!-- ── Dummy ── -->
<section class="section">
<div class="section-header">
<h2 class="section-title">Weitere Module</h2>
</div>
<div class="doc-grid">
<a :href="dummyTile.href" class="doc-tile">
<div class="doc-tile-top">
<div class="doc-icon-wrap" :style="{ background: dummyTile.color + '18', color: dummyTile.color }">
<font-awesome-icon :icon="dummyTile.icon" />
</div>
</div>
<div class="doc-tile-body">
<p class="doc-title">{{ dummyTile.label }}</p>
<p class="doc-desc">{{ dummyTile.description }}</p>
</div>
</a>
</div>
</section>
</div>
</AppLayout>
</template>
<style scoped>
.dashboard {
display: flex;
flex-direction: column;
gap: 28px;
}
/* ── Hero Banner ── */
.hero {
position: relative;
width: 100%;
height: 190px;
border-radius: 14px;
overflow: hidden;
box-shadow: 0 4px 16px rgba(0,0,0,0.10);
}
.hero-svg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.hero-text {
position: absolute;
left: 36px;
top: 50%;
transform: translateY(-50%);
z-index: 2;
max-width: 420px;
}
.hero-title {
margin: 0 0 8px;
font-size: 1.8rem;
font-weight: 800;
color: #0f2d5e;
text-shadow: 0 1px 3px rgba(255,255,255,0.6);
line-height: 1.15;
}
.hero-sub {
margin: 0;
font-size: 0.9rem;
color: #1c3a6b;
font-weight: 500;
text-shadow: 0 1px 2px rgba(255,255,255,0.5);
}
/* ── Section ── */
.section-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 14px;
}
.section-title {
margin: 0;
font-size: 1rem;
font-weight: 600;
color: #111827;
}
.section-link {
display: flex;
align-items: center;
gap: 5px;
font-size: 0.83rem;
font-weight: 500;
color: #1d4899;
text-decoration: none;
}
.section-link:hover { text-decoration: underline; }
/* ── Document tiles (white cards) ── */
.doc-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 14px;
}
.doc-tile {
background: #fff;
border-radius: 12px;
box-shadow: 0 1px 4px rgba(0,0,0,0.07);
text-decoration: none;
display: flex;
flex-direction: column;
overflow: hidden;
border: 1px solid #f0f0f0;
transition: box-shadow 0.2s, transform 0.15s;
}
.doc-tile:hover {
box-shadow: 0 6px 20px rgba(0,0,0,0.11);
transform: translateY(-2px);
}
.doc-tile-top {
padding: 18px 16px 12px;
}
.doc-icon-wrap {
width: 54px;
height: 54px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.6rem;
}
.doc-tile-body {
padding: 6px 16px 18px;
flex: 1;
}
.doc-title {
margin: 0 0 5px;
font-size: 0.88rem;
font-weight: 600;
color: #111827;
line-height: 1.35;
}
.doc-desc {
margin: 0;
font-size: 0.78rem;
color: #6b7280;
line-height: 1.5;
}
/* ── External app cards ── */
.app-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.app-card {
background: #fff;
border-radius: 12px;
border: 1px solid #f0f0f0;
box-shadow: 0 1px 4px rgba(0,0,0,0.06);
display: flex;
align-items: center;
gap: 16px;
padding: 16px 20px;
transition: box-shadow 0.2s;
}
.app-card:hover {
box-shadow: 0 4px 14px rgba(0,0,0,0.09);
}
.app-icon {
width: 46px;
height: 46px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
flex-shrink: 0;
}
.app-info {
flex: 1;
min-width: 0;
}
.app-name {
margin: 0 0 3px;
font-size: 0.9rem;
font-weight: 600;
color: #111827;
}
.app-desc {
margin: 0;
font-size: 0.78rem;
color: #6b7280;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.app-open-btn {
display: flex;
align-items: center;
gap: 7px;
padding: 7px 16px;
background: #dcfce7;
color: #16a34a;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 600;
text-decoration: none;
white-space: nowrap;
flex-shrink: 0;
transition: background 0.15s;
}
.app-open-btn:hover { background: #bbf7d0; }
.status-dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: #22c55e;
flex-shrink: 0;
}
.open-icon { font-size: 0.72rem; }
/* ── Responsive ── */
@media (max-width: 768px) {
.hero { height: 160px; }
.hero-title { font-size: 1.4rem; }
.doc-grid { grid-template-columns: 1fr 1fr; }
.app-card { flex-wrap: wrap; }
}
@media (max-width: 480px) {
.hero { height: 140px; }
.hero-text { left: 20px; }
.hero-title { font-size: 1.2rem; }
.doc-grid { grid-template-columns: 1fr; }
}
</style>