Skip to content

Commit 54e660d

Browse files
Leonabcd123Miodec
andauthored
impr(quote search modal): add custom length filter (@Leonabcd123) (monkeytypegame#7109)
### Description Added a new filter option in the quotes search page that allows the user to search for quotes by a minimum and a maximum length. Implementing monkeytypegame#1323 --------- Co-authored-by: Jack <[email protected]>
1 parent 71c0f43 commit 54e660d

File tree

3 files changed

+120
-3
lines changed

3 files changed

+120
-3
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { SimpleModal } from "../utils/simple-modal";
2+
3+
export let minFilterLength: number = 0;
4+
export let maxFilterLength: number = 0;
5+
export let removeCustom: boolean = false;
6+
7+
export function setRemoveCustom(value: boolean): void {
8+
removeCustom = value;
9+
}
10+
11+
function refresh(): void {
12+
const refreshEvent = new CustomEvent("refresh");
13+
document.dispatchEvent(refreshEvent);
14+
}
15+
16+
export const quoteFilterModal = new SimpleModal({
17+
id: "quoteFilter",
18+
title: "Enter minimum and maximum values",
19+
inputs: [
20+
{
21+
placeholder: "1",
22+
type: "number",
23+
},
24+
{
25+
placeholder: "100",
26+
type: "number",
27+
},
28+
],
29+
buttonText: "save",
30+
execFn: async (_thisPopup, min, max) => {
31+
const minNum = parseInt(min, 10);
32+
const maxNum = parseInt(max, 10);
33+
if (isNaN(minNum) || isNaN(maxNum)) {
34+
return {
35+
status: 0,
36+
message: "Invalid min/max values",
37+
};
38+
}
39+
40+
minFilterLength = minNum;
41+
maxFilterLength = maxNum;
42+
refresh();
43+
44+
let message: string = "saved custom filter";
45+
return { status: 1, message };
46+
},
47+
afterClickAway: () => {
48+
setRemoveCustom(true);
49+
refresh();
50+
},
51+
});

frontend/src/ts/modals/quote-search.ts

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as ManualRestart from "../test/manual-restart-tracker";
44
import * as Notifications from "../elements/notifications";
55
import * as QuoteSubmitPopup from "./quote-submit";
66
import * as QuoteApprovePopup from "./quote-approve";
7+
import * as QuoteFilterPopup from "./quote-filter";
78
import * as QuoteReportModal from "./quote-report";
89
import {
910
buildSearchService,
@@ -26,6 +27,7 @@ const searchServiceCache: Record<string, SearchService<Quote>> = {};
2627

2728
const pageSize = 100;
2829
let currentPageNumber = 1;
30+
let usingCustomLength = true;
2931

3032
function getSearchService<T>(
3133
language: string,
@@ -45,16 +47,65 @@ function getSearchService<T>(
4547

4648
function applyQuoteLengthFilter(quotes: Quote[]): Quote[] {
4749
if (!modal.isOpen()) return [];
48-
const quoteLengthFilterValue = $(
49-
"#quoteSearchModal .quoteLengthFilter",
50-
).val() as string[];
50+
const quoteLengthDropdown = $("#quoteSearchModal .quoteLengthFilter");
51+
const quoteLengthFilterValue = quoteLengthDropdown.val() as string[];
52+
5153
if (quoteLengthFilterValue.length === 0) {
54+
usingCustomLength = true;
5255
return quotes;
5356
}
5457

5558
const quoteLengthFilter = new Set(
5659
quoteLengthFilterValue.map((filterValue) => parseInt(filterValue, 10)),
5760
);
61+
62+
const customFilterIndex = quoteLengthFilterValue.indexOf("4");
63+
64+
if (customFilterIndex !== -1) {
65+
if (QuoteFilterPopup.removeCustom) {
66+
QuoteFilterPopup.setRemoveCustom(false);
67+
const selectElement = quoteLengthDropdown.get(0) as
68+
| HTMLSelectElement
69+
| null
70+
| undefined;
71+
72+
if (!selectElement) {
73+
return quotes;
74+
}
75+
76+
//@ts-expect-error SlimSelect adds slim to the element
77+
const ss = selectElement.slim as SlimSelect | undefined;
78+
79+
if (ss !== undefined) {
80+
const currentSelected = ss.getSelected();
81+
82+
// remove custom selection
83+
const customIndex = currentSelected.indexOf("4");
84+
if (customIndex > -1) {
85+
currentSelected.splice(customIndex, 1);
86+
}
87+
88+
ss.setSelected(currentSelected);
89+
}
90+
} else {
91+
if (usingCustomLength) {
92+
QuoteFilterPopup.quoteFilterModal.show(undefined, {});
93+
usingCustomLength = false;
94+
} else {
95+
const filteredQuotes = quotes.filter(
96+
(quote) =>
97+
(quote.length >= QuoteFilterPopup.minFilterLength &&
98+
quote.length <= QuoteFilterPopup.maxFilterLength) ||
99+
quoteLengthFilter.has(quote.group),
100+
);
101+
102+
return filteredQuotes;
103+
}
104+
}
105+
} else {
106+
usingCustomLength = true;
107+
}
108+
58109
const filteredQuotes = quotes.filter((quote) =>
59110
quoteLengthFilter.has(quote.group),
60111
);
@@ -281,6 +332,10 @@ export async function show(showOptions?: ShowOptions): Promise<void> {
281332
text: "thicc",
282333
value: "3",
283334
},
335+
{
336+
text: "custom",
337+
value: "4",
338+
},
284339
],
285340
});
286341
},
@@ -437,6 +492,13 @@ async function setup(modalEl: HTMLElement): Promise<void> {
437492
currentPageNumber--;
438493
void updateResults(searchText);
439494
});
495+
496+
document?.addEventListener("refresh", () => {
497+
const searchText = (
498+
document.getElementById("searchBox") as HTMLInputElement
499+
).value;
500+
void updateResults(searchText);
501+
});
440502
}
441503

442504
async function cleanup(): Promise<void> {

frontend/src/ts/utils/simple-modal.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ type SimpleModalOptions = {
111111
onlineOnly?: boolean;
112112
hideCallsExec?: boolean;
113113
showLabels?: boolean;
114+
afterClickAway?: () => void;
114115
};
115116

116117
export class SimpleModal {
@@ -131,6 +132,7 @@ export class SimpleModal {
131132
onlineOnly: boolean;
132133
hideCallsExec: boolean;
133134
showLabels: boolean;
135+
afterClickAway: (() => void) | undefined;
134136
constructor(options: SimpleModalOptions) {
135137
this.parameters = [];
136138
this.id = options.id;
@@ -149,6 +151,7 @@ export class SimpleModal {
149151
this.onlineOnly = options.onlineOnly ?? false;
150152
this.hideCallsExec = options.hideCallsExec ?? false;
151153
this.showLabels = options.showLabels ?? false;
154+
this.afterClickAway = options.afterClickAway;
152155
}
153156
reset(): void {
154157
this.element.innerHTML = `
@@ -480,6 +483,7 @@ const modal = new AnimatedModal({
480483
hide();
481484
},
482485
customWrapperClickHandler: (e): void => {
486+
activePopup?.afterClickAway?.();
483487
hide();
484488
},
485489
});

0 commit comments

Comments
 (0)