Migration to Nuxt v4 file structure and dependencies update

This commit is contained in:
Clément Pons
2025-11-13 10:05:41 +01:00
parent dd4191bea6
commit dfbb31595e
90 changed files with 652 additions and 924 deletions

View File

@@ -0,0 +1,18 @@
<template>
<Head>
<Title>d[any] - Validation de votre adresse mail</Title>
</Head>
<div class="flex flex-col justify-center items-center">
<h2 class="text-2xl font-bold">Votre compte a été validé ! 🎉</h2>
<div class="flex flex-row gap-8">
<Button class="bg-light-25 dark:bg-dark-25"><NuxtLink :to="{ name: 'user-login', replace: true }">Se connecter</NuxtLink></Button>
<Button class="bg-light-25 dark:bg-dark-25"><NuxtLink :to="{ name: 'index', replace: true }">Retourner à l'accueil</NuxtLink></Button>
</div>
</div>
</template>
<script setup lang="ts">
definePageMeta({
layout: 'login',
})
</script>

View File

@@ -0,0 +1,45 @@
<template>
<Head>
<Title>d[any] - Reinitialisation de mon mot de passe</Title>
</Head>
<div class="flex flex-1 flex-col justify-center items-center">
<div class="flex gap-8 items-center">
<span class="border border-transparent hover:border-light-35 dark:hover:border-dark-35 p-1 cursor-pointer" @click="() => $router.go(-1)"><Icon icon="radix-icons:arrow-left" class="text-light-50 dark:text-dark-50 w-6 h-6"/></span>
<h4 class="text-xl font-bold">Reinitialisation de mon mot de passe</h4>
</div>
<form @submit.prevent="() => submit()" class="flex flex-1 flex-col justify-center items-stretch">
<TextInput type="text" label="Utilisateur ou email" autocomplete="username" v-model="email"/>
<Button class="border border-light-35 dark:border-dark-35 self-center" :loading="status === 'pending'">Envoyer un email</Button>
</form>
<div v-if="status === 'success'" class="border border-light-green dark:border-dark-green bg-light-greenBack dark:bg-dark-greenBack text-wrap mt-4 py-2 px-4 max-w-96">
Un mail vous a été envoyé si un compte existe pour cet identifiant.
</div>
</div>
</template>
<script setup lang="ts">
import { Icon } from '@iconify/vue';
definePageMeta({
layout: 'login',
usersGoesTo: '/user/profile',
});
const email = ref(''), status = ref<'idle' | 'pending' | 'success' | 'error'>('idle');
async function submit()
{
status.value = 'pending';
try {
await $fetch(`/api/auth/request-reset`, {
body: { profile: email.value },
method: 'post',
});
status.value = 'success';
}
catch(e)
{
status.value = 'error';
}
}
</script>

View File

@@ -0,0 +1,87 @@
<template>
<Head>
<Title>d[any] - Reinitialisation de mon mot de passe</Title>
</Head>
<div class="flex flex-1 flex-col justify-center items-center">
<div class="flex gap-8 items-center">
<span class="border border-transparent hover:border-light-35 dark:hover:border-dark-35 p-1 cursor-pointer" @click="() => $router.go(-1)"><Icon icon="radix-icons:arrow-left" class="text-light-50 dark:text-dark-50 w-6 h-6"/></span>
<h4 class="text-center flex-1 text-xl font-bold">Reinitialisation de mon mot de passe</h4>
</div>
<form @submit.prevent="submit" class="flex flex-1 flex-col justify-center items-stretch">
<TextInput type="password" label="Nouveau mot de passe" autocomplete="newPassword" v-model="newPasswd" :class="{ '!border-light-red !dark:border-dark-red': error }"/>
<div class="grid grid-cols-2 flex-col font-light border border-light-35 dark:border-dark-35 px-4 py-2 m-4 ms-0 text-sm leading-[18px] lg:text-base order-8 col-span-2 md:col-span-1 md:order-none">
<span class="col-span-2">Prérequis de sécurité</span>
<span class="ps-4 flex items-center gap-2" :class="{'text-light-red dark:text-dark-red': !checkedLength}"><Icon v-show="!checkedLength" icon="radix-icons:cross-2" />8 à 128 caractères</span>
<span class="ps-4 flex items-center gap-2" :class="{'text-light-red dark:text-dark-red': !checkedLower}"><Icon v-show="!checkedLower" icon="radix-icons:cross-2" />Une minuscule</span>
<span class="ps-4 flex items-center gap-2" :class="{'text-light-red dark:text-dark-red': !checkedUpper}"><Icon v-show="!checkedUpper" icon="radix-icons:cross-2" />Une majuscule</span>
<span class="ps-4 flex items-center gap-2" :class="{'text-light-red dark:text-dark-red': !checkedDigit}"><Icon v-show="!checkedDigit" icon="radix-icons:cross-2" />Un chiffre</span>
<span class="ps-4 flex items-center gap-2" :class="{'text-light-red dark:text-dark-red': !checkedSymbol}"><Icon v-show="!checkedSymbol" icon="radix-icons:cross-2" />Un caractère special</span>
</div>
<TextInput type="password" label="Repeter le nouveau mot de passe" autocomplete="newPassword" v-model="repeatPasswd" :class="{ 'border-light-red dark:border-dark-red': manualError }"/>
<Button class="border border-light-35 dark:border-dark-35 self-center" :loading="status === 'pending'">Reinitialiser</Button>
</form>
</div>
</template>
<script setup lang="ts">
import { Icon } from '@iconify/vue';
import { Toaster } from '#shared/components.util';
definePageMeta({
layout: 'login',
usersGoesTo: '/user/login',
});
const query = useRouter().currentRoute.value.query;
const status = ref<'idle' | 'pending' | 'success' | 'error'>('idle'), manualError = ref(false);
const oldPasswd = ref(''), newPasswd = ref(''), repeatPasswd = ref('');
const checkedLength = computed(() => newPasswd.value.length >= 8 && newPasswd.value.length <= 128);
const checkedLower = computed(() => newPasswd.value.toUpperCase() !== newPasswd.value);
const checkedUpper = computed(() => newPasswd.value.toLowerCase() !== newPasswd.value);
const checkedDigit = computed(() => /[0-9]/.test(newPasswd.value));
const checkedSymbol = computed(() => " !\"#$%&'()*+,-./:;<=>?@[]^_`{|}~".split("").some(e => newPasswd.value.includes(e)));
const equalsPasswd = computed(() => newPasswd.value && repeatPasswd.value && newPasswd.value === repeatPasswd.value);
const error = computed(() => !checkedLength.value || !checkedLower.value || !checkedUpper.value || !checkedDigit.value || !checkedSymbol.value);
async function submit()
{
if(!equalsPasswd.value)
{
manualError.value = true;
return;
}
manualError.value = false;
status.value = 'pending';
try {
const result = await $fetch(`/api/users/${query.i}/reset-password`, {
method: 'post',
body: {
password: newPasswd.value,
},
query: query,
});
if(result && result.success)
{
status.value = 'success';
Toaster.add({ content: 'Votre mot de passe a été modifié avec succès.', duration: 10000, timer: true, type: 'success' });
useRouter().push({ name: 'user-login' });
}
else
{
throw result.error ?? new Error('Erreur inconnue.');
}
} catch(e) {
status.value = 'error';
const err = e as any;
Toaster.add({ content: err?.data?.message ?? err?.message ?? 'Erreur inconnue', duration: 10000, timer: true, type: 'error' });
}
}
</script>