These render the initial dashboard page today and will also be
rendered standalone by the sync routes in the next commits, so the
same markup drives both the full page and the post-sync response.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Self-hosted htmx v2.0.10 (no CDN) plus the toast container and CSS
this and the following tasks need for in-place sync updates.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Shows rider count, activities imported in the last 7 days, 7-day
sync success rate, and how many riders currently need attention,
right above the rider list where an admin looks first.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replaces the one-time UTC-to-local formatting with a per-second
countdown (HH:MM:SS, or MM:SS under an hour) that fits the dark
cockpit theme's instrument-panel feel, falling back to "due now" once
the target passes instead of showing a negative duration.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
input[type="number"] was missing from the form field selector added
in the redesign, so the scheduler settings fields stayed white-on-
white. Caught via manual browser verification.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replaces the light generic theme with a dark palette grounded in the
app's own subject matter (bike computer telemetry): electric-lime
accent, tabular monospace numerals for stats/timestamps, hairline
card borders instead of shadows, and visible focus rings. Every
template already shares the same classes, so no markup changes were
needed.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Exposes the scheduler's next_tick in the topbar via a safe Jinja
helper (falls back to nothing if the scheduler isn't running yet),
and converts the server-rendered UTC timestamp to the visitor's local
time client-side so it reads correctly regardless of timezone.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a self-hosted stylesheet (no CDN dependencies) with a card-based
dashboard and color-coded status badges, and shows the last 10 sync
runs per user on the detail page.
- C1: drop module-level app singleton in app/main.py so importing the
package no longer validates Settings or creates DATA_DIR; run uvicorn
with --factory in the Dockerfile. pytest now collects and passes with
no ambient env vars.
- I2: add missing app/auth, app/security, app/web __init__.py so
setuptools discovers all five packages.
- I3: resolve the Jinja2 template directory relative to __file__ instead
of the process CWD.
- I4: add .gitignore covering .env, data/, .venv/, caches and build
artifacts so example deployment secrets cannot be committed.
- I5: assert UserRepository.list_enabled() excludes disabled users.
- M6: encode both operands before hmac.compare_digest in validate_csrf so
a non-ASCII token yields 403 instead of an unhandled 500.
- M9: remove unused relationship / HealthState imports.
- M11: make session cookie https_only configurable via SESSION_HTTPS_ONLY
(default unchanged: false).
- M13: dispose SQLAlchemy engines in the db_session and client fixtures.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add an explicit non-empty check before encrypting user-submitted
email/password fields in the create and update user routes, so a
request that bypasses the HTML `required` attribute gets a clean
400 instead of an unhandled ValueError from CredentialCipher.encrypt
propagating as a 500. Applies to all four credential fields on
create, and to the two email fields on update (the password-blank-
means-keep-existing behavior on update is unchanged).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds full CRUD for sync users (create/list/detail/edit) behind admin
auth and CSRF protection. Passwords are encrypted at rest and never
decrypted into a template context; only emails may be decrypted for
display on the edit form. Blank password fields on edit preserve the
existing encrypted password.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>