Add timezone selector to Admin settings with full-stack support

- Add `timezone` column to User model (VARCHAR 64, default UTC) with
  idempotent startup migration
- Expose and persist timezone via PATCH /api/users/me
- Fix TimerEvent.occurred_at serialization to include UTC offset marker
  (+00:00) so JavaScript correctly parses timestamps as UTC
- Add frontend utility (src/utils/time.js) with timezone-aware
  formatTime, getHHMM, getDateInTZ, tzDateTimeToUTC helpers and a
  curated IANA timezone list
- Add Settings section to Admin page with timezone dropdown; saves to
  both the API and localStorage for the unauthenticated TV view
- Update Activity Log to display and edit times in the user's timezone
- Update TV dashboard clock to respect the saved timezone
- Update README: features, setup steps, usage table, WebSocket events

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-01 14:16:37 -08:00
parent fef03ec538
commit 823260cdd8
11 changed files with 230 additions and 24 deletions

View File

@@ -1,5 +1,5 @@
from datetime import date, datetime
from pydantic import BaseModel
from datetime import date, datetime, timezone
from pydantic import BaseModel, field_serializer
class ActivityLogCreate(BaseModel):
@@ -45,3 +45,9 @@ class TimelineEventOut(BaseModel):
subject_name: str | None = None
subject_icon: str | None = None
subject_color: str | None = None
@field_serializer("occurred_at")
def serialize_occurred_at(self, dt: datetime) -> str:
if dt.tzinfo is None:
dt = dt.replace(tzinfo=timezone.utc)
return dt.isoformat()

View File

@@ -7,6 +7,7 @@ class UserOut(BaseModel):
full_name: str
is_active: bool
is_admin: bool
timezone: str = "UTC"
model_config = {"from_attributes": True}
@@ -14,3 +15,4 @@ class UserOut(BaseModel):
class UserUpdate(BaseModel):
full_name: str | None = None
email: EmailStr | None = None
timezone: str | None = None