timeToMin destructured [h, m] from t.split(':').map(Number); under
noUncheckedIndexedAccess those are number|undefined, so `h * 60` errored. Use
default-value destructuring ([h = 0, m = 0]). Surfaced now that the Gitea runner
actually runs the typecheck job (it never ran before).
The apps were wired for the dev (.local) environment. Drive the base URLs from
env so one build serves dev and prod (.eu):
- portal nuxt.config: OIDC authorization/token/userinfo/discovery URLs +
redirectUri now derive from NUXT_PUBLIC_AUTH_URL / NUXT_PUBLIC_PORTAL_URL
(+ PORTAL_OIDC_APP_SLUG); .local defaults keep dev working with no env.
- portal sign-out handler: end-session + post-logout URLs env-driven.
- portal scheduling page: booking base/host from runtimeConfig.public.bookingUrl
(NUXT_PUBLIC_BOOKING_URL).
- platform-api: tenant mail domain suffix from PLATFORM_TENANT_DOMAIN (dezky.eu
in prod), defaulting to dezky.local.
(booking needs no change — its only .local ref is the dev-server allowedHosts.)
Surface pending/calendar_failed booking states in the admin bookings list with
proper status badges (failed shows the last calendar error as a tooltip), and
add an operator "Retry now" action. The retry re-drives the same Stalwart
calendar write (confirm + attendee email on success); for a terminal
calendar_failed booking it re-claims the slot lock atomically first and refuses
if the time was taken in the meantime, so a manual retry can never double-book.