Fix QC Kak Ayu 15 Des

Fix QC Kak Inno 15 Des
Fix UI User Font Size, Font Weight, Line Height
Fix UI Admin Font Size, Font Weight, Line Height & UI Mobile
This commit is contained in:
2025-12-16 16:37:17 +08:00
parent 342e9bbc65
commit c8484357cb
34 changed files with 1458 additions and 661 deletions

View File

@@ -10,10 +10,19 @@ import {
Stack,
Text,
Title,
Progress,
Group,
} from '@mantine/core';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { authStore } from '@/store/authStore'; // ✅ integrasi authStore
import { authStore } from '@/store/authStore';
// ⚙️ Configuration
const CONFIG = {
POLL_INTERVAL: 3000, // 3 detik
MAX_RETRIES: 2, // 2x retry
TIMEOUT_DURATION: 5 * 60 * 1000, // 5 menit (300 detik)
};
async function fetchUser() {
const res = await fetch('/api/auth/me', {
@@ -26,21 +35,48 @@ async function fetchUser() {
return res.json();
}
function formatTime(seconds: number): string {
const mins = Math.floor(seconds / 60);
const secs = seconds % 60;
return `${mins}:${secs.toString().padStart(2, '0')}`;
}
export default function WaitingRoom() {
const router = useRouter();
const [user, setUser] = useState<any>(null);
const [error, setError] = useState<string | null>(null);
const [isRedirecting, setIsRedirecting] = useState(false);
const [retryCount, setRetryCount] = useState(0);
const MAX_RETRIES = 2;
// ⏱️ Countdown timer
const [timeLeft, setTimeLeft] = useState(CONFIG.TIMEOUT_DURATION / 1000); // dalam detik
const [hasTimedOut, setHasTimedOut] = useState(false);
// ⏱️ Countdown effect
useEffect(() => {
if (isRedirecting || hasTimedOut) return;
const countdownInterval = setInterval(() => {
setTimeLeft((prev) => {
if (prev <= 1) {
setHasTimedOut(true);
setError('Waktu tunggu habis. Silakan hubungi administrator atau coba login ulang nanti.');
return 0;
}
return prev - 1;
});
}, 1000);
return () => clearInterval(countdownInterval);
}, [isRedirecting, hasTimedOut]);
// 🔄 Polling effect
useEffect(() => {
let isMounted = true;
let interval: ReturnType<typeof setInterval>;
const poll = async () => {
if (isRedirecting || !isMounted) return;
if (isRedirecting || !isMounted || hasTimedOut) return;
try {
const data = await fetchUser();
@@ -59,12 +95,11 @@ export default function WaitingRoom() {
});
}
// In the poll function
// ✅ Check if approved
if (currentUser?.isActive === true) {
setIsRedirecting(true);
clearInterval(interval);
// Update authStore with the current user data
authStore.setUser({
id: currentUser.id,
name: currentUser.name || 'User',
@@ -78,7 +113,7 @@ export default function WaitingRoom() {
localStorage.removeItem('auth_nomor');
localStorage.removeItem('auth_username');
// Force a session refresh
// Force session refresh
try {
const res = await fetch('/api/auth/refresh-session', {
method: 'POST',
@@ -99,26 +134,26 @@ export default function WaitingRoom() {
redirectPath = '/admin/pendidikan/info-sekolah/jenjang-pendidikan';
break;
}
window.location.href = redirectPath; // Use window.location to force full page reload
window.location.href = redirectPath;
}
} catch (error) {
console.error('Error refreshing session:', error);
router.refresh(); // Fallback to client-side refresh
router.refresh();
}
}
} catch (err: any) {
if (!isMounted) return;
if (err.message.includes('401')) {
if (retryCount < MAX_RETRIES) {
if (retryCount < CONFIG.MAX_RETRIES) {
setRetryCount((prev) => prev + 1);
setTimeout(() => {
if (isMounted) interval = setInterval(poll, 3000);
if (isMounted) interval = setInterval(poll, CONFIG.POLL_INTERVAL);
}, 800);
} else {
setError('Sesi tidak valid. Silakan login ulang.');
clearInterval(interval);
authStore.setUser(null); // ✅ clear sesi
authStore.setUser(null);
}
} else {
console.error('Error polling:', err);
@@ -126,26 +161,53 @@ export default function WaitingRoom() {
}
};
interval = setInterval(poll, 3000);
interval = setInterval(poll, CONFIG.POLL_INTERVAL);
return () => {
isMounted = false;
if (interval) clearInterval(interval);
};
}, [router, isRedirecting, retryCount]);
}, [router, isRedirecting, retryCount, hasTimedOut]);
// ✅ UI Error
if (error) {
// 🚨 Handle logout
const handleLogout = async () => {
try {
await fetch('/api/auth/logout', {
method: 'POST',
credentials: 'include'
});
} catch (err) {
console.error('Logout error:', err);
} finally {
authStore.setUser(null);
localStorage.clear();
router.push('/login');
}
};
// ❌ UI Error / Timeout
if (error || hasTimedOut) {
return (
<Center h="100vh">
<Paper p="xl" radius="md" bg={colors['white-trans-1']} w={400}>
<Center h="100vh" bg={colors.Bg}>
<Paper p="xl" radius="md" bg={colors['white-trans-1']} w={{ base: '90%', sm: 400 }}>
<Stack align="center" gap="md">
<Title order={3} c="red">
Sesi Tidak Valid
<Title order={3} c="red" ta="center">
{hasTimedOut ? '⏱️ Waktu Habis' : '❌ Sesi Tidak Valid'}
</Title>
<Text>{error}</Text>
<Button onClick={() => router.push('/login')}>
Login Ulang
</Button>
<Text ta="center" size="sm">
{error || 'Waktu tunggu persetujuan telah habis.'}
</Text>
<Text ta="center" size="xs" c="dimmed">
Silakan hubungi Superadmin atau coba login ulang nanti.
</Text>
<Group gap="sm" w="100%">
<Button
fullWidth
variant="outline"
onClick={handleLogout}
>
Kembali ke Login
</Button>
</Group>
</Stack>
</Paper>
</Center>
@@ -171,24 +233,56 @@ export default function WaitingRoom() {
);
}
// UI Default (MENUNGGU) — INI YANG KAMU HILANGKAN!
// UI Default (MENUNGGU)
const progressValue = ((CONFIG.TIMEOUT_DURATION / 1000 - timeLeft) / (CONFIG.TIMEOUT_DURATION / 1000)) * 100;
return (
<Center h="100vh" bg={colors.Bg}>
<Paper p="xl" radius="md" bg={colors['white-trans-1']} w={{ base: '90%', sm: 400 }}>
<Stack align="center" gap="lg">
<Title order={2} c={colors['blue-button']} ta="center">
Menunggu Persetujuan
Menunggu Persetujuan
</Title>
<Text ta="center" c="dimmed">
Akun Anda sedang dalam proses verifikasi oleh Superadmin.
</Text>
<Text ta="center" size="sm" c="dimmed">
<Text ta="center" size="sm" fw={500}>
Nomor: {user?.nomor || '...'}
</Text>
{/* ⏱️ Countdown Timer */}
<Stack w="100%" gap="xs">
<Group justify="space-between" w="100%">
<Text size="sm" c="dimmed">Sisa waktu:</Text>
<Text size="sm" fw={600} c={timeLeft < 60 ? 'red' : colors['blue-button']}>
{formatTime(timeLeft)}
</Text>
</Group>
<Progress
value={progressValue}
color={timeLeft < 60 ? 'red' : colors['blue-button']}
size="sm"
animated
/>
</Stack>
<Loader size="sm" color={colors['blue-button']} />
<Text ta="center" size="xs" c="dimmed">
Jangan tutup halaman ini. Anda akan dialihkan otomatis setelah disetujui.
</Text>
{/* 🚪 Tombol Keluar */}
<Button
variant="subtle"
size="xs"
onClick={handleLogout}
c="dimmed"
>
Keluar dari Halaman Ini
</Button>
</Stack>
</Paper>
</Center>