feat: add database export and import functionality with user confirmation

This commit is contained in:
Felix Schlusche
2025-10-24 15:23:10 +02:00
parent 1cc8dc3b6c
commit 73b83198cb
2 changed files with 145 additions and 0 deletions

View File

@@ -3215,6 +3215,115 @@ async function handleExportPDF() {
}
}
/**
* Export entire database
*/
async function exportDatabase() {
try {
// Fetch all data
const entries = await fetchEntries();
const settings = {
employeeName: await getSetting('employeeName') || '',
employeeId: await getSetting('employeeId') || '',
bundesland: await getSetting('bundesland') || 'NW',
vacationDaysPerYear: await getSetting('vacationDaysPerYear') || 30
};
// Create export object
const exportData = {
version: '1.0',
exportDate: new Date().toISOString(),
entries: entries,
settings: settings
};
// Convert to JSON
const jsonString = JSON.stringify(exportData, null, 2);
const blob = new Blob([jsonString], { type: 'application/json' });
// Create download link
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `zeiterfassung_backup_${new Date().toISOString().split('T')[0]}.json`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
showNotification(`Datenbank exportiert: ${entries.length} Einträge`, 'success');
} catch (error) {
console.error('Error exporting database:', error);
showNotification('Fehler beim Exportieren der Datenbank', 'error');
}
}
/**
* Import entire database
*/
async function importDatabase(file) {
try {
const text = await file.text();
const importData = JSON.parse(text);
// Validate data structure
if (!importData.entries || !Array.isArray(importData.entries)) {
throw new Error('Ungültiges Datenbankformat');
}
// Confirm before overwriting
const confirmed = confirm(
`Möchten Sie die Datenbank wirklich importieren?\n\n` +
`Dies wird alle ${importData.entries.length} Einträge importieren und vorhandene Daten überschreiben.\n\n` +
`Export-Datum: ${new Date(importData.exportDate).toLocaleString('de-DE')}`
);
if (!confirmed) {
return;
}
// Delete all existing entries
const existingEntries = await fetchEntries();
for (const entry of existingEntries) {
await fetch(`/api/entries/${entry.id}`, { method: 'DELETE' });
}
// Import entries
let imported = 0;
for (const entry of importData.entries) {
try {
// Remove ID to create new entries
const { id, ...entryData } = entry;
await fetch('/api/entries', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(entryData)
});
imported++;
} catch (err) {
console.error('Error importing entry:', err);
}
}
// Import settings
if (importData.settings) {
await setSetting('employeeName', importData.settings.employeeName || '');
await setSetting('employeeId', importData.settings.employeeId || '');
await setSetting('bundesland', importData.settings.bundesland || 'NW');
await setSetting('vacationDaysPerYear', importData.settings.vacationDaysPerYear || 30);
await loadSettings(); // Reload settings to UI
}
// Reload view
await reloadView();
showNotification(`Datenbank importiert: ${imported} Einträge`, 'success');
} catch (error) {
console.error('Error importing database:', error);
showNotification('Fehler beim Importieren der Datenbank: ' + error.message, 'error');
}
}
/**
* Show a temporary notification
*/
@@ -3535,6 +3644,19 @@ function initializeEventListeners() {
showNotification('Personalnummer gespeichert', 'success');
});
// Database export/import
document.getElementById('btnExportDB').addEventListener('click', exportDatabase);
document.getElementById('btnImportDB').addEventListener('click', () => {
document.getElementById('importDBFile').click();
});
document.getElementById('importDBFile').addEventListener('change', (e) => {
const file = e.target.files[0];
if (file) {
importDatabase(file);
e.target.value = ''; // Reset file input
}
});
// Close modal when clicking outside
document.getElementById('entryModal').addEventListener('click', (e) => {
if (e.target.id === 'entryModal') {