- Add confirmation alert when deleting clients with warning about
deleted dependencies (projects and time entries)
- Add confirmation alert when deleting projects with warning about
deleted time entries
- Add confirmation alert when deleting time entries
- All alerts include item name and emphasize action cannot be undone
Times were always showing 0 because ISO8601DateFormatter with default
options does not parse fractional seconds, but Prisma/Node.js serialises
dates as "2026-02-20T09:00:00.000Z" (with .000). Every date(from:) call
silently returned nil, so elapsedTime and duration always fell back to 0.
- Date+Extensions: fromISO8601 now tries .withFractionalSeconds first,
then falls back to whole seconds — single place to maintain
- OngoingTimer.elapsedTime: use Date.fromISO8601() instead of bare formatter
- TimeEntry.duration: use Date.fromISO8601() instead of bare formatters
- TimerView: add TimerUnitLabels view showing h/min/sec column headers
under the monospaced clock digits
The /login route was not passing an explicit redirect_uri to the IDP for
the web flow, so openid-client would silently pick a default which could
resolve to localhost:3001 if OIDC_REDIRECT_URI was not set.
- AuthSession.redirectUri is now required (non-optional)
- createAuthSession() requires a redirectUri; detects native vs web via
the timetracker:// scheme prefix instead of presence/absence of the arg
- /login route resolves the URI explicitly: request param for native
flows, config.oidc.redirectUri for web flows
- getAuthorizationUrl() reads redirect_uri from session, no longer
accepts it as a separate argument
- handleCallback() uses session.redirectUri directly, removing the
fallback to config.oidc.redirectUri
Add backend.oidc.iosRedirectUri (default: timetracker://oauth/callback) and
backend.jwt.secret to values.yaml and wire them into the backend deployment
as OIDC_IOS_REDIRECT_URI and JWT_SECRET env vars. Update NOTES.txt to surface
both values post-install.
GET /clients and GET /projects return bare arrays, not wrapped objects.
Remove ClientListResponse and ProjectListResponse wrapper structs and
update ClientsViewModel, ProjectsViewModel, and TimerViewModel to decode
[Client] and [Project] directly.
Keychain writes silently failed (missing keychain-access-groups entitlement
on simulator), causing the token to disappear between handleTokenResponse
and the first API call. The in-memory cache ensures the token is always
available within the session; the keychain still persists it across launches
when entitlements allow.
iOS clients now exchange the OIDC authorization code for a backend-signed
HS256 JWT via POST /auth/token. All subsequent API requests authenticate
using this JWT as a Bearer token, verified locally — no per-request IDP
call is needed. Web frontend session-cookie auth is unchanged.