switch to DOM based variant adding

tailwind
Harvey Tindall 3 years ago
parent 0f0355fd01
commit acc8892f26
No known key found for this signature in database
GPG Key ID: BBC65952848FB1A2

@ -54,12 +54,14 @@ ifeq ($(DEBUG), on)
# jank
COPYTS := rm -r $(DATA)/web/js/ts; cp -r tempts $(DATA)/web/js
UNCSS := cp $(DATA)/web/css/bundle.css $(DATA)/bundle.css
TAILWIND := --content ""
else
LDFLAGS := -s -w $(LDFLAGS)
SOURCEMAP :=
COPYTS :=
TYPECHECK :=
UNCSS := npx uncss $(DATA)/crash.html --csspath web/css --output $(DATA)/bundle.css
TAILWIND :=
endif
RACE ?= off
@ -123,7 +125,8 @@ bundle-css:
-mkdir -p $(DATA)/web/css
$(info bundling css)
$(ESBUILD) --bundle css/base.css --outfile=$(DATA)/web/css/bundle.css --external:remixicon.css --minify
npx tailwindcss -i $(DATA)/web/css/bundle.css -o $(DATA)/web/css/bundle.css
npx tailwindcss -i $(DATA)/web/css/bundle.css -o $(DATA)/web/css/bundle.css $(TAILWIND)
# npx postcss -o $(DATA)/web/css/bundle.css $(DATA)/web/css/bundle.css
inline:
cp html/crash.html $(DATA)/crash.html
@ -135,7 +138,7 @@ variants-html:
$(info copying html)
cp -r html $(DATA)/
$(info adding dark variants to html)
scripts/dark-variant.sh html $(DATA)/html
node scripts/missing-colors.js html $(DATA)/html
copy:
$(info copying fonts)

@ -1,13 +1,13 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "remixicon.css";
@import "./modal.css";
@import "./dark.css";
@import "./tooltip.css";
@import "./loader.css";
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--border-width-default: 2px;
--border-width-2: 3px;
@ -23,6 +23,18 @@
background-color: #101010;
}
.dark select, .dark option, .dark input {
background: #202020;
}
.light-only {
display: none;
}
.dark-only {
display: initial;
}
.page-container {
margin: 5% 20% 5% 20%;
}
@ -93,10 +105,6 @@ div.card:contains(section.banner.footer) {
margin-top: 1rem;
}
.ml-1 {
margin-left: 1rem;
}
.ml-half {
margin-left: 0.5rem;
}
@ -105,9 +113,6 @@ div.card:contains(section.banner.footer) {
margin-right: 0.5rem;
}
.mr-1 {
margin-right: 1rem;
}
.p-1 {
padding: 1rem;
@ -429,6 +434,10 @@ input {
border: 0 solid var(--color-neutral-300);
}
table {
color: var(--color-content);
}
p.top {
margin-top: 0px;
}

@ -1,91 +0,0 @@
.dark {
--settings-section-button-filter: 110%;
--color-neutral-900: rgba(255, 255, 255, 0.87);
--color-neutral-800: rgba(255, 255, 255, 0.8);
--color-neutral-700: rgba(255, 255, 255, 0.73);
--color-neutral-600: rgba(255, 255, 255, 0.66);
--color-neutral-500: rgb(153, 153, 153);
--color-neutral-400: #383838;
--color-neutral-300: #303030;
--color-neutral-200: #292929;
--color-neutral-100: #242424;
--color-neutral-50: #202020;
--color-neutral-000: #101010;
--color-critical-900: #fef2f2;
--color-critical-800: #fee2e2;
--color-critical-700: #fecaca;
--color-critical-600: #fca5a5;
--color-critical-500: #f87171;
--color-critical-400: #ef4444;
--color-critical-300: #dc2626;
--color-critical-200: #b91c1c;
--color-critical-100: #991b1b;
--color-critical-50: #7f1d1d;
--color-critical-000: #441313;
--color-warning-900: #fffbeb;
--color-warning-800: #fef3c7;
--color-warning-700: #fde68a;
--color-warning-600: #fcd34d;
--color-warning-500: #fbbf24;
--color-warning-400: #f59e0b;
--color-warning-300: #d97706;
--color-warning-200: #b45309;
--color-warning-100: #92400e;
--color-warning-50: #783900;
--color-warning-000: #411e01;
--color-positive-900: #f0fdf4;
--color-positive-800: #dcfce7;
--color-positive-700: #bbf7d0;
--color-positive-600: #86efac;
--color-positive-500: #4ade80;
--color-positive-400: #22c55e;
--color-positive-300: #16a34a;
--color-positive-200: #15803d;
--color-positive-100: #166534;
--color-positive-50: #14532d;
--color-positive-000: #0f2e1b;
--color-urge-900: #e0ffff;
--color-urge-800: #c0fbff;
--color-urge-700: #a0f4ff;
--color-urge-600: #80e9ff;
--color-urge-500: #60dbfb;
--color-urge-400: #40cbf3;
--color-urge-300: #20b9e9;
--color-urge-200: #00a4dc; /* tab buttons */
--color-urge-100: #0054bc;
--color-urge-50: #00169a;
--color-urge-000: #050076;
--color-info-900: #f5f3ff;
--color-info-800: #ede9fe;
--color-info-700: #ddd6fe;
--color-info-600: #c4b5fd;
--color-info-500: #a78bfa;
--color-info-400: #8b5cf6;
--color-info-300: #7c3aed;
--color-info-200: #6d28d9;
--color-info-100: #5b21b6;
--color-info-50: #4c1d95;
--color-info-000: #240e44;
--color-neutral-normal-content: #ffffff;
}
.light-only {
display: none;
}
.dark-only {
display: initial;
}
.dark select option {
background: #202020;
}

@ -571,7 +571,7 @@
<div class="card ~neutral @low accounts mb-1">
<div class="flex-expand row">
<div class="row">
<span class="heading mr-1 col sm">{{ .strings.accounts }}</span>
<span class="text-3xl font-bold mr-1 col">{{ .strings.accounts }}</span>
<input type="search" class="col sm field ~neutral @low input search ml-1 mr-1" id="accounts-search" placeholder="{{ .strings.search }}">
</div>
<div class="row">

2015
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -23,14 +23,17 @@
"browserslist": "^4.16.6",
"esbuild": "^0.8.57",
"inline-source": "^7.2.0",
"jsdom": "^19.0.0",
"lodash": "^4.17.21",
"mjml": "^4.8.0",
"nightwind": "github:yonson2/nightwind",
"remixicon": "^2.5.0",
"remove-markdown": "^0.3.0",
"typescript": "^4.0.3",
"uncss": "^0.17.3"
},
"devDependencies": {
"live-server": "^1.2.1"
"live-server": "^1.2.1",
"tailwindcss": "^3.0.8"
}
}

@ -0,0 +1,655 @@
<!DOCTYPE html>
<html lang="en" class="{{ .cssClass }}">
<head>
<link rel="stylesheet" type="text/css" href="{{ .urlBase }}/css/bundle.css">
<script>
window.URLBase = "{{ .urlBase }}";
window.notificationsEnabled = {{ .notifications }};
window.emailEnabled = {{ .emailEnabled }};
window.telegramEnabled = {{ .telegramEnabled }};
window.discordEnabled = {{ .discordEnabled }};
window.matrixEnabled = {{ .matrixEnabled }};
window.ombiEnabled = {{ .ombiEnabled }};
window.usernameEnabled = {{ .username }};
window.langFile = JSON.parse({{ .language }});
window.linkResetEnabled = {{ .linkResetEnabled }};
window.language = "{{ .langName }}";
</script>
{{ template "header.html" . }}
<title>Admin - jfa-go</title>
</head>
<body class="max-w-full overflow-x-hidden section">
<div id="modal-login" class="modal">
<form class="modal-content card" id="form-login" href="">
<span class="heading">{{ .strings.login }}</span>
<input type="text" class="field input ~neutral @high mt-half mb-1" placeholder="{{ .strings.username }}" id="login-user">
<input type="password" class="field input ~neutral @high mb-1" placeholder="{{ .strings.password }}" id="login-password">
<label>
<input type="submit" class="unfocused">
<span class="button ~urge @low full-width center supra submit">{{ .strings.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">{{ .strings.newUser }} <span class="modal-close">&times;</span></span>
<input type="text" class="field input ~neutral @high mt-half mb-1" placeholder="{{ .strings.username }}" id="add-user-user">
<input type="email" class="field input ~neutral @high mt-half mb-1" placeholder="{{ .strings.emailAddress }}">
<input type="password" class="field input ~neutral @high mb-1" placeholder="{{ .strings.password }}" id="add-user-password">
<label>
<input type="submit" class="unfocused">
<span class="button ~urge @low full-width center supra submit">{{ .strings.create }}</span>
</label>
</form>
</div>
<div id="modal-about" class="modal">
<div class="modal-content content card">
<img src="{{ .urlBase }}/banner.svg" class="banner header" alt="jfa-go banner">
<span class="heading"><span class="modal-close">&times;</span></span>
<p>{{ .strings.version }} <span class="code monospace">{{ .version }}</span></p>
<p>{{ .strings.commitNoun }} <span class="code monospace">{{ .commit }}</span></p>
<div class="row col flex">
<a class="button ~neutral mr-half mt-1 mb-1 lang-link" href="https://github.com/hrfee/jfa-go"><i class="ri-github-line mr-half"></i>github</a>
<a class="button ~urge mt-1 mb-1 mr-half lang-link" href="https://wiki.jfa-go.com">wiki/docs</a>
<a class="button ~positive mt-1 mb-1 mr-half lang-link" href="https://weblate.jfa-go.com">translation</a>
<div class="dropdown mr-half" tabindex="0">
<a href="https://github.com/sponsors/hrfee" target="_blank" class="button ~info mt-1 mb-1 dropdown-button lang-link">
<i class="ri-hand-heart-line mr-half"></i>
donate
<span class="ml-1 chev"></span>
</a>
<div class="dropdown-display">
<div class="card ~info @low">
<a href="https://github.com/sponsors/hrfee" target="_blank" class="button input ~neutral field mb-half lang-link">GitHub</a>
<a href="https://ko-fi.com/hrfee" target="_blank" class="button input ~neutral field mb-half lang-link">Ko-fi</a>
</div>
</div>
</div>
<a class="button ~urge mt-1 mb-1 @low discord lang-link" href="https://discord.com/invite/MrtvuQmyhP" target="_blank"><i class="ri-discord-line mr-half"></i>discord</a>
</div>
<p><a href="https://github.com/hrfee/jfa-go/blob/main/LICENSE">Available under the MIT License.</a></p>
<pre class="monospace">{{ .license }}</pre>
</div>
</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"></span> <span class="modal-close">&times;</span></span>
<p class="content">{{ .strings.modifySettingsDescription }}</p>
<div class="flex-row mb-1">
<label class="flex-row-group mr-1">
<input type="radio" name="modify-user-source" class="unfocused" id="radio-use-profile" checked>
<span class="button ~neutral @high supra full-width center">{{ .strings.profile }}</span>
</label>
<label class="flex-row-group ml-1">
<input type="radio" name="modify-user-source" class="unfocused" id="radio-use-user">
<span class="button ~neutral @low supra full-width center">{{ .strings.user }}</span>
</label>
</div>
<div class="select ~neutral @low mb-1">
<select id="modify-user-profiles"></select>
</div>
<div class="select ~neutral @low mb-1 unfocused">
<select id="modify-user-users"></select>
</div>
<label class="switch mb-1">
<input type="checkbox" id="modify-user-homescreen" checked>
<span>{{ .strings.applyHomescreenLayout }}</span>
</label>
<label>
<input type="submit" class="unfocused">
<span class="button ~urge @low full-width center supra submit">{{ .strings.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"></span> <span class="modal-close">&times;</span></span>
<div class="content mt-half">
<label class="switch mb-1">
<input type="checkbox" id="delete-user-notify" checked>
<span>{{ .strings.sendDeleteNotificationEmail }}</span>
</label>
<textarea id="textarea-delete-user" class="textarea full-width ~neutral @low mb-1" placeholder="{{ .strings.sendDeleteNotificationExample }}"></textarea>
<label>
<input type="submit" class="unfocused">
<span class="button ~critical @low full-width center supra submit">{{ .strings.delete }}</span>
</label>
</div>
</form>
</div>
<div id="modal-extend-expiry" class="modal">
<form class="modal-content card" id="form-extend-expiry" href="">
<span class="heading"><span id="header-extend-expiry"></span> <span class="modal-close">&times;</span></span>
<div class="content mt-half">
<div class="row">
<div class="col">
<label class="label supra" for="extend-expiry-months">{{ .strings.inviteMonths }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="extend-expiry-months">
<option>0</option>
</select>
</div>
</div>
<div class="col">
<label class="label supra" for="extend-expiry-days">{{ .strings.inviteDays }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="extend-expiry-days">
<option>0</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col">
<label class="label supra" for="extend-expiry-hours">{{ .strings.inviteHours }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="extend-expiry-hours">
<option>0</option>
</select>
</div>
</div>
<div class="col">
<label class="label supra" for="extend-expiry-minutes">{{ .strings.inviteMinutes }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="extend-expiry-minutes">
<option>0</option>
</select>
</div>
</div>
</div>
<label class="switch mb-1">
<input type="checkbox" id="expiry-extend-enable" checked>
<span>{{ .strings.sendDeleteNotificationEmail }}</span>
</label>
<textarea id="textarea-extend-enable" class="textarea full-width ~neutral @low mb-1" placeholder="{{ .strings.sendDeleteNotificationExample }}"></textarea>
<label>
<input type="submit" class="unfocused">
<span class="button ~critical @low full-width center supra submit">{{ .strings.submit }}</span>
</label>
</div>
</form>
</div>
<div id="modal-announce" class="modal">
<form class="modal-content wide card" id="form-announce" href="">
<span class="heading"><span id="header-announce"></span> <span class="modal-close">&times;</span></span>
<div class="row">
<div class="col flex-col content mt-half">
<div id="announce-details">
<span class="label supra" for="editor-variables" id="label-editor-variables">{{ .strings.variables }}</span>
<div id="announce-variables">
<span class="button ~urge @low mb-1 mt-half" id="announce-variables-username" style="margin-left: 0.25rem; margin-right: 0.25rem;"><span class="monospace">{username}</span></span>
</div>
<label class="label supra" for="announce-subject"> {{ .strings.subject }}</label>
<input type="text" id="announce-subject" class="input ~neutral @low mb-1 mt-half">
<label class="label supra" for="textarea-announce">{{ .strings.message }}</label>
<textarea id="textarea-announce" class="textarea full-width ~neutral @low mt-half monospace"></textarea>
<p class="support mt-half mb-1">{{ .strings.markdownSupported }}</p>
</div>
<label class="label unfocused" id="announce-name"><p class="supra">{{ .strings.name }}</p>
<input type="text" class="input ~neutral @low mb-1 mt-half">
<p class="support">{{ .strings.templateEnterName }}</p>
</label>
<div class="row flex-expand">
<label>
<input type="submit" class="unfocused">
<span class="button ~urge @low center supra submit">{{ .strings.send }}</span>
</label>
<span class="button ~info @low center supra" id="save-announce">{{ .strings.saveAsTemplate }}</span>
</div>
</div>
<div class="col card ~neutral @low">
<span class="subheading supra">{{ .strings.preview }}</span>
<div class="mt-half" id="announce-preview"></div>
</div>
</div>
</form>
</div>
<div id="modal-customize" class="modal">
<div class="modal-content card">
<span class="heading">{{ .strings.customizeMessages }} <span class="modal-close">&times;</span></span>
<p class="content">{{ .strings.customizeMessagesDescription }}</p>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>{{ .strings.name }}</th>
<th>{{ .strings.reset }}</th>
<th>{{ .strings.edit }}</th>
</tr>
</thead>
<tbody id="customize-list"></tbody>
</table>
</div>
</div>
</div>
<div id="modal-editor" class="modal">
<form class="modal-content wide card" id="form-editor" href="">
<span class="heading"><span id="header-editor"></span> <span class="modal-close">&times;</span></span>
<div class="row">
<div class="col flex-col content mt-half">
<span class="label supra" for="editor-variables" id="label-editor-variables">{{ .strings.variables }}</span>
<div id="editor-variables"></div>
<span class="label supra" for="editor-conditionals" id="label-editor-conditionals">{{ .strings.conditionals }}</span>
<div id="editor-conditionals"></div>
<label class="label supra" for="textarea-editor">{{ .strings.message }}</label>
<textarea id="textarea-editor" class="textarea full-width flex-auto ~neutral @low mt-half monospace"></textarea>
<p class="support mt-half mb-1">{{ .strings.markdownSupported }}</p>
<div class="flex-row">
<label class="full-width ml-half">
<input type="submit" class="unfocused">
<span class="button ~urge @low full-width center supra submit">{{ .strings.submit }}</span>
</label>
</div>
</div>
<div class="col card ~neutral @low">
<span class="subheading supra">{{ .strings.preview }}</span>
<div class="mt-half" id="editor-preview"></div>
</div>
</div>
</form>
</div>
<div id="modal-restart" class="modal">
<div class="modal-content card ~critical @low">
<span class="heading">{{ .strings.settingsRestartRequired }} <span class="modal-close">&times;</span></span>
<p class="content pb-1">{{ .strings.settingsRestartRequiredDescription }}</p>
<div class="fr">
<span class="button ~info @low mb-half" id="settings-apply-no-restart">{{ .strings.settingsApplyRestartLater }}</span>
<span class="button ~critical @low" id="settings-apply-restart">{{ .strings.settingsApplyRestartNow }}</span>
</div>
</div>
</div>
<div id="modal-refresh" class="modal">
<div class="modal-content card ~neutral @low">
<span class="heading">{{ .strings.settingsApplied }}</span>
<p class="content">{{ .strings.settingsRefreshPage }}</p>
</div>
</div>
<div id="modal-send-pwr" class="modal">
<div class="modal-content card ~neutral @low">
<span class="heading">{{ .strings.sendPWR }}</span>
<p class="content" id="send-pwr-note"></p>
<span class="button ~urge @low mt-half" id="send-pwr-link">{{ .strings.copy }}</span>
</div>
</div>
<div id="modal-ombi-profile" class="modal">
<form class="modal-content card" id="form-ombi-defaults" href="">
<span class="heading">{{ .strings.ombiProfile }} <span class="modal-close">&times;</span></span>
<p class="content">{{ .strings.ombiUserDefaultsDescription }}</p>
<div class="select ~neutral @low mb-1">
<select></select>
</div>
<label>
<input type="submit" class="unfocused">
<span class="button ~urge @low full-width center supra submit">{{ .strings.submit }}</span>
</label>
</form>
</div>
<div id="modal-user-profiles" class="modal">
<div class="modal-content wide card">
<span class="heading">{{ .strings.userProfiles }} <span class="modal-close">&times;</span></span>
<p class="support lg">{{ .strings.userProfilesDescription }}</p>
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>{{ .strings.name }}</th>
<th>{{ .strings.userProfilesIsDefault }}</th>
{{ if .ombiEnabled }}
<th>Ombi</th>
{{ end }}
<th>{{ .strings.from }}</th>
<th>{{ .strings.userProfilesLibraries }}</th>
<th><span class="button ~neutral @high" id="button-profile-create">{{ .strings.create }}</span></th>
</tr>
</thead>
<tbody id="table-profiles"></tbody>
</table>
</div>
</div>
</div>
<div id="modal-add-profile" class="modal">
<form class="modal-content card" id="form-add-profile" href="">
<span class="heading">{{ .strings.addProfile }} <span class="modal-close">&times;</span></span>
<p class="content">{{ .strings.addProfileDescription }}</p>
<label>
<span class="supra">{{ .strings.addProfileNameOf }} </span>
<input type="text" class="field input ~neutral @high mt-half mb-1" placeholder="{{ .strings.name }}" id="add-profile-name">
<label>
<span class="supra">{{ .strings.user }}</span>
<div class="select ~neutral @low mt-half mb-1">
<select id="add-profile-user"></select>
</div>
</label>
<label class="switch mb-1">
<input type="checkbox" id="add-profile-homescreen" checked>
<span>{{ .strings.addProfileStoreHomescreenLayout }}</span>
</label>
<label>
<input type="submit" class="unfocused">
<span class="button ~urge @low full-width center supra submit">{{ .strings.create }}</span>
</label>
</form>
</div>
<div id="modal-update" class="modal">
<div class="modal-content wide card">
<span class="heading">{{ .strings.updates }} <span class="modal-close">&times;</span></span>
<p class="content">
<h2>
<a id="update-version"></a> (<span class="monospace" id="update-commit"></span>)
</h2>
<p class="content" id="update-description"></p>
<p class="support" id="update-date"></p>
<div class="content markdown-box" id="update-changelog"></div>
</p>
<span class="button ~info @low full-width center" id="update-download">{{ .strings.download }}</span>
<span class="button ~urge @low full-width center" id="update-update">{{ .strings.update }}</span>
</div>
</div>
{{ if .telegramEnabled }}
<div id="modal-telegram" class="modal">
<div class="modal-content card">
<span class="heading mb-1">{{ .strings.linkTelegram }}</span>
<p class="content mb-1">{{ .strings.sendPIN }}</p>
<h1 class="ac" id="telegram-pin"></h1>
<a class="subheading link-center" id="telegram-link" target="_blank">
<span class="shield ~info mr-1">
<span class="icon">
<i class="ri-telegram-line"></i>
</span>
</span>
&#64;<span id="telegram-username">
</a>
<span class="button ~info @low full-width center mt-1" id="telegram-waiting">{{ .strings.success }}</span>
</div>
</div>
{{ end }}
{{ if .discordEnabled }}
<div id="modal-discord" class="modal">
<div class="modal-content card">
<span class="heading mb-1"><span id="discord-header"></span><span class="modal-close">&times;</span></span>
<p class="content mb-1" id="discord-description"></p>
<div class="row">
<input type="search" class="col sm field ~neutral @low input" id="discord-search" placeholder="user#1234">
</div>
<table class="table"><tbody id="discord-list"></tbody></table>
</div>
</div>
{{ end }}
<div id="modal-matrix" class="modal">
<form class="modal-content card" id="form-matrix" href="">
<span class="heading">{{ .strings.linkMatrix }}</span>
<p class="content">{{ .strings.linkMatrixDescription }}</p>
<input type="text" class="field input ~neutral @high mt-half mb-1" placeholder="{{ .strings.matrixHomeServer }}" id="matrix-homeserver">
<input type="text" class="field input ~neutral @high mt-half mb-1" placeholder="{{ .strings.username }}" id="matrix-user">
<input type="password" class="field input ~neutral @high mt-half mb-1" placeholder="{{ .strings.password }}" id="matrix-password">
<label>
<input type="submit" class="unfocused">
<span class="button ~urge @low full-width center supra submit">{{ .strings.submit }}</span>
</label>
</form>
</div>
<div id="notification-box"></div>
<span class="dropdown" tabindex="0" id="lang-dropdown">
<span class="button ~urge dropdown-button">
<i class="ri-global-line"></i>
<span class="ml-1 chev"></span>
</span>
<div class="dropdown-display">
<div class="card ~neutral @low">
<label class="switch pb-1">
<input type="radio" name="lang-time" id="lang-12h">
<span>{{ .strings.time12h }}</span>
</label>
<label class="switch pb-1">
<input type="radio" name="lang-time" id="lang-24h">
<span>{{ .strings.time24h }}</span>
</label>
<div id="lang-list"></div>
</div>
</div>
</span>
<span class="button ~warning" alt="{{ .strings.theme }}" id="button-theme"><i class="ri-sun-line"></i></span>
<div class="page-container">
<div class="mb-1">
<header class="flex flex-wrap items-center justify-between">
<div class="text-neutral-700">
<span id="button-tab-invites" class="tab-button portal">{{ .strings.invites }}</span>
<span id="button-tab-accounts" class="tab-button portal">{{ .strings.accounts }}</span>
<span id="button-tab-settings" class="tab-button portal">{{ .strings.settings }}</span>
</div>
</header>
</div>
<div class="mb-1">
<div class="text-neutral-700">
<span class="button ~critical @low mb-1 unfocused" id="logout-button">{{ .strings.logout }}</span>
</div>
</div>
<div id="tab-invites">
<div class="card ~neutral @low invites mb-1">
<span class="heading">{{ .strings.invites }}</span>
<div id="invites"></div>
</div>
<div class="card ~neutral @low">
<span class="heading">{{ .strings.create }}</span>
<div class="row" id="create-inv">
<div class="card ~neutral @low col">
<div class="row mb-1">
<label class="col mr-1">
<input type="radio" name="duration" class="unfocused" id="radio-inv-duration" checked>
<span class="button ~neutral @high supra full-width center">{{ .strings.inviteDuration }}</span>
</label>
<label class="col ml-1">
<input type="radio" name="duration" class="unfocused" id="radio-user-expiry">
<span class="button ~neutral @low supra full-width center">{{ .strings.userExpiry }}</span>
</label>
</div>
<div id="inv-duration">
<div class="row">
<div class="col">
<label class="label supra" for="create-months">{{ .strings.inviteMonths }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="create-months">
<option>0</option>
</select>
</div>
</div>
<div class="col">
<label class="label supra" for="create-days">{{ .strings.inviteDays }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="create-days">
<option>0</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col">
<label class="label supra" for="create-hours">{{ .strings.inviteHours }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="create-hours">
<option>0</option>
</select>
</div>
</div>
<div class="col">
<label class="label supra" for="create-minutes">{{ .strings.inviteMinutes }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="create-minutes">
<option>0</option>
</select>
</div>
</div>
</div>
</div>
<div id="user-expiry" class="unfocused">
<p class="support">{{ .strings.userExpiryDescription }}</p>
<div class="mb-half">
<label for="create-user-expiry-enabled" class="button ~neutral @low">
<input type="checkbox" id="create-user-expiry-enabled" aria-label="User duration enabled">
<span class="ml-half">{{ .strings.enabled }} </span>
</label>
</div>
<div class="row">
<div class="col">
<label class="label supra" for="user-months">{{ .strings.inviteMonths }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="user-months">
<option>0</option>
</select>
</div>
</div>
<div class="col">
<label class="label supra" for="user-days">{{ .strings.inviteDays }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="user-days">
<option>0</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col">
<label class="label supra" for="user-hours">{{ .strings.inviteHours }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="user-hours">
<option>0</option>
</select>
</div>
</div>
<div class="col">
<label class="label supra" for="user-minutes">{{ .strings.inviteMinutes }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="user-minutes">
<option>0</option>
</select>
</div>
</div>
</div>
</div>
<div class="col">
<label class="label supra" for="create-label"> {{ .strings.label }}</label>
<input type="text" id="create-label" class="input ~neutral @low mb-1 mt-half">
</div>
</div>
<div class="card ~neutral @low col">
<label class="label supra" for="create-uses">{{ .strings.inviteNumberOfUses }}</label>
<div class="flex-expand mb-1 mt-half">
<input type="number" min="0" id="create-uses" class="input ~neutral @low mr-1" value=1>
<label for="create-inf-uses" class="button ~neutral @low" title="Set uses to infinite">
<span></span>
<input type="checkbox" class="unfocused" id="create-inf-uses" aria-label="Set uses to infinite">
</label>
</div>
<p class="support unfocused" id="create-inf-uses-warning"><span class="badge ~critical">{{ .strings.warning }}</span> {{ .strings.inviteInfiniteUsesWarning }}</p>
<label class="label supra">{{ .strings.profile }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="create-profile">
</select>
</div>
<div id="create-send-to-container">
<label class="label supra">{{ .strings.inviteSendToEmail }}</label>
<div class="flex-expand mb-1 mt-half">
{{ if .discordEnabled }}
<input type="text" id="create-send-to" class="input ~neutral @low mr-1" placeholder="example@example.com | user#1234">
<span id="create-send-to-search" class="button ~neutral @low mr-1">
<i class="icon ri-search-2-line" title="{{ .strings.search }}"></i>
</span>
{{ else }}
<input type="email" id="create-send-to" class="input ~neutral @low mr-1" placeholder="example@example.com">
{{ end }}
<label for="create-send-to-enabled" class="button ~neutral @low">
<input type="checkbox" id="create-send-to-enabled" aria-label="Send to address enabled">
</label>
</div>
</div>
<span class="button ~urge @low supra full-width center lg" id="create-submit">{{ .strings.create }}</span>
</div>
</div>
</div>
</div>
<div id="tab-accounts" class="unfocused">
<div class="card ~neutral @low accounts mb-1">
<div class="flex-expand row">
<div class="row">
<span class="heading mr-1 col sm">{{ .strings.accounts }}</span>
<input type="search" class="col sm field ~neutral @low input search ml-1 mr-1" id="accounts-search" placeholder="{{ .strings.search }}">
</div>
<div class="row">
<span class="col sm button ~neutral @low center mb-half" id="accounts-add-user">{{ .quantityStrings.addUser.Singular }}</span>
<div id="accounts-announce-dropdown" class="col sm dropdown" tabindex="0">
<span class="h-100 sm button ~info @low center mb-half" id="accounts-announce">{{ .strings.announce }}</span>
<div class="dropdown-display">
<div class="card ~neutral @low">
<span class="supra sm">{{ .strings.templates }}</span>
<div id="accounts-announce-templates"></div>
</div>
</div>
</div>
<span class="col sm button ~urge @low center mb-half" id="accounts-modify-user">{{ .strings.modifySettings }}</span>
<span class="col sm button ~warning @low center mb-half" id="accounts-extend-expiry">{{ .strings.extendExpiry }}</span>
<div id="accounts-disable-enable-dropdown" class="col sm dropdown manual" tabindex="0">
<span class="h-100 sm button ~positive @low center mb-half" id="accounts-disable-enable">{{ .strings.disable }}</span>
<div class="dropdown-display">
<div class="card ~neutral @low">
<span class="button ~neutral sm full-width accounts-announce-template-button" id="accounts-enable-expiry">{{ .strings.setExpiry }}</span>
</div>
</div>
</div>
<span class="col sm button ~info @low center mb-half unfocused" id="accounts-send-pwr">{{ .strings.sendPWR }}</span>
<span class="col sm button ~critical @low center mb-half" id="accounts-delete-user">{{ .quantityStrings.deleteUser.Singular }}</span>
</div>
</div>
<div class="card ~neutral @low accounts-header table-responsive mt-half">
<table class="table">
<thead>
<tr>
<th><input type="checkbox" value="" id="accounts-select-all"></th>
<th>{{ .strings.username }}</th>
<th>{{ .strings.emailAddress }}</th>
{{ if .telegramEnabled }}
<th>Telegram</th>
{{ end }}
{{ if .matrixEnabled }}
<th>Matrix</th>
{{ end }}
{{ if .discordEnabled }}
<th>Discord</th>
{{ end }}
<th>{{ .strings.expiry }}</th>
<th>{{ .strings.lastActiveTime }}</th>
</tr>
</thead>
<tbody id="accounts-list"></tbody>
</table>
</div>
</div>
</div>
<div id="tab-settings" class="unfocused">
<div class="card ~neutral @low settings overflow">
<div class="flex-expand">
<div class="flex-row">
<span class="heading">{{ .strings.settings }}</span>
<label for="settings-advanced-enabled" class="button ~neutral @low ml-1">
<input type="checkbox" id="settings-advanced-enabled" aria-label="Advanced settings enabled">
<span class="ml-half">{{ .strings.advancedSettings }} </span>
</label>
</div>
<div>
<span class="button ~neutral @low" id="settings-restart">{{ .strings.settingsRestart }}</span>
<span class="button ~urge @low unfocused" id="settings-save">{{ .strings.settingsSave }}</span>
</div>
</div>
<div class="row">
<div class="card ~neutral @low col" id="settings-sidebar">
<aside class="aside sm ~info mb-half" id="settings-message">Note: <span class="badge ~critical">*</span> indicates a required field, <span class="badge ~info">R</span> indicates changes require a restart.</aside>
<span class="button ~neutral @low settings-section-button mb-half" id="setting-about"><span class="flex">{{ .strings.aboutProgram }} <i class="ri-information-line ml-half"></i></span></span>
<span class="button ~neutral @low settings-section-button mb-half" id="setting-profiles"><span class="flex">{{ .strings.userProfiles }} <i class="ri-user-line ml-half"></i></span></span>
</div>
<div class="card ~neutral @low col overflow" id="settings-panel"></div>
</div>
</div>
</div>
</div>
<script src="{{ .urlBase }}/js/admin.js" type="module"></script>
</body>
</html>

@ -0,0 +1,656 @@
<!DOCTYPE html>
<html lang="en" class="{{ .cssClass }}">
<head>
<link rel="stylesheet" type="text/css" href="{{ .urlBase }}/css/bundle.css">
<script>
window.URLBase = "{{ .urlBase }}";
window.notificationsEnabled = {{ .notifications }};
window.emailEnabled = {{ .emailEnabled }};
window.telegramEnabled = {{ .telegramEnabled }};
window.discordEnabled = {{ .discordEnabled }};
window.matrixEnabled = {{ .matrixEnabled }};
window.ombiEnabled = {{ .ombiEnabled }};
window.usernameEnabled = {{ .username }};
window.langFile = JSON.parse({{ .language }});
window.linkResetEnabled = {{ .linkResetEnabled }};
window.language = "{{ .langName }}";
</script>
{{ template "header.html" . }}
<title>Admin - jfa-go</title>
</head>
<div id="modal-login" class="modal">
<form class="modal-content card ~neutral" id="form-login" href>
<span class="heading">{{ .strings.login }}</span>
<input type="text" class="field input ~neutral @high mt-half mb-1" placeholder="{{ .strings.username }}" id="login-user">
<input type="password" class="field input ~neutral @high mb-1" placeholder="{{ .strings.password }}" id="login-password">
<label>
<input type="submit" class="unfocused">
<span class="button ~urge @low full-width center supra submit ~neutral">{{ .strings.login }}</span>
</label>
</form>
</div>
<div id="modal-add-user" class="modal">
<form class="modal-content card ~neutral" id="form-add-user" href>
<span class="heading">{{ .strings.newUser }} <span class="modal-close">&times;</span></span>
<input type="text" class="field input ~neutral @high mt-half mb-1" placeholder="{{ .strings.username }}" id="add-user-user">
<input type="email" class="field input ~neutral @high mt-half mb-1" placeholder="{{ .strings.emailAddress }}">
<input type="password" class="field input ~neutral @high mb-1" placeholder="{{ .strings.password }}" id="add-user-password">
<label>
<input type="submit" class="unfocused">
<span class="button ~urge @low full-width center supra submit ~neutral">{{ .strings.create }}</span>
</label>
</form>
</div>
<div id="modal-about" class="modal">
<div class="modal-content content card ~neutral">
<img src="{{ .urlBase }}/banner.svg" class="banner header" alt="jfa-go banner">
<span class="heading"><span class="modal-close">&times;</span></span>
<p>{{ .strings.version }} <span class="code monospace">{{ .version }}</span></p>
<p>{{ .strings.commitNoun }} <span class="code monospace">{{ .commit }}</span></p>
<div class="row col flex">
<a class="button ~neutral mr-half mt-1 mb-1 lang-link" href="https://github.com/hrfee/jfa-go"><i class="ri-github-line mr-half"></i>github</a>
<a class="button ~urge mt-1 mb-1 mr-half lang-link ~neutral" href="https://wiki.jfa-go.com">wiki/docs</a>
<a class="button ~positive mt-1 mb-1 mr-half lang-link ~neutral" href="https://weblate.jfa-go.com">translation</a>
<div class="dropdown mr-half" tabindex="0">
<a href="https://github.com/sponsors/hrfee" target="_blank" class="button ~info mt-1 mb-1 dropdown-button lang-link ~neutral">
<i class="ri-hand-heart-line mr-half"></i>
donate
<span class="ml-1 chev"></span>
</a>
<div class="dropdown-display">
<div class="card ~info @low ~neutral">
<a href="https://github.com/sponsors/hrfee" target="_blank" class="button input ~neutral field mb-half lang-link">GitHub</a>
<a href="https://ko-fi.com/hrfee" target="_blank" class="button input ~neutral field mb-half lang-link">Ko-fi</a>
</div>
</div>
</div>
<a class="button ~urge mt-1 mb-1 @low discord lang-link ~neutral" href="https://discord.com/invite/MrtvuQmyhP" target="_blank"><i class="ri-discord-line mr-half"></i>discord</a>
</div>
<p><a href="https://github.com/hrfee/jfa-go/blob/main/LICENSE">Available under the MIT License.</a></p>
<pre class="monospace">{{ .license }}</pre>
</div>
</div>
<div id="modal-modify-user" class="modal">
<form class="modal-content card ~neutral" id="form-modify-user" href>
<span class="heading"><span id="header-modify-user"></span> <span class="modal-close">&times;</span></span>
<p class="content">{{ .strings.modifySettingsDescription }}</p>
<div class="flex-row mb-1">
<label class="flex-row-group mr-1">
<input type="radio" name="modify-user-source" class="unfocused" id="radio-use-profile" checked>
<span class="button ~neutral @high supra full-width center">{{ .strings.profile }}</span>
</label>
<label class="flex-row-group ml-1">
<input type="radio" name="modify-user-source" class="unfocused" id="radio-use-user">
<span class="button ~neutral @low supra full-width center">{{ .strings.user }}</span>
</label>
</div>
<div class="select ~neutral @low mb-1">
<select id="modify-user-profiles"></select>
</div>
<div class="select ~neutral @low mb-1 unfocused">
<select id="modify-user-users"></select>
</div>
<label class="switch mb-1">
<input type="checkbox" id="modify-user-homescreen" checked>
<span>{{ .strings.applyHomescreenLayout }}</span>
</label>
<label>
<input type="submit" class="unfocused">
<span class="button ~urge @low full-width center supra submit ~neutral">{{ .strings.apply }}</span>
</label>
</form>
</div>
<div id="modal-delete-user" class="modal">
<form class="modal-content card ~neutral" id="form-delete-user" href>
<span class="heading"><span id="header-delete-user"></span> <span class="modal-close">&times;</span></span>
<div class="content mt-half">
<label class="switch mb-1">
<input type="checkbox" id="delete-user-notify" checked>
<span>{{ .strings.sendDeleteNotificationEmail }}</span>
</label>
<textarea id="textarea-delete-user" class="textarea full-width ~neutral @low mb-1" placeholder="{{ .strings.sendDeleteNotificationExample }}"></textarea>
<label>
<input type="submit" class="unfocused">
<span class="button ~critical @low full-width center supra submit ~neutral">{{ .strings.delete }}</span>
</label>
</div>
</form>
</div>
<div id="modal-extend-expiry" class="modal">
<form class="modal-content card ~neutral" id="form-extend-expiry" href>
<span class="heading"><span id="header-extend-expiry"></span> <span class="modal-close">&times;</span></span>
<div class="content mt-half">
<div class="row">
<div class="col">
<label class="label supra" for="extend-expiry-months">{{ .strings.inviteMonths }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="extend-expiry-months">
<option>0</option>
</select>
</div>
</div>
<div class="col">
<label class="label supra" for="extend-expiry-days">{{ .strings.inviteDays }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="extend-expiry-days">
<option>0</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col">
<label class="label supra" for="extend-expiry-hours">{{ .strings.inviteHours }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="extend-expiry-hours">
<option>0</option>
</select>
</div>
</div>
<div class="col">
<label class="label supra" for="extend-expiry-minutes">{{ .strings.inviteMinutes }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="extend-expiry-minutes">
<option>0</option>
</select>
</div>
</div>
</div>
<label class="switch mb-1">
<input type="checkbox" id="expiry-extend-enable" checked>
<span>{{ .strings.sendDeleteNotificationEmail }}</span>
</label>
<textarea id="textarea-extend-enable" class="textarea full-width ~neutral @low mb-1" placeholder="{{ .strings.sendDeleteNotificationExample }}"></textarea>
<label>
<input type="submit" class="unfocused">
<span class="button ~critical @low full-width center supra submit ~neutral">{{ .strings.submit }}</span>
</label>
</div>
</form>
</div>
<div id="modal-announce" class="modal">
<form class="modal-content wide card ~neutral" id="form-announce" href>
<span class="heading"><span id="header-announce"></span> <span class="modal-close">&times;</span></span>
<div class="row">
<div class="col flex-col content mt-half">
<div id="announce-details">
<span class="label supra" for="editor-variables" id="label-editor-variables">{{ .strings.variables }}</span>
<div id="announce-variables">
<span class="button ~urge @low mb-1 mt-half ~neutral" id="announce-variables-username" style="margin-left: 0.25rem; margin-right: 0.25rem;"><span class="monospace">{username}</span></span>
</div>
<label class="label supra" for="announce-subject"> {{ .strings.subject }}</label>
<input type="text" id="announce-subject" class="input ~neutral @low mb-1 mt-half">
<label class="label supra" for="textarea-announce">{{ .strings.message }}</label>
<textarea id="textarea-announce" class="textarea full-width ~neutral @low mt-half monospace"></textarea>
<p class="support mt-half mb-1">{{ .strings.markdownSupported }}</p>
</div>
<label class="label unfocused" id="announce-name"><p class="supra">{{ .strings.name }}</p>
<input type="text" class="input ~neutral @low mb-1 mt-half">
<p class="support">{{ .strings.templateEnterName }}</p>
</label>
<div class="row flex-expand">
<label>
<input type="submit" class="unfocused">
<span class="button ~urge @low center supra submit ~neutral">{{ .strings.send }}</span>
</label>
<span class="button ~info @low center supra ~neutral" id="save-announce">{{ .strings.saveAsTemplate }}</span>
</div>
</div>
<div class="col card ~neutral @low">
<span class="subheading supra">{{ .strings.preview }}</span>
<div class="mt-half" id="announce-preview"></div>
</div>
</div>
</form>
</div>
<div id="modal-customize" class="modal">
<div class="modal-content card ~neutral">
<span class="heading">{{ .strings.customizeMessages }} <span class="modal-close">&times;</span></span>
<p class="content">{{ .strings.customizeMessagesDescription }}</p>
<div class="table-responsive">
<table class="table ~neutral">
<thead>
<tr>
<th>{{ .strings.name }}</th>
<th>{{ .strings.reset }}</th>
<th>{{ .strings.edit }}</th>
</tr>
</thead>
<tbody id="customize-list"></tbody>
</table>
</div>
</div>
</div>
<div id="modal-editor" class="modal">
<form class="modal-content wide card ~neutral" id="form-editor" href>
<span class="heading"><span id="header-editor"></span> <span class="modal-close">&times;</span></span>
<div class="row">
<div class="col flex-col content mt-half">
<span class="label supra" for="editor-variables" id="label-editor-variables">{{ .strings.variables }}</span>
<div id="editor-variables"></div>
<span class="label supra" for="editor-conditionals" id="label-editor-conditionals">{{ .strings.conditionals }}</span>
<div id="editor-conditionals"></div>
<label class="label supra" for="textarea-editor">{{ .strings.message }}</label>
<textarea id="textarea-editor" class="textarea full-width flex-auto ~neutral @low mt-half monospace"></textarea>
<p class="support mt-half mb-1">{{ .strings.markdownSupported }}</p>
<div class="flex-row">
<label class="full-width ml-half">
<input type="submit" class="unfocused">
<span class="button ~urge @low full-width center supra submit ~neutral">{{ .strings.submit }}</span>
</label>
</div>
</div>
<div class="col card ~neutral @low">
<span class="subheading supra">{{ .strings.preview }}</span>
<div class="mt-half" id="editor-preview"></div>
</div>
</div>
</form>
</div>
<div id="modal-restart" class="modal">
<div class="modal-content card ~critical @low ~neutral">
<span class="heading">{{ .strings.settingsRestartRequired }} <span class="modal-close">&times;</span></span>
<p class="content pb-1">{{ .strings.settingsRestartRequiredDescription }}</p>
<div class="fr">
<span class="button ~info @low mb-half ~neutral" id="settings-apply-no-restart">{{ .strings.settingsApplyRestartLater }}</span>
<span class="button ~critical @low ~neutral" id="settings-apply-restart">{{ .strings.settingsApplyRestartNow }}</span>
</div>
</div>
</div>
<div id="modal-refresh" class="modal">
<div class="modal-content card ~neutral @low">
<span class="heading">{{ .strings.settingsApplied }}</span>
<p class="content">{{ .strings.settingsRefreshPage }}</p>
</div>
</div>
<div id="modal-send-pwr" class="modal">
<div class="modal-content card ~neutral @low">
<span class="heading">{{ .strings.sendPWR }}</span>
<p class="content" id="send-pwr-note"></p>
<span class="button ~urge @low mt-half ~neutral" id="send-pwr-link">{{ .strings.copy }}</span>
</div>
</div>
<div id="modal-ombi-profile" class="modal">
<form class="modal-content card ~neutral" id="form-ombi-defaults" href>
<span class="heading">{{ .strings.ombiProfile }} <span class="modal-close">&times;</span></span>
<p class="content">{{ .strings.ombiUserDefaultsDescription }}</p>
<div class="select ~neutral @low mb-1">
<select></select>
</div>
<label>
<input type="submit" class="unfocused">
<span class="button ~urge @low full-width center supra submit ~neutral">{{ .strings.submit }}</span>
</label>
</form>
</div>
<div id="modal-user-profiles" class="modal">
<div class="modal-content wide card ~neutral">
<span class="heading">{{ .strings.userProfiles }} <span class="modal-close">&times;</span></span>
<p class="support lg">{{ .strings.userProfilesDescription }}</p>
<div class="table-responsive">
<table class="table ~neutral">
<thead>
<tr>
<th>{{ .strings.name }}</th>
<th>{{ .strings.userProfilesIsDefault }}</th>
{{ if .ombiEnabled }}
<th>Ombi</th>
{{ end }}
<th>{{ .strings.from }}</th>
<th>{{ .strings.userProfilesLibraries }}</th>
<th><span class="button ~neutral @high" id="button-profile-create">{{ .strings.create }}</span></th>
</tr>
</thead>
<tbody id="table-profiles"></tbody>
</table>
</div>
</div>
</div>
<span class="heading">{{ .strings.addProfile }} <span class="modal-close">&times;</span></span>
<p class="content">{{ .strings.addProfileDescription }}</p>
<span class="supra">{{ .strings.addProfileNameOf }} </span>
<input type="text" class="field input ~neutral @high mt-half mb-1" placeholder="{{ .strings.name }}" id="add-profile-name">
<label>
<span class="supra">{{ .strings.user }}</span>
<div class="select ~neutral @low mt-half mb-1">
<select id="add-profile-user"></select>
</div>
</label>
<label class="switch mb-1">
<input type="checkbox" id="add-profile-homescreen" checked>
<span>{{ .strings.addProfileStoreHomescreenLayout }}</span>
</label>
<label>
<input type="submit" class="unfocused">
<span class="button ~urge @low full-width center supra submit ~neutral">{{ .strings.create }}</span>
</label>
<div id="modal-update" class="modal">
<div class="modal-content wide card ~neutral">
<span class="heading">{{ .strings.updates }} <span class="modal-close">&times;</span></span>
<p class="content">
<h2>
<a id="update-version"></a> (<span class="monospace" id="update-commit"></span>)
</h2>
</p><p class="content" id="update-description"></p>
<p class="support" id="update-date"></p>
<div class="content markdown-box" id="update-changelog"></div>
<span class="button ~info @low full-width center ~neutral" id="update-download">{{ .strings.download }}</span>
<span class="button ~urge @low full-width center ~neutral" id="update-update">{{ .strings.update }}</span>
</div>
</div>
{{ if .telegramEnabled }}
<div id="modal-telegram" class="modal">
</div>
<span class="heading mb-1">{{ .strings.linkTelegram }}</span>
<p class="content mb-1">{{ .strings.sendPIN }}</p>
<h1 class="ac" id="telegram-pin"></h1>
<span class="shield ~info mr-1 ~neutral">
<span class="icon">
<i class="ri-telegram-line"></i>
</span>
</span>
&#64;
<span class="button ~info @low full-width center mt-1 ~neutral" id="telegram-waiting">{{ .strings.success }}</span>
{{ end }}
{{ if .discordEnabled }}
<div id="modal-discord" class="modal">
<div class="modal-content card ~neutral">
<span class="heading mb-1"><span id="discord-header"></span><span class="modal-close">&times;</span></span>
<p class="content mb-1" id="discord-description"></p>
<div class="row">
<input type="search" class="col sm field ~neutral @low input" id="discord-search" placeholder="user#1234">
</div>
<table class="table ~neutral"><tbody id="discord-list"></tbody></table>
</div>
</div>
{{ end }}
<div id="modal-matrix" class="modal">
<form class="modal-content card ~neutral" id="form-matrix" href>
<span class="heading">{{ .strings.linkMatrix }}</span>
<p class="content">{{ .strings.linkMatrixDescription }}</p>
<input type="text" class="field input ~neutral @high mt-half mb-1" placeholder="{{ .strings.matrixHomeServer }}" id="matrix-homeserver">
<input type="text" class="field input ~neutral @high mt-half mb-1" placeholder="{{ .strings.username }}" id="matrix-user">
<input type="password" class="field input ~neutral @high mt-half mb-1" placeholder="{{ .strings.password }}" id="matrix-password">
<label>
<input type="submit" class="unfocused">
<span class="button ~urge @low full-width center supra submit ~neutral">{{ .strings.submit }}</span>
</label>
</form>
</div>
<div id="notification-box"></div>
<span class="dropdown" tabindex="0" id="lang-dropdown">
<span class="button ~urge dropdown-button ~neutral">
<i class="ri-global-line"></i>
<span class="ml-1 chev"></span>
</span>
<div class="dropdown-display">
<div class="card ~neutral @low">
<label class="switch pb-1">
<input type="radio" name="lang-time" id="lang-12h">
<span>{{ .strings.time12h }}</span>
</label>
<label class="switch pb-1">
<input type="radio" name="lang-time" id="lang-24h">
<span>{{ .strings.time24h }}</span>
</label>
<div id="lang-list"></div>
</div>
</div>
</span>
<span class="button ~warning ~neutral" alt="{{ .strings.theme }}" id="button-theme"><i class="ri-sun-line"></i></span>
<div class="page-container">
<div class="mb-1">
<header class="flex flex-wrap items-center justify-between">
<div class="text-neutral-700">
<span id="button-tab-invites" class="tab-button portal ~neutral">{{ .strings.invites }}</span>
<span id="button-tab-accounts" class="tab-button portal ~neutral">{{ .strings.accounts }}</span>
<span id="button-tab-settings" class="tab-button portal ~neutral">{{ .strings.settings }}</span>
</div>
</header>
</div>
<div class="mb-1">
<div class="text-neutral-700">
<span class="button ~critical @low mb-1 unfocused ~neutral" id="logout-button">{{ .strings.logout }}</span>
</div>
</div>
<div id="tab-invites">
<div class="card ~neutral @low invites mb-1">
<span class="heading">{{ .strings.invites }}</span>
<div id="invites"></div>
</div>
<div class="card ~neutral @low">
<span class="heading">{{ .strings.create }}</span>
<div class="row" id="create-inv">
<div class="card ~neutral @low col">
<div class="row mb-1">
<label class="col mr-1">
<input type="radio" name="duration" class="unfocused" id="radio-inv-duration" checked>
<span class="button ~neutral @high supra full-width center">{{ .strings.inviteDuration }}</span>
</label>
<label class="col ml-1">
<input type="radio" name="duration" class="unfocused" id="radio-user-expiry">
<span class="button ~neutral @low supra full-width center">{{ .strings.userExpiry }}</span>
</label>
</div>
<div id="inv-duration">
<div class="row">
<div class="col">
<label class="label supra" for="create-months">{{ .strings.inviteMonths }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="create-months">
<option>0</option>
</select>
</div>
</div>
<div class="col">
<label class="label supra" for="create-days">{{ .strings.inviteDays }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="create-days">
<option>0</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col">
<label class="label supra" for="create-hours">{{ .strings.inviteHours }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="create-hours">
<option>0</option>
</select>
</div>
</div>
<div class="col">
<label class="label supra" for="create-minutes">{{ .strings.inviteMinutes }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="create-minutes">
<option>0</option>
</select>
</div>
</div>
</div>
</div>
<div id="user-expiry" class="unfocused">
<p class="support">{{ .strings.userExpiryDescription }}</p>
<div class="mb-half">
<label for="create-user-expiry-enabled" class="button ~neutral @low">
<input type="checkbox" id="create-user-expiry-enabled" aria-label="User duration enabled">
<span class="ml-half">{{ .strings.enabled }} </span>
</label>
</div>
<div class="row">
<div class="col">
<label class="label supra" for="user-months">{{ .strings.inviteMonths }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="user-months">
<option>0</option>
</select>
</div>
</div>
<div class="col">
<label class="label supra" for="user-days">{{ .strings.inviteDays }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="user-days">
<option>0</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col">
<label class="label supra" for="user-hours">{{ .strings.inviteHours }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="user-hours">
<option>0</option>
</select>
</div>
</div>
<div class="col">
<label class="label supra" for="user-minutes">{{ .strings.inviteMinutes }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="user-minutes">
<option>0</option>
</select>
</div>
</div>
</div>
</div>
<div class="col">
<label class="label supra" for="create-label"> {{ .strings.label }}</label>
<input type="text" id="create-label" class="input ~neutral @low mb-1 mt-half">
</div>
</div>
<div class="card ~neutral @low col">
<label class="label supra" for="create-uses">{{ .strings.inviteNumberOfUses }}</label>
<div class="flex-expand mb-1 mt-half">
<input type="number" min="0" id="create-uses" class="input ~neutral @low mr-1" value=1>
<label for="create-inf-uses" class="button ~neutral @low" title="Set uses to infinite">
<span></span>
<input type="checkbox" class="unfocused" id="create-inf-uses" aria-label="Set uses to infinite">
</label>
</div>
<p class="support unfocused" id="create-inf-uses-warning"><span class="badge ~critical ~neutral">{{ .strings.warning }}</span> {{ .strings.inviteInfiniteUsesWarning }}</p>
<label class="label supra">{{ .strings.profile }}</label>
<div class="select ~neutral @low mb-1 mt-half">
<select id="create-profile">
</select>
</div>
<div id="create-send-to-container">
<label class="label supra">{{ .strings.inviteSendToEmail }}</label>
<div class="flex-expand mb-1 mt-half">
{{ if .discordEnabled }}
<input type="text" id="create-send-to" class="input ~neutral @low mr-1" placeholder="example@example.com | user#1234">
<span id="create-send-to-search" class="button ~neutral @low mr-1">
<i class="icon ri-search-2-line" title="{{ .strings.search }}"></i>
</span>
{{ else }}
<input type="email" id="create-send-to" class="input ~neutral @low mr-1" placeholder="example@example.com">
{{ end }}
<label for="create-send-to-enabled" class="button ~neutral @low">
<input type="checkbox" id="create-send-to-enabled" aria-label="Send to address enabled">
</label>
</div>
</div>
<span class="button ~urge @low supra full-width center lg ~neutral" id="create-submit">{{ .strings.create }}</span>
</div>
</div>
</div>
</div>
<div id="tab-accounts" class="unfocused">
<div class="card ~neutral @low accounts mb-1">
<div class="flex-expand row">
<div class="row">
<span class="heading mr-1 col sm">{{ .strings.accounts }}</span>
<input type="search" class="col sm field ~neutral @low input search ml-1 mr-1" id="accounts-search" placeholder="{{ .strings.search }}">
</div>
<div class="row">
<span class="col sm button ~neutral @low center mb-half" id="accounts-add-user">{{ .quantityStrings.addUser.Singular }}</span>
<div id="accounts-announce-dropdown" class="col sm dropdown" tabindex="0">
<span class="h-100 sm button ~info @low center mb-half ~neutral" id="accounts-announce">{{ .strings.announce }}</span>
<div class="dropdown-display">
<div class="card ~neutral @low">
<span class="supra sm">{{ .strings.templates }}</span>
<div id="accounts-announce-templates"></div>
</div>
</div>
</div>
<span class="col sm button ~urge @low center mb-half ~neutral" id="accounts-modify-user">{{ .strings.modifySettings }}</span>
<span class="col sm button ~warning @low center mb-half ~neutral" id="accounts-extend-expiry">{{ .strings.extendExpiry }}</span>
<div id="accounts-disable-enable-dropdown" class="col sm dropdown manual" tabindex="0">
<span class="h-100 sm button ~positive @low center mb-half ~neutral" id="accounts-disable-enable">{{ .strings.disable }}</span>
<div class="dropdown-display">
<div class="card ~neutral @low">
<span class="button ~neutral sm full-width accounts-announce-template-button" id="accounts-enable-expiry">{{ .strings.setExpiry }}</span>
</div>
</div>
</div>
<span class="col sm button ~info @low center mb-half unfocused ~neutral" id="accounts-send-pwr">{{ .strings.sendPWR }}</span>
<span class="col sm button ~critical @low center mb-half ~neutral" id="accounts-delete-user">{{ .quantityStrings.deleteUser.Singular }}</span>
</div>
</div>
<div class="card ~neutral @low accounts-header table-responsive mt-half">
<table class="table ~neutral">
<thead>
<tr>
<th><input type="checkbox" value="" id="accounts-select-all"></th>
<th>{{ .strings.username }}</th>
<th>{{ .strings.emailAddress }}</th>
{{ if .telegramEnabled }}
<th>Telegram</th>
{{ end }}
{{ if .matrixEnabled }}
<th>Matrix</th>
{{ end }}
{{ if .discordEnabled }}
<th>Discord</th>
{{ end }}
<th>{{ .strings.expiry }}</th>
<th>{{ .strings.lastActiveTime }}</th>
</tr>
</thead>
<tbody id="accounts-list"></tbody>
</table>
</div>
</div>
</div>
<div id="tab-settings" class="unfocused">
<div class="card ~neutral @low settings overflow">
<div class="flex-expand">
<div class="flex-row">
<span class="heading">{{ .strings.settings }}</span>
<label for="settings-advanced-enabled" class="button ~neutral @low ml-1">
<input type="checkbox" id="settings-advanced-enabled" aria-label="Advanced settings enabled">
<span class="ml-half">{{ .strings.advancedSettings }} </span>
</label>
</div>
<div>
<span class="button ~neutral @low" id="settings-restart">{{ .strings.settingsRestart }}</span>
<span class="button ~urge @low unfocused ~neutral" id="settings-save">{{ .strings.settingsSave }}</span>
</div>
</div>
<div class="row">
<div class="card ~neutral @low col" id="settings-sidebar">
<aside class="aside sm ~info mb-half ~neutral" id="settings-message">Note: <span class="badge ~critical ~neutral">*</span> indicates a required field, <span class="badge ~info ~neutral">R</span> indicates changes require a restart.</aside>
<span class="button ~neutral @low settings-section-button mb-half" id="setting-about"><span class="flex">{{ .strings.aboutProgram }} <i class="ri-information-line ml-half"></i></span></span>
<span class="button ~neutral @low settings-section-button mb-half" id="setting-profiles"><span class="flex">{{ .strings.userProfiles }} <i class="ri-user-line ml-half"></i></span></span>
</div>
<div class="card ~neutral @low col overflow" id="settings-panel"></div>
</div>
</div>
</div>
</div>
<script src="{{ .urlBase }}/js/admin.js" type="module"></script>
</html>

@ -1,12 +1,6 @@
#!/bin/bash
if [[ "$1" == "html" ]]; then
for f in $2/*.html; do
for color in neutral positive urge warning info critical; do
sed -i "s/~${color}/~${color} dark:~d_${color}/g" $f
done
done
elif [[ "$1" == "ts" ]]; then
if [[ "$1" == "ts" ]]; then
for f in $2/*.ts; do
# FIXME: inline html
for l in $(grep -n "~neutral\|~positive\|~urge\|~warning\|~info\|~critical" $f | sed -e 's/:.*//g'); do

@ -0,0 +1,41 @@
let parser = require("jsdom");
let fs = require("fs");
let path = require("path");
const fixHTML = (infile, outfile) => {
console.log(infile, outfile)
let doc = new parser.JSDOM(fs.readFileSync(infile));
for (let item of ["badge", "chip", "shield", "input", "table", "button", "portal", "select", "aside", "card"]) {
let items = doc.window.document.body.querySelectorAll("."+item);
for (let i = 0; i < items.length; i++) {
let hasColor = false;
for (let color of ["neutral", "positive", "urge", "warning", "info", "critical"]) {
//console.log(color);
if (items[i].classList.contains("~"+color)) {
hasColor = true;
// console.log("adding to", items[i].classList)
items[i].classList.add("dark:~d_"+color);
break;
}
}
if (!hasColor) {
items[i].classList.add("~neutral");
items[i].classList.add("dark:~d_neutral");
}
if (!items[i].classList.contains("@low") && !items[i].classList.contains("@high")) {
items[i].classList.add("@low");
}
}
}
fs.writeFileSync(outfile, doc.window.document.documentElement.outerHTML);
};
let inpath = process.argv[process.argv.length-2];
let outpath = process.argv[process.argv.length-1];
let files = fs.readdirSync(inpath);
for (let i = 0; i < files.length; i++) {
if (files[i].indexOf(".html")>=0) {
fixHTML(path.join(inpath, files[i]), path.join(outpath, files[i]));
}
}

@ -1,4 +1,4 @@
import { toggleTheme, loadTheme } from "./modules/theme.js";
import { nightwind } from "./modules/theme.js";
import { lang, LangFile, loadLangSelector } from "./modules/lang.js";
import { Modal } from "./modules/modal.js";
import { Tabs } from "./modules/tabs.js";
@ -9,7 +9,8 @@ import { ProfileEditor } from "./modules/profiles.js";
import { _get, _post, notificationBox, whichAnimationEvent, toggleLoader } from "./modules/common.js";
import { Updater } from "./modules/update.js";
loadTheme();
let theme = new nightwind();
const themeButton = document.getElementById('button-theme') as HTMLSpanElement;
const switchThemeIcon = () => {
const icon = themeButton.childNodes[0] as HTMLElement;
@ -28,7 +29,7 @@ const switchThemeIcon = () => {
}
};
themeButton.onclick = () => {
toggleTheme();
theme.toggle();
switchThemeIcon();
}
switchThemeIcon();

@ -17,3 +17,51 @@ export function loadTheme() {
document.documentElement.classList.remove('light');
}
}
export class nightwind {
beforeTransition = () => {
const doc = document.documentElement;
const onTransitionDone = () => {
doc.classList.remove('nightwind');
doc.removeEventListener('transitionend', onTransitionDone);
}
doc.addEventListener('transitionend', onTransitionDone);
if (!doc.classList.contains('nightwind')) {
doc.classList.add('nightwind');
}
};
constructor() {
const theme = localStorage.getItem("theme");
if (theme == "dark") {
this.enable(true);
} else if (theme == "light") {
this.enable(false);
} else if (window.matchMedia('(prefers-color-scheme: dark)').media !== 'not all') {
this.enable(true);
}
}
toggle = () => {
this.beforeTransition();
if (!document.documentElement.classList.contains('dark')) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
localStorage.setItem('theme', document.documentElement.classList.contains('dark') ? "dark" : "light");
};
enable = (dark: boolean) => {
const mode = dark ? "dark" : "light";
const opposite = dark ? "light" : "dark";
localStorage.setItem('theme', dark ? "dark" : "light");
this.beforeTransition();
if (document.documentElement.classList.contains(opposite)) {
document.documentElement.classList.remove(opposite);
}
document.documentElement.classList.add(mode);
};
}

Loading…
Cancel
Save