Files
TicketingSystem/client/src/pages/tickets/TicketListItem.tsx
T
josh 7f50783600 Split Tickets.tsx (631 lines) into focused sub-components
Extracted TicketFilters, BulkActions, and TicketListItem into
client/src/pages/tickets/. The main Tickets.tsx remains as the
page orchestrator with state management and pagination.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-21 20:44:21 -04:00

75 lines
2.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Link } from 'react-router-dom';
import { formatDistanceToNow } from 'date-fns';
import { SEVERITY_BG } from '../../lib/severityColors';
import SeverityBadge from '../../components/SeverityBadge';
import StatusBadge from '../../components/StatusBadge';
import Avatar from '../../components/Avatar';
import type { Ticket } from '../../types';
interface TicketListItemProps {
ticket: Ticket;
selected: boolean;
focused: boolean;
onToggle: () => void;
}
export default function TicketListItem({ ticket, selected, focused, onToggle }: TicketListItemProps) {
return (
<li
className={`flex items-center gap-3 px-4 py-3 transition-colors ${
focused
? 'bg-accent/50 ring-1 ring-inset ring-primary'
: 'hover:bg-accent/30'
}`}
>
<input
type="checkbox"
checked={selected}
onChange={onToggle}
aria-label={`Select ${ticket.displayId}`}
className="cursor-pointer flex-shrink-0"
/>
<div
className={`w-1 self-stretch rounded-full flex-shrink-0 ${SEVERITY_BG[ticket.severity] ?? 'bg-gray-600'}`}
/>
<Link
to={`/${ticket.displayId}`}
className="flex-1 min-w-0 flex items-center gap-3 group"
>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-0.5 flex-wrap">
<span className="text-sm font-medium text-foreground group-hover:text-primary truncate">
{ticket.title}
</span>
<span className="text-xs font-mono text-muted-foreground">
{ticket.displayId}
</span>
</div>
<div className="flex items-center gap-2 text-xs text-muted-foreground flex-wrap">
<SeverityBadge severity={ticket.severity} />
<StatusBadge status={ticket.status} />
<span>
opened {formatDistanceToNow(new Date(ticket.createdAt), {
addSuffix: true,
})}{' '}
by {ticket.createdBy.displayName}
</span>
<span className="hidden md:inline">
· {ticket.category.name} {ticket.type.name} {ticket.item.name}
</span>
{ticket.assignee && (
<span className="hidden md:inline">· assigned {ticket.assignee.displayName}</span>
)}
<span>· {ticket._count?.comments ?? 0} comments</span>
</div>
</div>
<div className="hidden sm:flex items-center flex-shrink-0">
{ticket.assignee ? (
<Avatar name={ticket.assignee.displayName} size="sm" />
) : null}
</div>
</Link>
</li>
);
}