Files
social-media/frontend/src/views/auth/RegisterView.vue

238 lines
8.7 KiB
Vue

<template>
<div class="flex min-h-full w-full items-center justify-center p-20">
<!-- Show verification message on success -->
<div
v-if="registrationSuccess"
class="card justify-items-center"
>
<img
:alt="t('alt')"
src="/images/hutopymedia/loginpage/hutopylogin.svg"
/>
<div class="flex flex-col gap-10 text-center">
<h1 class="login-text text-2xl font-bold text-green-600">
{{ t('success.title') }}
</h1>
<div class="text-hOnSurface">
<p>{{ t('success.message') }}</p>
<p class="mt-2 font-medium">{{ userEmail }}</p>
</div>
<div class="mt-4 flex flex-col gap-2">
<router-link
class="text-blue-500 hover:underline"
to="/login"
>
{{ t('success.backToLogin') }}
</router-link>
<router-link
:to="{ path: '/verify-email', query: { email: userEmail } }"
class="text-blue-500 hover:underline"
>
{{ t('success.resendVerification') }}
</router-link>
</div>
</div>
</div>
<!-- Show registration form -->
<div
v-else
class="card justify-items-center"
>
<img
:alt="t('alt')"
src="/images/hutopymedia/loginpage/hutopylogin.svg"
/>
<div class="flex flex-col gap-10">
<h1 class="login-text text-center text-2xl font-bold">
{{ t('title') }}
</h1>
<v-form @submit.prevent="handleRegister">
<div class="flex flex-col gap-4">
<v-text-field
v-model="name"
:label="t('name')"
required
></v-text-field>
<v-text-field
v-model="email"
:label="t('email')"
required
type="email"
></v-text-field>
<v-text-field
v-model="password"
:hint="t('passwordRequirements')"
:label="t('password')"
:type="showPassword ? 'text' : 'password'"
required
>
<template v-slot:append-inner>
<v-icon
:icon="showPassword ? mdiEyeOff : mdiEye"
class="visibility-toggle"
size="small"
@click="showPassword = !showPassword"
/>
</template>
</v-text-field>
<v-text-field
v-model="confirmPassword"
:label="t('confirmPassword')"
:type="showConfirmPassword ? 'text' : 'password'"
required
>
<template v-slot:append-inner>
<v-icon
:icon="showConfirmPassword ? mdiEyeOff : mdiEye"
class="visibility-toggle"
size="small"
@click="showConfirmPassword = !showConfirmPassword"
/>
</template>
</v-text-field>
<v-btn
:loading="isLoading"
block
color="primary"
type="submit"
>
{{ t('register') }}
</v-btn>
<!-- Error message displayed as block text below submit button -->
<div
v-if="errorMessage"
class="mt-2 p-3 bg-red-50 border border-red-200 rounded text-red-700 text-sm"
>
{{ errorMessage }}
</div>
<div class="mt-4 text-center">
{{ t('alreadyHaveAccount') }}
<router-link
class="text-blue-500"
to="/login"
>
{{ t('signIn') }}
</router-link>
</div>
</div>
</v-form>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { useClient } from '@/plugins/api.js';
import { useI18n } from 'vue-i18n';
import { mdiEye, mdiEyeOff } from '@mdi/js';
const { t } = useI18n();
const clientApi = useClient();
const name = ref('');
const email = ref('');
const password = ref('');
const confirmPassword = ref('');
const isLoading = ref(false);
const errorMessage = ref('');
const showPassword = ref(false);
const showConfirmPassword = ref(false);
const registrationSuccess = ref(false);
const userEmail = ref('');
async function handleRegister() {
if (password.value !== confirmPassword.value) {
errorMessage.value = t('passwordsDoNotMatch');
return;
}
isLoading.value = true;
errorMessage.value = '';
try {
await clientApi.post('api/users/register', {
name: name.value,
email: email.value.trim(),
password: password.value,
});
// On success, show verification message
userEmail.value = email.value.trim();
registrationSuccess.value = true;
} catch (error) {
console.error('Registration failed:', error);
errorMessage.value = error.response?.data?.message || t('registrationFailed');
} finally {
isLoading.value = false;
}
}
</script>
<style scoped>
.visibility-toggle {
@apply cursor-pointer;
@apply transition-opacity duration-300;
@apply opacity-60 hover:opacity-100;
@apply z-10;
}
/* Override Vuetify's default padding to accommodate our icon */
:deep(.v-field__append-inner) {
padding-inline-start: 0;
}
</style>
<i18n>
{
"en": {
"title": "Create your account",
"alt": "Hutopy Registration",
"name": "Full Name",
"email": "Email",
"password": "Password",
"confirmPassword": "Confirm Password",
"passwordRequirements": "Password must be at least 8 characters",
"register": "Register",
"alreadyHaveAccount": "Already have an account?",
"signIn": "Sign in",
"passwordsDoNotMatch": "Passwords do not match",
"registrationFailed": "Registration failed. Please try again.",
"success": {
"title": "Registration Successful!",
"message": "Please check your email to verify your account. We've sent a verification link to:",
"backToLogin": "Back to Login",
"resendVerification": "Didn't receive the email? Resend verification"
}
},
"fr": {
"title": "Créer votre compte",
"alt": "Inscription Hutopy",
"name": "Nom complet",
"email": "Email",
"password": "Mot de passe",
"confirmPassword": "Confirmer le mot de passe",
"passwordRequirements": "Le mot de passe doit comporter au moins 8 caractères",
"register": "S'inscrire",
"alreadyHaveAccount": "Vous avez déjà un compte?",
"signIn": "Se connecter",
"passwordsDoNotMatch": "Les mots de passe ne correspondent pas",
"registrationFailed": "L'inscription a échoué. Veuillez réessayer.",
"success": {
"title": "Inscription réussie!",
"message": "Veuillez vérifier votre email pour activer votre compte. Nous avons envoyé un lien de vérification à:",
"backToLogin": "Retour à la connexion",
"resendVerification": "Vous n'avez pas reçu l'email? Renvoyer la vérification"
}
}
}
</i18n>