chore: working on datatables implementation for feeds

This commit is contained in:
Corban-Lee Jones 2025-04-24 01:36:39 +01:00
parent 10d55be62d
commit 8450a9a7eb
4 changed files with 190 additions and 18 deletions

View File

@ -37,6 +37,32 @@
display: none !important;
}
.cj-table {
@apply min-w-full divide-y divide-gray-200 dark:divide-neutral-700;
}
.cj-thead {
@apply border-none bg-gray-50 dark:bg-neutral-800;
}
.cj-table-checkbox {
@apply form-checkbox shrink-0 disabled:opacity-50 rounded-sm
text-blue-600 focus:ring-blue-500 border-gray-300
dark:bg-neutral-800 dark:border-neutral-600 dark:checked:bg-blue-500
dark:checked:border-blue-500 dark:focus:ring-offset-gray-800;
}
.cj-table-link {
@apply block px-6 py-4 text-blue-500 hover:text-blue-600 focus:text-blue-600
dark:text-blue-400 dark:hover:text-blue-500 dark:focus:text-blue-500
text-nowrap cursor-pointer
}
.cj-table-text {
@apply text-sm text-gray-500 dark:text-neutral-500 text-nowrap;
}
/* Layout Sidebar */
.sidebar-btn {

View File

@ -1,8 +1,10 @@
import "preline";
import $ from "jquery";
import DataTable from "datatables.net";
import HSDropdown from "@preline/dropdown";
import HSDataTable, { IDataTableOptions } from "@preline/datatable";
import { ConfigColumnDefs, AjaxSettings } from "datatables.net-dt";
import { formatTimestamp } from "../main";
// Fix dependency bugs with preline
(window as any).DataTable = DataTable;
@ -21,21 +23,133 @@ const emptyTableHtml: string = `
</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-hidden focus:bg-blue-700 disabled:opacity-50 disabled:pointer-events-none" data-hs-overlay="#subModal">
<button type="button" class="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-hidden focus:bg-blue-700 disabled:opacity-50 disabled:pointer-events-none" data-hs-overlay="#TODO">
<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 feed
</button>
<button type="button" onclick="alert('not implemented');" class="py-2 px-3 inline-flex items-center gap-x-2 text-sm font-medium rounded-lg border border-gray-200 bg-white text-gray-800 shadow-xs hover:bg-gray-50 focus:outline-hidden 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">
<button type="button" onclick="alert('not implemented');" class="py-2 px-3 inline-flex justify-center items-center gap-x-2 text-sm font-medium rounded-lg border border-gray-200 bg-white text-gray-800 shadow-xs hover:bg-gray-50 focus:outline-hidden 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">
Use a Template
</button>
</div>
</div>
`;
const columnDefs: ConfigColumnDefs[] = [];
const columnDefs: ConfigColumnDefs[] = [
// Select checkbox column
{
target: 0,
orderable: false,
searchable: false,
render(_data: unknown, _type: unknown, row: any) { return `
<td class="size-px whitespace-nowrap">
<div class="ps-6 py-4">
<label class="rowSelect${row.id}-js" class="flex">
<input type="checkbox" id="rowSelect${row.id}-js" class="cj-table-checkbox" data-hs-datatable-row-selecting-individual=""/>
<span class="sr-only">Checkbox</span>
</label>
</div>
</td>
`}
},
{
target: 1,
data: "name",
orderable: true,
searchable: true,
render(data: string) { return `
<td class="size-px whitespace-nowrap align-top">
<span class="cj-table-link">
${data}
</span>
</td>
`}
},
{
target: 2,
data: "url",
orderable: true,
searchable: true,
render(data: string) { return `
<td class="size-px whitespace-nowrap align-top">
<span class="cj-table-link">
${data}
</span>
</td>
`}
},
{
target: 3,
data: "channels",
orderable: false,
searchable: false,
render(data: string) {
return `${data}`
}
},
{
target: 4,
data: "filters",
orderable: false,
searchable: false,
render(data: string) { return `
<td class="size-px whitespace-nowrap align-top">
<div class="px-6 py-4">
<span class="cj-table-text">
${data}
</span>
</div>
</td>
`}
},
{
target: 5,
data: "message_style",
orderable: true,
searchable: true,
render(data: string) { return `
<td class="size-px whitespace-nowrap align-top">
<div class="px-6 py-4">
<span class="cj-table-text">
${data}
</span>
</div>
</td>
`}
},
{
target: 6,
data: "created_at",
orderable: true,
searchable: false,
render(data: string) { return `
<td class="size-px whitespace-nowrap align-top">
<div class="px-6 py-4">
<span class="cj-table-text">
${formatTimestamp(data)}
</span>
</div>
</td>
`}
},
{
target: 7,
data: "active",
orderable: true,
searchable: false,
render(data: string) { return `
<td class="size-px whitespace-nowrap align-top">
<div class="px-6 py-4">
<span class="cj-table-text">
${data}
</span>
</div>
</td>
`}
}
];
const ajaxSettings: AjaxSettings = {
url: ``,
url: `/guild/${1204426362794811453}/feeds/api/datatable`,
dataSrc: "data",
data: (data: unknown) => {
if (data === undefined) return;

View File

@ -1,4 +1,36 @@
import $ from "jquery";
$(document).ready(() => {
console.log("ready!");
});
// Preline: necessary for header events.
window.addEventListener("load", () => {
const inputs = document.querySelectorAll('.dt-container thead input');
inputs.forEach(input => {
(input as HTMLInputElement).addEventListener("keydown", (event: KeyboardEvent) => {
if ((event.metaKey || event.ctrlKey) && event.key === "a") {
(event.target as HTMLInputElement).select();
}
});
});
});
/**
* Formats a given timestamp to one of two formats depending on its age.
* @param timestamp
* @returns 'DD MMM, HH:mm' if younger than 1 year, else 'DD MMM YYYY'
*/
export const formatTimestamp = (timestamp: string | number) => {
const date = new Date(
typeof timestamp === "string"
? timestamp.replace(" ", "T")
: timestamp
);
const now = new Date();
const difference = now.getTime() - date.getTime();
// Day and short month (example: 21 Oct)
const result = `${date.getDate()} ${date.toLocaleString("en-GB", { month: "short" })}`
// Difference is less than a year: 'DD MMM, HH:mm'
// Or, difference is more than a year: 'DD MMM YYYY'
return difference < 31536000000
? result + `, ${date.getHours().toString().padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}`
: result + ` ${date.getFullYear()}`;
}

View File

@ -10,17 +10,17 @@
<!-- Header -->
<div>
placeholder header content
</div>
<!-- Table -->
<div class="min-w-full overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200 dark:divide-neutral-700">
<thead class="border-none bg-gray-50 dark:bg-neutral-800">
<table class="cj-table">
<thead class="cj-thead">
<tr>
<th scope="col" class="ps-6 py-3 text-start --exclude-from-ordering">
<label for="selectAllBox" class="">
<input type="checkbox" id="selectAllBox" class="form-checkbox shrink-0 border-gray-300 rounded-sm text-blue-600 focus:ring-blue-500 disabled:opacity-50 dark:bg-neutral-800 dark:border-neutral-600 dark:checked:bg-blue-500 dark:checked:border-blue-500 dark:focus:ring-offset-gray-800">
<input type="checkbox" id="selectAllBox" class="cj-table-checkbox">
<span class="sr-only">Checkbox</span>
</label>
</th>
@ -35,7 +35,7 @@
</svg>
</div>
</th>
<th scope="col" class="px-6 py-3 text-start">
<th scope="col" data-dt-column="url" class="px-6 py-3 text-start">
<div class="flex justify-between items-center gap-x-2 cursor-pointer">
<span class="text-xs font-semibold uppercase tracking-wide text-gray-800 dark:text-neutral-200">
URL
@ -46,21 +46,21 @@
</svg>
</div>
</th>
<th scope="col" class="px-6 py-3 text-start --exclude-from-ordering">
<th scope="col" data-dt-column="channels" class="px-6 py-3 text-start --exclude-from-ordering">
<div class="flex justify-between items-center gap-x-2">
<span class="text-xs font-semibold uppercase tracking-wide text-gray-800 dark:text-neutral-200">
Channels
</span>
</div>
</th>
<th scope="col" class="px-6 py-3 text-start --exclude-from-ordering">
<th scope="col" data-dt-column="filters" class="px-6 py-3 text-start --exclude-from-ordering">
<div class="flex justify-between items-center gap-x-2">
<span class="text-xs font-semibold uppercase tracking-wide text-gray-800 dark:text-neutral-200">
Filters
</span>
</div>
</th>
<th scope="col" class="px-6 py-3 text-start">
<th scope="col" data-dt-column="style" class="px-6 py-3 text-start">
<div class="flex justify-between items-center gap-x-2 cursor-pointer">
<span class="text-xs font-semibold uppercase tracking-wide text-gray-800 dark:text-neutral-200">
Style
@ -71,7 +71,7 @@
</svg>
</div>
</th>
<th scope="col" class="px-6 py-3 text-start">
<th scope="col" data-dt-column="created_at" class="px-6 py-3 text-start">
<div class="flex justify-between items-center gap-x-2 cursor-pointer">
<span class="text-xs font-semibold uppercase tracking-wide text-gray-800 dark:text-neutral-200 text-nowrap">
Created at
@ -82,7 +82,7 @@
</svg>
</div>
</th>
<th scope="col" class="px-6 py-3 text-start">
<th scope="col" data-dt-column="active" class="px-6 py-3 text-start">
<div class="flex justify-between items-center gap-x-2 cursor-pointer">
<span class="text-xs font-semibold uppercase tracking-wide text-gray-800 dark:text-neutral-200">
Status