From 4a68c782246bab6a9a4cd0e37b246faeb22c8876 Mon Sep 17 00:00:00 2001
From: mrjvs
Date: Sun, 16 Aug 2026 14:45:39 +0200
Subject: [PATCH] feat: add loading indicators to every async action
---
src/assets/css/auth.css | 2 +-
src/assets/css/forgot-password.css | 131 --------------------------
src/pages/account/forgot-password.vue | 5 +-
src/pages/account/index.vue | 31 +++---
src/pages/account/login.vue | 6 +-
src/pages/account/register.vue | 5 +-
src/pages/account/reset-password.vue | 5 +-
src/pages/account/upgrade.vue | 32 ++++---
8 files changed, 52 insertions(+), 165 deletions(-)
delete mode 100644 src/assets/css/forgot-password.css
diff --git a/src/assets/css/auth.css b/src/assets/css/auth.css
index bf06e6f..e2598a3 100644
--- a/src/assets/css/auth.css
+++ b/src/assets/css/auth.css
@@ -33,7 +33,7 @@ form.account p {
margin: 12px 0;
}
-form.account div {
+form.account > div {
margin-top: 24px;
}
diff --git a/src/assets/css/forgot-password.css b/src/assets/css/forgot-password.css
deleted file mode 100644
index 337d3d0..0000000
--- a/src/assets/css/forgot-password.css
+++ /dev/null
@@ -1,131 +0,0 @@
-.account-form-wrapper {
- height: 80vh;
- display: flex;
- justify-content: center;
- align-content: center;
- flex-direction: column;
- margin: 15vh auto;
- width: fit-content;
- overflow: hidden;
-}
-
-form.account {
- display: block;
- padding: 40px 48px;
- background-color: var(--bg-shade-2);
- color: var(--text-shade-1);
- border-radius: 12px;
- width: min(480px, 90vw);
- box-sizing: border-box;
-}
-
-form.account h2 {
- margin: 0;
- color: var(--text-shade-3);
-}
-
-form.account p {
- margin: 12px 0;
-}
-
-form.account div {
- margin-top: 24px;
-}
-
-form.account label {
- display: block;
- margin-bottom: 6px;
- text-transform: uppercase;
- font-size: 12px;
-}
-
-form.account button {
- width: 100%;
- background: var(--accent-shade-0);
-}
-
-form.account a {
- text-decoration: none;
- display: block;
- color: var(--text-shade-1);
- text-align: right;
- margin: 6px 0;
- width: fit-content;
-}
-form.account a:hover {
- color: var(--text-shade-3);
-}
-
-form.account a.pwdreset {
- margin-left: auto;
- font-size: 14px;
-}
-
-form.account a.register {
- margin: auto;
- margin-top: 18px;
-}
-
-@keyframes banner-notice {
- 0% {
- top: -150px;
- }
- 20% {
- top: 35px;
- }
- 80% {
- top: 35px;
- }
- 100% {
- top: -150px;
- }
-}
-.banner-notice {
- display: flex;
- justify-content: center;
- position: fixed;
- top: -150px;
- width: 100%;
- animation: banner-notice 5s;
-}
-.banner-notice div {
- padding: 4px 36px;
- border-radius: 5px;
- z-index: 3;
-}
-.banner-notice.success div {
- background: var(--green-shade-0);
-}
-.banner-notice.error div {
- background: var(--red-shade-1);
-}
-
-form.account.register {
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- width: min(780px, 90vw);
- column-gap: 24px;
- margin-bottom: 48px;
-}
-form.account.forgot-password div.h-captcha {
- grid-column: 1 / span 2;
- display: flex;
- justify-content: center;
-}
-form.account.register p,
-form.account.register div.email,
-form.account.register div.buttons {
- grid-column: 1 / span 2;
-}
-
-@media screen and (max-width: 720px) {
- form.account.register {
- grid-template-columns: 1fr;
- }
- form.account.register div.h-captcha,
- form.account.register p,
- form.account.register div.email,
- form.account.register div.buttons {
- grid-column: unset;
- }
-}
\ No newline at end of file
diff --git a/src/pages/account/forgot-password.vue b/src/pages/account/forgot-password.vue
index 31a8a8f..d75473c 100644
--- a/src/pages/account/forgot-password.vue
+++ b/src/pages/account/forgot-password.vue
@@ -7,7 +7,7 @@ const captchaRef = useTemplateRef('captcha');
const form = reactive({ emailOrUsername: '' });
-const { execute } = useAsync({
+const { execute, isLoading } = useAsync({
async handler() {
let captchaResponse: string | null = null;
if (captchaRef.value) {
@@ -68,7 +68,8 @@ const { execute } = useAsync({
/>
diff --git a/src/pages/account/index.vue b/src/pages/account/index.vue
index e758a63..d6dccac 100644
--- a/src/pages/account/index.vue
+++ b/src/pages/account/index.vue
@@ -39,7 +39,7 @@ const dialogContainer = ref(null);
const deleteModalOpen = ref(false);
const editModalOpen = ref(false);
-const { execute: executeUpdateServerEnvironment } = useAsync({
+const { execute: executeUpdateServerEnvironment, isLoading: isLoadingUpdateServerEnv } = useAsync({
async handler(env: ApiAccountUpdateRequest['environment']) {
await apiFetch('/api/account/update', {
method: 'PATCH',
@@ -58,7 +58,7 @@ const { execute: executeUpdateServerEnvironment } = useAsync({
}
});
-const { execute: executeDeleteAccount } = useAsync({
+const { execute: executeDeleteAccount, isLoading: isLoadingDelete } = useAsync({
async handler() {
await apiFetch('/api/account/delete', {
method: 'POST'
@@ -75,7 +75,7 @@ const { execute: executeDeleteAccount } = useAsync({
}
});
-const { execute: executeLinkDiscord } = useAsync({
+const { execute: executeLinkDiscord, isLoading: isLoadingLink } = useAsync({
async handler() {
const result = await apiFetch('/api/account/discord-link', {
method: 'GET'
@@ -91,7 +91,7 @@ const { execute: executeLinkDiscord } = useAsync({
}
});
-const { execute: executeUnlinkDiscord } = useAsync({
+const { execute: executeUnlinkDiscord, isLoading: isLoadingUnlink } = useAsync({
async handler() {
await apiFetch('/api/account/discord-unlink', {
method: 'POST'
@@ -194,15 +194,19 @@ useHead({
@@ -338,7 +342,8 @@ useHead({
class="button secondary"
@click.prevent="() => executeUpdateServerEnvironment(selectedServerEnv)"
>
- Save
+
+ Save
- {{ $t("account.settings.settingCards.removeDiscord") }}
+
+ {{ $t("account.settings.settingCards.removeDiscord") }}
{{ $t("account.settings.settingCards.noDiscordLinked") }}
@@ -433,7 +439,8 @@ useHead({
:style="{cursor: 'pointer'}"
@click="executeLinkDiscord"
>
- {{ $t("account.settings.settingCards.linkDiscord") }}
+
+ {{ $t("account.settings.settingCards.linkDiscord") }}
diff --git a/src/pages/account/login.vue b/src/pages/account/login.vue
index 0e824c1..a495277 100644
--- a/src/pages/account/login.vue
+++ b/src/pages/account/login.vue
@@ -16,7 +16,7 @@ const registerURI = computed(() => {
const loginForm = reactive({ username: '', password: '' });
-const { execute } = useAsync({
+const { execute, isLoading } = useAsync({
async handler() {
const res = await $fetch('/api/auth/login', {
method: 'POST',
@@ -75,8 +75,8 @@ const { execute } = useAsync({