+
{{ .strings.accounts }}
+
+
{{ .strings.filters }}
+
+
+
{{ .strings.filters }}
+
+
+
{{ .quantityStrings.addUser.Singular }}
diff --git a/lang/admin/en-us.json b/lang/admin/en-us.json
index 9afde19..4dd03b3 100644
--- a/lang/admin/en-us.json
+++ b/lang/admin/en-us.json
@@ -118,6 +118,7 @@
"accessJFA": "Access jfa-go",
"accessJFASettings": "Cannot be changed as either \"Admin Only\" or \"Allow All\" has been set in Settings > General.",
"sortingBy": "Sorting By",
+ "filters": "Filters",
"clickToRemoveFilter": "Click to remove this filter."
},
"notifications": {
diff --git a/ts/modules/accounts.ts b/ts/modules/accounts.ts
index 9354afe..74f27e9 100644
--- a/ts/modules/accounts.ts
+++ b/ts/modules/accounts.ts
@@ -789,102 +789,109 @@ export class accountsList {
}
}
}
-
- search = (query: String): string[] => {
- const queries: { [field: string]: { name: string, getter: string, bool: boolean, string: boolean, date: boolean }} = {
- "id": {
- name: "Jellyfin ID",
- getter: "id",
- bool: false,
- string: true,
- date: false
- },
- "label": {
- name: "Label",
- getter: "label",
- bool: true,
- string: true,
- date: false
- },
- "username": {
- name: "Username",
- getter: "name",
- bool: false,
- string: true,
- date: false
- },
- "name": {
- name: "Username",
- getter: "name",
- bool: false,
- string: true,
- date: false
- },
- "admin": {
- name: "Admin",
- getter: "admin",
- bool: true,
- string: false,
- date: false
- },
- "disabled": {
- name: "Disabled",
- getter: "disabled",
- bool: true,
- string: false,
- date: false
- },
- "access-jfa": {
- name: "Access jfa-go",
- getter: "accounts_admin",
- bool: true,
- string: false,
- date: false
- },
- "email": {
- name: "Email",
- getter: "email",
- bool: true,
- string: true,
- date: false
- },
- "telegram": {
- name: "Telegram",
- getter: "telegram",
- bool: true,
- string: true,
- date: false
- },
- "matrix": {
- name: "Matrix",
- getter: "matrix",
- bool: true,
- string: true,
- date: false
- },
- "discord": {
- name: "Discord",
- getter: "discord",
- bool: true,
- string: true,
- date: false
- },
- "expiry": {
- name: "Expiry",
- getter: "expiry",
- bool: true,
- string: false,
- date: true
- },
- "last-active": {
- name: "Last Active",
- getter: "last_active",
- bool: true,
- string: false,
- date: true
- }
- }
+ private _queries: { [field: string]: { name: string, getter: string, bool: boolean, string: boolean, date: boolean, dependsOnTableHeader?: string, show?: boolean }} = {
+ "id": {
+ name: "Jellyfin ID",
+ getter: "id",
+ bool: false,
+ string: true,
+ date: false
+ },
+ "label": {
+ name: "Label",
+ getter: "label",
+ bool: true,
+ string: true,
+ date: false
+ },
+ "username": {
+ name: "Username",
+ getter: "name",
+ bool: false,
+ string: true,
+ date: false
+ },
+ "name": {
+ name: "Username",
+ getter: "name",
+ bool: false,
+ string: true,
+ date: false,
+ show: false
+ },
+ "admin": {
+ name: "Admin",
+ getter: "admin",
+ bool: true,
+ string: false,
+ date: false
+ },
+ "disabled": {
+ name: "Disabled",
+ getter: "disabled",
+ bool: true,
+ string: false,
+ date: false
+ },
+ "access-jfa": {
+ name: "Access jfa-go",
+ getter: "accounts_admin",
+ bool: true,
+ string: false,
+ date: false,
+ dependsOnTableHeader: "accounts-header-access-jfa"
+ },
+ "email": {
+ name: "Email",
+ getter: "email",
+ bool: true,
+ string: true,
+ date: false,
+ dependsOnTableHeader: "accounts-header-email"
+ },
+ "telegram": {
+ name: "Telegram",
+ getter: "telegram",
+ bool: true,
+ string: true,
+ date: false,
+ dependsOnTableHeader: "accounts-header-telegram"
+ },
+ "matrix": {
+ name: "Matrix",
+ getter: "matrix",
+ bool: true,
+ string: true,
+ date: false,
+ dependsOnTableHeader: "accounts-header-matrix"
+ },
+ "discord": {
+ name: "Discord",
+ getter: "discord",
+ bool: true,
+ string: true,
+ date: false,
+ dependsOnTableHeader: "accounts-header-discord"
+ },
+ "expiry": {
+ name: "Expiry",
+ getter: "expiry",
+ bool: true,
+ string: false,
+ date: true,
+ dependsOnTableHeader: "accounts-header-expiry"
+ },
+ "last-active": {
+ name: "Last Active",
+ getter: "last_active",
+ bool: true,
+ string: false,
+ date: true
+ }
+ }
+
+ search = (query: String): string[] => {
const filterArea = document.getElementById("accounts-filter-area");
filterArea.textContent = "";
@@ -945,9 +952,9 @@ export class accountsList {
}
const split = [word.substring(0, word.indexOf(":")), word.substring(word.indexOf(":")+1)];
- if (!(split[0] in queries)) continue;
+ if (!(split[0] in this._queries)) continue;
- const queryFormat = queries[split[0]];
+ const queryFormat = this._queries[split[0]];
if (queryFormat.bool) {
let isBool = false;
@@ -1004,7 +1011,8 @@ export class accountsList {
filterCard.addEventListener("click", () => {
for (let quote of [`"`, `'`, ``]) {
- this._search.value = this._search.value.replace(split[0] + ":" + quote + split[1] + quote, "");
+ let regex = new RegExp(split[0] + ":" + quote + split[1] + quote, "ig");
+ this._search.value = this._search.value.replace(regex, "");
}
this._search.oninput((null as Event));
})
@@ -1024,6 +1032,7 @@ export class accountsList {
if (queryFormat.date) {
// -1 = Before, 0 = On, 1 = After, 2 = No symbol, assume 0
let compareType = (split[1][0] == ">") ? 1 : ((split[1][0] == "<") ? -1 : ((split[1][0] == "=") ? 0 : 2));
+ let unmodifiedValue = split[1];
if (compareType != 2) {
split[1] = split[1].substring(1);
}
@@ -1046,7 +1055,8 @@ export class accountsList {
filterCard.addEventListener("click", () => {
for (let quote of [`"`, `'`, ``]) {
- this._search.value = this._search.value.replace(split[0] + ":" + quote + split[1] + quote, "");
+ let regex = new RegExp(split[0] + ":" + quote + unmodifiedValue + quote, "ig");
+ this._search.value = this._search.value.replace(regex, "");
}
this._search.oninput((null as Event));
@@ -1823,7 +1833,7 @@ export class accountsList {
const headerNames: string[] = ["username", "access-jfa", "email", "telegram", "matrix", "discord", "expiry", "last-active"];
const headerGetters: string[] = ["name", "accounts_admin", "email", "telegram", "matrix", "discord", "expiry", "last_active"];
for (let i = 0; i < headerNames.length; i++) {
- const header: HTMLTableHeaderCellElement = document.getElementsByClassName("accounts-header-" + headerNames[i])[0] as HTMLTableHeaderCellElement;
+ const header: HTMLTableHeaderCellElement = document.querySelector(".accounts-header-" + headerNames[i]) as HTMLTableHeaderCellElement;
if (header !== null) {
this._columns[header.className] = new Column(header, Object.getOwnPropertyDescriptor(user.prototype, headerGetters[i]).get);
}
@@ -1854,6 +1864,85 @@ export class accountsList {
});
defaultSort();
+
+ const filterList = document.getElementById("accounts-filter-list");
+
+ const fillInFilter = (name: string, value: string, offset?: number) => {
+ this._search.value = name + ":" + value + " " + this._search.value;
+ this._search.focus();
+ let newPos = name.length + 1 + value.length;
+ if (typeof offset !== 'undefined')
+ newPos += offset;
+ this._search.setSelectionRange(newPos, newPos);
+ this._search.oninput(null as any);
+ };
+
+ // Generate filter buttons
+ for (let queryName of Object.keys(this._queries)) {
+ const query = this._queries[queryName];
+ if ("show" in query && !query.show) continue;
+ if ("dependsOnTableHeader" in query && query.dependsOnTableHeader) {
+ const el = document.querySelector("."+query.dependsOnTableHeader);
+ if (el === null) continue;
+ }
+
+ const container = document.createElement("span") as HTMLSpanElement;
+ container.classList.add("button", "button-xl", "~neutral", "@low", "mb-1", "mr-2");
+ container.innerHTML = `${query.name}`;
+ if (query.bool) {
+ const pos = document.createElement("button") as HTMLButtonElement;
+ pos.type = "button";
+ pos.ariaLabel = `Filter by "${query.name}": True`;
+ pos.classList.add("button", "~positive", "ml-2");
+ pos.innerHTML = ``;
+ pos.addEventListener("click", () => fillInFilter(queryName, "true"));
+ const neg = document.createElement("button") as HTMLButtonElement;
+ neg.type = "button";
+ neg.ariaLabel = `Filter by "${query.name}": False`;
+ neg.classList.add("button", "~critical", "ml-2");
+ neg.innerHTML = ``;
+ neg.addEventListener("click", () => fillInFilter(queryName, "false"));
+
+ container.appendChild(pos);
+ container.appendChild(neg);
+ }
+ if (query.string) {
+ const button = document.createElement("button") as HTMLButtonElement;
+ button.type = "button";
+ button.classList.add("button", "~urge", "ml-2");
+ button.innerHTML = `Match Text`;
+
+ // Position cursor between quotes
+ button.addEventListener("click", () => fillInFilter(queryName, `""`, -1));
+
+ container.appendChild(button);
+ }
+ if (query.date) {
+ const onDate = document.createElement("button") as HTMLButtonElement;
+ onDate.type = "button";
+ onDate.classList.add("button", "~urge", "ml-2");
+ onDate.innerHTML = `On Date`;
+ onDate.addEventListener("click", () => fillInFilter(queryName, `"="`, -1));
+
+ const beforeDate = document.createElement("button") as HTMLButtonElement;
+ beforeDate.type = "button";
+ beforeDate.classList.add("button", "~urge", "ml-2");
+ beforeDate.innerHTML = `Before Date`;
+ beforeDate.addEventListener("click", () => fillInFilter(queryName, `"<"`, -1));
+
+ const afterDate = document.createElement("button") as HTMLButtonElement;
+ afterDate.type = "button";
+ afterDate.classList.add("button", "~urge", "ml-2");
+ afterDate.innerHTML = `After Date`;
+ afterDate.addEventListener("click", () => fillInFilter(queryName, `">"`, -1));
+
+ container.appendChild(onDate);
+ container.appendChild(beforeDate);
+ container.appendChild(afterDate);
+ }
+
+ filterList.appendChild(container);
+ }
}
reload = () => {