Übersicht polish: filter widths, transparent table buttons, Stitch docs modal

Three follow-ups to the previous overview redesign:

- Filter bar: replace flex-grow inputs with fixed widths so the
  Zurücksetzen button no longer ends up overlapping the second date
  field. The date range gets its own 320px slot wide enough for two
  flexible date pickers and the separator.
- Table icon buttons used to inherit the legacy global
  `button { background, padding, color }` rule and rendered as solid
  blue blocks. Add a `table button { background: transparent; … }`
  reset; class-level utilities still win for buttons that opt back in
  with bg-primary etc., so the "Neue Schaltung" CTA above the table
  keeps its colour.
- Documents modal rewritten in Stitch style: clean white card with a
  light header and footer separator, file rows as bordered cards with
  a Material icon, primary "Dokumente hinzufügen" pill and outline
  "Schließen". showDocuments / closeDocumentsModal toggle a Tailwind
  hidden/flex pair instead of the legacy `.show` class. The matching
  legacy CSS blocks (.documents-modal*, .document-info, .download-btn,
  .view-btn, .no-documents*) are removed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
kerimatilganandClaude Opus 4.7 committed 2026-05-15 18:06:15 +02:00
1 parent 0912a6812f
commit 40bd1b8009
2 files changed
+80 -226

No files matched your search

+46 -183
View File
@@ -156,6 +156,20 @@
box-shadow: inset 0 -2px 0 0 #b4c5ff; box-shadow: inset 0 -2px 0 0 #b4c5ff;
} }
/* Buttons inside data tables are icon-only by default in the new
Stitch style. The legacy `button { background, padding, color }`
rules would otherwise turn them into solid blue blobs — class-
level Tailwind utilities still win for explicit backgrounds. */
table button {
background: transparent;
color: inherit;
padding: 0;
border: 0;
cursor: pointer;
font-family: inherit;
font-size: inherit;
}
.form-group { .form-group {
margin-bottom: 20px; margin-bottom: 20px;
} }
@@ -621,149 +635,9 @@
} }
} }
/* Document Modal Styles */ /* Documents modal is now styled with Tailwind in the markup.
.documents-modal { Only keep the document-item rule at line ~574 — that one is
display: none; still used by other places (file upload preview list). */
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.6);
z-index: 2000;
animation: fadeIn 0.3s ease;
}
.documents-modal.show {
display: flex;
align-items: center;
justify-content: center;
}
.documents-modal-content {
background: white;
border-radius: 12px;
max-width: 600px;
width: 90%;
max-height: 80vh;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
animation: slideIn 0.3s ease;
}
.documents-modal-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px;
text-align: center;
position: relative;
}
.documents-modal-header h3 {
margin: 0;
font-size: 20px;
}
.documents-modal-close {
position: absolute;
top: 15px;
right: 20px;
background: none;
border: none;
color: white;
font-size: 24px;
cursor: pointer;
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.3s ease;
}
.documents-modal-close:hover {
background: rgba(255, 255, 255, 0.2);
}
.documents-modal-body {
padding: 20px;
max-height: 400px;
overflow-y: auto;
}
.document-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 15px;
border: 2px solid #e0e0e0;
border-radius: 8px;
margin-bottom: 10px;
transition: all 0.3s ease;
cursor: pointer;
}
.document-item:hover {
border-color: #3498db;
background: #f8f9fa;
transform: translateX(5px);
}
.document-info {
display: flex;
align-items: center;
gap: 15px;
}
.document-icon {
font-size: 24px;
color: #3498db;
}
.document-details {
display: flex;
flex-direction: column;
}
.document-name {
font-weight: bold;
color: #2c3e50;
margin-bottom: 5px;
}
.document-meta {
font-size: 12px;
color: #7f8c8d;
}
.download-btn {
background: #27ae60;
color: white;
border: none;
padding: 8px 16px;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: background 0.3s ease;
}
.download-btn:hover {
background: #219a52;
}
.no-documents {
text-align: center;
padding: 40px 20px;
color: #7f8c8d;
font-style: italic;
}
.no-documents-icon {
font-size: 48px;
margin-bottom: 15px;
color: #bdc3c7;
}
@keyframes fadeIn { @keyframes fadeIn {
from { opacity: 0; } from { opacity: 0; }
@@ -913,21 +787,6 @@
background: #219a52; background: #219a52;
} }
.view-btn {
background: #3498db;
color: white;
border: none;
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
margin-left: 8px;
}
.view-btn:hover {
background: #2980b9;
}
/* Enhanced Tarifoptionen Styling */ /* Enhanced Tarifoptionen Styling */
.tarifoption-item { .tarifoption-item {
display: flex; display: flex;
@@ -1182,40 +1041,40 @@
<!-- Filter bar --> <!-- Filter bar -->
<div class="bg-surface-container-lowest border border-outline-variant rounded-lg p-md shadow-[0_2px_4px_rgba(0,0,0,0.04)] flex flex-wrap items-end gap-md"> <div class="bg-surface-container-lowest border border-outline-variant rounded-lg p-md shadow-[0_2px_4px_rgba(0,0,0,0.04)] flex flex-wrap items-end gap-md">
<div class="flex flex-col gap-xs flex-grow min-w-[150px]"> <div class="flex flex-col gap-xs w-[180px]">
<label class="text-label-caps text-on-surface-variant uppercase" for="filterAnbieterOverview">Anbieter</label> <label class="text-label-caps text-on-surface-variant uppercase" for="filterAnbieterOverview">Anbieter</label>
<select id="filterAnbieterOverview" class="w-full bg-surface-container-lowest border border-outline-variant rounded h-8 px-sm text-body-sm text-on-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary"> <select id="filterAnbieterOverview" class="w-full bg-surface-container-lowest border border-outline-variant rounded h-8 px-sm text-body-sm text-on-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary">
<option value="">Alle Anbieter</option> <option value="">Alle Anbieter</option>
</select> </select>
</div> </div>
<div class="flex flex-col gap-xs flex-grow min-w-[150px]"> <div class="flex flex-col gap-xs w-[180px]">
<label class="text-label-caps text-on-surface-variant uppercase" for="filterTarifOverview">Tarif</label> <label class="text-label-caps text-on-surface-variant uppercase" for="filterTarifOverview">Tarif</label>
<select id="filterTarifOverview" class="w-full bg-surface-container-lowest border border-outline-variant rounded h-8 px-sm text-body-sm text-on-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary"> <select id="filterTarifOverview" class="w-full bg-surface-container-lowest border border-outline-variant rounded h-8 px-sm text-body-sm text-on-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary">
<option value="">Alle Tarife</option> <option value="">Alle Tarife</option>
</select> </select>
</div> </div>
<div class="flex flex-col gap-xs flex-grow min-w-[150px]"> <div class="flex flex-col gap-xs w-[160px]">
<label class="text-label-caps text-on-surface-variant uppercase" for="filterSchaltungsIdOverview">Schaltungs-ID</label> <label class="text-label-caps text-on-surface-variant uppercase" for="filterSchaltungsIdOverview">Schaltungs-ID</label>
<input type="text" id="filterSchaltungsIdOverview" placeholder="ID1, ID2…" <input type="text" id="filterSchaltungsIdOverview" placeholder="ID1, ID2…"
class="w-full bg-surface-container-lowest border border-outline-variant rounded h-8 px-sm font-label-numeric text-label-numeric text-on-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary"> class="w-full bg-surface-container-lowest border border-outline-variant rounded h-8 px-sm font-label-numeric text-label-numeric text-on-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary">
</div> </div>
<div class="flex flex-col gap-xs flex-grow min-w-[150px]"> <div class="flex flex-col gap-xs w-[160px]">
<label class="text-label-caps text-on-surface-variant uppercase" for="filterRufnummerOverview">Rufnummer</label> <label class="text-label-caps text-on-surface-variant uppercase" for="filterRufnummerOverview">Rufnummer</label>
<input type="text" id="filterRufnummerOverview" placeholder="Suche…" <input type="text" id="filterRufnummerOverview" placeholder="Suche…"
class="w-full bg-surface-container-lowest border border-outline-variant rounded h-8 px-sm font-label-numeric text-label-numeric text-on-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary"> class="w-full bg-surface-container-lowest border border-outline-variant rounded h-8 px-sm font-label-numeric text-label-numeric text-on-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary">
</div> </div>
<div class="flex flex-col gap-xs w-[240px]"> <div class="flex flex-col gap-xs w-[320px]">
<label class="text-label-caps text-on-surface-variant uppercase">Zeitraum</label> <label class="text-label-caps text-on-surface-variant uppercase">Zeitraum</label>
<div class="flex items-center gap-sm"> <div class="flex items-center gap-sm">
<input type="date" id="filterDatumVonOverview" <input type="date" id="filterDatumVonOverview"
class="w-full bg-surface-container-lowest border border-outline-variant rounded h-8 px-sm font-label-numeric text-[12px] text-on-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary"> class="flex-1 min-w-0 bg-surface-container-lowest border border-outline-variant rounded h-8 px-sm font-label-numeric text-[12px] text-on-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary">
<span class="text-outline-variant">–</span> <span class="text-outline-variant shrink-0">–</span>
<input type="date" id="filterDatumBisOverview" <input type="date" id="filterDatumBisOverview"
class="w-full bg-surface-container-lowest border border-outline-variant rounded h-8 px-sm font-label-numeric text-[12px] text-on-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary"> class="flex-1 min-w-0 bg-surface-container-lowest border border-outline-variant rounded h-8 px-sm font-label-numeric text-[12px] text-on-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary">
</div> </div>
</div> </div>
<button type="button" onclick="clearFiltersOverview()" <button type="button" onclick="clearFiltersOverview()"
class="bg-surface-container-high text-on-surface border border-outline-variant hover:bg-surface-variant transition-colors rounded px-md h-8 text-body-sm flex items-center gap-xs ml-auto"> class="bg-surface-container-high text-on-surface border border-outline-variant hover:bg-surface-variant transition-colors rounded px-md h-8 text-body-sm flex items-center gap-xs">
<span class="material-symbols-outlined text-[16px]">restart_alt</span> <span class="material-symbols-outlined text-[16px]">restart_alt</span>
Zurücksetzen Zurücksetzen
</button> </button>
@@ -1996,25 +1855,29 @@
</div> </div>
<!-- Documents Modal --> <!-- Documents Modal -->
<div id="documentsModal" class="documents-modal"> <div id="documentsModal" class="documents-modal fixed inset-0 z-[1000] hidden items-center justify-center bg-black/40 p-margin">
<div class="documents-modal-content"> <div class="bg-surface-container-lowest rounded-xl shadow-[0_10px_30px_rgba(0,0,0,0.15)] border border-outline-variant/30 w-full max-w-[640px] max-h-[85vh] flex flex-col overflow-hidden">
<div class="documents-modal-header"> <div class="flex items-center justify-between px-lg py-md border-b border-outline-variant">
<h3>📄 Dokumente</h3> <div class="flex items-center gap-sm">
<button class="documents-modal-close" onclick="closeDocumentsModal()">&times;</button> <span class="material-symbols-outlined text-primary">description</span>
<h3 class="text-title-sm text-on-surface">Dokumente</h3>
</div> </div>
<div class="documents-modal-body" id="documentsModalBody"> <button onclick="closeDocumentsModal()" class="text-outline hover:text-on-surface transition-colors" title="Schließen">
<!-- Documents will be loaded here --> <span class="material-symbols-outlined">close</span>
</div>
<div class="documents-modal-footer" style="padding: 15px 20px; border-top: 1px solid #e0e0e0; background: #f8f9fa;">
<div style="display: flex; justify-content: space-between; align-items: center;">
<div>
<input type="file" id="additionalDocumentUpload" multiple accept=".pdf,.jpg,.jpeg,.png,.doc,.docx" style="display: none;">
<button class="btn btn-primary" onclick="document.getElementById('additionalDocumentUpload').click()" style="background: #28a745; border-color: #28a745;">
📎 Dokumente hinzufügen
</button> </button>
</div> </div>
<button class="btn btn-secondary" onclick="closeDocumentsModal()">Schließen</button> <div class="flex-1 overflow-y-auto px-lg py-md flex flex-col gap-sm" id="documentsModalBody"></div>
</div> <div class="flex items-center justify-between gap-md px-lg py-md border-t border-outline-variant bg-surface-container-low/50">
<input type="file" id="additionalDocumentUpload" multiple accept=".pdf,.jpg,.jpeg,.png,.doc,.docx" style="display:none;">
<button onclick="document.getElementById('additionalDocumentUpload').click()"
class="bg-primary-container text-on-primary hover:bg-primary transition-colors rounded-lg px-md py-sm text-body-sm flex items-center gap-xs shadow-sm">
<span class="material-symbols-outlined text-[18px]">attach_file</span>
Dokumente hinzufügen
</button>
<button onclick="closeDocumentsModal()"
class="bg-surface-container-high text-on-surface border border-outline-variant hover:bg-surface-variant transition-colors rounded-lg px-md py-sm text-body-sm">
Schließen
</button>
</div> </div>
</div> </div>
</div> </div>
+34 -43
View File
@@ -794,61 +794,51 @@ async function showDocuments(schaltungId) {
modalBody.innerHTML = ''; modalBody.innerHTML = '';
if (documents.length === 0) { if (documents.length === 0) {
modalBody.innerHTML = ` const empty = document.createElement('div');
<div class="no-documents"> empty.className = 'flex flex-col items-center justify-center py-xl text-center gap-sm';
<div class="no-documents-icon">📄</div> empty.innerHTML = `
<div>Keine Dokumente vorhanden</div> <span class="material-symbols-outlined text-[40px] text-outline">draft</span>
<div style="font-size: 14px; margin-top: 10px;">Für diese Schaltung wurden noch keine Dokumente hochgeladen.</div> <div class="text-body-md text-on-surface">Keine Dokumente vorhanden</div>
<div style="margin-top: 15px;"> <div class="text-body-sm text-on-surface-variant">Für diese Schaltung wurden noch keine Dokumente hochgeladen.</div>
<button class="btn btn-primary" onclick="document.getElementById('additionalDocumentUpload').click()"
style="background: #007bff; border-color: #007bff;">
📎 Erste Dokumente hinzufügen
</button>
</div>
</div>
`; `;
modalBody.appendChild(empty);
} else { } else {
documents.forEach(doc => { documents.forEach(doc => {
const docItem = document.createElement('div');
docItem.className = 'document-item';
// Get file extension for icon
const fileExt = doc.original_filename.split('.').pop().toLowerCase(); const fileExt = doc.original_filename.split('.').pop().toLowerCase();
let icon = '📄'; let iconName = 'description';
if (['pdf'].includes(fileExt)) icon = '📋'; if (['jpg', 'jpeg', 'png', 'gif'].includes(fileExt)) iconName = 'image';
else if (['jpg', 'jpeg', 'png', 'gif'].includes(fileExt)) icon = '🖼️'; else if (['doc', 'docx'].includes(fileExt)) iconName = 'article';
else if (['doc', 'docx'].includes(fileExt)) icon = '📝';
// Format file size
const fileSize = doc.filesize ? `${(doc.filesize / 1024).toFixed(1)} KB` : 'Unbekannt'; const fileSize = doc.filesize ? `${(doc.filesize / 1024).toFixed(1)} KB` : 'Unbekannt';
// Format upload date
const uploadDate = doc.uploaded_at ? formatDate(doc.uploaded_at) : 'Unbekannt'; const uploadDate = doc.uploaded_at ? formatDate(doc.uploaded_at) : 'Unbekannt';
const showView = fileExt === 'pdf'; const showView = fileExt === 'pdf';
const safeName = escapeHtml(doc.original_filename); const safeName = escapeHtml(doc.original_filename);
docItem.innerHTML = ` const card = document.createElement('div');
<div class="document-info"> card.className = 'flex items-center gap-md p-md border border-outline-variant rounded-lg bg-surface-container-lowest hover:bg-surface-container-low transition-colors';
<div class="document-icon">${icon}</div> card.innerHTML = `
<div class="document-details"> <span class="material-symbols-outlined text-primary text-[28px] shrink-0">${iconName}</span>
<div class="document-name">${safeName}</div> <div class="flex flex-col min-w-0 flex-1">
<div class="document-meta">Größe: ${escapeHtml(fileSize)} • Hochgeladen: ${escapeHtml(uploadDate)}</div> <span class="text-body-md text-on-surface truncate" title="${safeName}">${safeName}</span>
<span class="text-body-sm text-on-surface-variant">${escapeHtml(fileSize)} · Hochgeladen: ${escapeHtml(uploadDate)}</span>
</div> </div>
</div> <div class="flex items-center gap-xs shrink-0">
<div style="display: flex; gap: 8px;"> ${showView ? `<button data-doc-action="view" data-id="${doc.id}" data-name="${safeName}" title="Anzeigen"
${showView ? `<button class="view-btn" data-doc-action="view" data-id="${doc.id}" data-name="${safeName}">👁️ Anzeigen</button>` : ''} class="text-outline hover:text-primary transition-colors p-1">
<button class="download-btn" data-doc-action="download" data-id="${doc.id}"> <span class="material-symbols-outlined text-[20px]">visibility</span>
📥 Download </button>` : ''}
<button data-doc-action="download" data-id="${doc.id}" title="Download"
class="text-outline hover:text-primary transition-colors p-1">
<span class="material-symbols-outlined text-[20px]">download</span>
</button> </button>
<button class="btn-danger" data-doc-action="delete" data-id="${doc.id}" data-name="${safeName}" data-schaltung-id="${schaltungId}" <button data-doc-action="delete" data-id="${doc.id}" data-name="${safeName}" data-schaltung-id="${schaltungId}" title="Löschen"
style="background: #dc3545; border-color: #dc3545; color: white; padding: 8px 12px; border-radius: 4px; font-size: 12px;" class="text-outline hover:text-error transition-colors p-1">
title="Dokument löschen"> <span class="material-symbols-outlined text-[20px]">delete</span>
🗑️
</button> </button>
</div> </div>
`; `;
docItem.querySelectorAll('button[data-doc-action]').forEach(btn => { card.querySelectorAll('button[data-doc-action]').forEach(btn => {
btn.addEventListener('click', () => { btn.addEventListener('click', () => {
const action = btn.dataset.docAction; const action = btn.dataset.docAction;
const id = parseInt(btn.dataset.id, 10); const id = parseInt(btn.dataset.id, 10);
@@ -858,17 +848,17 @@ async function showDocuments(schaltungId) {
}); });
}); });
modalBody.appendChild(docItem); modalBody.appendChild(card);
}); });
} }
// Setup file upload handler for additional documents
const fileInput = document.getElementById('additionalDocumentUpload'); const fileInput = document.getElementById('additionalDocumentUpload');
fileInput.onchange = function (event) { fileInput.onchange = function (event) {
uploadAdditionalDocuments(schaltungId, event.target.files); uploadAdditionalDocuments(schaltungId, event.target.files);
}; };
modal.classList.add('show'); modal.classList.remove('hidden');
modal.classList.add('flex');
} catch (error) { } catch (error) {
console.error('Fehler beim Laden der Dokumente:', error); console.error('Fehler beim Laden der Dokumente:', error);
@@ -878,7 +868,8 @@ async function showDocuments(schaltungId) {
function closeDocumentsModal() { function closeDocumentsModal() {
const modal = document.getElementById('documentsModal'); const modal = document.getElementById('documentsModal');
modal.classList.remove('show'); modal.classList.add('hidden');
modal.classList.remove('flex');
} }
function downloadDocument(documentId) { function downloadDocument(documentId) {