proj/Frontend/components/dashboard/room-management.tsx
2026-01-22 10:49:41 +01:00

203 lines
8.3 KiB
TypeScript

"use client"
import { useState, useEffect } from "react"
import { useAuth } from "@/lib/auth-context"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Trash2, Plus, Upload } from "lucide-react"
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import type { Room } from "@/lib/types"
export function RoomManagement() {
const { rooms, authHeader } = useAuth()
const [localRooms, setLocalRooms] = useState<Room[]>([])
const [newRoomName, setNewRoomName] = useState("")
const [isCreateOpen, setIsCreateOpen] = useState(false)
const [csvFile, setCsvFile] = useState<File | null>(null)
const [uploading, setUploading] = useState(false)
const API_URL = process.env.NEXT_PUBLIC_API_URL + "/api"
useEffect(() => {
setLocalRooms(rooms)
}, [rooms])
const refreshRooms = async () => {
if (!authHeader) return
const res = await fetch(`${API_URL}/rooms`, { headers: { Authorization: authHeader } })
if (res.ok) {
const data = await res.json()
setLocalRooms(data)
}
}
const handleCreate = async () => {
if (!newRoomName.trim() || !authHeader) return
try {
const res = await fetch(`${API_URL}/rooms`, {
method: "POST",
headers: {
"Authorization": authHeader,
"Content-Type": "application/json"
},
body: JSON.stringify({ name: newRoomName })
})
if (res.ok) {
setNewRoomName("")
setIsCreateOpen(false)
refreshRooms()
}
} catch (e) { console.error(e) }
}
const handleDelete = async (id: number) => {
if (!confirm("Raum wirklich löschen? Dies könnte fehlschlagen, wenn Tickets existieren.") || !authHeader) return
try {
const res = await fetch(`${API_URL}/rooms/${id}`, {
method: "DELETE",
headers: { "Authorization": authHeader }
})
if (res.ok) refreshRooms()
else alert("Löschen fehlgeschlagen (evtl. existieren Tickets für diesen Raum)")
} catch (e) { console.error(e) }
}
const handleUpdate = async (id: number, newName: string) => {
if (!authHeader) return
try {
const res = await fetch(`${API_URL}/rooms/${id}`, {
method: "PUT",
headers: {
"Authorization": authHeader,
"Content-Type": "application/json"
},
body: JSON.stringify({ name: newName })
})
if (res.ok) refreshRooms()
} catch (e) { console.error(e) }
}
const handleFileUpload = async () => {
if (!csvFile || !authHeader) return
setUploading(true)
const formData = new FormData()
formData.append("file", csvFile)
try {
const res = await fetch(`${API_URL}/rooms/import`, {
method: "POST",
headers: { "Authorization": authHeader },
body: formData
})
if (res.ok) {
const data = await res.json()
alert(data.message)
setCsvFile(null)
refreshRooms()
}
} catch (e) { console.error(e) }
finally { setUploading(false) }
}
return (
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle>Räume verwalten</CardTitle>
<CardDescription>Erstellen, Bearbeiten, Löschen und CSV-Import</CardDescription>
</div>
<Dialog open={isCreateOpen} onOpenChange={setIsCreateOpen}>
<DialogTrigger asChild>
<Button size="sm"><Plus className="mr-2 h-4 w-4" /> Neuer Raum</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Neuen Raum erstellen</DialogTitle>
</DialogHeader>
<div className="flex gap-2 mt-4">
<Input
placeholder="Raum Name (z.B. A-101)"
value={newRoomName}
onChange={(e) => setNewRoomName(e.target.value)}
/>
<Button onClick={handleCreate}>Erstellen</Button>
</div>
</DialogContent>
</Dialog>
</div>
</CardHeader>
<CardContent className="space-y-6">
{/* CSV Import Section */}
<div className="flex items-center gap-4 p-4 border border-dashed rounded-lg bg-muted/30">
<div className="grid gap-1.5 flex-1">
<span className="font-semibold text-sm">Raum-Import (CSV)</span>
<span className="text-xs text-muted-foreground">Datei mit Raumnamen pro Zeile</span>
</div>
<Input
type="file"
accept=".csv,.txt"
onChange={(e) => setCsvFile(e.target.files?.[0] || null)}
className="w-full max-w-xs"
/>
<Button
variant="secondary"
disabled={!csvFile || uploading}
onClick={handleFileUpload}
>
<Upload className="mr-2 h-4 w-4" /> Importieren
</Button>
</div>
<div className="rounded-md border">
<Table>
<TableHeader>
<TableRow>
<TableHead>ID</TableHead>
<TableHead className="w-full">Name</TableHead>
<TableHead className="text-right">Aktionen</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{localRooms.map((room) => (
<TableRow key={room.id}>
<TableCell>{room.id}</TableCell>
<TableCell>
<Input
defaultValue={room.name}
className="h-8 w-40"
onBlur={(e) => {
if (e.target.value !== room.name) {
handleUpdate(room.id, e.target.value)
}
}}
/>
</TableCell>
<TableCell className="text-right">
<Button
variant="ghost"
size="icon"
className="text-destructive hover:text-destructive hover:bg-destructive/10"
onClick={() => handleDelete(room.id)}
>
<Trash2 className="h-4 w-4" />
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</CardContent>
</Card>
)
}