PDF View i Adminbereich
This commit is contained in:
@@ -304,8 +304,257 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// PDF-Archiv (Timesheets)
|
||||
const pdfYearSelect = document.getElementById('pdfYearSelect');
|
||||
const pdfUserSelect = document.getElementById('pdfUserSelect');
|
||||
const pdfFilesContainer = document.getElementById('pdfFilesContainer');
|
||||
const pdfRefreshBtn = document.getElementById('pdfRefreshBtn');
|
||||
|
||||
if (pdfYearSelect && pdfUserSelect && pdfFilesContainer) {
|
||||
if (pdfRefreshBtn) {
|
||||
pdfRefreshBtn.addEventListener('click', function() {
|
||||
clearPdfPreview();
|
||||
loadPdfYears();
|
||||
});
|
||||
}
|
||||
|
||||
pdfYearSelect.addEventListener('change', function() {
|
||||
clearPdfPreview();
|
||||
const year = this.value;
|
||||
if (year) loadPdfUsers(year);
|
||||
});
|
||||
|
||||
pdfUserSelect.addEventListener('change', function() {
|
||||
clearPdfPreview();
|
||||
const year = pdfYearSelect.value;
|
||||
const userId = this.value;
|
||||
if (year && userId) loadPdfFiles(year, userId);
|
||||
});
|
||||
|
||||
loadPdfYears();
|
||||
}
|
||||
});
|
||||
|
||||
function setPdfStatus(text, color) {
|
||||
const el = document.getElementById('pdfArchiveStatus');
|
||||
if (!el) return;
|
||||
el.textContent = text || '';
|
||||
el.style.color = color || '';
|
||||
}
|
||||
|
||||
function clearPdfPreview() {
|
||||
const container = document.getElementById('pdfPreviewContainer');
|
||||
const iframe = document.getElementById('pdfPreviewIframe');
|
||||
if (container) container.style.display = 'none';
|
||||
if (iframe) iframe.src = '';
|
||||
}
|
||||
|
||||
function formatBytes(bytes) {
|
||||
if (bytes === null || bytes === undefined || !Number.isFinite(Number(bytes))) return '-';
|
||||
const num = Number(bytes);
|
||||
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
let i = 0;
|
||||
let n = num;
|
||||
while (n >= 1024 && i < units.length - 1) {
|
||||
n /= 1024;
|
||||
i += 1;
|
||||
}
|
||||
const fixed = i === 0 ? 0 : 1;
|
||||
return `${n.toFixed(fixed)} ${units[i]}`;
|
||||
}
|
||||
|
||||
async function loadPdfYears() {
|
||||
const pdfYearSelect = document.getElementById('pdfYearSelect');
|
||||
const pdfUserSelect = document.getElementById('pdfUserSelect');
|
||||
const pdfFilesContainer = document.getElementById('pdfFilesContainer');
|
||||
if (!pdfYearSelect || !pdfUserSelect || !pdfFilesContainer) return;
|
||||
|
||||
setPdfStatus('Lade PDF-Jahre...', 'blue');
|
||||
pdfYearSelect.innerHTML = '<option value="">-- Laden... --</option>';
|
||||
pdfUserSelect.innerHTML = '';
|
||||
pdfUserSelect.disabled = true;
|
||||
pdfFilesContainer.innerHTML = '';
|
||||
clearPdfPreview();
|
||||
|
||||
try {
|
||||
const response = await fetch('/admin/api/pdfs/years');
|
||||
const result = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(result && result.error ? result.error : 'Fehler beim Laden der PDF-Jahre');
|
||||
}
|
||||
|
||||
const years = Array.isArray(result.years) ? result.years : [];
|
||||
if (years.length === 0) {
|
||||
pdfYearSelect.innerHTML = '<option value="">Keine PDFs gefunden</option>';
|
||||
setPdfStatus('Keine PDF-Dateien im Archiv gefunden.', '');
|
||||
return;
|
||||
}
|
||||
|
||||
pdfYearSelect.innerHTML = '';
|
||||
years.forEach(year => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = year;
|
||||
opt.textContent = year;
|
||||
pdfYearSelect.appendChild(opt);
|
||||
});
|
||||
|
||||
const firstYear = years[0];
|
||||
pdfYearSelect.value = firstYear;
|
||||
setPdfStatus('Jahre geladen. Lade User...', '');
|
||||
await loadPdfUsers(firstYear);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der PDF-Jahre:', error);
|
||||
setPdfStatus('Fehler beim Laden der PDF-Jahre', 'red');
|
||||
pdfYearSelect.innerHTML = '<option value="">Fehler</option>';
|
||||
pdfUserSelect.disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadPdfUsers(year) {
|
||||
const pdfYearSelect = document.getElementById('pdfYearSelect');
|
||||
const pdfUserSelect = document.getElementById('pdfUserSelect');
|
||||
const pdfFilesContainer = document.getElementById('pdfFilesContainer');
|
||||
if (!pdfYearSelect || !pdfUserSelect || !pdfFilesContainer) return;
|
||||
|
||||
setPdfStatus(`Lade PDFs für ${year}...`, 'blue');
|
||||
pdfUserSelect.disabled = true;
|
||||
pdfUserSelect.innerHTML = '<option value="">-- Laden... --</option>';
|
||||
pdfFilesContainer.innerHTML = '';
|
||||
clearPdfPreview();
|
||||
|
||||
try {
|
||||
const response = await fetch(`/admin/api/pdfs/users?year=${encodeURIComponent(year)}`);
|
||||
const result = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(result && result.error ? result.error : 'Fehler beim Laden der User-Ordner');
|
||||
}
|
||||
|
||||
const users = Array.isArray(result.users)
|
||||
? result.users
|
||||
: (Array.isArray(result.userIds) ? result.userIds.map(id => ({ id, name: id })) : []);
|
||||
|
||||
const userIds = users.map(u => u.id);
|
||||
if (users.length === 0) {
|
||||
pdfUserSelect.innerHTML = '<option value="">Keine PDFs</option>';
|
||||
pdfUserSelect.disabled = true;
|
||||
setPdfStatus(`Keine PDF-Dateien für das Jahr ${year} gefunden.`, '');
|
||||
return;
|
||||
}
|
||||
|
||||
pdfUserSelect.disabled = false;
|
||||
pdfUserSelect.innerHTML = '';
|
||||
users.forEach(user => {
|
||||
const userId = user.id;
|
||||
const opt = document.createElement('option');
|
||||
opt.value = userId;
|
||||
opt.textContent = user.name || userId;
|
||||
pdfUserSelect.appendChild(opt);
|
||||
});
|
||||
|
||||
const firstUserId = userIds[0];
|
||||
pdfUserSelect.value = firstUserId;
|
||||
setPdfStatus('User geladen. Lade Dateien...', '');
|
||||
await loadPdfFiles(year, firstUserId);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der PDF-User:', error);
|
||||
setPdfStatus('Fehler beim Laden der PDF-User', 'red');
|
||||
pdfUserSelect.innerHTML = '<option value="">Fehler</option>';
|
||||
pdfUserSelect.disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadPdfFiles(year, userId) {
|
||||
const pdfFilesContainer = document.getElementById('pdfFilesContainer');
|
||||
if (!pdfFilesContainer) return;
|
||||
|
||||
setPdfStatus('Lade PDF-Dateien...', 'blue');
|
||||
pdfFilesContainer.innerHTML = '';
|
||||
clearPdfPreview();
|
||||
|
||||
try {
|
||||
const response = await fetch(`/admin/api/pdfs/files?year=${encodeURIComponent(year)}&userId=${encodeURIComponent(userId)}`);
|
||||
const result = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(result && result.error ? result.error : 'Fehler beim Laden der PDF-Dateien');
|
||||
}
|
||||
|
||||
const files = Array.isArray(result.files) ? result.files : [];
|
||||
if (files.length === 0) {
|
||||
pdfFilesContainer.innerHTML = '<p style="color:#666;">Keine PDF-Dateien gefunden.</p>';
|
||||
setPdfStatus(`Keine PDFs für User ${userId} (${year}).`, '');
|
||||
return;
|
||||
}
|
||||
|
||||
const rowsHtml = files
|
||||
.map(file => {
|
||||
const mtimeLabel = file.mtime ? new Date(file.mtime).toLocaleString('de-DE') : '-';
|
||||
const sizeLabel = formatBytes(file.size);
|
||||
// name ist serverseitig streng gefiltert; hier daher direkt in JS-String verwenden.
|
||||
return `
|
||||
<tr>
|
||||
<td style="padding:4px; word-break:break-all;">
|
||||
<code>${file.name}</code>
|
||||
</td>
|
||||
<td style="padding:4px;">${sizeLabel}</td>
|
||||
<td style="padding:4px;">${mtimeLabel}</td>
|
||||
<td style="padding:4px; white-space:nowrap;">
|
||||
<button type="button" class="btn btn-primary btn-sm" onclick="previewPdf('${year}','${userId}','${file.name}')">Vorschau</button>
|
||||
<button type="button" class="btn btn-secondary btn-sm" style="margin-left:6px;" onclick="downloadPdf('${year}','${userId}','${file.name}')">Download</button>
|
||||
</td>
|
||||
</tr>
|
||||
`;
|
||||
})
|
||||
.join('');
|
||||
|
||||
pdfFilesContainer.innerHTML = `
|
||||
<table style="width: 100%; min-width: 980px; border-collapse: collapse;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="text-align:left; padding:4px;">Datei</th>
|
||||
<th style="text-align:left; padding:4px;">Größe</th>
|
||||
<th style="text-align:left; padding:4px;">Änderung</th>
|
||||
<th style="text-align:left; padding:4px;">Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${rowsHtml}
|
||||
</tbody>
|
||||
</table>
|
||||
`;
|
||||
|
||||
setPdfStatus(`${files.length} PDF(s) geladen.`, 'green');
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der PDF-Dateien:', error);
|
||||
pdfFilesContainer.innerHTML = '<p style="color: red;">Fehler beim Laden der PDF-Dateien.</p>';
|
||||
setPdfStatus('Fehler beim Laden der PDF-Dateien', 'red');
|
||||
}
|
||||
}
|
||||
|
||||
function previewPdf(year, userId, name) {
|
||||
const iframe = document.getElementById('pdfPreviewIframe');
|
||||
const container = document.getElementById('pdfPreviewContainer');
|
||||
if (!iframe || !container) return;
|
||||
|
||||
setPdfStatus(`Vorschau: ${name}`, 'blue');
|
||||
container.style.display = 'block';
|
||||
iframe.src = `/admin/api/pdfs/file?year=${encodeURIComponent(year)}&userId=${encodeURIComponent(userId)}&name=${encodeURIComponent(name)}&inline=true`;
|
||||
}
|
||||
|
||||
function downloadPdf(year, userId, name) {
|
||||
const url = `/admin/api/pdfs/file?year=${encodeURIComponent(year)}&userId=${encodeURIComponent(userId)}&name=${encodeURIComponent(name)}&inline=false`;
|
||||
|
||||
setPdfStatus('Download gestartet...', '');
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.target = '_blank';
|
||||
link.rel = 'noopener';
|
||||
link.click();
|
||||
}
|
||||
|
||||
async function loadTimesheetDuplicates() {
|
||||
const container = document.getElementById('timesheetDuplicatesContainer');
|
||||
if (!container) return;
|
||||
|
||||
Reference in New Issue
Block a user