723 lines
21 KiB
HTML
Executable File
723 lines
21 KiB
HTML
Executable File
{% extends "base.html" %}
|
|
|
|
{% block title %}Register{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container">
|
|
<div class="header-section">
|
|
<h1>Neuen Benutzer registrieren</h1>
|
|
<p class="subtitle">Erstellen Sie ein neues Benutzerkonto oder importieren Sie mehrere Benutzer per CSV</p>
|
|
</div>
|
|
|
|
<div class="flash-container">
|
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
|
{% if messages %}
|
|
{% for category, message in messages %}
|
|
<div class="flash {{ category }}">
|
|
<span class="flash-icon">{% if category == 'success' %}✓{% else %}!{% endif %}</span>
|
|
<span class="flash-message">{{ message }}</span>
|
|
</div>
|
|
{% endfor %}
|
|
{% endif %}
|
|
{% endwith %}
|
|
</div>
|
|
|
|
<div class="content">
|
|
<!-- 1. CSV BULK IMPORT CARD -->
|
|
<div class="form-card" style="margin-bottom: 2rem; border-top: 4px solid #059669;">
|
|
<div class="card-header" style="margin-bottom: 1rem;">
|
|
<h2>Massenregistrierung via CSV</h2>
|
|
<p class="subtitle" style="color: #4b5563;">
|
|
Laden Sie eine CSV-Datei hoch (Format: <code>Vorname, Nachname</code>).
|
|
Benutzernamen und sichere Passwörter werden serverseitig generiert. Nach dem Upload erhalten Sie direkt ein PDF mit Zugangsdaten (2 pro Seite zum Ausschneiden).
|
|
</p>
|
|
</div>
|
|
|
|
<form method="POST" action="{{ url_for('register_csv') }}" enctype="multipart/form-data">
|
|
<div class="form-group">
|
|
<label for="permission-preset-csv">Berechtigungs-Preset für alle CSV-Benutzer</label>
|
|
<select id="permission-preset-csv" name="permission_preset" class="form-select" style="margin-bottom: 1rem;">
|
|
{% for preset_key, preset in permission_presets.items() %}
|
|
<option value="{{ preset_key }}">{{ preset.label }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
|
|
<label for="csv_file">CSV-Datei auswählen</label>
|
|
<div class="input-container">
|
|
<span class="input-icon">📄</span>
|
|
<input type="file" id="csv_file" name="csv_file" accept=".csv" required style="padding: 10px;">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group form-actions" style="margin-top: 1.5rem;">
|
|
<button type="submit" class="action-button" style="background-color: #059669; color: white;">
|
|
📥 CSV Importieren & Zugangsdaten-PDF Herunterladen
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- 2. SINGLE USER REGISTRATION CARD -->
|
|
<div class="form-card">
|
|
<h2>Einzelnen Benutzer registrieren</h2>
|
|
<form method="POST" action="{{ url_for('register') }}">
|
|
|
|
<div class="form-group">
|
|
<label for="name">Vorname</label>
|
|
<div class="input-container">
|
|
<span class="input-icon">👤</span>
|
|
<input type="text" id="name" name="name" placeholder="Geben Sie den Vornamen ein" required onchange="generateUsername()" oninput="generateUsername()">
|
|
</div>
|
|
|
|
<label for="last-name">Nachname</label>
|
|
<div class="input-container">
|
|
<span class="input-icon">👤</span>
|
|
<input type="text" id="last-name" name="last-name" placeholder="Geben Sie den Nachnamen ein" required onchange="generateUsername()" oninput="generateUsername()">
|
|
</div>
|
|
|
|
<label for="username">Benutzername <span style="color: #9ca3af;">(Vorschau - wird serverseitig finalisiert)</span></label>
|
|
<div class="input-container">
|
|
<span class="input-icon">👤</span>
|
|
<input type="text" id="username" name="username" placeholder="Automatisch aus Name und Nachname" readonly style="background-color: #f3f4f6; cursor: not-allowed;">
|
|
</div>
|
|
<p class="anonymize-hint">Klarnamen werden nur zur Erzeugung des Benutzernamens als Kürzel (z.B. SimFri) verwendet; bei Kollision wird automatisch ein Buchstabe mehr genommen.</p>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="password">Passwort</label>
|
|
<div class="password-rules" id="password-rules" aria-live="polite">
|
|
<p class="password-rules-title">Passwort-Anforderungen:</p>
|
|
<ul>
|
|
<li id="pw-rule-length" class="pw-rule">Mindestens 12 Zeichen</li>
|
|
<li id="pw-rule-lower" class="pw-rule">Mindestens ein Kleinbuchstabe</li>
|
|
<li id="pw-rule-upper" class="pw-rule">Mindestens ein Grossbuchstabe</li>
|
|
<li id="pw-rule-digit" class="pw-rule">Mindestens eine Zahl</li>
|
|
<li id="pw-rule-symbol" class="pw-rule">Mindestens ein Sonderzeichen</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="input-wrapper">
|
|
<div class="input-container">
|
|
<span class="input-icon">🔒</span>
|
|
<input
|
|
type="password"
|
|
id="password"
|
|
name="password"
|
|
placeholder="Geben Sie ein sicheres Passwort ein"
|
|
required
|
|
pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).{12,}">
|
|
<button type="button" id="toggle-pw-btn" class="toggle-pw-btn" onclick="togglePasswordVisibility()" style="background:none; border:none; cursor:pointer; padding-right:10px;">👁️</button>
|
|
</div>
|
|
</div>
|
|
<div class="pw-actions" style="margin-top: 8px;">
|
|
<button type="button" class="btn-secondary" onclick="generateSecurePassword()">Passwort generieren</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="permission-preset">Berechtigungs-Preset</label>
|
|
<select id="permission-preset" name="permission_preset" class="form-select">
|
|
{% for preset_key, preset in permission_presets.items() %}
|
|
<option value="{{ preset_key }}">{{ preset.label }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
|
|
<label style="display:flex; align-items:center; gap:8px; margin-top:12px; color:#1f2937;">
|
|
<input type="checkbox" id="use-custom-permissions" name="use_custom_permissions" style="width:auto;">
|
|
Individuelle Berechtigungen statt Preset setzen
|
|
</label>
|
|
|
|
<div id="custom-permissions" style="display:none; margin-top:12px;">
|
|
<div class="permission-panels">
|
|
<div class="permission-panel">
|
|
<h4>Aktionsrechte</h4>
|
|
{% for action_key, action_label in permission_action_options %}
|
|
<label class="permission-check">
|
|
<input type="checkbox" class="permission-action-checkbox" name="action_{{ action_key }}">
|
|
<span>{{ action_label }}</span>
|
|
</label>
|
|
{% endfor %}
|
|
</div>
|
|
<div class="permission-panel">
|
|
<h4>Seitenrechte</h4>
|
|
{% for endpoint_name, endpoint_label in permission_page_options %}
|
|
<label class="permission-check">
|
|
<input type="checkbox" class="permission-page-checkbox" name="page_{{ endpoint_name }}">
|
|
<span>{{ endpoint_label }}</span>
|
|
</label>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group form-actions">
|
|
<button type="submit" class="action-button register-button">Benutzer registrieren</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Hilfsfunktion: Umlaute auflösen und Sonderzeichen entfernen
|
|
function cleanNameForUsername(text) {
|
|
if (!text) return '';
|
|
let cleaned = text.trim().toLowerCase()
|
|
.replace(/ä/g, 'ae')
|
|
.replace(/ö/g, 'oe')
|
|
.replace(/ü/g, 'ue')
|
|
.replace(/ß/g, 'ss')
|
|
.replace(/[^a-z]/g, '');
|
|
return cleaned;
|
|
}
|
|
|
|
// Hilfsfunktion: Erster Buchstabe groß
|
|
function formatPart(str, len) {
|
|
if (!str) return '';
|
|
const part = str.slice(0, len);
|
|
return part.charAt(0).toUpperCase() + part.slice(1);
|
|
}
|
|
|
|
// Generiert die Vorschau des Benutzernamens (z.B. SimFri)
|
|
function generateUsername() {
|
|
const firstName = cleanNameForUsername(document.getElementById('name').value);
|
|
const lastName = cleanNameForUsername(document.getElementById('last-name').value);
|
|
let username = '';
|
|
|
|
if (firstName && lastName) {
|
|
username = formatPart(firstName, 3) + formatPart(lastName, 3);
|
|
} else if (firstName) {
|
|
username = formatPart(firstName, 6);
|
|
} else if (lastName) {
|
|
username = formatPart(lastName, 6);
|
|
}
|
|
|
|
const usernameField = document.getElementById('username');
|
|
if (usernameField) {
|
|
usernameField.value = username || '';
|
|
}
|
|
}
|
|
|
|
// PASSWORT GENERATOR
|
|
function generateSecurePassword() {
|
|
const length = 16;
|
|
const charsetLower = "abcdefghijklmnopqrstuvwxyz";
|
|
const charsetUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
const charsetNum = "0123456789";
|
|
const charsetSym = "!@#$%^&*()_+~|}{[]:;?><,.-=";
|
|
|
|
let password = "";
|
|
// Garantiert mindestens 1 Zeichen aus jeder Kategorie
|
|
password += charsetLower[Math.floor(Math.random() * charsetLower.length)];
|
|
password += charsetUpper[Math.floor(Math.random() * charsetUpper.length)];
|
|
password += charsetNum[Math.floor(Math.random() * charsetNum.length)];
|
|
password += charsetSym[Math.floor(Math.random() * charsetSym.length)];
|
|
|
|
const allChars = charsetLower + charsetUpper + charsetNum + charsetSym;
|
|
// Restliche Zeichen auffüllen
|
|
for (let i = 4; i < length; i++) {
|
|
password += allChars[Math.floor(Math.random() * allChars.length)];
|
|
}
|
|
|
|
// Passwort durchmischen
|
|
password = password.split('').sort(() => 0.5 - Math.random()).join('');
|
|
|
|
const pwField = document.getElementById('password');
|
|
pwField.value = password;
|
|
|
|
// Automatisch sichtbar machen
|
|
pwField.type = 'text';
|
|
const toggleBtn = document.getElementById('toggle-pw-btn');
|
|
if (toggleBtn) toggleBtn.textContent = '🙈';
|
|
|
|
updatePasswordRules();
|
|
}
|
|
|
|
// PASSWORT SICHTBARKEIT UMSCHALTEN
|
|
function togglePasswordVisibility() {
|
|
const pwField = document.getElementById('password');
|
|
const toggleBtn = document.getElementById('toggle-pw-btn');
|
|
if (pwField.type === "password") {
|
|
pwField.type = "text";
|
|
if (toggleBtn) toggleBtn.textContent = '🙈';
|
|
} else {
|
|
pwField.type = "password";
|
|
if (toggleBtn) toggleBtn.textContent = '👁️';
|
|
}
|
|
}
|
|
|
|
// Globale Funktion für Passwort-Update
|
|
function updatePasswordRules() {
|
|
const passwordInput = document.getElementById('password');
|
|
if (!passwordInput) return;
|
|
|
|
const value = String(passwordInput.value || '');
|
|
|
|
const setRuleState = (id, ok) => {
|
|
const node = document.getElementById(id);
|
|
if (node) node.classList.toggle('ok', !!ok);
|
|
};
|
|
|
|
setRuleState('pw-rule-length', value.length >= 12);
|
|
setRuleState('pw-rule-lower', /[a-z]/.test(value));
|
|
setRuleState('pw-rule-upper', /[A-Z]/.test(value));
|
|
setRuleState('pw-rule-digit', /[0-9]/.test(value));
|
|
setRuleState('pw-rule-symbol', /[^A-Za-z0-9]/.test(value));
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
const permissionPresets = {{ permission_presets | tojson }};
|
|
const presetSelect = document.getElementById('permission-preset');
|
|
const useCustomPermissions = document.getElementById('use-custom-permissions');
|
|
const customPermissions = document.getElementById('custom-permissions');
|
|
|
|
function applyPresetToPermissionForm(presetKey) {
|
|
const preset = permissionPresets[presetKey] || {};
|
|
const actionDefaults = preset.actions || {};
|
|
const pageDefaults = preset.pages || {};
|
|
|
|
document.querySelectorAll('.permission-action-checkbox').forEach(function (checkbox) {
|
|
const key = checkbox.name.replace('action_', '');
|
|
checkbox.checked = !!actionDefaults[key];
|
|
});
|
|
|
|
document.querySelectorAll('.permission-page-checkbox').forEach(function (checkbox) {
|
|
const key = checkbox.name.replace('page_', '');
|
|
checkbox.checked = !!pageDefaults[key];
|
|
});
|
|
}
|
|
|
|
function toggleCustomPermissions() {
|
|
if (!useCustomPermissions || !customPermissions) {
|
|
return;
|
|
}
|
|
customPermissions.style.display = useCustomPermissions.checked ? 'block' : 'none';
|
|
}
|
|
|
|
if (presetSelect) {
|
|
presetSelect.addEventListener('change', function () {
|
|
applyPresetToPermissionForm(this.value);
|
|
});
|
|
applyPresetToPermissionForm(presetSelect.value);
|
|
}
|
|
|
|
if (useCustomPermissions) {
|
|
useCustomPermissions.addEventListener('change', toggleCustomPermissions);
|
|
toggleCustomPermissions();
|
|
}
|
|
|
|
const passwordInput = document.getElementById('password');
|
|
if (passwordInput) {
|
|
passwordInput.addEventListener('input', updatePasswordRules);
|
|
passwordInput.addEventListener('blur', updatePasswordRules);
|
|
updatePasswordRules();
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<style>
|
|
:root {
|
|
--primary-color: #3498db;
|
|
--primary-dark: #2980b9;
|
|
--success-color: #2ecc71;
|
|
--success-dark: #27ae60;
|
|
--csv-color: #059669;
|
|
--csv-dark: #047857;
|
|
--error-color: #e74c3c;
|
|
--text-color: var(--ui-text, #1f2937);
|
|
--light-bg: #f9f9f9;
|
|
--border-radius: 8px;
|
|
--shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
|
|
--transition: all 0.3s ease;
|
|
}
|
|
|
|
body {
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
background-color: var(--light-bg);
|
|
margin: 0;
|
|
padding: 0;
|
|
color: var(--text-color);
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.container {
|
|
max-width: 800px;
|
|
margin: 2rem auto;
|
|
padding: 0 1.5rem;
|
|
}
|
|
|
|
.header-section {
|
|
text-align: center;
|
|
margin-bottom: 2rem;
|
|
padding-bottom: 1.5rem;
|
|
border-bottom: 1px solid #eee;
|
|
}
|
|
|
|
.header-section h1 {
|
|
font-size: 2.5rem;
|
|
margin-bottom: 0.5rem;
|
|
color: var(--primary-color);
|
|
}
|
|
|
|
.subtitle {
|
|
font-size: 1.05rem;
|
|
color: #6b7280;
|
|
margin-top: 0;
|
|
}
|
|
|
|
.content {
|
|
background-color: var(--ui-surface, #ffffff);
|
|
padding: 2rem;
|
|
border-radius: var(--border-radius);
|
|
box-shadow: var(--shadow);
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
/* Karten-Layout für Einzel- & Massenregistrierung */
|
|
.form-card {
|
|
max-width: 620px;
|
|
margin: 0 auto 2.5rem auto;
|
|
padding: 1.5rem;
|
|
background: var(--ui-surface-soft, #f9fafb);
|
|
border: 1px solid #e5e7eb;
|
|
border-radius: var(--border-radius);
|
|
}
|
|
|
|
.form-card:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.form-card h2 {
|
|
font-size: 1.4rem;
|
|
margin-top: 0;
|
|
margin-bottom: 0.5rem;
|
|
color: #111827;
|
|
}
|
|
|
|
.form-group {
|
|
margin-bottom: 1.25rem;
|
|
}
|
|
|
|
.form-group label {
|
|
display: block;
|
|
margin-bottom: 0.4rem;
|
|
font-weight: 600;
|
|
font-size: 0.95rem;
|
|
color: #374151;
|
|
}
|
|
|
|
.input-container {
|
|
position: relative;
|
|
width: 100%;
|
|
}
|
|
|
|
.input-icon {
|
|
position: absolute;
|
|
left: 1rem;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
color: #9ca3af;
|
|
font-size: 1.1rem;
|
|
pointer-events: none;
|
|
z-index: 1;
|
|
}
|
|
|
|
/* Eingabefelder & Selects */
|
|
input[type="text"],
|
|
input[type="password"],
|
|
input[type="file"],
|
|
.form-select {
|
|
width: 100%;
|
|
padding: 0.75rem 1rem 0.75rem 2.8rem;
|
|
border: 1px solid #d1d5db;
|
|
border-radius: var(--border-radius);
|
|
font-size: 0.95rem;
|
|
background-color: #ffffff;
|
|
transition: var(--transition);
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
input[type="text"]:focus,
|
|
input[type="password"]:focus,
|
|
input[type="file"]:focus,
|
|
.form-select:focus {
|
|
border-color: var(--primary-color);
|
|
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);
|
|
outline: none;
|
|
}
|
|
|
|
.form-select {
|
|
padding-left: 1rem;
|
|
cursor: pointer;
|
|
}
|
|
|
|
/* Stylings für den CSV-Datei-Upload */
|
|
input[type="file"] {
|
|
padding-left: 2.8rem;
|
|
cursor: pointer;
|
|
}
|
|
|
|
input[type="file"]::file-selector-button {
|
|
padding: 0.35rem 0.75rem;
|
|
border-radius: 4px;
|
|
border: 1px solid #d1d5db;
|
|
background: #f3f4f6;
|
|
color: #374151;
|
|
cursor: pointer;
|
|
margin-right: 12px;
|
|
font-weight: 500;
|
|
transition: var(--transition);
|
|
}
|
|
|
|
input[type="file"]::file-selector-button:hover {
|
|
background: #e5e7eb;
|
|
}
|
|
|
|
input::placeholder {
|
|
color: #9ca3af;
|
|
}
|
|
|
|
code {
|
|
background: #e0f2fe;
|
|
color: #0369a1;
|
|
padding: 2px 6px;
|
|
border-radius: 4px;
|
|
font-family: monospace;
|
|
font-size: 0.9em;
|
|
}
|
|
|
|
/* Buttons */
|
|
.form-actions {
|
|
margin-top: 1.5rem;
|
|
}
|
|
|
|
.action-button {
|
|
background-color: var(--primary-color);
|
|
color: white;
|
|
border: none;
|
|
padding: 0.8rem 1.5rem;
|
|
border-radius: var(--border-radius);
|
|
cursor: pointer;
|
|
font-weight: 600;
|
|
transition: var(--transition);
|
|
width: 100%;
|
|
font-size: 1rem;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.action-button:hover {
|
|
background-color: var(--primary-dark);
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.register-button {
|
|
background-color: var(--success-color);
|
|
}
|
|
|
|
.register-button:hover {
|
|
background-color: var(--success-dark);
|
|
}
|
|
|
|
/* Toggle-Button für Passwort-Auge */
|
|
.input-wrapper {
|
|
position: relative;
|
|
width: 100%;
|
|
}
|
|
|
|
.toggle-pw-btn {
|
|
position: absolute;
|
|
right: 0.75rem;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
background: none;
|
|
border: none;
|
|
cursor: pointer;
|
|
font-size: 1.1rem;
|
|
padding: 4px;
|
|
z-index: 2;
|
|
opacity: 0.7;
|
|
transition: opacity 0.2s;
|
|
}
|
|
|
|
.toggle-pw-btn:hover {
|
|
opacity: 1;
|
|
}
|
|
|
|
/* Passwort-Regeln Box */
|
|
.password-rules {
|
|
margin-bottom: 10px;
|
|
padding: 10px 12px;
|
|
border: 1px solid #e5e7eb;
|
|
border-radius: 8px;
|
|
background: #ffffff;
|
|
}
|
|
|
|
.password-rules-title {
|
|
margin: 0 0 6px;
|
|
font-weight: 700;
|
|
color: #1f2937;
|
|
font-size: 0.88rem;
|
|
}
|
|
|
|
.password-rules ul {
|
|
list-style: none;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
.pw-rule {
|
|
position: relative;
|
|
padding-left: 20px;
|
|
margin: 4px 0;
|
|
color: #dc2626;
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
.pw-rule::before {
|
|
content: '✗';
|
|
position: absolute;
|
|
left: 0;
|
|
top: 0;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.pw-rule.ok {
|
|
color: #166534;
|
|
}
|
|
|
|
.pw-rule.ok::before {
|
|
content: '✓';
|
|
}
|
|
|
|
.pw-actions {
|
|
display: flex;
|
|
gap: 8px;
|
|
margin-top: 8px;
|
|
}
|
|
|
|
.btn-secondary {
|
|
padding: 6px 12px;
|
|
border: 1px solid #d1d5db;
|
|
background-color: #ffffff;
|
|
color: #374151;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
font-size: 0.88rem;
|
|
font-weight: 500;
|
|
transition: var(--transition);
|
|
}
|
|
|
|
.btn-secondary:hover {
|
|
background-color: #f3f4f6;
|
|
border-color: #9ca3af;
|
|
}
|
|
|
|
/* Hinweise & Rechte-Panels */
|
|
.anonymize-hint {
|
|
margin-top: 8px;
|
|
margin-bottom: 0;
|
|
background: #f0f9ff;
|
|
border: 1px solid #bae6fd;
|
|
border-radius: 6px;
|
|
padding: 8px 12px;
|
|
color: #0369a1;
|
|
font-size: 0.88rem;
|
|
}
|
|
|
|
.permission-panels {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
gap: 12px;
|
|
}
|
|
|
|
.permission-panel {
|
|
border: 1px solid #d1d5db;
|
|
border-radius: 8px;
|
|
padding: 12px;
|
|
background: #ffffff;
|
|
}
|
|
|
|
.permission-panel h4 {
|
|
margin: 0 0 8px;
|
|
font-size: 0.95rem;
|
|
color: #1f2937;
|
|
}
|
|
|
|
.permission-check {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
margin: 6px 0;
|
|
color: #374151;
|
|
font-size: 0.9rem;
|
|
cursor: pointer;
|
|
}
|
|
|
|
/* Flash Nachrichten */
|
|
.flash-container {
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.flash {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 0.9rem 1rem;
|
|
margin-bottom: 1rem;
|
|
border-radius: var(--border-radius);
|
|
animation: fadeIn 0.3s ease-in-out;
|
|
}
|
|
|
|
.flash-icon {
|
|
margin-right: 0.8rem;
|
|
font-size: 1rem;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 22px;
|
|
height: 22px;
|
|
border-radius: 50%;
|
|
background-color: rgba(255, 255, 255, 0.4);
|
|
}
|
|
|
|
.flash.success {
|
|
background-color: #d1fae5;
|
|
color: #065f46;
|
|
border-left: 4px solid var(--csv-color);
|
|
}
|
|
|
|
.flash.error {
|
|
background-color: #fee2e2;
|
|
color: #991b1b;
|
|
border-left: 4px solid var(--error-color);
|
|
}
|
|
|
|
@keyframes fadeIn {
|
|
from { opacity: 0; transform: translateY(-8px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.container {
|
|
padding: 0 0.75rem;
|
|
margin: 1rem auto;
|
|
}
|
|
|
|
.content {
|
|
padding: 1rem;
|
|
}
|
|
|
|
.form-card {
|
|
padding: 1rem;
|
|
}
|
|
|
|
.header-section h1 {
|
|
font-size: 1.8rem;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %} |