First csv download

initial-version
Simon Stürz 2025-12-11 13:54:25 +01:00
parent 0f87148e29
commit bcb7b41976
2 changed files with 87 additions and 0 deletions

View File

@ -17,6 +17,7 @@ class DashboardApp {
chargerTableBody: document.getElementById('chargerTableBody'),
chargerEmptyRow: document.getElementById('chargerEmptyRow'),
fetchSessionsButton: document.getElementById('fetchSessionsButton'),
downloadSessionsButton: document.getElementById('downloadSessionsButton'),
chargerFilter: document.getElementById('chargerFilter'),
chargingSessionsTableBody: document.getElementById('chargingSessionsTableBody'),
chargingSessionsEmptyRow: document.getElementById('chargingSessionsEmptyRow'),
@ -79,6 +80,12 @@ class DashboardApp {
this.fetchChargingSessions();
});
}
if (this.elements.downloadSessionsButton) {
this.elements.downloadSessionsButton.addEventListener('click', () => {
this.downloadChargingSessionsCsv();
});
}
}
initializePanelNavigation() {
@ -1005,6 +1012,85 @@ class DashboardApp {
return end - start;
}
downloadChargingSessionsCsv() {
const sessions = Array.isArray(this.sessions) ? this.sessions : [];
if (!sessions.length) {
console.warn('No charging sessions to download.');
return;
}
const csvContent = this.buildSessionsCsv(sessions);
if (!csvContent) {
console.warn('Failed to build CSV for charging sessions.');
return;
}
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `charging-sessions-${new Date().toISOString().slice(0, 10)}.csv`;
document.body.appendChild(link);
link.click();
setTimeout(() => {
document.body.removeChild(link);
URL.revokeObjectURL(url);
}, 0);
}
buildSessionsCsv(sessions) {
if (!Array.isArray(sessions) || !sessions.length)
return '';
const headers = this.collectSessionColumns(sessions);
if (!headers.length)
return '';
const lines = [];
lines.push(headers.join(';'));
sessions.forEach(session => {
const row = headers.map(header => this.escapeCsvValue(session ? session[header] : ''));
lines.push(row.join(';'));
});
return lines.join('\n');
}
collectSessionColumns(sessions) {
const headers = [];
sessions.forEach(session => {
if (!session)
return;
Object.keys(session).forEach(key => {
if (!headers.includes(key))
headers.push(key);
});
});
return headers;
}
escapeCsvValue(value) {
if (value === null || value === undefined)
return '';
if (typeof value === 'object') {
try {
value = JSON.stringify(value);
} catch (error) {
value = String(value);
}
}
let stringValue = String(value);
if (stringValue.includes('"'))
stringValue = stringValue.replace(/"/g, '""');
if (stringValue.search(/[;\n"]/g) !== -1)
stringValue = `"${stringValue}"`;
return stringValue;
}
updateConnectionStatus(text, state) {
if (this.elements.connectionStatus)
this.elements.connectionStatus.textContent = text;

View File

@ -692,6 +692,7 @@
<option value="">All chargers</option>
</select>
<button type="button" id="fetchSessionsButton" class="primary">Fetch sessions</button>
<button type="button" id="downloadSessionsButton">Download CSV</button>
</div>
</div>
<p class="helper-text">Select a charger to filter by its assigned car and fetch sessions from nymea.</p>