Add delete with confirmation for trips and dashcam

- Confirmation modal in server base.html (window.confirmDelete promise helper)
- Trip delete: trash button in Replay column (server-only via data-deletable on
  trip-list div), DELETE /api/v1/portal/trip/<id>, removes computed trip only
  (raw tracking_points kept)
- Dashcam delete: trash button next to download, DELETE /api/v1/portal/dashcam/<id>,
  removes from DB + disk (best-effort file removal)
- history.js: delete button gated on data-deletable='1' attribute; click handler
  intercepts before replay logic
- db.py: delete_trip(), delete_dashcam_file() helpers

Co-authored-by: Copilot <[email protected]>
This commit is contained in:
Mathieu Moran
2026-08-10 04:40:22 -04:00
co-authored by Copilot
parent a13d74e18e
commit 7aa9ffda16
7 changed files with 151 additions and 6 deletions
+29 -1
View File
@@ -179,6 +179,9 @@
initializeTimeline(result.points);
}
const tripList = document.getElementById("trip-list");
const deletable = tripList && tripList.dataset.deletable === "1";
function renderTrips(trips) {
body.replaceChildren();
trips.forEach(function (trip) {
@@ -189,6 +192,11 @@
row.dataset.maxSpeed = trip.max_speed_kmh;
row.dataset.tripId = trip.id;
row.style.cursor = "pointer";
const deleteBtn = deletable
? `<button class="btn btn-sm btn-outline-danger ms-1" type="button" data-delete-trip-id="${trip.id}" title="Delete trip">
<i class="fas fa-trash fa-fw" aria-hidden="true"></i>
</button>`
: "";
row.innerHTML = `
<td>${formatDate(trip.start_datetime)}</td>
<td>${formatDistance(trip.distance_km)}</td>
@@ -199,13 +207,33 @@
<button class="btn btn-sm btn-primary" type="button" data-trip-id="${trip.id}">
Replay
</button>
${deleteBtn}
</td>`;
body.appendChild(row);
});
}
body.addEventListener("click", function (event) {
// Button click (desktop) or row click (mobile)
// Delete button
const deleteBtn = event.target.closest("[data-delete-trip-id]");
if (deleteBtn) {
event.stopPropagation();
const tripId = deleteBtn.dataset.deleteTripId;
const date = deleteBtn.closest("tr").querySelector("td").textContent;
window.confirmDelete(`Delete trip from ${date}? The raw GPS data is kept — only the computed trip record is removed.`)
.then(function(confirmed) {
if (!confirmed) return;
return fetch(`/api/v1/portal/trip/${tripId}`, { method: "DELETE" })
.then(function(res) {
if (!res.ok) throw new Error("Delete failed");
deleteBtn.closest("tr").remove();
})
.catch(function(e) { showError(e.message); });
});
return;
}
// Replay button click (desktop) or row click (mobile)
const button = event.target.closest("[data-trip-id]");
const row = event.target.closest("tr[data-trip-id]");
const tripId = button ? button.dataset.tripId : (row ? row.dataset.tripId : null);