proj/Frontend/lib/auth-context.tsx
2026-01-21 09:52:33 +01:00

206 lines
5.8 KiB
TypeScript

"use client"
import { createContext, useContext, useState, useCallback, useEffect, type ReactNode } from "react"
import { useSession, signIn, signOut } from "next-auth/react"
import type { User, Ticket, TicketStatus, Room } from "./types"
const API_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8080/api"
interface AuthContextType {
user: User | null
tickets: Ticket[]
rooms: Room[]
login: (email: string, password: string) => Promise<boolean>
register: (user: Omit<User, "id" | "name"> & { firstname: string; lastname: string; password: string; roomIds?: number[] }) => Promise<boolean>
logout: () => void
createTicket: (ticket: { roomId: number; title: string; description: string }) => Promise<void>
updateTicketStatus: (ticketId: number, status: TicketStatus) => Promise<void>
updateRooms: (roomIds: number[]) => Promise<void>
}
const AuthContext = createContext<AuthContextType | null>(null)
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null)
const [tickets, setTickets] = useState<Ticket[]>([])
const [rooms, setRooms] = useState<Room[]>([])
const [authHeader, setAuthHeader] = useState<string | null>(null)
// Fetch rooms on mount
useEffect(() => {
fetch(`${API_URL}/rooms`)
.then(res => {
if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`)
return res.json()
})
.then(data => setRooms(data))
.catch(err => console.error("Failed to fetch rooms", err))
}, [])
const fetchTickets = useCallback(async (auth: string) => {
try {
const res = await fetch(`${API_URL}/tickets`, {
headers: { "Authorization": auth }
})
if (res.ok) {
const data = await res.json()
setTickets(data)
}
} catch (err) {
console.error("Failed to fetch tickets", err)
}
}, [])
// Sync with NextAuth session
const { data: session, status } = useSession()
useEffect(() => {
if (session?.user && session?.authHeader) {
// @ts-ignore
setUser(session.user)
// @ts-ignore
setAuthHeader(session.authHeader)
// @ts-ignore
fetchTickets(session.authHeader)
} else if (status === "unauthenticated") {
setUser(null)
setAuthHeader(null)
setTickets([])
}
}, [session, status, fetchTickets])
const login = useCallback(async (email: string, password: string) => {
// We use next-auth signIn, which calls our authorize callback
const result = await signIn("credentials", {
email,
password,
redirect: false
})
if (result?.ok) {
// Session update will trigger useEffect
return true
}
return false
}, [])
const register = useCallback(async (newUser: any) => {
const payload = {
firstname: newUser.firstname,
lastname: newUser.lastname,
email: newUser.email,
password: newUser.password,
role: newUser.role, // LEHRKRAFT or RAUMBETREUER
roomIds: newUser.roomIds
}
try {
const res = await fetch(`${API_URL}/auth/register`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
})
if (res.ok) {
// Auto login after register? Or just return true
// Implementation requirement: "Alle Nutzer ... müssen sich registrieren ... Vor der Nutzung ... anmelden"
// So we redirect to login.
return true
}
} catch (e) {
console.error(e)
}
return false
}, [])
const logout = useCallback(() => {
signOut({ redirect: false })
setUser(null)
setAuthHeader(null)
setTickets([])
}, [])
const createTicket = useCallback(async (ticket: { roomId: number; title: string; description: string }) => {
if (!authHeader) return
try {
const res = await fetch(`${API_URL}/tickets`, {
method: "POST",
headers: {
"Authorization": authHeader,
"Content-Type": "application/json"
},
body: JSON.stringify(ticket)
})
if (res.ok) {
fetchTickets(authHeader) // Refresh
}
} catch (e) {
console.error(e)
}
}, [authHeader, fetchTickets])
const updateRooms = useCallback(async (roomIds: number[]) => {
if (!session?.authHeader) return
try {
const res = await fetch(`${API_URL}/auth/profile/rooms`, {
method: "PUT",
headers: {
"Authorization": session.authHeader as string,
"Content-Type": "application/json"
},
body: JSON.stringify({ roomIds })
})
if (res.ok) {
const updatedUser = await res.json()
setUser(updatedUser)
// Note: session.user won't be updated automatically until next session fetch, but local state is updated.
}
} catch (e) {
console.error(e)
}
}, [session])
const updateTicketStatus = useCallback(async (ticketId: number, status: TicketStatus) => {
if (!authHeader) return
try {
const res = await fetch(`${API_URL}/tickets/${ticketId}/status`, {
method: "PATCH",
headers: {
"Authorization": authHeader,
"Content-Type": "application/json"
},
body: JSON.stringify({ status })
})
if (res.ok) {
fetchTickets(authHeader) // Refresh
}
} catch (e) {
console.error(e)
}
}, [authHeader, fetchTickets])
return (
<AuthContext.Provider
value={{
user,
tickets,
rooms,
login,
register,
logout,
createTicket,
updateTicketStatus,
updateRooms,
}}
>
{children}
</AuthContext.Provider>
)
}
export function useAuth() {
const context = useContext(AuthContext)
if (!context) {
throw new Error("useAuth must be used within an AuthProvider")
}
return context
}