add:table filters
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { Badge } from '../ui/Badge.tsx'
|
||||
import { Button } from '../ui/Button.tsx'
|
||||
import { parseDescription } from '../../lib/ticket.ts'
|
||||
import type { Ticket } from '../../lib/types.ts'
|
||||
|
||||
@@ -8,12 +9,28 @@ function formatDate(iso: string): string {
|
||||
})
|
||||
}
|
||||
|
||||
interface AdminTableProps {
|
||||
tickets: Ticket[]
|
||||
interface PaginationProps {
|
||||
page: number
|
||||
totalPages: number
|
||||
total: number
|
||||
pageSize: number
|
||||
onPrev: () => void
|
||||
onNext: () => void
|
||||
}
|
||||
|
||||
export function AdminTable({ tickets }: AdminTableProps) {
|
||||
if (tickets.length === 0) {
|
||||
interface AdminTableProps {
|
||||
tickets: Ticket[]
|
||||
onOpen: (ticket: Ticket) => void
|
||||
currentUserId: string | null
|
||||
pagination: PaginationProps
|
||||
}
|
||||
|
||||
export function AdminTable({ tickets, onOpen, currentUserId, pagination }: AdminTableProps) {
|
||||
const { page, totalPages, total, pageSize, onPrev, onNext } = pagination
|
||||
const start = (page - 1) * pageSize + 1
|
||||
const end = Math.min(page * pageSize, total)
|
||||
|
||||
if (tickets.length === 0 && total === 0) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center rounded-lg border border-border-100 bg-bg-200 py-16 text-center">
|
||||
<p className="text-sm text-fg-300">No tickets in the system.</p>
|
||||
@@ -28,7 +45,7 @@ export function AdminTable({ tickets }: AdminTableProps) {
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="border-b border-border-100 bg-bg-200">
|
||||
{(['Subject', 'Type', 'Status', ...(hasBilling ? ['Transaction'] : []), 'Description', 'Created'] as const).map(col => (
|
||||
{(['Subject', 'User', 'Type', 'Status', ...(hasBilling ? ['Transaction'] : []), 'Description', 'Created'] as const).map(col => (
|
||||
<th
|
||||
key={col}
|
||||
className="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider text-fg-300"
|
||||
@@ -36,6 +53,7 @@ export function AdminTable({ tickets }: AdminTableProps) {
|
||||
{col}
|
||||
</th>
|
||||
))}
|
||||
<th className="px-4 py-3" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-border-100 bg-bg-100">
|
||||
@@ -44,9 +62,23 @@ export function AdminTable({ tickets }: AdminTableProps) {
|
||||
const hasTxn = ticket.type === 'billing' && txnId !== null
|
||||
|
||||
return (
|
||||
<tr key={ticket.id} className="transition-colors hover:bg-bg-200">
|
||||
<tr
|
||||
key={ticket.id}
|
||||
className="transition-colors hover:bg-bg-200 cursor-pointer"
|
||||
onClick={() => onOpen(ticket)}
|
||||
>
|
||||
<td className="px-4 py-3 font-medium text-fg-100">
|
||||
{ticket.subject}
|
||||
<div className="flex items-center gap-2">
|
||||
{ticket.subject}
|
||||
{currentUserId && ticket.userId === currentUserId && (
|
||||
<span className="inline-flex items-center rounded-full border border-border-100 bg-bg-300 px-1.5 py-0.5 text-[10px] font-medium text-fg-300">
|
||||
mine
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-4 py-3 text-xs text-fg-200 whitespace-nowrap">
|
||||
{ticket.username ?? <span className="italic text-fg-300">guest</span>}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-xs capitalize text-fg-200">
|
||||
{ticket.type.replace('-', ' ')}
|
||||
@@ -73,11 +105,43 @@ export function AdminTable({ tickets }: AdminTableProps) {
|
||||
<td className="whitespace-nowrap px-4 py-3 text-xs text-fg-300">
|
||||
{formatDate(ticket.createdAt)}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-right">
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={e => { e.stopPropagation(); onOpen(ticket) }}
|
||||
>
|
||||
Open
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{/* Pagination footer */}
|
||||
<div className="flex items-center justify-between border-t border-border-100 bg-bg-200 px-4 py-3">
|
||||
<p className="text-xs text-fg-300">
|
||||
{total === 0 ? 'No tickets' : `${start}–${end} of ${total}`}
|
||||
</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button variant="ghost" onClick={onPrev} disabled={page <= 1}>
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" aria-hidden="true">
|
||||
<path d="M9 3L5 7l4 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
|
||||
</svg>
|
||||
Prev
|
||||
</Button>
|
||||
<span className="text-xs text-fg-300">
|
||||
{page} / {totalPages}
|
||||
</span>
|
||||
<Button variant="ghost" onClick={onNext} disabled={page >= totalPages}>
|
||||
Next
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" aria-hidden="true">
|
||||
<path d="M5 3l4 4-4 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
|
||||
</svg>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user