diff --git a/frontend/src/app/(protected)/layout.tsx b/frontend/src/app/(protected)/layout.tsx index 952bd7d..8463939 100644 --- a/frontend/src/app/(protected)/layout.tsx +++ b/frontend/src/app/(protected)/layout.tsx @@ -12,7 +12,7 @@ export default function ProtectedLayout({ children: React.ReactNode; }) { const { data } = useSession(); - const isAdmin = (data?.user as any)?.isAdmin; + const isAdmin = data?.user?.isAdmin; return ( diff --git a/frontend/src/app/(protected)/shifts/[id]/page.tsx b/frontend/src/app/(protected)/shifts/[id]/page.tsx index a1e4fb6..71a2e17 100644 --- a/frontend/src/app/(protected)/shifts/[id]/page.tsx +++ b/frontend/src/app/(protected)/shifts/[id]/page.tsx @@ -7,7 +7,6 @@ import { Button } from '@/components/ui/button'; import { Progress } from '@/components/ui/progress'; import { Badge } from '@/components/ui/badge'; import { useRouter } from 'next/navigation'; -import { useSession } from 'next-auth/react'; export default function ShiftDetailPage({ params }: { params: Promise<{ id: string }> }) { const resolvedParams = use(params); @@ -15,7 +14,6 @@ export default function ShiftDetailPage({ params }: { params: Promise<{ id: stri const signUpMutation = useSignUpShift(); const cancelMutation = useCancelSignUp(); const router = useRouter(); - const { data: session } = useSession(); if (isLoading) return
Loading shift...
; if (!shift) return
Shift not found
; diff --git a/frontend/src/auth/auth.ts b/frontend/src/auth/auth.ts index 7724480..961ba2d 100644 --- a/frontend/src/auth/auth.ts +++ b/frontend/src/auth/auth.ts @@ -47,14 +47,14 @@ export const { handlers, signIn, signOut, auth } = NextAuth({ async jwt({ token, account }) { if (account && account.access_token) { // Add clubs claim from Keycloak access token - token.clubs = (account as any).clubs as Record || {} - token.accessToken = account.access_token as string + token.clubs = (account as { clubs?: Record }).clubs || {} + token.accessToken = account.access_token try { const payload = JSON.parse(Buffer.from((token.accessToken as string).split('.')[1], 'base64').toString()); - const roles = payload.realm_access?.roles || []; + const roles = (payload.realm_access?.roles as string[]) || []; token.isAdmin = roles.includes('admin'); - } catch (e) { + } catch { token.isAdmin = false; } } @@ -63,10 +63,10 @@ export const { handlers, signIn, signOut, auth } = NextAuth({ async session({ session, token }) { // Expose clubs to client if (session.user) { - session.user.clubs = (token as any).clubs as Record | undefined - session.user.isAdmin = (token as any).isAdmin as boolean | undefined + session.user.clubs = token.clubs as Record | undefined + session.user.isAdmin = token.isAdmin as boolean | undefined } - session.accessToken = (token as any).accessToken as string | undefined + session.accessToken = token.accessToken as string | undefined return session } } diff --git a/frontend/src/components/admin/club-management.tsx b/frontend/src/components/admin/club-management.tsx index 3791fe5..eb24953 100644 --- a/frontend/src/components/admin/club-management.tsx +++ b/frontend/src/components/admin/club-management.tsx @@ -17,10 +17,30 @@ export function ClubManagement() { const [isCreating, setIsCreating] = useState(false); const [newClub, setNewClub] = useState({ name: '', sportType: 'Tennis', description: '' }); + useEffect(() => { + const fetchClubsLocally = async () => { + try { + const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/admin/clubs`, { + headers: { Authorization: `Bearer ${session?.accessToken}` }, + }); + if (res.ok) { + const data = await res.json(); + setClubs(data); + } + } catch (error) { + console.error('Failed to fetch clubs', error); + } finally { + setLoading(false); + } + }; + + if (session) fetchClubsLocally(); + }, [session]); + const fetchClubs = async () => { try { const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/admin/clubs`, { - headers: { Authorization: `Bearer ${(session as any)?.accessToken}` }, + headers: { Authorization: `Bearer ${session?.accessToken}` }, }); if (res.ok) { const data = await res.json(); @@ -33,10 +53,6 @@ export function ClubManagement() { } }; - useEffect(() => { - if (session) fetchClubs(); - }, [session]); - const handleCreate = async (e: React.FormEvent) => { e.preventDefault(); try { @@ -44,7 +60,7 @@ export function ClubManagement() { method: 'POST', headers: { 'Content-Type': 'application/json', - Authorization: `Bearer ${(session as any)?.accessToken}`, + Authorization: `Bearer ${session?.accessToken}`, }, body: JSON.stringify({ name: newClub.name, @@ -67,7 +83,7 @@ export function ClubManagement() { try { const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/admin/clubs/${id}`, { method: 'DELETE', - headers: { Authorization: `Bearer ${(session as any)?.accessToken}` }, + headers: { Authorization: `Bearer ${session?.accessToken}` }, }); if (res.ok) { fetchClubs(); diff --git a/frontend/src/components/auth-guard.tsx b/frontend/src/components/auth-guard.tsx index 087cf98..62849e4 100644 --- a/frontend/src/components/auth-guard.tsx +++ b/frontend/src/components/auth-guard.tsx @@ -18,7 +18,7 @@ export function AuthGuard({ children }: { children: ReactNode }) { useEffect(() => { if (status === 'authenticated') { - const isAdmin = (data?.user as any)?.isAdmin; + const isAdmin = data?.user?.isAdmin; // Admin routing if (isAdmin) { @@ -59,7 +59,7 @@ export function AuthGuard({ children }: { children: ReactNode }) { ); } - const isAdmin = (data?.user as any)?.isAdmin; + const isAdmin = data?.user?.isAdmin; if (clubs.length === 0 && status === 'authenticated' && !isAdmin) { const handleSwitchAccount = () => { const keycloakLogoutUrl = `${process.env.NEXT_PUBLIC_KEYCLOAK_ISSUER || 'http://localhost:8080/realms/workclub'}/protocol/openid-connect/logout?redirect_uri=${encodeURIComponent(window.location.origin + '/login')}`;