From cfc31569064d704c94adb00fd7cf5ed117e39690 Mon Sep 17 00:00:00 2001 From: Jacob Hagstedt Date: Thu, 4 Jun 2026 16:37:37 +0200 Subject: [PATCH] refactor: update sleep session logic, models, and agent protocols --- AGENTS.md | 6 ++++++ backend/app/models.py | 1 - backend/app/schemas.py | 5 ----- frontend/src/components/stats/SessionList.tsx | 16 ++++++++-------- frontend/src/utils/time.ts | 12 +++++++++--- todo.md | 4 ++-- 6 files changed, 25 insertions(+), 19 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 68e1019..b8fba60 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -56,6 +56,12 @@ Baby monitor - self-hosted web app for tracking infant sleep. Dual-stack monorep - **Frontend JS Slop**: NEVER create `.js` or `.jsx` files for the frontend. Use only TypeScript (`.ts`, `.tsx`). Write clean, type-safe code. - **Assumption Error**: NEVER assume the content of ANY files. ALWAYS read the target file to confirm its structure and implementation before modifying or referencing it. +## MANDATORY EXECUTION PROTOCOLS (INJECTABLE) + +The following protocols must be strictly adhered to upon injection: +- **Read Before Edit**: ALWAYS read the entire target file before performing any `edit` operation. +- **Post-Edit Verification**: Immediately after an edit, verify that no lines have been duplicated. +- **Stability Check**: Following any code change, run both backend and frontend services AND the full test suite to ensure system stability. ## COMMANDS ### Backend Development diff --git a/backend/app/models.py b/backend/app/models.py index 068c6d3..c0b8af7 100644 --- a/backend/app/models.py +++ b/backend/app/models.py @@ -9,6 +9,5 @@ class SleepSession(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) start_time: datetime = Field(nullable=False) end_time: Optional[datetime] = Field(default=None, nullable=True) - notes: Optional[str] = Field(default=None) created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) updated_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) diff --git a/backend/app/schemas.py b/backend/app/schemas.py index 8e5cb38..689aa6b 100644 --- a/backend/app/schemas.py +++ b/backend/app/schemas.py @@ -6,22 +6,17 @@ from pydantic import BaseModel class SleepSessionCreate(BaseModel): start_time: datetime - class SleepSessionUpdate(BaseModel): start_time: Optional[datetime] = None end_time: Optional[datetime] = None - notes: Optional[str] = None - class SleepSessionRead(BaseModel): id: int start_time: datetime end_time: Optional[datetime] - notes: Optional[str] created_at: datetime updated_at: datetime - class SessionListResponse(BaseModel): sessions: List[SleepSessionRead] total: int diff --git a/frontend/src/components/stats/SessionList.tsx b/frontend/src/components/stats/SessionList.tsx index 1645506..4e88406 100644 --- a/frontend/src/components/stats/SessionList.tsx +++ b/frontend/src/components/stats/SessionList.tsx @@ -4,7 +4,7 @@ import { useSettings } from '../../hooks/useSettings'; import { Pagination } from './Pagination'; import { Edit2, Check, X, Trash2 } from 'lucide-react'; import type { SleepSessionRead } from '../../types'; -import { formatDateTime, toLocalDatetimeInputValue, getDurationMinutes, formatMinutes } from '../../utils/time'; +import { formatDateTime, toLocalDatetimeInputValue, getDurationMinutes, formatMinutes, toUTCFromLocalInput } from '../../utils/time'; export function SessionList() { const { sessions, isLoading, updateSession, deleteSession, isDeleting } = useSessions(); @@ -29,13 +29,13 @@ export function SessionList() { const handleSave = async (id: number) => { try { - await updateSession({ - sessionId: id, - payload: { - start_time: new Date(editStart).toISOString(), - end_time: editEnd ? new Date(editEnd).toISOString() : undefined, - } - }); + await updateSession({ + sessionId: id, + payload: { + start_time: toUTCFromLocalInput(editStart), + end_time: editEnd ? toUTCFromLocalInput(editEnd) : undefined, + }, + }); setEditingId(null); } catch (err) { console.error(err); diff --git a/frontend/src/utils/time.ts b/frontend/src/utils/time.ts index 2a2cff8..e4530e1 100644 --- a/frontend/src/utils/time.ts +++ b/frontend/src/utils/time.ts @@ -38,9 +38,15 @@ export function adjustForTimezone(utcMinutes: number): number { * @returns string in "YYYY-MM-DDTHH:MM" format */ export function toLocalDatetimeInputValue(date: Date): string { - const offset = date.getTimezoneOffset() * 60 * 1000; - const localDate = new Date(date.getTime() - offset); - return localDate.toISOString().slice(0, 16); + const formatter = new Intl.DateTimeFormat('sv-SE', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + hour12: false, + }); + return formatter.format(date).replace(' ', 'T'); } export function formatElapsedTime(utcTimestamp: string): string { diff --git a/todo.md b/todo.md index adcfa37..8c85fcf 100644 --- a/todo.md +++ b/todo.md @@ -19,10 +19,10 @@ exitcode '1': failure ``` - [ ] Editing a sleep session in "sleep sessions", when I click the edit button the times show up as UTC but should show as my local timezone -- [/] We should drop the "notes" column from the sleep_session table +- [x] We should drop the "notes" column from the sleep_session table - [x] Switch over to oh-my-opencode and try it out - [x] Adda footer at the bottom of the app that shows the version (aka git tag) of the app -- [-] Fix race condition of gitea workflows +- [x] Fix race condition of gitea workflows If both workflows run at the same time, a race condition occurs: 1. Collision: Both workflows will query the latest tag (e.g., v1.0.5) and calculate the same next version (v1.0.6).