feat(shared,api): add pause + sort_order columns and contracts

Adds server-side pause accounting and activity ordering primitives.
- WorkSession contract gains paused_seconds (number) and paused_at (ISO string | null).
- Activity contract gains sort_order (number); new ReorderActivitiesInput zod.
- work_sessions += paused_seconds (int NOT NULL DEFAULT 0) + paused_at (timestamp_ms nullable).
- activities += sort_order (int NOT NULL DEFAULT 0).
- toWorkSession / toActivity map the new fields; generated migration 0003.

The new fields are additive; existing api/worker/admin tests stay green.

Products affected: SoleLog backend (apps/api), shared contracts (packages/shared)
This commit is contained in:
Bas van Rossem
2026-06-17 20:49:56 +02:00
parent eae9a53a26
commit 0d82b6efbc
8 changed files with 671 additions and 0 deletions

View File

@@ -124,6 +124,7 @@ export const activities = sqliteTable('activities', {
.$type<string[]>()
.notNull()
.default(['Kurk', 'Berk', '3D']),
sortOrder: integer('sort_order').notNull().default(0),
createdAt: integer('created_at', { mode: 'timestamp_ms' })
.default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`)
.notNull(),
@@ -144,6 +145,8 @@ export const workSessions = sqliteTable(
startTime: integer('start_time', { mode: 'timestamp_ms' }).notNull(),
endTime: integer('end_time', { mode: 'timestamp_ms' }), // null = active
durationSeconds: integer('duration_seconds'),
pausedSeconds: integer('paused_seconds').notNull().default(0),
pausedAt: integer('paused_at', { mode: 'timestamp_ms' }), // null = running
status: text('status').notNull().default('active'), // 'active' | 'completed' | 'discarded'
source: text('source').notNull().default('app'), // 'app' | 'manual'
notes: text('notes'),

View File

@@ -19,6 +19,8 @@ export function toWorkSession(
start_time: new Date(row.startTime).toISOString(),
end_time: row.endTime ? new Date(row.endTime).toISOString() : null,
duration_seconds: row.durationSeconds ?? null,
paused_seconds: row.pausedSeconds ?? 0,
paused_at: row.pausedAt ? new Date(row.pausedAt).toISOString() : null,
status: row.status as WorkSession['status'],
source: row.source as WorkSession['source'],
notes: row.notes ?? null,

View File

@@ -16,6 +16,7 @@ function toActivity(row: ActivityRow): Activity {
name: row.name,
insole_types: row.insoleTypes as Activity['insole_types'],
created_at: new Date(row.createdAt).toISOString(),
sort_order: row.sortOrder ?? 0,
};
}