From 8450a9a7eb413ee27a37992f033e703625c08430 Mon Sep 17 00:00:00 2001 From: Corban-Lee Jones Date: Thu, 24 Apr 2025 01:36:39 +0100 Subject: [PATCH] chore: working on datatables implementation for feeds --- src/client/public/css/main.css | 26 ++++++ src/client/typescript/guild/feeds.ts | 122 ++++++++++++++++++++++++++- src/client/typescript/main.ts | 40 ++++++++- src/client/views/guild/feeds.ejs | 20 ++--- 4 files changed, 190 insertions(+), 18 deletions(-) diff --git a/src/client/public/css/main.css b/src/client/public/css/main.css index 9b1d885..fff18cf 100644 --- a/src/client/public/css/main.css +++ b/src/client/public/css/main.css @@ -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 { diff --git a/src/client/typescript/guild/feeds.ts b/src/client/typescript/guild/feeds.ts index eb841e5..6ec0040 100644 --- a/src/client/typescript/guild/feeds.ts +++ b/src/client/typescript/guild/feeds.ts @@ -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 = `

- -
`; -const columnDefs: ConfigColumnDefs[] = []; +const columnDefs: ConfigColumnDefs[] = [ + // Select checkbox column + { + target: 0, + orderable: false, + searchable: false, + render(_data: unknown, _type: unknown, row: any) { return ` + +
+ +
+ + `} + }, + { + target: 1, + data: "name", + orderable: true, + searchable: true, + render(data: string) { return ` + + + ${data} + + + `} + }, + { + target: 2, + data: "url", + orderable: true, + searchable: true, + render(data: string) { return ` + + + ${data} + + + `} + }, + { + 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 ` + +
+ + ${data} + +
+ + `} + }, + { + target: 5, + data: "message_style", + orderable: true, + searchable: true, + render(data: string) { return ` + +
+ + ${data} + +
+ + `} + }, + { + target: 6, + data: "created_at", + orderable: true, + searchable: false, + render(data: string) { return ` + +
+ + ${formatTimestamp(data)} + +
+ + `} + }, + { + target: 7, + data: "active", + orderable: true, + searchable: false, + render(data: string) { return ` + +
+ + ${data} + +
+ + `} + } +]; const ajaxSettings: AjaxSettings = { - url: ``, + url: `/guild/${1204426362794811453}/feeds/api/datatable`, dataSrc: "data", data: (data: unknown) => { if (data === undefined) return; diff --git a/src/client/typescript/main.ts b/src/client/typescript/main.ts index f1376fd..aed3f84 100644 --- a/src/client/typescript/main.ts +++ b/src/client/typescript/main.ts @@ -1,4 +1,36 @@ -import $ from "jquery"; -$(document).ready(() => { - console.log("ready!"); -}); \ No newline at end of file +// 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()}`; +} \ No newline at end of file diff --git a/src/client/views/guild/feeds.ejs b/src/client/views/guild/feeds.ejs index f7c0e89..6ec10ab 100644 --- a/src/client/views/guild/feeds.ejs +++ b/src/client/views/guild/feeds.ejs @@ -10,17 +10,17 @@
- + placeholder header content
- - +
+ @@ -35,7 +35,7 @@ - - - - - -
+
URL @@ -46,21 +46,21 @@
+
Channels
+
Filters
+
Style @@ -71,7 +71,7 @@
+
Created at @@ -82,7 +82,7 @@
+
Status