143 lines
5.3 KiB
TypeScript
143 lines
5.3 KiB
TypeScript
"use client"
|
|
|
|
import { useState, useMemo } from "react"
|
|
import type { Ticket, TicketStatus } from "@/lib/types"
|
|
import { StatusBadge } from "./status-badge"
|
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "@/components/ui/select"
|
|
import { ArrowUpDown, ArrowDown, ArrowUp, Calendar, MapPin, FileText } from "lucide-react"
|
|
import { Button } from "@/components/ui/button"
|
|
|
|
interface TicketTableProps {
|
|
tickets: Ticket[]
|
|
showStatusUpdate?: boolean
|
|
onStatusUpdate?: (ticketId: number, status: TicketStatus) => void
|
|
}
|
|
|
|
type SortDirection = "asc" | "desc"
|
|
|
|
export function TicketTable({ tickets, showStatusUpdate = false, onStatusUpdate }: TicketTableProps) {
|
|
const [sortDirection, setSortDirection] = useState<SortDirection>("desc")
|
|
|
|
const sortedTickets = useMemo(() => {
|
|
return [...tickets].sort((a, b) => {
|
|
const dateA = new Date(a.createdAt).getTime()
|
|
const dateB = new Date(b.createdAt).getTime()
|
|
return sortDirection === "desc" ? dateB - dateA : dateA - dateB
|
|
})
|
|
}, [tickets, sortDirection])
|
|
|
|
const toggleSort = () => {
|
|
setSortDirection((prev) => (prev === "desc" ? "asc" : "desc"))
|
|
}
|
|
|
|
const formatDate = (dateString: string) => {
|
|
return new Date(dateString).toLocaleDateString("de-DE", {
|
|
year: "numeric",
|
|
month: "2-digit",
|
|
day: "2-digit",
|
|
hour: "2-digit",
|
|
minute: "2-digit"
|
|
})
|
|
}
|
|
|
|
if (tickets.length === 0) {
|
|
return (
|
|
<div className="flex flex-col items-center justify-center rounded-lg border border-dashed border-border p-12 text-center">
|
|
<FileText className="h-12 w-12 text-muted-foreground/50" />
|
|
<h3 className="mt-4 text-lg font-medium">No tickets found</h3>
|
|
<p className="mt-2 text-sm text-muted-foreground">
|
|
There are no tickets to display at this time.
|
|
</p>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="rounded-lg border border-border bg-card overflow-hidden">
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow className="bg-muted/50 hover:bg-muted/50">
|
|
<TableHead className="font-semibold">
|
|
<div className="flex items-center gap-2">
|
|
<MapPin className="h-4 w-4" />
|
|
Room
|
|
</div>
|
|
</TableHead>
|
|
<TableHead className="font-semibold">Title</TableHead>
|
|
<TableHead className="font-semibold hidden md:table-cell">Description</TableHead>
|
|
<TableHead className="font-semibold">
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={toggleSort}
|
|
className="flex items-center gap-1 -ml-2 h-auto p-2 hover:bg-transparent"
|
|
>
|
|
<Calendar className="h-4 w-4" />
|
|
Date
|
|
{sortDirection === "desc" ? (
|
|
<ArrowDown className="h-3 w-3" />
|
|
) : (
|
|
<ArrowUp className="h-3 w-3" />
|
|
)}
|
|
</Button>
|
|
</TableHead>
|
|
<TableHead className="font-semibold">Status</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{sortedTickets.map((ticket) => (
|
|
<TableRow key={ticket.id} className="group">
|
|
<TableCell className="font-medium">{ticket.room.name}</TableCell>
|
|
<TableCell className="font-medium">{ticket.title}</TableCell>
|
|
<TableCell className="hidden md:table-cell max-w-xs">
|
|
<p className="truncate text-muted-foreground">{ticket.description}</p>
|
|
</TableCell>
|
|
<TableCell className="text-muted-foreground">{formatDate(ticket.createdAt)}</TableCell>
|
|
<TableCell>
|
|
{showStatusUpdate && onStatusUpdate ? (
|
|
<Select
|
|
value={ticket.status}
|
|
onValueChange={(value: TicketStatus) => onStatusUpdate(ticket.id, value)}
|
|
>
|
|
<SelectTrigger className="w-32 h-8">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="OPEN">
|
|
<div className="flex items-center gap-2">
|
|
<span className="h-2 w-2 rounded-full bg-status-open" />
|
|
Offen
|
|
</div>
|
|
</SelectItem>
|
|
<SelectItem value="IN_PROGRESS">
|
|
<div className="flex items-center gap-2">
|
|
<span className="h-2 w-2 rounded-full bg-status-progress" />
|
|
In Bearbeitung
|
|
</div>
|
|
</SelectItem>
|
|
<SelectItem value="CLOSED">
|
|
<div className="flex items-center gap-2">
|
|
<span className="h-2 w-2 rounded-full bg-status-done" />
|
|
Erledigt
|
|
</div>
|
|
</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
) : (
|
|
<StatusBadge status={ticket.status} />
|
|
)}
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
)
|
|
}
|