feat(api): user-scoped CSV export matching legacy format

This commit is contained in:
Bas van Rossem
2026-06-17 15:49:20 +02:00
parent b067bb65b0
commit 85184d3287
3 changed files with 214 additions and 1 deletions

View File

@@ -1,10 +1,11 @@
import { Hono } from 'hono';
import { and, desc, eq } from 'drizzle-orm';
import { and, asc, desc, eq } from 'drizzle-orm';
import { StartSessionInput } from '@solelog/shared';
import type { WorkSession } from '@solelog/shared';
import { db } from '../db/client';
import { activities, workSessions } from '../db/schema';
import { getSessionUser } from '../lib/require-user';
import { quote, formatDuration } from '../lib/csv';
export const sessionsRoutes = new Hono();
@@ -28,6 +29,61 @@ function toWorkSession(row: WorkSessionRow, activityName?: string | null): WorkS
};
}
sessionsRoutes.get('/api/export', async (c) => {
const sessionUser = await getSessionUser(c);
if (!sessionUser) return c.json({ error: 'Unauthorized' }, 401);
const rows = await db
.select({ session: workSessions, activityName: activities.name })
.from(workSessions)
.leftJoin(activities, eq(workSessions.activityId, activities.id))
.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',
'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),
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');
return c.body(csv, 200, {
'Content-Type': 'text/csv; charset=utf-8',
'Content-Disposition': 'attachment; filename="insole-production-report.csv"',
});
});
sessionsRoutes.get('/api/sessions/active', async (c) => {
const sessionUser = await getSessionUser(c);
if (!sessionUser) return c.json({ error: 'Unauthorized' }, 401);