On the map I'd like to be able to right click and have an option to create a location (storage or delivery) at the point selected.
The location editor now opens with the clicked coordinates prefilled.
Clicking an existing zone now opens the popup with an Edit button that launches the location editor.
When deleting a location, do we consider cascading it to remove history records where a tracker entered the zone?
Campaign Management
The Open locations button at the top of the page is unnecessary.
Removed from the admin app's Campaign Management page
(tracker-admin-svelte/.../clients/+page.svelte) -- it duplicated the
"Open locations" icon already on each campaign/child-run card.
Unified CRUD
On the campaign management page the interface does not work well on
mobile.
I think the mobile display should stack as rows not columns, so each
selection fills beneath the next.
On selecting a client the mobile interface should jump to brand, on
selecting a brand jump to campaign. Fixed all three together: the
Clients/Brands/Campaigns/Child runs columns were a fixed-height
side-by-side row, which just doesn't fit four columns on a phone.
Built a shared EntityColumnGroup component (both apps' campaign
management pages now use it, replacing four hand-written
EntityColumns each) that keeps the side-by-side layout at the lg
breakpoint and up, and below it stacks the columns as full-width rows,
each with its own capped height and internal scroll. Selecting an item
smooth-scrolls the next column into view (mobile only -- on desktop
everything's already visible side by side).
Tracker Cards
The tooltips on mobile flow off screen to the left for cards that are in the left column. Can the tooltips be aligned to the right for left hand cards?
Fixed by flipping the status tooltip outward on compact card grids, while leaving the wider layouts unchanged.
Trying to scroll in mobile causes the drag/drop trigger and the wheel appears. Is there anything to be done to make scrolling better?
Fixed by allowing vertical touch panning on move-enabled cards/rows and only starting the radial drag wheel from mouse input.
Admin Panel
Users
The user invite gets sent out with the link to the admin panel.
The invite should have the frontend link, unless they are an admin,
then it should have both. Fixed: the invite link was derived from
whichever app's origin submitted the create-user request (always
the admin panel, since that's the only place POST /users is
called from), not the invited user's own role. Now role-based —
ADMIN_URL/FRONTEND_URL are both real settings, and the email
includes the admin panel link only when "admin" is in the new
user's roles.
The user cards should behave like tracker cards. The edit/delete only
appears on hover. Fixed in the shared EntityCard component (also used
by clients and campaign management), so all three pick up the same
hover-reveal behaviour as TrackerCard.
User cards could take up less space. Two columns, unless mobile?
Added an opt-in columns prop to EntityColumn (default stays 1, so
clients/campaign management are unaffected) and set it to 2 on the
Users page; falls back to a single column below the sm breakpoint.
Cards could show the clients they have access to instead of just "1 client"
Now lists the actual client names (comma-separated, capped at 3 with
"+N more"), resolved from the clients already loaded for the editor
modal.
Add a filter to show users with specific client access. Added a
"Client access" dropdown next to the Role filter. Along the way,
found that User.client_list/User.roles used the generic
sqlalchemy.ARRAY type instead of the Postgres-specific one, so
.contains() raised NotImplementedError at query time -- this was
already broken for the pre-existing Role filter too, just never
exercised by a test. Fixed the column type (no migration needed --
the actual DB column was always a real Postgres array; this was a
Python-level type annotation bug). Admins match every client filter
since they implicitly have access to all clients.
Check that no password reset email is sent unless the user is using local auth.
Authentication
better-auth
Did we over engineer our own auth? Would be better using https://better-auth.com/docs/concepts/oauth
Discussed and rejected. The current JWT + OIDC provider-registry setup
(app/core/oidc.py) is a fairly standard size for what it does, not
over-engineered. better-auth is TypeScript-only (expects a Node/Bun/Deno
backend with Drizzle/Prisma/Kysely) -- this app's auth lives in the
Python/FastAPI backend, so adopting it would mean standing up a
separate Node auth microservice or rewriting auth in TypeScript, not a
drop-in swap.
Would it give us scope for other auths facebook/github?
Not a reason to switch -- GitHub/Facebook fit the existing
_PROVIDERS-dict pattern already used for Google/Microsoft (with
per-provider quirks, e.g. GitHub isn't strictly OIDC-compliant so it
needs a token-exchange/userinfo step instead of JWKS validation). A
scoped addition to the current architecture, not a reason to migrate.
Backend API
Alembic
Can we merge the alembic migrations so we don't have branches?
Linearized both branch points (alembic heads was already a single
head -- the "branches" were two already-resolved fork/merge pairs
inside the history, not a live problem). Checked every environment's
current stamped revision first (local dev and staging both at head;
production intentionally 11 migrations behind, on 46408209ddfd,
well before either fork) to confirm nothing was stamped at a revision
being removed. Reordered the two branch pairs by creation date,
repointed down_revisions to chain them sequentially instead of
merging, and deleted the now-unneeded pure no-op merge revision
(ab12cd34ef56; the other merge point, e3f1a2b4c5d6, had real
schema changes so it was kept, just with a single down_revision
instead of a tuple). Verified by building a completely fresh database
through the whole chain from scratch -- all 36 revisions apply
cleanly in the new linear order, ending at the same head
(d0e1f2a3b4c5) as before.
Once we get staging, and production to the current head we should flatten the migrations using alembic revision --autogenerate -m "Schema Baseline"
Schema Improvement Notes
Several geography-backed tables currently expose both legacy and modern GIST index names on the same column. If backward compatibility is no longer required, consider keeping one canonical index per table.
If OAuth sign-in becomes the dominant login path, adding an index on users.auth_provider or users.provider_subject_id may help lookup-heavy auth flows.
Short-lived tables such as password_reset_tokens may benefit from an explicit retention or cleanup policy being documented alongside the schema.
Documentation
We need to look at replacing MkDocs if we want to keep Material.
See docs/development/docs-platform-evaluation.md
for the current analysis of MkDocs 2.0, Zensical, plugin compatibility,
and Cloudflare hosting impact.
Usage Guide
The user guide needs to be updated to cover the new design and move to the unified crud in Campaign Management
Added a "Managing Clients, Brands, and Campaigns" section covering the
drill-down interaction, hover-to-reveal edit/delete, mobile behavior,
child runs, and per-run tracker management. Cross-linked from
"Setup — Required Order".
Remove Stale Documents
We need to do a review of documentation and remove cruft relating to the previous React version and any other outdated materials.
Rewrote the Admin Panel (8 files) and Front End (7 files) sections
wholesale -- both described the deleted React/react-admin apps as
current, with dead paths and code samples. Fixed scattered stale
mentions in docs/index.md, reference/system-architecture.md, and
several guides/services docs. Left legitimate historical/migration
docs (the Svelte migration assessments, the cutover TODO) untouched --
they're supposed to talk about the old React app for comparison.
The documentation requires a restructure as the top level menu contains too many items and it looks disorganised.
Root cause: mkdocs-awesome-pages-plugin defaults to reading
.pages files, but every nav file in this repo is named
.nav.yml and the plugin was never told to look for that instead --
so none of the curated navigation was ever actually being read, and
mkdocs was silently falling back to a flat alphabetical dump of all
~90 pages. Fixed the plugin config (one line), fixed the "*"
wildcard token (should be "..." for this plugin version), fixed a
broken nested-path reference and a dead redoc link, and moved/removed
the handful of stray top-level directories that had no real content
(an empty dir, a confusingly-duplicate database/, asset-only dirs).
Verified by building the full site and confirming the rendered nav
matches the curated structure.