feat(api): user-scoped CSV export matching legacy format
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user