Phase 1b: React Query + Vitest on client
- @tanstack/react-query v5 with QueryClientProvider at app root - client/src/api/queries.ts: query-key factory, hooks for tickets, ticket, audit, comments, users, CTI tree + cascade, plus full mutation set (create/update/delete ticket, add/delete comment, CTI CRUD, user CRUD) - All page-level useEffect + useState fetching replaced: Dashboard, MyTickets, TicketDetail, NewTicket, admin/CTI, admin/Users - Dashboard preserves 300ms debounced search via separate debouncedSearch state - CTISelect cascades via useCategories / useTypes(categoryId) / useItems(typeId); dependent hooks disabled until parent selected - vitest + @testing-library/react + jsdom; 6 client tests cover SeverityBadge + StatusBadge Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,36 +1,29 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import api from '../api/client';
|
||||
import Layout from '../components/Layout';
|
||||
import SeverityBadge from '../components/SeverityBadge';
|
||||
import StatusBadge from '../components/StatusBadge';
|
||||
import { Ticket } from '../types';
|
||||
import { useTickets } from '../api/queries';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
|
||||
export default function MyTickets() {
|
||||
const { user } = useAuth();
|
||||
const [tickets, setTickets] = useState<Ticket[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const openQ = useTickets(user ? { assigneeId: user.id, status: 'OPEN' } : {});
|
||||
const inProgressQ = useTickets(user ? { assigneeId: user.id, status: 'IN_PROGRESS' } : {});
|
||||
|
||||
useEffect(() => {
|
||||
if (!user) return;
|
||||
// Only show active tickets — OPEN and IN_PROGRESS
|
||||
Promise.all([
|
||||
api.get<Ticket[]>('/tickets', { params: { assigneeId: user.id, status: 'OPEN' } }),
|
||||
api.get<Ticket[]>('/tickets', { params: { assigneeId: user.id, status: 'IN_PROGRESS' } }),
|
||||
])
|
||||
.then(([openRes, inProgressRes]) => {
|
||||
const combined = [...openRes.data, ...inProgressRes.data];
|
||||
combined.sort(
|
||||
(a, b) =>
|
||||
a.severity - b.severity ||
|
||||
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(),
|
||||
);
|
||||
setTickets(combined);
|
||||
})
|
||||
.finally(() => setLoading(false));
|
||||
}, [user]);
|
||||
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">
|
||||
|
||||
Reference in New Issue
Block a user