UI redesign step 2: Übersicht in Stitch style
- Page header gains the headline / subtitle pair and a primary "Neue Schaltung" CTA on the right that jumps back to the wizard. - Filter bar moves into a bordered white card with label-caps labels, compact 32px inputs, JetBrains Mono numeric inputs for ID/phone/date and a "Zurücksetzen" pill on the right. - Data table uses the Stitch shell: sticky surface-container-high header row with label-caps column titles, 40px body rows, zebra striping on the even rows and a hover highlight. The previously separate Eingabe/Schaltungs-Datum columns are merged into a single "Daten" cell (E:/A:), Anbieter renders as a primary pill, and the Bearbeiten/Löschen/Dokumente buttons become icon-only actions. - updateSchaltungenTable() now delegates to a single renderer used by both the initial load and the client-side filter, so the layout stays consistent after applying filters. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
a1cab7ea6d
commit
0912a6812f
2 files changed
+134
-127
No files matched your search
+69
-79
@@ -703,46 +703,76 @@ async function refreshDataAfterSchaltungChange() {
|
||||
async function loadSchaltungen() {
|
||||
try {
|
||||
const schaltungen = await apiRequest('/api/schaltungen');
|
||||
const tbody = document.querySelector('#schaltungenTable tbody');
|
||||
|
||||
tbody.innerHTML = '';
|
||||
|
||||
schaltungen.forEach(s => {
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
<td>${escapeHtml(s.schaltungs_id)}</td>
|
||||
<td>${escapeHtml(formatDate(s.eingabe_datum))}</td>
|
||||
<td>${escapeHtml(formatDate(s.schaltungs_datum))}</td>
|
||||
<td>${escapeHtml(s.vorname)} ${escapeHtml(s.nachname)}</td>
|
||||
<td>${escapeHtml(s.firma || '-')}</td>
|
||||
<td>${escapeHtml(s.rufnummer)}</td>
|
||||
<td>${escapeHtml(s.anbieter_name)}</td>
|
||||
<td>${escapeHtml(s.tarif_name)}</td>
|
||||
<td>${escapeHtml(s.hardware || '')}</td>
|
||||
<td><button data-action="documents" data-id="${s.id}" class="btn-success">Dokumente</button></td>
|
||||
<td>
|
||||
<button data-action="edit" data-id="${s.id}" class="btn-success" style="margin-right: 5px;">Bearbeiten</button>
|
||||
<button data-action="delete" data-id="${s.id}" data-schaltungs-id="${escapeHtml(s.schaltungs_id)}" data-name="${escapeHtml(s.vorname + ' ' + s.nachname)}" class="btn-danger">Löschen</button>
|
||||
</td>
|
||||
`;
|
||||
// Wire delegated handlers so we don't need inline onclick (avoids JS-injection via names).
|
||||
row.querySelectorAll('button[data-action]').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const action = btn.dataset.action;
|
||||
const id = parseInt(btn.dataset.id, 10);
|
||||
if (action === 'documents') showDocuments(id);
|
||||
else if (action === 'edit') editSchaltung(id);
|
||||
else if (action === 'delete') deleteSchaltung(id, btn.dataset.schaltungsId, btn.dataset.name);
|
||||
});
|
||||
});
|
||||
tbody.appendChild(row);
|
||||
});
|
||||
|
||||
renderSchaltungenRows(schaltungen);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Schaltungen:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Render schaltungen rows in the Stitch overview table.
|
||||
function renderSchaltungenRows(schaltungen) {
|
||||
const tbody = document.querySelector('#schaltungenTable tbody');
|
||||
if (!tbody) return;
|
||||
tbody.innerHTML = '';
|
||||
if (!schaltungen || !schaltungen.length) {
|
||||
const tr = document.createElement('tr');
|
||||
tr.innerHTML = '<td colspan="9" class="px-md py-lg text-center text-body-sm text-on-surface-variant">Keine Schaltungen vorhanden</td>';
|
||||
tbody.appendChild(tr);
|
||||
return;
|
||||
}
|
||||
schaltungen.forEach(s => {
|
||||
const tr = document.createElement('tr');
|
||||
tr.className = 'border-b border-outline-variant/30 even:bg-surface-container-low/50 hover:bg-surface-variant/30 transition-colors';
|
||||
tr.innerHTML = `
|
||||
<td class="px-md py-xs font-label-numeric text-label-numeric text-right text-on-surface-variant">${escapeHtml(s.schaltungs_id)}</td>
|
||||
<td class="px-md py-xs">
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<span class="font-label-numeric text-[11px] text-on-surface-variant">E: ${escapeHtml(formatDate(s.eingabe_datum))}</span>
|
||||
<span class="font-label-numeric text-[11px] text-primary">A: ${escapeHtml(formatDate(s.schaltungs_datum))}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-md py-xs">
|
||||
<div class="font-semibold text-on-background">${escapeHtml((s.vorname || '') + ' ' + (s.nachname || ''))}</div>
|
||||
<div class="text-[12px] text-on-surface-variant">${escapeHtml(s.firma || '–')}</div>
|
||||
</td>
|
||||
<td class="px-md py-xs font-label-numeric text-label-numeric text-on-surface-variant">${escapeHtml(s.rufnummer || '–')}</td>
|
||||
<td class="px-md py-xs">
|
||||
<span class="bg-primary/10 text-primary border border-primary/20 px-2 py-0.5 rounded-full text-label-caps">${escapeHtml(s.anbieter_name)}</span>
|
||||
</td>
|
||||
<td class="px-md py-xs">${escapeHtml(s.tarif_name)}</td>
|
||||
<td class="px-md py-xs text-on-surface-variant">${escapeHtml(s.hardware || '–')}</td>
|
||||
<td class="px-md py-xs text-center">
|
||||
<button data-action="documents" data-id="${s.id}" title="Dokumente anzeigen"
|
||||
class="bg-tertiary/10 text-tertiary hover:bg-tertiary/20 transition-colors rounded p-1 inline-flex items-center justify-center border border-tertiary/20">
|
||||
<span class="material-symbols-outlined text-[16px]">description</span>
|
||||
</button>
|
||||
</td>
|
||||
<td class="px-md py-xs">
|
||||
<div class="flex items-center justify-center gap-sm">
|
||||
<button data-action="edit" data-id="${s.id}" title="Bearbeiten"
|
||||
class="text-outline hover:text-primary transition-colors">
|
||||
<span class="material-symbols-outlined text-[18px]">edit</span>
|
||||
</button>
|
||||
<button data-action="delete" data-id="${s.id}" data-schaltungs-id="${escapeHtml(s.schaltungs_id)}" data-name="${escapeHtml((s.vorname || '') + ' ' + (s.nachname || ''))}" title="Löschen"
|
||||
class="text-outline hover:text-error transition-colors">
|
||||
<span class="material-symbols-outlined text-[18px]">delete</span>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
`;
|
||||
tr.querySelectorAll('button[data-action]').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const action = btn.dataset.action;
|
||||
const id = parseInt(btn.dataset.id, 10);
|
||||
if (action === 'documents') showDocuments(id);
|
||||
else if (action === 'edit') editSchaltung(id);
|
||||
else if (action === 'delete') deleteSchaltung(id, btn.dataset.schaltungsId, btn.dataset.name);
|
||||
});
|
||||
});
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
}
|
||||
|
||||
// Format any ISO-like date string (YYYY-MM-DD or full timestamp) as DD.MM.YYYY
|
||||
// with leading zeros. Returns the original string unchanged if it cannot be parsed.
|
||||
function formatDate(dateString) {
|
||||
@@ -2256,52 +2286,12 @@ function applyFiltersProvisionen() {
|
||||
function updateSchaltungenTable(schaltungen) {
|
||||
const tbody = document.querySelector('#schaltungenTable tbody');
|
||||
if (!tbody) return;
|
||||
|
||||
tbody.innerHTML = '';
|
||||
|
||||
if (schaltungen.length === 0) {
|
||||
// Show "no results" message
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
<td colspan="11" style="text-align: center; padding: 40px; color: #7f8c8d; font-style: italic; font-size: 16px;">
|
||||
📋 Keine Schaltungen entsprechen den gewählten Filterkriterien
|
||||
</td>
|
||||
`;
|
||||
tbody.appendChild(row);
|
||||
if (!schaltungen || schaltungen.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="9" class="px-md py-lg text-center text-body-sm text-on-surface-variant">Keine Schaltungen entsprechen den Filterkriterien</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
schaltungen.forEach(s => {
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
<td>${escapeHtml(s.schaltungs_id)}</td>
|
||||
<td>${escapeHtml(formatDate(s.eingabe_datum))}</td>
|
||||
<td>${escapeHtml(formatDate(s.schaltungs_datum))}</td>
|
||||
<td>${escapeHtml(s.vorname)} ${escapeHtml(s.nachname)}</td>
|
||||
<td>${escapeHtml(s.firma || '-')}</td>
|
||||
<td>${escapeHtml(s.rufnummer)}</td>
|
||||
<td>${escapeHtml(s.anbieter_name)}</td>
|
||||
<td>${escapeHtml(s.tarif_name)}</td>
|
||||
<td>${escapeHtml(s.hardware || '')}</td>
|
||||
<td>
|
||||
<button data-filt-action="documents" data-id="${s.id}" class="btn-success">Dokumente</button>
|
||||
</td>
|
||||
<td>
|
||||
<button data-filt-action="edit" data-id="${s.id}" class="btn-secondary">Bearbeiten</button>
|
||||
<button data-filt-action="delete" data-id="${s.id}" data-schaltungs-id="${escapeHtml(s.schaltungs_id)}" data-name="${escapeHtml(s.vorname + ' ' + s.nachname)}" class="btn-danger">Löschen</button>
|
||||
</td>
|
||||
`;
|
||||
row.querySelectorAll('button[data-filt-action]').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const action = btn.dataset.filtAction;
|
||||
const id = parseInt(btn.dataset.id, 10);
|
||||
if (action === 'documents') showDocuments(id);
|
||||
else if (action === 'edit') editSchaltung(id);
|
||||
else if (action === 'delete') deleteSchaltung(id, btn.dataset.schaltungsId, btn.dataset.name);
|
||||
});
|
||||
});
|
||||
tbody.appendChild(row);
|
||||
});
|
||||
// Delegate to the canonical renderer used by loadSchaltungen.
|
||||
renderSchaltungenRows(schaltungen);
|
||||
}
|
||||
|
||||
// Update Provisionen table with filtered data
|
||||
|
||||
Reference in new issue
Block a user