fix(tickets): fix permissions toast, board fallback, assignment search, remove load more
All checks were successful
Mirror to GitHub / mirror (push) Successful in 2s

- list_resources: return [] on PSAError instead of 502 — stops global interceptor
  toast when CW API key lacks ticket members permission (Lesson 111)
- list_boards/list_priorities: add warning logging so Railway logs reveal the
  root cause when CW permissions are missing
- TicketsPage: derive board options from ticket search results when listBoards
  returns empty (CW permissions fallback)
- TicketFilterBar: replace assignment <select> with searchable member picker —
  fixed options (All/Mine/Unassigned) + text-filtered member dropdown
- TicketQueue: remove Load More / infinite scroll; page now exists at /tickets

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-16 04:59:03 +00:00
parent 00cd8b7c55
commit 6044d5a88b
4 changed files with 96 additions and 66 deletions

View File

@@ -1,6 +1,6 @@
import { useState, useEffect, useRef, useCallback } from 'react'
import { useNavigate, Link } from 'react-router-dom'
import { Ticket, ChevronDown, Check, Loader2, AlertCircle } from 'lucide-react'
import { Ticket, ChevronDown, Check, AlertCircle } from 'lucide-react'
import { integrationsApi } from '@/api/integrations'
import type { PSABoard, PSATicketSearchResult } from '@/types/integrations'
import { cn } from '@/lib/utils'
@@ -193,10 +193,7 @@ export function TicketQueue() {
const [selectedBoardIds, setSelectedBoardIds] = useState<number[]>([])
const [activeTab, setActiveTab] = useState<Tab>('mine')
const [tickets, setTickets] = useState<PSATicketSearchResult[]>([])
const [page, setPage] = useState(1)
const [hasMore, setHasMore] = useState(false)
const [loading, setLoading] = useState(false)
const [loadingMore, setLoadingMore] = useState(false)
const [error, setError] = useState<string | null>(null)
// Check connection on mount
@@ -227,9 +224,9 @@ export function TicketQueue() {
}, [hasConnection])
const fetchTickets = useCallback(
async (tab: Tab, boardIds: number[], pageNum: number, append: boolean) => {
async (tab: Tab, boardIds: number[]) => {
const params: Parameters<typeof integrationsApi.searchTicketsQueue>[0] = {
page: pageNum,
page: 1,
page_size: PAGE_SIZE,
}
if (tab === 'mine') {
@@ -243,12 +240,7 @@ export function TicketQueue() {
try {
const results = await integrationsApi.searchTicketsQueue(params)
if (append) {
setTickets((prev) => [...prev, ...results.items])
} else {
setTickets(results.items)
}
setHasMore(results.items.length === PAGE_SIZE)
setTickets(results.items)
setError(null)
} catch {
setError('Failed to load tickets. Check your PSA connection.')
@@ -261,21 +253,11 @@ export function TicketQueue() {
useEffect(() => {
if (!hasConnection) return
if (activeTab === 'mine' && hasMemberMapping !== true) return
setPage(1)
setTickets([])
setHasMore(false)
setLoading(true)
fetchTickets(activeTab, selectedBoardIds, 1, false).finally(() => setLoading(false))
fetchTickets(activeTab, selectedBoardIds).finally(() => setLoading(false))
}, [activeTab, selectedBoardIds, hasConnection, hasMemberMapping, fetchTickets])
const handleLoadMore = async () => {
const nextPage = page + 1
setPage(nextPage)
setLoadingMore(true)
await fetchTickets(activeTab, selectedBoardIds, nextPage, true)
setLoadingMore(false)
}
const handleStartSession = (ticket: PSATicketSearchResult) => {
navigate('/pilot', {
state: {
@@ -368,7 +350,7 @@ export function TicketQueue() {
<TicketRow
key={ticket.id}
ticket={ticket}
isLast={i === tickets.length - 1 && !hasMore}
isLast={i === tickets.length - 1}
onStartSession={handleStartSession}
/>
))}
@@ -404,28 +386,6 @@ export function TicketQueue() {
</div>
)}
{/* Load more */}
{!error && !loading && hasMore && (
<div
className="px-5 py-3"
style={{ borderTop: '1px solid var(--color-border-default)' }}
>
<button
onClick={handleLoadMore}
disabled={loadingMore}
className="flex w-full items-center justify-center gap-2 rounded-lg border border-[rgba(255,255,255,0.08)] bg-transparent py-2 text-xs text-muted-foreground hover:text-foreground hover:border-[rgba(255,255,255,0.14)] disabled:opacity-50 transition-colors"
>
{loadingMore ? (
<>
<Loader2 size={12} className="animate-spin" />
Loading...
</>
) : (
'Load more'
)}
</button>
</div>
)}
</div>
</div>
)