feat: Initial auth components
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
<script setup lang="ts">
|
||||
const { login } = useAuthConnexion();
|
||||
|
||||
const fields = [
|
||||
{
|
||||
name: "username",
|
||||
type: "text" as const,
|
||||
label: "Courriel",
|
||||
placeholder: "Entrez votre courriel",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "password",
|
||||
label: "Mot de passe",
|
||||
type: "password" as const,
|
||||
placeholder: "Entrez votre mot de passe",
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UAuthForm
|
||||
:schema="authLoginFormSchema"
|
||||
:fields="fields"
|
||||
title="Connexion"
|
||||
description="Veuillez vous identifier."
|
||||
loading-auto
|
||||
@submit="login"
|
||||
/>
|
||||
</template>
|
||||
@@ -0,0 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
const { logout } = useAuthConnexion();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full space-y-6">
|
||||
<div class="flex flex-col text-center">
|
||||
<div class="text-xl font-semibold text-pretty text-highlighted">Déconnexion</div>
|
||||
<div class="mt-1 text-base text-pretty text-muted">Veuillez confirmer la déconnexion.</div>
|
||||
</div>
|
||||
<UButton
|
||||
icon="i-lucide-log-out"
|
||||
block
|
||||
loading-auto
|
||||
to="#"
|
||||
label="Déconnexion"
|
||||
@click="logout()"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<div class="w-full space-y-6">
|
||||
<div class="flex flex-col text-center">
|
||||
<div class="text-xl font-semibold text-pretty text-highlighted">Redirection en cours</div>
|
||||
<div class="mt-1 text-base text-pretty text-muted">Veuillez patienter...</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
7
wp-content/themes/headless/app/composables/useAuth.ts
Normal file
7
wp-content/themes/headless/app/composables/useAuth.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export function useAuth() {
|
||||
const { loggedIn: isLoggedIn, session } = useUserSession();
|
||||
const hasRole = (role: string) => session.value?.user?.roles?.includes(role) || false;
|
||||
const isAdmin = computed(() => hasRole("administrator"));
|
||||
|
||||
return { isLoggedIn, hasRole, isAdmin };
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import { delay } from "es-toolkit/promise";
|
||||
|
||||
import type { FormSubmitEvent } from "@nuxt/ui";
|
||||
|
||||
export function useAuthConnexion() {
|
||||
const toast = useToast();
|
||||
|
||||
const defaultRedirect = (useRoute().query.redirect as string) || undefined;
|
||||
const isRedirecting = useState("auth.isRedirecting", () => false);
|
||||
const { fetch: refreshUserSession } = useUserSession();
|
||||
|
||||
// Helper: Redirect after login / logout
|
||||
async function redirectTo(to: string | undefined) {
|
||||
isRedirecting.value = true;
|
||||
await delay(1000);
|
||||
await refreshUserSession();
|
||||
await navigateTo(to || defaultRedirect || "/");
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt login with the provided credentials.
|
||||
*
|
||||
* @param event The form submit event containing the login credentials.
|
||||
* @param redirect Optional URL to redirect to after successful login.
|
||||
* @returns A promise that resolves when the login process is complete.
|
||||
*/
|
||||
async function login({ data: body }: FormSubmitEvent<AuthLoginForm>, redirect?: string) {
|
||||
try {
|
||||
const { success, message } = await $fetch("/api/login", { method: "POST", body });
|
||||
if (!success) throw new Error(message);
|
||||
|
||||
toast.add({
|
||||
color: "success",
|
||||
title: "Connexion réussie",
|
||||
description: message,
|
||||
});
|
||||
|
||||
await redirectTo(redirect);
|
||||
} catch (error) {
|
||||
toast.add({
|
||||
color: "error",
|
||||
title: "Erreur de connexion",
|
||||
description:
|
||||
error instanceof Error ? error.message : "Une erreur est survenue lors de la connexion.",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logout the current user.
|
||||
*
|
||||
* @param redirect Optional URL to redirect to after successful logout.
|
||||
* @returns A promise that resolves when the logout process is complete.
|
||||
*/
|
||||
async function logout(redirect?: string) {
|
||||
try {
|
||||
const { success, message } = await $fetch("/api/logout", { method: "POST" });
|
||||
if (!success) throw new Error(message);
|
||||
|
||||
toast.add({
|
||||
color: "success",
|
||||
title: "Déconnexion réussie",
|
||||
description: message,
|
||||
});
|
||||
|
||||
await redirectTo(redirect);
|
||||
} catch (error) {
|
||||
toast.add({
|
||||
color: "error",
|
||||
title: "Erreur de déconnexion",
|
||||
description:
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Une erreur est survenue lors de la déconnexion.",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { isRedirecting, login, logout };
|
||||
}
|
||||
17
wp-content/themes/headless/app/pages/connexion.vue
Normal file
17
wp-content/themes/headless/app/pages/connexion.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<script setup lang="ts">
|
||||
const { isLoggedIn } = useAuth();
|
||||
const { isRedirecting } = useAuthConnexion();
|
||||
onBeforeMount(() => (isRedirecting.value = false));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="page-connexion" class="container-sm py-12">
|
||||
<AuthState>
|
||||
<AuthRedirecting v-if="isRedirecting" />
|
||||
<template v-else>
|
||||
<AuthLoginForm v-if="!isLoggedIn" />
|
||||
<AuthLogoutForm v-else />
|
||||
</template>
|
||||
</AuthState>
|
||||
</div>
|
||||
</template>
|
||||
@@ -48,7 +48,7 @@ export default defineNuxtConfig({
|
||||
|
||||
vite: {
|
||||
optimizeDeps: {
|
||||
include: ["@vue/devtools-core", "@vue/devtools-kit", "zod"],
|
||||
include: ["@vue/devtools-core", "@vue/devtools-kit", "zod", "es-toolkit/promise"],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
"@lewebsimple/nuxt-graphql": "^0.7.5",
|
||||
"@nuxt/ui": "^4.6.0",
|
||||
"@nuxtjs/seo": "^4.0.2",
|
||||
"es-toolkit": "^1.45.1",
|
||||
"nuxt": "^4.4.2",
|
||||
"nuxt-auth-utils": "^0.5.29",
|
||||
"nuxt-svgo": "^4.2.6",
|
||||
|
||||
3
wp-content/themes/headless/pnpm-lock.yaml
generated
3
wp-content/themes/headless/pnpm-lock.yaml
generated
@@ -23,6 +23,9 @@ importers:
|
||||
'@nuxtjs/seo':
|
||||
specifier: ^4.0.2
|
||||
version: 4.0.2(489f84e1ce5b91b262b98d380824761d)
|
||||
es-toolkit:
|
||||
specifier: ^1.45.1
|
||||
version: 1.45.1
|
||||
nuxt:
|
||||
specifier: ^4.4.2
|
||||
version: 4.4.2(@babel/core@7.29.0)(@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0))(@parcel/watcher@2.5.6)(@types/node@25.5.0)(@vue/compiler-sfc@3.5.31)(cac@6.7.14)(db0@0.3.4)(ioredis@5.10.1)(lightningcss@1.32.0)(magicast@0.5.2)(oxlint@1.57.0)(rollup-plugin-visualizer@7.0.1(rollup@4.60.0))(rollup@4.60.0)(terser@5.46.1)(typescript@5.9.3)(vite@7.3.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(yaml@2.8.3)
|
||||
|
||||
@@ -4,13 +4,12 @@ export default defineEventHandler(async (event) => {
|
||||
const variables = authLoginFormSchema.parse(await readBody(event));
|
||||
|
||||
// Execute the GraphQL operation to authenticate the user
|
||||
const { data, error } = await executeSchemaOperation(event, {
|
||||
const { data } = await executeSchemaOperation(event, {
|
||||
operationName: "AuthLogin",
|
||||
variables,
|
||||
});
|
||||
|
||||
// Handle errors and validate the response data
|
||||
if (error) throw error;
|
||||
// Validate the response data
|
||||
if (!data?.login) throw new Error("Identifiants invalides. Veuillez réessayer.");
|
||||
|
||||
// Handle the login process by setting the session data
|
||||
@@ -18,7 +17,7 @@ export default defineEventHandler(async (event) => {
|
||||
throw new Error("Une erreur est survenue lors de la connexion.");
|
||||
}
|
||||
|
||||
return { success: true, message: "Connexion réussie" };
|
||||
return { success: true, message: "Vous avez été connecté avec succès." };
|
||||
} catch (error) {
|
||||
const message =
|
||||
error instanceof Error ? error.message : "Une erreur est survenue lors de la connexion.";
|
||||
|
||||
@@ -4,7 +4,7 @@ export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
await handleLogout(event);
|
||||
|
||||
return { success: true, message: "Déconnexion réussie" };
|
||||
return { success: true, message: "Vous avez été déconnecté avec succès." };
|
||||
} catch (error) {
|
||||
const message =
|
||||
error instanceof Error ? error.message : "Une erreur est survenue lors de la déconnexion.";
|
||||
|
||||
Reference in New Issue
Block a user