refactor(api): extract buildSessionsCsv shared by worker + admin exports
This commit is contained in:
@@ -11,3 +11,62 @@ export function formatDuration(totalSeconds: number): string {
|
||||
const sec = s % 60;
|
||||
return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:${String(sec).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
// One completed session, flattened for CSV. Times accept Date | number | string.
|
||||
export interface SessionCsvRow {
|
||||
id: number;
|
||||
activityName: string | null;
|
||||
userName?: string | null;
|
||||
insoleType: string | null;
|
||||
pairCount: number;
|
||||
startTime: Date | number | string;
|
||||
endTime: Date | number | string | null;
|
||||
durationSeconds: number | null;
|
||||
pausedSeconds: number | null;
|
||||
}
|
||||
|
||||
// Build the CSV body shared by the worker self-export and the admin all-users export.
|
||||
// includeWorker prepends a Worker column; everything else matches the legacy format byte-for-byte.
|
||||
export function buildSessionsCsv(
|
||||
rows: SessionCsvRow[],
|
||||
opts: { includeWorker?: boolean } = {},
|
||||
): string {
|
||||
const includeWorker = opts.includeWorker ?? false;
|
||||
const header = [
|
||||
...(includeWorker ? ['Worker'] : []),
|
||||
'ID',
|
||||
'Task',
|
||||
'Insole Type',
|
||||
'No. of Insoles',
|
||||
'Date',
|
||||
'Total Duration',
|
||||
'Paused Duration',
|
||||
'Start Time',
|
||||
'End Time',
|
||||
]
|
||||
.map(quote)
|
||||
.join(',');
|
||||
|
||||
const dataLines = rows.map((row) => {
|
||||
const start = new Date(row.startTime);
|
||||
const end = row.endTime ? new Date(row.endTime) : null;
|
||||
return [
|
||||
...(includeWorker ? [row.userName ?? ''] : []),
|
||||
row.id,
|
||||
row.activityName ?? '',
|
||||
row.insoleType ?? 'Kurk',
|
||||
row.pairCount ?? 2,
|
||||
start.toLocaleDateString('nl-BE', { day: '2-digit', month: '2-digit', year: 'numeric' }),
|
||||
formatDuration(row.durationSeconds ?? 0),
|
||||
formatDuration(row.pausedSeconds ?? 0),
|
||||
start.toLocaleTimeString('nl-BE', { hour: '2-digit', minute: '2-digit', second: '2-digit' }),
|
||||
end
|
||||
? end.toLocaleTimeString('nl-BE', { hour: '2-digit', minute: '2-digit', second: '2-digit' })
|
||||
: '',
|
||||
]
|
||||
.map(quote)
|
||||
.join(',');
|
||||
});
|
||||
|
||||
return [header, ...dataLines].join('\n');
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { db } from '../db/client';
|
||||
import { activities, workSessions } from '../db/schema';
|
||||
import { getSessionUser } from '../lib/require-user';
|
||||
import { toWorkSession } from '../lib/work-session';
|
||||
import { quote, formatDuration } from '../lib/csv';
|
||||
import { buildSessionsCsv } from '../lib/csv';
|
||||
|
||||
export const sessionsRoutes = new Hono();
|
||||
|
||||
@@ -20,45 +20,18 @@ sessionsRoutes.get('/api/export', async (c) => {
|
||||
.where(and(eq(workSessions.userId, sessionUser.id), eq(workSessions.status, 'completed')))
|
||||
.orderBy(asc(workSessions.startTime));
|
||||
|
||||
const header = [
|
||||
'ID',
|
||||
'Task',
|
||||
'Insole Type',
|
||||
'No. of Insoles',
|
||||
'Date',
|
||||
'Total Duration',
|
||||
'Paused Duration',
|
||||
'Start Time',
|
||||
'End Time',
|
||||
]
|
||||
.map(quote)
|
||||
.join(',');
|
||||
|
||||
const dataLines = rows.map(({ session, activityName }) => {
|
||||
const start = new Date(session.startTime);
|
||||
const end = session.endTime ? new Date(session.endTime) : null;
|
||||
return [
|
||||
session.id,
|
||||
activityName ?? '',
|
||||
session.insoleType ?? 'Kurk',
|
||||
session.pairCount ?? 2,
|
||||
start.toLocaleDateString('nl-BE', { day: '2-digit', month: '2-digit', year: 'numeric' }),
|
||||
formatDuration(session.durationSeconds ?? 0),
|
||||
formatDuration(session.pausedSeconds ?? 0),
|
||||
start.toLocaleTimeString('nl-BE', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
}),
|
||||
end
|
||||
? end.toLocaleTimeString('nl-BE', { hour: '2-digit', minute: '2-digit', second: '2-digit' })
|
||||
: '',
|
||||
]
|
||||
.map(quote)
|
||||
.join(',');
|
||||
});
|
||||
|
||||
const csv = [header, ...dataLines].join('\n');
|
||||
const csv = buildSessionsCsv(
|
||||
rows.map(({ session, activityName }) => ({
|
||||
id: session.id,
|
||||
activityName,
|
||||
insoleType: session.insoleType,
|
||||
pairCount: session.pairCount,
|
||||
startTime: session.startTime,
|
||||
endTime: session.endTime,
|
||||
durationSeconds: session.durationSeconds,
|
||||
pausedSeconds: session.pausedSeconds,
|
||||
})),
|
||||
);
|
||||
|
||||
return c.body(csv, 200, {
|
||||
'Content-Type': 'text/csv; charset=utf-8',
|
||||
|
||||
Reference in New Issue
Block a user