Add timefix and vacation routes to backend; update frontend for new routes and page titles

This commit is contained in:
Torsten Schulz (local)
2025-10-17 15:54:30 +02:00
parent e95bb4cb76
commit b65a13d815
16 changed files with 1913 additions and 13 deletions

View File

@@ -0,0 +1,216 @@
<template>
<Teleport to="body">
<Transition name="modal">
<div v-if="show" class="modal-overlay" @click.self="onCancel">
<div class="modal-container">
<div class="modal-header">
<h3 class="modal-title">{{ title }}</h3>
<button class="modal-close" @click="onCancel" aria-label="Schließen">×</button>
</div>
<div class="modal-body">
<p>{{ message }}</p>
</div>
<div class="modal-footer">
<button
v-if="type === 'confirm'"
class="btn btn-secondary"
@click="onCancel"
>
{{ cancelText }}
</button>
<button
class="btn"
:class="type === 'confirm' ? 'btn-danger' : 'btn-primary'"
@click="onConfirm"
>
{{ confirmText }}
</button>
</div>
</div>
</div>
</Transition>
</Teleport>
</template>
<script setup>
defineProps({
show: {
type: Boolean,
default: false
},
title: {
type: String,
default: 'Hinweis'
},
message: {
type: String,
required: true
},
type: {
type: String,
default: 'alert', // 'alert' oder 'confirm'
validator: (value) => ['alert', 'confirm'].includes(value)
},
confirmText: {
type: String,
default: 'OK'
},
cancelText: {
type: String,
default: 'Abbrechen'
}
})
const emit = defineEmits(['confirm', 'cancel'])
const onConfirm = () => {
emit('confirm')
}
const onCancel = () => {
emit('cancel')
}
</script>
<style scoped>
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 2000;
}
.modal-container {
background: white;
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
max-width: 500px;
width: 90%;
max-height: 90vh;
overflow: auto;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 16px;
background: linear-gradient(135deg, #f0ffec, #e8f5e0);
border-bottom: 2px solid #d0f0c0;
border-radius: 8px 8px 0 0;
}
.modal-title {
margin: 0;
font-size: 16px;
font-weight: 600;
color: #2c5e1a;
}
.modal-close {
background: none;
border: none;
font-size: 24px;
line-height: 1;
color: #999;
cursor: pointer;
padding: 0;
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
transition: all 0.2s;
}
.modal-close:hover {
background: #f0f0f0;
color: #666;
}
.modal-body {
padding: 24px;
color: #555;
line-height: 1.6;
}
.modal-body p {
margin: 0;
}
.modal-footer {
padding: 16px 24px;
border-top: 1px solid #e0e0e0;
display: flex;
justify-content: flex-end;
gap: 12px;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 4px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.btn-primary {
background: #4CAF50;
color: white;
}
.btn-primary:hover {
background: #45a049;
}
.btn-secondary {
background: #ecf0f1;
color: #555;
}
.btn-secondary:hover {
background: #d5dbdd;
}
.btn-danger {
background: #e74c3c;
color: white;
}
.btn-danger:hover {
background: #c0392b;
}
/* Transition */
.modal-enter-active,
.modal-leave-active {
transition: opacity 0.3s ease;
}
.modal-enter-active .modal-container,
.modal-leave-active .modal-container {
transition: transform 0.3s ease;
}
.modal-enter-from,
.modal-leave-to {
opacity: 0;
}
.modal-enter-from .modal-container,
.modal-leave-to .modal-container {
transform: scale(0.9);
}
</style>