diff --git a/backend/app/schemas/psa_tickets.py b/backend/app/schemas/psa_tickets.py index 91a37d73..0bb1565d 100644 --- a/backend/app/schemas/psa_tickets.py +++ b/backend/app/schemas/psa_tickets.py @@ -24,6 +24,7 @@ class PSATicketStatusUpdateSchema(BaseModel): ticket_id: int previous_status: str new_status: str + new_status_id: int class TicketCreatePayloadSchema(BaseModel): diff --git a/backend/app/services/ticket_service.py b/backend/app/services/ticket_service.py index 66e9552e..542c0b46 100644 --- a/backend/app/services/ticket_service.py +++ b/backend/app/services/ticket_service.py @@ -87,6 +87,7 @@ async def update_status( ticket_id=ticket_id, previous_status=previous_status, new_status=new_status, + new_status_id=status_id, ) diff --git a/frontend/src/components/tickets/TicketDetailPanel.tsx b/frontend/src/components/tickets/TicketDetailPanel.tsx index 42125910..30736c80 100644 --- a/frontend/src/components/tickets/TicketDetailPanel.tsx +++ b/frontend/src/components/tickets/TicketDetailPanel.tsx @@ -16,7 +16,7 @@ import type { PSAResource } from '@/types/tickets' interface Props { ticket: PSATicketSearchResult onClose: () => void - onStatusUpdated?: (ticketId: number, newStatus: string) => void + onStatusUpdated?: (ticketId: number, newStatus: string, newStatusId: number) => void onSelectRelated?: (ticketId: number) => void } @@ -37,6 +37,11 @@ export function TicketDetailPanel({ ticket, onClose, onStatusUpdated, onSelectRe const [contextLoading, setContextLoading] = useState(true) const [resourcesLoading, setResourcesLoading] = useState(true) + // Local status state so the select reflects updates immediately, independent + // of the parent list's stale `selectedTicket` snapshot. + const [currentStatusId, setCurrentStatusId] = useState(ticket.status_id ?? null) + const [currentStatusName, setCurrentStatusName] = useState(ticket.status_name ?? null) + const ticketIdNum = Number(ticket.id) const loadResources = useCallback(() => { @@ -51,6 +56,8 @@ export function TicketDetailPanel({ ticket, onClose, onStatusUpdated, onSelectRe setContext(null) setResources([]) setStatuses([]) + setCurrentStatusId(ticket.status_id ?? null) + setCurrentStatusName(ticket.status_name ?? null) Promise.all([ psaContextApi.getTicketContext(ticketIdNum), @@ -69,10 +76,13 @@ export function TicketDetailPanel({ ticket, onClose, onStatusUpdated, onSelectRe setContextLoading(false) setResourcesLoading(false) }) + // eslint-disable-next-line react-hooks/exhaustive-deps }, [ticket.id, ticketIdNum]) - function handleStatusUpdated(ticketId: number, newStatus: string) { - onStatusUpdated?.(ticketId, newStatus) + function handleStatusUpdated(ticketId: number, newStatus: string, newStatusId: number) { + setCurrentStatusId(newStatusId) + setCurrentStatusName(newStatus) + onStatusUpdated?.(ticketId, newStatus, newStatusId) } return ( @@ -96,6 +106,8 @@ export function TicketDetailPanel({ ticket, onClose, onStatusUpdated, onSelectRe {/* Header with status selector — optimistic, no loading gate */} diff --git a/frontend/src/components/tickets/detail/TicketDetailHeader.tsx b/frontend/src/components/tickets/detail/TicketDetailHeader.tsx index 83c0c523..0916dff0 100644 --- a/frontend/src/components/tickets/detail/TicketDetailHeader.tsx +++ b/frontend/src/components/tickets/detail/TicketDetailHeader.tsx @@ -6,11 +6,13 @@ import type { PSATicketStatusUpdate } from '@/types/tickets' interface Props { ticket: PSATicketSearchResult + currentStatusId: number | null + currentStatusName: string | null statuses: PSATicketStatusItem[] - onStatusUpdated: (ticketId: number, newStatus: string) => void + onStatusUpdated: (ticketId: number, newStatus: string, newStatusId: number) => void } -export function TicketDetailHeader({ ticket, statuses, onStatusUpdated }: Props) { +export function TicketDetailHeader({ ticket, currentStatusId, currentStatusName, statuses, onStatusUpdated }: Props) { const [updating, setUpdating] = useState(false) async function handleStatusChange(statusId: number) { @@ -18,7 +20,7 @@ export function TicketDetailHeader({ ticket, statuses, onStatusUpdated }: Props) setUpdating(true) try { const result: PSATicketStatusUpdate = await ticketsApi.updateStatus(Number(ticket.id), statusId) - onStatusUpdated(result.ticket_id, result.new_status) + onStatusUpdated(result.ticket_id, result.new_status, result.new_status_id) toast.success(`Status updated to ${result.new_status}`) } catch { toast.error('Failed to update status') @@ -48,7 +50,7 @@ export function TicketDetailHeader({ ticket, statuses, onStatusUpdated }: Props) {statuses.length > 0 ? ( ) : ( - ticket.status_name && ( + currentStatusName && ( - {ticket.status_name} + {currentStatusName} ) )} diff --git a/frontend/src/pages/TicketsPage.tsx b/frontend/src/pages/TicketsPage.tsx index 52145ff2..256b0314 100644 --- a/frontend/src/pages/TicketsPage.tsx +++ b/frontend/src/pages/TicketsPage.tsx @@ -231,10 +231,15 @@ export default function TicketsPage() { setSelectedTicket(null)} - onStatusUpdated={(ticketId, newStatus) => { + onStatusUpdated={(ticketId, newStatus, newStatusId) => { setTickets(prev => prev.map(t => - t.id === String(ticketId) ? { ...t, status_name: newStatus } : t + t.id === String(ticketId) ? { ...t, status_name: newStatus, status_id: newStatusId } : t )) + setSelectedTicket(prev => + prev && prev.id === String(ticketId) + ? { ...prev, status_name: newStatus, status_id: newStatusId } + : prev + ) }} /> diff --git a/frontend/src/types/tickets.ts b/frontend/src/types/tickets.ts index 0ba6d49b..a3664ac7 100644 --- a/frontend/src/types/tickets.ts +++ b/frontend/src/types/tickets.ts @@ -63,6 +63,7 @@ export interface PSATicketStatusUpdate { ticket_id: number previous_status: string new_status: string + new_status_id: number } export interface TicketListResponse {