Ü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:
1 parent
0912a6812f
commit
40bd1b8009
2 files changed
+81
-227
No files matched your search
+46
-183
@@ -156,6 +156,20 @@
|
||||
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 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
@@ -621,149 +635,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Document Modal Styles */
|
||||
.documents-modal {
|
||||
display: none;
|
||||
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;
|
||||
}
|
||||
/* Documents modal is now styled with Tailwind in the markup.
|
||||
Only keep the document-item rule at line ~574 — that one is
|
||||
still used by other places (file upload preview list). */
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
@@ -913,21 +787,6 @@
|
||||
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 */
|
||||
.tarifoption-item {
|
||||
display: flex;
|
||||
@@ -1182,40 +1041,40 @@
|
||||
|
||||
<!-- 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="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>
|
||||
<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>
|
||||
</select>
|
||||
</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>
|
||||
<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>
|
||||
</select>
|
||||
</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>
|
||||
<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">
|
||||
</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>
|
||||
<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">
|
||||
</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>
|
||||
<div class="flex items-center gap-sm">
|
||||
<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">
|
||||
<span class="text-outline-variant">–</span>
|
||||
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 shrink-0">–</span>
|
||||
<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>
|
||||
<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>
|
||||
Zurücksetzen
|
||||
</button>
|
||||
@@ -1996,25 +1855,29 @@
|
||||
</div>
|
||||
|
||||
<!-- Documents Modal -->
|
||||
<div id="documentsModal" class="documents-modal">
|
||||
<div class="documents-modal-content">
|
||||
<div class="documents-modal-header">
|
||||
<h3>📄 Dokumente</h3>
|
||||
<button class="documents-modal-close" onclick="closeDocumentsModal()">×</button>
|
||||
<div id="documentsModal" class="documents-modal fixed inset-0 z-[1000] hidden items-center justify-center bg-black/40 p-margin">
|
||||
<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="flex items-center justify-between px-lg py-md border-b border-outline-variant">
|
||||
<div class="flex items-center gap-sm">
|
||||
<span class="material-symbols-outlined text-primary">description</span>
|
||||
<h3 class="text-title-sm text-on-surface">Dokumente</h3>
|
||||
</div>
|
||||
<div class="documents-modal-body" id="documentsModalBody">
|
||||
<!-- Documents will be loaded here -->
|
||||
</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 onclick="closeDocumentsModal()" class="text-outline hover:text-on-surface transition-colors" title="Schließen">
|
||||
<span class="material-symbols-outlined">close</span>
|
||||
</button>
|
||||
</div>
|
||||
<button class="btn btn-secondary" onclick="closeDocumentsModal()">Schließen</button>
|
||||
</div>
|
||||
<div class="flex-1 overflow-y-auto px-lg py-md flex flex-col gap-sm" id="documentsModalBody"></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>
|
||||
|
||||
+35
-44
@@ -794,61 +794,51 @@ async function showDocuments(schaltungId) {
|
||||
modalBody.innerHTML = '';
|
||||
|
||||
if (documents.length === 0) {
|
||||
modalBody.innerHTML = `
|
||||
<div class="no-documents">
|
||||
<div class="no-documents-icon">📄</div>
|
||||
<div>Keine Dokumente vorhanden</div>
|
||||
<div style="font-size: 14px; margin-top: 10px;">Für diese Schaltung wurden noch keine Dokumente hochgeladen.</div>
|
||||
<div style="margin-top: 15px;">
|
||||
<button class="btn btn-primary" onclick="document.getElementById('additionalDocumentUpload').click()"
|
||||
style="background: #007bff; border-color: #007bff;">
|
||||
📎 Erste Dokumente hinzufügen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
const empty = document.createElement('div');
|
||||
empty.className = 'flex flex-col items-center justify-center py-xl text-center gap-sm';
|
||||
empty.innerHTML = `
|
||||
<span class="material-symbols-outlined text-[40px] text-outline">draft</span>
|
||||
<div class="text-body-md text-on-surface">Keine Dokumente vorhanden</div>
|
||||
<div class="text-body-sm text-on-surface-variant">Für diese Schaltung wurden noch keine Dokumente hochgeladen.</div>
|
||||
`;
|
||||
modalBody.appendChild(empty);
|
||||
} else {
|
||||
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();
|
||||
let icon = '📄';
|
||||
if (['pdf'].includes(fileExt)) icon = '📋';
|
||||
else if (['jpg', 'jpeg', 'png', 'gif'].includes(fileExt)) icon = '🖼️';
|
||||
else if (['doc', 'docx'].includes(fileExt)) icon = '📝';
|
||||
let iconName = 'description';
|
||||
if (['jpg', 'jpeg', 'png', 'gif'].includes(fileExt)) iconName = 'image';
|
||||
else if (['doc', 'docx'].includes(fileExt)) iconName = 'article';
|
||||
|
||||
// Format file size
|
||||
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 showView = fileExt === 'pdf';
|
||||
const safeName = escapeHtml(doc.original_filename);
|
||||
|
||||
docItem.innerHTML = `
|
||||
<div class="document-info">
|
||||
<div class="document-icon">${icon}</div>
|
||||
<div class="document-details">
|
||||
<div class="document-name">${safeName}</div>
|
||||
<div class="document-meta">Größe: ${escapeHtml(fileSize)} • Hochgeladen: ${escapeHtml(uploadDate)}</div>
|
||||
const card = document.createElement('div');
|
||||
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';
|
||||
card.innerHTML = `
|
||||
<span class="material-symbols-outlined text-primary text-[28px] shrink-0">${iconName}</span>
|
||||
<div class="flex flex-col min-w-0 flex-1">
|
||||
<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 style="display: flex; gap: 8px;">
|
||||
${showView ? `<button class="view-btn" data-doc-action="view" data-id="${doc.id}" data-name="${safeName}">👁️ Anzeigen</button>` : ''}
|
||||
<button class="download-btn" data-doc-action="download" data-id="${doc.id}">
|
||||
📥 Download
|
||||
<div class="flex items-center gap-xs shrink-0">
|
||||
${showView ? `<button data-doc-action="view" data-id="${doc.id}" data-name="${safeName}" title="Anzeigen"
|
||||
class="text-outline hover:text-primary transition-colors p-1">
|
||||
<span class="material-symbols-outlined text-[20px]">visibility</span>
|
||||
</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 class="btn-danger" data-doc-action="delete" data-id="${doc.id}" data-name="${safeName}" data-schaltung-id="${schaltungId}"
|
||||
style="background: #dc3545; border-color: #dc3545; color: white; padding: 8px 12px; border-radius: 4px; font-size: 12px;"
|
||||
title="Dokument löschen">
|
||||
🗑️
|
||||
<button data-doc-action="delete" data-id="${doc.id}" data-name="${safeName}" data-schaltung-id="${schaltungId}" title="Löschen"
|
||||
class="text-outline hover:text-error transition-colors p-1">
|
||||
<span class="material-symbols-outlined text-[20px]">delete</span>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
docItem.querySelectorAll('button[data-doc-action]').forEach(btn => {
|
||||
card.querySelectorAll('button[data-doc-action]').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const action = btn.dataset.docAction;
|
||||
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');
|
||||
fileInput.onchange = function(event) {
|
||||
fileInput.onchange = function (event) {
|
||||
uploadAdditionalDocuments(schaltungId, event.target.files);
|
||||
};
|
||||
|
||||
modal.classList.add('show');
|
||||
modal.classList.remove('hidden');
|
||||
modal.classList.add('flex');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Dokumente:', error);
|
||||
@@ -878,7 +868,8 @@ async function showDocuments(schaltungId) {
|
||||
|
||||
function closeDocumentsModal() {
|
||||
const modal = document.getElementById('documentsModal');
|
||||
modal.classList.remove('show');
|
||||
modal.classList.add('hidden');
|
||||
modal.classList.remove('flex');
|
||||
}
|
||||
|
||||
function downloadDocument(documentId) {
|
||||
|
||||
Reference in new issue
Block a user