diff --git a/src/client/src/css/main.css b/src/client/src/css/main.css index 6cf405f..04d702e 100644 --- a/src/client/src/css/main.css +++ b/src/client/src/css/main.css @@ -136,7 +136,7 @@ @apply z-80 min-w-fit - min-h-[150px] + min-h-fit max-h-72 p-1.5 space-y-0.5 @@ -184,6 +184,38 @@ @apply text-xs text-gray-500 dark:text-neutral-500; } +.cj-tag-select-search { + @apply + block + w-full + rounded-lg + py-1.5 + sm:py-2 + px-3 + sm:text-sm + + bg-white + text-gray-800 + border-gray-200 + dark:text-neutral-400 + dark:bg-neutral-900 + dark:border-neutral-700; +} + +.cj-tag-select-search-wrapper { + @apply + p-2 + -mx-1 + -mt-1 + sticky + top-0 + bg-none +} + +.cj-tag-select-search-no-results { + @apply block p-4; +} + /* Normal Select */ .cj-select-toggle { diff --git a/src/client/src/ts/guild/feeds.ts b/src/client/src/ts/guild/feeds.ts index 98a4aa9..3a936cb 100644 --- a/src/client/src/ts/guild/feeds.ts +++ b/src/client/src/ts/guild/feeds.ts @@ -109,16 +109,15 @@ const columnDefs: ConfigColumnDefs[] = [ if (data.length === 1) { return wrapper.get(0); } - else if (data.length <= 2) { - const secondChannelName = "# " + channels.find(c => c.id === data[1].channel_id).name; + + data.shift(); + + if (data.length <= 1) { + const secondChannelName = "# " + channels.find(c => c.id === data[0].channel_id).name; wrapper.append(tag.clone().text(secondChannelName)); - data.shift(); return wrapper.get(0); } - // drop the first element to exclude it from the dropdown - data.shift(); - const dropdown = $("
").addClass("hs-dropdown inline-block"); const dropdownBtn = $("
`, tagsInputId: "formChannelsInput", wrapperClasses: "cj-tag-select-wrapper", @@ -422,14 +449,79 @@ const channelsSelectOptions: ISelectOptions = { dropdownScope: "window", dropdownSpace: 10, dropdownPlacement: "bottom", - dropdownVerticalFixedPlacement: null + dropdownVerticalFixedPlacement: null, + + hasSearch: false, + searchNoResultClasses: "cj-tag-select-search-no-results", }; -const channelSelect: HSSelect = new HSSelect( +const channelSelect = new HSSelect( $("#formChannels").get(0) as HTMLElement, channelsSelectOptions ); +const filterSelectOptions: ISelectOptions = { + placeholder: "Select option....", + mode: "tags", + + tagsItemTemplate: ` +
+
+ +
+
+
+ +
+
+ `, + optionTemplate: ` +
+
+ +
+
+
+
+
+
+ +
+
+ `, + 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 filterSelect = new HSSelect( + $("#formFilters").get(0), + filterSelectOptions +); + $("#editForm").on("submit", async event => { event.preventDefault(); diff --git a/src/client/views/guild/feeds.ejs b/src/client/views/guild/feeds.ejs index be34441..171648f 100644 --- a/src/client/views/guild/feeds.ejs +++ b/src/client/views/guild/feeds.ejs @@ -197,9 +197,18 @@ <% }); %> +

+ The recipients of content from this feed. +

-
- +
+ + +

+ Filter out unwanted content from this feed. +

diff --git a/src/server/controllers/guild/api/filter.controller.ts b/src/server/controllers/guild/api/filter.controller.ts index 6de6d60..e727419 100644 --- a/src/server/controllers/guild/api/filter.controller.ts +++ b/src/server/controllers/guild/api/filter.controller.ts @@ -2,6 +2,7 @@ import { Request, Response } from "express"; import prisma, { Prisma } from "@server/prisma"; import { datatableRequest } from "@server/controllers/guild/api/dt.module"; +// TODO: this doesn't account for guild ID or permissions export const get = async (request: Request, response: Response) => { if (!request.query.id) { response.status(400).json({ error: "missing 'id' query" }); @@ -118,4 +119,27 @@ export const datatable = async (request: Request, response: Response) => { ); }; -export default { get, post, patch, del, datatable }; \ No newline at end of file +export const select = async (request: Request, response: Response) => { + const guildId = request.params.guildId; + const { search } = request.query; + + const data = await prisma.filter.findMany({ + where: { + guild_id: guildId, + name: { contains: `${search}` } + } + }); + + // Preline Bug: https://github.com/htmlstreamofficial/preline/issues/567 + // The returned data must have a "title" key, otherwise the advanced + // select component with 'tags' mode will have no title, regardless of + // mapping. + const modifiedResults = data.map(filter => ({ + ...filter, + title: filter.name + })); + + response.json(modifiedResults); +}; + +export default { get, post, patch, del, datatable, select }; \ No newline at end of file diff --git a/src/server/routers/guild.router.ts b/src/server/routers/guild.router.ts index bd6f1c0..dfd1b18 100644 --- a/src/server/routers/guild.router.ts +++ b/src/server/routers/guild.router.ts @@ -32,6 +32,7 @@ router.patch("/:guildId/feeds/api", feedApiController.patch); router.delete("/:guildId/feeds/api", feedApiController.del); router.post("/:guildId/filters/api/datatable", filterApiController.datatable); +router.get("/:guildId/filters/api/select", filterApiController.select); router.get("/:guildId/filters/api", filterApiController.get); router.post("/:guildId/filters/api", filterApiController.post); router.patch("/:guildId/filters/api", filterApiController.patch);