web: remove almost every use of ml/mr

replace with flex and gap mostly. For #450.
This commit is contained in:
Harvey Tindall
2025-12-08 15:12:40 +00:00
parent 9c9e55147d
commit 362984a391
25 changed files with 339 additions and 268 deletions

View File

@@ -1,16 +1,16 @@
{{ if .discordEnabled }}
<div id="modal-discord" class="modal">
<div class="card relative mx-auto my-[10%] w-4/5 lg:w-1/3">
<span class="heading mb-4">{{ .strings.linkDiscord }}</span>
<p class="content mb-4"> {{ .discordSendPINMessage }}</p>
<h1 class="text-center text-2xl mb-2 pin"></h1>
<div class="row center">
<a class="my-5 hover:underline">
<span class="mr-2">{{ .strings.joinTheServer }}</span>
<span id="discord-invite"></span>
<div class="card relative mx-auto my-[10%] w-4/5 lg:w-1/3 flex flex-col gap-4">
<span class="heading">{{ .strings.linkDiscord }}</span>
<p class="content"> {{ .discordSendPINMessage }}</p>
<h1 class="text-center text-2xl pin"></h1>
<div class="flex flex-row gap-2 justify-center items-center">
<a class="hover:underline flex flex-row gap-4 items-center">
<span>{{ .strings.joinTheServer }}</span>
<span id="discord-invite" class="flex flex-row gap-2 items-center"></span>
</a>
</div>
<span class="button ~info @low full-width center mt-4" id="discord-waiting">{{ .strings.success }}</span>
<span class="button ~info @low full-width center" id="discord-waiting">{{ .strings.success }}</span>
</div>
</div>
{{ end }}

View File

@@ -1,18 +1,18 @@
{{ if .matrixEnabled }}
<div id="modal-matrix" class="modal">
<div class="card relative mx-auto my-[10%] w-4/5 lg:w-1/3">
<span class="heading mb-4">{{ .strings.linkMatrix }}</span>
<p class="content mb-4"> {{ .strings.matrixEnterUser }}</p>
<div class="card relative mx-auto my-[10%] w-4/5 lg:w-1/3 flex flex-col gap-4">
<span class="heading">{{ .strings.linkMatrix }}</span>
<p class="content"> {{ .strings.matrixEnterUser }}</p>
<input type="text" class="input ~neutral @high" placeholder="@user:riot.im" id="matrix-userid">
<div class="subheading link-center mt-4">
<span class="shield ~info mr-4">
<div class="subheading flex flex-row gap-2 justify-center items-center">
<span class="shield ~info">
<span class="icon">
<i class="ri-chat-3-line"></i>
</span>
</span>
{{ .matrixUser }}
<span>{{ .matrixUser }}</span>
</div>
<span class="button ~info @low full-width center mt-4" id="matrix-send">{{ .strings.submit }}</span>
<span class="button ~info @low full-width center" id="matrix-send">{{ .strings.submit }}</span>
</div>
</div>
{{ end }}

View File

@@ -1,18 +1,18 @@
{{ if .telegramEnabled }}
<div id="modal-telegram" class="modal">
<div class="card relative mx-auto my-[10%] w-4/5 lg:w-1/3">
<span class="heading mb-4">{{ .strings.linkTelegram }}</span>
<p class="content mb-4">{{ .strings.sendPIN }}</p>
<p class="text-center text-2xl mb-2 pin"></p>
<a class="subheading link link-center" href="{{ .telegramURL }}" target="_blank">
<span class="shield ~info mr-4">
<div class="card relative mx-auto my-[10%] w-4/5 lg:w-1/3 flex flex-col gap-4">
<span class="heading">{{ .strings.linkTelegram }}</span>
<p class="content">{{ .strings.sendPIN }}</p>
<p class="text-center text-2xl pin"></p>
<a class="subheading link flex flex-row gap-2 justify-center items-center" href="{{ .telegramURL }}" target="_blank">
<span class="shield ~info">
<span class="icon">
<i class="ri-telegram-line"></i>
</span>
</span>
&#64;<span class="username">{{ .telegramUsername }}</span>
<span class="hover:underline">&#64;<span class="username">{{ .telegramUsername }}</span></span>
</a>
<span class="button ~info @low full-width center mt-4" id="telegram-waiting">{{ .strings.success }}</span>
<span class="button ~info @low full-width center" id="telegram-waiting">{{ .strings.success }}</span>
</div>
</div>
{{ end }}

View File

@@ -45,7 +45,7 @@
<p>{{ .strings.buildTime }} <span class="text-black dark:text-white font-mono bg-inherit">{{ .buildTime }}</span></p>
<p>{{ .strings.builtBy }} <span class="text-black dark:text-white font-mono bg-inherit">{{ .builtBy }}</span></p>
<p>{{ .strings.buildTags }} <span class="text-black dark:text-white font-mono bg-inherit">{{ .buildTags }}</span></p>
<div class="flex flex-row flex-wrap gap-2 my-2">
<div class="flex flex-row flex-wrap gap-2">
<a class="button ~neutral lang-link flex flex-row gap-2" href="https://github.com/hrfee/jfa-go"><i class="ri-github-line"></i>github</a>
<a class="button ~urge lang-link" href="https://wiki.jfa-go.com">wiki/docs</a>
<a class="button ~positive lang-link" href="https://weblate.jfa-go.com">translation</a>
@@ -56,9 +56,9 @@
<span class="chev"></span>
</a>
<div class="dropdown-display">
<div class="card ~neutral @low">
<a href="https://github.com/sponsors/hrfee" target="_blank" class="button ~neutral mb-2 w-full lang-link">GitHub</a>
<a href="https://ko-fi.com/hrfee" target="_blank" class="button ~neutral mb-2 w-full lang-link">Ko-fi</a>
<div class="card ~neutral @low flex flex-col gap-2">
<a href="https://github.com/sponsors/hrfee" target="_blank" class="button ~neutral w-full lang-link">GitHub</a>
<a href="https://ko-fi.com/hrfee" target="_blank" class="button ~neutral w-full lang-link">Ko-fi</a>
</div>
</div>
</div>
@@ -82,10 +82,10 @@
</div>
</div>
<div id="modal-modify-user" class="modal">
<form class="card relative mx-auto my-[10%] w-11/12 sm:w-4/5 lg:w-1/3" id="form-modify-user" href="">
<form class="card relative mx-auto my-[10%] w-11/12 sm:w-4/5 lg:w-1/3 flex flex-col gap-2" id="form-modify-user" href="">
<span class="heading"><span id="header-modify-user"></span> <span class="modal-close">&times;</span></span>
<p class="content my-4">{{ .strings.modifySettingsDescription }}</p>
<div class="flex flex-col gap-4 my-2">
<p class="content">{{ .strings.modifySettingsDescription }}</p>
<div class="flex flex-col gap-4">
<div class="flex flex-row gap-2">
<label class="grow">
<input type="radio" name="modify-user-source" class="unfocused" id="radio-use-profile" checked>
@@ -257,7 +257,7 @@
<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-4" placeholder="{{ .strings.sendDeleteNotificationExample }}"></textarea>
<textarea id="textarea-extend-enable" class="textarea full-width ~neutral @low" placeholder="{{ .strings.sendDeleteNotificationExample }}"></textarea>
<label>
<input type="submit" class="unfocused">
<span class="button ~critical @low full-width center supra submit">{{ .strings.submit }}</span>
@@ -375,7 +375,7 @@
<button class="button ~info @low" id="settings-backups-backup">{{ .strings.backupNow }}</button>
<button class="button ~neutral @low" id="settings-backups-upload">{{ .strings.backupUpload }}</button>
<input id="backups-file" name="backups-file" type="file" hidden>
<button class="button ~neutral @low" id="settings-backups-sort-direction">{{ .strings.sortDirection }}</button>
<button class="button ~neutral @low flex flex-row gap-2" id="settings-backups-sort-direction">{{ .strings.sortDirection }}</button>
</div>
<div class="overflow-x-auto text-xs md:text-sm">
<table class="table">
@@ -400,7 +400,7 @@
<p class="content">{{ .strings.backupCanDownload }}</p>
</div>
<div>
<button class="button flex w-full ~info @low"><span class="flex items-center" id="settings-backed-up-download">{{ .strings.download }}</span></button>
<button class="button flex w-full ~info @low"><span class="flex flex-row gap-2 items-center" id="settings-backed-up-download">{{ .strings.download }}</span></button>
</div>
</div>
</div>
@@ -515,27 +515,27 @@
</form>
</div>
<div id="modal-update" class="modal">
<div class="relative mx-auto my-[10%] w-11/12 sm:w-4/5 lg:w-1/3 content card">
<div class="relative mx-auto my-[10%] w-11/12 sm:w-4/5 lg:w-1/3 card flex flex-col gap-2">
<span class="heading">{{ .strings.updates }} <span class="modal-close">&times;</span></span>
<p class="content">
<h2 class="mt-2">
<div class="content flex flex-col">
<h2>
<a id="update-version"></a> (<span class="font-mono bg-inherit" id="update-commit"></span>)
</h2>
<p class="content mt-2" id="update-description"></p>
<p class="support mt-2" id="update-date"></p>
<div class="content markdown-box mt-2" id="update-changelog"></div>
</p>
<span class="button ~info @low full-width center mt-2" id="update-download">{{ .strings.download }}</span>
<span class="button ~urge @low full-width center mt-2" id="update-update">{{ .strings.update }}</span>
<p class="content" id="update-description"></p>
<div class="content markdown-box" id="update-changelog"></div>
<p class="support" id="update-date"></p>
</div>
<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>
{{ template "account-linking-telegram.html" . }}
{{ if .discordEnabled }}
<div id="modal-discord" class="modal">
<div class="card relative mx-auto my-[10%] w-11/12 sm:w-4/5 lg:w-1/3">
<span class="heading mb-4"><span id="discord-header"></span><span class="modal-close">&times;</span></span>
<p class="content mb-4" id="discord-description"></p>
<div class="row">
<div class="card relative mx-auto my-[10%] w-11/12 sm:w-4/5 lg:w-1/3 flex flex-col gap-2">
<span class="heading"><span id="discord-header"></span><span class="modal-close">&times;</span></span>
<p class="content" id="discord-description"></p>
<div>
<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>
@@ -543,12 +543,21 @@
</div>
{{ end }}
<div id="modal-matrix" class="modal">
<form class="card relative mx-auto my-[10%] w-11/12 sm:w-4/5 lg:w-1/3" id="form-matrix" href="">
<form class="card relative mx-auto my-[10%] w-11/12 sm:w-4/5 lg:w-1/3 flex flex-col gap-2" id="form-matrix" href="">
<span class="heading">{{ .strings.linkMatrix }}</span>
<p class="content my-4">{{ .strings.linkMatrixDescription }}</p>
<input type="text" class="field input ~neutral @high mt-4 mb-2" placeholder="{{ .strings.matrixHomeServer }}" id="matrix-homeserver">
<input type="text" class="field input ~neutral @high mt-4 mb-2" placeholder="{{ .strings.username }}" id="matrix-user">
<input type="password" class="field input ~neutral @high mt-4 mb-2" placeholder="{{ .strings.password }}" id="matrix-password">
<p class="content">{{ .strings.linkMatrixDescription }}</p>
<label class="flex flex-col gap-2">
<span class="supra">{{ .strings.matrixHomeServer }}</span>
<input type="text" class="field input ~neutral @high" placeholder="{{ .strings.matrixHomeServer }}" id="matrix-homeserver">
</label>
<label class="flex flex-col gap-2">
<span class="supra">{{ .strings.username }}</span>
<input type="text" class="field input ~neutral @high" placeholder="{{ .strings.username }}" id="matrix-user">
</label>
<label class="flex flex-col gap-2">
<span class="supra">{{ .strings.password }}</span>
<input type="password" class="field input ~neutral @high" placeholder="{{ .strings.password }}" id="matrix-password">
</label>
<label>
<input type="submit" class="unfocused">
<span class="button ~urge @low full-width center supra submit">{{ .strings.submit }}</span>
@@ -726,7 +735,7 @@
</div>
</div>
<div id="tab-accounts" class="flex flex-col gap-4 unfocused">
<div class="card @low dark:~d_neutral accounts mb-4 overflow-visible flex flex-col gap-2">
<div class="card @low dark:~d_neutral accounts overflow-visible flex flex-col gap-2">
<div id="accounts-filter-dropdown" class="dropdown manual z-10 w-full">
<div class="flex flex-col md:flex-row align-middle gap-2">
<div class="flex flex-row gap-4 align-middle justify-between md:justify-normal">
@@ -747,7 +756,7 @@
</div>
</div>
<div class="dropdown-display max-w-full">
<div class="card ~neutral @low mt-2 overflow-x-scroll" id="accounts-filter-list">
<div class="card ~neutral @low overflow-x-scroll" id="accounts-filter-list">
<p class="supra pb-2">{{ .strings.filters }}</p>
</div>
</div>
@@ -765,11 +774,11 @@
<button class="button ~neutral @low center accounts-load-all">{{ .strings.loadAll }}</button>
<span class="button ~neutral @low center " id="accounts-add-user">{{ .quantityStrings.addUser.Singular }}</span>
<div id="accounts-announce-dropdown" class="dropdown pb-0i " tabindex="0">
<span class="w-full button ~info @low center items-baseline" id="accounts-announce">{{ .strings.announce }}</span>
<span class="w-full button ~info @low center items-baseline flex flex-row gap-2" 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 id="accounts-announce-templates" class="flex flex-col gap-2"></div>
</div>
</div>
</div>
@@ -780,9 +789,9 @@
<div id="accounts-expiry-dropdown" class="dropdown pb-0i " tabindex="0">
<span class="w-full button ~positive @low center items-baseline flex flex-row gap-2" id="accounts-expiry-dropdown-button">{{ .strings.expiry }}<i class="ri-arrow-down-s-line"></i></span>
<div class="dropdown-display">
<div class="card ~neutral @low">
<div class="card ~neutral @low flex flex-col gap-2">
<span class="button ~warning full-width @low center" id="accounts-extend-expiry">{{ .strings.extendExpiry }}</span>
<span class="button ~critical full-width @low center mt-2" id="accounts-remove-expiry">{{ .strings.removeExpiry }}</span>
<span class="button ~critical full-width @low center" id="accounts-remove-expiry">{{ .strings.removeExpiry }}</span>
</div>
</div>
</div>
@@ -850,7 +859,7 @@
</div>
</div>
<div id="tab-activity" class="flex flex-col gap-4 unfocused">
<div class="card @low dark:~d_neutral activity mb-4 overflow-visible flex flex-col gap-2">
<div class="card @low dark:~d_neutral activity overflow-visible flex flex-col gap-2">
<div id="activity-filter-dropdown" class="dropdown manual z-10 w-full" tabindex="0">
<div class="flex flex-col md:flex-row align-middle gap-2">
<div class="flex flex-row gap-4 align-middle justify-between md:justify-normal">
@@ -874,7 +883,7 @@
</div>
</div>
<div class="dropdown-display max-w-full">
<div class="card ~neutral @low mt-2 overflow-x-scroll" id="activity-filter-list">
<div class="card ~neutral @low overflow-x-scroll" id="activity-filter-list">
<p class="supra pb-2">{{ .strings.filters }}</p>
</div>
</div>
@@ -899,7 +908,7 @@
</div>
</div>
</div>
<div id="activity-card-list"></div>
<div id="activity-card-list" class="flex flex-col gap-2"></div>
<div id="activity-loader"></div>
<div class="flex flex-row gap-2 justify-center">
<button class="button ~neutral @low" id="activity-load-more">{{ .strings.loadMore }}</button>
@@ -944,9 +953,9 @@
</div>
<div class="card ~neutral @low overflow flex-1 grow" id="settings-panel">
<div class="settings-section unfocused h-[100%]" id="settings-not-found">
<div class="flex flex-col h-[100%] justify-center items-center">
<span class="text-2xl font-medium italic mb-2">{{ .strings.noResultsFound }}</span>
<span class="mb-2 px-12 text-center">{{ .strings.settingsMaybeUnderAdvanced }}</span>
<div class="flex flex-col gap-4 h-[100%] justify-center items-center">
<span class="text-2xl font-medium italic">{{ .strings.noResultsFound }}</span>
<span class="px-12 text-center">{{ .strings.settingsMaybeUnderAdvanced }}</span>
<button class="button ~neutral @low settings-search-clear flex flex-row gap-2">
<span>{{ .strings.clearSearch }}</span><i class="ri-close-line"></i>
</button>

View File

@@ -9,34 +9,30 @@
<body>
<div class="page-container m-2 lg:my-20 lg:mx-64">
<div class="card ~critical sectioned">
<section class="section ~critical">
<section class="section ~critical flex flex-col gap-2">
<span class="heading">Crash report for jfa-go</span>
{{ if .Err }}
<div class="font-mono bg-inherit pre-line mt-4 mb-4">
<div class="font-mono bg-inherit pre-line">
Error: {{ .Err }}
</div>
{{ end }}
<a class="button ~critical mb-4" target="_blank" href="https://github.com/hrfee/jfa-go/issues/new/choose">Create an Issue</a>
<a class="button ~critical w-full center" target="_blank" href="https://github.com/hrfee/jfa-go/issues/new/choose">Create an Issue</a>
</section>
<section class="section ~neutral @low">
<div class="flex flex-row justify-between">
<span class="subheading">Full Log</span>
<span class="button ~urge ml-4" id="copy-log">Copy</span>
<section class="section ~neutral @low flex flex-col gap-4">
<div class="flex flex-row justify-between gap-4">
<span class="subheading font-medium">Full Log</span>
<span class="button ~urge" id="copy-log">Copy</span>
</div>
<div class="row mb-4">
<label class="col mr-4">
<span class="button ~neutral @high supra full-width center" id="button-log-normal">Normal</span>
</label>
<label class="col mr-4">
<span class="button ~neutral @low supra full-width center" id="button-log-sanitized">Sanitized</span>
</label>
<div class="flex flex-row gap-2 justify-between">
<button class="button ~neutral @high supra w-full center" id="button-log-normal">Normal</button>
<button class="button ~neutral @low supra w-full center" id="button-log-sanitized">Sanitized</button>
</div>
<div id="log-normal">
<pre class="font-mono bg-inherit pre-line">{{ .Log }}</pre>
<pre class="card font-mono bg-inherit pre-line">{{ .Log }}</pre>
</div>
<div id="log-sanitized" class="unfocused">
<p class="subheading">An attempt has been made to remove sensitive info, but make sure to check yourself.</p>
<pre class="font-mono bg-inherit pre-line">{{ .SanitizedLog }}</pre>
<div id="log-sanitized" class="flex flex-col gap-2 unfocused">
<p class="support subheading">An attempt has been made to remove sensitive info, but make sure to check yourself.</p>
<pre class="card font-mono bg-inherit pre-line">{{ .SanitizedLog }}</pre>
</div>
</section>
</div>

View File

@@ -41,7 +41,7 @@
</div>
<div class="card dark:~d_neutral @low">
<div class="flex flex-col md:flex-row gap-3 items-baseline mb-2">
<span class="heading mr-5">
<span class="heading">
{{ if .passwordReset }}
{{ .strings.passwordReset }}
{{ else }}
@@ -82,23 +82,23 @@
<span class="button ~info @low full-width center mb-4" id="link-matrix">{{ .strings.linkMatrix }} {{ if .matrixRequired }}({{ .strings.required }}){{ end }}</span>
{{ end }}
{{ if or (.telegramEnabled) (or .discordEnabled .matrixEnabled) }}
<div id="contact-via" class="unfocused">
<label class="row switch pb-4 unfocused">
<input type="checkbox" name="contact-via" value="email" id="contact-via-email" class="mr-2"><span>Contact through Email</span>
<div id="contact-via" class="unfocused flex flex-col gap-2">
<label class="flex flex-row gap-2 switch unfocused">
<input type="checkbox" name="contact-via" value="email" id="contact-via-email"><span>Contact through Email</span>
</label>
{{ if .telegramEnabled }}
<label class="row switch pb-4 unfocused">
<input type="checkbox" name="contact-via" value="telegram" id="contact-via-telegram" class="mr-2"><span>Contact through Telegram</span>
<label class="flex flex-row gap-2 switch unfocused">
<input type="checkbox" name="contact-via" value="telegram" id="contact-via-telegram"><span>Contact through Telegram</span>
</label>
{{ end }}
{{ if .discordEnabled }}
<label class="row switch pb-4 unfocused">
<input type="checkbox" name="contact-via" value="discord" id="contact-via-discord" class="mr-2"><span>Contact through Discord</span>
<label class="flex flex-row gap-2 switch unfocused">
<input type="checkbox" name="contact-via" value="discord" id="contact-via-discord"><span>Contact through Discord</span>
</label>
{{ end }}
{{ if .matrixEnabled }}
<label class="row switch pb-4 unfocused">
<input type="checkbox" name="contact-via" value="matrix" id="contact-via-matrix" class="mr-2"><span>Contact through Matrix</span>
<label class="flex flex-row gap-2 switch unfocused">
<input type="checkbox" name="contact-via" value="matrix" id="contact-via-matrix"><span>Contact through Matrix</span>
</label>
{{ end }}
</div>
@@ -141,11 +141,19 @@
</ul>
</div>
{{ if .captcha }}
<div class="card ~neutral @low mb-4">
<span class="label supra mb-2">CAPTCHA {{ if not .reCAPTCHA }}<span id="captcha-regen" title="{{ .strings.refresh }}" class="badge lg @low ~info ml-2 float-right"><i class="ri-refresh-line"></i></span><span id="captcha-success" class="badge lg @low ~critical ml-2 float-right"><i class="ri-close-line"></i></span>{{ end }}</span>
<div id="captcha-img" class="mt-2 mb-2 {{ if .reCAPTCHA }}g-recaptcha{{ end }}"></div>
<div class="card ~neutral @low mb-4 flex flex-col gap-2">
<div class="flex flex-row justify-between gap-2">
<span class="label supra">CAPTCHA</span>
{{ if not .reCAPTCHA }}
<input class="field ~neutral @low" id="captcha-input" class="mt-2" placeholder="CAPTCHA">
<div class="flex flex-row gap-2">
<button id="captcha-regen" aria-label="{{ .strings.refresh }}" title="{{ .strings.refresh }}" class="badge lg @low ~info"><i class="ri-refresh-line"></i></button>
<span id="captcha-success" class="badge lg @low ~critical"><i class="ri-close-line"></i></span>
</div>
{{ end }}
</div>
<div id="captcha-img" class="{{ if .reCAPTCHA }}g-recaptcha{{ end }}"></div>
{{ if not .reCAPTCHA }}
<input class="field ~neutral @low" id="captcha-input" placeholder="CAPTCHA">
{{ end }}
</div>
{{ end }}

View File

@@ -1,7 +1,7 @@
<span class="dropdown z-[11]" tabindex="0" id="lang-dropdown">
<span class="button ~urge dropdown-button">
<i class="ri-global-line"></i>
<span class="ml-2 chev"></span>
<span class="button ~urge dropdown-button flex flex-row gap-2 h-full" title="{{ .strings.language }}" aria-label="{{ .strings.language }}">
<i class="icon ri-global-line"></i>
<i class="icon ri-arrow-down-s-line"></i>
</span>
<div class="dropdown-display">
<div class="card ~neutral @low flex flex-col gap-2">
@@ -13,7 +13,7 @@
<input type="radio" name="lang-time" id="lang-24h">
<span>{{ .strings.time24h }}</span>
</label>
<div id="lang-list"></div>
<div id="lang-list" class="flex flex-col gap-2"></div>
</div>
</div>
</span>

View File

@@ -15,7 +15,7 @@
{{ $hasTwoCards = 1 }}
<div class="card mx-2 flex-initial w-full lg:w-[35%] mb-4 lg:mb-0 dark:~d_neutral @low content">
<span class="heading row">{{ .strings.loginNotAdmin }}</span>
<a class="button ~info h-12 w-full" href="{{ .pages.Base }}{{ .pages.MyAccount }}"><i class="ri-account-circle-fill mr-2"></i>{{ .strings.myAccount }}</a>
<a class="button ~info h-12 w-full flex flex-row gap-2" href="{{ .pages.Base }}{{ .pages.MyAccount }}"><i class="ri-account-circle-fill"></i>{{ .strings.myAccount }}</a>
</div>
{{ end }}
{{ end }}

View File

@@ -6,7 +6,7 @@
</head>
<body class="max-w-full overflow-x-hidden section">
<div id="notification-box"></div>
<div class="page-container m-2 lg:my-20 lg:mx-64 flex flex-col gap-4 items-center">
<div class="page-container m-2 lg:my-20 lg:mx-64 flex flex-col gap-4">
<div class="top-2 inset-x-2 lg:absolute flex flex-row justify-between">
<div class="flex flex-row gap-2">
{{ template "lang-select.html" . }}
@@ -322,10 +322,10 @@
</label>
<div class="flex flex-col gap-2">
<label class="label flex flex-col gap-2">
<div class="switch"><input type="radio" class="mr-2" name="email-24h" value="true" checked><span>{{ .lang.Strings.time24h }}</span></div>
<div class="switch flex flex-row gap-2"><input type="radio" name="email-24h" value="true" checked><span>{{ .lang.Strings.time24h }}</span></div>
</label>
<label class="label flex flex-col gap-2">
<div class="switch"><input type="radio" class="mr-2" name="email-24h" value="false"><span>{{ .lang.Strings.time12h }}</span></div>
<div class="switch flex flex-row gap-2"><input type="radio" name="email-24h" value="false"><span>{{ .lang.Strings.time12h }}</span></div>
</label>
</div>
<div id="email-sect" class="flex flex-row gap-2 justify-between">

View File

@@ -23,46 +23,44 @@
<body class="max-w-full overflow-x-hidden section">
<div id="modal-email" class="modal">
<div class="card relative mx-auto my-[10%] w-4/5 lg:w-1/3">
<div class="content">
<span class="heading mb-4 my-2"></span>
<label class="label supra row m-1" for="modal-email-input">{{ .strings.emailAddress }}</label>
<div class="row">
<input type="email" class="col sm field ~neutral @low input" id="modal-email-input" placeholder="{{ .strings.emailAddress }}">
<div class="flex flex-col gap-2">
<span class="heading"></span>
<label class="label flex flex-col gap-2">
<span class="supra">{{ .strings.emailAddress }}</span>
<input type="email" class="field ~neutral @low input" id="modal-email-input" placeholder="{{ .strings.emailAddress }}">
</label>
<button class="button ~urge @low supra full-width center lg modal-submit">{{ .strings.submit }}</button>
</div>
<button class="button ~urge @low supra full-width center lg my-2 modal-submit">{{ .strings.submit }}</button>
</div>
<div class="confirmation-required unfocused">
<span class="heading mb-4">{{ .strings.confirmationRequired }} <span class="modal-close">&times;</span></span>
<p class="content mb-4">{{ .strings.confirmationRequiredMessage }}</p>
<div class="confirmation-required unfocused flex flex-col gap-2">
<span class="heading">{{ .strings.confirmationRequired }} <span class="modal-close">&times;</span></span>
<p class="content">{{ .strings.confirmationRequiredMessage }}</p>
</div>
</div>
</div>
{{ if .pwrEnabled }}
<div id="modal-pwr" class="modal">
<div class="card content relative mx-auto my-[10%] w-4/5 lg:w-1/3 ~neutral @low">
<div class="card relative mx-auto my-[10%] w-4/5 lg:w-1/3 ~neutral @low flex flex-col gap-2">
<span class="heading">{{ .strings.resetPassword }}</span>
<p class="content my-2">
<div class="content">
{{ if .linkResetEnabled }}
{{ .strings.resetPasswordThroughLinkStart }}
<p>{{ .strings.resetPasswordThroughLinkStart }}</p>
<ul class="content">
{{ if .resetPasswordUsername }}<li>{{ .strings.resetPasswordUsername }}</li>{{ end }}
{{ if .resetPasswordEmail }}<li>{{ .strings.resetPasswordEmail }}</li>{{ end }}
{{ if .resetPasswordContactMethod }}<li>{{ .strings.resetPasswordContactMethod }}</li>{{ end }}
</ul>
{{ .strings.resetPasswordThroughLinkEnd }}
<p>{{ .strings.resetPasswordThroughLinkEnd }}</p>
{{ else }}
{{ .strings.resetPasswordThroughJellyfin }}
<p>{{ .strings.resetPasswordThroughJellyfin }}</p>
{{ end }}
</p>
<div class="row">
<input type="text" class="col sm field ~neutral @low input" id="pwr-address" placeholder="username | example@example.com | user#1234 | @user:host | @username">
</div>
<input type="text" class="col sm field ~neutral @low input" id="pwr-address" placeholder="username | example@example.com | user#1234 | @user:host | @username">
{{ if .linkResetEnabled }}
<span class="button ~info @low full-width center mt-4" id="pwr-submit">
<span class="button ~info @low full-width center" id="pwr-submit">
{{ .strings.submit }}
</span>
{{ else }}
<a class="button ~info @low full-width center mt-4" href="{{ .jfLink }}" target="_blank">{{ .strings.continue }}</a>
<a class="button ~info @low full-width center" href="{{ .jfLink }}" target="_blank">{{ .strings.continue }}</a>
{{ end }}
</div>
</div>
@@ -75,12 +73,12 @@
<div class="flex flex-row gap-2">
{{ template "lang-select.html" . }}
<span class="button ~warning h-min" alt="{{ .strings.theme }}" id="button-theme"><i class="ri-sun-line"></i></span>
<span class="button ~critical @low mb-4 unfocused" id="logout-button">{{ .strings.logout }}</span>
<span class="button ~critical @low unfocused" id="logout-button">{{ .strings.logout }}</span>
</div>
<a class="button ~info unfocused h-min" href="/" id="admin-back-button"><i class="ri-arrow-left-fill mr-2"></i>{{ .strings.admin }}</a>
<a class="button ~info unfocused h-min flex flex-row gap-2" href="/" id="admin-back-button"><i class="ri-arrow-left-fill"></i>{{ .strings.admin }}</a>
</div>
<div class="card @low dark:~d_neutral mb-4" id="card-user">
<span class="heading mb-2"></span>
<div class="card @low dark:~d_neutral" id="card-user">
<span class="heading flex flex-row gap-4"></span>
</div>
<div class="columns-1 sm:columns-2 gap-4" id="user-cardlist">
{{ if index . "PageMessageEnabled" }}
@@ -90,15 +88,15 @@
</div>
{{ end }}
{{ end }}
<div class="card @low dark:~d_neutral flex-col" id="card-contact">
<span class="heading mb-2">{{ .strings.contactMethods }}</span>
<div class="card @low dark:~d_neutral flex flex-col gap-2" id="card-contact">
<span class="heading">{{ .strings.contactMethods }}</span>
<div class="content flex justify-between flex-col h-100"></div>
</div>
<div>
<div class="card @low dark:~d_neutral content" id="card-password">
<span class="heading row mb-2">{{ .strings.changePassword }}</span>
<div class="">
<div class="my-2">
<div class="card @low dark:~d_neutral flex flex-col gap-2" id="card-password">
<span class="heading">{{ .strings.changePassword }}</span>
<div class="flex flex-col gap-2">
<div class="content">
<span class="label supra row">{{ .strings.passwordRequirementsHeader }}</span>
<ul>
{{ range $key, $value := .requirements }}
@@ -108,15 +106,15 @@
{{ end }}
</ul>
</div>
<div class="my-2">
<div class="flex flex-col gap-2">
<label class="label supra" for="user-old-password">{{ .strings.oldPassword }}</label>
<input type="password" class="input ~neutral @low mt-2 mb-4" placeholder="{{ .strings.password }}" id="user-old-password" aria-label="{{ .strings.oldPassword }}">
<input type="password" class="input ~neutral @low" placeholder="{{ .strings.password }}" id="user-old-password" aria-label="{{ .strings.oldPassword }}">
<label class="label supra" for="user-new-password">{{ .strings.newPassword }}</label>
<input type="password" class="input ~neutral @low mt-2 mb-4" placeholder="{{ .strings.password }}" id="user-new-password" aria-label="{{ .strings.newPassword }}">
<input type="password" class="input ~neutral @low" placeholder="{{ .strings.password }}" id="user-new-password" aria-label="{{ .strings.newPassword }}">
<label class="label supra" for="user-reenter-password">{{ .strings.reEnterPassword }}</label>
<input type="password" class="input ~neutral @low mt-2 mb-4" placeholder="{{ .strings.password }}" id="user-reenter-new-password" aria-label="{{ .strings.reEnterPassword }}">
<span class="button ~info @low full-width center mt-4" id="user-password-submit">
<input type="password" class="input ~neutral @low" placeholder="{{ .strings.password }}" id="user-reenter-new-password" aria-label="{{ .strings.reEnterPassword }}">
<span class="button ~info @low full-width center" id="user-password-submit">
{{ .strings.changePassword }}
</span>
</div>
@@ -124,21 +122,21 @@
</div>
</div>
<div>
<div class="card @low dark:~d_neutral unfocused" id="card-status">
<span class="heading mb-2">{{ .strings.expiry }}</span>
<aside class="aside ~warning user-expiry my-4"></aside>
<div class="card @low dark:~d_neutral unfocused flex flex-col gap-2" id="card-status">
<span class="heading">{{ .strings.expiry }}</span>
<aside class="aside ~warning user-expiry"></aside>
<div class="user-expiry-countdown"></div>
</div>
</div>
{{ if .referralsEnabled }}
<div>
<div class="card @low dark:~d_neutral unfocused" id="card-referrals">
<span class="heading mb-2">{{ .strings.referrals }}</span>
<aside class="aside ~neutral my-4 col user-referrals-description"></aside>
<div class="card @low dark:~d_neutral unfocused flex flex-col gap-2" id="card-referrals">
<span class="heading">{{ .strings.referrals }}</span>
<aside class="aside ~neutral col user-referrals-description"></aside>
<div class="flex flex-row justify-between gap-2">
<div class="user-referrals-info"></div>
<div class="grid my-2">
<button type="button" class="user-referrals-button button ~info dark:~d_info @low" title="Copy">{{ .strings.copyReferral }}<i class="ri-file-copy-line ml-2"></i></button>
<div class="user-referrals-info flex flex-col gap-2"></div>
<div class="grid">
<button type="button" class="user-referrals-button button ~info dark:~d_info @low flex flex-row gap-2" title="Copy">{{ .strings.copyReferral }}<i class="ri-file-copy-line"></i></button>
</div>
</div>
</div>

View File

@@ -3,6 +3,7 @@
"name": "English (US)"
},
"strings": {
"language": "Language",
"username": "Username",
"password": "Password",
"emailAddress": "Email Address",

View File

@@ -40,7 +40,7 @@ func (app *appContext) ServeSetup(gc *gin.Context) {
return
}
pages := PagePathsDTO{PagePaths: PAGES}
gc.HTML(200, "setup.html", gin.H{
app.gcHTML(gc, 200, "setup.html", SetupPage, lang, gin.H{
"cssVersion": cssVersion,
"pages": pages,
"lang": app.storage.lang.Setup[lang],

View File

@@ -34,5 +34,11 @@ copyButton.onclick = () => {
toClipboard("```\n" + logSanitized.textContent + "```");
}
copyButton.textContent = "Copied.";
setTimeout(() => { copyButton.textContent = "Copy"; }, 1500);
copyButton.classList.add("~positive");
copyButton.classList.remove("~urge");
setTimeout(() => {
copyButton.textContent = "Copy";
copyButton.classList.add("~urge");
copyButton.classList.remove("~positive");
}, 1500);
};

View File

@@ -266,7 +266,7 @@ interface sendDTO {
if (window.captcha && !window.reCAPTCHA) {
captcha.generate();
(document.getElementById("captcha-regen") as HTMLSpanElement).onclick = captcha.generate;
(document.getElementById("captcha-regen") as HTMLButtonElement).onclick = captcha.generate;
captcha.input.onkeyup = validator.validate;
}

View File

@@ -150,10 +150,10 @@ export class Discord extends ServiceLinker {
(link.parentElement as HTMLAnchorElement).target = "_blank";
let innerHTML = ``;
if (inv.icon != "") {
innerHTML += `<span class="img-circle lg mr-4"><img class="img-circle" src="${inv.icon}" width="64" height="64"></span>${window.discordServerName}`;
innerHTML += `<span class="img-circle lg"><img class="img-circle" src="${inv.icon}" width="64" height="64"></span>${window.discordServerName}`;
} else {
innerHTML += `
<span class="shield mr-4 bg-discord"><i class="ri-discord-fill ri-xl text-white"></i></span>${window.discordServerName}
<span class="shield bg-discord"><i class="ri-discord-fill ri-xl text-white"></i></span>${window.discordServerName}
`;
}
link.innerHTML = innerHTML;

View File

@@ -313,44 +313,44 @@ class user implements User, SearchableItem {
const email = this._emailAddress != "";
if (!telegram && !discord && !matrix && !email) return;
let innerHTML = `
<i class="icon ri-settings-2-line ml-2 dropdown-button"></i>
<i class="icon ri-settings-2-line dropdown-button"></i>
<div class="dropdown manual over-top">
<div class="dropdown-display lg">
<div class="card ~neutral @low">
<div class="supra sm mb-2">${window.lang.strings("contactThrough")}</div>
<div class="card ~neutral @low flex flex-col gap-2">
<div class="supra sm">${window.lang.strings("contactThrough")}</div>
<div class="accounts-area-email">
<label class="row switch pb-2">
<input type="checkbox" name="accounts-contact-${this.id}" class="accounts-contact-email mr-2">
<label class="row switch flex flex-row gap-2">
<input type="checkbox" name="accounts-contact-${this.id}" class="accounts-contact-email">
</span>Email</span>
</label>
</div>
<div class="accounts-area-telegram">
<label class="row switch pb-2">
<input type="checkbox" name="accounts-contact-${this.id}" class="accounts-contact-telegram mr-2">
<label class="row switch flex flex-row gap-2">
<input type="checkbox" name="accounts-contact-${this.id}" class="accounts-contact-telegram">
<span>Telegram</span>
</label>
</div>
<div class="accounts-area-discord">
<label class="row switch pb-2">
<input type="checkbox" name="accounts-contact-${this.id}" class="accounts-contact-discord mr-2">
<label class="row switch flex flex-row gap-2">
<input type="checkbox" name="accounts-contact-${this.id}" class="accounts-contact-discord">
<span>Discord</span>
</label>
</div>
<div class="accounts-area-matrix">
<label class="row switch pb-2">
<input type="checkbox" name="accounts-contact-${this.id}" class="accounts-contact-matrix mr-2">
<label class="row switch flex flex-row gap-2">
<input type="checkbox" name="accounts-contact-${this.id}" class="accounts-contact-matrix">
<span>Matrix</span>
</label>
</div>
<div class="supra sm mb-2 accounts-unlink-header">${window.lang.strings("unlink")}:</div>
<div class="supra sm accounts-unlink-header">${window.lang.strings("unlink")}:</div>
<div class="accounts-unlink-telegram">
<button class="button ~critical mb-2 w-full">Telegram</button>
<button class="button ~critical w-full">Telegram</button>
</div>
<div class="accounts-unlink-discord">
<button class="button ~critical mb-2 w-full">Discord</button>
<button class="button ~critical w-full">Discord</button>
</div>
<div class="accounts-unlink-matrix">
<button class="button ~critical mb-2 w-full">Matrix</button>
<button class="button ~critical w-full">Matrix</button>
</div>
</div>
</div>
@@ -404,12 +404,12 @@ class user implements User, SearchableItem {
this._notifyDropdown.querySelector(".accounts-area-matrix").classList.remove("unfocused");
this._notifyDropdown.querySelector(".accounts-unlink-matrix").classList.remove("unfocused");
this._matrix.innerHTML = `
<div class="table-inline">
<div class="accounts-settings-area flex flex-row gap-2 justify-center">
${u}
</div>
`;
if (lastNotifyMethod) {
(this._matrix.querySelector(".table-inline") as HTMLDivElement).appendChild(this._notifyDropdown);
(this._matrix.querySelector(".accounts-settings-area") as HTMLDivElement).appendChild(this._notifyDropdown);
}
}
this._checkUnlinkArea();
@@ -475,12 +475,12 @@ class user implements User, SearchableItem {
this._notifyDropdown.querySelector(".accounts-area-telegram").classList.remove("unfocused");
this._notifyDropdown.querySelector(".accounts-unlink-telegram").classList.remove("unfocused");
this._telegram.innerHTML = `
<div class="table-inline">
<div class="accounts-settings-area flex flex-row gap-2 justify-center">
<a href="https://t.me/${u}" target="_blank">@${u}</a>
</div>
`;
if (lastNotifyMethod) {
(this._telegram.querySelector(".table-inline") as HTMLDivElement).appendChild(this._notifyDropdown);
(this._telegram.querySelector(".accounts-settings-area") as HTMLDivElement).appendChild(this._notifyDropdown);
}
}
this._checkUnlinkArea();
@@ -545,12 +545,12 @@ class user implements User, SearchableItem {
this._notifyDropdown.querySelector(".accounts-area-discord").classList.remove("unfocused");
this._notifyDropdown.querySelector(".accounts-unlink-discord").classList.remove("unfocused");
this._discord.innerHTML = `
<div class="table-inline">
<div class="accounts-settings-area flex flex-row gap-2 justify-center">
<a href="https://discord.com/users/${this._discordID}" class="discord-link" target="_blank">${u}</a>
</div>
`;
if (lastNotifyMethod) {
(this._discord.querySelector(".table-inline") as HTMLDivElement).appendChild(this._notifyDropdown);
(this._discord.querySelector(".accounts-settings-area") as HTMLDivElement).appendChild(this._notifyDropdown);
}
}
this._checkUnlinkArea();
@@ -1647,15 +1647,15 @@ export class accountsList extends PaginatedList {
return;
}
if (list.length > 0) {
this._announceButton.innerHTML = `${window.lang.strings("announce")} <i class="ml-2 ri-arrow-drop-down-line"></i>`;
this._announceButton.innerHTML = `${window.lang.strings("announce")} <i class="ri-arrow-drop-down-line"></i>`;
}
const dList = document.getElementById("accounts-announce-templates") as HTMLDivElement;
dList.textContent = '';
for (let name of list) {
const el = document.createElement("div") as HTMLDivElement;
el.classList.add("flex", "flex-row", "justify-between", "truncate", "mt-2");
el.classList.add("flex", "flex-row", "gap-2", "justify-between", "truncate");
el.innerHTML = `
<span class="button ~neutral sm full-width accounts-announce-template-button">${name}</span><span class="button ~critical fr ml-4 accounts-announce-template-delete">&times;</span>
<span class="button ~neutral sm full-width accounts-announce-template-button">${name}</span><span class="button ~critical accounts-announce-template-delete">&times;</span>
`;
let urlSafeName = encodeURIComponent(encodeURIComponent(name));
(el.querySelector("span.accounts-announce-template-button") as HTMLSpanElement).onclick = () => {

View File

@@ -244,6 +244,8 @@ export class Activity implements activity, SearchableItem {
else this._card.classList.remove(moodColours[i]);
} */
// lazy late addition, hide then unhide if needed
this._expiryTypeBadge.classList.add("unfocused");
if (this.type == "changePassword" || this.type == "resetPassword") {
let innerHTML = ``;
if (this.type == "changePassword") innerHTML = window.lang.strings("accountChangedPassword");
@@ -265,13 +267,16 @@ export class Activity implements activity, SearchableItem {
} else if (this.type == "creation") {
this._title.innerHTML = window.lang.strings("accountCreated").replace("{user}", this._genUserLink());
if (this.source_type == "user") {
this._referrer.innerHTML = `<span class="supra mr-2">${window.lang.strings("referrer")}</span>${this._genSrcUserLink()}`;
this._referrer.classList.remove("unfocused");
this._referrer.innerHTML = `<span class="supra">${window.lang.strings("referrer")}</span>${this._genSrcUserLink()}`;
} else {
this._referrer.classList.add("unfocused");
this._referrer.textContent = ``;
}
} else if (this.type == "deletion") {
if (this.source_type == "daemon") {
this._title.innerHTML = window.lang.strings("accountExpired").replace("{user}", this._genUserText());
this._expiryTypeBadge.classList.remove("unfocused");
this._expiryTypeBadge.classList.add("~critical");
this._expiryTypeBadge.classList.remove("~info");
this._expiryTypeBadge.textContent = window.lang.strings("deleted");
@@ -283,6 +288,7 @@ export class Activity implements activity, SearchableItem {
} else if (this.type == "disabled") {
if (this.source_type == "daemon") {
this._title.innerHTML = window.lang.strings("accountExpired").replace("{user}", this._genUserLink());
this._expiryTypeBadge.classList.remove("unfocused");
this._expiryTypeBadge.classList.add("~info");
this._expiryTypeBadge.classList.remove("~critical");
this._expiryTypeBadge.textContent = window.lang.strings("disabled");
@@ -327,8 +333,10 @@ export class Activity implements activity, SearchableItem {
set ip(v: string) {
this._act.ip = v;
if (v) {
this._ip.innerHTML = `<span class="supra mr-2">IP</span><span class="font-mono bg-inherit">${v}</span>`;
this._ip.classList.remove("unfocused");
this._ip.innerHTML = `<span class="supra">IP</span><span class="font-mono bg-inherit">${v}</span>`;
} else {
this._ip.classList.add("unfocused");
this._ip.textContent = ``;
}
}
@@ -382,23 +390,23 @@ export class Activity implements activity, SearchableItem {
constructor(act: activity) {
this._card = document.createElement("div");
this._card.classList.add("card", "@low", "my-2");
this._card.classList.add("card", "@low", "flex", "flex-col", "gap-2");
this._card.innerHTML = `
<div class="flex flex-col md:flex-row justify-between mb-2">
<div class="flex flex-row flex-wrap justify-between items-start gap-1">
<span class="heading truncate flex-initial md:text-2xl text-xl activity-title"></span>
<div class="flex flex-col flex-none ml-0 md:ml-2">
<div class="flex flex-row flex-wrap gap-2">
<span class="activity-expiry-type badge self-stretch"></span>
<span class="font-medium md:text-sm text-xs activity-time" aria-label="${window.lang.strings("date")}"></span>
<span class="activity-expiry-type badge self-start md:self-end mt-1"></span>
</div>
</div>
<div class="flex flex-row justify-between items-end">
<div class="flex flex-col md:flex-row gap-2">
<div>
<span class="content supra mr-2 activity-source-type"></span><span class="activity-source"></span>
<div class="flex flex-row justify-between items-baseline">
<div class="flex flex-col md:flex-row gap-1 items-baseline">
<div class="flex flex-row gap-2 items-baseline">
<span class="supra activity-source-type"></span><span class="activity-source"></span>
</div>
<span class="content activity-referrer"></span>
<span class="content activity-ip"></span>
<span class="activity-referrer flex flex-row gap-2 items-baseline"></span>
<span class="activity-ip flex flex-row gap-2 items-baseline"></span>
</div>
<div>
<button class="button @low hover:~critical rounded-full px-1 py-px activity-delete" aria-label="${window.lang.strings("delete")}"><i class="ri-close-line"></i></button>
@@ -583,7 +591,7 @@ export class activityList extends PaginatedList {
this._ascending = v;
// Setting default sort makes sense, since this is the only sort ever being done.
this._c.defaultSortAscending = this.ascending;
this._sortDirection.innerHTML = `${window.lang.strings("sortDirection")} <i class="ri-arrow-${v ? "up" : "down"}-s-line ml-2"></i>`;
this._sortDirection.innerHTML = `${window.lang.strings("sortDirection")} <i class="ri-arrow-${v ? "up" : "down"}-s-line"></i>`;
// NOTE: We don't actually re-sort the list here, instead just use setOrdering to apply this.ascending before a reload.
this._search.setOrdering(this._search.ordering, this._c.defaultSortField, this.ascending);
if (this._hasLoaded) {

View File

@@ -184,18 +184,24 @@ export class notificationBox implements NotificationBox {
private _errorTypes: { [type: string]: boolean } = {};
private _positiveTypes: { [type: string]: boolean } = {};
timeout: number;
constructor(box: HTMLDivElement, timeout?: number) { this._box = box; this.timeout = timeout || 5; }
constructor(box: HTMLDivElement, timeout?: number) {
this._box = box;
this._box.classList.add("flex", "flex-col", "gap-2");
this.timeout = timeout || 5;
}
static baseClasses = ["aside", "flex", "flex-row", "justify-between", "gap-4"];
private _error = (message: string): HTMLElement => {
const noti = document.createElement('aside');
noti.classList.add("aside", "~critical", "@low", "mt-2", "notification-error");
noti.classList.add(...notificationBox.baseClasses, "~critical", "@low", "notification-error");
let error = "";
if (window.lang) {
error = window.lang.strings("error") + ":"
}
noti.innerHTML = `<strong>${error}</strong> ${message}`;
noti.innerHTML = `<div><strong>${error}</strong> ${message}</div>`;
const closeButton = document.createElement('span') as HTMLSpanElement;
closeButton.classList.add("button", "~critical", "@low", "ml-4");
closeButton.classList.add("button", "~critical", "@low");
closeButton.innerHTML = `<i class="icon ri-close-line"></i>`;
closeButton.onclick = () => this._close(noti);
noti.classList.add("animate-slide-in");
@@ -205,10 +211,10 @@ export class notificationBox implements NotificationBox {
private _positive = (bold: string, message: string): HTMLElement => {
const noti = document.createElement('aside');
noti.classList.add("aside", "~positive", "@low", "mt-2", "notification-positive");
noti.innerHTML = `<strong>${bold}</strong> ${message}`;
noti.classList.add(...notificationBox.baseClasses, "~positive", "@low", "notification-positive");
noti.innerHTML = `<div><strong>${bold}</strong> ${message}</div>`;
const closeButton = document.createElement('span') as HTMLSpanElement;
closeButton.classList.add("button", "~positive", "@low", "ml-4");
closeButton.classList.add("button", "~positive", "@low");
closeButton.innerHTML = `<i class="icon ri-close-line"></i>`;
closeButton.onclick = () => this._close(noti);
noti.classList.add("animate-slide-in");

View File

@@ -76,13 +76,22 @@ export class PageManager {
}
load(name: string = "") {
name = decodeURI(name);
if (!this.pages.has(name)) return window.history.pushState(name || this.defaultName, this.defaultTitle, "")
const p = this.pages.get(name);
this.loadPage(p);
}
loadPage (p: Page) {
window.history.pushState(p.name || this.defaultName, p.title, p.url + window.location.search);
let url = p.url;
// Fix ordering of query params and hash
if (url.includes("#")) {
let split = url.split("#");
url = split[0] + window.location.search + "#" + split[1];
} else {
url = url + window.location.search;
}
window.history.pushState(p.name || this.defaultName, p.title, url);
}
prev(name: string = "") {

View File

@@ -50,10 +50,12 @@ class profile implements Profile {
get admin(): boolean { return this._adminChip.classList.contains("chip"); }
set admin(state: boolean) {
if (state) {
this._adminChip.classList.add("chip", "~info", "ml-2");
this._adminChip.classList.remove("unfocused");
this._adminChip.classList.add("chip", "~info");
this._adminChip.textContent = "Admin";
} else {
this._adminChip.classList.remove("chip", "~info", "ml-2");
this._adminChip.classList.add("unfocused");
this._adminChip.classList.remove("chip", "~info");
this._adminChip.textContent = "";
}
}
@@ -115,7 +117,7 @@ class profile implements Profile {
constructor(name: string, p: Profile) {
this._row = document.createElement("tr") as HTMLTableRowElement;
let innerHTML = `
<td><b class="profile-name"></b> <span class="profile-admin"></span></td>
<td><div class="flex flex-row items-baseline gap-2"><b class="profile-name"></b> <span class="profile-admin"></span></div></td>
<td><input type="radio" name="profile-default"></td>
`;
if (window.ombiEnabled) innerHTML += `

View File

@@ -114,9 +114,14 @@ export class BoolQuery extends Query {
super(subject, QueryOperator.Equal);
this.type = "bool";
this._value = value;
this._card.classList.add("button", "~" + (this._value ? "positive" : "critical"), "@high", "center");
this._card.classList.add("button", "@high", "center", "flex", "flex-row", "gap-2");
if (this._value) {
this._card.classList.add("~positive");
} else {
this._card.classList.add("~critical");
}
this._card.innerHTML = `
<span class="font-bold mr-2">${subject.name}</span>
<span class="font-bold">${subject.name}</span>
<i class="text-2xl ri-${this._value? "checkbox" : "close"}-circle-fill"></i>
`;
}
@@ -156,9 +161,9 @@ export class StringQuery extends Query {
super(subject, QueryOperator.Equal);
this.type = "string";
this._value = value.toLowerCase();
this._card.classList.add("button", "~neutral", "@low", "center");
this._card.classList.add("button", "~neutral", "@low", "center", "flex", "flex-row", "gap-2");
this._card.innerHTML = `
<span class="font-bold mr-2">${subject.name}:</span> "${this._value}"
<span class="font-bold">${subject.name}:</span> "${this._value}"
`;
}
@@ -203,10 +208,10 @@ export class DateQuery extends Query {
super(subject, operator);
this.type = "date";
this._value = value;
this._card.classList.add("button", "~neutral", "@low", "center");
this._card.classList.add("button", "~neutral", "@low", "center", "flex", "flex-row", "gap-2");
let dateText = QueryOperatorToDateText(operator);
this._card.innerHTML = `
<span class="font-bold mr-2">${subject.name}:</span> ${dateText != "" ? dateText+" " : ""}${value.text}
<span class="font-bold">${subject.name}:</span> ${dateText != "" ? dateText+" " : ""}${value.text}
`;
}
@@ -575,6 +580,8 @@ export class Search {
};
generateFilterList = () => {
const filterListContainer = document.createElement("div");
filterListContainer.classList.add("flex", "flex-row", "flex-wrap", "gap-2");
// Generate filter buttons
for (let queryName of Object.keys(this._c.queries)) {
const query = this._c.queries[queryName];
@@ -585,9 +592,9 @@ export class Search {
}
const container = document.createElement("span") as HTMLSpanElement;
container.classList.add("button", "button-xl", "~neutral", "@low", "mb-1", "mr-2", "align-bottom");
container.classList.add("button", "button-xl", "~neutral", "@low", "align-bottom", "flex", "flex-row", "gap-2");
container.innerHTML = `
<div class="flex flex-col mr-2">
<div class="flex flex-col">
<span>${query.name}</span>
<span class="support">${query.description || ""}</span>
</div>
@@ -596,13 +603,13 @@ export class Search {
const pos = document.createElement("button") as HTMLButtonElement;
pos.type = "button";
pos.ariaLabel = `Filter by "${query.name}": True`;
pos.classList.add("button", "~positive", "ml-2");
pos.classList.add("button", "~positive");
pos.innerHTML = `<i class="ri-checkbox-circle-fill"></i>`;
pos.addEventListener("click", () => this.fillInFilter(queryName, "true"));
const neg = document.createElement("button") as HTMLButtonElement;
neg.type = "button";
neg.ariaLabel = `Filter by "${query.name}": False`;
neg.classList.add("button", "~critical", "ml-2");
neg.classList.add("button", "~critical");
neg.innerHTML = `<i class="ri-close-circle-fill"></i>`;
neg.addEventListener("click", () => this.fillInFilter(queryName, "false"));
@@ -612,8 +619,8 @@ export class Search {
if (query.string) {
const button = document.createElement("button") as HTMLButtonElement;
button.type = "button";
button.classList.add("button", "~urge", "ml-2");
button.innerHTML = `<i class="ri-equal-line mr-2"></i>${window.lang.strings("matchText")}`;
button.classList.add("button", "~urge", "flex", "flex-row", "gap-2");
button.innerHTML = `<i class="ri-equal-line"></i>${window.lang.strings("matchText")}`;
// Position cursor between quotes
button.addEventListener("click", () => this.fillInFilter(queryName, `""`, -1));
@@ -623,20 +630,20 @@ export class Search {
if (query.date) {
const onDate = document.createElement("button") as HTMLButtonElement;
onDate.type = "button";
onDate.classList.add("button", "~urge", "ml-2");
onDate.innerHTML = `<i class="ri-calendar-check-line mr-2"></i>On Date`;
onDate.classList.add("button", "~urge", "flex", "flex-row", "gap-2");
onDate.innerHTML = `<i class="ri-calendar-check-line"></i>On Date`;
onDate.addEventListener("click", () => this.fillInFilter(queryName, `"="`, -1));
const beforeDate = document.createElement("button") as HTMLButtonElement;
beforeDate.type = "button";
beforeDate.classList.add("button", "~urge", "ml-2");
beforeDate.innerHTML = `<i class="ri-calendar-check-line mr-2"></i>Before Date`;
beforeDate.classList.add("button", "~urge", "flex", "flex-row", "gap-2");
beforeDate.innerHTML = `<i class="ri-calendar-check-line"></i>Before Date`;
beforeDate.addEventListener("click", () => this.fillInFilter(queryName, `"<"`, -1));
const afterDate = document.createElement("button") as HTMLButtonElement;
afterDate.type = "button";
afterDate.classList.add("button", "~urge", "ml-2");
afterDate.innerHTML = `<i class="ri-calendar-check-line mr-2"></i>After Date`;
afterDate.classList.add("button", "~urge", "flex", "flex-row", "gap-2");
afterDate.innerHTML = `<i class="ri-calendar-check-line"></i>After Date`;
afterDate.addEventListener("click", () => this.fillInFilter(queryName, `">"`, -1));
container.appendChild(onDate);
@@ -644,8 +651,9 @@ export class Search {
container.appendChild(afterDate);
}
this._c.filterList.appendChild(container);
filterListContainer.appendChild(container);
}
this._c.filterList.appendChild(filterListContainer)
}
onServerSearch = () => {

View File

@@ -345,7 +345,7 @@ class DOMList extends DOMSetting implements SList {
container.classList.add("flex", "flex-row", "justify-between");
container.innerHTML = `
<input type="text" class="input ~neutral @low">
<button class="button ~neutral @low center -ml-10 rounded-s-none aria-label="${window.lang.strings("delete")}" title="${window.lang.strings("delete")}">
<button class="button ~neutral @low center inside-input rounded-s-none aria-label="${window.lang.strings("delete")}" title="${window.lang.strings("delete")}">
<i class="ri-close-line"></i>
</button>
`;
@@ -536,6 +536,7 @@ class groupButton extends groupableItem {
private _indent: number;
private _parentSidebar: HTMLElement;
// one of the few sanctioned uses of ml/mr. Looks worse with ms/me.
private static readonly _margin = "ml-6";
private _indentClasses = ["h-11", "h-10", "h-9"];
private _indentClass = () => {
@@ -1087,7 +1088,7 @@ export class settingsList {
setBackupSort = (ascending: boolean) => {
this._backupSortAscending = ascending;
this._backupSortDirection.innerHTML = `${window.lang.strings("sortDirection")} <i class="${ascending ? "ri-arrow-up-s-line" : "ri-arrow-down-s-line"} ml-2"></i>`;
this._backupSortDirection.innerHTML = `${window.lang.strings("sortDirection")} <i class="${ascending ? "ri-arrow-up-s-line" : "ri-arrow-down-s-line"}"></i>`;
this._getBackups();
};
@@ -1103,8 +1104,8 @@ export class settingsList {
location.innerHTML = window.lang.strings("backupCanBeFound").replace("{filepath}", `<span class="text-black dark:text-white font-mono bg-inherit">"`+backupDTO.path+`"</span>`);
download.innerHTML = `
<i class="ri-download-line"></i>
<span class="ml-2">${window.lang.strings("download")}</span>
<span class="badge ~info @low ml-2">${backupDTO.size}</span>
<span>${window.lang.strings("download")}</span>
<span class="badge ~info @low">${backupDTO.size}</span>
`;
download.parentElement.onclick = () => _download("/backups/" + backupDTO.name, backupDTO.name);
@@ -1127,9 +1128,9 @@ export class settingsList {
<td>${toDateString(new Date(b.date*1000))}</td>
<td class="font-mono">${b.commit || "?"}</td>
<td><div class="flex flex-row gap-2 items-stretch justify-center">
<span class="backup-download button ~positive @low" title="${window.lang.strings("backupDownload")}">
<span class="backup-download button ~positive @low flex flex-row gap-2" title="${window.lang.strings("backupDownload")}">
<i class="ri-download-line"></i>
<span class="badge ~positive @low ml-2">${b.size}</span>
<span class="badge ~positive @low">${b.size}</span>
</span>
<span class="backup-restore button ~critical @low" title="${window.lang.strings("backupRestore")}"><i class="icon ri-restart-line"></i></span>
</div></td>
@@ -1461,7 +1462,6 @@ export class settingsList {
continue;
}
// element.classList.remove("-mx-2", "my-2", "p-2", "aside", "~neutral", "@low");
element.classList.add("opacity-50", "pointer-events-none");
element.setAttribute("aria-disabled", "true");
let matchedSetting = setting.setting.toLowerCase().includes(query) ||
@@ -1478,7 +1478,6 @@ export class settingsList {
((setting.advanced && this._advanced) ||
!(setting.advanced)));
if (shouldShow || query == "") {
// element.classList.add("-mx-2", "my-2", "p-2", "aside", "~neutral", "@low");
element.classList.remove("opacity-50", "pointer-events-none");
element.setAttribute("aria-disabled", "false");
}

View File

@@ -568,7 +568,7 @@ const cards = Array.from(document.getElementsByClassName("page-container")[0].qu
pages.setPage({
name: title,
title: titleEl.textContent + " - jfa-go",
url: "/#" + title,
url: "/" + (!title ? "" : "#") + title,
show: () => {
cards[i].classList.remove("unfocused");
return true;

View File

@@ -1,7 +1,7 @@
import { ThemeManager } from "./modules/theme.js";
import { lang, LangFile, loadLangSelector } from "./modules/lang.js";
import { Modal } from "./modules/modal.js";
import { _get, _post, _delete, notificationBox, whichAnimationEvent, toDateString, toggleLoader, addLoader, removeLoader, toClipboard } from "./modules/common.js";
import { _get, _post, _delete, notificationBox, whichAnimationEvent, toDateString, addLoader, removeLoader, toClipboard } from "./modules/common.js";
import { Login } from "./modules/login.js";
import { Discord, Telegram, Matrix, ServiceConfiguration, MatrixConfiguration } from "./modules/account-linking.js";
import { Validator, ValidatorConf, ValidatorRespDTO } from "./modules/validator.js";
@@ -108,10 +108,10 @@ if (window.pwrEnabled && window.linkResetEnabled) {
const submitButton = document.getElementById("pwr-submit");
const input = document.getElementById("pwr-address") as HTMLInputElement;
submitButton.onclick = () => {
toggleLoader(submitButton);
addLoader(submitButton);
_post("/my/password/reset/" + input.value, null, (req: XMLHttpRequest) => {
if (req.readyState != 4) return;
toggleLoader(submitButton);
removeLoader(submitButton);
if (req.status != 204) {
window.notifications.customError("unkownError", window.lang.notif("errorUnknown"));;
window.modals.pwr.close();
@@ -183,26 +183,28 @@ class ContactMethods {
append = (name: string, details: MyDetailsContactMethod, icon: string, addEditFunc?: (add: boolean) => void, required?: boolean) => {
const row = document.createElement("div");
row.classList.add("flex", "flex-row", "justify-between", "my-2", "flex-nowrap");
row.classList.add("flex", "flex-row", "justify-between", "gap-2", "flex-nowrap");
let innerHTML = `
<div class="flex items-baseline flex-nowrap truncate">
<div class="flex items-baseline flex-row gap-2 flex-nowrap truncate">
<span class="shield ~urge" alt="${name}">
<span class="icon">
${icon}
</span>
</span>
<span class="ml-2 font-bold text-ellipsis overflow-hidden">${(details.value == "") ? window.lang.strings("notSet") : details.value}</span>
<span class="font-bold text-ellipsis overflow-hidden">${(details.value == "") ? window.lang.strings("notSet") : details.value}</span>
</div>
<div class="flex items-center ml-2">
<button class="user-contact-enabled-disabled button ~neutral" ${details.value == "" ? "disabled" : ""}>
<input type="checkbox" class="mr-2" ${details.value == "" ? "disabled" : ""}>
<div class="flex flex-col justify-center">
<div class="flex items-center flex-row gap-2">
<button class="user-contact-enabled-disabled button ~neutral flex flex-row gap-2" ${details.value == "" ? "disabled" : ""}>
<input type="checkbox" ${details.value == "" ? "disabled" : ""}>
<span>${window.lang.strings("enabled")}</span>
</button>
`;
if (addEditFunc) {
innerHTML += `
<button class="user-contact-edit button ~info ml-2">
<i class="ri-${details.value == "" ? "add" : "edit"}-fill mr-2"></i>
<button class="user-contact-edit button ~info flex flex-row gap-2">
<i class="ri-${details.value == "" ? "add" : "edit"}-fill"></i>
<span>${details.value == "" ? window.lang.strings("add") : window.lang.strings("edit")}</span>
</button>
`;
@@ -210,14 +212,15 @@ class ContactMethods {
if (!required && details.value != "") {
innerHTML += `
<button class="user-contact-delete button ~critical ml-2" alt="${window.lang.strings("delete")}" text="${window.lang.strings("delete")}">
&times;
<button class="user-contact-delete button ~critical h-[100%]" alt="${window.lang.strings("delete")}" text="${window.lang.strings("delete")}">
<i class="icon ri-close-line"></i>
</button>
`;
}
innerHTML += `
</div>
</div>
`;
row.innerHTML = innerHTML;
@@ -358,12 +361,12 @@ class ReferralCard {
this._descriptionEl = this._card.querySelector(".user-referrals-description") as HTMLSpanElement;
this._infoArea.innerHTML = `
<div class="row my-3">
<div class="">
<div class="inline baseline">
<span class="text-2xl referral-remaining-uses"></span> <span class="text-gray-400 text-lg">${window.lang.strings("inviteRemainingUses")}</span>
</div>
</div>
<div class="row my-3">
<div class="">
<div class="inline baseline">
<span class="text-gray-400 text-lg">${window.lang.strings("expiry")}</span> <span class="text-2xl referral-expiry"></span>
<div>
@@ -381,7 +384,7 @@ class ReferralCard {
toClipboard(this._url);
const content = this._button.innerHTML;
this._button.innerHTML = `
${window.lang.strings("copied")} <i class="ri-check-line ml-2"></i>
${window.lang.strings("copied")} <i class="ri-check-line"></i>
`;
this._button.classList.add("~positive");
this._button.classList.remove("~info");
@@ -454,7 +457,7 @@ class ExpiryCard {
if (ymd[i] == 0) continue;
const words = window.lang.quantity(langKeys[i], ymd[i]).split(" ");
innerHTML += `
<div class="row my-3">
<div class="row">
<div class="inline baseline">
<span class="text-2xl">${words[0]}</span> <span class="text-gray-400 text-lg">${words[1]}</span>
</div>
@@ -504,14 +507,18 @@ const addEditEmail = (add: boolean): void => {
const submit = window.modals.email.modal.querySelector(".modal-submit") as HTMLButtonElement;
submit.onclick = () => {
toggleLoader(submit);
addLoader(submit);
_post("/my/email", {"email": input.value}, (req: XMLHttpRequest) => {
if (req.readyState == 4 && (req.status == 303 || req.status == 200)) {
if (req.readyState != 4) return;
removeLoader(submit);
if (req.status == 303 || req.status == 200) {
document.dispatchEvent(new CustomEvent("details-reload"));
window.modals.email.close();
}
}, true, (req: XMLHttpRequest) => {
if (req.readyState == 4 && req.status == 401) {
if (req.readyState != 4) return;
removeLoader(submit);
if (req.status == 401) {
content.classList.add("unfocused");
confirmationRequired.classList.remove("unfocused");
}
@@ -607,8 +614,8 @@ changePasswordButton.addEventListener("click", () => {
}
}, true, (req: XMLHttpRequest) => {
if (req.readyState != 4) return;
if (req.status == 401) {
removeLoader(changePasswordButton);
if (req.status == 401) {
window.notifications.customError("oldPasswordError", window.lang.notif("errorOldPassword"));
return;
}
@@ -629,10 +636,10 @@ document.addEventListener("details-reload", () => {
<span>${window.lang.strings("welcomeUser").replace("{user}", window.username)}</span>
`;
if (details.admin) {
innerHTML += `<span class="chip ~info ml-4">${window.lang.strings("admin")}</span>`;
innerHTML += `<span class="chip ~info">${window.lang.strings("admin")}</span>`;
}
if (details.disabled) {
innerHTML += `<span class="chip ~warning ml-4">${window.lang.strings("disabled")}</span>`;
innerHTML += `<span class="chip ~warning">${window.lang.strings("disabled")}</span>`;
}
rootCard.querySelector(".heading").innerHTML = innerHTML;

View File

@@ -54,7 +54,11 @@ func (app *appContext) getURLBase(gc *gin.Context) string {
func (app *appContext) gcHTML(gc *gin.Context, code int, file string, page Page, lang string, templ gin.H) {
gc.Header("Cache-Control", "no-cache")
if page == SetupPage {
app.SetBaseLangTemplateValues(gc, lang, templ)
} else {
app.BasePageTemplateValues(gc, lang, page, templ)
}
gc.HTML(code, file, templ)
}
@@ -121,6 +125,15 @@ func (app *appContext) BasePageTemplateValues(gc *gin.Context, lang string, page
set("pwrEnabled", app.config.Section("password_resets").Key("enabled").MustBool(false))
}
set("referralsEnabled", app.config.Section("user_page").Key("enabled").MustBool(false) && app.config.Section("user_page").Key("referrals").MustBool(false))
app.SetBaseLangTemplateValues(gc, lang, base)
}
func (app *appContext) SetBaseLangTemplateValues(gc *gin.Context, lang string, base gin.H) {
set := func(k string, v any) {
if _, ok := base[k]; !ok {
base[k] = v
}
}
langComponents := strings.Split(lang, "-")
var shortLang string
if len(langComponents) < 1 {
@@ -142,6 +155,7 @@ const (
FormPage
PWRPage
UserPage
SetupPage
OtherPage
)