>({});
useEffect(() => {
- fetch('/api/auth/me')
- .then((res) => {
- if (!res.ok) {
- router.push('/login');
- return null;
- }
- return res.json();
- })
- .then((data) => {
- if (data && data.user) {
- setRole(data.user.role);
- setCurrentUser(data.user);
- if (data.user.role !== 'admin' && data.user.role !== 'analyst') {
- router.push('/unauthorized');
- return;
- }
- }
- setIsLoading(false);
- })
- .catch((err) => {
- console.error('Failed to fetch auth state:', err);
+ if (!contextLoading) {
+ if (!currentUser) {
router.push('/login');
- });
+ return;
+ }
+ if (role !== 'admin' && role !== 'analyst') {
+ router.push('/unauthorized');
+ return;
+ }
+ }
+ }, [contextLoading, currentUser, role, router]);
+
+ useEffect(() => {
+ if (contextLoading || !currentUser || (role !== 'admin' && role !== 'analyst')) {
+ return;
+ }
// Load users list for username mapping
fetch('/api/users')
@@ -162,7 +154,7 @@ export default function SimulationPage() {
}
};
- if (isLoading) {
+ if (contextLoading) {
return (
diff --git a/src/app/settlements/approvals/page.module.css b/src/app/settlements/approvals/page.module.css
index 5c5eb1b..11bdfcf 100644
--- a/src/app/settlements/approvals/page.module.css
+++ b/src/app/settlements/approvals/page.module.css
@@ -1,8 +1,10 @@
.container {
min-height: 100vh;
- background-color: #0f172a;
- color: #f8fafc;
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
+ background: radial-gradient(circle at top right, hsla(var(--primary-h), var(--primary-s), var(--primary-l), 0.08), transparent 45%),
+ var(--background);
+ color: var(--foreground);
+ font-family: var(--font-sans);
+ transition: background var(--transition-slow);
padding-bottom: 3rem;
}
diff --git a/src/app/settlements/approvals/page.tsx b/src/app/settlements/approvals/page.tsx
index 0ec5f97..600bbd5 100644
--- a/src/app/settlements/approvals/page.tsx
+++ b/src/app/settlements/approvals/page.tsx
@@ -6,6 +6,7 @@ import styles from './page.module.css';
import Header from '@/components/Header';
import { useRouter } from 'next/navigation';
import { useLocale } from '@/lib/i18n/LocaleContext';
+import { useUser } from '@/lib/auth/UserContext';
interface Settlement {
id: number;
@@ -40,7 +41,7 @@ interface Settlement {
export default function ApprovalsPage() {
const router = useRouter();
const { t } = useLocale();
- const [role, setRole] = useState(null);
+ const { role, user: currentUser, isLoading: contextLoading } = useUser();
const [isLoading, setIsLoading] = useState(true);
const [settlements, setSettlements] = useState([]);
const [error, setError] = useState(null);
@@ -71,35 +72,23 @@ export default function ApprovalsPage() {
};
useEffect(() => {
- fetch('/api/auth/me')
- .then((res) => {
- if (!res.ok) {
- router.push('/login');
- return null;
- }
- return res.json();
- })
- .then((data) => {
- if (data && data.user) {
- setRole(data.user.role);
- if (
- data.user.role !== 'commercial_leader' &&
- data.user.role !== 'admin' &&
- data.user.role !== 'analyst' &&
- data.user.role !== 'director'
- ) {
- router.push('/unauthorized');
- return;
- } else {
- fetchSettlements();
- }
- }
- })
- .catch((err) => {
- console.error('Failed to get session user details', err);
+ if (!contextLoading) {
+ if (!currentUser) {
router.push('/login');
- });
- }, []);
+ return;
+ }
+ if (
+ role !== 'commercial_leader' &&
+ role !== 'admin' &&
+ role !== 'analyst' &&
+ role !== 'director'
+ ) {
+ router.push('/unauthorized');
+ return;
+ }
+ fetchSettlements();
+ }
+ }, [contextLoading, currentUser, role, router]);
const handleApprove = async (id: number) => {
setError(null);
diff --git a/src/components/Header.tsx b/src/components/Header.tsx
index edbd381..c909eb3 100644
--- a/src/components/Header.tsx
+++ b/src/components/Header.tsx
@@ -5,6 +5,7 @@ import Link from 'next/link';
import { useRouter } from 'next/navigation';
import styles from './Header.module.css';
import { useLocale } from '@/lib/i18n/LocaleContext';
+import { useUser } from '@/lib/auth/UserContext';
interface HeaderProps {
activeTab: 'plans' | 'goals' | 'import' | 'simulation' | 'approvals' | 'none';
@@ -13,25 +14,12 @@ interface HeaderProps {
export default function Header({ activeTab }: HeaderProps) {
const router = useRouter();
const { locale, setLocale, t } = useLocale();
- const [role, setRole] = React.useState(null);
-
- React.useEffect(() => {
- fetch('/api/auth/me')
- .then(res => {
- if (res.ok) return res.json();
- throw new Error('Not authenticated');
- })
- .then(data => {
- setRole(data.user?.role || null);
- })
- .catch(err => {
- console.error('Failed to get session user details', err);
- });
- }, []);
+ const { role, refreshUser } = useUser();
const handleLogout = async () => {
try {
await fetch('/api/auth/logout', { method: 'POST' });
+ await refreshUser();
router.push('/login');
router.refresh();
} catch (err) {
diff --git a/src/lib/auth/UserContext.tsx b/src/lib/auth/UserContext.tsx
new file mode 100644
index 0000000..6eb1422
--- /dev/null
+++ b/src/lib/auth/UserContext.tsx
@@ -0,0 +1,71 @@
+'use client';
+
+import React, { createContext, useContext, useState, useEffect } from 'react';
+
+interface User {
+ id: number;
+ username: string;
+ email: string;
+ role: string;
+ area: string;
+ hotelId: number;
+ regionId: number;
+}
+
+interface UserContextProps {
+ user: User | null;
+ role: string | null;
+ isLoading: boolean;
+ refreshUser: () => Promise;
+}
+
+const UserContext = createContext(undefined);
+
+export function UserProvider({ children }: { children: React.ReactNode }) {
+ const [user, setUser] = useState(null);
+ const [role, setRole] = useState(null);
+ const [isLoading, setIsLoading] = useState(true);
+
+ const fetchUser = async () => {
+ try {
+ const res = await fetch('/api/auth/me');
+ if (res.ok) {
+ const data = await res.json();
+ if (data?.user) {
+ setUser(data.user);
+ setRole(data.user.role);
+ } else {
+ setUser(null);
+ setRole(null);
+ }
+ } else {
+ setUser(null);
+ setRole(null);
+ }
+ } catch (err) {
+ console.error('Error fetching user context:', err);
+ setUser(null);
+ setRole(null);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ useEffect(() => {
+ fetchUser();
+ }, []);
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useUser() {
+ const context = useContext(UserContext);
+ if (!context) {
+ throw new Error('useUser must be used within a UserProvider');
+ }
+ return context;
+}