diff --git a/static/js/data-list.js b/static/js/data-list.js new file mode 100644 index 0000000..1d0745e --- /dev/null +++ b/static/js/data-list.js @@ -0,0 +1,69 @@ +(function () { + "use strict"; + + function compareRows(left, right, key, direction) { + const leftValue = left.dataset[key] || ""; + const rightValue = right.dataset[key] || ""; + const numericLeft = Number(leftValue); + const numericRight = Number(rightValue); + const bothNumeric = leftValue !== "" && rightValue !== "" + && Number.isFinite(numericLeft) && Number.isFinite(numericRight); + const comparison = bothNumeric + ? numericLeft - numericRight + : leftValue.localeCompare(rightValue, undefined, { + numeric: true, + sensitivity: "base", + }); + return comparison * direction; + } + + function refreshList(list) { + const body = list.querySelector("[data-data-list-body]"); + const table = list.querySelector("[data-data-list-table]"); + const empty = list.querySelector("[data-data-list-empty]"); + const hasRows = body && body.children.length > 0; + table.hidden = !hasRows; + empty.hidden = hasRows; + } + + function initializeList(list) { + refreshList(list); + + list.addEventListener("click", function (event) { + const sortButton = event.target.closest("[data-sort-key]"); + if (sortButton) { + const body = list.querySelector("[data-data-list-body]"); + const key = sortButton.dataset.sortKey; + const ascending = sortButton.dataset.sortDirection !== "asc"; + const rows = Array.from(body.children); + rows.sort(function (left, right) { + return compareRows(left, right, key, ascending ? 1 : -1); + }); + rows.forEach(function (row) { + body.appendChild(row); + }); + list.querySelectorAll("[data-sort-key]").forEach(function (button) { + button.removeAttribute("data-sort-direction"); + button.closest("th").setAttribute("aria-sort", "none"); + }); + sortButton.dataset.sortDirection = ascending ? "asc" : "desc"; + sortButton.closest("th").setAttribute( + "aria-sort", + ascending ? "ascending" : "descending" + ); + } + + const confirmButton = event.target.closest("[data-confirm]"); + if (confirmButton && !window.confirm(confirmButton.dataset.confirm)) { + event.preventDefault(); + event.stopImmediatePropagation(); + } + }, true); + + new MutationObserver(function () { + refreshList(list); + }).observe(list.querySelector("[data-data-list-body]"), { childList: true }); + } + + document.querySelectorAll("[data-data-list]").forEach(initializeList); +}()); diff --git a/static/yaris.css b/static/yaris.css index 90b9b33..3be8e7c 100644 --- a/static/yaris.css +++ b/static/yaris.css @@ -1 +1,40 @@ -#map { height: 20rem; } \ No newline at end of file +#map { height: 20rem; } + +.app-sidebar { + color: var(--bs-accordion-btn-focus-border-color); + background: #000; +} + +.data-list { + min-height: 10rem; +} + +.data-list-empty { + padding: 2.5rem 1rem; +} + +.data-list-sort { + display: inline-flex; + gap: .5rem; + align-items: center; + padding: 0; + color: inherit; + font: inherit; + font-weight: 700; + background: none; + border: 0; +} + +.data-list-sort[data-sort-direction="asc"] .fa-sort::before { + content: "\f0de"; +} + +.data-list-sort[data-sort-direction="desc"] .fa-sort::before { + content: "\f0dd"; +} + +.data-list-actions { + display: flex; + gap: .5rem; + justify-content: flex-end; +} \ No newline at end of file diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..4363f50 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,79 @@ + + + + + + + {% block title %}Yaris OBD{% endblock %} + + + + {% block styles %}{% endblock %} + + + +
+ +
+
+ +
+ {% block content %}{% endblock %} +
+
+
+
+ +
+
+
+ + + +
+ + + + + {% block scripts %}{% endblock %} + + + diff --git a/templates/components.html b/templates/components.html new file mode 100644 index 0000000..474a437 --- /dev/null +++ b/templates/components.html @@ -0,0 +1,31 @@ +{% macro data_table(id, columns, empty_title, empty_message) %} +
+ +
+ +
{{ empty_title }}
+

{{ empty_message }}

+
+
+{% endmacro %} diff --git a/templates/dash.html b/templates/dash.html index 9402fd4..62a00b6 100644 --- a/templates/dash.html +++ b/templates/dash.html @@ -1,62 +1,8 @@ - - +{% extends "base.html" %} - - - - Dashboard - yaris vroom vroom de course - - - - - +{% block title %}Dashcam - Yaris OBD{% endblock %} - -
- -
-
- -
+{% block content %}

Dashcam

@@ -97,19 +43,8 @@

-
-
-
-
- -
-
-
-
- - - - - +{% endblock %} - \ No newline at end of file +{% block scripts %} + +{% endblock %} \ No newline at end of file diff --git a/templates/dtc.html b/templates/dtc.html index 909caa0..fb2d6d3 100644 --- a/templates/dtc.html +++ b/templates/dtc.html @@ -1,82 +1,25 @@ - - +{% extends "base.html" %} +{% from "components.html" import data_table %} - - - - Dashboard - yaris vroom vroom de course - - - - - +{% block title %}DTC Codes - Yaris OBD{% endblock %} - -
- -
-
- -
+{% block content %}

DTC Codes

Output

-
+
+ {{ data_table( + "dtc-list", + [ + {"key": "code", "label": "Code", "sortable": true}, + {"key": "description", "label": "Description", "sortable": true}, + {"key": "actions", "label": "Actions", "sortable": false} + ], + "No trouble codes available", + "Stored diagnostic trouble codes will appear here once OBD data is connected." + ) }} +
-
-
-
-
- -
-
-
-
- - - - - - - \ No newline at end of file +{% endblock %} \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 1b042dd..3dd7277 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,63 +1,12 @@ - - +{% extends "base.html" %} - - - - Dashboard - yaris vroom vroom de course - - - +{% block title %}Dashboard - Yaris OBD{% endblock %} + +{% block styles %} - +{% endblock %} - -
- -
-
- -
+{% block content %} @@ -170,21 +119,11 @@
-
- - - - +{% endblock %} + +{% block scripts %} - - - - - \ No newline at end of file +{% endblock %} \ No newline at end of file