slight correction to also account for name and last_name
Release Inventarsystem / release-docker (push) Successful in 2m16s
Release Inventarsystem / release-docker (push) Successful in 2m16s
This commit is contained in:
+91
-38
@@ -7865,6 +7865,82 @@ def register():
|
|||||||
permission_page_options=PERMISSION_PAGE_OPTIONS
|
permission_page_options=PERMISSION_PAGE_OPTIONS
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def parse_csv_users(file_bytes):
|
||||||
|
"""
|
||||||
|
Liest CSV-Dateien extrem robust ein:
|
||||||
|
- Erkennt automatisch UTF-8, UTF-8-SIG und Latin-1 (für Excel-Umlaute).
|
||||||
|
- Erkennt automatisch das Trennzeichen (; oder , oder Tab).
|
||||||
|
- Erkennt Vor- und Nachname unabhängig von der Spaltenreihenfolge und Headernamen.
|
||||||
|
"""
|
||||||
|
# 1. Dekodierung mit Fallback für deutsche Excel-Dateien
|
||||||
|
try:
|
||||||
|
content = file_bytes.decode('utf-8-sig')
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
content = file_bytes.decode('latin-1')
|
||||||
|
|
||||||
|
lines = [line.strip() for line in content.splitlines() if line.strip()]
|
||||||
|
if not lines:
|
||||||
|
return []
|
||||||
|
|
||||||
|
# 2. Trennzeichen ermitteln (; , oder \t)
|
||||||
|
first_line = lines[0]
|
||||||
|
if first_line.count(';') >= first_line.count(','):
|
||||||
|
delimiter = ';'
|
||||||
|
elif first_line.count('\t') > first_line.count(','):
|
||||||
|
delimiter = '\t'
|
||||||
|
else:
|
||||||
|
delimiter = ','
|
||||||
|
|
||||||
|
stream = io.StringIO(content, newline=None)
|
||||||
|
reader = csv.DictReader(stream, delimiter=delimiter)
|
||||||
|
|
||||||
|
parsed_users = []
|
||||||
|
|
||||||
|
# 3. Auswertung mit DictReader (wenn Header vorhanden sind)
|
||||||
|
if reader.fieldnames:
|
||||||
|
for row in reader:
|
||||||
|
# Keys normalisieren (kleingeschrieben, ohne Leerzeichen)
|
||||||
|
cleaned_row = {str(k).strip().lower(): str(v).strip() for k, v in row.items() if k and v}
|
||||||
|
|
||||||
|
# Dynamisches Mapping für Vornamen
|
||||||
|
name = (
|
||||||
|
cleaned_row.get('vorname') or
|
||||||
|
cleaned_row.get('first_name') or
|
||||||
|
cleaned_row.get('firstname') or
|
||||||
|
cleaned_row.get('name') or ''
|
||||||
|
)
|
||||||
|
|
||||||
|
# Dynamisches Mapping für Nachnamen
|
||||||
|
last_name = (
|
||||||
|
cleaned_row.get('nachname') or
|
||||||
|
cleaned_row.get('last_name') or
|
||||||
|
cleaned_row.get('lastname') or
|
||||||
|
cleaned_row.get('surname') or
|
||||||
|
cleaned_row.get('familienname') or ''
|
||||||
|
)
|
||||||
|
|
||||||
|
# Falls Header nicht erkannt wurden, aber Werte vorhanden sind (z.B. CSV ohne Header)
|
||||||
|
if not name and not last_name:
|
||||||
|
vals = [str(v).strip() for v in row.values() if v]
|
||||||
|
if len(vals) >= 2:
|
||||||
|
name, last_name = vals[0], vals[1]
|
||||||
|
|
||||||
|
if name or last_name:
|
||||||
|
parsed_users.append({'name': name, 'last_name': last_name})
|
||||||
|
else:
|
||||||
|
# Fallback für dateien ohne Header
|
||||||
|
stream.seek(0)
|
||||||
|
raw_reader = csv.reader(stream, delimiter=delimiter)
|
||||||
|
for row in raw_reader:
|
||||||
|
clean_row = [str(cell).strip() for cell in row if str(cell).strip()]
|
||||||
|
if len(clean_row) >= 2:
|
||||||
|
# Header-Zeilen überspringen
|
||||||
|
if clean_row[0].lower() in ['vorname', 'first_name', 'name'] and clean_row[1].lower() in ['nachname', 'last_name']:
|
||||||
|
continue
|
||||||
|
parsed_users.append({'name': clean_row[0], 'last_name': clean_row[1]})
|
||||||
|
|
||||||
|
return parsed_users
|
||||||
|
|
||||||
def generate_compliant_password(length=16):
|
def generate_compliant_password(length=16):
|
||||||
lowers = string.ascii_lowercase
|
lowers = string.ascii_lowercase
|
||||||
uppers = string.ascii_uppercase
|
uppers = string.ascii_uppercase
|
||||||
@@ -7983,46 +8059,28 @@ def register_csv():
|
|||||||
|
|
||||||
permission_preset = (request.form.get('permission_preset') or 'standard_user').strip()
|
permission_preset = (request.form.get('permission_preset') or 'standard_user').strip()
|
||||||
|
|
||||||
|
# 1. CSV über die robuste Funktion einlesen
|
||||||
|
file_bytes = file.stream.read()
|
||||||
|
raw_users = parse_csv_users(file_bytes)
|
||||||
|
|
||||||
|
if not raw_users:
|
||||||
|
flash('Keine gültigen Benutzer in der CSV-Datei gefunden. Bitte prüfen Sie das Format.', 'error')
|
||||||
|
return redirect(url_for('register'))
|
||||||
|
|
||||||
created_users = []
|
created_users = []
|
||||||
|
|
||||||
try:
|
for entry in raw_users:
|
||||||
# Read file stream handling UTF-8 and BOM (Excel exports)
|
name = entry['name']
|
||||||
file_stream = io.StringIO(file.stream.read().decode("utf-8-sig"), newline=None)
|
last_name = entry['last_name']
|
||||||
|
|
||||||
# Sniff delimiter (comma or semicolon)
|
|
||||||
sample = file_stream.read(2048)
|
|
||||||
file_stream.seek(0)
|
|
||||||
delimiter = ';' if ';' in sample else ','
|
|
||||||
|
|
||||||
reader = csv.reader(file_stream, delimiter=delimiter)
|
|
||||||
|
|
||||||
for row_num, row in enumerate(reader, start=1):
|
|
||||||
if not row or all(field.strip() == '' for field in row):
|
|
||||||
continue # Skip empty rows
|
|
||||||
|
|
||||||
# Clean entries
|
|
||||||
row = [field.strip() for field in row]
|
|
||||||
|
|
||||||
# Header row detection (Skip row if it looks like "Name, Nachname")
|
|
||||||
if row_num == 1 and ('name' in row[0].lower() or 'vorname' in row[0].lower()):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if len(row) < 2:
|
|
||||||
continue
|
|
||||||
|
|
||||||
name = row[0]
|
|
||||||
last_name = row[1]
|
|
||||||
|
|
||||||
if not name or not last_name:
|
if not name or not last_name:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 1. Generate Username via backend logic
|
# Benutzernamen & Passwort generieren
|
||||||
username = us.build_unique_username_from_name(name, last_name)
|
username = us.build_unique_username_from_name(name, last_name)
|
||||||
|
|
||||||
# 2. Generate Random Secure Password
|
|
||||||
password = generate_compliant_password(16)
|
password = generate_compliant_password(16)
|
||||||
|
|
||||||
# 3. Add User to DB
|
# In DB speichern
|
||||||
success = us.add_user(
|
success = us.add_user(
|
||||||
username=username,
|
username=username,
|
||||||
password=password,
|
password=password,
|
||||||
@@ -8042,18 +8100,13 @@ def register_csv():
|
|||||||
'password': password
|
'password': password
|
||||||
})
|
})
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
flash(f'Fehler beim Verarbeiten der CSV-Datei: {str(e)}', 'error')
|
|
||||||
return redirect(url_for('register'))
|
|
||||||
|
|
||||||
if not created_users:
|
if not created_users:
|
||||||
flash('Keine gültigen Benutzer in der CSV-Datei gefunden.', 'error')
|
flash('Fehler beim Erstellen der Benutzer aus der CSV.', 'error')
|
||||||
return redirect(url_for('register'))
|
return redirect(url_for('register'))
|
||||||
|
|
||||||
# Generate PDF
|
# PDF mit Zugangsdaten generieren
|
||||||
pdf_buffer = generate_credentials_pdf(created_users)
|
pdf_buffer = generate_credentials_pdf(created_users)
|
||||||
|
|
||||||
# Return downloadable PDF file
|
|
||||||
return send_file(
|
return send_file(
|
||||||
pdf_buffer,
|
pdf_buffer,
|
||||||
as_attachment=True,
|
as_attachment=True,
|
||||||
|
|||||||
+201
-114
@@ -321,11 +321,14 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
--primary-color: #3498db;
|
--primary-color: #3498db;
|
||||||
--primary-dark: #2980b9;
|
--primary-dark: #2980b9;
|
||||||
--success-color: #2ecc71;
|
--success-color: #2ecc71;
|
||||||
|
--success-dark: #27ae60;
|
||||||
|
--csv-color: #059669;
|
||||||
|
--csv-dark: #047857;
|
||||||
--error-color: #e74c3c;
|
--error-color: #e74c3c;
|
||||||
--text-color: var(--ui-text);
|
--text-color: var(--ui-text, #1f2937);
|
||||||
--light-bg: #f9f9f9;
|
--light-bg: #f9f9f9;
|
||||||
--border-radius: 8px;
|
--border-radius: 8px;
|
||||||
--shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
--shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
|
||||||
--transition: all 0.3s ease;
|
--transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,37 +361,55 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.subtitle {
|
.subtitle {
|
||||||
font-size: 1.1rem;
|
font-size: 1.05rem;
|
||||||
color: #777;
|
color: #6b7280;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
background-color: var(--ui-surface);
|
background-color: var(--ui-surface, #ffffff);
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
box-shadow: var(--shadow);
|
box-shadow: var(--shadow);
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Karten-Layout für Einzel- & Massenregistrierung */
|
||||||
.form-card {
|
.form-card {
|
||||||
max-width: 500px;
|
max-width: 620px;
|
||||||
margin: 0 auto;
|
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 {
|
.form-group {
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-group label {
|
.form-group label {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.4rem;
|
||||||
font-weight: 500;
|
font-weight: 600;
|
||||||
color: var(--primary-dark);
|
font-size: 0.95rem;
|
||||||
|
color: #374151;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-container {
|
.input-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-icon {
|
.input-icon {
|
||||||
@@ -396,24 +417,30 @@ body {
|
|||||||
left: 1rem;
|
left: 1rem;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
color: #aaa;
|
color: #9ca3af;
|
||||||
font-size: 1.2rem;
|
font-size: 1.1rem;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Eingabefelder & Selects */
|
||||||
input[type="text"],
|
input[type="text"],
|
||||||
input[type="password"],
|
input[type="password"],
|
||||||
|
input[type="file"],
|
||||||
.form-select {
|
.form-select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0.8rem 1rem 0.8rem 3rem;
|
padding: 0.75rem 1rem 0.75rem 2.8rem;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #d1d5db;
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
font-size: 1rem;
|
font-size: 0.95rem;
|
||||||
|
background-color: #ffffff;
|
||||||
transition: var(--transition);
|
transition: var(--transition);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="text"]:focus,
|
input[type="text"]:focus,
|
||||||
input[type="password"]:focus,
|
input[type="password"]:focus,
|
||||||
|
input[type="file"]:focus,
|
||||||
.form-select:focus {
|
.form-select:focus {
|
||||||
border-color: var(--primary-color);
|
border-color: var(--primary-color);
|
||||||
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);
|
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);
|
||||||
@@ -422,14 +449,47 @@ input[type="password"]:focus,
|
|||||||
|
|
||||||
.form-select {
|
.form-select {
|
||||||
padding-left: 1rem;
|
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 {
|
input::placeholder {
|
||||||
color: #aaa;
|
color: #9ca3af;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background: #e0f2fe;
|
||||||
|
color: #0369a1;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buttons */
|
||||||
.form-actions {
|
.form-actions {
|
||||||
margin-top: 2rem;
|
margin-top: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-button {
|
.action-button {
|
||||||
@@ -437,17 +497,21 @@ input::placeholder {
|
|||||||
color: white;
|
color: white;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0.8rem 1.5rem;
|
padding: 0.8rem 1.5rem;
|
||||||
border-radius: 30px;
|
border-radius: var(--border-radius);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
transition: var(--transition);
|
transition: var(--transition);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action-button:hover {
|
.action-button:hover {
|
||||||
background-color: var(--primary-dark);
|
background-color: var(--primary-dark);
|
||||||
transform: translateY(-2px);
|
transform: translateY(-1px);
|
||||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,79 +520,48 @@ input::placeholder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.register-button:hover {
|
.register-button:hover {
|
||||||
background-color: #27ae60;
|
background-color: var(--success-dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
.flash-container {
|
/* Toggle-Button für Passwort-Auge */
|
||||||
margin-bottom: 1.5rem;
|
.input-wrapper {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flash {
|
.toggle-pw-btn {
|
||||||
display: flex;
|
position: absolute;
|
||||||
align-items: center;
|
right: 0.75rem;
|
||||||
padding: 1rem;
|
top: 50%;
|
||||||
margin-bottom: 1rem;
|
transform: translateY(-50%);
|
||||||
border-radius: var(--border-radius);
|
background: none;
|
||||||
animation: fadeIn 0.3s ease-in-out;
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
padding: 4px;
|
||||||
|
z-index: 2;
|
||||||
|
opacity: 0.7;
|
||||||
|
transition: opacity 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flash-icon {
|
.toggle-pw-btn:hover {
|
||||||
margin-right: 0.8rem;
|
opacity: 1;
|
||||||
font-size: 1.2rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: rgba(255, 255, 255, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.flash.success {
|
|
||||||
background-color: #d4edda;
|
|
||||||
color: #155724;
|
|
||||||
border-left: 4px solid var(--success-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.flash.error {
|
|
||||||
background-color: #f8d7da;
|
|
||||||
color: #721c24;
|
|
||||||
border-left: 4px solid var(--error-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fadeIn {
|
|
||||||
from { opacity: 0; transform: translateY(-10px); }
|
|
||||||
to { opacity: 1; transform: translateY(0); }
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.container {
|
|
||||||
padding: 0 1rem;
|
|
||||||
margin: 1rem auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
padding: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-section h1 {
|
|
||||||
font-size: 2rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Passwort-Regeln Box */
|
||||||
.password-rules {
|
.password-rules {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
border: 1px solid #e5e7eb;
|
border: 1px solid #e5e7eb;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background: var(--ui-surface-soft);
|
background: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.password-rules-title {
|
.password-rules-title {
|
||||||
margin: 0 0 8px;
|
margin: 0 0 6px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: #1f2937;
|
color: #1f2937;
|
||||||
font-size: 0.92rem;
|
font-size: 0.88rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.password-rules ul {
|
.password-rules ul {
|
||||||
@@ -539,10 +572,10 @@ input::placeholder {
|
|||||||
|
|
||||||
.pw-rule {
|
.pw-rule {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-left: 22px;
|
padding-left: 20px;
|
||||||
margin: 5px 0;
|
margin: 4px 0;
|
||||||
color: #b91c1c;
|
color: #dc2626;
|
||||||
font-size: 0.9rem;
|
font-size: 0.85rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pw-rule::before {
|
.pw-rule::before {
|
||||||
@@ -561,15 +594,39 @@ input::placeholder {
|
|||||||
content: '✓';
|
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 {
|
.anonymize-hint {
|
||||||
margin-top: 10px;
|
margin-top: 8px;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
background: #f0f9ff;
|
background: #f0f9ff;
|
||||||
border: 1px solid #bae6fd;
|
border: 1px solid #bae6fd;
|
||||||
border-radius: 8px;
|
border-radius: 6px;
|
||||||
padding: 10px 12px;
|
padding: 8px 12px;
|
||||||
color: #0c4a6e;
|
color: #0369a1;
|
||||||
font-size: 0.92rem;
|
font-size: 0.88rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.permission-panels {
|
.permission-panels {
|
||||||
@@ -581,13 +638,13 @@ input::placeholder {
|
|||||||
.permission-panel {
|
.permission-panel {
|
||||||
border: 1px solid #d1d5db;
|
border: 1px solid #d1d5db;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 10px;
|
padding: 12px;
|
||||||
background: var(--ui-surface-soft);
|
background: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.permission-panel h4 {
|
.permission-panel h4 {
|
||||||
margin: 0 0 8px;
|
margin: 0 0 8px;
|
||||||
font-size: 1rem;
|
font-size: 0.95rem;
|
||||||
color: #1f2937;
|
color: #1f2937;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -595,42 +652,72 @@ input::placeholder {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
margin: 4px 0;
|
margin: 6px 0;
|
||||||
color: #1f2937;
|
color: #374151;
|
||||||
}
|
font-size: 0.9rem;
|
||||||
|
|
||||||
/* Neue Styles für die Passwort-Erweiterungen */
|
|
||||||
.pw-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-secondary {
|
|
||||||
padding: 8px 12px;
|
|
||||||
border: 1px solid #d1d5db;
|
|
||||||
background-color: #f3f4f6;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 0.9em;
|
|
||||||
transition: background-color 0.2s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-secondary:hover {
|
/* Flash Nachrichten */
|
||||||
background-color: #e5e7eb;
|
.flash-container {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-wrapper {
|
.flash {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
padding: 0.9rem 1rem;
|
||||||
width: 100%;
|
margin-bottom: 1rem;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
animation: fadeIn 0.3s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-wrapper .input-container {
|
.flash-icon {
|
||||||
flex-grow: 1;
|
margin-right: 0.8rem;
|
||||||
margin: 0;
|
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>
|
</style>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user