fix(phase26): add Pi trip deletion (BUG-08)
This commit is contained in:
@@ -146,6 +146,13 @@ def trip_route(trip_id):
|
|||||||
return jsonify(error=f"Trip not found: {trip_id}"), 404
|
return jsonify(error=f"Trip not found: {trip_id}"), 404
|
||||||
return jsonify(trip)
|
return jsonify(trip)
|
||||||
|
|
||||||
|
@app.route('/api/trip/<int:trip_id>', methods=['DELETE'])
|
||||||
|
def delete_trip(trip_id):
|
||||||
|
deleted = app_sql.delete_trip(trip_id)
|
||||||
|
if not deleted:
|
||||||
|
return jsonify(error=f"Trip not found: {trip_id}"), 404
|
||||||
|
return jsonify(status="deleted"), 200
|
||||||
|
|
||||||
import functools
|
import functools
|
||||||
import urllib.request
|
import urllib.request
|
||||||
|
|
||||||
|
|||||||
@@ -540,6 +540,17 @@ def get_trip_route(trip_id, max_points=2000):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def delete_trip(trip_id):
|
||||||
|
"""Delete a computed trip record. Raw tracking rows are NOT removed."""
|
||||||
|
conn = get_conn()
|
||||||
|
if conn is None:
|
||||||
|
return False
|
||||||
|
with _conn_lock:
|
||||||
|
cur = conn.execute("DELETE FROM trips WHERE id = ?", (trip_id,))
|
||||||
|
conn.commit()
|
||||||
|
return cur.rowcount > 0
|
||||||
|
|
||||||
|
|
||||||
def get_setting(key, default=None):
|
def get_setting(key, default=None):
|
||||||
"""Read a persisted UI/app setting (e.g. the dashcam-upload toggle)."""
|
"""Read a persisted UI/app setting (e.g. the dashcam-upload toggle)."""
|
||||||
conn = get_conn()
|
conn = get_conn()
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
"Trips appear once the Pi has uploaded tracking data.",
|
"Trips appear once the Pi has uploaded tracking data.",
|
||||||
true
|
true
|
||||||
) }}
|
) }}
|
||||||
|
<script>document.getElementById('trip-list').dataset.deleteUrl = '/api/v1/portal/trip/';</script>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -182,6 +182,7 @@
|
|||||||
|
|
||||||
const tripList = document.getElementById("trip-list");
|
const tripList = document.getElementById("trip-list");
|
||||||
const deletable = tripList && tripList.dataset.deletable === "1";
|
const deletable = tripList && tripList.dataset.deletable === "1";
|
||||||
|
const deleteUrl = (tripList && tripList.dataset.deleteUrl) || "/api/trip/";
|
||||||
|
|
||||||
function renderTrips(trips) {
|
function renderTrips(trips) {
|
||||||
body.replaceChildren();
|
body.replaceChildren();
|
||||||
@@ -225,7 +226,7 @@
|
|||||||
window.confirmDelete(`Delete trip from ${date}?\n\nThe raw GPS data is kept — only this computed trip record is removed.`)
|
window.confirmDelete(`Delete trip from ${date}?\n\nThe raw GPS data is kept — only this computed trip record is removed.`)
|
||||||
.then(function(confirmed) {
|
.then(function(confirmed) {
|
||||||
if (!confirmed) return;
|
if (!confirmed) return;
|
||||||
return fetch(`/api/v1/portal/trip/${tripId}`, { method: "DELETE" })
|
return fetch(deleteUrl + tripId, { method: "DELETE" })
|
||||||
.then(function(res) {
|
.then(function(res) {
|
||||||
if (!res.ok) throw new Error("Delete failed");
|
if (!res.ok) throw new Error("Delete failed");
|
||||||
deleteBtn.closest("tr").remove();
|
deleteBtn.closest("tr").remove();
|
||||||
|
|||||||
@@ -131,6 +131,13 @@
|
|||||||
<script src="{{ url_for('static', filename='js/theme.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/theme.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/data-list.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/data-list.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='js/alerts.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/alerts.js') }}"></script>
|
||||||
|
|
||||||
|
{# ── Shared confirmation helper ────────────────────────────────────────── #}
|
||||||
|
<script>
|
||||||
|
window.confirmDelete = function(message) {
|
||||||
|
return Promise.resolve(window.confirm(message));
|
||||||
|
};
|
||||||
|
</script>
|
||||||
{% block scripts %}{% endblock %}
|
{% block scripts %}{% endblock %}
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{% macro data_table(id, columns, empty_title, empty_message) %}
|
{% macro data_table(id, columns, empty_title, empty_message, deletable=false) %}
|
||||||
<div class="data-list" id="{{ id }}" data-data-list>
|
<div class="data-list" id="{{ id }}" data-data-list{% if deletable %} data-deletable="1"{% endif %}>
|
||||||
<div class="table-responsive data-list-table" data-data-list-table hidden>
|
<div class="table-responsive data-list-table" data-data-list-table hidden>
|
||||||
<table class="table table-hover align-middle mb-0">
|
<table class="table table-hover align-middle mb-0">
|
||||||
<thead>
|
<thead>
|
||||||
|
|||||||
@@ -35,8 +35,10 @@
|
|||||||
{"key": "action", "label": "", "sortable": false, "class": "d-none d-sm-table-cell"}
|
{"key": "action", "label": "", "sortable": false, "class": "d-none d-sm-table-cell"}
|
||||||
],
|
],
|
||||||
"No trips detected",
|
"No trips detected",
|
||||||
"Trip history appears after sustained GPS or OBD movement is recorded."
|
"Trip history appears after sustained GPS or OBD movement is recorded.",
|
||||||
|
deletable=true
|
||||||
) }}
|
) }}
|
||||||
|
<script>document.getElementById('trip-list').dataset.deleteUrl = '/api/trip/';</script>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user