From 7acae19e84c5476b6a5357c0cf219afb7d3fc426 Mon Sep 17 00:00:00 2001 From: Upstream Data Date: Tue, 26 Nov 2024 09:32:59 -0700 Subject: [PATCH] Use new datatable column handling description for software --- goosebit/ui/bff/common/columns.py | 8 + goosebit/ui/bff/software/routes.py | 23 ++ goosebit/ui/static/js/software.js | 242 +++++++++++----------- goosebit/ui/templates/software.html.jinja | 11 - 4 files changed, 153 insertions(+), 131 deletions(-) diff --git a/goosebit/ui/bff/common/columns.py b/goosebit/ui/bff/common/columns.py index 46235170..64f30651 100644 --- a/goosebit/ui/bff/common/columns.py +++ b/goosebit/ui/bff/common/columns.py @@ -34,3 +34,11 @@ class RolloutColumns: paused = DTColumnDescription(title="Paused", name="paused", data="paused") success_count = DTColumnDescription(title="Success Count", data="success_count", name="success_count") failure_count = DTColumnDescription(title="Failure Count", data="failure_count", name="failure_count") + + +class SoftwareColumns: + id = DTColumnDescription(title="ID", data="id", visible=False) + name = DTColumnDescription(title="Name", data="name", name="name") + version = DTColumnDescription(title="Version", data="version", name="version", searchable=True, orderable=True) + compatibility = DTColumnDescription(title="Compatibility", name="compatibility", data="compatibility") + size = DTColumnDescription(title="Size", name="size", data="size") diff --git a/goosebit/ui/bff/software/routes.py b/goosebit/ui/bff/software/routes.py index 1edb6d7f..e38227f9 100644 --- a/goosebit/ui/bff/software/routes.py +++ b/goosebit/ui/bff/software/routes.py @@ -15,6 +15,8 @@ from goosebit.ui.bff.common.util import parse_datatables_query from goosebit.updates import create_software_update +from ..common.columns import SoftwareColumns +from ..common.responses import DTColumns from .responses import BFFSoftwareResponse router = APIRouter(prefix="/software") @@ -94,3 +96,24 @@ async def post_update( await create_software_update(absolute.as_uri(), temp_file) finally: await temp_file.unlink(missing_ok=True) + + +@router.get( + "/columns", + dependencies=[Security(validate_user_permissions, scopes=["software.read"])], + response_model_exclude_none=True, +) +async def devices_get_columns() -> DTColumns: + columns = list( + filter( + None, + [ + SoftwareColumns.id, + SoftwareColumns.name, + SoftwareColumns.version, + SoftwareColumns.compatibility, + SoftwareColumns.size, + ], + ) + ) + return DTColumns(columns=columns) diff --git a/goosebit/ui/static/js/software.js b/goosebit/ui/static/js/software.js index 84dad5f6..09f29bd8 100644 --- a/goosebit/ui/static/js/software.js +++ b/goosebit/ui/static/js/software.js @@ -6,6 +6,128 @@ const uploadProgressBar = document.getElementById("upload-progress"); let dataTable; +const renderFunctions = { + compatibility: (data, type) => { + const result = data.reduce((acc, { model, revision }) => { + if (!acc[model]) { + acc[model] = []; + } + acc[model].push(revision); + return acc; + }, {}); + + return Object.entries(result) + .map(([model, revision]) => `${model} - ${revision.join(", ")}`) + .join("\n"); + }, + size: (data, type) => { + if (type === "display" || type === "filter") { + return `${(data / 1024 / 1024).toFixed(2)}MB`; + } + return data; + }, +}; + +document.addEventListener("DOMContentLoaded", async () => { + const columnConfig = await get_request("/ui/bff/software/columns"); + for (const col in columnConfig.columns) { + const colDesc = columnConfig.columns[col]; + const colName = colDesc.data; + if (renderFunctions[colName]) { + columnConfig.columns[col].render = renderFunctions[colName]; + } + } + + const buttons = [ + { + text: '', + action: (e, dt) => { + const selectedSoftware = dt + .rows({ selected: true }) + .data() + .toArray() + .map((d) => d.id); + downloadSoftware(selectedSoftware[0]); + }, + className: "buttons-download", + titleAttr: "Download Software", + }, + { + text: '', + action: async (e, dt) => { + const selectedSoftware = dt + .rows({ selected: true }) + .data() + .toArray() + .map((d) => d.id); + await deleteSoftware(selectedSoftware); + }, + className: "buttons-delete", + titleAttr: "Delete Software", + }, + ]; + + // add create button at the beginning if upload modal exists + if ($("#upload-modal").length > 0) { + buttons.unshift({ + text: '', + action: () => { + new bootstrap.Modal("#upload-modal").show(); + }, + className: "buttons-create", + titleAttr: "Add Software", + }); + } + + dataTable = new DataTable("#software-table", { + responsive: true, + paging: true, + processing: false, + serverSide: true, + scrollCollapse: true, + scroller: true, + scrollY: "60vh", + stateSave: true, + ajax: { + url: "/ui/bff/software", + data: (data) => { + // biome-ignore lint/performance/noDelete: really has to be deleted + delete data.columns; + }, + contentType: "application/json", + }, + initComplete: () => { + updateBtnState(); + }, + columnDefs: [ + { + targets: "_all", + searchable: false, + orderable: false, + render: (data) => data || "-", + }, + ], + columns: columnConfig.columns, + select: true, + rowId: "id", + layout: { + bottom1Start: { + buttons, + }, + }, + }); + + dataTable + .on("select", () => { + updateBtnState(); + }) + .on("deselect", () => { + updateBtnState(); + }); + + updateSoftwareList(); +}); + uploadForm.addEventListener("submit", async (e) => { e.preventDefault(); await sendFileChunks(uploadFileInput.files[0]); @@ -125,126 +247,6 @@ function resetProgress() { updateSoftwareList(); } -document.addEventListener("DOMContentLoaded", () => { - const buttons = [ - { - text: '', - action: (e, dt) => { - const selectedSoftware = dt - .rows({ selected: true }) - .data() - .toArray() - .map((d) => d.id); - downloadSoftware(selectedSoftware[0]); - }, - className: "buttons-download", - titleAttr: "Download Software", - }, - { - text: '', - action: async (e, dt) => { - const selectedSoftware = dt - .rows({ selected: true }) - .data() - .toArray() - .map((d) => d.id); - await deleteSoftware(selectedSoftware); - }, - className: "buttons-delete", - titleAttr: "Delete Software", - }, - ]; - - // add create button at the beginning if upload modal exists - if ($("#upload-modal").length > 0) { - buttons.unshift({ - text: '', - action: () => { - new bootstrap.Modal("#upload-modal").show(); - }, - className: "buttons-create", - titleAttr: "Add Software", - }); - } - - dataTable = new DataTable("#software-table", { - responsive: true, - paging: true, - processing: false, - serverSide: true, - scrollCollapse: true, - scroller: true, - scrollY: "60vh", - stateSave: true, - ajax: { - url: "/ui/bff/software", - data: (data) => { - // biome-ignore lint/performance/noDelete: really has to be deleted - delete data.columns; - }, - contentType: "application/json", - }, - initComplete: () => { - updateBtnState(); - }, - columnDefs: [ - { - targets: "_all", - searchable: false, - orderable: false, - render: (data) => data || "-", - }, - ], - columns: [ - { data: "id", visible: false }, - { data: "name" }, - { data: "version", name: "version", searchable: true, orderable: true }, - { - data: "compatibility", - render: (data) => { - const result = data.reduce((acc, { model, revision }) => { - if (!acc[model]) { - acc[model] = []; - } - acc[model].push(revision); - return acc; - }, {}); - - return Object.entries(result) - .map(([model, revision]) => `${model} - ${revision.join(", ")}`) - .join("\n"); - }, - }, - { - data: "size", - render: (data, type) => { - if (type === "display" || type === "filter") { - return `${(data / 1024 / 1024).toFixed(2)}MB`; - } - return data; - }, - }, - ], - select: true, - rowId: "id", - layout: { - bottom1Start: { - buttons, - }, - }, - }); - - dataTable - .on("select", () => { - updateBtnState(); - }) - .on("deselect", () => { - updateBtnState(); - }); - - updateSoftwareList(); -}); - function updateBtnState() { if (dataTable.rows({ selected: true }).any()) { document.querySelector("button.buttons-delete").classList.remove("disabled"); diff --git a/goosebit/ui/templates/software.html.jinja b/goosebit/ui/templates/software.html.jinja index e664527f..513d8b01 100644 --- a/goosebit/ui/templates/software.html.jinja +++ b/goosebit/ui/templates/software.html.jinja @@ -4,17 +4,6 @@
- - - - - - - - - - -
IDNameVersionCompatibilitySize