User yang sudah registrasi sudah langsung diarahkan ke layout sesuai dengan roleIdnya
Superadmin sudah bisa menambah atau mengurangkan menu pad user yang diinginkan Next------------------------------- Ada bug saat tampilan menu sudah di edit superamin berhasil namun saat user logout tampilan menunya balik ke sebelumnya
This commit is contained in:
@@ -82,64 +82,48 @@ export default function Validasi() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// ✅ Verifikasi OTP untuk REGISTRASI
|
const handleRegistrationVerification = async () => {
|
||||||
const handleRegistrationVerification = async () => {
|
const username = localStorage.getItem('auth_username');
|
||||||
const username = localStorage.getItem('auth_username');
|
if (!username) {
|
||||||
if (!username) {
|
toast.error('Data registrasi tidak ditemukan.');
|
||||||
toast.error('Data registrasi tidak ditemukan. Silakan ulangi dari awal.');
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// ✅ Validasi format
|
const cleanNomor = nomor?.replace(/\D/g, '') ?? '';
|
||||||
const cleanNomor = nomor?.replace(/\D/g, '') ?? '';
|
if (cleanNomor.length < 10 || username.trim().length < 5) {
|
||||||
if (cleanNomor.length < 10) {
|
toast.error('Data tidak valid');
|
||||||
toast.error('Nomor tidak valid');
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (username.trim().length < 5) {
|
// Verifikasi OTP dulu
|
||||||
toast.error('Username minimal 5 karakter');
|
const verifyRes = await fetch('/api/auth/verify-otp-register', {
|
||||||
return;
|
method: 'POST',
|
||||||
}
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ nomor: cleanNomor, otp, kodeId }),
|
||||||
|
});
|
||||||
|
|
||||||
// 1. Verifikasi OTP via endpoint register
|
const verifyData = await verifyRes.json();
|
||||||
const verifyRes = await fetch('/api/auth/verify-otp-register', {
|
if (!verifyRes.ok) {
|
||||||
method: 'POST',
|
toast.error(verifyData.message || 'Verifikasi OTP gagal');
|
||||||
headers: { 'Content-Type': 'application/json' },
|
return;
|
||||||
body: JSON.stringify({ nomor: cleanNomor, otp, kodeId }),
|
}
|
||||||
});
|
|
||||||
|
|
||||||
const verifyData = await verifyRes.json();
|
// ✅ Kirim ke finalize-registration → akan redirect ke /waiting-room
|
||||||
|
const finalizeRes = await fetch('/api/auth/finalize-registration', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ nomor, username, kodeId }),
|
||||||
|
credentials: 'include'
|
||||||
|
});
|
||||||
|
|
||||||
if (!verifyRes.ok) {
|
if (finalizeRes.redirected) {
|
||||||
toast.error(verifyData.message || 'Verifikasi OTP gagal');
|
// ✅ Redirect otomatis oleh server
|
||||||
return;
|
window.location.href = finalizeRes.url;
|
||||||
}
|
} else {
|
||||||
|
const data = await finalizeRes.json();
|
||||||
// 2. Finalisasi registrasi
|
toast.error(data.message || 'Registrasi gagal');
|
||||||
const finalizeRes = await fetch('/api/auth/finalize-registration', {
|
}
|
||||||
method: 'POST',
|
};
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ nomor, username, kodeId }), // 🔴 Tidak perlu kirim `otp` ke sini
|
|
||||||
});
|
|
||||||
|
|
||||||
const finalizeData = await finalizeRes.json();
|
|
||||||
|
|
||||||
if (!finalizeRes.ok) {
|
|
||||||
toast.error(finalizeData.message || 'Registrasi gagal');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Set user & redirect
|
|
||||||
authStore.setUser({
|
|
||||||
id: finalizeData.user.id,
|
|
||||||
name: finalizeData.user.name,
|
|
||||||
roleId: Number(finalizeData.user.roleId),
|
|
||||||
});
|
|
||||||
|
|
||||||
cleanupStorage();
|
|
||||||
window.location.href = '/waiting-room';
|
|
||||||
};
|
|
||||||
|
|
||||||
// ✅ Verifikasi OTP untuk LOGIN
|
// ✅ Verifikasi OTP untuk LOGIN
|
||||||
const handleLoginVerification = async () => {
|
const handleLoginVerification = async () => {
|
||||||
|
|||||||
@@ -50,38 +50,45 @@ export default function Layout({ children }: { children: React.ReactNode }) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchUser = async () => {
|
const fetchUser = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/auth/me');
|
const res = await fetch('/api/auth/me');
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
|
|
||||||
if (data.user) {
|
if (data.user) {
|
||||||
const menuRes = await fetch(`/api/admin/user-menu-access?userId=${data.user.id}`);
|
// Check if user is active
|
||||||
const menuData = await menuRes.json();
|
if (!data.user.isActive) {
|
||||||
|
|
||||||
// ✅ Clone ke array mutable
|
|
||||||
const menuIds = menuData.success && Array.isArray(menuData.menuIds)
|
|
||||||
? [...menuData.menuIds] // Converts readonly array to mutable
|
|
||||||
: null;
|
|
||||||
|
|
||||||
authStore.setUser({
|
|
||||||
id: data.user.id,
|
|
||||||
name: data.user.name,
|
|
||||||
roleId: Number(data.user.roleId),
|
|
||||||
menuIds,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
authStore.setUser(null);
|
|
||||||
router.replace('/login');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Gagal memuat data pengguna:', error);
|
|
||||||
authStore.setUser(null);
|
authStore.setUser(null);
|
||||||
router.replace('/login');
|
router.replace('/waiting-room');
|
||||||
} finally {
|
return;
|
||||||
setLoading(false);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
const menuRes = await fetch(`/api/admin/user-menu-access?userId=${data.user.id}`);
|
||||||
|
const menuData = await menuRes.json();
|
||||||
|
|
||||||
|
const menuIds = menuData.success && Array.isArray(menuData.menuIds)
|
||||||
|
? [...menuData.menuIds]
|
||||||
|
: null;
|
||||||
|
|
||||||
|
authStore.setUser({
|
||||||
|
id: data.user.id,
|
||||||
|
name: data.user.name,
|
||||||
|
roleId: Number(data.user.roleId),
|
||||||
|
menuIds,
|
||||||
|
isActive: data.user.isActive // Add isActive to store
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
authStore.setUser(null);
|
||||||
|
router.replace('/login');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Gagal memuat data pengguna:', error);
|
||||||
|
authStore.setUser(null);
|
||||||
|
router.replace('/login');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
fetchUser();
|
fetchUser();
|
||||||
}, [router]);
|
}, [router]);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// app/api/auth/_lib/session_create.ts
|
// src/app/api/auth/_lib/sessionCreate.ts
|
||||||
import { cookies } from "next/headers";
|
import { cookies } from "next/headers";
|
||||||
import { encrypt } from "./encrypt";
|
import { encrypt } from "./encrypt";
|
||||||
import prisma from "@/lib/prisma";
|
import prisma from "@/lib/prisma";
|
||||||
@@ -9,11 +9,13 @@ export async function sessionCreate({
|
|||||||
exp = "30 day",
|
exp = "30 day",
|
||||||
jwtSecret,
|
jwtSecret,
|
||||||
user,
|
user,
|
||||||
|
invalidatePrevious = true, // 🔑 kontrol apakah sesi lama di-nonaktifkan
|
||||||
}: {
|
}: {
|
||||||
sessionKey: string;
|
sessionKey: string;
|
||||||
exp?: string;
|
exp?: string;
|
||||||
jwtSecret: string;
|
jwtSecret: string;
|
||||||
user: Record<string, unknown> & { id: string };
|
user: Record<string, unknown> & { id: string };
|
||||||
|
invalidatePrevious?: boolean; // default true untuk login, false untuk registrasi
|
||||||
}) {
|
}) {
|
||||||
// ✅ Validasi env vars
|
// ✅ Validasi env vars
|
||||||
if (!sessionKey || sessionKey.length === 0) {
|
if (!sessionKey || sessionKey.length === 0) {
|
||||||
@@ -28,18 +30,19 @@ export async function sessionCreate({
|
|||||||
throw new Error("Token generation failed");
|
throw new Error("Token generation failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ Hitung expiresAt sesuai exp
|
// ✅ Hitung expiresAt
|
||||||
let expiresAt = add(new Date(), { days: 30 });
|
let expiresAt = add(new Date(), { days: 30 });
|
||||||
if (exp === "7 day") expiresAt = add(new Date(), { days: 7 });
|
if (exp === "7 day") expiresAt = add(new Date(), { days: 7 });
|
||||||
// tambahkan opsi lain jika perlu
|
|
||||||
|
|
||||||
// Sebelum create session baru, nonaktifkan session aktif sebelumnya
|
// 🔐 Hanya nonaktifkan sesi aktif sebelumnya jika diminta (misal: saat login ulang)
|
||||||
await prisma.userSession.updateMany({
|
if (invalidatePrevious) {
|
||||||
where: { userId: user.id, active: true },
|
await prisma.userSession.updateMany({
|
||||||
data: { active: false },
|
where: { userId: user.id, active: true },
|
||||||
});
|
data: { active: false },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ✅ Simpan ke database
|
// ✅ Simpan sesi baru
|
||||||
await prisma.userSession.create({
|
await prisma.userSession.create({
|
||||||
data: {
|
data: {
|
||||||
token,
|
token,
|
||||||
@@ -55,8 +58,8 @@ export async function sessionCreate({
|
|||||||
sameSite: "lax",
|
sameSite: "lax",
|
||||||
path: "/",
|
path: "/",
|
||||||
secure: process.env.NODE_ENV === "production",
|
secure: process.env.NODE_ENV === "production",
|
||||||
maxAge: 30 * 24 * 60 * 60, // seconds
|
maxAge: 30 * 24 * 60 * 60, // 30 hari dalam detik
|
||||||
});
|
});
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
@@ -30,13 +30,7 @@ export async function verifySession() {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ❌ Hanya tolak jika sessionInvalid = true
|
// Don't check isActive here, let the frontend handle it
|
||||||
if (dbSession.user.sessionInvalid) {
|
|
||||||
console.log('⚠️ Session di-invalidate');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ✅ Return user, meskipun isActive = false
|
|
||||||
return dbSession.user;
|
return dbSession.user;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('Session verification failed:', error);
|
console.warn('Session verification failed:', error);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// src/app/api/auth/finalize-registration/route.ts
|
// src/app/api/auth/finalize-registration/route.ts
|
||||||
|
|
||||||
import prisma from "@/lib/prisma";
|
import prisma from "@/lib/prisma";
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import { sessionCreate } from "../_lib/session_create";
|
import { sessionCreate } from "../_lib/session_create";
|
||||||
@@ -7,7 +6,6 @@ import { sessionCreate } from "../_lib/session_create";
|
|||||||
export async function POST(req: Request) {
|
export async function POST(req: Request) {
|
||||||
try {
|
try {
|
||||||
const { nomor, username, kodeId } = await req.json();
|
const { nomor, username, kodeId } = await req.json();
|
||||||
|
|
||||||
const cleanNomor = nomor.replace(/\D/g, "");
|
const cleanNomor = nomor.replace(/\D/g, "");
|
||||||
|
|
||||||
if (!cleanNomor || !username || !kodeId) {
|
if (!cleanNomor || !username || !kodeId) {
|
||||||
@@ -17,13 +15,7 @@ export async function POST(req: Request) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Di awal fungsi POST
|
const otpRecord = await prisma.kodeOtp.findUnique({ where: { id: kodeId } });
|
||||||
console.log("📦 Received payload:", { nomor, username, kodeId });
|
|
||||||
|
|
||||||
// Validasi OTP
|
|
||||||
const otpRecord = await prisma.kodeOtp.findUnique({
|
|
||||||
where: { id: kodeId },
|
|
||||||
});
|
|
||||||
if (!otpRecord?.isActive || otpRecord.nomor !== cleanNomor) {
|
if (!otpRecord?.isActive || otpRecord.nomor !== cleanNomor) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ success: false, message: "OTP tidak valid" },
|
{ success: false, message: "OTP tidak valid" },
|
||||||
@@ -31,7 +23,6 @@ export async function POST(req: Request) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cek duplikat username
|
|
||||||
if (await prisma.user.findFirst({ where: { username } })) {
|
if (await prisma.user.findFirst({ where: { username } })) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ success: false, message: "Username sudah digunakan" },
|
{ success: false, message: "Username sudah digunakan" },
|
||||||
@@ -39,7 +30,6 @@ export async function POST(req: Request) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ Gunakan username dari input user
|
|
||||||
const defaultRole = await prisma.role.findFirst({
|
const defaultRole = await prisma.role.findFirst({
|
||||||
where: { name: "ADMIN DESA" },
|
where: { name: "ADMIN DESA" },
|
||||||
select: { id: true },
|
select: { id: true },
|
||||||
@@ -52,23 +42,20 @@ export async function POST(req: Request) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✅ Buat user dengan username yang diinput
|
|
||||||
const newUser = await prisma.user.create({
|
const newUser = await prisma.user.create({
|
||||||
data: {
|
data: {
|
||||||
username, // ✅ Ini yang benar
|
username,
|
||||||
nomor,
|
nomor,
|
||||||
roleId: defaultRole.id,
|
roleId: defaultRole.id,
|
||||||
isActive: false,
|
isActive: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Nonaktifkan OTP
|
|
||||||
await prisma.kodeOtp.update({
|
await prisma.kodeOtp.update({
|
||||||
where: { id: kodeId },
|
where: { id: kodeId },
|
||||||
data: { isActive: false },
|
data: { isActive: false },
|
||||||
});
|
});
|
||||||
|
|
||||||
// ✅ BUAT SESI untuk user baru (meski isActive = false)
|
|
||||||
const token = await sessionCreate({
|
const token = await sessionCreate({
|
||||||
sessionKey: process.env.BASE_SESSION_KEY!,
|
sessionKey: process.env.BASE_SESSION_KEY!,
|
||||||
jwtSecret: process.env.BASE_TOKEN_KEY!,
|
jwtSecret: process.env.BASE_TOKEN_KEY!,
|
||||||
@@ -76,24 +63,15 @@ export async function POST(req: Request) {
|
|||||||
user: {
|
user: {
|
||||||
id: newUser.id,
|
id: newUser.id,
|
||||||
nomor: newUser.nomor,
|
nomor: newUser.nomor,
|
||||||
username: newUser.username, // ✅ Pastikan sesuai
|
username: newUser.username,
|
||||||
roleId: newUser.roleId,
|
|
||||||
isActive: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set cookie
|
|
||||||
const response = NextResponse.json({
|
|
||||||
success: true,
|
|
||||||
message: "Registrasi berhasil. Menunggu persetujuan admin.",
|
|
||||||
user: {
|
|
||||||
id: newUser.id,
|
|
||||||
name: newUser.username,
|
|
||||||
roleId: newUser.roleId,
|
roleId: newUser.roleId,
|
||||||
isActive: false,
|
isActive: false,
|
||||||
},
|
},
|
||||||
|
invalidatePrevious: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ✅ REDIRECT DARI SERVER — cookie pasti tersedia
|
||||||
|
const response = NextResponse.redirect(new URL('/waiting-room', req.url));
|
||||||
response.cookies.set(process.env.BASE_SESSION_KEY!, token, {
|
response.cookies.set(process.env.BASE_SESSION_KEY!, token, {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
secure: process.env.NODE_ENV === "production",
|
secure: process.env.NODE_ENV === "production",
|
||||||
@@ -111,4 +89,4 @@ export async function POST(req: Request) {
|
|||||||
} finally {
|
} finally {
|
||||||
await prisma.$disconnect();
|
await prisma.$disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
55
src/app/api/auth/refresh-session/route.ts
Normal file
55
src/app/api/auth/refresh-session/route.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
import { verifySession } from '../_lib/session_verify';
|
||||||
|
import { sessionCreate } from '../_lib/session_create';
|
||||||
|
import prisma from '@/lib/prisma';
|
||||||
|
|
||||||
|
export async function POST() {
|
||||||
|
try {
|
||||||
|
const sessionUser = await verifySession();
|
||||||
|
if (!sessionUser) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ success: false, message: "Unauthorized" },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get fresh user data
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: { id: sessionUser.id },
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
username: true,
|
||||||
|
roleId: true,
|
||||||
|
isActive: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ success: false, message: "User not found" },
|
||||||
|
{ status: 404 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new session with updated data
|
||||||
|
await sessionCreate({
|
||||||
|
sessionKey: process.env.BASE_SESSION_KEY!,
|
||||||
|
jwtSecret: process.env.BASE_TOKEN_KEY!,
|
||||||
|
user: {
|
||||||
|
id: user.id,
|
||||||
|
username: user.username,
|
||||||
|
roleId: user.roleId,
|
||||||
|
isActive: user.isActive,
|
||||||
|
},
|
||||||
|
invalidatePrevious: false, // Keep existing sessions
|
||||||
|
});
|
||||||
|
|
||||||
|
return NextResponse.json({ success: true });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error refreshing session:', error);
|
||||||
|
return NextResponse.json(
|
||||||
|
{ success: false, message: "Internal server error" },
|
||||||
|
{ status: 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,14 +2,22 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Center, Loader, Paper, Stack, Text, Title } from '@mantine/core';
|
import {
|
||||||
|
Button,
|
||||||
|
Center,
|
||||||
|
Loader,
|
||||||
|
Paper,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
Title,
|
||||||
|
} from '@mantine/core';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
import { authStore } from '@/store/authStore'; // ✅ integrasi authStore
|
||||||
|
|
||||||
async function fetchUser() {
|
async function fetchUser() {
|
||||||
const res = await fetch('/api/auth/me');
|
const res = await fetch('/api/auth/me');
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
// Jangan throw error — biarkan handle status code
|
|
||||||
const text = await res.text();
|
const text = await res.text();
|
||||||
throw new Error(`HTTP ${res.status}: ${text}`);
|
throw new Error(`HTTP ${res.status}: ${text}`);
|
||||||
}
|
}
|
||||||
@@ -21,10 +29,14 @@ export default function WaitingRoom() {
|
|||||||
const [user, setUser] = useState<any>(null);
|
const [user, setUser] = useState<any>(null);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [isRedirecting, setIsRedirecting] = useState(false);
|
const [isRedirecting, setIsRedirecting] = useState(false);
|
||||||
|
const [retryCount, setRetryCount] = useState(0);
|
||||||
|
const MAX_RETRIES = 2;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let isMounted = true;
|
let isMounted = true;
|
||||||
const interval = setInterval(async () => {
|
let interval: ReturnType<typeof setInterval>;
|
||||||
|
|
||||||
|
const poll = async () => {
|
||||||
if (isRedirecting || !isMounted) return;
|
if (isRedirecting || !isMounted) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -34,63 +46,110 @@ export default function WaitingRoom() {
|
|||||||
const currentUser = data.user;
|
const currentUser = data.user;
|
||||||
setUser(currentUser);
|
setUser(currentUser);
|
||||||
|
|
||||||
// ✅ Periksa isActive dan redirect
|
// ✅ Update authStore
|
||||||
|
if (currentUser) {
|
||||||
|
authStore.setUser({
|
||||||
|
id: currentUser.id,
|
||||||
|
name: currentUser.name,
|
||||||
|
roleId: Number(currentUser.roleId),
|
||||||
|
menuIds: currentUser.menuIds || null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the poll function
|
||||||
if (currentUser?.isActive === true) {
|
if (currentUser?.isActive === true) {
|
||||||
setIsRedirecting(true);
|
setIsRedirecting(true);
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
|
|
||||||
// ✅ roleId adalah STRING → gunakan string literal
|
// Update authStore with the current user data
|
||||||
let redirectPath = '/admin';
|
authStore.setUser({
|
||||||
|
id: currentUser.id,
|
||||||
|
name: currentUser.name || 'User',
|
||||||
|
roleId: Number(currentUser.roleId),
|
||||||
|
menuIds: currentUser.menuIds || null,
|
||||||
|
isActive: true
|
||||||
|
});
|
||||||
|
|
||||||
switch (currentUser.roleId) {
|
// Clean up storage
|
||||||
case "0": // DEVELOPER
|
localStorage.removeItem('auth_kodeId');
|
||||||
case "1": // SUPERADMIN
|
localStorage.removeItem('auth_nomor');
|
||||||
case "2": // ADMIN_DESA
|
localStorage.removeItem('auth_username');
|
||||||
redirectPath = '/admin/landing-page/profil/program-inovasi';
|
|
||||||
break;
|
// Force a session refresh
|
||||||
case "3": // ADMIN_KESEHATAN
|
try {
|
||||||
redirectPath = '/admin/kesehatan/posyandu';
|
const res = await fetch('/api/auth/refresh-session', {
|
||||||
break;
|
method: 'POST',
|
||||||
case "4": // ADMIN_PENDIDIKAN
|
credentials: 'include'
|
||||||
redirectPath = '/admin/pendidikan/info-sekolah/jenjang-pendidikan';
|
});
|
||||||
break;
|
|
||||||
|
if (res.ok) {
|
||||||
|
// Redirect based on role
|
||||||
|
let redirectPath = '/admin';
|
||||||
|
switch (String(currentUser.roleId)) {
|
||||||
|
case "0": case "1": case "2":
|
||||||
|
redirectPath = '/admin/landing-page/profil/program-inovasi';
|
||||||
|
break;
|
||||||
|
case "3":
|
||||||
|
redirectPath = '/admin/kesehatan/posyandu';
|
||||||
|
break;
|
||||||
|
case "4":
|
||||||
|
redirectPath = '/admin/pendidikan/info-sekolah/jenjang-pendidikan';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
window.location.href = redirectPath; // Use window.location to force full page reload
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error refreshing session:', error);
|
||||||
|
router.refresh(); // Fallback to client-side refresh
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => router.push(redirectPath), 500);
|
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
if (!isMounted) return;
|
if (!isMounted) return;
|
||||||
|
|
||||||
// ❌ Hanya redirect ke /login jika benar-benar tidak ada sesi
|
|
||||||
if (err.message.includes('401')) {
|
if (err.message.includes('401')) {
|
||||||
setError('Sesi tidak valid');
|
if (retryCount < MAX_RETRIES) {
|
||||||
clearInterval(interval);
|
setRetryCount((prev) => prev + 1);
|
||||||
router.push('/login');
|
setTimeout(() => {
|
||||||
|
if (isMounted) interval = setInterval(poll, 3000);
|
||||||
|
}, 800);
|
||||||
|
} else {
|
||||||
|
setError('Sesi tidak valid. Silakan login ulang.');
|
||||||
|
clearInterval(interval);
|
||||||
|
authStore.setUser(null); // ✅ clear sesi
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.error('Error polling:', err);
|
console.error('Error polling:', err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 3000);
|
};
|
||||||
|
|
||||||
|
interval = setInterval(poll, 3000);
|
||||||
return () => {
|
return () => {
|
||||||
isMounted = false;
|
isMounted = false;
|
||||||
clearInterval(interval);
|
if (interval) clearInterval(interval);
|
||||||
};
|
};
|
||||||
}, [router, isRedirecting]);
|
}, [router, isRedirecting, retryCount]);
|
||||||
|
|
||||||
|
// ✅ UI Error
|
||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<Center h="100vh">
|
<Center h="100vh">
|
||||||
<Paper p="xl" radius="md" bg={colors['white-trans-1']} w={400}>
|
<Paper p="xl" radius="md" bg={colors['white-trans-1']} w={400}>
|
||||||
<Stack align="center" gap="md">
|
<Stack align="center" gap="md">
|
||||||
<Title order={3} c="red">Error</Title>
|
<Title order={3} c="red">
|
||||||
|
Sesi Tidak Valid
|
||||||
|
</Title>
|
||||||
<Text>{error}</Text>
|
<Text>{error}</Text>
|
||||||
|
<Button onClick={() => router.push('/login')}>
|
||||||
|
Login Ulang
|
||||||
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Center>
|
</Center>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ✅ UI Redirecting
|
||||||
if (isRedirecting) {
|
if (isRedirecting) {
|
||||||
return (
|
return (
|
||||||
<Center h="100vh" bg={colors.Bg}>
|
<Center h="100vh" bg={colors.Bg}>
|
||||||
@@ -109,6 +168,7 @@ export default function WaitingRoom() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ✅ UI Default (MENUNGGU) — INI YANG KAMU HILANGKAN!
|
||||||
return (
|
return (
|
||||||
<Center h="100vh" bg={colors.Bg}>
|
<Center h="100vh" bg={colors.Bg}>
|
||||||
<Paper p="xl" radius="md" bg={colors['white-trans-1']} w={{ base: '90%', sm: 400 }}>
|
<Paper p="xl" radius="md" bg={colors['white-trans-1']} w={{ base: '90%', sm: 400 }}>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export type User = {
|
|||||||
name: string;
|
name: string;
|
||||||
roleId: number;
|
roleId: number;
|
||||||
menuIds?: string[] | null; // ✅ Pastikan pakai `string[]`
|
menuIds?: string[] | null; // ✅ Pastikan pakai `string[]`
|
||||||
|
isActive?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const authStore = proxy<{
|
export const authStore = proxy<{
|
||||||
|
|||||||
Reference in New Issue
Block a user