integrate login page wih api and keycloak
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
<template>
|
||||
<div class="w-100 login-form-wrap">
|
||||
<div class="d-flex align-center mb-10">
|
||||
<img :src="Logoimg" alt="home" width="200" />
|
||||
</div>
|
||||
|
||||
<h1 class="text-h3 font-weight-bold text-grey-darken-4 mb-2">Selamat Datang</h1>
|
||||
<p class="text-body-1 text-grey mb-7">
|
||||
Silakan masuk dengan akun yang telah terdaftar
|
||||
</p>
|
||||
<!-- Error Message -->
|
||||
<v-alert v-if="errorMessage" type="error" closable class="mb-4" @click:close="errorMessage = ''">
|
||||
{{ errorMessage }}
|
||||
</v-alert>
|
||||
|
||||
<!-- Success Message -->
|
||||
<v-alert v-if="successMessage" type="success" class="mb-4">
|
||||
{{ successMessage }}
|
||||
</v-alert>
|
||||
|
||||
<v-btn :loading="isLoading" :disabled="isLoading" block color="primary" size="large" rounded="pill" class="text-none mb-7 btn-login" @click="loginWithKeycloak">
|
||||
<v-icon size="16" class="mr-2">mdi-key-variant</v-icon>
|
||||
Masuk dengan Keycloak
|
||||
</v-btn>
|
||||
|
||||
<div class="d-flex align-center mb-7">
|
||||
<v-divider />
|
||||
<div class="mx-3 text-caption text-medium-emphasis width-100">Atau</div>
|
||||
<v-divider />
|
||||
</div>
|
||||
|
||||
<v-form v-model="valid" @submit.prevent="login">
|
||||
<label class="text-subtitle-2 text-grey-darken-1 mb-2 d-inline-block">Email </label>
|
||||
<v-text-field
|
||||
v-model="email"
|
||||
placeholder="Masukkan email"
|
||||
density="comfortable"
|
||||
variant="outlined"
|
||||
rounded="pill"
|
||||
class="mb-5"
|
||||
:rules="emailRules"
|
||||
/>
|
||||
|
||||
<div class="d-flex align-center justify-space-between mb-2">
|
||||
<label class="text-subtitle-2 text-grey-darken-1">Password</label>
|
||||
</div>
|
||||
<v-text-field
|
||||
v-model="password"
|
||||
:type="showPassword ? 'text' : 'password'"
|
||||
placeholder="Masukkan password"
|
||||
density="comfortable"
|
||||
variant="outlined"
|
||||
rounded="pill"
|
||||
class="mb-6"
|
||||
:rules="passwordRules"
|
||||
:append-inner-icon="showPassword ? 'mdi-eye' : 'mdi-eye-off'"
|
||||
@click:append-inner="showPassword = !showPassword"
|
||||
/>
|
||||
<v-btn
|
||||
block
|
||||
type="submit"
|
||||
color="primary"
|
||||
size="large"
|
||||
rounded="pill"
|
||||
variant="tonal"
|
||||
class="text-none mb-8 btn-login"
|
||||
>
|
||||
Masuk
|
||||
</v-btn>
|
||||
</v-form>
|
||||
|
||||
<p class="text-center text-body-2 text-medium-emphasis mb-0">
|
||||
© 2026 RSUD Dr. Saiful Anwar Provinsi Jawa Timur.
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import Logoimg from "/images/logos/logo-farmasi.svg";
|
||||
import { useValidation } from '~/composables/useValidation';
|
||||
import api from '~/utils/api';
|
||||
|
||||
import { useSnackbarStore } from '~/store/snackbar';
|
||||
|
||||
interface LoginResponse {
|
||||
data : {
|
||||
provider: string;
|
||||
access_token: string;
|
||||
refresh_token: string;
|
||||
expires_in: number;
|
||||
token_type: string;
|
||||
}
|
||||
}
|
||||
|
||||
const snackbarStore = useSnackbarStore();
|
||||
const { required } = useValidation();
|
||||
|
||||
const email = ref("");
|
||||
const password = ref("");
|
||||
const valid = ref(false);
|
||||
const showPassword = ref(false);
|
||||
const isLoading = ref(false);
|
||||
const errorMessage = ref("");
|
||||
const successMessage = ref("");
|
||||
|
||||
const emailRules = [
|
||||
required,
|
||||
(v : string) => /.+@.+\..+/.test(v) || 'Email tidak valid',
|
||||
];
|
||||
const passwordRules = [required];
|
||||
|
||||
const login = async () => {
|
||||
if (!valid.value) {
|
||||
snackbarStore.showSnackbar('Password dan email harus diisi', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
isLoading.value = true;
|
||||
|
||||
try {
|
||||
const response = await api.post<LoginResponse>('api/v1/auth/login', {
|
||||
email: email.value,
|
||||
password: password.value
|
||||
});
|
||||
|
||||
const loginData = response.data.data;
|
||||
console.log('Login response:', loginData);
|
||||
|
||||
if (!loginData?.access_token || !loginData?.refresh_token) {
|
||||
throw new Error('Token login tidak valid');
|
||||
}
|
||||
|
||||
await $fetch('/api/auth/sessionUserStore', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
accessToken: loginData.access_token,
|
||||
refreshToken: loginData.refresh_token,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
window.location.href = "/apps/dashboard";
|
||||
} catch (error: any) {
|
||||
const loginError = error?.response?.data?.message || error?.message || 'Terjadi kesalahan saat login';
|
||||
snackbarStore.showSnackbar(loginError, 'error');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const loginWithKeycloak = async (): Promise<void> => {
|
||||
isLoading.value = true;
|
||||
errorMessage.value = '';
|
||||
successMessage.value = '';
|
||||
|
||||
try {
|
||||
|
||||
// Call API route to initiate Keycloak login process
|
||||
const response = await $fetch<any>('/api/auth/keycloak-login', {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
if (response?.success && response?.data?.authUrl) {
|
||||
successMessage.value = 'Redirecting to Keycloak...';
|
||||
|
||||
// Redirect the user to the Keycloak authorization URL
|
||||
setTimeout(() => {
|
||||
window.location.href = response.data!.authUrl;
|
||||
}, 500);
|
||||
} else {
|
||||
throw new Error('Failed to get authorization URL');
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('Login error:', error);
|
||||
// Display the error message
|
||||
errorMessage.value = `Login failed: ${error.message || 'Please try again.'}`;
|
||||
} finally {
|
||||
// Only set isLoading back to false if no redirect is happening
|
||||
if (!successMessage.value.includes('Redirecting')) {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user