Files
notely/frontend/src/pages/Login.vue
T
2026-03-29 15:28:44 +01:00

149 lines
4.6 KiB
Vue

<template>
<div class="login-page">
<div class="auth-container">
<div class="login-card">
<div class="brand-block">
<div class="brand-mark">
<i class="mdi mdi-note-text-outline" aria-hidden="true"></i>
</div>
<h1 class="brand-title">Notely</h1>
</div>
<h2 class="auth-title">Login</h2>
<form @submit.prevent="handleLogin">
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input id="email" v-model="form.email" type="email" class="form-control" required />
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input id="password" v-model="form.password" type="password" class="form-control" required />
</div>
<div v-if="error" class="alert alert-danger">{{ error }}</div>
<button type="submit" class="btn btn-primary w-100 auth-submit">Login</button>
</form>
<div v-if="providerLoginEnabled && providers.length" class="mt-4">
<div class="oauth-divider"><span>or continue with</span></div>
<div class="d-grid gap-2 mt-3">
<button v-for="provider in providers" :key="provider.id" type="button" class="btn btn-outline-dark auth-provider-btn" @click="startProviderLogin(provider.id)">
Sign in with {{ provider.name }}
</button>
</div>
</div>
<p v-if="registrationEnabled" class="text-center mt-4 mb-0 auth-switch-link">
Don't have an account?
<router-link to="/register">Register here</router-link>
</p>
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, ref } from "vue";
import { useRouter } from "vue-router";
import { useAuthStore } from "../stores/authStore";
import { useSettingsStore } from "../stores/settingsStore";
import apiClient from "../services/apiClient";
const router = useRouter();
const authStore = useAuthStore();
const settingsStore = useSettingsStore();
const form = ref({
email: "",
password: "",
});
const error = ref("");
const providers = ref([]);
const registrationEnabled = ref(true);
const providerLoginEnabled = ref(true);
const handleLogin = async () => {
error.value = "";
try {
await authStore.login(form.value.email, form.value.password);
router.push("/");
} catch (err) {
error.value = err;
}
};
const loadProviders = async () => {
try {
const response = await apiClient.get("/api/v1/auth/providers");
providers.value = response.data.providers || [];
} catch {
providers.value = [];
}
};
const startProviderLogin = (providerId) => {
window.location.href = `${apiClient.defaults.baseURL}/api/v1/auth/providers/${providerId}/start`;
};
const completeOAuthRedirect = async () => {
const params = new URLSearchParams(window.location.search);
const status = params.get("status");
if (status === "oauth_error") {
error.value = params.get("message") || "Provider sign-in failed.";
return true;
}
if (status !== "oauth_success") {
return false;
}
try {
await authStore.ensureInitialized();
} catch {
error.value = "Unable to restore provider session.";
return true;
}
if (authStore.isAuthenticated) {
await router.replace("/");
return true;
}
error.value = "Provider sign-in returned an incomplete session.";
return true;
};
onMounted(async () => {
const flags = await settingsStore.loadFeatureFlags();
registrationEnabled.value = !!flags.registration_enabled;
providerLoginEnabled.value = !!flags.provider_login_enabled;
await authStore.ensureInitialized();
if (authStore.isAuthenticated) {
await router.replace("/");
return;
}
const handledOAuthCallback = await completeOAuthRedirect();
if (!handledOAuthCallback && providerLoginEnabled.value) {
await loadProviders();
}
const queryMessage = typeof router.currentRoute.value.query.message === "string" ? router.currentRoute.value.query.message : "";
if (!error.value && queryMessage) {
error.value = queryMessage;
}
});
</script>
<style scoped src="../assets/styles/scoped/pages/Login.css"></style>