mirror of
https://github.com/PretendoNetwork/website.git
synced 2026-08-27 02:56:47 -05:00
feat: add loading indicators to every async action
This commit is contained in:
@@ -33,7 +33,7 @@ form.account p {
|
||||
margin: 12px 0;
|
||||
}
|
||||
|
||||
form.account div {
|
||||
form.account > div {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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({
|
||||
/>
|
||||
<div class="buttons">
|
||||
<button type="submit">
|
||||
{{ t('account.forgotPassword.submit') }}
|
||||
<Loader v-if="isLoading" />
|
||||
<span v-else>{{ $t("account.forgotPassword.submit") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -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({
|
||||
</p>
|
||||
</AlertDialog.Description>
|
||||
<div class="modal-button-wrapper">
|
||||
<AlertDialog.Cancel class="cancel">
|
||||
<AlertDialog.Cancel
|
||||
:disabled="isLoadingDelete"
|
||||
class="cancel"
|
||||
>
|
||||
{{ $t("modals.cancel") }}
|
||||
</AlertDialog.Cancel>
|
||||
<AlertDialog.Action
|
||||
class="alert."
|
||||
<button
|
||||
class="alert"
|
||||
@click="executeDeleteAccount"
|
||||
>
|
||||
{{ $t("account.settings.delete.modalConfirm") }}
|
||||
</AlertDialog.Action>
|
||||
<Loader v-if="isLoadingDelete" />
|
||||
<span v-else>{{ $t("account.settings.delete.modalConfirm") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</AlertDialog.Content>
|
||||
</AlertDialog.Portal>
|
||||
@@ -338,7 +342,8 @@ useHead({
|
||||
class="button secondary"
|
||||
@click.prevent="() => executeUpdateServerEnvironment(selectedServerEnv)"
|
||||
>
|
||||
Save
|
||||
<Loader v-if="isLoadingUpdateServerEnv" />
|
||||
<span v-else>Save</span>
|
||||
</button>
|
||||
<p
|
||||
v-html="
|
||||
@@ -425,7 +430,8 @@ useHead({
|
||||
class="button secondary"
|
||||
@click="executeUnlinkDiscord"
|
||||
>
|
||||
{{ $t("account.settings.settingCards.removeDiscord") }}
|
||||
<Loader v-if="isLoadingUnlink" />
|
||||
<span v-else>{{ $t("account.settings.settingCards.removeDiscord") }}</span>
|
||||
</button>
|
||||
<p v-else>
|
||||
{{ $t("account.settings.settingCards.noDiscordLinked") }}
|
||||
@@ -433,7 +439,8 @@ useHead({
|
||||
:style="{cursor: 'pointer'}"
|
||||
@click="executeLinkDiscord"
|
||||
>
|
||||
{{ $t("account.settings.settingCards.linkDiscord") }}
|
||||
<Loader v-if="isLoadingLink" />
|
||||
<span v-else>{{ $t("account.settings.settingCards.linkDiscord") }}</span>
|
||||
</NuxtLink>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -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({
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit">
|
||||
<!-- TODO: loading state on button. ... style maybe? -->
|
||||
{{ $t("account.loginForm.login") }}
|
||||
<Loader v-if="isLoading" />
|
||||
<span v-else>{{ $t("account.loginForm.login") }}</span>
|
||||
</button>
|
||||
<a
|
||||
:href="registerURI"
|
||||
|
||||
@@ -19,7 +19,7 @@ const loginURI = computed(() => {
|
||||
const registerForm = reactive({ email: '', username: '', mii_name: '', password: '', password_confirm: '' });
|
||||
const captchaRef = useTemplateRef('captcha');
|
||||
|
||||
const { execute } = useAsync({
|
||||
const { execute, isLoading } = useAsync({
|
||||
async handler() {
|
||||
let captchaResponse: string | null = null;
|
||||
if (captchaRef.value) {
|
||||
@@ -128,7 +128,8 @@ const { execute } = useAsync({
|
||||
/>
|
||||
<div class="buttons">
|
||||
<button type="submit">
|
||||
{{ $t("account.loginForm.register") }}
|
||||
<Loader v-if="isLoading" />
|
||||
<span v-else>{{ $t("account.loginForm.register") }}</span>
|
||||
</button>
|
||||
<a
|
||||
:href="loginURI"
|
||||
|
||||
@@ -6,7 +6,7 @@ const toasts = useToasts();
|
||||
const { t } = useI18n();
|
||||
const form = reactive({ password: '', passwordConfirm: '' });
|
||||
|
||||
const { execute } = useAsync({
|
||||
const { execute, isLoading } = useAsync({
|
||||
async handler() {
|
||||
const resetToken = route.query.token?.toString();
|
||||
if (!resetToken) {
|
||||
@@ -65,7 +65,8 @@ const { execute } = useAsync({
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit">
|
||||
{{ t('account.resetPassword.submit') }}
|
||||
<Loader v-if="isLoading" />
|
||||
<span v-else>{{ $t("account.resetPassword.submit") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
/* eslint-disable vue/no-v-html -- locale files still have raw html */
|
||||
import {
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
@@ -42,7 +41,7 @@ const goalTextVars = computed(() => {
|
||||
};
|
||||
});
|
||||
|
||||
const { execute: executeUnsubscribe } = useAsync({
|
||||
const { execute: executeUnsubscribe, isLoading: isLoadingUnsubscribe } = useAsync({
|
||||
async handler() {
|
||||
await apiFetch('/api/account/unsubscribe', {
|
||||
method: 'POST'
|
||||
@@ -58,7 +57,7 @@ const { execute: executeUnsubscribe } = useAsync({
|
||||
}
|
||||
});
|
||||
|
||||
const { execute: executeCheckout } = useAsync({
|
||||
const { execute: executeCheckout, isLoading: isLoadingCheckout } = useAsync({
|
||||
async handler(priceId: string) {
|
||||
const result = await apiFetch('/api/account/checkout', {
|
||||
method: 'POST',
|
||||
@@ -288,15 +287,19 @@ useHead({
|
||||
<span v-html="$t('upgrade.unsubPrompt').replace('tiername', profile?.stripeTier?.tierName ?? '')" />
|
||||
</AlertDialogDescription>
|
||||
<div class="modal-button-wrapper">
|
||||
<AlertDialogCancel class="cancel">
|
||||
<AlertDialogCancel
|
||||
:disabled="isLoadingUnsubscribe"
|
||||
class="cancel"
|
||||
>
|
||||
{{ $t("modals.cancel") }}
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
<button
|
||||
class="alert"
|
||||
@click="executeUnsubscribe"
|
||||
>
|
||||
{{ $t("upgrade.unsubConfirm") }}
|
||||
</AlertDialogAction>
|
||||
<Loader v-if="isLoadingUnsubscribe" />
|
||||
<span v-else>{{ $t("upgrade.unsubConfirm") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</AlertDialogContent>
|
||||
</AlertDialogPortal>
|
||||
@@ -318,15 +321,19 @@ useHead({
|
||||
<span v-html="$t('upgrade.changeTierPrompt').replace('oldtiername', profile.stripeTier.tierName).replace('newtiername', selectedTier?.name ?? '')" />
|
||||
</AlertDialogDescription>
|
||||
<div class="modal-button-wrapper">
|
||||
<AlertDialogCancel class="cancel">
|
||||
<AlertDialogCancel
|
||||
:disabled="isLoadingCheckout"
|
||||
class="cancel"
|
||||
>
|
||||
{{ $t("modals.cancel") }}
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
<button
|
||||
class="action"
|
||||
@click="() => executeCheckout(selectedTier?.priceId || '')"
|
||||
>
|
||||
{{ $t("modals.confirm") }}
|
||||
</AlertDialogAction>
|
||||
<Loader v-if="isLoadingCheckout" />
|
||||
<span v-else>{{ $t("modals.confirm") }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</AlertDialogContent>
|
||||
</AlertDialogPortal>
|
||||
@@ -341,7 +348,8 @@ useHead({
|
||||
|
||||
@click.prevent="executeCheckout(selectedTier.priceId)"
|
||||
>
|
||||
Subscribe to {{ selectedTier.name }}
|
||||
<Loader v-if="isLoadingCheckout" />
|
||||
<span v-else>Subscribe to {{ selectedTier.name }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
|
||||
Reference in New Issue
Block a user