65 lines
2.5 KiB
Vue
65 lines
2.5 KiB
Vue
<script setup lang="ts">
|
|
const props = defineProps<{ mode: 'login' | 'register' }>();
|
|
const route = useRoute();
|
|
const router = useRouter();
|
|
const next = computed(() => (route.query.next as string) || '/dashboard');
|
|
const busy = ref(false);
|
|
const error = ref<string | null>(null);
|
|
const isRegister = computed(() => props.mode === 'register');
|
|
|
|
async function onSubmit(e: Event) {
|
|
e.preventDefault();
|
|
busy.value = true;
|
|
error.value = null;
|
|
const form = new FormData(e.target as HTMLFormElement);
|
|
const body = Object.fromEntries(form.entries());
|
|
try {
|
|
await $fetch(`/api/auth/${props.mode}`, { method: 'POST', body });
|
|
await router.push(next.value);
|
|
await refreshNuxtData('current-user');
|
|
} catch (err: unknown) {
|
|
error.value = (err as { statusMessage?: string; message?: string })?.statusMessage || 'Request failed';
|
|
busy.value = false;
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="card">
|
|
<h1 class="text-xl font-semibold">{{ isRegister ? t('auth.registerTitle') : t('auth.loginTitle') }}</h1>
|
|
<form class="mt-6 space-y-4" novalidate @submit="onSubmit">
|
|
<div v-if="isRegister">
|
|
<label class="label" for="name">{{ t('auth.name') }}</label>
|
|
<input id="name" name="name" class="input" autocomplete="name">
|
|
</div>
|
|
<div>
|
|
<label class="label" for="email">{{ t('auth.email') }}</label>
|
|
<input id="email" name="email" type="email" required class="input" autocomplete="email">
|
|
</div>
|
|
<div>
|
|
<label class="label" for="password">{{ t('auth.password') }}</label>
|
|
<input
|
|
id="password"
|
|
name="password"
|
|
type="password"
|
|
required
|
|
minlength="8"
|
|
class="input"
|
|
:autocomplete="isRegister ? 'new-password' : 'current-password'"
|
|
>
|
|
</div>
|
|
<p v-if="error" class="text-sm text-red-600">{{ error }}</p>
|
|
<button type="submit" class="btn-primary w-full" :disabled="busy">
|
|
{{ busy ? '…' : isRegister ? t('auth.submitRegister') : t('auth.submitLogin') }}
|
|
</button>
|
|
</form>
|
|
<p class="muted mt-5 text-center text-sm">
|
|
{{ isRegister ? t('auth.hasAccount') : t('auth.noAccount') }}
|
|
<NuxtLink :to="isRegister ? '/login' : '/register'" class="font-medium text-brand-600 hover:underline">
|
|
{{ isRegister ? t('auth.submitLogin') : t('auth.submitRegister') }}
|
|
</NuxtLink>
|
|
</p>
|
|
<p v-if="!isRegister" class="muted mt-3 text-center text-xs">{{ t('auth.demoHint') }}</p>
|
|
</div>
|
|
</template>
|