stage 1
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* CSV and attachment export module (F2). Generates a CSV file from the current
|
||||
* report's answers and allows downloading individual attachments. XLSX and ZIP
|
||||
* export can be added by integrating SheetJS and JSZip libraries.
|
||||
*/
|
||||
|
||||
import { state, getCurrentReport, getTemplateRecord } from './state.js';
|
||||
import { dbGetAllByIndex } from './db.js';
|
||||
import { STORE_ATTACHMENTS } from './constants.js';
|
||||
|
||||
/*
|
||||
* Exports the active report as a CSV file. Columns are derived from the template
|
||||
* definition so field labels appear as headers and field values as the row.
|
||||
*/
|
||||
export async function exportReportCSV() {
|
||||
const report = getCurrentReport();
|
||||
|
||||
if (!report) {
|
||||
throw new Error('No report to export');
|
||||
}
|
||||
|
||||
const template = getTemplateRecord(report.templateCode, report.templateVersion);
|
||||
|
||||
if (!template) {
|
||||
throw new Error('Template definition needed for export');
|
||||
}
|
||||
|
||||
const headers = [];
|
||||
const values = [];
|
||||
|
||||
/* Meta columns. */
|
||||
headers.push('Report Number', 'Template', 'Version', 'Status', 'Created', 'Updated');
|
||||
values.push(
|
||||
report.reportNumber,
|
||||
report.templateCode,
|
||||
String(report.templateVersion),
|
||||
report.status,
|
||||
report.createdAt,
|
||||
report.updatedAt
|
||||
);
|
||||
|
||||
/* Dynamic field columns derived from the template definition. */
|
||||
for (const section of template.definition.sections || []) {
|
||||
for (const field of section.fields || []) {
|
||||
if (field.type === 'attachment') {
|
||||
continue;
|
||||
}
|
||||
|
||||
headers.push(field.label);
|
||||
const raw = report.answers[field.id];
|
||||
values.push(raw === undefined || raw === null ? '' : String(raw));
|
||||
}
|
||||
}
|
||||
|
||||
const csvContent = [
|
||||
headers.map(csvEscape).join(','),
|
||||
values.map(csvEscape).join(',')
|
||||
].join('\r\n');
|
||||
|
||||
downloadBlob(
|
||||
new Blob([csvContent], { type: 'text/csv;charset=utf-8' }),
|
||||
`${report.reportNumber || 'report'}.csv`
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Exports all attachments for the active report as individual file downloads.
|
||||
* A future iteration could bundle these into a ZIP archive using JSZip.
|
||||
*/
|
||||
export async function exportReportAttachments() {
|
||||
const report = getCurrentReport();
|
||||
|
||||
if (!report) {
|
||||
throw new Error('No report to export');
|
||||
}
|
||||
|
||||
const attachments = await dbGetAllByIndex(STORE_ATTACHMENTS, 'byReportId', report.id);
|
||||
|
||||
for (const attachment of attachments) {
|
||||
downloadBlob(attachment.blob, attachment.generatedFilename);
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Helpers ────────────────────────────────────────────────────────────── */
|
||||
|
||||
function csvEscape(value) {
|
||||
const str = String(value).replace(/"/g, '""');
|
||||
return /[",\r\n]/.test(str) ? `"${str}"` : str;
|
||||
}
|
||||
|
||||
function downloadBlob(blob, filename) {
|
||||
const url = URL.createObjectURL(blob);
|
||||
const anchor = document.createElement('a');
|
||||
anchor.href = url;
|
||||
anchor.download = filename;
|
||||
document.body.appendChild(anchor);
|
||||
anchor.click();
|
||||
anchor.remove();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
Reference in New Issue
Block a user