315 lines
8.7 KiB
Vue
Executable File
315 lines
8.7 KiB
Vue
Executable File
<!-- components/auth/LoginForm.vue -->
|
|
<script setup lang="ts">
|
|
import { ref, computed, onMounted, onUnmounted } from "vue";
|
|
import { useUserInfo } from "~/composables/useUserInfo";
|
|
import { useRouter, useRoute } from "vue-router";
|
|
|
|
const router = useRouter();
|
|
const route = useRoute();
|
|
const userInfo = useUserInfo();
|
|
|
|
// State
|
|
const isLoggingIn = ref(false);
|
|
const isLoggingOut = ref(false);
|
|
const errorMessage = ref("");
|
|
const showDebugInfo = ref(false);
|
|
|
|
// **PERBAIKAN: Enhanced Query Parameter Handling**
|
|
const reason = computed(() => route.query.reason as string);console.log("reason:", reason.value);
|
|
const shouldShowContinue = computed(() => route.query.continue === "true");
|
|
const returnUrl = computed(() => (route.query.returnUrl as string) || "/");console.log("returnUrl:", returnUrl.value);
|
|
|
|
const getReasonMessage = () => {
|
|
// console.log("LoginForm: current reason value:", reason.value);
|
|
switch (reason.value) {
|
|
case "idle":
|
|
return {
|
|
type: "warning" as const,
|
|
title: "Sesi Tidak Aktif",
|
|
message:
|
|
"Anda telah tidak aktif selama 15 menit. Sesi Keycloak masih valid.",
|
|
icon: "mdi-clock-alert-outline"
|
|
};
|
|
case "tab_inactive":
|
|
return {
|
|
type: "info" as const,
|
|
title: "Tab Kembali Aktif",
|
|
message:
|
|
"Tab telah tidak aktif lebih dari 10 menit. Sesi Keycloak masih valid.",
|
|
icon: "mdi-tab"
|
|
};
|
|
case "session_expired":
|
|
return {
|
|
type: "error" as const,
|
|
title: "Sesi Berakhir",
|
|
message: "Sesi Anda telah berakhir. Silakan login kembali.",
|
|
icon: "mdi-clock-alert"
|
|
};
|
|
case "session_error":
|
|
return {
|
|
type: "error" as const,
|
|
title: "Error Sesi",
|
|
message: "Terjadi error pada sesi Anda. Silakan login kembali.",
|
|
icon: "mdi-alert"
|
|
};
|
|
case "auth_required":
|
|
return {
|
|
type: "error" as const,
|
|
title: "Autentikasi Diperlukan",
|
|
message: "Anda harus login untuk mengakses halaman ini.",
|
|
icon: "mdi-lock-alert"
|
|
};
|
|
default:
|
|
return null;
|
|
}
|
|
};
|
|
|
|
// **PERBAIKAN: Enhanced Keycloak Login**
|
|
const signInKeycloak = async () => {
|
|
try {
|
|
isLoggingIn.value = true;
|
|
errorMessage.value = "";
|
|
|
|
await userInfo.login("keycloak");
|
|
|
|
} catch (error: any) {
|
|
errorMessage.value = error.message || "Login gagal. Silakan coba lagi.";
|
|
} finally {
|
|
isLoggingIn.value = false;
|
|
}
|
|
};
|
|
|
|
// **PERBAIKAN: Enhanced Continue Handler**
|
|
const handleContinue = async () => {
|
|
try {
|
|
// Update activity untuk reset idle timer
|
|
userInfo.updateActivity();
|
|
|
|
// Force refresh session to ensure session is valid
|
|
await userInfo.forceRefreshSession();
|
|
|
|
// Navigate ke return URL atau dashboard
|
|
await router.push(returnUrl.value);
|
|
} catch (error) {
|
|
console.error("Navigation error:", error);
|
|
errorMessage.value = "Gagal melanjutkan sesi. Silakan coba lagi.";
|
|
}
|
|
};
|
|
|
|
// **PERBAIKAN: Enhanced Logout Handler**
|
|
const handleSignOut = async () => {
|
|
try {
|
|
isLoggingOut.value = true;
|
|
await userInfo.fullLogout();
|
|
} catch (error) {
|
|
//console.error("Logout error:", error);
|
|
errorMessage.value = "Logout gagal. Silakan coba lagi.";
|
|
} finally {
|
|
isLoggingOut.value = false;
|
|
}
|
|
};
|
|
|
|
// **PERBAIKAN: Ubah tipe variabel untuk menerima null**
|
|
let cleanupSessionMonitoring: (() => void) | null = null;
|
|
|
|
// **PERBAIKAN: Enhanced onMounted dengan proper type handling**
|
|
onMounted(() => {
|
|
if (userInfo.isAuthenticated.value) {
|
|
const cleanup = userInfo.startSessionMonitoring();
|
|
if (cleanup) {
|
|
cleanupSessionMonitoring = cleanup;
|
|
}
|
|
}
|
|
});
|
|
|
|
// **PERBAIKAN: Enhanced onUnmounted dengan null check**
|
|
onUnmounted(() => {
|
|
if (cleanupSessionMonitoring) {
|
|
cleanupSessionMonitoring();
|
|
cleanupSessionMonitoring = null;
|
|
}
|
|
});
|
|
|
|
// User display functions
|
|
const getUserDisplayName = () => {
|
|
if (!userInfo.user.value) return "";
|
|
|
|
const user = userInfo.user.value as any;
|
|
return (
|
|
user.name ||
|
|
user.given_name ||
|
|
`${user.given_name || ""} ${user.family_name || ""}`.trim() ||
|
|
user.preferred_username ||
|
|
user.username ||
|
|
user.email?.split("@")[0] ||
|
|
"User"
|
|
);
|
|
};
|
|
console.log("user is authenticated:", userInfo.isAuthenticated.value, shouldShowContinue.value);
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<v-card-title class="text-h4 text-center mb-6">
|
|
<v-icon
|
|
icon="mdi-shield-account"
|
|
size="48"
|
|
color="primary"
|
|
class="mb-2"
|
|
></v-icon>
|
|
<br />
|
|
Masuk ke Sistem
|
|
</v-card-title>
|
|
|
|
<!-- **PERBAIKAN: Loading State** -->
|
|
<div v-if="userInfo.isLoading.value" class="text-center py-8">
|
|
<v-progress-circular
|
|
indeterminate
|
|
color="primary"
|
|
size="64"
|
|
></v-progress-circular>
|
|
<p class="text-body-1 mt-4">Memeriksa autentikasi...</p>
|
|
</div>
|
|
|
|
<!-- **PERBAIKAN: Continue Panel untuk User yang Sudah Authenticated** -->
|
|
<div
|
|
v-else-if="!shouldShowContinue || !reason || reason === 'auth_required'"
|
|
class="text-center"
|
|
>
|
|
<v-card-text>
|
|
<!-- **PERBAIKAN: Error Alert** -->
|
|
<v-alert
|
|
v-if="errorMessage"
|
|
type="error"
|
|
variant="tonal"
|
|
class="mb-6"
|
|
closable
|
|
@click:close="errorMessage = ''"
|
|
>
|
|
{{ errorMessage }}
|
|
</v-alert>
|
|
|
|
<!-- **PERBAIKAN: Reason Alert** -->
|
|
<v-alert
|
|
v-if="getReasonMessage()"
|
|
:type="getReasonMessage()!.type"
|
|
variant="tonal"
|
|
class="mb-6"
|
|
:icon="getReasonMessage()!.icon"
|
|
>
|
|
<div class="text-h6 mb-2">{{ getReasonMessage()!.title }}</div>
|
|
<div class="text-body-2">{{ getReasonMessage()!.message }}</div>
|
|
</v-alert>
|
|
|
|
<!-- Welcome Message -->
|
|
<div class="text-center mb-6">
|
|
<h5 class="text-h5 mb-2">Selamat Datang di Sistem RSSA</h5>
|
|
<p class="text-body-2 text-medium-emphasis">
|
|
Silakan masuk untuk melanjutkan ke dashboard Anda
|
|
</p>
|
|
</div>
|
|
|
|
<!-- **PERBAIKAN: Keycloak Login Button** -->
|
|
<v-btn
|
|
color="primary"
|
|
size="large"
|
|
variant="flat"
|
|
block
|
|
class="mb-4"
|
|
@click="signInKeycloak"
|
|
:loading="isLoggingIn"
|
|
:disabled="isLoggingIn"
|
|
prepend-icon="mdi-key"
|
|
>
|
|
<span>{{
|
|
isLoggingIn ? "Menghubungkan..." : "Masuk dengan Keycloak"
|
|
}}</span>
|
|
</v-btn>
|
|
|
|
<!-- Additional Info -->
|
|
<div class="text-center mt-6">
|
|
<p class="text-body-2 text-medium-emphasis">
|
|
Belum memiliki akun?
|
|
<span class="text-primary font-weight-medium">
|
|
Hubungi Administrator
|
|
</span>
|
|
</p>
|
|
</div>
|
|
</v-card-text>
|
|
</div>
|
|
|
|
<!-- **PERBAIKAN: Login Form untuk User yang Belum Authenticated** -->
|
|
<div v-else>
|
|
<v-card-text class="pb-4">
|
|
<!-- **PERBAIKAN: Reason-based Alert** -->
|
|
<v-alert
|
|
v-if="getReasonMessage()"
|
|
:type="getReasonMessage()!.type"
|
|
variant="tonal"
|
|
class="mb-6 text-left"
|
|
:icon="getReasonMessage()!.icon"
|
|
>
|
|
<div class="text-h6 mb-2">{{ getReasonMessage()!.title }}</div>
|
|
<div class="text-body-2">{{ getReasonMessage()!.message }}</div>
|
|
</v-alert>
|
|
|
|
<!-- Welcome Message -->
|
|
<v-alert
|
|
v-else
|
|
type="success"
|
|
variant="tonal"
|
|
class="mb-6 text-left"
|
|
icon="mdi-check-circle"
|
|
>
|
|
<div class="text-h6 mb-2">
|
|
Selamat datang kembali, <strong>{{ getUserDisplayName() }}</strong
|
|
>!
|
|
</div>
|
|
<div class="text-body-2">Anda masih terhubung dengan Keycloak.</div>
|
|
</v-alert>
|
|
|
|
<!-- Session Info -->
|
|
<v-card variant="outlined" class="mb-4">
|
|
<v-card-title class="text-h6 d-flex align-center">
|
|
<v-icon icon="mdi-account-circle" class="mr-2"></v-icon>
|
|
Informasi Sesi
|
|
</v-card-title>
|
|
<v-card-text>
|
|
<div class="d-flex justify-center">
|
|
<v-chip color="success" variant="outlined" size="small">
|
|
<v-icon start icon="mdi-clock-check"></v-icon>
|
|
Sesi Keycloak Aktif
|
|
</v-chip>
|
|
</div>
|
|
</v-card-text>
|
|
</v-card>
|
|
</v-card-text>
|
|
|
|
<!-- **PERBAIKAN: Action Buttons** -->
|
|
<v-card-actions class="justify-center px-6 pb-6">
|
|
<v-btn
|
|
color="primary"
|
|
size="large"
|
|
variant="flat"
|
|
class="mr-3 px-8"
|
|
@click="handleContinue"
|
|
prepend-icon="mdi-arrow-right"
|
|
>
|
|
Lanjutkan ke Aplikasi
|
|
</v-btn>
|
|
|
|
<v-btn
|
|
color="error"
|
|
size="large"
|
|
variant="outlined"
|
|
@click="handleSignOut"
|
|
prepend-icon="mdi-logout"
|
|
:loading="isLoggingOut"
|
|
:disabled="isLoggingOut"
|
|
>
|
|
{{ isLoggingOut ? "Keluar..." : "Keluar dari Keycloak" }}
|
|
</v-btn>
|
|
</v-card-actions>
|
|
</div>
|
|
</template>
|