Project location cards now reorderable via drag-and-drop, and the
four inline action buttons (Unassign/Edit/Remove/Delete) collapse into
a single three-dot kebab menu — much cleaner card layout, especially
for projects with many locations.
Data
- MonitoringLocation.sort_order: nullable Integer, default 0.
Migration `migrate_add_location_sort_order.py` adds the column and
seeds existing rows with sort_order = alphabetical index per project
(so the post-migration display order matches what operators see
today — no surprise reordering).
- get_project_locations + locations-json: ORDER BY sort_order, name.
- Location-create: assigns max(sort_order) + 1 so new locations land
at the END of the list rather than being interleaved alphabetically.
Reorder endpoint
- POST /api/projects/{p}/locations/reorder
Body: { location_ids: [uuid, uuid, ...] }
Validates: all ids belong to this project; raises 404 on missing.
Applies 0-indexed sort_order matching the provided order.
UI changes (templates/partials/projects/location_list.html)
- Active cards get a draggable="true" attribute + native HTML5
drag/drop handlers. Drop reorders the DOM immediately, then posts
the new order to the reorder endpoint. Drop-zone visual feedback
(orange ring on hover, opacity on source during drag).
- Six-dot drag handle icon on the left of each active card; whole
card body is the drag source but the handle is the visual cue.
- Right side: small Assign pill (only shown when unassigned) +
three-dot kebab menu containing Unassign/Edit/Remove/Delete.
Click ⋮ to toggle; click outside or Escape to close. Only one
menu open at a time.
- Removed locations are NOT draggable (their order is historical) and
keep their existing Restore button visible.
The card also shows "{N} events" instead of "Sessions: N" when the
location_type is vibration AND the backend passes event_count in
the payload — which lands in commit 2 of this redesign.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The buttons used inline `onclick="...({{ name | tojson }})"`, which
emits the location name as a JSON-quoted string with double quotes —
those double quotes collide with the onclick attribute's own double
quotes, terminating the attribute early. Result: the browser parses
the attribute as broken HTML and the click handler never fires.
Switched both Remove and Restore to the data-attribute pattern the
Edit button already uses (data-loc-id / data-loc-name read via
this.dataset in the onclick). Robust against any character in the
location name.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
When a client drops a location from scope mid-project (e.g. the office
half of a museum+office monitoring job), operators couldn't previously
mark it as no-longer-active without either deleting it (which would
orphan historical events) or leaving it in the active list looking
deployable. Now there's a proper middle ground.
Data model
- MonitoringLocation gets two new nullable columns:
- removed_at — NULL means active; set means soft-removed
- removal_reason — optional operator note
Migration: backend/migrate_add_location_removed.py (idempotent)
Endpoints
- POST /api/projects/{p}/locations/{l}/remove
Body: { effective_date?: ISO-datetime, reason?: str }
Side effects (cascade):
1. Closes active UnitAssignment rows at this location
(assigned_until = effective_date, status = "completed")
2. Cancels pending ScheduledActions at this location
3. Marks location.removed_at = effective_date
Returns counts of assignments closed + actions cancelled.
- POST /api/projects/{p}/locations/{l}/restore
Clears removed_at + removal_reason. Does NOT auto-reopen
assignments — operator creates new ones if resuming monitoring.
Active-surface filters
- locations-json defaults to active-only; pass include_removed=true
for historical / reporting views. Schedule modal dropdowns now
exclude removed locations automatically.
- Metadata-backfill fuzzy matcher excludes removed locations from
proposed targets (don't want backfill creating new assignments at
decommissioned locations).
- Vibration-summary per_location rollup includes removed locations
(so historical event totals stay accurate) but tags each with
removed_at so the UI can show a badge.
UI
- Project detail page's Monitoring Locations section now splits into:
Active locations (full card with Assign / Edit / Remove / Delete)
Removed locations (collapsed <details>, greyed cards, Restore button,
shows removal date + reason)
- New per-card "Remove" button → opens confirmation modal explaining
the cascade, with optional effective-date (defaults to now,
backdateable) and reason fields.
- Unit detail's SFM Events attribution cell shows a small "removed"
badge next to historical attributions whose location is no longer
active. Same pattern in vibration_summary's top-locations list.
- Soft-removal indicator surfaced through the events_for_unit
attribution payload as location_removed_at.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Introduced a new section for displaying soft-deleted projects.
- Implemented loading of deleted projects via an API call.
- Added restore and permanently delete options for each deleted project.
- Integrated loading of deleted projects when the data tab is shown.
- Created a new template for displaying a list of data files in `file_list.html`, including file details and actions for downloading and viewing file details.
- Added a new template for displaying recording sessions in `session_list.html`, featuring session status, details, and action buttons for stopping recordings and viewing session details.
- Introduced a legacy dashboard template `slm_legacy_dashboard.html` for sound level meter control, including a live view panel and configuration modal with dynamic content loading.
- Updated project_dashboard.html to conditionally display NRLs or Locations based on project type, and added a button to open a modal for adding locations.
- Enhanced slm_device_list.html with a configuration button for each unit, allowing users to open a modal for device configuration.
- Modified detail.html to include an edit project modal with a form for updating project details, including client name, status, and dates.
- Improved sound_level_meters.html by restructuring the layout and adding a configuration modal for SLM devices.
- Implemented JavaScript functions for handling modal interactions, including opening, closing, and submitting forms for project and location management.