104f7773ba
Severity-to-color mapping was duplicated in SeverityBadge, Tickets, and MyTickets. Consolidated into lib/severityColors.ts with both solid-bg (for stripes) and badge-style (for badges) variants. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
80 lines
3.1 KiB
TypeScript
80 lines
3.1 KiB
TypeScript
import { useMemo } from 'react';
|
||
import { Link } from 'react-router-dom';
|
||
import { formatDistanceToNow } from 'date-fns';
|
||
import Layout from '../components/Layout';
|
||
import SeverityBadge from '../components/SeverityBadge';
|
||
import StatusBadge from '../components/StatusBadge';
|
||
import { useTickets } from '../api/queries';
|
||
import { useAuth } from '../contexts/AuthContext';
|
||
import { SEVERITY_BG } from '../lib/severityColors';
|
||
|
||
export default function MyTickets() {
|
||
const { user } = useAuth();
|
||
const openQ = useTickets(user ? { assigneeId: user.id, status: 'OPEN' } : {});
|
||
const inProgressQ = useTickets(user ? { assigneeId: user.id, status: 'IN_PROGRESS' } : {});
|
||
|
||
const loading = openQ.isLoading || inProgressQ.isLoading;
|
||
|
||
const tickets = useMemo(() => {
|
||
if (!user) return [];
|
||
const combined = [...(openQ.data ?? []), ...(inProgressQ.data ?? [])];
|
||
combined.sort(
|
||
(a, b) =>
|
||
a.severity - b.severity ||
|
||
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(),
|
||
);
|
||
return combined;
|
||
}, [user, openQ.data, inProgressQ.data]);
|
||
|
||
return (
|
||
<Layout title="My Tickets">
|
||
{loading ? (
|
||
<div className="text-center py-16 text-gray-600 text-sm">Loading...</div>
|
||
) : tickets.length === 0 ? (
|
||
<div className="text-center py-16 text-gray-600 text-sm">
|
||
No active tickets assigned to you
|
||
</div>
|
||
) : (
|
||
<div className="space-y-1.5">
|
||
{tickets.map((ticket) => (
|
||
<Link
|
||
key={ticket.id}
|
||
to={`/${ticket.displayId}`}
|
||
className="flex items-center gap-4 bg-gray-900 border border-gray-800 rounded-lg px-4 py-3 hover:border-indigo-500/50 transition-all group"
|
||
>
|
||
<div
|
||
className={`w-1 self-stretch rounded-full flex-shrink-0 ${SEVERITY_BG[ticket.severity] ?? 'bg-gray-600'}`}
|
||
/>
|
||
|
||
<div className="flex-1 min-w-0">
|
||
<div className="flex items-center gap-2 mb-0.5 flex-wrap">
|
||
<span className="text-xs font-mono font-medium text-gray-600">
|
||
{ticket.displayId}
|
||
</span>
|
||
<SeverityBadge severity={ticket.severity} />
|
||
<StatusBadge status={ticket.status} />
|
||
<span className="text-xs text-gray-600">
|
||
{ticket.category.name} › {ticket.type.name} › {ticket.item.name}
|
||
</span>
|
||
</div>
|
||
<p className="text-sm font-medium text-gray-200 truncate group-hover:text-indigo-400">
|
||
{ticket.title}
|
||
</p>
|
||
</div>
|
||
|
||
<div className="flex items-center gap-3 flex-shrink-0">
|
||
<span className="text-xs text-gray-600">
|
||
{ticket._count?.comments ?? 0} comments
|
||
</span>
|
||
<span className="text-xs text-gray-600">
|
||
{formatDistanceToNow(new Date(ticket.createdAt), { addSuffix: true })}
|
||
</span>
|
||
</div>
|
||
</Link>
|
||
))}
|
||
</div>
|
||
)}
|
||
</Layout>
|
||
);
|
||
}
|