mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-22 17:10:10 +00:00
accounts: add list of available filters, fix deletion of existing date filters
The "Filters" button gives a list of filterable fields, and buttons to select the type, including true/false, text match, and on/before/after a date. When clicked, the appropriate values are put in the search box and the cursor is placed if any input is needed. Dates and strings are also now matched correctly, and case-insensitively when deleting a filter.
This commit is contained in:
parent
0e241f56fb
commit
76bb95098c
@ -579,11 +579,19 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="tab-accounts" class="unfocused">
|
||||
<div class="card @low dark:~d_neutral accounts mb-4">
|
||||
<div class="card @low dark:~d_neutral accounts mb-4 overflow-visible">
|
||||
<div class="flex-expand row">
|
||||
<div class="row">
|
||||
<div class="row w-6/12">
|
||||
<span class="text-3xl font-bold mr-2 col">{{ .strings.accounts }}</span>
|
||||
<input type="search" class="col sm field ~neutral @low input search ml-2 mr-2" id="accounts-search" placeholder="{{ .strings.search }}">
|
||||
<div id="accounts-filter-dropdown" class="col sm dropdown pb-0i" tabindex="0">
|
||||
<span class="h-100 sm button ~neutral @low center mb-2" id="accounts-filter-button">{{ .strings.filters }}</span>
|
||||
<div class="dropdown-display">
|
||||
<div class="card ~neutral @low mt-2" id="accounts-filter-list">
|
||||
<p class="supra pb-2">{{ .strings.filters }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="col sm button ~neutral @low center mb-2" id="accounts-add-user">{{ .quantityStrings.addUser.Singular }}</span>
|
||||
|
@ -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": {
|
||||
|
@ -790,101 +790,108 @@ 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 = `<span class="mr-2">${query.name}</span>`;
|
||||
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 = `<i class="ri-checkbox-circle-fill"></i>`;
|
||||
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 = `<i class="ri-close-circle-fill"></i>`;
|
||||
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 = `<i class="ri-equal-line mr-2"></i>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 = `<i class="ri-calendar-check-line mr-2"></i>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 = `<i class="ri-calendar-check-line mr-2"></i>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 = `<i class="ri-calendar-check-line mr-2"></i>After Date`;
|
||||
afterDate.addEventListener("click", () => fillInFilter(queryName, `">"`, -1));
|
||||
|
||||
container.appendChild(onDate);
|
||||
container.appendChild(beforeDate);
|
||||
container.appendChild(afterDate);
|
||||
}
|
||||
|
||||
filterList.appendChild(container);
|
||||
}
|
||||
}
|
||||
|
||||
reload = () => {
|
||||
|
Loading…
Reference in New Issue
Block a user