Root cause: GPX parser only handled <trk>/<trkpt> (track)
elements but ignored <rte>/<rtept> (route) elements. 37 of 39
Sevenoaks walking route GPX files use <rte> format, so only 2
routes were rendering on the map
Fix: added parseRoute() and parseRoutePoints() methods to
convert <rte>/<rtept> elements into renderable GpxTrack objects
alongside existing <trk> parsing
Waypoint dedup: changed parseWaypoints() from selecting
"wpt, rtept" to just "wpt" — <rtept> elements are now parsed
as route tracks, not waypoints, preventing duplicate rendering
Map Viewport Hang Fix
Root cause: Dragging/zooming the map triggered an infinite feedback
loop — moveend → loadRoutes() → layer reassignment + fitBounds
→ Leaflet re-render → moveend again
Debounced viewport handler: 300ms debounce on moveend/zoomend
prevents rapid-fire reloads during panning
Re-entrancy guard: loadRoutesInProgress flag skips concurrent
loadRoutes() calls
Suppress programmatic events: suppressViewportHandler flag
prevents layer/bounds assignment from re-triggering the viewport
handler (cleared after 200ms)
Skip fitBounds on viewport reloads: loadRoutes(skipFitBounds)
parameter prevents calculateFitBounds on user-initiated pan/zoom,
which was the primary loop trigger
Removed cache eviction on viewport change: the viewport filtering
already uses currentBounds.intersects() on cached data, so deleting
and re-fetching GPX on every drag was unnecessary
Mail API Resilience (No API Key / Invalid Key)
Short-circuit when no API key: MailMessagingService.initialise()
checks mailConfig.apiKey after loading config — if absent, skips all
Brevo API calls (account, lists, folders, templates) and sets safe
empty defaults. Zero 401 errors in the console
Short-circuit on account error: if the account query fails (invalid
key), remaining Brevo calls are skipped with empty defaults
Senders list guard: MailSendersListComponent waits for the
mailMessagingConfig via the events() observable before calling
senders/domain APIs — skips if accountError is set
Create-or-amend sender guard: checks brevoAccountConfigured()
before calling querySenders()
Mail provider stats guard: calculateMailProviderStats() handles
this.list being undefined when Brevo lists fail to load, showing
"No Brevo lists available" instead of crashing
List key values null safety: added ?. and || [] fallback when
brevo.lists.lists is undefined
Alert icon: changed error alert icon from faCircleCheck to
faExclamationTriangle for the account error panel
CommonDataService Double HTTP Request Fix
Root cause: responseFrom() used .subscribe() + .toPromise()
on a shared observable, causing two subscriptions and duplicate HTTP
requests
Fix: replaced with single firstValueFrom() call — one
subscription, one HTTP request, proper error propagation via throw
Config Save: Allow Clearing Sensitive Fields
Root cause: restoreSensitiveFields() in the backend config
controller treated both missing keys AND falsy values ("", null)
as "restore from DB", making it impossible to clear the API key
Fix: changed condition from !(k in incoming) || !incoming[k] to
!(k in incoming) — only restores when the key is truly absent
(redacted), not when explicitly cleared
Mail Settings: Prevent Config Overwrite During Editing
Root cause: MailMessagingService.initialise() created new
subscriptions to committeeConfig and systemConfigService on every
call, causing accumulated subscriptions that re-emitted config and
overwrote unsaved edits
Fix: moved one-time subscriptions to the constructor;
acceptNextConfigEmission flag in MailSettingsComponent prevents
subscription emissions from overwriting local edits except after
save or undo
Domain API URL Refactor
Changed domain endpoints from path parameters
(/domains/:domainName/configuration) to query parameters
(/domains/configuration?domainName=...) across backend routes and
frontend service
Walk Search Layout
Alert inline when default filtering, own row with pagination/filters