server side filters

This commit is contained in:
Corban-Lee Jones 2025-02-05 17:13:02 +00:00
parent dede2ee06e
commit ae53457344
7 changed files with 214 additions and 79 deletions

View File

@ -917,6 +917,11 @@ video {
margin-right: 1rem;
}
.mx-\[1px\] {
margin-left: 1px;
margin-right: 1px;
}
.mx-auto {
margin-left: auto;
margin-right: auto;
@ -1075,6 +1080,11 @@ video {
display: none;
}
.size-16 {
width: 4rem;
height: 4rem;
}
.size-2\.5 {
width: 0.625rem;
height: 0.625rem;
@ -1254,14 +1264,14 @@ video {
min-width: 15rem;
}
.min-w-\[40px\] {
min-width: 40px;
}
.min-w-full {
min-width: 100%;
}
.max-w-\[120rem\] {
max-width: 120rem;
}
.max-w-\[28rem\] {
max-width: 28rem;
}
@ -1286,6 +1296,10 @@ video {
flex: none;
}
.flex-shrink-0 {
flex-shrink: 0;
}
.shrink-0 {
flex-shrink: 0;
}
@ -1396,6 +1410,10 @@ video {
gap: 1rem;
}
.gap-5 {
gap: 1.25rem;
}
.gap-x-1 {
-moz-column-gap: 0.25rem;
column-gap: 0.25rem;
@ -1525,6 +1543,10 @@ video {
border-radius: 0.25rem;
}
.rounded-2xl {
border-radius: 1rem;
}
.rounded-full {
border-radius: 9999px;
}
@ -1587,6 +1609,10 @@ video {
border-style: solid;
}
.border-none {
border-style: none;
}
.\!border-gray-200 {
--tw-border-opacity: 1 !important;
border-color: rgb(229 231 235 / var(--tw-border-opacity, 1)) !important;
@ -1774,6 +1800,10 @@ video {
padding-bottom: 1rem;
}
.\!pb-1 {
padding-bottom: 0.25rem !important;
}
.pb-1 {
padding-bottom: 0.25rem;
}
@ -1834,6 +1864,10 @@ video {
padding-top: 1rem;
}
.pt-5 {
padding-top: 1.25rem;
}
.text-center {
text-align: center;
}
@ -2449,6 +2483,16 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
inset-inline-start: 0px;
}
.before\:top-0::before {
content: var(--tw-content);
top: 0px;
}
.before\:-z-10::before {
content: var(--tw-content);
z-index: -10;
}
.before\:z-\[1\]::before {
content: var(--tw-content);
z-index: 1;
@ -2470,6 +2514,11 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
height: 1rem;
}
.before\:h-\[200px\]::before {
content: var(--tw-content);
height: 200px;
}
.before\:w-full::before {
content: var(--tw-content);
width: 100%;
@ -2964,6 +3013,11 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
display: inline-flex;
}
.sm\:size-20 {
width: 5rem;
height: 5rem;
}
.sm\:grid-cols-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
@ -2972,6 +3026,10 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
flex-direction: row;
}
.sm\:items-center {
align-items: center;
}
.sm\:gap-6 {
gap: 1.5rem;
}
@ -3001,6 +3059,10 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));
}
.sm\:rounded-3xl {
border-radius: 1.5rem;
}
.sm\:p-5 {
padding: 1.25rem;
}
@ -3100,6 +3162,11 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
padding-top: 0px;
padding-bottom: 0px;
}
.md\:text-3xl {
font-size: 1.875rem;
line-height: 2.25rem;
}
}
@media (min-width: 1024px) {
@ -3499,13 +3566,3 @@ hs-accordion-toggle w-full text-start flex items-center gap-x-3.5 py-2 px-2.5 te
.\[\&\:\:-webkit-scrollbar\]\:w-2::-webkit-scrollbar {
width: 0.5rem;
}
.\[\&\>\.active\]\:bg-gray-100>.active {
--tw-bg-opacity: 1;
background-color: rgb(243 244 246 / var(--tw-bg-opacity, 1));
}
.dark\:\[\&\>\.active\]\:bg-neutral-700>.active:where(.dark, .dark *) {
--tw-bg-opacity: 1;
background-color: rgb(64 64 64 / var(--tw-bg-opacity, 1));
}

View File

@ -1,8 +1,9 @@
// const { dataTable } = HSDataTable.getInstance("#table");
// // Filter by status
// (function () {
// const radioButtons = document.querySelectorAll('input[type="radio"][name="filter"]');
// const { dataTable } = new HSDataTable('#table');
// dataTable.search.fixed('status', function (searchStr, data, index) {
// const status = data[2].trim().toLowerCase(); // Adjust index based on your dataset
@ -17,7 +18,6 @@
// });
// })();
// const { dataTable } = HSDataTable.getInstance("#table");
// dataTable.on("draw.dt", () => {
// console.log("Table redrawn")
@ -34,17 +34,24 @@ const formatTimestamp = timestamp => {
: `${d.getDate()} ${d.toLocaleString("en-GB", { month: "short" })} ${d.getFullYear()}`;
}
var table;
const defineTable = () => {
new HSDataTable("#table", {
table = new HSDataTable("#table", {
ajax: {
url: `/guild/${guild}/subscriptions/api/datatable`,
dataSrc: "data"
dataSrc: "data",
data: (d) => {
d.filters = {};
const active = $("input[name='filterActive']:checked").val();
d.filters.active = active;
}
},
serverSide: true,
processing: true,
selecting: true,
pagingOptions: {
pageBtnClasses: "min-w-[40px] flex justify-center items-center text-gray-800 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 py-2.5 text-sm rounded-full disabled:opacity-50 disabled:pointer-events-none dark:text-white dark:focus:bg-neutral-700 dark:hover:bg-neutral-700"
pageBtnClasses: "hidden"
},
rowSelectingOptions: {
selectAllSelector: "#selectAllBox"
@ -63,7 +70,7 @@ const defineTable = () => {
</p>
<div class="mt-5 flex flex-col sm:flex-row gap-2">
<button type="button" class="openSubModal-js py-2 px-3 inline-flex justify-center items-center gap-x-2 text-sm font-medium rounded-lg border border-transparent bg-blue-600 text-white hover:bg-blue-700 focus:outline-none focus:bg-blue-700 disabled:opacity-50 disabled:pointer-events-none">
<button type="button" class="openSubModal-js py-2 px-3 inline-flex justify-center items-center gap-x-2 text-sm font-medium rounded-lg border border-transparent bg-blue-600 text-white hover:bg-blue-700 focus:outline-none focus:bg-blue-700 disabled:opacity-50 disabled:pointer-events-none" data-hs-overlay="#subModal">
<svg class="shrink-0 size-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="M12 5v14"/></svg>
Create a subscription
</button>
@ -135,9 +142,9 @@ const defineTable = () => {
orderable: false,
searchable: false,
render: (data, type, row) => {
// ${data.length} channels
return `
<span class="block px-6 py-4 text-nowrap">
${data.length} channels
</span>
`;
}
@ -226,7 +233,6 @@ $(window).ready(() => {
const submitForm = async event => {
event.preventDefault();
const formData = new FormData($("#subForm"));

View File

@ -1,4 +1,4 @@
<nav class="bg-white dark:bg-neutral-900 border dark:border-none m-4 sm:m-6 !mb-0 rounded-md">
<!-- <nav class="bg-white dark:bg-neutral-900 border dark:border-none m-4 sm:m-6 !mb-0 rounded-md">
<div class="w-full mx-auto md:flex md:flex-row md:justify-between md:items-center sm:gap-x-3 py-3 sm:py-5 px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center gap-x-3">
<div class="grow flex items-center gap-x-4">
@ -34,4 +34,58 @@
</div>
</div>
</div>
</nav>
</nav> -->
<!-- bg-neutral-100 dark:bg-neutral-900 -->
<div class="max-w-[120rem]">
<div class="relative pt-5 before:w-full before:h-[200px] before:-z-10 before:top-0 before:start-0 before:absolute">
<div class="flex sm:items-center gap-5 p-4 sm:p-6 !pb-1">
<div class="flex-shrink-0">
<div class="relative">
<% if (guild.icon) { %>
<img src="<%= guild.iconURL() %>" class="rounded-2xl sm:rounded-3xl size-16 sm:size-20" alt="">
<% } else { %>
<div class="size-16 sm:size-20 rounded-2xl sm:rounded-3xl flex shrink-0 justify-center items-center bg-white dark:bg-neutral-800">
<span class="text-2xl"><%= guild.nameAcronym %></span>
</div>
<% } %>
</div>
</div>
<div class="grow">
<h1 class="text-2xl md:text-3xl font-semibold text-gray-800 dark:text-neutral-200">
<%= guild.name %>
</h1>
<ul class="flex flex-wrap items-center gap-3 mt-2">
<li class="relative">
<span class="text-sm">ID:</span>
<span class="text-sm inline-flex items-center gap-2 text-gray-800 dark:text-neutral-200"><%= guild.id %></span>
</li>
<li class="relative">
<span class="text-sm">Members:</span>
<span class="text-sm inline-flex items-center gap-2 text-gray-800 dark:text-neutral-200"><%= guild.memberCount %></span>
</li>
<li class="relative">
<span class="text-sm">Channels:</span>
<span class="text-sm inline-flex items-center gap-2 text-gray-800 dark:text-neutral-200"><%= guild.channels.channelCountWithoutThreads %></span>
</li>
</ul>
</div>
<div></div>
</div>
</div>
<div class="flex flex-col justify-center items-center mx-auto p-4 sm:p-6">
<div class="flex flex-row w-full pb-1 whitespace-nowrap overflow-x-auto overflow-y-hidden">
<!-- <a href="/guild/<%= guild.id %>" class="inline-flex items-center gap-2 whitespace-nowrap rounded-md px-3 py-2 text-sm text-gray-800 dark:text-neutral-200 dark:hover:text-neutral-400 dark:focus:text-neutral-400 <%= !isNaN(+guildPage) ? 'bg-white dark:bg-neutral-800 dark:!border-neutral-700 border shadow-sm' : 'mx-[1px]' %>">Overview</a> -->
<a href="/guild/<%= guild.id %>/subscriptions" class="inline-flex items-center gap-2 whitespace-nowrap rounded-md px-3 py-2 text-sm text-gray-800 dark:text-neutral-200 dark:hover:text-neutral-400 dark:focus:text-neutral-400 <%= guildPage === 'subscriptions' ? 'bg-white dark:bg-neutral-800 dark:!border-neutral-700 border shadow-sm' : 'mx-[1px]' %>">Subscriptions</a>
<a href="/guild/<%= guild.id %>/filters" class="inline-flex items-center gap-2 whitespace-nowrap rounded-md px-3 py-2 text-sm text-gray-800 dark:text-neutral-200 dark:hover:text-neutral-400 dark:focus:text-neutral-400 <%= guildPage === 'filters' ? 'bg-white dark:bg-neutral-800 dark:!border-neutral-700 border shadow-sm' : 'mx-[1px]' %>">Filters</a>
<a href="/guild/<%= guild.id %>/styles" class="inline-flex items-center gap-2 whitespace-nowrap rounded-md px-3 py-2 text-sm text-gray-800 dark:text-neutral-200 dark:hover:text-neutral-400 dark:focus:text-neutral-400 <%= guildPage === 'styles' ? 'bg-white dark:bg-neutral-800 dark:!border-neutral-700 border shadow-sm' : 'mx-[1px]' %>">Styles</a>
<a href="/guild/<%= guild.id %>/content" class="inline-flex items-center gap-2 whitespace-nowrap rounded-md px-3 py-2 text-sm text-gray-800 dark:text-neutral-200 dark:hover:text-neutral-400 dark:focus:text-neutral-400 <%= guildPage === 'content' ? 'bg-white dark:bg-neutral-800 dark:!border-neutral-700 border shadow-sm' : 'mx-[1px]' %>">Content</a>
<a href="#" class="inline-flex items-center gap-2 whitespace-nowrap rounded-md px-3 py-2 text-sm text-gray-800 dark:text-neutral-200 dark:hover:text-neutral-400 dark:focus:text-neutral-400">Settings</a>
</div>
</div>
</div>

View File

@ -2,23 +2,15 @@
<%- include("guildHeader") -%>
<div class="flex flex-col justify-center items-center mx-auto px-4 sm:px-6">
<div class="flex flex-row w-full pb-1 whitespace-nowrap overflow-x-auto overflow-y-hidden">
<a href="#" class="inline-flex items-center gap-2 whitespace-nowrap rounded-md px-3 py-2 text-sm text-gray-800 dark:text-neutral-200 dark:hover:text-neutral-400 dark:focus:text-neutral-400 bg-white dark:bg-neutral-800 dark:!border-neutral-700 border shadow-sm">Subscriptions</a>
<a href="#" class="inline-flex items-center gap-2 whitespace-nowrap rounded-md px-3 py-2 text-sm text-gray-800 dark:text-neutral-200 dark:hover:text-neutral-400 dark:focus:text-neutral-400">Filters</a>
<a href="#" class="inline-flex items-center gap-2 whitespace-nowrap rounded-md px-3 py-2 text-sm text-gray-800 dark:text-neutral-200 dark:hover:text-neutral-400 dark:focus:text-neutral-400">Style</a>
</div>
</div>
<!-- Table Section -->
<div id="table" class="max-w-full overflow-hidden p-4 sm:p-6"> <!-- px-4 py-10 sm:px-6 lg:px-8 lg:py-14 -->
<div id="table" class="--prevent-on-load-init max-w-full overflow-hidden px-4 sm:px-6"> <!-- px-4 py-10 sm:px-6 lg:px-8 lg:py-14 -->
<!-- Card -->
<div class="flex flex-col">
<div class="-m-1.5">
<div class="max-w-full p-1.5 min-w-full inline-block align-middle">
<div class="bg-white border border-gray-200 rounded-md shadow-sm overflow-hidden dark:bg-neutral-900 dark:border-neutral-700">
<!-- Header -->
<div class="px-6 py-4 gap-3 flex flex-nowrap justify-between items-center border-gray-200 dark:border-neutral-700">
<div class="px-6 py-4 gap-3 flex flex-nowrap justify-between items-center border-b border-gray-200 dark:border-neutral-700">
<!-- Input -->
<div class="hidden sm:block sm:col-span-1">
<label for="search" class="sr-only">Search</label>
@ -53,22 +45,22 @@
<div class="hs-dropdown-menu transition-[opacity,margin] duration hs-dropdown-open:opacity-100 opacity-0 hidden divide-y divide-gray-200 min-w-48 z-10 bg-white shadow-md rounded-md mt-2 dark:divide-neutral-700 dark:bg-neutral-800 dark:border dark:border-neutral-700" role="menu" aria-orientation="vertical" aria-labelledby="hs-as-table-table-filter-dropdown">
<div class="divide-y divide-gray-200 dark:divide-neutral-700">
<label for="hs-as-filters-dropdown-all" class="flex py-2.5 px-3">
<input type="radio" name="filter" class="shrink-0 mt-0.5 border-gray-200 rounded-full text-blue-600 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-800 dark:border-neutral-700 dark:checked:bg-blue-500 dark:checked:border-blue-500 dark:focus:ring-offset-gray-800" id="hs-as-filters-dropdown-all" checked>
<input type="radio" name="filterActive" class="shrink-0 mt-0.5 border-gray-200 rounded-full text-blue-600 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-800 dark:border-neutral-700 dark:checked:bg-blue-500 dark:checked:border-blue-500 dark:focus:ring-offset-gray-800" id="hs-as-filters-dropdown-all" checked value="1">
<span class="ms-3 text-sm text-gray-800 dark:text-neutral-200">All</span>
</label>
<label for="hs-as-filters-dropdown-published" class="flex py-2.5 px-3">
<input type="radio" name="filter" class="shrink-0 mt-0.5 border-gray-200 rounded-full text-blue-600 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-800 dark:border-neutral-700 dark:checked:bg-blue-500 dark:checked:border-blue-500 dark:focus:ring-offset-gray-800" id="hs-as-filters-dropdown-published">
<span class="ms-3 text-sm text-gray-800 dark:text-neutral-200">Enabled</span>
<input type="radio" name="filterActive" class="shrink-0 mt-0.5 border-gray-200 rounded-full text-blue-600 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-800 dark:border-neutral-700 dark:checked:bg-blue-500 dark:checked:border-blue-500 dark:focus:ring-offset-gray-800" id="hs-as-filters-dropdown-published" value="1">
<span class="ms-3 text-sm text-gray-800 dark:text-neutral-200">Active</span>
</label>
<label for="hs-as-filters-dropdown-pending" class="flex py-2.5 px-3">
<input type="radio" name="filter" class="shrink-0 mt-0.5 border-gray-200 rounded-full text-blue-600 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-800 dark:border-neutral-700 dark:checked:bg-blue-500 dark:checked:border-blue-500 dark:focus:ring-offset-gray-800" id="hs-as-filters-dropdown-pending">
<span class="ms-3 text-sm text-gray-800 dark:text-neutral-200">Disabled</span>
<input type="radio" name="filterActive" class="shrink-0 mt-0.5 border-gray-200 rounded-full text-blue-600 focus:ring-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-800 dark:border-neutral-700 dark:checked:bg-blue-500 dark:checked:border-blue-500 dark:focus:ring-offset-gray-800" id="hs-as-filters-dropdown-pending" value="0">
<span class="ms-3 text-sm text-gray-800 dark:text-neutral-200">Inactive</span>
</label>
</div>
</div>
</div>
<button type="button" class="py-2 px-3 inline-flex items-center gap-x-2 text-sm font-medium rounded-md border border-transparent bg-blue-600 text-white hover:bg-blue-700 focus:outline-none focus:bg-blue-700 disabled:opacity-50 disabled:pointer-events-none" data-hs-overlay="#hs-scale-animation-modal">
<button type="button" class="py-2 px-3 inline-flex items-center gap-x-2 text-sm font-medium rounded-md border border-transparent bg-blue-600 text-white hover:bg-blue-700 focus:outline-none focus:bg-blue-700 disabled:opacity-50 disabled:pointer-events-none" data-hs-overlay="#subModal">
<svg class="shrink-0 size-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="M12 5v14"/></svg>
<span>
Add
@ -84,7 +76,7 @@
<div class="min-w-full overflow-x-auto">
<!-- Table -->
<table class="min-w-full divide-y divide-gray-200 dark:divide-neutral-700">
<thead class="bg-gray-50 dark:bg-neutral-800">
<thead class="bg-gray-50 dark:bg-neutral-800 border-none">
<tr>
<th scope="col" class="ps-6 py-3 text-start --exclude-from-ordering">
<label for="hs-at-with-checkboxes-main" class="flex ml-[1px]">
@ -240,7 +232,7 @@
<!-- End Table Section -->
<!-- Popup -->
<div id="hs-scale-animation-modal" class="hs-overlay hidden size-full fixed top-0 start-0 z-[80] overflow-x-hidden overflow-y-auto pointer-events-none" role="dialog" tabindex="-1" aria-labelledby="hs-scale-animation-modal-label">
<div id="subModal" class="hs-overlay hidden size-full fixed top-0 start-0 z-[80] overflow-x-hidden overflow-y-auto pointer-events-none" role="dialog" tabindex="-1" aria-labelledby="hs-scale-animation-modal-label">
<div class="hs-overlay-animation-target hs-overlay-open:scale-100 hs-overlay-open:opacity-100 scale-95 opacity-0 ease-in-out transition-all duration-200 lg:max-w-4xl lg:w-full m-3 lg:mx-auto min-h-[calc(100%-3.5rem)] flex items-center">
<div class="w-full p-4 sm:p-7 flex flex-col bg-white border shadow-sm rounded-md pointer-events-auto dark:bg-neutral-800 dark:border-neutral-700 dark:shadow-neutral-700/70">
<div class="mb-8">
@ -371,7 +363,7 @@
<button type="button" class="me-auto py-2 px-3 inline-flex items-center gap-x-2 text-sm font-medium rounded-md border border-gray-200 bg-white text-gray-800 shadow-sm hover:bg-gray-50 focus:outline-none focus:bg-gray-50 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-800 dark:border-neutral-700 dark:text-white dark:hover:bg-neutral-700 dark:focus:bg-neutral-700">
Templates
</button>
<button type="button" class="py-2 px-3 inline-flex items-center gap-x-2 text-sm font-medium rounded-md border border-gray-200 bg-white text-gray-800 shadow-sm hover:bg-gray-50 focus:outline-none focus:bg-gray-50 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-800 dark:border-neutral-700 dark:text-white dark:hover:bg-neutral-700 dark:focus:bg-neutral-700" data-hs-overlay="#hs-scale-animation-modal">
<button type="button" class="py-2 px-3 inline-flex items-center gap-x-2 text-sm font-medium rounded-md border border-gray-200 bg-white text-gray-800 shadow-sm hover:bg-gray-50 focus:outline-none focus:bg-gray-50 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-800 dark:border-neutral-700 dark:text-white dark:hover:bg-neutral-700 dark:focus:bg-neutral-700" data-hs-overlay="#subModal">
Close
</button>
<button type="submit" form="subForm" class="py-2 px-3 inline-flex items-center gap-x-2 text-sm font-medium rounded-md border border-transparent bg-blue-600 text-white hover:bg-blue-700 focus:outline-none focus:bg-blue-700 disabled:opacity-50 disabled:pointer-events-none">

View File

@ -3,17 +3,21 @@ import { client as bot } from "@bot/bot";
export const get = async (request: Request, response: Response) => {
const guildId = request.params.guildId;
const guild = bot.guilds.cache.get(guildId);
if (!guild) {
response.status(404).send("404: guild not found");
return;
}
response.redirect(`/guild/${guildId}/subscriptions`);
return
response.render("guild/overview", {
title: `${guild.name} - Relay`,
guild: guild,
});
// const guild = bot.guilds.cache.get(guildId);
// if (!guild) {
// response.status(404).send("404: guild not found");
// return;
// }
// response.render("guild/overview", {
// title: `${guild.name} - Relay`,
// guild: guild,
// });
};
export default { get };

View File

@ -1,39 +1,43 @@
import { NextFunction, Request, Response } from "express";
import { Request, Response } from "express";
import { buildDatatableQuery } from "@utils/datatable";
import { db } from "@db/db";
import { client as bot } from "@bot/bot";
const isPostgres = db.client.config.client === "pg";
const TABLE = "subscriptions";
export const datatable = async (request: Request, response: Response) => {
try {
const { query, recordsTotal, recordsFiltered } = await buildDatatableQuery(
request as any,
"subscriptions"
);
query.select("subscriptions.*")
.leftJoin("channels", "subscriptions.id", "=", "channels.subscription_id")
.select(db.raw(isPostgres
? "json_agg(channels.channel_id) as channels"
:"JSON_GROUP_ARRAY(channels.channel_id) as channels"
))
.groupBy("subscriptions.id")
let query = db(TABLE).where({ guild_id: request.params.guildId });
// query.select("subscriptions.*")
// .leftJoin("channels", "subscriptions.id", "=", "channels.subscription_id")
// .select(db.raw(isPostgres
// ? "json_agg(channels.channel_id) as channels"
// :"JSON_GROUP_ARRAY(channels.channel_id) as channels"
// ))
// .groupBy("subscriptions.id")
const data = await query.where({ guild_id: request.params.guildId });
const datatableQuery = await buildDatatableQuery(request as any, query, TABLE);
const { recordsTotal, recordsFiltered } = datatableQuery;
query = datatableQuery.query;
const data = await query;
data.forEach((item: any) => {
item.channels = item.channels === "[null]"
? []
: JSON.parse(item.channels);
console.log(`total: ${recordsTotal} filtered: ${recordsFiltered} filtered+paged: ${data.length}`);
// data.forEach((item: any) => {
// item.channels = item.channels === "[null]"
// ? []
// : JSON.parse(item.channels);
// item.channels.forEach((channel: any) => {
// channel = {
// "channel_id": channel,
// "name": bot.channels.cache.filter(a => a.id === id).first().name;
// }
// })
});
// // item.channels.forEach((channel: any) => {
// // channel = {
// // "channel_id": channel,
// // "name": bot.channels.cache.filter(a => a.id === id).first().name;
// // }
// // })
// });
response.json({
data,

View File

@ -7,6 +7,7 @@ export interface RequestQuery {
order: { column: string; dir: string }[];
columns: { [key: string]: { data: string; searchable: string } };
search: { value: string };
filters: { [key: string]: any };
}
export interface ResponseQuery {
@ -15,7 +16,11 @@ export interface ResponseQuery {
recordsFiltered: number | string;
}
export const buildDatatableQuery = async (request: { query: RequestQuery }, tableName: string): Promise<ResponseQuery> => {
type FilterConfig = {
[key: string]: (value: any) => Knex.QueryBuilder;
};
export const buildDatatableQuery = async (request: { query: RequestQuery }, query: Knex.QueryBuilder, tableName: string): Promise<ResponseQuery> => {
const size: number = parseInt(request.query.length) || 10;
const start: number = parseInt(request.query.start);
const order: string = (
@ -25,7 +30,18 @@ export const buildDatatableQuery = async (request: { query: RequestQuery }, tabl
const direction: string = (request.query.order && request.query.order[0].dir) || "asc";
const search: string = request.query.search.value;
let query = db(tableName);
const filterConfig: FilterConfig = {
active: (value: number) => query.where('active', '=', value),
};
if (request.query.filters) {
Object.keys(request.query.filters).forEach((filterName: any) => {
const filterValue = request.query.filters[filterName];
if (filterConfig[filterName] && filterValue) {
query = filterConfig[filterName](filterValue);
}
});
}
if (search) {
console.log("applying search: " + search)
@ -39,7 +55,6 @@ export const buildDatatableQuery = async (request: { query: RequestQuery }, tabl
: builder.orWhere(`${tableName}.${col.data}`, "like", `%${search}%`)
);
});
console.log(query.toSQL());
}
const recordsTotalResult = await db(tableName).count("* as count").first();
@ -48,6 +63,9 @@ export const buildDatatableQuery = async (request: { query: RequestQuery }, tabl
const recordsFilteredResult = await query.clone().count("* as count").first();
const recordsFiltered = recordsFilteredResult ? recordsFilteredResult.count : 0;
console.log(query.toSQL())
console.log(query.clone().count("* as count").toSQL())
query = query.orderBy(`${tableName}.${order}`, direction).limit(size).offset(start);
return {