166 lines
6.0 KiB
HTML
166 lines
6.0 KiB
HTML
<!doctype html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>CDH Merger</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link href="https://unpkg.com/tabulator-tables@5.4.4/dist/css/tabulator.min.css" rel="stylesheet">
|
|
<style>
|
|
body { padding: 2rem; background: #f8f9fa; }
|
|
#table-container { height: 800px; width: 100%; overflow-x: auto;}
|
|
#table { width: 100%; }
|
|
|
|
/* Loading Overlay */
|
|
#loadingOverlay {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100vw;
|
|
height: 100vh;
|
|
background: rgba(0, 0, 0, 0.5);
|
|
display: none;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 9999;
|
|
}
|
|
|
|
/* Ensure toolbar stretches full width */
|
|
#toolbar { width: 100%; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container-fluid px-0">
|
|
<h1 class="mb-4 ps-3">CDH Merger</h1>
|
|
|
|
<!-- Toolbar -->
|
|
<div class="d-flex flex-wrap align-items-center mb-3 px-3" id="toolbar">
|
|
<form id="upload-form" class="d-flex align-items-center me-3">
|
|
<input type="file" name="files" multiple class="form-control form-control-sm me-2">
|
|
<button type="submit" class="btn btn-primary btn-sm">Upload</button>
|
|
</form>
|
|
<button id="download-excel" class="btn btn-success btn-sm me-3">Excel Download</button>
|
|
<button id="clear-session" class="btn btn-warning btn-sm me-3">Daten löschen</button>
|
|
<div class="d-flex align-items-center">
|
|
<label for="table-select" class="form-label me-2 mb-0">Table:</label>
|
|
<select id="table-select" class="form-select form-select-sm">
|
|
<option value="stripe_import">Stripe Import</option>
|
|
<option value="raiseNow_import">RaiseNow Import</option>
|
|
<option value="merged">Merged</option>
|
|
<option value="stripe_only">Stripe Only</option>
|
|
<option value="raisenow_only">RaiseNow Only</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Table -->
|
|
<div id="table-container" class="border rounded shadow-sm px-3">
|
|
<div id="table"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Loading Overlay -->
|
|
<div id="loadingOverlay">
|
|
<div class="text-center text-white">
|
|
<div class="spinner-border" role="status" style="width: 4rem; height: 4rem;"><span class="visually-hidden">Loading...</span></div>
|
|
<p class="mt-3">Loading, please wait...</p>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script src="https://unpkg.com/tabulator-tables@5.4.4/dist/js/tabulator.min.js"></script>
|
|
<script>
|
|
const uploadForm = document.getElementById('upload-form');
|
|
const tableSelect = document.getElementById('table-select');
|
|
const downloadBtn = document.getElementById('download-excel');
|
|
const clearBtn = document.getElementById('clear-session');
|
|
const loadingOverlay = document.getElementById('loadingOverlay');
|
|
let table;
|
|
|
|
function showLoading() { loadingOverlay.style.display = 'flex'; }
|
|
function hideLoading() { loadingOverlay.style.display = 'none'; }
|
|
|
|
uploadForm.addEventListener('submit', async e => {
|
|
e.preventDefault(); showLoading();
|
|
try {
|
|
const fd = new FormData(uploadForm);
|
|
const resp = await fetch('/upload', { method: 'POST', body: fd });
|
|
const res = await resp.json();
|
|
if (resp.ok) await loadTable(tableSelect.value);
|
|
else alert(res.error || 'Upload failed');
|
|
} catch (err) {
|
|
console.error(err); alert('An error occurred');
|
|
} finally { hideLoading(); }
|
|
});
|
|
|
|
tableSelect.addEventListener('change', () => loadTable(tableSelect.value));
|
|
|
|
downloadBtn.addEventListener('click', async () => {
|
|
showLoading();
|
|
try {
|
|
const resp = await fetch('/download');
|
|
if (!resp.ok) throw new Error('Download failed');
|
|
const blob = await resp.blob();
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
const disposition = resp.headers.get('Content-Disposition') || '';
|
|
const match = disposition.match(/filename="?([^";]+)"?/);
|
|
a.download = match ? match[1] : 'tables.xlsx';
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
a.remove();
|
|
URL.revokeObjectURL(url);
|
|
} catch (err) {
|
|
console.error(err); alert(err.message);
|
|
} finally { hideLoading(); }
|
|
});
|
|
|
|
clearBtn.addEventListener('click', async () => {
|
|
if (!confirm('Are you sure you want to clear the server session? This will reset all loaded data.')) return;
|
|
showLoading();
|
|
try {
|
|
const resp = await fetch('/clear_session', { method: 'POST' });
|
|
const res = await resp.json();
|
|
if (!resp.ok) throw new Error(res.error || 'Failed to clear session');
|
|
// Reset UI
|
|
tableSelect.value = 'stripe_import';
|
|
if (table) table.destroy();
|
|
table = null;
|
|
alert('Session cleared successfully.');
|
|
} catch (err) {
|
|
console.error(err); alert(err.message);
|
|
} finally {
|
|
hideLoading();
|
|
}
|
|
});
|
|
|
|
async function loadTable(name) {
|
|
showLoading();
|
|
try {
|
|
const resp = await fetch(`/get_table?table=${encodeURIComponent(name)}`);
|
|
const json = await resp.json();
|
|
if (!resp.ok) { alert(json.error || 'Error loading'); return; }
|
|
if (!Array.isArray(json.data) || !json.data.length) { alert('No data for this table'); return; }
|
|
|
|
const cols = json.columns.map(c => ({ title: c, field: c, headerFilter: true }));
|
|
const opts = {
|
|
data: json.data,
|
|
layout: 'fitData',
|
|
height: '100%',
|
|
columns: cols,
|
|
};
|
|
|
|
if (table) table.destroy();
|
|
table = new Tabulator('#table', opts);
|
|
} catch (err) {
|
|
console.error(err); alert('Failed to load table data');
|
|
} finally { hideLoading(); }
|
|
}
|
|
|
|
// initialize
|
|
loadTable(tableSelect.value);
|
|
</script>
|
|
</body>
|
|
</html>
|