diff --git a/apps/home/static/home/css/index.css b/apps/home/static/home/css/index.css index a6ef60e..5122fc4 100644 --- a/apps/home/static/home/css/index.css +++ b/apps/home/static/home/css/index.css @@ -56,17 +56,23 @@ justify-content: flex-start; align-items: center; flex-wrap: nowrap; + color: var(--bs-text-body); background-color: var(--bg-tertiary-bg); transition: 0.3s; } -.server-item-selector:hover { +.server-item-selector .font-monospace { + color: var(--bs-secondary-color) +} + +.server-item-selector:hover, +.server-item-selector:focus, +.server-item-selector:active, +.server-item-selector.active { background-color: var(--bs-tertiary-bg); } -.server-item-selector:active, .server-item-selector.active { - background-color: var(--bs-tertiary-bg) !important; -} + /* widths */ diff --git a/apps/home/static/home/css/tables.css b/apps/home/static/home/css/tables.css index 88a0cf4..6d864cc 100644 --- a/apps/home/static/home/css/tables.css +++ b/apps/home/static/home/css/tables.css @@ -25,6 +25,18 @@ td > .btn-link { padding-left: 0; } } +/* Empty Table */ + +.table .dt-empty { + padding: 2rem 0; + border-bottom: none !important; +} + +.table tr:hover > .dt-empty { + box-shadow: none !important; +} + + /* Table Search */ .table-search-group { @@ -76,3 +88,31 @@ td > .btn-link { padding-left: 0; } outline: 0; box-shadow: none; } + + +/* Button Controls */ + +.table-search-buttons > div { + margin-left: 1rem; +} + +@media (max-width: 576px) { + .table-search-buttons > div { + margin-left: 0.25rem; + } +} + +.table-search-buttons > div:first-of-type { + margin-left: 0 !important; +} + + +/* Table Border Colour */ + +table.dataTable > thead > tr > th, table.dataTable > thead > tr > td { + border-bottom: 1px solid var(--bs-border-color); +} + +div.dt-container.dt-empty-footer tbody > tr:last-child > * { + border-bottom: 1px solid var(--bs-border-color); +} \ No newline at end of file diff --git a/apps/home/static/home/js/tables.js b/apps/home/static/home/js/tables.js index 53d07ba..40f3ef8 100644 --- a/apps/home/static/home/js/tables.js +++ b/apps/home/static/home/js/tables.js @@ -9,6 +9,9 @@ function initializeDataTable(tableId, columns) { searching: false, autoWidth: false, order: [], + language: { + emptyTable: "No results found" + }, select: { style: "multi+shift", selector: 'th:first-child input[type="checkbox"]' @@ -39,6 +42,7 @@ function initializeDataTable(tableId, columns) { bindTablePageSizer(tableId); bindTableSearch(tableId); bindRefreshButton(tableId); + bindDeleteButton(tableId); bindTableSelectColumn(tableId); } @@ -263,12 +267,19 @@ function bindTableSearch(tableId) { // region Button Controls function bindRefreshButton(tableId) { - let $tableFilters = $(tableId).closest('.js-tableBody').siblings('.js-tableFilters'); + const $tableFilters = $(tableId).closest('.js-tableBody').siblings('.js-tableFilters'); $tableFilters.on("click", ".table-refresh-btn", function() { $(tableId).trigger("doDataLoad"); }) } +function bindDeleteButton(tableId) { + const $tableFilters = $(tableId).closest(".js-tableBody").siblings(".js-tableFilters"); + $tableFilters.on("click", ".table-del-btn", function() { + alert("delete"); + }) +} + // region Select Checkboxes @@ -395,9 +406,53 @@ async function loadModalData($modal, url) { }); } +async function onModalSubmit($modal, $table, url) { + if (!selectedServer) { + return; + } + + let data = { server: selectedServer.id }; + + $modal.find("[data-field]").each(function() { + const type = $(this).attr("type"); + const key = $(this).attr("data-field"); + if (!key) { + return; + } + + let value; + if (type === "checkbox") { + value = $(this).prop("checked"); + } + else { + value = $(this).val(); + } + + data[key] = value; + }); + + const formData = objectToFormData(data); + const id = $modal.data("primary-key"); + const isNewItem = parseInt(id) !== -1; + const method = isNewItem ? "PATCH" : "POST"; + url = isNewItem ? url + `${id}/` : url; + + ajaxRequest(url, method, formData) + .then(response => { + $modal.modal("hide"); + $table.trigger("doDataLoad"); + }) + .catch(error => logError(error)); +} + // region Table Column Types +function renderEditColumn(data) { + const name = sanitise(data); + return ``; +} + function renderBooleanColumn(data) { const iconClass = data ? "bi-check-circle-fill text-success" : "bi-x-circle-fill text-danger"; return ``; diff --git a/apps/home/static/home/js/tabs/filters.js b/apps/home/static/home/js/tabs/filters.js index 8c66c52..553f3e0 100644 --- a/apps/home/static/home/js/tabs/filters.js +++ b/apps/home/static/home/js/tabs/filters.js @@ -10,7 +10,8 @@ function initFiltersModule() { [ { title: "Name", - data: "name" + data: "name", + render: renderEditColumn }, { title: "Match", @@ -79,3 +80,32 @@ $(filterTableId).on("click", ".edit-modal", async function() { const id = $(filterTableId).DataTable().row($(this).closest("tr")).data().id; await openDataModal(filterModalId, id, `/api/filters/${id}/`); }); + +$(filterModalId).on("submit", async function(event) { + event.preventDefault(); + await onModalSubmit( + $(filterModalId), + $(filterTableId), + "/api/filters/" + ); +}); + + +// region Load Modal Options + +$(document).ready(async function() { + await loadMatchingAlgorithms(); +}); + +async function loadMatchingAlgorithms() { + const data = await ajaxRequest("/api/filters/", "OPTIONS"); + data.actions.GET.matching_algorithm.choices.forEach(algorithm => { + $(filterModalId).find('[data-field="matching_algorithm"]').append($( + "