go-quasar-partial-ssr/frontend/src/pages/dev/SignupPage.vue

197 lines
4.9 KiB
Vue

<template>
<q-page class="auth-page">
<div class="auth-shell auth-shell-wide">
<q-card flat bordered class="auth-card">
<q-card-section>
<div class="text-overline text-primary">Registrazione</div>
<div class="text-h4">Sign up</div>
<div class="text-body2 text-grey-7">Crea un nuovo utente.</div>
</q-card-section>
<q-separator />
<q-card-section>
<q-form v-if="!sent" class="auth-form" autocomplete="off" @submit.prevent="submit">
<q-input ref="firstNameRef" v-model="form.firstName" outlined label="Nome" autocomplete="off" />
<q-input v-model="form.lastName" outlined label="Cognome" autocomplete="off" />
<q-input v-model="form.email" outlined type="email" label="Email" autocomplete="off" />
<q-input
v-model="form.password"
outlined
type="password"
label="Password"
autocomplete="new-password"
/>
<q-input
v-model="form.confirmPassword"
outlined
type="password"
label="Ripeti password"
autocomplete="new-password"
/>
<q-checkbox
v-model="form.acceptTerms"
label="Accetto le condizioni"
/>
<div class="auth-actions">
<q-btn color="primary" label="Crea account" type="submit" :loading="loading" />
</div>
</q-form>
<div v-else class="success-state">
<q-icon name="task_alt" size="56px" color="positive" />
<div class="text-h6">Registrazione completata</div>
<div class="text-body2 text-grey-7">
Il tuo account e stato creato con successo.
</div>
<div class="success-actions">
<q-btn flat color="primary" label="Home" to="/" />
<q-btn color="primary" label="Login" to="/login" />
</div>
</div>
</q-card-section>
</q-card>
</div>
</q-page>
</template>
<script setup lang="ts">
import { nextTick, onMounted, reactive, ref, watch } from 'vue';
import { useQuasar } from 'quasar';
import { EnumUserStatus, register, type UserCreateRequest } from 'src/api/users';
const $q = useQuasar();
const loading = ref(false);
const sent = ref(false);
const firstNameRef = ref();
const form = reactive({
firstName: '',
lastName: '',
email: '',
password: '',
confirmPassword: '',
acceptTerms: false,
});
onMounted(async () => {
await focusFirstField();
});
watch(sent, async (value) => {
if (!value) {
await focusFirstField();
}
});
async function submit(): Promise<void> {
if (!form.firstName.trim() || !form.lastName.trim() || !form.email.trim()) {
$q.notify({ type: 'negative', message: 'Compila tutti i campi obbligatori.' });
return;
}
if (form.password.length < 8) {
$q.notify({ type: 'negative', message: 'La password deve contenere almeno 8 caratteri.' });
return;
}
if (form.password !== form.confirmPassword) {
$q.notify({ type: 'negative', message: 'Le password non coincidono.' });
return;
}
if (!form.acceptTerms) {
$q.notify({ type: 'negative', message: 'Devi accettare le condizioni.' });
return;
}
loading.value = true;
try {
const payload: UserCreateRequest = {
name: `${form.firstName.trim()} ${form.lastName.trim()}`.trim(),
email: form.email.trim(),
password: form.password,
permission: "user",
status: EnumUserStatus.UserStatusPending,
types: ['internal'],
avatar: null,
details: {
title: '',
firstName: form.firstName.trim(),
lastName: form.lastName.trim(),
address: '',
city: '',
zipCode: '',
country: '',
phone: '',
id: 0,
userId: 0,
createdAt: new Date(),
updatedAt: new Date(),
},
preferences: null,
};
const response = await register(payload);
if (response.error) {
throw new Error(response.error);
}
sent.value = true;
} catch (error) {
$q.notify({
type: 'negative',
message: error instanceof Error ? error.message : String(error),
});
} finally {
loading.value = false;
}
}
async function focusFirstField(): Promise<void> {
await nextTick();
firstNameRef.value?.focus?.();
}
</script>
<style scoped>
.auth-page {
background: linear-gradient(180deg, #f7fafc 0%, #e9f0f7 100%);
}
.auth-shell {
max-width: 520px;
margin: 0 auto;
padding: 40px 20px;
}
.auth-shell-wide {
max-width: 760px;
}
.auth-card {
border-radius: 24px;
}
.auth-form {
display: grid;
gap: 14px;
}
.auth-actions {
display: flex;
justify-content: flex-end;
}
.success-state {
display: grid;
justify-items: center;
gap: 12px;
text-align: center;
padding: 12px 0;
}
.success-actions {
display: flex;
gap: 12px;
}
</style>