abstract table logic to table.js

This commit is contained in:
Corban-Lee Jones 2024-07-15 21:29:52 +01:00
parent 8d527092cc
commit 2c0174ccba
6 changed files with 212 additions and 183 deletions

View File

@ -1,6 +1,8 @@
var contentTable;
function initContentTable() {
async function initContentTable() {
await initTable("#contentTabPane", "contentTable", loadContent);
contentTable = $("#contentTable").DataTable({
info: false,
paging: false,
@ -62,6 +64,10 @@ function initContentTable() {
{
title: "Channel",
data: "channel_id",
className: "text-start content-channel-id",
render: function(data) {
return $("<span>").text(data)[0];
}
},
{
title: "Created",
@ -72,11 +78,13 @@ function initContentTable() {
let dateTime = new Date(data);
let dateTimeString = formatDate(dateTime);
return $(`
<span data-bs-trigger="hover focus"
data-bs-toggle="popover"
data-bs-content="${dateTimeString}">
${dateTime.toISOString().split("T")[0]}
</span>
<span data-bs-trigger="hover focus"
data-bs-html="true"
data-bs-custom-class="text-center"
data-bs-toggle="popover"
data-bs-content="${dateTimeString}">
${dateTime.toISOString().split("T")[0]}
</span>
`).popover()[0];
}
},
@ -89,6 +97,49 @@ function initContentTable() {
}
]
});
bindTableCheckboxes("#contentTable", contentTable, "#deleteSelectedContentBtn");
}
function handleDiscordChannelNames() {
let interval = setInterval(function() {
if (!discordChannels.length)
return;
$("#contentTable tr td.content-channel-id").each(function() {
console.debug("row")
let tracked = contentTable.row($(this).closest("tr")).data();
channel = discordChannels.find(item => item.value === tracked.channel_id);
if (!channel)
return;
console.debug("inserting")
$(this).text("").append(
$("<a>")
.attr("href", `https://discord.com/channels/${getCurrentlyActiveServer().guild_id}/${channel.value}/${tracked.message_id}`)
.attr("target", "_blank")
.addClass("text-decoration-none")
.text(channel.text)
);
});
// $(".replace-discord-channel-id-with-name").each(function() {
// let displayChannel = discordChannels.find(channel => channel.value == $(this).text());
// if (displayChannel) {
// $(this).text("").append(
// $("<a>")
// .attr("href", `https://discord.com/channels/${getCurrentlyActiveServer().guild_id}/${displayChannel.value}`)
// .attr("target", "_blank")
// .addClass("text-decoration-none")
// .text(displayChannel.text)
// );
// }
// });
}, 600);
}
async function goToSubscription(subId) {
@ -133,8 +184,8 @@ async function loadContent(guildId, page=1, pageSize=null) {
const content = await getTrackedContent(guildId, null, page, pageSize);
contentTable.rows.add(content.results).draw(false);
updateTablePagination("#contentPagination", page, pageSize, content.count, content.next, content.previous);
updateTablePaginationInfo("#contentTablePageInfo", content.results.length, content.count);
updateTablePagination("#contentTabPane .table-pagination", page, pageSize, content.count, content.next, content.previous);
updateTablePaginationInfo("#contentTabPane .table-page-info", content.results.length, content.count);
$("#contentTable thead .table-select-all").prop("disabled", content.results.length === 0);
}

View File

@ -1,7 +1,9 @@
var filtersTable;
// Create filters table
function initFiltersTable() {
async function initFiltersTable() {
await initTable("#filtersTabPane", "filtersTable", loadFilters);
filtersTable = $("#filtersTable").DataTable({
info: false,
paging: false,
@ -74,6 +76,8 @@ function initFiltersTable() {
}
]
});
bindTableCheckboxes("#filtersTable", filtersTable, "#deleteSelectedFiltersBtn");
}
$("#addFilterBtn").on("click", async function() {
@ -181,8 +185,8 @@ async function loadFilters(guildId, page=1, pageSize=null) {
const filters = await getFilters(guildId);
filtersTable.rows.add(filters.results).draw(false);
updateTablePagination("#filtersPagination", page, pageSize, filters.count, filters.next, filters.previous);
updateTablePaginationInfo("#filtersTablePageInfo", filters.results.length, filters.count);
updateTablePagination("#filtersTabPane .table-pagination", page, pageSize, filters.count, filters.next, filters.previous);
updateTablePaginationInfo("#filtersTabPane .table-page-info", filters.results.length, filters.count);
$("#filtersTable thead .table-select-all").prop("disabled", filters.results.length === 0);
}

View File

@ -1,30 +1,14 @@
$(document).ready(async function() {
initSubscriptionTable();
initFiltersTable();
initContentTable();
await initSubscriptionTable();
await initFiltersTable();
await initContentTable();
$("#subscriptionsTab").click();
// Bind table select checkboxes
bindTableCheckboxes("#subTable", subTable, "#deleteSelectedSubscriptionsBtn");
bindTableCheckboxes("#filtersTable", filtersTable, "#deleteSelectedFiltersBtn");
bindTableCheckboxes("#contentTable", contentTable, "#deleteSelectedContentBtn")
// Bind pagination - subTable
await bindTablePagination("#subPagination", loadSubscriptions);
await bindTablePaginationResizer("#subTablePageSize", loadSubscriptions);
// Bind pagination filtersTable
await bindTablePagination("#filtersPagination", loadFilters);
await bindTablePaginationResizer("#filtersTablePageSize", loadFilters);
// Bind pagination - contentTable
await bindTablePagination("#contentPagination", loadContent);
await bindTablePaginationResizer("#contentTablePageSize", loadContent);
await loadSavedGuilds();
await loadServerOptions();
handleDiscordChannelNames();
});
$('#serverTabs [data-bs-toggle="tab"]').on("show.bs.tab", function(event) {
@ -37,51 +21,6 @@ $(document).on("selectedServerChange", function() {
$("#subscriptionsTab").click();
});
function bindTableCheckboxes(tableSelector, tableObject, deleteButtonSelector) {
// Select a row via checkbox
$(tableSelector).on("change", "tbody tr .table-select-row", function() {
var selected = $(this).prop("checked");
rowIndex = $(this).closest("tr").index();
row = tableObject.row(rowIndex);
if (selected) row.select();
else row.deselect();
determineSelectAllState(tableSelector, tableObject, deleteButtonSelector);
});
// Select all rows checkbox
$(tableSelector).on("change", "thead .table-select-all", function() {
var selected = $(this).prop("checked");
$(`${tableSelector} tbody tr`).each(function(rowIndex) {
var row = tableObject.row(rowIndex);
if (selected) row.select();
else row.deselect();
$(this).find('.table-select-row').prop("checked", selected);
});
determineSelectAllState(tableSelector, tableObject, deleteButtonSelector);
});
}
function determineSelectAllState(tableSelector, tableObject, deleteButtonSelector) {
var selectedRowsCount = tableObject.rows(".selected").data().toArray().length;
allRowsCount = tableObject.rows().data().toArray().length;
selectAllCheckbox = $(`${tableSelector} thead .table-select-all`);
checked = selectedRowsCount === allRowsCount;
indeterminate = !checked && selectedRowsCount > 0;
selectAllCheckbox.prop("checked", checked);
selectAllCheckbox.prop("indeterminate", indeterminate);
$(deleteButtonSelector).prop("disabled", !(checked || indeterminate) || !(allRowsCount > 0));
}
function formatDate(date) {
// Array of weekday names
@ -115,7 +54,7 @@ function formatDate(date) {
}
// `${hours}:${minutes}:${seconds} · `
return `${dayOfWeek}, ${dayOfMonthSuffix} ${month}, ${year}`;
return `${dayOfWeek}, ${dayOfMonthSuffix} ${month}, ${year}<br>${hours}:${minutes}:${seconds}`;
}
function genHexString(len) {

View File

@ -1,7 +1,10 @@
var subTable = null;
discordChannels = [];
// Create subscription table
function initSubscriptionTable() {
async function initSubscriptionTable() {
await initTable("#subscriptionsTabPane", "subTable", loadSubscriptions);
subTable = $("#subTable").DataTable({
info: false,
paging: false,
@ -70,6 +73,8 @@ function initSubscriptionTable() {
let dateTimeString = formatDate(dateTime);
return $(`
<span data-bs-trigger="hover focus"
data-bs-html="true"
data-bs-custom-class="text-center"
data-bs-toggle="popover"
data-bs-content="${dateTimeString}">
${dateTime.toISOString().split("T")[0]}
@ -113,9 +118,7 @@ function initSubscriptionTable() {
]
});
// subTable.on('page.dt length.dt', async function() {
// await loadSubscriptions(getCurrentlyActiveServer().guild_id);
// });
bindTableCheckboxes("#subTable", subTable, "#deleteSelectedSubscriptionsBtn");
}
$("#subTable").on("change", ".sub-toggle-active", async function () {
@ -348,8 +351,8 @@ async function loadSubscriptions(guildId, page=1, pageSize=null) {
const subscriptions = await getSubscriptions(guildId, page, pageSize);
subTable.rows.add(subscriptions.results).draw(false);
updateTablePagination("#subPagination", page, pageSize, subscriptions.count, subscriptions.next, subscriptions.previous);
updateTablePaginationInfo("#subTablePageInfo", subscriptions.results.length, subscriptions.count);
updateTablePagination("#subscriptionsTabPane .table-pagination", page, pageSize, subscriptions.count, subscriptions.next, subscriptions.previous);
updateTablePaginationInfo("#subscriptionsTabPane .table-page-info", subscriptions.results.length, subscriptions.count);
$("#subTable thead .table-select-all").prop("disabled", subscriptions.results.length === 0);
console.debug("loading subs, " + subscriptions.results.length + " found");
@ -368,6 +371,8 @@ $(document).on("selectedServerChange", async function() {
// Hide alerts
$("#serverJoinAlert").attr("style", "display: none !important");
updateBotInviteLink();
const activeServer = getCurrentlyActiveServer();
await loadSubscriptions(activeServer.guild_id);
await loadChannelOptions(activeServer.guild_id);
@ -493,16 +498,16 @@ async function loadChannelOptions(guildId) {
);
}
discordChannels = [];
channels.forEach(channel => {
// We only want TextChannels, which have a type of 0
if (channel.type !== 0)
return;
$("#subChannels").append($("<option>", {
text: "#" + channel.name,
value: channel.id
}));
let channelObj = {text: `#${channel.name}`, value: channel.id}
$("#subChannels").append($("<option>", channelObj));
discordChannels.push(channelObj);
});
}
catch(error) {
@ -603,7 +608,16 @@ function showServerJoinAlert() {
}
// #endregion
function updateBotInviteLink() {
const guildId = getCurrentlyActiveServer().guild_id;
const inviteUrl = `https://discord.com/oauth2/authorize
?client_id=1129345991758336020
&permissions=2147534848&scope=bot+applications.commands
&guild_id=${guildId}
&disable_guild_select=true`
$("#invitePyrssToServerBtn").attr("href", inviteUrl);
}
// #region Colour Controls
$("#subEmbedColour").on("change", function() {

View File

@ -1,4 +1,65 @@
async function initTable(containingSelector, tableId, loadDataFunc) {
let pageSizeId = tableId + "PageSize";
createTable(containingSelector, tableId);
createTableControls(containingSelector, pageSizeId);
await bindTablePagination(`${containingSelector} .table-pagination`, loadDataFunc);
await bindTablePaginationResizer(`${containingSelector} .table-page-sizer`, loadDataFunc);
}
function createTable(containingSelector, tableId) {
$(containingSelector).append(`
<div class="table-responsive my-3 px-3">
<table id="${tableId}" class="table table-hover align-middle"></table>
</div>
`);
}
function createTableControls(containingSelector, pageSizeId) {
$(containingSelector).append(`
<div class="table-controls row mb-3 px-3">
<div class="col-lg-2">
<div class="table-page-info d-flex justify-content-start align-items-center mx-auto">
<span class="pageinfo-total"></span>&nbsp;Results
</div>
</div>
<div class="col-lg-8">
<nav class="table-pagination d-flex justify-content-center">
<ul class="pagination mb-0">
<li class="page-item">
<button type="button" class="page-link page-prev rounded-start-1">
<i class="bi bi-chevron-left"></i>
</button>
</li>
<li class="page-item">
<button type="button" class="page-link page-next rounded-end-1">
<i class="bi bi-chevron-right"></i>
</button>
</li>
</ul>
</nav>
</div>
<div class="col-lg-2">
<div class="d-flex justify-content-end">
<label for="${pageSizeId}" class="form-label align-self-center mb-0 me-2">Per Page</label>
<select name="${pageSizeId}" id="${pageSizeId}" class="select-2 table-page-sizer">
<option value="10" selected>10&emsp;</option>
<option value="15">15&emsp;</option>
<option value="20">20&emsp;</option>
<option value="25">25&emsp;</option>
</select>
</div>
</div>
</div>
`);
$("#" + pageSizeId).select2({
theme: "bootstrap",
minimumResultsForSearch: 10,
});
}
// Updates the pagination text for a given pageInfoId
function updateTablePaginationInfo(pageInfoId, showing, total) {
$(`${pageInfoId} .pageinfo-showing`).text(showing);
@ -18,7 +79,7 @@ function updateTablePagination(pageControlsId, currentPage, pageSize, totalItems
// Calculate amount of pages to account for
const pages = Math.max(Math.ceil(totalItems / pageSize), 1);
const maxVisiblePages = 5;
const maxVisiblePages = 10;
let startPage, endPage;
@ -109,4 +170,49 @@ async function bindTablePaginationResizer(resizerControlId, dataLoadFunc) {
await dataLoadFunc(getCurrentlyActiveServer().guild_id, page, pageSize);
});
}
}
function bindTableCheckboxes(tableSelector, tableObject, deleteButtonSelector) {
// Select a row via checkbox
$(tableSelector).on("change", "tbody tr .table-select-row", function() {
var selected = $(this).prop("checked");
rowIndex = $(this).closest("tr").index();
row = tableObject.row(rowIndex);
if (selected) row.select();
else row.deselect();
determineSelectAllState(tableSelector, tableObject, deleteButtonSelector);
});
// Select all rows checkbox
$(tableSelector).on("change", "thead .table-select-all", function() {
var selected = $(this).prop("checked");
$(`${tableSelector} tbody tr`).each(function(rowIndex) {
var row = tableObject.row(rowIndex);
if (selected) row.select();
else row.deselect();
$(this).find('.table-select-row').prop("checked", selected);
});
determineSelectAllState(tableSelector, tableObject, deleteButtonSelector);
});
}
function determineSelectAllState(tableSelector, tableObject, deleteButtonSelector) {
var selectedRowsCount = tableObject.rows(".selected").data().toArray().length;
allRowsCount = tableObject.rows().data().toArray().length;
selectAllCheckbox = $(`${tableSelector} thead .table-select-all`);
checked = selectedRowsCount === allRowsCount;
indeterminate = !checked && selectedRowsCount > 0;
selectAllCheckbox.prop("checked", checked);
selectAllCheckbox.prop("indeterminate", indeterminate);
$(deleteButtonSelector).prop("disabled", !(checked || indeterminate) || !(allRowsCount > 0));
}

View File

@ -34,8 +34,11 @@
<h5 class="mb-0 selected-server-id text-truncate text-body-secondary"></h5>
</div>
<div class="ms-auto">
<button type="button" id="deleteSelectedServerBtn" class="btn btn-outline-danger rounded-1">
<i class="bi bi-trash3"></i>
<a href="" id="invitePyrssToServerBtn" class="btn btn-outline-info rounded-1" target="_blank" data-bs-toggle="tooltip" data-bs-title="Invite @PYRSS Bot">
<i class="bi bi-envelope"></i>
</a>
<button type="button" id="deleteSelectedServerBtn" class="btn btn-outline-danger rounded-1 ms-3">
<i class="bi bi-x-lg"></i>
</button>
</div>
</div>
@ -100,99 +103,11 @@
</div>
</div>
</div>
<div class="col-12">
<div id="serverTabContent" class="tab-content">
<div id="subscriptionsTabPane" class="tab-pane fade" role="tabpanel" aria-labelledby="subscriptionsTab" tabindex="0">
<div class="table-responsive my-3 px-3">
<table id="subTable" class="table table-hover align-middle"></table>
</div>
<div class="table-controls d-flex mb-3 px-3">
<nav id="subPagination">
<ul class="pagination mb-0">
<li class="page-item">
<button type="button" class="page-link page-prev rounded-start-1">Previous</button>
</li>
<li class="page-item">
<button type="button" class="page-link page-next rounded-end-1">Next</button>
</li>
</ul>
</nav>
<div id="subTablePageInfo" class="d-flex align-items-center mx-auto">
showing&nbsp;<span class="pageinfo-showing"></span>
&nbsp;of&nbsp;<span class="pageinfo-total"></span>
</div>
<div class="d-flex">
<label for="subTablePageSize" class="form-label align-self-center mb-0 me-2">Per Page</label>
<select name="subTablePageSize" id="subTablePageSize" class="select-2">
<option value="10" selected>10&emsp;</option>
<option value="15">15&emsp;</option>
<option value="20">20&emsp;</option>
<option value="25">25&emsp;</option>
</select>
</div>
</div>
</div>
<div id="filtersTabPane" class="tab-pane fade" role="tabpanel" aria-labelledby="filtersTab" tabindex="0">
<div class="table-responsive my-3 px-3">
<table id="filtersTable" class="table table-hover align-middle"></table>
</div>
<div class="table-controls d-flex mb-3 px-3">
<nav id="filtersPagination">
<ul class="pagination mb-0">
<li class="page-item">
<button type="button" class="page-link page-prev rounded-start-1">Previous</button>
</li>
<li class="page-item">
<button type="button" class="page-link page-next rounded-end-1">Next</button>
</li>
</ul>
</nav>
<div id="filtersTablePageInfo" class="d-flex align-items-center mx-auto">
showing&nbsp;<span class="pageinfo-showing"></span>
&nbsp;of&nbsp;<span class="pageinfo-total"></span>
</div>
<div class="d-flex">
<label for="filtersTablePageSize" class="form-label align-self-center mb-0 me-2">Per Page</label>
<select name="filtersTablePageSize" id="filtersTablePageSize" class="select-2">
<option value="10" selected>10&emsp;</option>
<option value="15">15&emsp;</option>
<option value="20">20&emsp;</option>
<option value="25">25&emsp;</option>
</select>
</div>
</div>
</div>
<div id="contentTabPane" class="tab-pane fade" role="tabpanel" aria-labelledby="contentTab" tabindex="0">
<div class="table-responsive my-3 px-3">
<table id="contentTable" class="table table-hover align-middle"></table>
</div>
<div class="table-controls d-flex mb-3 px-3">
<nav id="contentPagination">
<ul class="pagination mb-0">
<li class="page-item">
<button type="button" class="page-link page-prev rounded-start-1">Previous</button>
</li>
<li class="page-item">
<button type="button" class="page-link page-next rounded-end-1">Next</button>
</li>
</ul>
</nav>
<div id="contentTablePageInfo" class="d-flex align-items-center mx-auto">
showing&nbsp;<span class="pageinfo-showing"></span>
&nbsp;of&nbsp;<span class="pageinfo-total"></span>
</div>
<div class="d-flex">
<label for="contentTablePageSize" class="form-label align-self-center mb-0 me-2">Per Page</label>
<select name="contentTablePageSize" id="contentTablePageSize" class="select-2">
<option value="10" selected>10&emsp;</option>
<option value="15">15&emsp;</option>
<option value="20">20&emsp;</option>
<option value="25">25&emsp;</option>
</select>
</div>
</div>
</div>
<div id="subscriptionsTabPane" class="tab-pane fade" role="tabpanel" aria-labelledby="subscriptionsTab" tabindex="0"></div>
<div id="filtersTabPane" class="tab-pane fade includes-table includes-table-controls includes-table-search" role="tabpanel" aria-labelledby="filtersTab" tabindex="0"> </div>
<div id="contentTabPane" class="tab-pane fade" role="tabpanel" aria-labelledby="contentTab" tabindex="0"></div>
</div>
</div>
</div>