mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-28 03:50:10 +00:00
implement accounts tab functionality
also: * added a homemade loading animation to buttons * respect disabled invite emails and email address as username
This commit is contained in:
parent
2d443fb50b
commit
88f4de9c46
@ -3,6 +3,7 @@
|
||||
@import "modal.css";
|
||||
@import "dark.css";
|
||||
@import "tooltip.css";
|
||||
@import "loader.css";
|
||||
|
||||
:root {
|
||||
--border-width-default: 2px;
|
||||
@ -233,7 +234,7 @@ sup.\~critical, .text-critical {
|
||||
padding-bottom: 0.1rem;
|
||||
margin-left: 0.5rem;
|
||||
margin-right: 1rem;
|
||||
max-width: 50%;
|
||||
max-width: 75%;
|
||||
}
|
||||
|
||||
.stealth-input-hidden {
|
||||
|
40
css/loader.css
Normal file
40
css/loader.css
Normal file
@ -0,0 +1,40 @@
|
||||
.loader {
|
||||
height: auto;
|
||||
color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
.loader .dot {
|
||||
--diameter: 0.5rem;
|
||||
--radius: calc(var(--diameter) / 2);
|
||||
--deviation: 20%;
|
||||
height: var(--diameter);
|
||||
width: var(--diameter);
|
||||
background-color: var(--color-content);
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
left: calc(50% - var(--radius));
|
||||
animation: osc 1s cubic-bezier(.72,.16,.31,.97) infinite;
|
||||
}
|
||||
.loader.loader-sm .dot {
|
||||
--deviation: 10%;
|
||||
}
|
||||
|
||||
@keyframes osc {
|
||||
25% {
|
||||
left: calc(50% + var(--deviation) - var(--radius));
|
||||
}
|
||||
75% {
|
||||
left: calc(50% - var(--deviation));
|
||||
}
|
||||
0%, 50%, 100% {
|
||||
left: calc(50% - var(--radius));
|
||||
}
|
||||
/*
|
||||
0%, 100% {
|
||||
left: calc(50% - var(--deviation))
|
||||
}
|
||||
50% {
|
||||
left: calc(50% + var(--deviation) - var(--radius));
|
||||
}
|
||||
*/
|
||||
}
|
@ -5,6 +5,10 @@
|
||||
|
||||
<script>
|
||||
window.URLBase = "{{ .urlBase }}";
|
||||
window.notificationsEnabled = {{ .notifications }};
|
||||
window.emailEnabled = {{ .email_enabled }};
|
||||
window.ombiEnabled = {{ .ombiEnabled }};
|
||||
window.usernamesEnabled = {{ .username }};
|
||||
</script>
|
||||
{{ template "header.html" . }}
|
||||
<title>Admin - jfa-go</title>
|
||||
@ -15,15 +19,22 @@
|
||||
<span class="heading">Login</span>
|
||||
<input type="text" class="field input ~neutral !high mt-half mb-1" placeholder="username" id="login-user">
|
||||
<input type="password" class="field input ~neutral !high mb-1" placeholder="password" id="login-password">
|
||||
<input type="submit" class="button ~urge !normal full-width center supra submit" value="Login">
|
||||
<label>
|
||||
<input type="submit" class="unfocused">
|
||||
<span class="button ~urge !normal full-width center supra submit">Login</span>
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
<div id="modal-add-user" class="modal">
|
||||
<form class="modal-content card" id="form-add-user" href="">
|
||||
<span class="heading">New User <span class="modal-close">×</span></span>
|
||||
<input type="text" class="field input ~neutral !high mt-half mb-1" placeholder="username" id="add-user-user">
|
||||
<input type="email" class="field input ~neutral !high mt-half mb-1" placeholder="email address">
|
||||
<input type="password" class="field input ~neutral !high mb-1" placeholder="password" id="add-user-password">
|
||||
<input type="submit" class="button ~urge !normal full-width center supra submit" value="Create">
|
||||
<label>
|
||||
<input type="submit" class="unfocused">
|
||||
<span class="button ~urge !normal full-width center supra submit">Create</span>
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
<div id="modal-about" class="modal">
|
||||
@ -38,7 +49,7 @@
|
||||
</div>
|
||||
<div id="modal-modify-user" class="modal">
|
||||
<form class="modal-content card" id="form-modify-user" href="">
|
||||
<span class="heading"><span id="header-modify-user">Modify Settings</span> <span class="modal-close">×</span></span>
|
||||
<span class="heading">Modify Settings for <span id="header-modify-user"></span> <span class="modal-close">×</span></span>
|
||||
<p class="content">Apply settings from an existing profile, or source them directly from a user.</p>
|
||||
<div class="flex-row mb-1">
|
||||
<label class="flex-row-group mr-1">
|
||||
@ -50,15 +61,15 @@
|
||||
<span class="button ~neutral !normal supra full-width center">User</span>
|
||||
</label>
|
||||
</div>
|
||||
<div id="modify-user-profiles" class="select ~neutral !normal mb-1">
|
||||
<select>
|
||||
<div class="select ~neutral !normal mb-1">
|
||||
<select id="modify-user-profiles">
|
||||
<option>Friends</option>
|
||||
<option>Family</option>
|
||||
<option>Default</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="modify-user-users" class="select ~neutral !normal mb-1 unfocused">
|
||||
<select>
|
||||
<div class="select ~neutral !normal mb-1 unfocused">
|
||||
<select id="modify-user-users">
|
||||
<option>Person</option>
|
||||
<option>Other person</option>
|
||||
</select>
|
||||
@ -67,19 +78,25 @@
|
||||
<input type="checkbox" id="modify-user-homescreen" checked>
|
||||
<span>Apply homescreen layout</span>
|
||||
</label>
|
||||
<input type="submit" class="button ~urge !normal full-width center supra submit" value="Apply">
|
||||
<label>
|
||||
<input type="submit" class="unfocused">
|
||||
<span class="button ~urge !normal full-width center supra submit">Apply</span>
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
<div id="modal-delete-user" class="modal">
|
||||
<form class="modal-content card" id="form-delete-user" href="">
|
||||
<span class="heading"><span id="header-delete-user">Delete User</span> <span class="modal-close">×</span></span>
|
||||
<span class="heading">Delete <span id="header-delete-user"></span> <span class="modal-close">×</span></span>
|
||||
<div class="content mt-half">
|
||||
<label class="switch mb-1">
|
||||
<input type="checkbox" id="delete-user-notify" checked>
|
||||
<span>Send notification email</span>
|
||||
</label>
|
||||
<textarea id="textarea-delete-user" class="textarea full-width ~neutral !normal mb-1" placeholder="Your account has been deleted."></textarea>
|
||||
<input type="submit" class="button ~urge !normal full-width center supra submit" value="Apply">
|
||||
<label>
|
||||
<input type="submit" class="unfocused">
|
||||
<span class="button ~critical !normal full-width center supra submit">Delete</span>
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@ -109,7 +126,10 @@
|
||||
<option>Other person</option>
|
||||
</select>
|
||||
</div>
|
||||
<input type="submit" class="button ~urge !normal full-width center supra submit" value="Submit">
|
||||
<label>
|
||||
<input type="submit" class="unfocused">
|
||||
<span class="button ~urge !normal full-width center supra submit">Submit</span>
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
<div id="notification-box"></div>
|
||||
@ -172,12 +192,14 @@
|
||||
<select id="create-profile">
|
||||
</select>
|
||||
</div>
|
||||
<label class="label supra">Send to</label>
|
||||
<div class="flex-expand mb-1 mt-half">
|
||||
<input type="email" id="create-send-to" class="input ~neutral !normal mr-1" placeholder="example@example.com">
|
||||
<label for="create-send-to-enabled" class="button ~neutral !normal">
|
||||
<input type="checkbox" id="create-send-to-enabled" aria-label="Send to address enabled">
|
||||
</label>
|
||||
<div id="create-send-to-container">
|
||||
<label class="label supra">Send to</label>
|
||||
<div class="flex-expand mb-1 mt-half">
|
||||
<input type="email" id="create-send-to" class="input ~neutral !normal mr-1" placeholder="example@example.com">
|
||||
<label for="create-send-to-enabled" class="button ~neutral !normal">
|
||||
<input type="checkbox" id="create-send-to-enabled" aria-label="Send to address enabled">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<span class="button ~urge !normal supra full-width center lg" id="create-submit">Create</span>
|
||||
</div>
|
||||
@ -202,20 +224,7 @@
|
||||
<th>Last Active</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="accounts-list">
|
||||
<tr>
|
||||
<td><input type="checkbox" value=""></td>
|
||||
<td>Person <span class="chip ~info ml-1">Admin</span></td>
|
||||
<td><i class="icon ri-edit-line"></i><input type="email" class="input ~neutral !normal stealth-input stealth-input-hidden" value="email@addr.ess" readonly></td>
|
||||
<td>13/12/20 00:39</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox" value=""></td>
|
||||
<td>Other person</td>
|
||||
<td><i class="icon ri-edit-line"></i><input type="email" class="input ~neutral !normal stealth-input stealth-input-hidden" value="eee@ma.il" readonly></td>
|
||||
<td>12/12/20 17:46</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody id="accounts-list"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
177
ts/admin.ts
177
ts/admin.ts
@ -2,7 +2,8 @@ import { toggleTheme, loadTheme } from "./modules/theme.js";
|
||||
import { Modal } from "./modules/modal.js";
|
||||
import { Tabs } from "./modules/tabs.js";
|
||||
import { inviteList, createInvite } from "./modules/invites.js";
|
||||
import { _post, notificationBox, whichAnimationEvent } from "./modules/common.js";
|
||||
import { accountsList } from "./modules/accounts.js";
|
||||
import { _post, notificationBox, whichAnimationEvent, toggleLoader } from "./modules/common.js";
|
||||
|
||||
loadTheme();
|
||||
(document.getElementById('button-theme') as HTMLSpanElement).onclick = toggleTheme;
|
||||
@ -13,75 +14,37 @@ window.token = "";
|
||||
|
||||
window.availableProfiles = window.availableProfiles || [];
|
||||
|
||||
// load modals
|
||||
(() => {
|
||||
window.modals = {} as Modals;
|
||||
|
||||
window.modals.login = new Modal(document.getElementById('modal-login'), true);
|
||||
|
||||
window.modals.addUser = new Modal(document.getElementById('modal-add-user'));
|
||||
|
||||
window.modals.about = new Modal(document.getElementById('modal-about'));
|
||||
(document.getElementById('setting-about') as HTMLSpanElement).onclick = window.modals.about.toggle;
|
||||
|
||||
window.modals.modifyUser = new Modal(document.getElementById('modal-modify-user'));
|
||||
|
||||
window.modals.deleteUser = new Modal(document.getElementById('modal-delete-user'));
|
||||
|
||||
window.modals.settingsRestart = new Modal(document.getElementById('modal-restart'));
|
||||
|
||||
window.modals.settingsRefresh = new Modal(document.getElementById('modal-refresh'));
|
||||
|
||||
window.modals.ombiDefaults = new Modal(document.getElementById('modal-ombi-defaults'));
|
||||
document.getElementById('form-ombi-defaults').addEventListener('submit', window.modals.ombiDefaults.close);
|
||||
})();
|
||||
|
||||
var inviteCreator = new createInvite();
|
||||
var accounts = new accountsList();
|
||||
|
||||
window.invites = new inviteList();
|
||||
|
||||
window.notifications = new notificationBox(document.getElementById('notification-box') as HTMLDivElement, 5);
|
||||
|
||||
|
||||
const loadAccounts = function () {
|
||||
const rows: HTMLTableRowElement[] = Array.from(document.getElementById("accounts-list").children);
|
||||
const selectAll = document.getElementById("accounts-select-all") as HTMLInputElement;
|
||||
selectAll.onchange = () => {
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
(rows[i].querySelector("input[type=checkbox]") as HTMLInputElement).checked = selectAll.checked;
|
||||
}
|
||||
}
|
||||
|
||||
const checkAllChecks = (state: boolean): boolean => {
|
||||
let s = true;
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const selectCheck = rows[i].querySelector("input[type=checkbox]") as HTMLInputElement;
|
||||
if (selectCheck.checked != state) {
|
||||
s = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const row = rows[i];
|
||||
const selectCheck = row.querySelector("input[type=checkbox]") as HTMLInputElement;
|
||||
selectCheck.addEventListener("change", () => {
|
||||
if (checkAllChecks(false)) {
|
||||
selectAll.indeterminate = false;
|
||||
selectAll.checked = false;
|
||||
} else if (checkAllChecks(true)) {
|
||||
selectAll.checked = true;
|
||||
selectAll.indeterminate = false;
|
||||
} else {
|
||||
selectAll.indeterminate = true;
|
||||
selectAll.checked = false;
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
const editButton = row.querySelector(".icon") as HTMLElement;
|
||||
const emailInput = row.querySelector(".input") as HTMLInputElement;
|
||||
const outerClickListener = (event: Event) => {
|
||||
if (!(event.target instanceof HTMLElement && (emailInput.contains(event.target) || editButton.contains(event.target)))) {
|
||||
emailInput.classList.toggle('stealth-input-hidden');
|
||||
emailInput.readOnly = !emailInput.readOnly;
|
||||
editButton.classList.toggle('ri-edit-line');
|
||||
editButton.classList.toggle('ri-check-line');
|
||||
document.removeEventListener('click', outerClickListener);
|
||||
}
|
||||
};
|
||||
editButton.onclick = function () {
|
||||
emailInput.classList.toggle('stealth-input-hidden');
|
||||
emailInput.readOnly = !emailInput.readOnly;
|
||||
editButton.classList.toggle('ri-edit-line');
|
||||
editButton.classList.toggle('ri-check-line');
|
||||
if (editButton.classList.contains('ri-check-line')) {
|
||||
document.addEventListener('click', outerClickListener);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const modifySettingsSource = function () {
|
||||
/*const modifySettingsSource = function () {
|
||||
const profile = document.getElementById('radio-use-profile') as HTMLInputElement;
|
||||
const user = document.getElementById('radio-use-user') as HTMLInputElement;
|
||||
const profileSelect = document.getElementById('modify-user-profiles') as HTMLDivElement;
|
||||
@ -92,64 +55,16 @@ const modifySettingsSource = function () {
|
||||
(profile.nextElementSibling as HTMLSpanElement).classList.toggle('!high');
|
||||
profileSelect.classList.toggle('unfocused');
|
||||
userSelect.classList.toggle('unfocused');
|
||||
}
|
||||
|
||||
const radioUseProfile = document.getElementById('radio-use-profile') as HTMLInputElement;
|
||||
radioUseProfile.addEventListener("change", modifySettingsSource);
|
||||
radioUseProfile.checked = true;
|
||||
const radioUseUser = document.getElementById('radio-use-user') as HTMLInputElement;
|
||||
radioUseUser.addEventListener("change", modifySettingsSource);
|
||||
radioUseUser.checked = false;
|
||||
|
||||
const checkDeleteUserNotify = function () {
|
||||
if ((document.getElementById('delete-user-notify') as HTMLInputElement).checked) {
|
||||
document.getElementById('textarea-delete-user').classList.remove('unfocused');
|
||||
} else {
|
||||
document.getElementById('textarea-delete-user').classList.add('unfocused');
|
||||
}
|
||||
};
|
||||
|
||||
(document.getElementById('delete-user-notify') as HTMLInputElement).onchange = checkDeleteUserNotify;
|
||||
checkDeleteUserNotify();
|
||||
|
||||
}*/
|
||||
|
||||
// load tabs
|
||||
window.tabs = new Tabs();
|
||||
for (let tabID of ["invitesTab", "accountsTab", "settingsTab"]) {
|
||||
for (let tabID of ["invitesTab", "settingsTab"]) {
|
||||
window.tabs.addTab(tabID);
|
||||
}
|
||||
window.tabs.addTab("accountsTab", loadAccounts);
|
||||
window.tabs.addTab("accountsTab", null, accounts.reload);
|
||||
|
||||
// load modals
|
||||
(() => {
|
||||
window.modals = {} as Modals;
|
||||
|
||||
window.modals.login = new Modal(document.getElementById('modal-login'), true);
|
||||
|
||||
window.modals.addUser = new Modal(document.getElementById('modal-add-user'));
|
||||
(document.getElementById('accounts-add-user') as HTMLSpanElement).onclick = window.modals.addUser.toggle;
|
||||
document.getElementById('form-add-user').addEventListener('submit', window.modals.addUser.close);
|
||||
|
||||
window.modals.about = new Modal(document.getElementById('modal-about'));
|
||||
(document.getElementById('setting-about') as HTMLSpanElement).onclick = window.modals.about.toggle;
|
||||
|
||||
window.modals.modifyUser = new Modal(document.getElementById('modal-modify-user'));
|
||||
document.getElementById('form-modify-user').addEventListener('submit', window.modals.modifyUser.close);
|
||||
(document.getElementById('accounts-modify-user') as HTMLSpanElement).onclick = window.modals.modifyUser.toggle;
|
||||
|
||||
window.modals.deleteUser = new Modal(document.getElementById('modal-delete-user'));
|
||||
document.getElementById('form-delete-user').addEventListener('submit', window.modals.deleteUser.close);
|
||||
(document.getElementById('accounts-delete-user') as HTMLSpanElement).onclick = window.modals.deleteUser.toggle;
|
||||
|
||||
window.modals.settingsRestart = new Modal(document.getElementById('modal-restart'));
|
||||
|
||||
window.modals.settingsRefresh = new Modal(document.getElementById('modal-refresh'));
|
||||
|
||||
window.modals.ombiDefaults = new Modal(document.getElementById('modal-ombi-defaults'));
|
||||
document.getElementById('form-ombi-defaults').addEventListener('submit', window.modals.ombiDefaults.close);
|
||||
})();
|
||||
|
||||
function login(username: string, password: string) {
|
||||
function login(username: string, password: string, run?: (state?: number) => void) {
|
||||
const req = new XMLHttpRequest();
|
||||
req.responseType = 'json';
|
||||
let url = window.URLBase;
|
||||
@ -183,41 +98,28 @@ function login(username: string, password: string) {
|
||||
window.token = data["token"];
|
||||
window.modals.login.close();
|
||||
window.invites.reload();
|
||||
setInterval(window.invites.reload, 30*1000);
|
||||
accounts.reload();
|
||||
setInterval(() => { window.invites.reload(); accounts.reload(); }, 30*1000);
|
||||
document.getElementById("logout-button").classList.remove("unfocused");
|
||||
|
||||
/*generateInvites();
|
||||
setInterval((): void => generateInvites(), 60 * 1000);
|
||||
addOptions(30, document.getElementById('days') as HTMLSelectElement);
|
||||
addOptions(24, document.getElementById('hours') as HTMLSelectElement);
|
||||
const minutes = document.getElementById('minutes') as HTMLSelectElement;
|
||||
addOptions(59, minutes);
|
||||
minutes.value = "30";
|
||||
checkDuration();
|
||||
if (modal) {
|
||||
window.Modals.login.hide();
|
||||
}
|
||||
Focus(document.getElementById('logoutButton'));
|
||||
}
|
||||
if (run) {
|
||||
run(+this.status);
|
||||
}*/
|
||||
}
|
||||
if (run) { run(+this.status); }
|
||||
}
|
||||
};
|
||||
req.send();
|
||||
}
|
||||
|
||||
document.getElementById('form-login').addEventListener('submit', (event: Event) => {
|
||||
(document.getElementById('form-login') as HTMLFormElement).onsubmit = (event: SubmitEvent) => {
|
||||
event.preventDefault();
|
||||
const button = (event.target as HTMLElement).querySelector(".submit") as HTMLSpanElement;
|
||||
const username = (document.getElementById("login-user") as HTMLInputElement).value;
|
||||
const password = (document.getElementById("login-password") as HTMLInputElement).value;
|
||||
if (!username || !password) {
|
||||
window.notifications.customError("loginError", "The username and/or password were left blank.");
|
||||
return;
|
||||
}
|
||||
login(username, password);
|
||||
});
|
||||
toggleLoader(button);
|
||||
login(username, password, () => toggleLoader(button));
|
||||
};
|
||||
|
||||
login("", "");
|
||||
|
||||
@ -228,3 +130,4 @@ login("", "");
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
|
409
ts/modules/accounts.ts
Normal file
409
ts/modules/accounts.ts
Normal file
@ -0,0 +1,409 @@
|
||||
import { _get, _post, _delete, toggleLoader } from "../modules/common.js";
|
||||
|
||||
interface User {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string | undefined;
|
||||
last_active: string;
|
||||
admin: boolean;
|
||||
}
|
||||
|
||||
class user implements User {
|
||||
private _row: HTMLTableRowElement;
|
||||
private _check: HTMLInputElement;
|
||||
private _username: HTMLSpanElement;
|
||||
private _admin: HTMLSpanElement;
|
||||
private _email: HTMLInputElement;
|
||||
private _emailAddress: string;
|
||||
private _emailEditButton: HTMLElement;
|
||||
private _lastActive: HTMLTableDataCellElement;
|
||||
id: string;
|
||||
private _selected: boolean;
|
||||
|
||||
get selected(): boolean { return this._selected; }
|
||||
set selected(state: boolean) {
|
||||
this._selected = state;
|
||||
this._check.checked = state;
|
||||
state ? document.dispatchEvent(this._checkEvent) : document.dispatchEvent(this._uncheckEvent);
|
||||
}
|
||||
|
||||
get name(): string { return this._username.textContent; }
|
||||
set name(value: string) { this._username.textContent = value; }
|
||||
|
||||
get admin(): boolean { return this._admin.classList.contains("chip"); }
|
||||
set admin(state: boolean) {
|
||||
if (state) {
|
||||
this._admin.classList.add("chip", "~info", "ml-1");
|
||||
this._admin.textContent = "Admin";
|
||||
} else {
|
||||
this._admin.classList.remove("chip", "~info", "ml-1");
|
||||
this._admin.textContent = ""
|
||||
}
|
||||
}
|
||||
|
||||
get email(): string { return this._emailAddress; }
|
||||
set email(value: string) { this._email.value = value; this._emailAddress = value; }
|
||||
|
||||
get last_active(): string { return this._lastActive.textContent; }
|
||||
set last_active(value: string) { this._lastActive.textContent = value; }
|
||||
|
||||
private _checkEvent = new CustomEvent("accountCheckEvent");
|
||||
private _uncheckEvent = new CustomEvent("accountUncheckEvent");
|
||||
|
||||
constructor(user: User) {
|
||||
this._row = document.createElement("tr") as HTMLTableRowElement;
|
||||
this._row.innerHTML = `
|
||||
<td><input type="checkbox" value=""></td>
|
||||
<td><span class="accounts-username"></span> <span class="accounts-admin"></span></td>
|
||||
<td><i class="icon ri-edit-line accounts-email-edit"></i><input type="email" class="input ~neutral !normal stealth-input stealth-input-hidden accounts-email" readonly></td>
|
||||
<td class="accounts-last-active"></td>
|
||||
`;
|
||||
this._check = this._row.querySelector("input[type=checkbox]") as HTMLInputElement;
|
||||
this._username = this._row.querySelector(".accounts-username") as HTMLSpanElement;
|
||||
this._admin = this._row.querySelector(".accounts-admin") as HTMLSpanElement;
|
||||
this._email = this._row.querySelector(".accounts-email") as HTMLInputElement;
|
||||
this._emailEditButton = this._row.querySelector(".accounts-email-edit") as HTMLElement;
|
||||
this._lastActive = this._row.querySelector(".accounts-last-active") as HTMLTableDataCellElement;
|
||||
this._check.onchange = () => { this.selected = this._check.checked; }
|
||||
|
||||
const toggleStealthInput = () => {
|
||||
this._email.classList.toggle("stealth-input-hidden");
|
||||
this._email.readOnly = !this._email.readOnly;
|
||||
this._emailEditButton.classList.toggle("ri-check-line");
|
||||
this._emailEditButton.classList.toggle("ri-edit-line");
|
||||
};
|
||||
const outerClickListener = (event: Event) => {
|
||||
if (!(event.target instanceof HTMLElement && (this._email.contains(event.target) || this._emailEditButton.contains(event.target)))) {
|
||||
toggleStealthInput();
|
||||
this.email = this.email;
|
||||
document.removeEventListener("click", outerClickListener);
|
||||
}
|
||||
};
|
||||
this._emailEditButton.onclick = () => {
|
||||
if (this._email.classList.contains("stealth-input-hidden")) {
|
||||
document.addEventListener('click', outerClickListener);
|
||||
} else {
|
||||
this._updateEmail();
|
||||
document.removeEventListener('click', outerClickListener);
|
||||
}
|
||||
toggleStealthInput();
|
||||
};
|
||||
|
||||
this.update(user);
|
||||
}
|
||||
|
||||
private _updateEmail = () => {
|
||||
let oldEmail = this.email;
|
||||
this.email = this._email.value;
|
||||
let send = {};
|
||||
send[this.id] = this.email;
|
||||
_post("/users/emails", send, (req: XMLHttpRequest) => {
|
||||
if (req.readyState == 4) {
|
||||
if (req.status == 200) {
|
||||
window.notifications.customPositive("emailChanged", "Success:", `Changed email address of "${this.name}".`);
|
||||
} else {
|
||||
this.email = oldEmail;
|
||||
window.notifications.customError("emailChanged", `Couldn't change email address of "${this.name}".`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
update = (user: User) => {
|
||||
this.id = user.id;
|
||||
this.name = user.name;
|
||||
this.email = user.email || "";
|
||||
this.last_active = user.last_active;
|
||||
this.admin = user.admin;
|
||||
}
|
||||
|
||||
asElement = (): HTMLTableRowElement => { return this._row; }
|
||||
remove = () => {
|
||||
if (this.selected) {
|
||||
document.dispatchEvent(this._uncheckEvent);
|
||||
}
|
||||
this._row.remove();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
export class accountsList {
|
||||
private _table = document.getElementById("accounts-list") as HTMLTableSectionElement;
|
||||
|
||||
private _addUserButton = document.getElementById("accounts-add-user") as HTMLSpanElement;
|
||||
private _deleteUser = document.getElementById("accounts-delete-user") as HTMLSpanElement;
|
||||
private _deleteNotify = document.getElementById("delete-user-notify") as HTMLInputElement;
|
||||
private _deleteReason = document.getElementById("textarea-delete-user") as HTMLTextAreaElement;
|
||||
private _modifySettings = document.getElementById("accounts-modify-user") as HTMLSpanElement;
|
||||
private _modifySettingsProfile = document.getElementById("radio-use-profile") as HTMLInputElement;
|
||||
private _modifySettingsUser = document.getElementById("radio-use-user") as HTMLInputElement;
|
||||
private _profileSelect = document.getElementById("modify-user-profiles") as HTMLSelectElement;
|
||||
private _userSelect = document.getElementById("modify-user-users") as HTMLSelectElement;
|
||||
|
||||
private _selectAll = document.getElementById("accounts-select-all") as HTMLInputElement;
|
||||
private _users: { [id: string]: user };
|
||||
private _checkCount: number = 0;
|
||||
|
||||
private _addUserForm = document.getElementById("form-add-user") as HTMLFormElement;
|
||||
private _addUserName = this._addUserForm.querySelector("input[type=text]") as HTMLInputElement;
|
||||
private _addUserEmail = this._addUserForm.querySelector("input[type=email]") as HTMLInputElement;
|
||||
private _addUserPassword = this._addUserForm.querySelector("input[type=password]") as HTMLInputElement;
|
||||
|
||||
get selectAll(): boolean { return this._selectAll.checked; }
|
||||
set selectAll(state: boolean) {
|
||||
for (let id in this._users) {
|
||||
this._users[id].selected = state;
|
||||
}
|
||||
this._selectAll.checked = state;
|
||||
this._selectAll.indeterminate = false;
|
||||
state ? this._checkCount = Object.keys(this._users).length : 0;
|
||||
|
||||
}
|
||||
|
||||
add = (u: User) => {
|
||||
let domAccount = new user(u);
|
||||
this._users[u.id] = domAccount;
|
||||
this._table.appendChild(domAccount.asElement());
|
||||
}
|
||||
|
||||
private _checkCheckCount = () => {
|
||||
if (this._checkCount == 0) {
|
||||
this._selectAll.indeterminate = false;
|
||||
this._selectAll.checked = false;
|
||||
this._modifySettings.classList.add("unfocused");
|
||||
this._deleteUser.classList.add("unfocused");
|
||||
} else {
|
||||
if (this._checkCount == Object.keys(this._users).length) {
|
||||
this._selectAll.checked = true;
|
||||
this._selectAll.indeterminate = false;
|
||||
} else {
|
||||
this._selectAll.checked = false;
|
||||
this._selectAll.indeterminate = true;
|
||||
}
|
||||
this._modifySettings.classList.remove("unfocused");
|
||||
this._deleteUser.classList.remove("unfocused");
|
||||
(this._checkCount == 1) ? this._deleteUser.textContent = "Delete User" : this._deleteUser.textContent = "Delete Users";
|
||||
}
|
||||
}
|
||||
|
||||
private _genCountString = (): string => { return `${this._checkCount} user${(this._checkCount > 1) ? "s" : ""}`; }
|
||||
|
||||
private _collectUsers = (): string[] => {
|
||||
let list: string[] = [];
|
||||
for (let id in this._users) {
|
||||
if (this._users[id].selected) { list.push(id); }
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private _addUser = (event: Event) => {
|
||||
event.preventDefault();
|
||||
const button = this._addUserForm.querySelector("span.submit") as HTMLSpanElement;
|
||||
const send = {
|
||||
"username": this._addUserName.value,
|
||||
"email": this._addUserEmail.value,
|
||||
"password": this._addUserPassword.value
|
||||
};
|
||||
for (let field in send) {
|
||||
if (!send[field]) {
|
||||
window.notifications.customError("addUserBlankField", "Fields were left blank.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
toggleLoader(button);
|
||||
_post("/users", send, (req: XMLHttpRequest) => {
|
||||
if (req.readyState == 4) {
|
||||
toggleLoader(button);
|
||||
if (req.status == 200) {
|
||||
window.notifications.customPositive("addUser", "Success:", `user "${send['username']}" created.`);
|
||||
}
|
||||
this.reload();
|
||||
window.modals.addUser.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
deleteUsers = () => {
|
||||
const modalHeader = document.getElementById("header-delete-user");
|
||||
modalHeader.textContent = this._genCountString();
|
||||
let list = this._collectUsers();
|
||||
const form = document.getElementById("form-delete-user") as HTMLFormElement;
|
||||
const button = form.querySelector("span.submit") as HTMLSpanElement;
|
||||
this._deleteNotify.checked = false;
|
||||
this._deleteReason.value = "";
|
||||
this._deleteReason.classList.add("unfocused");
|
||||
form.onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
toggleLoader(button);
|
||||
let send = {
|
||||
"users": list,
|
||||
"notify": this._deleteNotify.checked,
|
||||
"reason": this._deleteNotify ? this._deleteReason.value : ""
|
||||
};
|
||||
_delete("/users", send, (req: XMLHttpRequest) => {
|
||||
if (req.readyState == 4) {
|
||||
toggleLoader(button);
|
||||
window.modals.deleteUser.close();
|
||||
if (req.status != 200 && req.status != 204) {
|
||||
let errorMsg = "Failed (check console/logs).";
|
||||
if (!("error" in req.response)) {
|
||||
errorMsg = "Partial failure (check console/logs).";
|
||||
}
|
||||
window.notifications.customError("deleteUserError", errorMsg);
|
||||
} else {
|
||||
window.notifications.customPositive("deleteUserSuccess", "Success:", `deleted ${this._genCountString()}.`);
|
||||
}
|
||||
this.reload();
|
||||
}
|
||||
});
|
||||
};
|
||||
window.modals.deleteUser.show();
|
||||
}
|
||||
|
||||
modifyUsers = () => {
|
||||
const modalHeader = document.getElementById("header-modify-user");
|
||||
modalHeader.textContent = this._genCountString();
|
||||
let list = this._collectUsers();
|
||||
(() => {
|
||||
let innerHTML = "";
|
||||
for (const profile of window.availableProfiles) {
|
||||
innerHTML += `<option value="${profile}">${profile}</option>`;
|
||||
}
|
||||
this._profileSelect.innerHTML = innerHTML;
|
||||
})();
|
||||
|
||||
(() => {
|
||||
let innerHTML = "";
|
||||
for (let id in this._users) {
|
||||
innerHTML += `<option value="${id}">${this._users[id].name}</option>`;
|
||||
}
|
||||
this._userSelect.innerHTML = innerHTML;
|
||||
})();
|
||||
|
||||
const form = document.getElementById("form-modify-user") as HTMLFormElement;
|
||||
const button = form.querySelector("span.submit") as HTMLSpanElement;
|
||||
this._modifySettingsProfile.checked = true;
|
||||
this._modifySettingsUser.checked = false;
|
||||
form.onsubmit = (event: Event) => {
|
||||
event.preventDefault();
|
||||
toggleLoader(button);
|
||||
let send = {
|
||||
"apply_to": list,
|
||||
"homescreen": (document.getElementById("modify-user-homescreen") as HTMLInputElement).checked
|
||||
};
|
||||
if (this._modifySettingsProfile.checked && !this._modifySettingsUser.checked) {
|
||||
send["from"] = "profile";
|
||||
send["profile"] = this._profileSelect.value;
|
||||
} else if (this._modifySettingsUser.checked && !this._modifySettingsProfile.checked) {
|
||||
send["from"] = "user";
|
||||
send["id"] = this._userSelect.value;
|
||||
}
|
||||
_post("/users/settings", send, (req: XMLHttpRequest) => {
|
||||
if (req.readyState == 4) {
|
||||
toggleLoader(button);
|
||||
if (req.status == 500) {
|
||||
let response = JSON.parse(req.response);
|
||||
let errorMsg = "";
|
||||
if ("homescreen" in response && "policy" in response) {
|
||||
const homescreen = Object.keys(response["homescreen"]).length;
|
||||
const policy = Object.keys(response["policy"]).length;
|
||||
if (homescreen != 0 && policy == 0) {
|
||||
errorMsg = "Settings were applied, but applying homescreen layout may have failed.";
|
||||
} else if (policy != 0 && homescreen == 0) {
|
||||
errorMsg = "Homescreen layout was applied, but applying settings may have failed.";
|
||||
} else if (policy != 0 && homescreen != 0) {
|
||||
errorMsg = "Application failed.";
|
||||
}
|
||||
} else if ("error" in response) {
|
||||
errorMsg = response["error"];
|
||||
}
|
||||
window.notifications.customError("modifySettingsError", errorMsg);
|
||||
} else if (req.status == 200 || req.status == 204) {
|
||||
window.notifications.customPositive("modifySettingsSuccess", "Success:", `applied settings to ${this._genCountString()}.`);
|
||||
}
|
||||
this.reload();
|
||||
window.modals.modifyUser.close();
|
||||
}
|
||||
});
|
||||
};
|
||||
window.modals.modifyUser.show();
|
||||
}
|
||||
|
||||
|
||||
|
||||
constructor() {
|
||||
this._users = {};
|
||||
this._selectAll.checked = false;
|
||||
this._selectAll.onchange = () => { this.selectAll = this._selectAll.checked };
|
||||
document.addEventListener("accountCheckEvent", () => { this._checkCount++; this._checkCheckCount(); });
|
||||
document.addEventListener("accountUncheckEvent", () => { this._checkCount--; this._checkCheckCount(); });
|
||||
this._addUserButton.onclick = window.modals.addUser.toggle;
|
||||
this._addUserForm.addEventListener("submit", this._addUser);
|
||||
|
||||
this._deleteNotify.onchange = () => {
|
||||
if (this._deleteNotify.checked) {
|
||||
this._deleteReason.classList.remove("unfocused");
|
||||
} else {
|
||||
this._deleteReason.classList.add("unfocused");
|
||||
}
|
||||
};
|
||||
this._modifySettings.onclick = this.modifyUsers;
|
||||
this._modifySettings.classList.add("unfocused");
|
||||
const checkSource = () => {
|
||||
const profileSpan = this._modifySettingsProfile.nextElementSibling as HTMLSpanElement;
|
||||
const userSpan = this._modifySettingsUser.nextElementSibling as HTMLSpanElement;
|
||||
if (this._modifySettingsProfile.checked) {
|
||||
this._userSelect.parentElement.classList.add("unfocused");
|
||||
this._profileSelect.parentElement.classList.remove("unfocused")
|
||||
profileSpan.classList.add("!high");
|
||||
profileSpan.classList.remove("!normal");
|
||||
userSpan.classList.remove("!high");
|
||||
userSpan.classList.add("!normal");
|
||||
} else {
|
||||
this._userSelect.parentElement.classList.remove("unfocused");
|
||||
this._profileSelect.parentElement.classList.add("unfocused");
|
||||
userSpan.classList.add("!high");
|
||||
userSpan.classList.remove("!normal");
|
||||
profileSpan.classList.remove("!high");
|
||||
profileSpan.classList.add("!normal");
|
||||
}
|
||||
};
|
||||
this._modifySettingsProfile.onchange = checkSource;
|
||||
this._modifySettingsUser.onchange = checkSource;
|
||||
|
||||
this._deleteUser.onclick = this.deleteUsers;
|
||||
this._deleteUser.classList.add("unfocused");
|
||||
|
||||
if (!window.usernameEnabled) {
|
||||
this._addUserName.classList.add("unfocused");
|
||||
this._addUserName = this._addUserEmail;
|
||||
}
|
||||
/*if (!window.emailEnabled) {
|
||||
this._deleteNotify.parentElement.classList.add("unfocused");
|
||||
this._deleteNotify.checked = false;
|
||||
}*/
|
||||
}
|
||||
|
||||
reload = () => _get("/users", null, (req: XMLHttpRequest) => {
|
||||
if (req.readyState == 4 && req.status == 200) {
|
||||
// same method as inviteList.reload()
|
||||
let accountsOnDOM: { [id: string]: boolean } = {};
|
||||
for (let id in this._users) { accountsOnDOM[id] = true; }
|
||||
for (let u of (req.response["users"] as User[])) {
|
||||
if (u.id in this._users) {
|
||||
this._users[u.id].update(u);
|
||||
delete accountsOnDOM[u.id];
|
||||
} else {
|
||||
this.add(u);
|
||||
}
|
||||
}
|
||||
for (let id in accountsOnDOM) {
|
||||
this._users[id].remove();
|
||||
delete this._users[id];
|
||||
}
|
||||
this._checkCheckCount;
|
||||
}
|
||||
})
|
||||
}
|
@ -124,6 +124,7 @@ export function toClipboard (str: string) {
|
||||
export class notificationBox implements NotificationBox {
|
||||
private _box: HTMLDivElement;
|
||||
private _errorTypes: { [type: string]: boolean } = {};
|
||||
private _positiveTypes: { [type: string]: boolean } = {};
|
||||
timeout: number;
|
||||
constructor(box: HTMLDivElement, timeout?: number) { this._box = box; this.timeout = timeout || 5; }
|
||||
|
||||
@ -139,7 +140,19 @@ export class notificationBox implements NotificationBox {
|
||||
return noti;
|
||||
}
|
||||
|
||||
connectionError = () => { this.customError("connectionError", "Couldn't connect to jfa-go"); }
|
||||
private _positive = (bold: string, message: string): HTMLElement => {
|
||||
const noti = document.createElement('aside');
|
||||
noti.classList.add("aside", "~positive", "!normal", "mt-half", "notification-positive");
|
||||
noti.innerHTML = `<strong>${bold}</strong> ${message}`;
|
||||
const closeButton = document.createElement('span') as HTMLSpanElement;
|
||||
closeButton.classList.add("button", "~positive", "!low", "ml-1");
|
||||
closeButton.innerHTML = `<i class="icon ri-close-line"></i>`;
|
||||
closeButton.onclick = () => { this._box.removeChild(noti); };
|
||||
noti.appendChild(closeButton);
|
||||
return noti;
|
||||
}
|
||||
|
||||
connectionError = () => { this.customError("connectionError", "Couldn't connect to jfa-go."); }
|
||||
|
||||
customError = (type: string, message: string) => {
|
||||
this._errorTypes[type] = this._errorTypes[type] || false;
|
||||
@ -153,6 +166,19 @@ export class notificationBox implements NotificationBox {
|
||||
this._errorTypes[type] = true;
|
||||
setTimeout(() => { if (this._box.contains(noti)) { this._box.removeChild(noti); this._errorTypes[type] = false; } }, this.timeout*1000);
|
||||
}
|
||||
|
||||
customPositive = (type: string, bold: string, message: string) => {
|
||||
this._positiveTypes[type] = this._positiveTypes[type] || false;
|
||||
const noti = this._positive(bold, message);
|
||||
noti.classList.add("positive-" + type);
|
||||
const previousNoti: HTMLElement | undefined = this._box.querySelector("aside.positive-" + type);
|
||||
if (this._positiveTypes[type] && previousNoti !== undefined && previousNoti != null) {
|
||||
previousNoti.remove();
|
||||
}
|
||||
this._box.appendChild(noti);
|
||||
this._positiveTypes[type] = true;
|
||||
setTimeout(() => { if (this._box.contains(noti)) { this._box.removeChild(noti); this._positiveTypes[type] = false; } }, this.timeout*1000);
|
||||
}
|
||||
}
|
||||
|
||||
export const whichAnimationEvent = () => {
|
||||
@ -162,3 +188,18 @@ export const whichAnimationEvent = () => {
|
||||
}
|
||||
return "webkitAnimationEnd";
|
||||
}
|
||||
|
||||
export function toggleLoader(el: HTMLElement, small: boolean = true) {
|
||||
if (el.classList.contains("loader")) {
|
||||
el.classList.remove("loader");
|
||||
el.classList.remove("loader-sm");
|
||||
const dot = el.querySelector("span.dot");
|
||||
if (dot) { dot.remove(); }
|
||||
} else {
|
||||
el.classList.add("loader");
|
||||
if (small) { el.classList.add("loader-sm"); }
|
||||
const dot = document.createElement("span") as HTMLSpanElement;
|
||||
dot.classList.add("dot")
|
||||
el.appendChild(dot);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { _get, _post, _delete, toClipboard } from "../modules/common.js";
|
||||
import { _get, _post, _delete, toClipboard, toggleLoader } from "../modules/common.js";
|
||||
|
||||
export class DOMInvite implements Invite {
|
||||
// TODO
|
||||
@ -390,8 +390,10 @@ export class inviteList implements inviteList {
|
||||
reload = () => _get("/invites", null, (req: XMLHttpRequest) => {
|
||||
if (req.readyState == 4) {
|
||||
let data = req.response;
|
||||
window.availableProfiles = data["profiles"];
|
||||
document.dispatchEvent(this._profileLoadEvent);
|
||||
if (req.status == 200) {
|
||||
window.availableProfiles = data["profiles"];
|
||||
document.dispatchEvent(this._profileLoadEvent);
|
||||
}
|
||||
if (data["invites"] === undefined || data["invites"] == null || data["invites"].length == 0) {
|
||||
this.empty = true;
|
||||
return;
|
||||
@ -446,8 +448,13 @@ export class createInvite {
|
||||
private _uses = document.getElementById('create-uses') as HTMLInputElement;
|
||||
private _infUses = document.getElementById("create-inf-uses") as HTMLInputElement;
|
||||
private _infUsesWarning = document.getElementById('create-inf-uses-warning') as HTMLParagraphElement;
|
||||
private _createButton = document.getElementById("create-submit") as HTMLInputElement; // Actually a <span> but this allows "disabled"
|
||||
private _createButton = document.getElementById("create-submit") as HTMLSpanElement;
|
||||
private _profile = document.getElementById("create-profile") as HTMLSelectElement;
|
||||
|
||||
private _days = document.getElementById("create-days") as HTMLSelectElement;
|
||||
private _hours = document.getElementById("create-hours") as HTMLSelectElement;
|
||||
private _minutes = document.getElementById("create-minutes") as HTMLSelectElement;
|
||||
|
||||
// Broadcast when new invite created
|
||||
private _newInviteEvent = new CustomEvent("newInviteEvent");
|
||||
private _firstLoad = true;
|
||||
@ -503,28 +510,34 @@ export class createInvite {
|
||||
set uses(n: number) { this._uses.valueAsNumber = n; }
|
||||
|
||||
private _checkDurationValidity = () => {
|
||||
this._createButton.disabled = (this.days + this.hours + this.minutes == 0);
|
||||
if (this.days + this.hours + this.minutes == 0) {
|
||||
this._createButton.setAttribute("disabled", "");
|
||||
this._createButton.onclick = null;
|
||||
} else {
|
||||
this._createButton.removeAttribute("disabled");
|
||||
this._createButton.onclick = this.create;
|
||||
}
|
||||
}
|
||||
|
||||
get days(): number {
|
||||
return +(document.getElementById("create-days") as HTMLSelectElement).value;
|
||||
return +this._days.value;
|
||||
}
|
||||
set days(n: number) {
|
||||
(document.getElementById("create-days") as HTMLSelectElement).value = ""+n;
|
||||
this._days.value = ""+n;
|
||||
this._checkDurationValidity();
|
||||
}
|
||||
get hours(): number {
|
||||
return +(document.getElementById("create-hours") as HTMLSelectElement).value;
|
||||
return +this._hours.value;
|
||||
}
|
||||
set hours(n: number) {
|
||||
(document.getElementById("create-hours") as HTMLSelectElement).value = ""+n;
|
||||
this._hours.value = ""+n;
|
||||
this._checkDurationValidity();
|
||||
}
|
||||
get minutes(): number {
|
||||
return +(document.getElementById("create-minutes") as HTMLSelectElement).value;
|
||||
return +this._minutes.value;
|
||||
}
|
||||
set minutes(n: number) {
|
||||
(document.getElementById("create-minutes") as HTMLSelectElement).value = ""+n;
|
||||
this._minutes.value = ""+n;
|
||||
this._checkDurationValidity();
|
||||
}
|
||||
|
||||
@ -559,6 +572,7 @@ export class createInvite {
|
||||
}
|
||||
|
||||
create = () => {
|
||||
toggleLoader(this._createButton);
|
||||
let send = {
|
||||
"days": this.days,
|
||||
"hours": this.hours,
|
||||
@ -570,8 +584,11 @@ export class createInvite {
|
||||
"profile": this.profile
|
||||
};
|
||||
_post("/invites", send, (req: XMLHttpRequest) => {
|
||||
if (req.readyState == 4 && (req.status == 200 || req.status == 204)) {
|
||||
document.dispatchEvent(this._newInviteEvent);
|
||||
if (req.readyState == 4) {
|
||||
if (req.status == 200 || req.status == 204) {
|
||||
document.dispatchEvent(this._newInviteEvent);
|
||||
}
|
||||
toggleLoader(this._createButton);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -588,7 +605,15 @@ export class createInvite {
|
||||
this._createButton.onclick = this.create;
|
||||
this.sendTo = "";
|
||||
this.uses = 1;
|
||||
|
||||
this._days.onchange = this._checkDurationValidity;
|
||||
this._hours.onchange = this._checkDurationValidity;
|
||||
this._minutes.onchange = this._checkDurationValidity;
|
||||
document.addEventListener("profileLoadEvent", () => { this.loadProfiles(); }, false);
|
||||
|
||||
if (!window.emailEnabled) {
|
||||
document.getElementById("create-send-to-container").classList.add("unfocused");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,14 +20,14 @@ export class Tabs implements Tabs {
|
||||
for (let t of this.tabs) {
|
||||
if (t.tabID == tabID) {
|
||||
t.buttonEl.classList.add("active", "~urge");
|
||||
t.preFunc();
|
||||
if (t.preFunc) { t.preFunc(); }
|
||||
t.tabEl.classList.remove("unfocused");
|
||||
if (t.postFunc) { t.postFunc(); }
|
||||
} else {
|
||||
t.buttonEl.classList.remove("active");
|
||||
t.buttonEl.classList.remove("~urge");
|
||||
t.tabEl.classList.add("unfocused");
|
||||
}
|
||||
t.postFunc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,10 @@ declare interface Window {
|
||||
cssFile: string;
|
||||
availableProfiles: string[];
|
||||
jfUsers: Array<Object>;
|
||||
notifications_enabled: boolean;
|
||||
notificationsEnabled: boolean;
|
||||
emailEnabled: boolean;
|
||||
ombiEnabled: boolean;
|
||||
usernameEnabled: boolean;
|
||||
token: string;
|
||||
buttonWidth: number;
|
||||
transitionEvent: string;
|
||||
@ -29,6 +32,7 @@ declare interface Window {
|
||||
declare interface NotificationBox {
|
||||
connectionError: () => void;
|
||||
customError: (type: string, message: string) => void;
|
||||
customPositive: (type: string, bold: string, message: string) => void;
|
||||
}
|
||||
|
||||
declare interface Tabs {
|
||||
@ -77,5 +81,9 @@ interface inviteList {
|
||||
reload: () => void;
|
||||
}
|
||||
|
||||
declare interface SubmitEvent extends Event {
|
||||
submitter: HTMLInputElement;
|
||||
}
|
||||
|
||||
declare var config: Object;
|
||||
declare var modifiedConfig: Object;
|
||||
|
Loading…
Reference in New Issue
Block a user