initial commit
This commit is contained in:
73
app/pages/confirmation.vue
Normal file
73
app/pages/confirmation.vue
Normal file
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<h1>{{ $t('confirmation.title') }}</h1>
|
||||
|
||||
<uiLoading v-if="result === null" />
|
||||
|
||||
<div class="confirm-main-text" v-if="result!=null">
|
||||
<div v-if="result === true">
|
||||
<p v-if="authStore.isLoggedIn" >{{ $t('confirmation.successConnected') }}
|
||||
<NuxtLink :to="localePath('/lists')">{{ $t('confirmation.listsLink') }}</NuxtLink>
|
||||
</p>
|
||||
<p v-else>{{ $t('confirmation.successNotConnected') }} <NuxtLink :to="localePath('/login')">{{ $t('confirmation.loginLink') }}</NuxtLink></p>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p class="confirm-main-text" v-if="result === 'expired'">{{ $t('confirmation.failureMessage')}}<br/><span>{{ $t('confirmation.failureCauseExpired') }}</span></p>
|
||||
<p class="confirm-main-text" v-else>{{ $t('confirmation.failureMessage')}}<br/><span>{{ $t('confirmation.failureCauseInvalid') }}</span></p>
|
||||
<p class="confirm-main-text last">{{ $t('confirmation.failureYouCan')}} <NuxtLink :to="localePath('/signup')">{{ $t('confirmation.failureCreateNewAccount') }}</NuxtLink> {{ $t('confirmation.failureOr') }} <NuxtLink :to="localePath('/')">{{ $t('confirmation.backHome') }}</NuxtLink>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: 'confirmation', // utilisation du layout confirm
|
||||
public: true,
|
||||
})
|
||||
import type { ConfirmResult} from '~/types/auth';
|
||||
const authStore = useAuthStore()
|
||||
const localePath = useLocalePath()
|
||||
const loading = ref<boolean>(true)
|
||||
const result = ref<ConfirmResult | null>(null)
|
||||
const userConnected = ref<boolean>(false)
|
||||
|
||||
onMounted(async () => {
|
||||
const route = useRoute()
|
||||
// const user = route.query.user as string
|
||||
const token = route.query.token as string
|
||||
|
||||
if (!token) {
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
result.value = await authStore.confirmUser(token)
|
||||
if (result.value === true) {
|
||||
if (authStore.isLoggedIn && authStore.user){
|
||||
authStore.user.confirmed = true;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
throw new Error("Invalid !");
|
||||
|
||||
}
|
||||
finally{
|
||||
loading.value=false
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
p > span{
|
||||
color:red;
|
||||
text-align: center;
|
||||
display: block;
|
||||
margin-block: 1.2em;
|
||||
font-weight: bold;
|
||||
}
|
||||
.confirm-main-text:first-of-type{
|
||||
margin-top: 2em;
|
||||
}
|
||||
</style>
|
||||
49
app/pages/index.vue
Normal file
49
app/pages/index.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
public: true,
|
||||
pageId: 'index'
|
||||
})
|
||||
const authStore = useAuthStore();
|
||||
const localePath = useLocalePath()
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>{{ $t('index.title') }}</h1>
|
||||
<h2 class="title">{{ $t('index.subtitle1')}}</h2>
|
||||
<div class="index-main-text" v-html="$t('index.mainText')"></div>
|
||||
|
||||
<div v-if="!authStore.isLoggedIn">
|
||||
<div class="index-main-text-last">
|
||||
<i18n-t keypath="index.lastSentenceUnconnect" tag="p">
|
||||
<template #login>
|
||||
<NuxtLink :to="localePath('/login')">
|
||||
{{ $t('index.login') }}
|
||||
</NuxtLink>
|
||||
</template>
|
||||
|
||||
<template #signup>
|
||||
<NuxtLink :to="localePath('/signup')">
|
||||
{{ $t('index.signup') }}
|
||||
</NuxtLink>
|
||||
</template>
|
||||
</i18n-t>
|
||||
<ButtonGoogle/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p>{{ $t('index.lastSentenceConnected') }} <NuxtLink :to="localePath('lists')">{{ $t('index.seeLists') }}.</NuxtLink>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.button {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
color: #06c;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}</style>
|
||||
51
app/pages/lists.vue
Normal file
51
app/pages/lists.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
pageId: 'lists'
|
||||
})
|
||||
import type {List} from '~/types/lists'
|
||||
const listStore = useListStore()
|
||||
|
||||
// activeTabId réactif
|
||||
const activeTabId = ref<number | undefined>(undefined)
|
||||
|
||||
onMounted(async () => {
|
||||
if (!listStore.lists.length) {
|
||||
await listStore.fetchLists()
|
||||
}
|
||||
|
||||
const defaultOpen = listStore.lists.find((l) => l.is_open === true)
|
||||
activeTabId.value = defaultOpen?.id ?? listStore.lists[0]?.id
|
||||
})
|
||||
|
||||
// Fonction utilitaire pour parser le contenu chiffré
|
||||
const parseContent = (list: List) => {
|
||||
try {
|
||||
return JSON.parse(list.encrypted_content) as any[]
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="!listStore.loading">
|
||||
<div v-for="list in listStore.lists" :key="list.id">
|
||||
<h3>{{ list.list_title }}</h3>
|
||||
<ul>
|
||||
<li v-for="item in parseContent(list)" :key="item.id ?? item.name ?? item">
|
||||
{{ item.name ?? JSON.stringify(item) }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<UiLoading/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
h3{
|
||||
font-size: 1.2em;
|
||||
color:blueviolet
|
||||
}
|
||||
</style>
|
||||
114
app/pages/login.vue
Normal file
114
app/pages/login.vue
Normal file
@@ -0,0 +1,114 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
public: true, pageId: 'login'
|
||||
})
|
||||
const localePath = useLocalePath()
|
||||
const authStore = useAuthStore() // Store authenticate
|
||||
const login = ref('')
|
||||
const password = ref('')
|
||||
const awaiting = ref(false)
|
||||
const displayPwdReset=true
|
||||
|
||||
const errors = ref({
|
||||
loginEmpty: false,
|
||||
passwordEmpty: false,
|
||||
loginFailed: false,
|
||||
unconfirmedUser: false,
|
||||
})
|
||||
|
||||
const handleFormSubmit = async() => {
|
||||
// On retire l'erreur précédente
|
||||
errors.value.loginFailed = false;
|
||||
errors.value.unconfirmedUser = false;
|
||||
awaiting.value = true
|
||||
// --- Vérification des données du formulaire --- //
|
||||
|
||||
// En version raccourcie : on stocke le résultat de la condition dans la variable
|
||||
|
||||
errors.value.loginEmpty = ( login.value == "" );
|
||||
errors.value.passwordEmpty = ( password.value == "" );
|
||||
|
||||
// Si une erreur est rencontrée, on n'envoi pas la requete au serveur !
|
||||
if( !errors.value.loginEmpty && !errors.value.passwordEmpty )
|
||||
{
|
||||
|
||||
// Envoie de la requette à l'endpoint JWT et récupération d'un token de connexion
|
||||
const success = await authStore.login(login.value, password.value)
|
||||
if (success) {
|
||||
// Redirection
|
||||
await navigateTo(localePath('/lists'))
|
||||
}
|
||||
|
||||
else{
|
||||
errors.value.loginFailed = true;
|
||||
awaiting.value = false
|
||||
}
|
||||
}
|
||||
else{
|
||||
awaiting.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section>
|
||||
<h1>{{$t('loginTitle')}}</h1>
|
||||
<form @submit.prevent="handleFormSubmit">
|
||||
<InputText
|
||||
name="login"
|
||||
:label="$t('loginLabelUser')"
|
||||
placeholder=""
|
||||
v-model="login"
|
||||
>
|
||||
<template #message>
|
||||
<p v-if="errors.loginEmpty" class="error">{{$t('loginErrorLoginEmpty')}}</p>
|
||||
</template>
|
||||
</InputText>
|
||||
|
||||
<InputPassword
|
||||
name="password"
|
||||
:label="$t('loginLabelPwd')"
|
||||
placeholder=""
|
||||
v-model="password"
|
||||
displayPwdReset
|
||||
>
|
||||
<template #message>
|
||||
<p v-if="errors.passwordEmpty" class="error">{{ $t('loginErrorPwdEmpty') }}</p>
|
||||
</template>
|
||||
</InputPassword>
|
||||
|
||||
<div class="connection-error" >
|
||||
<p v-if="errors.loginFailed">{{ $t('loginErrorLoginFailed') }}</p>
|
||||
<p v-if="errors.unconfirmedUser">{{ $t('loginErrorUnconfirmedUSer') }}</p>
|
||||
</div>
|
||||
<ButtonBase
|
||||
:disabled="login === '' || password === '' || awaiting"
|
||||
:loading="awaiting"
|
||||
>
|
||||
{{ $t('loginFormBtn') }}
|
||||
</ButtonBase>
|
||||
</form>
|
||||
<p>{{ $t('loginGoogle') }}</p>
|
||||
<ButtonGoogle/>
|
||||
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.connection-error{
|
||||
height:1.2em;
|
||||
& > p{
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
color: rgb(185, 0, 0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
16
app/pages/otherPage.vue
Normal file
16
app/pages/otherPage.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<h1>
|
||||
value is still : {{ counter }}
|
||||
</h1>
|
||||
|
||||
<NuxtLink to="/">
|
||||
retour
|
||||
</NuxtLink>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { counter } = useCounter()
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
118
app/pages/passwordReset.vue
Normal file
118
app/pages/passwordReset.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<h1>{{$t('pwdReset.title')}}</h1>
|
||||
|
||||
<form @submit.prevent="handleSubmit">
|
||||
|
||||
<InputPassword
|
||||
name="password"
|
||||
placeholder=""
|
||||
:label="$t('pwdReset.pwdLabel1')"
|
||||
v-model="password"
|
||||
@input="check()"
|
||||
>
|
||||
<template #message>
|
||||
<p v-if="pwErrors.isEmpty" class="error">{{ $t('pwdInput.errorPwdEmpty') }}</p>
|
||||
<p v-if="pwErrors.isntValid" class="error">{{ $t('pwdInput.errorPwdIsntValid') }}</p>
|
||||
</template>
|
||||
</InputPassword>
|
||||
<PasswordChecker/>
|
||||
<InputPassword
|
||||
name="passwordVerif"
|
||||
placeholder=""
|
||||
:label="$t('pwdReset.pwdLabel2')"
|
||||
v-model="passwordVerif"
|
||||
>
|
||||
<template #message>
|
||||
<p v-if="pwErrors.doesNotMatch" class="error">{{ $t('pwdInput.pwdsDoesNotMatch') }}</p>
|
||||
<p v-if="pwErrors.verifyEmpty" class="error">{{ $t('pwdInput.pwdConfirmEmpty') }}</p>
|
||||
</template>
|
||||
</InputPassword>
|
||||
|
||||
<ButtonBase
|
||||
:loading="loading"
|
||||
:disabled="loading || success === true">
|
||||
{{ $t('pwdReset.formBtn') }}
|
||||
</ButtonBase>
|
||||
</form>
|
||||
<div v-if="success">
|
||||
<p>{{ $t('pwdReset.successMessage') }}</p>
|
||||
<NuxtLink :to="localePath('login')">{{ $t('pwdReset.loginLink') }}</NuxtLink>
|
||||
</div>
|
||||
<div v-else-if="success===false" class="error">
|
||||
<p>{{ $t('pwdReset.errorMessage') }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
public: true,
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
const localePath=useLocalePath()
|
||||
const token = ref(route.query.token)
|
||||
const authStore = useAuthStore()
|
||||
|
||||
|
||||
//** pwdReset up management **/
|
||||
const passwordToolBox = usePasswordToolBoxStore()
|
||||
|
||||
// password relative functions
|
||||
const check = () => {
|
||||
passwordToolBox.updatePassword(password.value)
|
||||
}
|
||||
|
||||
const password=ref('')
|
||||
const passwordVerif=ref('')
|
||||
|
||||
const pwErrors = ref({
|
||||
isEmpty: false as boolean,
|
||||
verifyEmpty: false as boolean,
|
||||
isntValid: false as boolean,
|
||||
doesNotMatch:false as boolean,
|
||||
})
|
||||
const loading = ref(false as boolean)
|
||||
const success = ref(null as null|boolean);
|
||||
|
||||
// form relative functions
|
||||
const handleSubmit = async() => {
|
||||
loading.value = true
|
||||
//réinitialisation des erreurs
|
||||
pwErrors.value.isEmpty = false;
|
||||
pwErrors.value.verifyEmpty = false;
|
||||
pwErrors.value.isntValid = false;
|
||||
pwErrors.value.doesNotMatch = false;
|
||||
// Check errors
|
||||
pwErrors.value.isEmpty = ( password.value == "" );
|
||||
pwErrors.value.verifyEmpty = ( passwordVerif.value == "" );
|
||||
pwErrors.value.isntValid = !passwordToolBox.isPasswordValid();
|
||||
pwErrors.value.doesNotMatch = ( password.value != passwordVerif.value );
|
||||
|
||||
if ( !pwErrors.value.isEmpty &&
|
||||
!pwErrors.value.verifyEmpty &&
|
||||
!pwErrors.value.isntValid &&
|
||||
!pwErrors.value.doesNotMatch) {
|
||||
success.value = await authStore.pwdReset(password.value, token.value)
|
||||
}
|
||||
loading.value = false
|
||||
password.value = ""
|
||||
passwordVerif.value = ""
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.error{
|
||||
& > p{
|
||||
font-weight: bold;
|
||||
color: rgb(185, 0, 0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
105
app/pages/passwordResetRequest.vue
Normal file
105
app/pages/passwordResetRequest.vue
Normal file
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<h1>{{$t('pwdResetRequest.title')}}</h1>
|
||||
<form @submit.prevent="handleFormSubmit()">
|
||||
<InputEmail
|
||||
v-model="email"
|
||||
name="email"
|
||||
:label="$t('signupLabelEmail')"
|
||||
placeholder=""
|
||||
:class="emailStatus"
|
||||
|
||||
>
|
||||
<template #message>
|
||||
<p v-if="emailErrors.isEmpty" class="error">{{ $t('signupErrorEmailEmpty') }}</p>
|
||||
<p v-else-if="emailErrors.isntValid" class="error">{{ $t('signupErrorEmailIsntValid') }}</p>
|
||||
<p v-else-if="emailValid" class="success">{{ $t('signupEmailIsValid') }}</p>
|
||||
</template>
|
||||
</InputEmail>
|
||||
|
||||
<ButtonBase
|
||||
:disabled="!emailValid || awaiting"
|
||||
:loading="awaiting">
|
||||
{{ $t('pwdResetRequest.formBtn') }}
|
||||
</ButtonBase>
|
||||
|
||||
<div class="error" v-if="formErrors.submitFailed">
|
||||
<p>{{ $t('pwdResetRequest.pb') }}</p>
|
||||
<p>{{ $t('pwdResetRequest.message') }} {{formErrors.message}}</p>
|
||||
</div>
|
||||
<div class="success" v-if="success">
|
||||
<p>{{ $t('pwdResetRequest.successMessage') }}</p>
|
||||
<p>{{ $t('pwdResetRequest.linkValidity') }}</p>
|
||||
<p>{{ $t('pwdResetRequest.seeU') }}</p>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
public: true
|
||||
})
|
||||
const { locale } = useI18n()
|
||||
const authStore = useAuthStore()
|
||||
const email = ref("")
|
||||
const awaiting = ref(false)
|
||||
const emailErrors = ref({
|
||||
isEmpty: false as boolean,
|
||||
isntValid: false as boolean,
|
||||
})
|
||||
const formErrors = ref({
|
||||
submitFailed: false as boolean,
|
||||
message: null as string | null,
|
||||
})
|
||||
const success = ref(false)
|
||||
|
||||
const emailValid = computed(() => isValidEmail(email.value))
|
||||
const emailStatus = computed(() => {
|
||||
return emailValid.value ? "mail-is-valid" : "mail-is-invalid"
|
||||
})
|
||||
|
||||
function isValidEmail(value: string):boolean {
|
||||
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)
|
||||
}
|
||||
|
||||
const handleFormSubmit = async() => {
|
||||
//* Errors reinitialisation...
|
||||
awaiting.value = true
|
||||
emailErrors.value.isEmpty = false;
|
||||
emailErrors.value.isntValid = false;
|
||||
formErrors.value.submitFailed = false;
|
||||
formErrors.value.message = '';
|
||||
success.value = false;
|
||||
|
||||
//* Now come the verifications
|
||||
emailErrors.value.isEmpty = (email.value == '')
|
||||
emailErrors.value.isntValid = ( !isValidEmail(email.value) );
|
||||
|
||||
if (emailErrors.value.isEmpty || emailErrors.value.isntValid){
|
||||
return false;
|
||||
}
|
||||
const response = await authStore.pwdResetResquest(email.value, locale.value);
|
||||
//* If we have a message, it means we have an error...
|
||||
|
||||
if (response == true){
|
||||
success.value = true;
|
||||
email.value = "";
|
||||
awaiting.value = false;
|
||||
}
|
||||
else {
|
||||
formErrors.value.submitFailed = true;
|
||||
formErrors.value.message = authStore.error;
|
||||
awaiting.value = false;
|
||||
success.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1em;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
109
app/pages/profile.vue
Normal file
109
app/pages/profile.vue
Normal file
@@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<div class="profile-page">
|
||||
<h1>{{ $t('profile.title') }}</h1>
|
||||
|
||||
<!-- Onglets -->
|
||||
<div role="tablist" aria-label="Profil utilisateur" class="tabs">
|
||||
<button
|
||||
:class="['tab', activeTab === 'general' ? 'active' : '']"
|
||||
role="tab"
|
||||
:aria-selected="activeTab === 'general'"
|
||||
aria-controls="tab-general"
|
||||
id="tab-button-general"
|
||||
@click="activeTab = 'general'; authStore.user.sudo_token = null"
|
||||
>
|
||||
{{ $t('profile.tabGeneral') }}
|
||||
</button>
|
||||
|
||||
<button
|
||||
:class="['tab', activeTab === 'redzone' ? 'active' : '']"
|
||||
class='redzone'
|
||||
role="tab"
|
||||
:aria-selected="activeTab === 'redzone'"
|
||||
aria-controls="tab-redzone"
|
||||
id="tab-button-redzone"
|
||||
@click="activeTab = 'redzone'; authStore.user.sudo_token = null"
|
||||
>
|
||||
{{ $t('profile.tabRedZone')}}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Contenu des onglets -->
|
||||
<div class="tab-panels">
|
||||
<!-- Onglet Général -->
|
||||
<section
|
||||
v-show="activeTab === 'general'"
|
||||
role="tabpanel"
|
||||
aria-labelledby="tab-button-general"
|
||||
id="tab-general"
|
||||
>
|
||||
<ProfileGeneral />
|
||||
</section>
|
||||
|
||||
<!-- Onglet RED ZONE -->
|
||||
<section
|
||||
v-show="activeTab === 'redzone'"
|
||||
role="tabpanel"
|
||||
aria-labelledby="tab-button-redzone"
|
||||
id="tab-redzone"
|
||||
>
|
||||
<ProfileRedZone />
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const authStore = useAuthStore()
|
||||
// Onglet actif (TypeScript infère automatiquement le type string)
|
||||
const activeTab = ref('general')
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
/* Onglets */
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.tab {
|
||||
padding: 0.5rem 1rem;
|
||||
background: none;
|
||||
border: none;
|
||||
border-bottom: 3px solid transparent;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
font-size: 1rem;
|
||||
}
|
||||
//
|
||||
.tab:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.tab.active {
|
||||
border-bottom-color: blueviolet;
|
||||
color: blueviolet;
|
||||
}
|
||||
button.redzone{
|
||||
color:red;
|
||||
&.active{
|
||||
border-bottom-color: red;
|
||||
color:red;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Contenu onglet */
|
||||
.tab-panels section {
|
||||
animation: fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
/* Animation simple */
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(5px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
</style>
|
||||
160
app/pages/signup.vue
Executable file
160
app/pages/signup.vue
Executable file
@@ -0,0 +1,160 @@
|
||||
<template>
|
||||
<section>
|
||||
<UiModale @close="closeModal" :modalActive="modalActive">
|
||||
<div class="modal-content">
|
||||
<h1 class="modal-title">{{ $t('modalTitle') }}</h1>
|
||||
<p>{{ $t('modalText') }}</p>
|
||||
</div>
|
||||
</UiModale>
|
||||
<h1>{{ $t('signupTitle') }}</h1>
|
||||
|
||||
<form @submit.prevent="handleSubmit">
|
||||
<InputEmail
|
||||
v-model="email"
|
||||
name="email"
|
||||
:label="$t('signupLabelEmail')"
|
||||
placeholder=""
|
||||
:class="emailStatus"
|
||||
@blur="touched = true"
|
||||
>
|
||||
<template #message>
|
||||
<p v-if="emailErrors.isEmpty" class="error">{{ $t('signupErrorEmailEmpty') }}</p>
|
||||
<p v-else-if="emailErrors.isntValid" class="error">{{ $t('signupErrorEmailIsntValid') }}</p>
|
||||
<p v-else-if="emailValid" class="success">{{ $t('signupEmailIsValid') }}</p>
|
||||
</template>
|
||||
</InputEmail>
|
||||
|
||||
<InputPassword
|
||||
name="password"
|
||||
placeholder=""
|
||||
:label="$t('signupLabelPwd')"
|
||||
v-model="password"
|
||||
@input="check()"
|
||||
>
|
||||
<template #message>
|
||||
<p v-if="pwErrors.isEmpty" class="error">{{ $t('signupErrorPwdEmpty') }}</p>
|
||||
<p v-if="pwErrors.isntValid" class="error">{{ $t('signupErrorPwsIsntValid') }}</p>
|
||||
</template>
|
||||
</InputPassword>
|
||||
<PasswordChecker/>
|
||||
<InputPassword
|
||||
name="passwordVerif"
|
||||
placeholder=""
|
||||
:label="$t('signupLabelConfirmPwd')"
|
||||
v-model="passwordVerif"
|
||||
>
|
||||
<template #message>
|
||||
<p v-if="pwErrors.doesNotMatch" class="error">{{ $t('signupErrorPwdsDoesNotMatch') }}</p>
|
||||
<p v-if="pwErrors.verifyEmpty" class="error">{{ $t('signupErrorPwdConfirmEmpty') }}</p>
|
||||
</template>
|
||||
</InputPassword>
|
||||
|
||||
<ButtonBase>
|
||||
{{ $t('signupFormBtn') }}
|
||||
</ButtonBase>
|
||||
</form>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
public: true
|
||||
})
|
||||
const { locale } = useI18n()
|
||||
//** Modale management **//
|
||||
const localePath=useLocalePath()
|
||||
const modalActive = ref(false)
|
||||
|
||||
const closeModal = () => {
|
||||
modalActive.value = false
|
||||
navigateTo(localePath('lists'))
|
||||
}
|
||||
|
||||
//** Sign up management **/
|
||||
const authStore = useAuthStore()
|
||||
const passwordToolBox = usePasswordToolBoxStore()
|
||||
// email relative functions
|
||||
const email = ref("")
|
||||
const touched = ref(false)
|
||||
|
||||
function isValidEmail(value: string) {
|
||||
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)
|
||||
}
|
||||
|
||||
const emailValid = computed(() => touched.value && isValidEmail(email.value))
|
||||
const emailErrors = ref({
|
||||
isEmpty: false as boolean,
|
||||
isntValid: false as boolean
|
||||
})
|
||||
//const emailInvalid = computed(() => touched.value && !isValidEmail(email.value))
|
||||
|
||||
const emailStatus = computed(() => {
|
||||
if (!touched.value) return ""
|
||||
return emailValid.value ? "mail-is-valid" : "mail-is-invalid"
|
||||
})
|
||||
|
||||
// password relative functions
|
||||
const check = () => {
|
||||
passwordToolBox.updatePassword(password.value)
|
||||
}
|
||||
|
||||
const password=ref('')
|
||||
const passwordVerif=ref('')
|
||||
|
||||
const pwErrors = ref({
|
||||
isEmpty: false as boolean,
|
||||
verifyEmpty: false as boolean,
|
||||
isntValid: false as boolean,
|
||||
doesNotMatch:false as boolean,
|
||||
})
|
||||
|
||||
// form relative functions
|
||||
const handleSubmit = async() => {
|
||||
//réinitialisation des erreurs
|
||||
emailErrors.value.isEmpty = false
|
||||
emailErrors.value.isntValid = false
|
||||
pwErrors.value.isEmpty = false;
|
||||
pwErrors.value.verifyEmpty = false;
|
||||
pwErrors.value.isntValid = false;
|
||||
pwErrors.value.doesNotMatch = false;
|
||||
// Check errors
|
||||
emailErrors.value.isEmpty = ( email.value == "" );
|
||||
emailErrors.value.isntValid = !isValidEmail(email.value)
|
||||
pwErrors.value.isEmpty = ( password.value == "" );
|
||||
pwErrors.value.verifyEmpty = ( passwordVerif.value == "" );
|
||||
pwErrors.value.isntValid = !passwordToolBox.isPasswordValid();
|
||||
pwErrors.value.doesNotMatch = ( password.value != passwordVerif.value );
|
||||
|
||||
if ( !emailErrors.value.isEmpty &&
|
||||
!emailErrors.value.isntValid &&
|
||||
!pwErrors.value.isEmpty &&
|
||||
!pwErrors.value.verifyEmpty &&
|
||||
!pwErrors.value.isntValid &&
|
||||
!pwErrors.value.doesNotMatch) {
|
||||
const success = await authStore.register(email.value, password.value, locale.value)
|
||||
if (success) {
|
||||
modalActive.value=true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.connection-error{
|
||||
height:1.2em;
|
||||
& > p{
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
color: rgb(185, 0, 0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user