This commit is contained in:
parent
816da70229
commit
31c4779bf2
@ -1,45 +1,18 @@
|
||||
import $ from "jquery";
|
||||
import HSDropdown from "@preline/dropdown";
|
||||
import HSOverlay, { IOverlayOptions } from "@preline/overlay";
|
||||
import HSSelect, { ISelectOptions } from "@preline/select";
|
||||
import HSDataTable, { IDataTableOptions } from "@preline/datatable";
|
||||
import DataTable, { Api, ConfigColumnDefs, AjaxSettings } from "datatables.net-dt";
|
||||
import { autoUpdate, computePosition, offset } from "@floating-ui/dom";
|
||||
import { formatTimestamp, verifyChannels } from "../../../src/ts/main";
|
||||
import { formatTimestamp, verifyChannels } from "../main";
|
||||
import HSDropdown from "preline/dist/dropdown";
|
||||
import HSDataTable, { IDataTableOptions } from "preline/dist/datatable";
|
||||
import HSSelect, { ISelectOptions } from "preline/dist/select";
|
||||
import HSOverlay, { IOverlayOptions } from "preline/dist/overlay";
|
||||
import HSDatepicker, { ICustomDatepickerOptions } from "preline/dist/datepicker";
|
||||
import { AjaxSettings, ConfigColumnDefs } from "datatables.net-dt";
|
||||
import prisma from "../../../../../generated/prisma";
|
||||
import HSDatepicker, { ICustomDatepickerOptions } from "@preline/datepicker";
|
||||
|
||||
|
||||
|
||||
// import "preline";
|
||||
// import _ from "lodash";
|
||||
// import noUiSlider from "nouislider";
|
||||
// import "datatables.net";
|
||||
// import "dropzone/dist/dropzone-min.js";
|
||||
// import * as VanillaCalendarPro from "vanilla-calendar-pro";
|
||||
|
||||
// // Preline requirements
|
||||
// window._ = _;
|
||||
// window.$ = $;
|
||||
// window.jQuery = $;
|
||||
// window.DataTable = $.fn.dataTable;
|
||||
// window.noUiSlider = noUiSlider;
|
||||
// window.VanillaCalendarPro = VanillaCalendarPro;
|
||||
|
||||
// window.HSStaticMethods.autoInit();
|
||||
// // document.addEventListener("DOMContentLoaded", () => {
|
||||
// // if (window.HSStaticMethods && typeof window.HSStaticMethods.autoInit === "function") {
|
||||
// // } else {
|
||||
// // console.warn("Preline is not available on window.HSStaticMethods.");
|
||||
// // }
|
||||
// // });
|
||||
import { ISingleOption } from "preline";
|
||||
import { TextChannel } from "discord.js";
|
||||
|
||||
declare let guildId: string;
|
||||
declare let channels: Array<any>;
|
||||
|
||||
|
||||
// #region DataTable
|
||||
//
|
||||
|
||||
const emptyTableHtml: string = `
|
||||
<div class="max-w-md w-full min-h-[400px] flex flex-col justify-center mx-auto px-6 py-4">
|
||||
@ -70,8 +43,6 @@ const columnDefs: ConfigColumnDefs[] = [
|
||||
// Select checkbox column
|
||||
{
|
||||
target: 0,
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
className: "size-px whitespace-nowrap",
|
||||
render: (_data: unknown, _type: unknown, row: prisma.Feed) => { return `
|
||||
<div class="ps-6 py-4">
|
||||
@ -109,8 +80,6 @@ const columnDefs: ConfigColumnDefs[] = [
|
||||
{
|
||||
target: 3,
|
||||
data: "channels",
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
className: "size-px",
|
||||
render: (data: prisma.Channel[], type: string, row: prisma.Feed) => {
|
||||
if (type !== "display") { return data; }
|
||||
@ -159,8 +128,6 @@ const columnDefs: ConfigColumnDefs[] = [
|
||||
{
|
||||
target: 4,
|
||||
data: "filters",
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
className: "size-px whitespace-nowrap",
|
||||
render: (data: prisma.Filter[], type: string, row: prisma.Feed) => {
|
||||
if (type !== "display") return data;
|
||||
@ -200,8 +167,6 @@ const columnDefs: ConfigColumnDefs[] = [
|
||||
{
|
||||
target: 5,
|
||||
data: null, // "message_style_id"
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
className: "size-px whitespace-nowrap",
|
||||
render: (_data: unknown, type: string, row: any) => {
|
||||
if (!row.message_style || type !== "display") return null;
|
||||
@ -221,7 +186,6 @@ const columnDefs: ConfigColumnDefs[] = [
|
||||
target: 6,
|
||||
data: "created_at",
|
||||
orderable: true,
|
||||
searchable: false,
|
||||
className: "size-px whitespace-nowrap",
|
||||
render: (data: string) => { return `
|
||||
<div class="px-6 py-4">
|
||||
@ -235,7 +199,6 @@ const columnDefs: ConfigColumnDefs[] = [
|
||||
target: 7,
|
||||
data: "active",
|
||||
orderable: true,
|
||||
searchable: false,
|
||||
className: "size-px whitespace-nowrap",
|
||||
render: (data: boolean) => {
|
||||
const wrapper = $("<div>").addClass("px-6 py-4");
|
||||
@ -292,68 +255,18 @@ const tableOptions: IDataTableOptions = {
|
||||
}
|
||||
};
|
||||
|
||||
const table: HSDataTable = new HSDataTable(
|
||||
$("#table").get(0) as HTMLElement,
|
||||
tableOptions
|
||||
);
|
||||
let table: HSDataTable;
|
||||
|
||||
const onTableSelectChange = () => {
|
||||
const selectedRowsCount = (table as any).dataTable.rows({ selected: true }).count();
|
||||
$("#deleteRowsBtn").prop("disabled", selectedRowsCount === 0);
|
||||
$(".rows-selected-count-js").text(selectedRowsCount);
|
||||
|
||||
const $elem = $(".rows-selected-count-js.zero-empty-js");
|
||||
selectedRowsCount === 0 ? $elem.hide() : $elem.show();
|
||||
};
|
||||
|
||||
(table as any).dataTable
|
||||
.on("select", onTableSelectChange)
|
||||
.on("deselect", onTableSelectChange)
|
||||
.on("draw", onTableSelectChange);
|
||||
|
||||
$("#selectAllBox").on("change", function() {
|
||||
(this as HTMLInputElement).checked
|
||||
? (table as any).dataTable.rows().select()
|
||||
: (table as any).dataTable.rows().deselect();
|
||||
});
|
||||
|
||||
$("#deleteRowsBtn").on("click", async () => {
|
||||
const dt: Api = (table as any).dataTable;
|
||||
const rowsData = dt.rows({ selected: true }).data().toArray();
|
||||
const rowIds = rowsData.map((row: prisma.Feed) => row.id);
|
||||
|
||||
await $.ajax({
|
||||
url: `/guild/${guildId}/feeds/api`,
|
||||
method: "delete",
|
||||
dataType: "json",
|
||||
data: { ids: rowIds },
|
||||
success: () => {
|
||||
dt.draw();
|
||||
dt.rows().deselect();
|
||||
},
|
||||
error: error => {
|
||||
alert(typeof error === "object" ? JSON.stringify(error, null, 4) : error);
|
||||
}
|
||||
});
|
||||
window.addEventListener("preline:ready", () => {
|
||||
const tableEl = $("#table").get(0);
|
||||
if (!HSDataTable.getInstance(tableEl, true)) {
|
||||
table = new HSDataTable(tableEl, tableOptions);
|
||||
}
|
||||
});
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Page Size Select
|
||||
// https://preline.co/plugins/html/advanced-select.html
|
||||
|
||||
(window as any)["FloatingUIDOM"] = {
|
||||
computePosition: computePosition,
|
||||
autoUpdate: autoUpdate,
|
||||
offset: offset
|
||||
};
|
||||
|
||||
// Close on click.
|
||||
window.addEventListener('click', (evt) => {
|
||||
const evtTarget = evt.target;
|
||||
|
||||
HSSelect.closeCurrentlyOpened(evtTarget as HTMLElement);
|
||||
});
|
||||
// #region Table Paging Select
|
||||
|
||||
const pageSelectOptions: ISelectOptions = {
|
||||
toggleTag: '<button type="button" aria-expanded="false"></button>',
|
||||
@ -373,31 +286,50 @@ const pageSelectOptions: ISelectOptions = {
|
||||
dropdownVerticalFixedPlacement: null
|
||||
};
|
||||
|
||||
const pageSizeSelect: HSSelect = new HSSelect(
|
||||
$("#selectPageSize-js").get(0) as HTMLElement,
|
||||
pageSelectOptions
|
||||
);
|
||||
window.addEventListener("preline:ready", () => {
|
||||
const selectEl = $("#selectPageSize-js").get(0);
|
||||
if (!HSSelect.getInstance(selectEl, true)) {
|
||||
new HSSelect(selectEl, pageSelectOptions);
|
||||
}
|
||||
});
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Edit Modal
|
||||
|
||||
(window as any).$hsOverlayCollection = [];
|
||||
const closeEditModal = () => { editModal.close() };
|
||||
|
||||
const editModalOptions: IOverlayOptions = {};
|
||||
const openEditModal = async (id: number | undefined) => {
|
||||
$("#editForm").removeClass("submitted");
|
||||
editModal.open();
|
||||
|
||||
const editModal: HSOverlay = new HSOverlay(
|
||||
$("#editModal").get(0) as HTMLElement,
|
||||
editModalOptions
|
||||
);
|
||||
id === undefined
|
||||
? clearEditModalData()
|
||||
: loadEditModalData(id);
|
||||
};
|
||||
|
||||
$(document).on("click", ".open-edit-modal-js", async event => {
|
||||
await openEditModal($(event.target).data("id"));
|
||||
});
|
||||
|
||||
const editModalOptions: IOverlayOptions = {};
|
||||
|
||||
let editModal: HSOverlay;
|
||||
|
||||
window.addEventListener("preline:ready", () => {
|
||||
const modalEl = $("#editModal").get(0);
|
||||
if (!HSOverlay.getInstance(modalEl, true)) {
|
||||
editModal = new HSOverlay(modalEl, editModalOptions);
|
||||
}
|
||||
});
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Edit Form
|
||||
|
||||
interface ExpandedFeed extends prisma.Feed {
|
||||
channels: prisma.Channel[],
|
||||
filters: prisma.Feed[]
|
||||
channels: prisma.Channel[];
|
||||
filters: prisma.Feed[];
|
||||
}
|
||||
|
||||
const clearEditModalData = () => {
|
||||
@ -405,6 +337,7 @@ const clearEditModalData = () => {
|
||||
|
||||
$("#formName").val("");
|
||||
$("#formUrl").val("");
|
||||
$("#formPublishedThreshold").val(new Date().toISOString().slice(0, 16));
|
||||
$("#formActive").prop("checked", true);
|
||||
channelSelect.setValue([]);
|
||||
filterSelect.setValue([]);
|
||||
@ -419,8 +352,11 @@ const loadEditModalData = async (id: number) => {
|
||||
|
||||
$(editModal.el).data("id", feed.id);
|
||||
|
||||
const publishedThreshold = new Date(feed.published_threshold as unknown as string)
|
||||
|
||||
$("#formName").val(feed.name);
|
||||
$("#formUrl").val(feed.url);
|
||||
$("#formPublishedThreshold").val(publishedThreshold.toISOString().slice(0, 16));
|
||||
$("#formActive").prop("checked", feed.active);
|
||||
|
||||
channelSelect.setValue(feed.channels.map(channel => channel.channel_id));
|
||||
@ -428,25 +364,44 @@ const loadEditModalData = async (id: number) => {
|
||||
styleSelect.setValue(`${feed.message_style_id}`);
|
||||
}
|
||||
|
||||
const openEditModal = async (id: number | undefined) => {
|
||||
// ISSUE:
|
||||
// The calculation with `channelSelect.setValue([])` assumes components are visible
|
||||
// when determining the width of the input placeholder 'Select option...'. This
|
||||
// requires the modal to be opened before running the calculation, which could be
|
||||
// bad.
|
||||
$("#editForm").removeClass("submitted");
|
||||
editModal.open();
|
||||
$("#editForm").on("submit", async event => {
|
||||
event.preventDefault();
|
||||
|
||||
id === undefined
|
||||
? clearEditModalData()
|
||||
: loadEditModalData(id);
|
||||
};
|
||||
const form = $(event.target).get(0) as HTMLFormElement;
|
||||
$(form).addClass("submitted");
|
||||
|
||||
const closeEditModal = () => {
|
||||
editModal.close();
|
||||
};
|
||||
const validity = form.checkValidity();
|
||||
if (!validity) {
|
||||
console.debug(`Submit form invalid: ${validity}`);
|
||||
return;
|
||||
};
|
||||
|
||||
const channelsSelectOptions: ISelectOptions = {
|
||||
let method = "post";
|
||||
const data = $(event.target).serializeArray();
|
||||
|
||||
// If 'id' has a value, we are patching an existing entry
|
||||
const id: number | undefined = $(editModal.el).data("id");
|
||||
if (id !== undefined) {
|
||||
data.push({ name: "id", value: `${id}` });
|
||||
method = "patch";
|
||||
}
|
||||
|
||||
await $.ajax({
|
||||
url: `/guild/${guildId}/feeds/api`,
|
||||
dataType: "json",
|
||||
method: method,
|
||||
data: data,
|
||||
success: () => {
|
||||
(table as any).dataTable.draw()
|
||||
closeEditModal();
|
||||
},
|
||||
error: error => {
|
||||
alert(JSON.stringify(error, null, 4));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const channelSelectOptions: ISelectOptions = {
|
||||
placeholder: "Select option....",
|
||||
mode: "tags",
|
||||
|
||||
@ -487,11 +442,6 @@ const channelsSelectOptions: ISelectOptions = {
|
||||
searchNoResultClasses: "cj-tag-select-search-no-results",
|
||||
};
|
||||
|
||||
const channelSelect = new HSSelect(
|
||||
$("#formChannels").get(0) as HTMLElement,
|
||||
channelsSelectOptions
|
||||
);
|
||||
|
||||
const filterSelectOptions: ISelectOptions = {
|
||||
placeholder: "Select option....",
|
||||
mode: "tags",
|
||||
@ -549,11 +499,6 @@ const filterSelectOptions: ISelectOptions = {
|
||||
|
||||
};
|
||||
|
||||
const filterSelect = new HSSelect(
|
||||
$("#formFilters").get(0),
|
||||
filterSelectOptions
|
||||
);
|
||||
|
||||
const styleSelectOptions: ISelectOptions = {
|
||||
placeholder: "Select option...",
|
||||
|
||||
@ -588,56 +533,40 @@ const styleSelectOptions: ISelectOptions = {
|
||||
optionAllowEmptyOption: true
|
||||
};
|
||||
|
||||
const styleSelect = new HSSelect(
|
||||
$("#formMessageStyle").get(0),
|
||||
styleSelectOptions
|
||||
);
|
||||
let channelSelect: HSSelect;
|
||||
let filterSelect: HSSelect;
|
||||
let styleSelect: HSSelect;
|
||||
|
||||
const publishedThresholdOptions: ICustomDatepickerOptions = {
|
||||
type: "default",
|
||||
dateMax: "2050-00-00",
|
||||
mode: "default",
|
||||
layouts: {
|
||||
window.addEventListener("preline:ready", () => {
|
||||
const exists = (element: HTMLElement) => HSSelect.getInstance(element, true);
|
||||
|
||||
},
|
||||
inputModeOptions: { itemsSeparator: " / " },
|
||||
templates: {
|
||||
arrowPrev: '<button data-vc-arrow="prev"><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="m15 18-6-6 6-6"></path></svg></button>',
|
||||
arrowNext: '<button data-vc-arrow="next"><svg class="shrink-0 size-4" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"></path></svg></button>'
|
||||
}
|
||||
};
|
||||
const channelEl = $("#formChannels").get(0);
|
||||
const filterEl = $("#formFilters").get(0);
|
||||
const styleEl = $("#formMessageStyle").get(0);
|
||||
|
||||
$("#editForm").on("submit", async event => {
|
||||
event.preventDefault();
|
||||
if (exists(channelEl) || exists(filterEl) || exists(styleEl)) return;
|
||||
|
||||
const form = $(event.target).get(0) as HTMLFormElement;
|
||||
$(form).addClass("submitted");
|
||||
channelSelect = new HSSelect(channelEl, channelSelectOptions);
|
||||
filterSelect = new HSSelect(filterEl, filterSelectOptions);
|
||||
styleSelect = new HSSelect(styleEl, styleSelectOptions);
|
||||
|
||||
if (!form.checkValidity()) return;
|
||||
|
||||
let method = "post";
|
||||
const data = $(event.target).serializeArray();
|
||||
|
||||
// If 'id' has a value, we are patching an existing entry
|
||||
const id: number | undefined = $(editModal.el).data("id");
|
||||
if (id !== undefined) {
|
||||
data.push({ name: "id", value: `${id}` });
|
||||
method = "patch";
|
||||
}
|
||||
|
||||
await $.ajax({
|
||||
url: `/guild/${guildId}/feeds/api`,
|
||||
dataType: "json",
|
||||
method: method,
|
||||
data: data,
|
||||
success: () => {
|
||||
(table as any).dataTable.draw() // is this okay? dataTable is private, but there is no other method I know of to redraw...
|
||||
closeEditModal();
|
||||
},
|
||||
error: error => {
|
||||
alert(JSON.stringify(error, null, 4));
|
||||
}
|
||||
});
|
||||
// Add options to the channel select
|
||||
channels.forEach((channel: TextChannel) => {
|
||||
channelSelect.addOption({
|
||||
title: channel.name,
|
||||
val: channel.id,
|
||||
options: {
|
||||
description: channel.id,
|
||||
icon: `
|
||||
<svg class="shrink-0 size-[16px]" viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="4" y1="9" x2="20" y2="9"></line>
|
||||
<line x1="4" y1="15" x2="20" y2="15"></line>
|
||||
<line x1="10" y1="3" x2="8" y2="21"></line>
|
||||
<line x1="16" y1="3" x2="14" y2="21"></line>
|
||||
</svg>` // hashtag icon
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
// #endregion
|
||||
|
@ -1,593 +0,0 @@
|
||||
import { formatTimestamp, verifyChannels } from "../main";
|
||||
import HSDropdown from "preline/dist/dropdown";
|
||||
import HSDataTable, { IDataTableOptions } from "preline/dist/datatable";
|
||||
import HSSelect, { ISelectOptions } from "preline/dist/select";
|
||||
import HSOverlay, { IOverlayOptions } from "preline/dist/overlay";
|
||||
import HSDatepicker, { ICustomDatepickerOptions } from "preline/dist/datepicker";
|
||||
import { AjaxSettings, ConfigColumnDefs } from "datatables.net-dt";
|
||||
import prisma from "../../../../../generated/prisma";
|
||||
|
||||
declare let guildId: string;
|
||||
declare let channels: Array<any>;
|
||||
|
||||
// #region DataTable
|
||||
|
||||
const emptyTableHtml: string = `
|
||||
<div class="max-w-md w-full min-h-[400px] flex flex-col justify-center mx-auto px-6 py-4">
|
||||
<div class="flex justify-center items-center size-[46px] bg-gray-100 rounded-lg dark:bg-neutral-800">
|
||||
<svg class="shrink-0 size-6 text-gray-600 dark:text-neutral-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M4 11a9 9 0 0 1 9 9"></path><path d="M4 4a16 16 0 0 1 16 16"></path><circle cx="5" cy="19" r="1"></circle></svg>
|
||||
</div>
|
||||
<h2 class="mt-5 font-semibold text-gray-800 dark:text-white">
|
||||
No results found
|
||||
</h2>
|
||||
<p class="mt-2 text-sm text-gray-600 dark:text-neutral-400">
|
||||
Refine your search or create a new feed.
|
||||
Alternatively, use a template to deploy a ready-made feed.
|
||||
</p>
|
||||
|
||||
<div class="mt-5 flex flex-col sm:flex-row gap-2">
|
||||
<button type="button" class="open-edit-modal-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">
|
||||
<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 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[] = [
|
||||
// Select checkbox column
|
||||
{
|
||||
target: 0,
|
||||
className: "size-px whitespace-nowrap",
|
||||
render: (_data: unknown, _type: unknown, row: prisma.Feed) => { return `
|
||||
<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">Select Row</span>
|
||||
</label>
|
||||
</div>
|
||||
`}
|
||||
},
|
||||
{
|
||||
target: 1,
|
||||
data: "name",
|
||||
orderable: true,
|
||||
searchable: true,
|
||||
className: "size-px whitespace-nowrap",
|
||||
render: (data: string, _type: string, row: prisma.Feed) => { return `
|
||||
<span class="cj-table-link open-edit-modal-js max-w-[250px] truncate" data-id="${row.id}">
|
||||
${data}
|
||||
</span>
|
||||
`}
|
||||
},
|
||||
{
|
||||
target: 2,
|
||||
data: "url",
|
||||
orderable: true,
|
||||
searchable: true,
|
||||
className: "size-px whitespace-nowrap",
|
||||
render: (data: string) => { return `
|
||||
<a href="${data}" class="cj-table-link max-w-[450px] truncate">
|
||||
${data}
|
||||
</a>
|
||||
`}
|
||||
},
|
||||
{
|
||||
target: 3,
|
||||
data: "channels",
|
||||
className: "size-px",
|
||||
render: (data: prisma.Channel[], type: string, row: prisma.Feed) => {
|
||||
if (type !== "display") { return data; }
|
||||
if (!data.length) { return ""; }
|
||||
|
||||
const wrapper = $("<div>").addClass("flex flex-nowrap gap-1 px-6 py-4");
|
||||
const tag = $("<span>").addClass("inline-flex items-center whitespace-nowrap gap-1 py-1 px-2.5 border text-xs rounded-md bg-white dark:bg-neutral-800 border-gray-200 dark:border-neutral-700 text-gray-800 dark:text-neutral-200");
|
||||
|
||||
if (!verifyChannels(data, channels)) {
|
||||
wrapper.text("invalid channels").addClass("whitespace-nowrap");
|
||||
return wrapper.get(0);
|
||||
}
|
||||
|
||||
const firstChannelName = "# " + channels.find(c => c.id === data[0].channel_id).name;
|
||||
wrapper.append(tag.clone().text(firstChannelName));
|
||||
|
||||
// No need to run the dropdown code if there's no more to show
|
||||
if (data.length === 1) {
|
||||
return wrapper.get(0);
|
||||
}
|
||||
|
||||
data.shift();
|
||||
|
||||
if (data.length <= 1) {
|
||||
const secondChannelName = "# " + channels.find(c => c.id === data[0].channel_id).name;
|
||||
wrapper.append(tag.clone().text(secondChannelName));
|
||||
return wrapper.get(0);
|
||||
}
|
||||
|
||||
const dropdown = $("<div>").addClass("hs-dropdown inline-block");
|
||||
const dropdownBtn = $("<button>").attr("id", `channelDrop-${row.id}`).attr("type", "button").addClass("cursor-pointer inline-flex items-center gap-1 py-1 px-2.5 border text-xs rounded-md bg-white dark:bg-neutral-800 border-gray-200 dark:border-neutral-700 text-gray-800 dark:text-neutral-200");
|
||||
const dropdownMenu = $("<div>").addClass("hs-dropdown-menu hidden opacity-0 hs-dropdown-open:opacity-100 transition-[opacity,margin] overflow-hidden z-10 w-fit max-w-64 border p-2 rounded-md bg-gray-200 dark:bg-neutral-700 border-gray-300 dark:border-neutral-600");
|
||||
|
||||
dropdown.append(dropdownBtn.text(`+${data.length}`));
|
||||
|
||||
data.forEach(channel => {
|
||||
const channelName = "# " + channels.find(c => c.id === channel.channel_id).name;
|
||||
dropdownMenu.append(tag.clone().text(channelName));
|
||||
});
|
||||
|
||||
dropdown.append(dropdownMenu);
|
||||
wrapper.append(dropdown);
|
||||
return wrapper.get(0);
|
||||
}
|
||||
},
|
||||
{
|
||||
target: 4,
|
||||
data: "filters",
|
||||
className: "size-px whitespace-nowrap",
|
||||
render: (data: prisma.Filter[], type: string, row: prisma.Feed) => {
|
||||
if (type !== "display") return data;
|
||||
if (!data.length) return "";
|
||||
|
||||
const wrapper = $("<div>").addClass("flex flex-nowrap gap-1 px-6 py-4");
|
||||
const tag = $("<span>").addClass("inline-flex items-center whitespace-nowrap gap-1 py-1 px-2.5 border text-xs rounded-md bg-white dark:bg-neutral-800 border-gray-200 dark:border-neutral-700 text-gray-800 dark:text-neutral-200");
|
||||
|
||||
wrapper.append(tag.clone().text(data[0].name));
|
||||
|
||||
if (data.length === 1) {
|
||||
return wrapper.get(0);
|
||||
}
|
||||
|
||||
data.shift();
|
||||
|
||||
if (data.length <= 1) {
|
||||
wrapper.append(tag.clone().text(data[0].name));
|
||||
return wrapper.get(0);
|
||||
}
|
||||
|
||||
const dropdown = $("<div>").addClass("hs-dropdown inline-block");
|
||||
const dropdownBtn = $("<button>").attr("id", `channelDrop-${row.id}`).attr("type", "button").addClass("cursor-pointer inline-flex items-center gap-1 py-1 px-2.5 border text-xs rounded-md bg-white dark:bg-neutral-800 border-gray-200 dark:border-neutral-700 text-gray-800 dark:text-neutral-200");
|
||||
const dropdownMenu = $("<div>").addClass("hs-dropdown-menu hidden opacity-0 hs-dropdown-open:opacity-100 transition-[opacity,margin] overflow-hidden z-10 w-fit max-w-64 border p-2 rounded-md bg-gray-200 dark:bg-neutral-700 border-gray-300 dark:border-neutral-600");
|
||||
|
||||
dropdown.append(dropdownBtn.text(`+${data.length}`));
|
||||
|
||||
data.forEach(filter => {
|
||||
dropdownMenu.append(tag.clone().text(filter.name));
|
||||
});
|
||||
|
||||
dropdown.append(dropdownMenu);
|
||||
wrapper.append(dropdown);
|
||||
return wrapper.get(0);
|
||||
}
|
||||
},
|
||||
{
|
||||
target: 5,
|
||||
data: null, // "message_style_id"
|
||||
className: "size-px whitespace-nowrap",
|
||||
render: (_data: unknown, type: string, row: any) => {
|
||||
if (!row.message_style || type !== "display") return null;
|
||||
|
||||
const wrapper = $("<div>").addClass("flex px-6 py-4");
|
||||
const badge = $("<span>").addClass("inline-flex items-center whitespace-nowrap border rounded-md bg-white dark:bg-neutral-800 border-gray-200 dark:border-neutral-700 overflow-hidden");
|
||||
const colour = $("<span>").addClass("size-6 shrink-0").css("background-color", row.message_style.colour);
|
||||
const label = $("<span>").addClass("py-1 px-2.5 text-xs text-gray-800 dark:text-neutral-200");
|
||||
label.text(row.message_style.name);
|
||||
|
||||
badge.append(colour).append(label);
|
||||
wrapper.append(badge);
|
||||
return wrapper.get(0);
|
||||
}
|
||||
},
|
||||
{
|
||||
target: 6,
|
||||
data: "created_at",
|
||||
orderable: true,
|
||||
className: "size-px whitespace-nowrap",
|
||||
render: (data: string) => { return `
|
||||
<div class="px-6 py-4">
|
||||
<span class="cj-table-text">
|
||||
${formatTimestamp(data)}
|
||||
</span>
|
||||
</div>
|
||||
`}
|
||||
},
|
||||
{
|
||||
target: 7,
|
||||
data: "active",
|
||||
orderable: true,
|
||||
className: "size-px whitespace-nowrap",
|
||||
render: (data: boolean) => {
|
||||
const wrapper = $("<div>").addClass("px-6 py-4");
|
||||
const badge = $("<span>").addClass("py-1 px-1.5 inline-flex items-center gap-x-1 text-xs font-medium rounded-full");
|
||||
const label = $("<span>");
|
||||
|
||||
if (data) {
|
||||
badge.addClass("bg-teal-100 text-teal-800 dark:bg-teal-500/10 dark:text-teal-500");
|
||||
badge.append($('<svg class="size-2.5" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"/></svg>'))
|
||||
.append(label.text("Active"));
|
||||
} else {
|
||||
badge.addClass("bg-red-100 text-red-800 dark:bg-red-500/10 dark:text-red-500");
|
||||
badge.append($('<svg class="size-2.5" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z"/></svg>'))
|
||||
.append(label.text("Inactive"));
|
||||
}
|
||||
|
||||
wrapper.append(badge);
|
||||
return wrapper.get(0);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const ajaxSettings: AjaxSettings = {
|
||||
url: `/guild/${guildId}/feeds/api/datatable`,
|
||||
type: "POST",
|
||||
contentType: "application/json",
|
||||
dataSrc: "data",
|
||||
data: (data: unknown) => {
|
||||
if (data === undefined) return;
|
||||
// TODO,
|
||||
return JSON.stringify(data);
|
||||
}
|
||||
};
|
||||
|
||||
const tableOptions: IDataTableOptions = {
|
||||
ajax: ajaxSettings,
|
||||
serverSide: true,
|
||||
processing: true,
|
||||
select: {
|
||||
style: "multi",
|
||||
selector: "td:first-child input[type='checkbox']"
|
||||
},
|
||||
columnDefs: columnDefs,
|
||||
pagingOptions: { pageBtnClasses: "hidden" },
|
||||
rowSelectingOptions: { selectAllSelector: "#selectAllBox" },
|
||||
language: {
|
||||
zeroRecords: emptyTableHtml,
|
||||
emptyTable: emptyTableHtml,
|
||||
loadingRecords: "Placeholder loading message..."
|
||||
},
|
||||
drawCallback: () => HSDropdown.autoInit(),
|
||||
rowCallback: (row: HTMLTableRowElement) => {
|
||||
$(row).addClass("bg-white dark:bg-neutral-900");
|
||||
}
|
||||
};
|
||||
|
||||
let table: HSDataTable;
|
||||
|
||||
window.addEventListener("preline:ready", () => {
|
||||
const tableEl = $("#table").get(0);
|
||||
if (!HSDataTable.getInstance(tableEl, true)) {
|
||||
table = new HSDataTable(tableEl, tableOptions);
|
||||
}
|
||||
});
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Table Paging Select
|
||||
|
||||
const pageSelectOptions: ISelectOptions = {
|
||||
toggleTag: '<button type="button" aria-expanded="false"></button>',
|
||||
optionTemplate: `
|
||||
<div class="flex justify-between items-center w-full">
|
||||
<span data-title></span>
|
||||
<span class="hidden hs-selected:block">
|
||||
<svg class="shrink-0 size-3.5 text-blue-600 dark:text-blue-500" xmlns="http:.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"><polyline points="20 6 9 17 4 12"/></svg>
|
||||
</span>
|
||||
</div>`,
|
||||
toggleClasses: "cj-table-paging-select-toggle",
|
||||
optionClasses: "cj-table-paging-select-option",
|
||||
dropdownClasses: "cj-table-paging-select-dropdown",
|
||||
dropdownSpace: 10,
|
||||
dropdownScope: "parent",
|
||||
dropdownPlacement: "top",
|
||||
dropdownVerticalFixedPlacement: null
|
||||
};
|
||||
|
||||
window.addEventListener("preline:ready", () => {
|
||||
const selectEl = $("#selectPageSize-js").get(0);
|
||||
if (!HSSelect.getInstance(selectEl, true)) {
|
||||
new HSSelect(selectEl, pageSelectOptions);
|
||||
}
|
||||
});
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Edit Modal
|
||||
|
||||
const closeEditModal = () => { editModal.close() };
|
||||
|
||||
const openEditModal = async (id: number | undefined) => {
|
||||
$("#editForm").removeClass("submitted");
|
||||
editModal.open();
|
||||
|
||||
id === undefined
|
||||
? clearEditModalData()
|
||||
: loadEditModalData(id);
|
||||
};
|
||||
|
||||
$(document).on("click", ".open-edit-modal-js", async event => {
|
||||
await openEditModal($(event.target).data("id"));
|
||||
});
|
||||
|
||||
const editModalOptions: IOverlayOptions = {};
|
||||
|
||||
let editModal: HSOverlay;
|
||||
|
||||
window.addEventListener("preline:ready", () => {
|
||||
const modalEl = $("#editModal").get(0);
|
||||
if (!HSOverlay.getInstance(modalEl, true)) {
|
||||
editModal = new HSOverlay(modalEl, editModalOptions);
|
||||
}
|
||||
});
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Edit Form
|
||||
|
||||
interface ExpandedFeed extends prisma.Feed {
|
||||
channels: prisma.Channel[],
|
||||
filters: prisma.Feed[]
|
||||
}
|
||||
|
||||
const clearEditModalData = () => {
|
||||
$(editModal.el).removeData("id");
|
||||
|
||||
$("#formName").val("");
|
||||
$("#formUrl").val("");
|
||||
$("#formActive").prop("checked", true);
|
||||
channelSelect.setValue([]);
|
||||
filterSelect.setValue([]);
|
||||
styleSelect.setValue("");
|
||||
};
|
||||
|
||||
const loadEditModalData = async (id: number) => {
|
||||
const feed: ExpandedFeed = await $.ajax({
|
||||
url: `/guild/${guildId}/feeds/api?id=${id}`,
|
||||
method: "get"
|
||||
});
|
||||
|
||||
$(editModal.el).data("id", feed.id);
|
||||
|
||||
$("#formName").val(feed.name);
|
||||
$("#formUrl").val(feed.url);
|
||||
$("#formActive").prop("checked", feed.active);
|
||||
|
||||
channelSelect.setValue(feed.channels.map(channel => channel.channel_id));
|
||||
filterSelect.setValue(feed.filters.map(filter => `${filter.id}`));
|
||||
styleSelect.setValue(`${feed.message_style_id}`);
|
||||
}
|
||||
|
||||
$("#editForm").on("submit", async event => {
|
||||
event.preventDefault();
|
||||
|
||||
const form = $(event.target).get(0) as HTMLFormElement;
|
||||
$(form).addClass("submitted");
|
||||
|
||||
const validity = form.checkValidity();
|
||||
if (!validity) {
|
||||
console.debug(`Submit form invalid: ${validity}`);
|
||||
return;
|
||||
};
|
||||
|
||||
let method = "post";
|
||||
const data = $(event.target).serializeArray();
|
||||
|
||||
// If 'id' has a value, we are patching an existing entry
|
||||
const id: number | undefined = $(editModal.el).data("id");
|
||||
if (id !== undefined) {
|
||||
data.push({ name: "id", value: `${id}` });
|
||||
method = "patch";
|
||||
}
|
||||
|
||||
await $.ajax({
|
||||
url: `/guild/${guildId}/feeds/api`,
|
||||
dataType: "json",
|
||||
method: method,
|
||||
data: data,
|
||||
success: () => {
|
||||
(table as any).dataTable.draw()
|
||||
closeEditModal();
|
||||
},
|
||||
error: error => {
|
||||
alert(JSON.stringify(error, null, 4));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const channelSelectOptions: ISelectOptions = {
|
||||
placeholder: "Select option....",
|
||||
mode: "tags",
|
||||
|
||||
tagsItemTemplate: `
|
||||
<div class="flex flex-nowrap items-center relative z-10 bg-white border border-gray-200 rounded-lg p-1 m-1 dark:bg-neutral-900 dark:border-neutral-700 ">
|
||||
<div class="size-6 flex justify-center items-center" data-icon></div>
|
||||
<div class="whitespace-nowrap text-gray-800 dark:text-neutral-200" data-title></div>
|
||||
<div class="inline-flex shrink-0 justify-center items-center size-5 ms-2 rounded-lg text-gray-800 bg-gray-200 hover:bg-gray-300 focus:outline-hidden focus:ring-2 focus:ring-gray-400 text-sm dark:bg-neutral-700/50 dark:hover:bg-neutral-700 dark:text-neutral-400 cursor-pointer" data-remove>
|
||||
<svg class="shrink-0 size-3" 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="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
optionTemplate: `
|
||||
<div class="cj-tag-select-option">
|
||||
<div data-icon></div>
|
||||
<div>
|
||||
<div data-title></div>
|
||||
<div data-description></div>
|
||||
</div>
|
||||
<div class="ms-auto">
|
||||
<span class="hidden hs-selected:block">
|
||||
<svg class="shrink-0 size-4 text-blue-600" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M12.736 3.97a.733.733 0 0 1 1.047 0c.286.289.29.756.01 1.05L7.88 12.01a.733.733 0 0 1-1.065.02L3.217 8.384a.757.757 0 0 1 0-1.06.733.733 0 0 1 1.047 0l3.052 3.093 5.4-6.425a.247.247 0 0 1 .02-.022Z"/></svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
tagsInputId: "formChannelsInput",
|
||||
wrapperClasses: "cj-tag-select-wrapper",
|
||||
dropdownClasses: "cj-tag-select-dropdown w-full",
|
||||
tagsInputClasses: "cj-tag-select-input",
|
||||
|
||||
dropdownScope: "window",
|
||||
dropdownSpace: 10,
|
||||
dropdownPlacement: "bottom",
|
||||
dropdownVerticalFixedPlacement: null,
|
||||
|
||||
hasSearch: false,
|
||||
searchNoResultClasses: "cj-tag-select-search-no-results",
|
||||
};
|
||||
|
||||
const filterSelectOptions: ISelectOptions = {
|
||||
placeholder: "Select option....",
|
||||
mode: "tags",
|
||||
|
||||
tagsItemTemplate: `
|
||||
<div class="flex flex-nowrap items-center relative z-10 bg-white border border-gray-200 rounded-lg p-1 m-1 dark:bg-neutral-900 dark:border-neutral-700 ">
|
||||
<div class="size-6 flex justify-center items-center">
|
||||
<svg class="shrink-0 size-[16px]" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M12 3c2.755 0 5.455.232 8.083.678.533.09.917.556.917 1.096v1.044a2.25 2.25 0 0 1-.659 1.591l-5.432 5.432a2.25 2.25 0 0 0-.659 1.591v2.927a2.25 2.25 0 0 1-1.244 2.013L9.75 21v-6.568a2.25 2.25 0 0 0-.659-1.591L3.659 7.409A2.25 2.25 0 0 1 3 5.818V4.774c0-.54.384-1.006.917-1.096A48.32 48.32 0 0 1 12 3Z" /></svg>
|
||||
</div>
|
||||
<div class="whitespace-nowrap text-gray-800 dark:text-neutral-200" data-title></div>
|
||||
<div class="inline-flex shrink-0 justify-center items-center size-5 ms-2 rounded-lg text-gray-800 bg-gray-200 hover:bg-gray-300 focus:outline-hidden focus:ring-2 focus:ring-gray-400 text-sm dark:bg-neutral-700/50 dark:hover:bg-neutral-700 dark:text-neutral-400 cursor-pointer" data-remove>
|
||||
<svg class="shrink-0 size-3" 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="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
optionTemplate: `
|
||||
<div class="cj-tag-select-option">
|
||||
<div data-icon>
|
||||
<svg class="shrink-0 size-[18px]" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M12 3c2.755 0 5.455.232 8.083.678.533.09.917.556.917 1.096v1.044a2.25 2.25 0 0 1-.659 1.591l-5.432 5.432a2.25 2.25 0 0 0-.659 1.591v2.927a2.25 2.25 0 0 1-1.244 2.013L9.75 21v-6.568a2.25 2.25 0 0 0-.659-1.591L3.659 7.409A2.25 2.25 0 0 1 3 5.818V4.774c0-.54.384-1.006.917-1.096A48.32 48.32 0 0 1 12 3Z" /></svg>
|
||||
</div>
|
||||
<div>
|
||||
<div data-title></div>
|
||||
<div data-description></div>
|
||||
</div>
|
||||
<div class="ms-auto">
|
||||
<span class="hidden hs-selected:block">
|
||||
<svg class="shrink-0 size-4 text-blue-600" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M12.736 3.97a.733.733 0 0 1 1.047 0c.286.289.29.756.01 1.05L7.88 12.01a.733.733 0 0 1-1.065.02L3.217 8.384a.757.757 0 0 1 0-1.06.733.733 0 0 1 1.047 0l3.052 3.093 5.4-6.425a.247.247 0 0 1 .02-.022Z"/></svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
tagsInputId: "formFiltersInput",
|
||||
wrapperClasses: "cj-tag-select-wrapper",
|
||||
dropdownClasses: "cj-tag-select-dropdown w-full",
|
||||
tagsInputClasses: "cj-tag-select-input",
|
||||
|
||||
dropdownScope: "window",
|
||||
dropdownSpace: 10,
|
||||
dropdownPlacement: "bottom",
|
||||
dropdownVerticalFixedPlacement: null,
|
||||
|
||||
// API
|
||||
apiUrl: `/guild/${guildId}/filters/api/select`,
|
||||
apiQuery: "limit=15",
|
||||
apiFieldsMap: {
|
||||
id: "id",
|
||||
val: "id",
|
||||
title: "name",
|
||||
description: "value",
|
||||
name: "title"
|
||||
},
|
||||
apiSearchQueryKey: "search",
|
||||
hasSearch: false,
|
||||
searchNoResultClasses: "cj-tag-select-search-no-results",
|
||||
|
||||
};
|
||||
|
||||
const styleSelectOptions: ISelectOptions = {
|
||||
placeholder: "Select option...",
|
||||
|
||||
toggleTag: '<button type="button" aria-expanded="false"><span data-title></span></button>',
|
||||
optionTemplate: `
|
||||
<div class="flex justify-between items-center w-full">
|
||||
<span data-title></span>
|
||||
<span class="hidden hs-selected:block">
|
||||
<svg class="shrink-0 size-3.5 text-blue-600 dark:text-blue-500" xmlns="http:.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"><polyline points="20 6 9 17 4 12"/></svg>
|
||||
</span>
|
||||
</div>`,
|
||||
toggleClasses: "cj-select-toggle select-input",
|
||||
optionClasses: "cj-select-option",
|
||||
dropdownClasses: "cj-select-dropdown",
|
||||
wrapperClasses: "peer",
|
||||
dropdownSpace: 10,
|
||||
dropdownScope: "parent",
|
||||
dropdownPlacement: "top",
|
||||
dropdownVerticalFixedPlacement: null,
|
||||
|
||||
apiUrl: `/guild/${guildId}/styles/api/select`,
|
||||
// apiQuery: "limit=15",
|
||||
apiFieldsMap: {
|
||||
id: "id",
|
||||
val: "id",
|
||||
title: "name",
|
||||
description: "value",
|
||||
name: "title"
|
||||
},
|
||||
apiSearchQueryKey: "search",
|
||||
hasSearch: false,
|
||||
optionAllowEmptyOption: true
|
||||
};
|
||||
|
||||
let channelSelect: HSSelect;
|
||||
let filterSelect: HSSelect;
|
||||
let styleSelect: HSSelect;
|
||||
|
||||
window.addEventListener("preline:ready", () => {
|
||||
const exists = (element: HTMLElement) => HSSelect.getInstance(element, true);
|
||||
|
||||
const channelEl = $("#formChannels").get(0);
|
||||
const filterEl = $("#formFilters").get(0);
|
||||
const styleEl = $("#formMessageStyle").get(0);
|
||||
|
||||
if (!exists(channelEl) && !exists(filterEl) && !exists(styleEl)) {
|
||||
channelSelect = new HSSelect(channelEl, channelSelectOptions);
|
||||
filterSelect = new HSSelect(filterEl, filterSelectOptions);
|
||||
styleSelect = new HSSelect(styleEl, styleSelectOptions);
|
||||
}
|
||||
});
|
||||
|
||||
const publishedThresholdOptions: ICustomDatepickerOptions = {
|
||||
type: "default",
|
||||
dateMax: "2050-00-00",
|
||||
mode: "custom-select",
|
||||
layouts: {
|
||||
default: `
|
||||
<div class="--single-month flex flex-col overflow-hidden dark:bg-neutral-900 dark:border-neutral-700">
|
||||
<div class="grid grid-cols-5 items-center gap-x-3 mx-1.5 pb-3" data-vc="header">
|
||||
<div class="col-span-1">
|
||||
<#CustomArrowPrev />
|
||||
</div>
|
||||
<div class="col-span-3 flex justify-center items-center gap-x-1">
|
||||
<#CustomMonth />
|
||||
<span class="text-gray-800 dark:text-neutral-200">/</span>
|
||||
<#CustomYear />
|
||||
</div>
|
||||
<div class="col-span-1 flex justify-end">
|
||||
<#CustomArrowNext />
|
||||
</div>
|
||||
</div>
|
||||
<div data-vc="wrapper">
|
||||
<div data-vc="content">
|
||||
<#Week /><#Dates />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<#CustomTime />`
|
||||
},
|
||||
inputModeOptions: { itemsSeparator: " / " },
|
||||
templates: {
|
||||
arrowPrev: '<button data-vc-arrow="prev"><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="m15 18-6-6 6-6"></path></svg></button>',
|
||||
arrowNext: '<button data-vc-arrow="next"><svg class="shrink-0 size-4" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"></path></svg></button>'
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
let publishedThresholdInput: HSDatepicker;
|
||||
|
||||
window.addEventListener("preline:ready", () => {
|
||||
const publishedEl = $("#formPublishedThreshold").get(0);
|
||||
if (!HSDatepicker.getInstance(publishedEl, true)) {
|
||||
publishedThresholdInput = new HSDatepicker(publishedEl, publishedThresholdOptions);
|
||||
}
|
||||
});
|
||||
|
||||
// #endregion
|
@ -176,17 +176,6 @@
|
||||
<label for="formChannels" class="text-input-label">Channels</label>
|
||||
<select name="channels" id="formChannels" class="--prevent-on-load-init" multiple>
|
||||
<option value="">Choose</option>
|
||||
<% guild.channels.cache
|
||||
.filter(channel => channel.type == 0)
|
||||
.sort((a, b) => a.rawPosition - b.rawPosition)
|
||||
.forEach(channel => { %>
|
||||
<option value="<%= channel.id %>" data-hs-select-option='{
|
||||
"description": "<%= channel.id %>",
|
||||
"icon": "<svg class=\"shrink-0 size-[16px]\" viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" stroke=\"currentColor\" stroke-width=\"2\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><line x1=\"4\" y1=\"9\" x2=\"20\" y2=\"9\"></line><line x1=\"4\" y1=\"15\" x2=\"20\" y2=\"15\"></line><line x1=\"10\" y1=\"3\" x2=\"8\" y2=\"21\"></line><line x1=\"16\" y1=\"3\" x2=\"14\" y2=\"21\"></line></svg>"
|
||||
}'>
|
||||
<%= channel.name %>
|
||||
</option>
|
||||
<% }); %>
|
||||
</select>
|
||||
<p class="text-input-help">
|
||||
The recipients of content from this feed.
|
||||
@ -207,14 +196,14 @@
|
||||
<option value="">None</option>
|
||||
</select>
|
||||
<p class="text-input-help">
|
||||
placeholder.
|
||||
A custom appearance used to display content from this feed.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<label for="formPublishedThreshold" class="text-input-label">Published Threshold</label>
|
||||
<input type="text" id="formPublishedThreshold" name="published_threshold" class="z-[80] py-3 px-4 block w-full border-gray-200 rounded-lg sm:text-sm focus:border-blue-600 focus:ring-blue-600 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-900 dark:border-neutral-700 dark:text-neutral-400 dark:placeholder:text-neutral-400 dark:focus:border-blue-500 dark:focus:ring-neutral-500" readonly required>
|
||||
<input type="datetime-local" id="formPublishedThreshold" name="published_threshold" class="text-input form-input peer invalid:group-[.submitted]:border-red-500 invalid:group-[.submitted]:ring-red-500" required>
|
||||
<p class="text-input-help block peer-invalid:group-[.submitted]:hidden">
|
||||
placeholder helper text.
|
||||
This feed won't process content older than this date & time.
|
||||
</p>
|
||||
<p class="mt-2 text-sm text-red-500 hidden peer-invalid:group-[.submitted]:block">
|
||||
Please enter a date.
|
||||
@ -255,4 +244,4 @@
|
||||
.map(channel => channel.toJSON())
|
||||
) %>`);
|
||||
</script>
|
||||
<% block("scripts").append('<script src="/public/generated/js/guild/test.js"></script>'); %>
|
||||
<% block("scripts").append('<script src="/public/generated/js/guild/feeds.js"></script>'); %>
|
Loading…
x
Reference in New Issue
Block a user