- Backend: page=-1 wird als 'letzte Seite' interpretiert (COUNT + Berechnung) - Frontend: Tabellen-Klick ruft page=-1 auf statt page=1 - Aufgelöster page-Wert aus API-Response für korrekte Anzeige (34/34 statt -1/34)
184 lines
6.3 KiB
JavaScript
184 lines
6.3 KiB
JavaScript
// ── Database Explorer Page ──
|
|
|
|
registerPage('db-explorer', {
|
|
async init() {
|
|
if (!currentUser || !currentUser.isAdmin) {
|
|
navigateTo('dashboard');
|
|
return;
|
|
}
|
|
|
|
var titleEl = document.getElementById('dbExplorerTitle');
|
|
var contentEl = document.getElementById('dbExplorerContent');
|
|
var currentTable = null;
|
|
|
|
await loadTables();
|
|
|
|
async function loadTables() {
|
|
currentTable = null;
|
|
titleEl.textContent = 'Datenbank-Explorer';
|
|
|
|
try {
|
|
var resp = await fetch('/api/admin/db/tables');
|
|
var data = await resp.json();
|
|
|
|
contentEl.innerHTML = '';
|
|
var table = document.createElement('table');
|
|
table.className = 'db-table';
|
|
|
|
var thead = document.createElement('thead');
|
|
thead.innerHTML = '<tr><th>Tabelle</th><th>Spalten</th><th>Zeilen</th></tr>';
|
|
table.appendChild(thead);
|
|
|
|
var tbody = document.createElement('tbody');
|
|
data.tables.forEach(function(t) {
|
|
var tr = document.createElement('tr');
|
|
tr.className = 'db-table-row clickable';
|
|
|
|
var tdName = document.createElement('td');
|
|
tdName.innerHTML = '<i class="fa fa-table"></i> ' + esc(t.name);
|
|
tdName.className = 'db-table-name';
|
|
|
|
var tdCols = document.createElement('td');
|
|
tdCols.textContent = t.columns;
|
|
tdCols.className = 'db-table-num';
|
|
|
|
var tdRows = document.createElement('td');
|
|
tdRows.textContent = t.rows.toLocaleString();
|
|
tdRows.className = 'db-table-num';
|
|
|
|
tr.appendChild(tdName);
|
|
tr.appendChild(tdCols);
|
|
tr.appendChild(tdRows);
|
|
|
|
tr.addEventListener('click', function() {
|
|
loadTableData(t.name, -1);
|
|
});
|
|
|
|
tbody.appendChild(tr);
|
|
});
|
|
|
|
table.appendChild(tbody);
|
|
contentEl.appendChild(table);
|
|
} catch (err) {
|
|
contentEl.innerHTML = '<p class="error">Fehler beim Laden: ' + esc(err.message) + '</p>';
|
|
}
|
|
}
|
|
|
|
async function loadTableData(name, page) {
|
|
currentTable = name;
|
|
titleEl.innerHTML = '<span class="db-back-link">Datenbank-Explorer</span> <i class="fa fa-chevron-right chevron-separator"></i> ' + esc(name);
|
|
|
|
// Back-Link klickbar
|
|
titleEl.querySelector('.db-back-link').classList.add('clickable');
|
|
titleEl.querySelector('.db-back-link').addEventListener('click', function() {
|
|
loadTables();
|
|
});
|
|
|
|
try {
|
|
var resp = await fetch('/api/admin/db/tables/' + encodeURIComponent(name) + '?page=' + page + '&pageSize=1000');
|
|
var data = await resp.json();
|
|
page = data.pagination.page;
|
|
|
|
contentEl.innerHTML = '';
|
|
|
|
// Info-Zeile
|
|
var info = document.createElement('div');
|
|
info.className = 'db-info-bar';
|
|
info.innerHTML = '<span>' + data.pagination.totalRows.toLocaleString() + ' Zeilen</span>';
|
|
|
|
// Pagination
|
|
if (data.pagination.totalPages > 1) {
|
|
var pagDiv = document.createElement('span');
|
|
pagDiv.className = 'db-pagination';
|
|
|
|
if (page > 1) {
|
|
var prevBtn = document.createElement('button');
|
|
prevBtn.className = 'btn btn-small';
|
|
prevBtn.innerHTML = '<i class="fa fa-chevron-left"></i>';
|
|
prevBtn.addEventListener('click', function() { loadTableData(name, page - 1); });
|
|
pagDiv.appendChild(prevBtn);
|
|
}
|
|
|
|
var pageInfo = document.createElement('span');
|
|
pageInfo.className = 'db-page-info';
|
|
pageInfo.textContent = 'Seite ' + page + ' / ' + data.pagination.totalPages;
|
|
pagDiv.appendChild(pageInfo);
|
|
|
|
if (page < data.pagination.totalPages) {
|
|
var nextBtn = document.createElement('button');
|
|
nextBtn.className = 'btn btn-small';
|
|
nextBtn.innerHTML = '<i class="fa fa-chevron-right"></i>';
|
|
nextBtn.addEventListener('click', function() { loadTableData(name, page + 1); });
|
|
pagDiv.appendChild(nextBtn);
|
|
}
|
|
|
|
info.appendChild(pagDiv);
|
|
}
|
|
|
|
contentEl.appendChild(info);
|
|
|
|
// Tabelle mit Daten
|
|
var wrapper = document.createElement('div');
|
|
wrapper.className = 'db-table-wrapper';
|
|
|
|
var table = document.createElement('table');
|
|
table.className = 'db-table db-data-table';
|
|
|
|
// Header aus Spalten-Info
|
|
var thead = document.createElement('thead');
|
|
var headerRow = document.createElement('tr');
|
|
data.columns.forEach(function(col) {
|
|
var th = document.createElement('th');
|
|
th.textContent = col.name;
|
|
if (col.pk) th.innerHTML += ' <i class="fa fa-key icon-dim-sm"></i>';
|
|
th.title = col.type + (col.notnull ? ' NOT NULL' : '') + (col.pk ? ' PRIMARY KEY' : '');
|
|
headerRow.appendChild(th);
|
|
});
|
|
thead.appendChild(headerRow);
|
|
table.appendChild(thead);
|
|
|
|
// Body
|
|
var tbody = document.createElement('tbody');
|
|
if (data.rows.length === 0) {
|
|
var emptyRow = document.createElement('tr');
|
|
var emptyCell = document.createElement('td');
|
|
emptyCell.colSpan = data.columns.length;
|
|
emptyCell.className = 'db-empty';
|
|
emptyCell.textContent = 'Keine Daten';
|
|
emptyRow.appendChild(emptyCell);
|
|
tbody.appendChild(emptyRow);
|
|
} else {
|
|
data.rows.forEach(function(row) {
|
|
var tr = document.createElement('tr');
|
|
data.columns.forEach(function(col) {
|
|
var td = document.createElement('td');
|
|
var val = row[col.name];
|
|
if (val === null || val === undefined) {
|
|
td.innerHTML = '<span class="db-null">NULL</span>';
|
|
} else {
|
|
var str = String(val);
|
|
if (str.length > 120) {
|
|
td.textContent = str.substring(0, 120) + '…';
|
|
td.title = str;
|
|
} else {
|
|
td.textContent = str;
|
|
}
|
|
}
|
|
tr.appendChild(td);
|
|
});
|
|
tbody.appendChild(tr);
|
|
});
|
|
}
|
|
|
|
table.appendChild(tbody);
|
|
wrapper.appendChild(table);
|
|
contentEl.appendChild(wrapper);
|
|
} catch (err) {
|
|
contentEl.innerHTML = '<p class="error">Fehler beim Laden: ' + esc(err.message) + '</p>';
|
|
}
|
|
}
|
|
},
|
|
|
|
destroy() {}
|
|
});
|