womp womp

This commit is contained in:
Hymmel 2026-01-21 11:50:23 +01:00
parent 3e95e2d4f2
commit b96fb0d0e4
4 changed files with 74 additions and 32 deletions

View file

@ -39,7 +39,14 @@ public class AuthService {
user.setName(request.getFirstname() + " " + request.getLastname()); // Populate legacy/fallback fields user.setName(request.getFirstname() + " " + request.getLastname()); // Populate legacy/fallback fields
user.setEmail(request.getEmail()); user.setEmail(request.getEmail());
user.setPassword(passwordEncoder.encode(request.getPassword())); user.setPassword(passwordEncoder.encode(request.getPassword()));
user.setRole(request.getRole()); user.setPassword(passwordEncoder.encode(request.getPassword()));
// Auto-assign ADMIN role to the very first user
if (userRepository.count() == 0) {
user.setRole("ADMIN");
} else {
user.setRole(request.getRole());
}
if ("RAUMBETREUER".equals(request.getRole()) && request.getRoomIds() != null) { if ("RAUMBETREUER".equals(request.getRole()) && request.getRoomIds() != null) {
List<Room> rooms = roomRepository.findAllById(request.getRoomIds()); List<Room> rooms = roomRepository.findAllById(request.getRoomIds());

View file

@ -7,7 +7,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
import { ShieldAlert, Users } from "lucide-react" import { ShieldAlert, Users } from "lucide-react"
export function AdminDashboard() { export function AdminDashboard() {
const { tickets, updateTicketStatus, authHeader } = useAuth() const { tickets, updateTicketStatus, deleteTicket, authHeader } = useAuth()
const API_URL = process.env.NEXT_PUBLIC_API_URL + "/api" const API_URL = process.env.NEXT_PUBLIC_API_URL + "/api"
// Admin sees all tickets (logic handled in Backend/TicketService) // Admin sees all tickets (logic handled in Backend/TicketService)
@ -62,6 +62,7 @@ export function AdminDashboard() {
tickets={tickets} tickets={tickets}
showStatusUpdate showStatusUpdate
onStatusUpdate={updateTicketStatus} onStatusUpdate={updateTicketStatus}
onDeleteTicket={deleteTicket}
/> />
</CardContent> </CardContent>
</Card> </Card>

View file

@ -11,7 +11,8 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from "@/components/ui/select" } from "@/components/ui/select"
import { ArrowUpDown, ArrowDown, ArrowUp, Calendar, MapPin, FileText } from "lucide-react" import { ArrowUpDown, ArrowDown, ArrowUp, Calendar, MapPin, FileText, Trash2 } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input" import { Input } from "@/components/ui/input"
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog" import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"
import { TicketComments } from "./ticket-comments" import { TicketComments } from "./ticket-comments"
@ -20,11 +21,12 @@ interface TicketTableProps {
tickets: Ticket[] tickets: Ticket[]
showStatusUpdate?: boolean showStatusUpdate?: boolean
onStatusUpdate?: (ticketId: number, status: TicketStatus) => void onStatusUpdate?: (ticketId: number, status: TicketStatus) => void
onDeleteTicket?: (ticketId: number) => void
} }
type SortDirection = "asc" | "desc" type SortDirection = "asc" | "desc"
export function TicketTable({ tickets, showStatusUpdate = false, onStatusUpdate }: TicketTableProps) { export function TicketTable({ tickets, showStatusUpdate = false, onStatusUpdate, onDeleteTicket }: TicketTableProps) {
const [sortConfig, setSortConfig] = useState<{ key: keyof Ticket | "room" | "owner"; direction: SortDirection }>({ key: "createdAt", direction: "desc" }) const [sortConfig, setSortConfig] = useState<{ key: keyof Ticket | "room" | "owner"; direction: SortDirection }>({ key: "createdAt", direction: "desc" })
const [search, setSearch] = useState("") const [search, setSearch] = useState("")
const [statusFilter, setStatusFilter] = useState<TicketStatus | "ALL">("ALL") const [statusFilter, setStatusFilter] = useState<TicketStatus | "ALL">("ALL")
@ -133,6 +135,7 @@ export function TicketTable({ tickets, showStatusUpdate = false, onStatusUpdate
<TableHead className="font-semibold cursor-pointer" onClick={() => handleSort("status")}> <TableHead className="font-semibold cursor-pointer" onClick={() => handleSort("status")}>
Status {sortConfig.key === "status" && (sortConfig.direction === "asc" ? <ArrowUp className="h-3 w-3" /> : <ArrowDown className="h-3 w-3" />)} Status {sortConfig.key === "status" && (sortConfig.direction === "asc" ? <ArrowUp className="h-3 w-3" /> : <ArrowDown className="h-3 w-3" />)}
</TableHead> </TableHead>
{onDeleteTicket && <TableHead className="w-[50px]"></TableHead>}
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
@ -174,6 +177,23 @@ export function TicketTable({ tickets, showStatusUpdate = false, onStatusUpdate
<StatusBadge status={ticket.status} /> <StatusBadge status={ticket.status} />
)} )}
</TableCell> </TableCell>
{onDeleteTicket && (
<TableCell onClick={(e) => e.stopPropagation()}>
<Button
variant="ghost"
size="icon"
className="h-8 w-8 text-destructive hover:text-destructive hover:bg-destructive/10"
onClick={(e) => {
e.stopPropagation()
if (confirm("Ticket wirklich löschen?")) {
onDeleteTicket(ticket.id)
}
}}
>
<Trash2 className="h-4 w-4" />
</Button>
</TableCell>
)}
</TableRow> </TableRow>
)) ))
)} )}

View file

@ -16,6 +16,7 @@ interface AuthContextType {
logout: () => void logout: () => void
createTicket: (ticket: { roomId: number; title: string; description: string }) => Promise<void> createTicket: (ticket: { roomId: number; title: string; description: string }) => Promise<void>
updateTicketStatus: (ticketId: number, status: TicketStatus) => Promise<void> updateTicketStatus: (ticketId: number, status: TicketStatus) => Promise<void>
deleteTicket: (ticketId: number) => Promise<void>
updateRooms: (roomIds: number[]) => Promise<void> updateRooms: (roomIds: number[]) => Promise<void>
} }
@ -176,33 +177,46 @@ export function AuthProvider({ children }: { children: ReactNode }) {
} }
} catch (e) { } catch (e) {
console.error(e) console.error(e)
const deleteTicket = useCallback(async (ticketId: number) => {
if (!authHeader) return
try {
const res = await fetch(`${API_URL}/tickets/${ticketId}`, {
method: "DELETE",
headers: { "Authorization": authHeader }
})
if (res.ok) {
fetchTickets(authHeader)
}
} catch (e) {
console.error(e)
}
}, [authHeader, fetchTickets])
return (
<AuthContext.Provider
value={{
user,
tickets,
rooms,
authHeader,
login,
register,
logout,
createTicket,
updateTicketStatus,
deleteTicket,
updateRooms,
}}
>
{children}
</AuthContext.Provider>
)
} }
}, [authHeader, fetchTickets])
return ( export function useAuth() {
<AuthContext.Provider const context = useContext(AuthContext)
value={{ if (!context) {
user, throw new Error("useAuth must be used within an AuthProvider")
tickets, }
rooms, return context
authHeader, }
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
}