fix: harden ticket client data guards
This commit is contained in:
parent
5c5207ceb8
commit
a3d431efa8
9 changed files with 129 additions and 111 deletions
|
|
@ -48,12 +48,12 @@ export default function NewTicketPage() {
|
||||||
const queuesEnabled = Boolean(isStaff && convexUserId)
|
const queuesEnabled = Boolean(isStaff && convexUserId)
|
||||||
const queueArgs = queuesEnabled
|
const queueArgs = queuesEnabled
|
||||||
? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> }
|
? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> }
|
||||||
: "skip"
|
: undefined
|
||||||
const queuesRaw = useQuery(
|
const queuesRemote = useQuery(queuesEnabled ? api.queues.summary : "skip", queueArgs)
|
||||||
queuesEnabled ? api.queues.summary : "skip",
|
const queues = useMemo(
|
||||||
queueArgs
|
() => (Array.isArray(queuesRemote) ? (queuesRemote as TicketQueueSummary[]) : []),
|
||||||
) as TicketQueueSummary[] | undefined
|
[queuesRemote]
|
||||||
const queues = useMemo(() => queuesRaw ?? [], [queuesRaw])
|
)
|
||||||
const create = useMutation(api.tickets.create)
|
const create = useMutation(api.tickets.create)
|
||||||
const addComment = useMutation(api.tickets.addComment)
|
const addComment = useMutation(api.tickets.addComment)
|
||||||
const staffRaw = useQuery(api.users.listAgents, { tenantId: DEFAULT_TENANT_ID }) as Doc<"users">[] | undefined
|
const staffRaw = useQuery(api.users.listAgents, { tenantId: DEFAULT_TENANT_ID }) as Doc<"users">[] | undefined
|
||||||
|
|
@ -63,29 +63,34 @@ export default function NewTicketPage() {
|
||||||
)
|
)
|
||||||
|
|
||||||
const directoryQueryEnabled = queuesEnabled && Boolean(convexUserId)
|
const directoryQueryEnabled = queuesEnabled && Boolean(convexUserId)
|
||||||
const companiesRaw = useQuery(
|
const companiesArgs = directoryQueryEnabled
|
||||||
directoryQueryEnabled ? api.companies.list : "skip",
|
|
||||||
directoryQueryEnabled
|
|
||||||
? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> }
|
? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> }
|
||||||
: "skip"
|
: undefined
|
||||||
) as Array<{ id: string; name: string; slug?: string | null }> | undefined
|
const companiesRemote = useQuery(
|
||||||
|
directoryQueryEnabled ? api.companies.list : "skip",
|
||||||
|
companiesArgs
|
||||||
|
)
|
||||||
const companies = useMemo(
|
const companies = useMemo(
|
||||||
() =>
|
() =>
|
||||||
(companiesRaw ?? []).map((company) => ({
|
(Array.isArray(companiesRemote) ? companiesRemote : []).map((company) => ({
|
||||||
id: String(company.id),
|
id: String(company.id),
|
||||||
name: company.name,
|
name: company.name,
|
||||||
slug: company.slug ?? null,
|
slug: company.slug ?? null,
|
||||||
})),
|
})),
|
||||||
[companiesRaw]
|
[companiesRemote]
|
||||||
)
|
)
|
||||||
|
|
||||||
const customersRaw = useQuery(
|
const customersArgs = directoryQueryEnabled
|
||||||
directoryQueryEnabled ? api.users.listCustomers : "skip",
|
|
||||||
directoryQueryEnabled
|
|
||||||
? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> }
|
? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> }
|
||||||
: "skip"
|
: undefined
|
||||||
) as CustomerOption[] | undefined
|
const customersRemote = useQuery(
|
||||||
const customers = useMemo(() => customersRaw ?? [], [customersRaw])
|
directoryQueryEnabled ? api.users.listCustomers : "skip",
|
||||||
|
customersArgs
|
||||||
|
)
|
||||||
|
const customers = useMemo(
|
||||||
|
() => (Array.isArray(customersRemote) ? (customersRemote as CustomerOption[]) : []),
|
||||||
|
[customersRemote]
|
||||||
|
)
|
||||||
|
|
||||||
const [subject, setSubject] = useState("")
|
const [subject, setSubject] = useState("")
|
||||||
const [summary, setSummary] = useState("")
|
const [summary, setSummary] = useState("")
|
||||||
|
|
|
||||||
|
|
@ -132,12 +132,17 @@ export function CloseTicketDialog({
|
||||||
const addComment = useMutation(api.tickets.addComment)
|
const addComment = useMutation(api.tickets.addComment)
|
||||||
const adjustWorkSummary = useMutation(api.tickets.adjustWorkSummary)
|
const adjustWorkSummary = useMutation(api.tickets.adjustWorkSummary)
|
||||||
|
|
||||||
const closingTemplates = useQuery(
|
const closingTemplateArgs =
|
||||||
|
actorId && open ? { tenantId, viewerId: actorId, kind: "closing" as const } : undefined
|
||||||
|
const closingTemplatesRemote = useQuery(
|
||||||
actorId && open ? api.commentTemplates.list : "skip",
|
actorId && open ? api.commentTemplates.list : "skip",
|
||||||
actorId && open ? { tenantId, viewerId: actorId, kind: "closing" as const } : "skip"
|
closingTemplateArgs
|
||||||
) as { id: string; title: string; body: string }[] | undefined
|
)
|
||||||
|
const closingTemplates = Array.isArray(closingTemplatesRemote)
|
||||||
|
? (closingTemplatesRemote as { id: string; title: string; body: string }[])
|
||||||
|
: undefined
|
||||||
|
|
||||||
const templatesLoading = Boolean(actorId && open && closingTemplates === undefined)
|
const templatesLoading = Boolean(actorId && open && !Array.isArray(closingTemplatesRemote))
|
||||||
const templates = useMemo<ClosingTemplate[]>(() => {
|
const templates = useMemo<ClosingTemplate[]>(() => {
|
||||||
if (closingTemplates && closingTemplates.length > 0) {
|
if (closingTemplates && closingTemplates.length > 0) {
|
||||||
return closingTemplates.map((t) => ({ id: t.id, title: t.title, body: t.body }))
|
return closingTemplates.map((t) => ({ id: t.id, title: t.title, body: t.body }))
|
||||||
|
|
|
||||||
|
|
@ -128,14 +128,14 @@ export function NewTicketDialog({ triggerClassName }: { triggerClassName?: strin
|
||||||
const queuesEnabled = Boolean(isStaff && convexUserId)
|
const queuesEnabled = Boolean(isStaff && convexUserId)
|
||||||
const queueArgs = queuesEnabled
|
const queueArgs = queuesEnabled
|
||||||
? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> }
|
? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> }
|
||||||
: "skip"
|
: undefined
|
||||||
|
|
||||||
useDefaultQueues(DEFAULT_TENANT_ID)
|
useDefaultQueues(DEFAULT_TENANT_ID)
|
||||||
const queuesRaw = useQuery(
|
const queuesRemote = useQuery(queuesEnabled ? api.queues.summary : "skip", queueArgs)
|
||||||
queuesEnabled ? api.queues.summary : "skip",
|
const queues = useMemo(
|
||||||
queueArgs
|
() => (Array.isArray(queuesRemote) ? (queuesRemote as TicketQueueSummary[]) : []),
|
||||||
) as TicketQueueSummary[] | undefined
|
[queuesRemote]
|
||||||
const queues = useMemo(() => queuesRaw ?? [], [queuesRaw])
|
)
|
||||||
const create = useMutation(api.tickets.create)
|
const create = useMutation(api.tickets.create)
|
||||||
const addComment = useMutation(api.tickets.addComment)
|
const addComment = useMutation(api.tickets.addComment)
|
||||||
const staffRaw = useQuery(api.users.listAgents, { tenantId: DEFAULT_TENANT_ID }) as Doc<"users">[] | undefined
|
const staffRaw = useQuery(api.users.listAgents, { tenantId: DEFAULT_TENANT_ID }) as Doc<"users">[] | undefined
|
||||||
|
|
@ -145,29 +145,34 @@ export function NewTicketDialog({ triggerClassName }: { triggerClassName?: strin
|
||||||
)
|
)
|
||||||
|
|
||||||
const directoryQueryEnabled = queuesEnabled && Boolean(convexUserId)
|
const directoryQueryEnabled = queuesEnabled && Boolean(convexUserId)
|
||||||
const companiesRaw = useQuery(
|
const companiesArgs = directoryQueryEnabled
|
||||||
directoryQueryEnabled ? api.companies.list : "skip",
|
|
||||||
directoryQueryEnabled
|
|
||||||
? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> }
|
? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> }
|
||||||
: "skip"
|
: undefined
|
||||||
) as Array<{ id: string; name: string; slug?: string | null }> | undefined
|
const companiesRemote = useQuery(
|
||||||
|
directoryQueryEnabled ? api.companies.list : "skip",
|
||||||
|
companiesArgs
|
||||||
|
)
|
||||||
const companies = useMemo(
|
const companies = useMemo(
|
||||||
() =>
|
() =>
|
||||||
(companiesRaw ?? []).map((company) => ({
|
(Array.isArray(companiesRemote) ? companiesRemote : []).map((company) => ({
|
||||||
id: String(company.id),
|
id: String(company.id),
|
||||||
name: company.name,
|
name: company.name,
|
||||||
slug: company.slug ?? null,
|
slug: company.slug ?? null,
|
||||||
})),
|
})),
|
||||||
[companiesRaw]
|
[companiesRemote]
|
||||||
)
|
)
|
||||||
|
|
||||||
const customersRaw = useQuery(
|
const customersArgs = directoryQueryEnabled
|
||||||
directoryQueryEnabled ? api.users.listCustomers : "skip",
|
|
||||||
directoryQueryEnabled
|
|
||||||
? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> }
|
? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> }
|
||||||
: "skip"
|
: undefined
|
||||||
) as CustomerOption[] | undefined
|
const customersRemote = useQuery(
|
||||||
const customers = useMemo(() => customersRaw ?? [], [customersRaw])
|
directoryQueryEnabled ? api.users.listCustomers : "skip",
|
||||||
|
customersArgs
|
||||||
|
)
|
||||||
|
const customers = useMemo(
|
||||||
|
() => (Array.isArray(customersRemote) ? (customersRemote as CustomerOption[]) : []),
|
||||||
|
[customersRemote]
|
||||||
|
)
|
||||||
const [attachments, setAttachments] = useState<Array<{ storageId: string; name: string; size?: number; type?: string }>>([])
|
const [attachments, setAttachments] = useState<Array<{ storageId: string; name: string; size?: number; type?: string }>>([])
|
||||||
const [customersInitialized, setCustomersInitialized] = useState(false)
|
const [customersInitialized, setCustomersInitialized] = useState(false)
|
||||||
const attachmentsTotalBytes = useMemo(
|
const attachmentsTotalBytes = useMemo(
|
||||||
|
|
|
||||||
|
|
@ -32,12 +32,9 @@ export function PlayNextTicketCard({ context }: PlayNextTicketCardProps) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { convexUserId, isStaff } = useAuth()
|
const { convexUserId, isStaff } = useAuth()
|
||||||
const queuesEnabled = Boolean(isStaff && convexUserId)
|
const queuesEnabled = Boolean(isStaff && convexUserId)
|
||||||
const queueArgs = queuesEnabled
|
const queueArgs = queuesEnabled ? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> } : undefined
|
||||||
? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> }
|
const queueSummaryResult = useQuery(queuesEnabled ? api.queues.summary : "skip", queueArgs)
|
||||||
: "skip"
|
const queueSummary: TicketQueueSummary[] = Array.isArray(queueSummaryResult) ? queueSummaryResult : []
|
||||||
const queueSummary = (
|
|
||||||
useQuery(queuesEnabled ? api.queues.summary : "skip", queueArgs) as TicketQueueSummary[] | undefined
|
|
||||||
) ?? []
|
|
||||||
const playNext = useMutation(api.tickets.playNext)
|
const playNext = useMutation(api.tickets.playNext)
|
||||||
const [selectedQueueId, setSelectedQueueId] = useState<string | undefined>(undefined)
|
const [selectedQueueId, setSelectedQueueId] = useState<string | undefined>(undefined)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,15 +74,16 @@ function TicketRow({ ticket, entering }: { ticket: Ticket; entering: boolean })
|
||||||
|
|
||||||
export function RecentTicketsPanel() {
|
export function RecentTicketsPanel() {
|
||||||
const { convexUserId } = useAuth()
|
const { convexUserId } = useAuth()
|
||||||
const ticketsRaw = useQuery(
|
const ticketsArgs = convexUserId
|
||||||
api.tickets.list,
|
? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users">, limit: 12 }
|
||||||
convexUserId ? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users">, limit: 12 } : "skip"
|
: undefined
|
||||||
)
|
const ticketsResult = useQuery(convexUserId ? api.tickets.list : "skip", ticketsArgs)
|
||||||
const [enteringId, setEnteringId] = useState<string | null>(null)
|
const [enteringId, setEnteringId] = useState<string | null>(null)
|
||||||
const previousIdsRef = useRef<string[]>([])
|
const previousIdsRef = useRef<string[]>([])
|
||||||
|
|
||||||
const tickets = useMemo(() => {
|
const tickets = useMemo(() => {
|
||||||
const all = mapTicketsFromServerList((ticketsRaw ?? []) as unknown[]).filter((t) => t.status !== "RESOLVED")
|
if (!Array.isArray(ticketsResult)) return []
|
||||||
|
const all = mapTicketsFromServerList(ticketsResult as unknown[]).filter((t) => t.status !== "RESOLVED")
|
||||||
// Unassigned first (no assignee), oldest first among unassigned; then the rest by updatedAt desc
|
// Unassigned first (no assignee), oldest first among unassigned; then the rest by updatedAt desc
|
||||||
const unassigned = all
|
const unassigned = all
|
||||||
.filter((t) => !t.assignee)
|
.filter((t) => !t.assignee)
|
||||||
|
|
@ -91,10 +92,10 @@ export function RecentTicketsPanel() {
|
||||||
.filter((t) => !!t.assignee)
|
.filter((t) => !!t.assignee)
|
||||||
.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime())
|
.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime())
|
||||||
return [...unassigned, ...assigned].slice(0, 6)
|
return [...unassigned, ...assigned].slice(0, 6)
|
||||||
}, [ticketsRaw])
|
}, [ticketsResult])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (ticketsRaw === undefined) {
|
if (!Array.isArray(ticketsResult)) {
|
||||||
previousIdsRef.current = []
|
previousIdsRef.current = []
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -113,7 +114,7 @@ export function RecentTicketsPanel() {
|
||||||
setEnteringId(topId)
|
setEnteringId(topId)
|
||||||
}
|
}
|
||||||
previousIdsRef.current = ids
|
previousIdsRef.current = ids
|
||||||
}, [tickets, ticketsRaw])
|
}, [tickets, ticketsResult])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!enteringId) return
|
if (!enteringId) return
|
||||||
|
|
@ -121,7 +122,7 @@ export function RecentTicketsPanel() {
|
||||||
return () => window.clearTimeout(timer)
|
return () => window.clearTimeout(timer)
|
||||||
}, [enteringId])
|
}, [enteringId])
|
||||||
|
|
||||||
if (ticketsRaw === undefined) {
|
if (convexUserId && !Array.isArray(ticketsResult)) {
|
||||||
return (
|
return (
|
||||||
<Card className="rounded-2xl border border-slate-200 bg-white shadow-sm">
|
<Card className="rounded-2xl border border-slate-200 bg-white shadow-sm">
|
||||||
<CardHeader className="pb-2">
|
<CardHeader className="pb-2">
|
||||||
|
|
|
||||||
|
|
@ -70,14 +70,15 @@ export function TicketComments({ ticket }: TicketCommentsProps) {
|
||||||
const [localBodies, setLocalBodies] = useState<Record<string, string>>({})
|
const [localBodies, setLocalBodies] = useState<Record<string, string>>({})
|
||||||
const [commentsOrder, setCommentsOrder] = useState<CommentsOrder>("descending")
|
const [commentsOrder, setCommentsOrder] = useState<CommentsOrder>("descending")
|
||||||
|
|
||||||
const templateArgs = convexUserId && isStaff
|
const templateArgs =
|
||||||
|
convexUserId && isStaff
|
||||||
? { tenantId: ticket.tenantId, viewerId: convexUserId as Id<"users">, kind: "comment" as const }
|
? { tenantId: ticket.tenantId, viewerId: convexUserId as Id<"users">, kind: "comment" as const }
|
||||||
: "skip"
|
: undefined
|
||||||
const templatesResult = useQuery(convexUserId && isStaff ? api.commentTemplates.list : "skip", templateArgs) as
|
const templatesResult = useQuery(convexUserId && isStaff ? api.commentTemplates.list : "skip", templateArgs)
|
||||||
| { id: string; title: string; body: string }[]
|
const templates = Array.isArray(templatesResult)
|
||||||
| undefined
|
? (templatesResult as { id: string; title: string; body: string }[])
|
||||||
const templates = templatesResult ?? []
|
: []
|
||||||
const templatesLoading = Boolean(convexUserId && isStaff) && templatesResult === undefined
|
const templatesLoading = Boolean(convexUserId && isStaff) && !Array.isArray(templatesResult)
|
||||||
const canUseTemplates = Boolean(convexUserId && isStaff)
|
const canUseTemplates = Boolean(convexUserId && isStaff)
|
||||||
|
|
||||||
const insertTemplateIntoBody = (html: string) => {
|
const insertTemplateIntoBody = (html: string) => {
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,10 @@ interface TicketQueueSummaryProps {
|
||||||
export function TicketQueueSummaryCards({ queues }: TicketQueueSummaryProps) {
|
export function TicketQueueSummaryCards({ queues }: TicketQueueSummaryProps) {
|
||||||
const { convexUserId, isStaff } = useAuth()
|
const { convexUserId, isStaff } = useAuth()
|
||||||
const enabled = Boolean(isStaff && convexUserId)
|
const enabled = Boolean(isStaff && convexUserId)
|
||||||
const queueArgs = enabled
|
const queueArgs = enabled ? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> } : undefined
|
||||||
? { tenantId: DEFAULT_TENANT_ID, viewerId: convexUserId as Id<"users"> }
|
|
||||||
: "skip"
|
|
||||||
const fromServer = useQuery(enabled ? api.queues.summary : "skip", queueArgs)
|
const fromServer = useQuery(enabled ? api.queues.summary : "skip", queueArgs)
|
||||||
const data: TicketQueueSummary[] = (queues ?? (fromServer as TicketQueueSummary[] | undefined) ?? [])
|
const serverData = Array.isArray(fromServer) ? fromServer : undefined
|
||||||
|
const data: TicketQueueSummary[] = queues ?? serverData ?? []
|
||||||
|
|
||||||
if (!queues && fromServer === undefined) {
|
if (!queues && fromServer === undefined) {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -167,42 +167,47 @@ export function TicketSummaryHeader({ ticket }: TicketHeaderProps) {
|
||||||
const updateCategories = useMutation(api.tickets.updateCategories)
|
const updateCategories = useMutation(api.tickets.updateCategories)
|
||||||
const agents = (useQuery(api.users.listAgents, { tenantId: ticket.tenantId }) as Doc<"users">[] | undefined) ?? []
|
const agents = (useQuery(api.users.listAgents, { tenantId: ticket.tenantId }) as Doc<"users">[] | undefined) ?? []
|
||||||
const queuesEnabled = Boolean(isStaff && convexUserId)
|
const queuesEnabled = Boolean(isStaff && convexUserId)
|
||||||
const companiesRaw = useQuery(
|
const companiesArgs = convexUserId
|
||||||
convexUserId ? api.companies.list : "skip",
|
|
||||||
convexUserId
|
|
||||||
? { tenantId: ticket.tenantId, viewerId: convexUserId as Id<"users"> }
|
? { tenantId: ticket.tenantId, viewerId: convexUserId as Id<"users"> }
|
||||||
: "skip"
|
: undefined
|
||||||
) as Array<{ id: string; name: string; slug?: string | null }> | undefined
|
const companiesRemote = useQuery(
|
||||||
|
convexUserId ? api.companies.list : "skip",
|
||||||
|
companiesArgs
|
||||||
|
)
|
||||||
const companies = useMemo(
|
const companies = useMemo(
|
||||||
() =>
|
() =>
|
||||||
(companiesRaw ?? []).map((company) => ({
|
(Array.isArray(companiesRemote) ? companiesRemote : []).map((company) => ({
|
||||||
id: String(company.id),
|
id: String(company.id),
|
||||||
name: company.name,
|
name: company.name,
|
||||||
slug: company.slug ?? null,
|
slug: company.slug ?? null,
|
||||||
})),
|
})),
|
||||||
[companiesRaw]
|
[companiesRemote]
|
||||||
)
|
)
|
||||||
|
|
||||||
const customersRaw = useQuery(
|
const customersArgs = convexUserId
|
||||||
convexUserId ? api.users.listCustomers : "skip",
|
|
||||||
convexUserId
|
|
||||||
? { tenantId: ticket.tenantId, viewerId: convexUserId as Id<"users"> }
|
? { tenantId: ticket.tenantId, viewerId: convexUserId as Id<"users"> }
|
||||||
: "skip"
|
: undefined
|
||||||
) as CustomerOption[] | undefined
|
const customersRemote = useQuery(
|
||||||
const customers = useMemo(() => customersRaw ?? [], [customersRaw])
|
convexUserId ? api.users.listCustomers : "skip",
|
||||||
|
customersArgs
|
||||||
|
)
|
||||||
|
const customers = useMemo(
|
||||||
|
() => (Array.isArray(customersRemote) ? (customersRemote as CustomerOption[]) : []),
|
||||||
|
[customersRemote]
|
||||||
|
)
|
||||||
|
|
||||||
const queueArgs = queuesEnabled
|
const queueArgs = queuesEnabled
|
||||||
? { tenantId: ticket.tenantId, viewerId: convexUserId as Id<"users"> }
|
? { tenantId: ticket.tenantId, viewerId: convexUserId as Id<"users"> }
|
||||||
: "skip"
|
: undefined
|
||||||
const queues = (
|
const queuesResult = useQuery(queuesEnabled ? api.queues.summary : "skip", queueArgs)
|
||||||
useQuery(queuesEnabled ? api.queues.summary : "skip", queueArgs) as TicketQueueSummary[] | undefined
|
const queues: TicketQueueSummary[] = Array.isArray(queuesResult) ? queuesResult : []
|
||||||
) ?? []
|
|
||||||
const { categories, isLoading: categoriesLoading } = useTicketCategories(ticket.tenantId)
|
const { categories, isLoading: categoriesLoading } = useTicketCategories(ticket.tenantId)
|
||||||
const workSummaryRemote = useQuery(
|
const workSummaryArgs = convexUserId
|
||||||
api.tickets.workSummary,
|
|
||||||
convexUserId
|
|
||||||
? { ticketId: ticket.id as Id<"tickets">, viewerId: convexUserId as Id<"users"> }
|
? { ticketId: ticket.id as Id<"tickets">, viewerId: convexUserId as Id<"users"> }
|
||||||
: "skip"
|
: undefined
|
||||||
|
const workSummaryRemote = useQuery(
|
||||||
|
convexUserId ? api.tickets.workSummary : "skip",
|
||||||
|
workSummaryArgs
|
||||||
) as
|
) as
|
||||||
| {
|
| {
|
||||||
ticketId: Id<"tickets">
|
ticketId: Id<"tickets">
|
||||||
|
|
|
||||||
|
|
@ -65,14 +65,11 @@ export function TicketsView({ initialFilters }: TicketsViewProps = {}) {
|
||||||
useDefaultQueues(tenantId)
|
useDefaultQueues(tenantId)
|
||||||
|
|
||||||
const queuesEnabled = Boolean(isStaff && convexUserId)
|
const queuesEnabled = Boolean(isStaff && convexUserId)
|
||||||
const queues = useQuery(
|
const queueArgs = queuesEnabled ? { tenantId, viewerId: convexUserId as Id<"users"> } : undefined
|
||||||
queuesEnabled ? api.queues.summary : "skip",
|
const queuesResult = useQuery(queuesEnabled ? api.queues.summary : "skip", queueArgs)
|
||||||
queuesEnabled ? { tenantId, viewerId: convexUserId as Id<"users"> } : "skip"
|
const queues: TicketQueueSummary[] = Array.isArray(queuesResult) ? queuesResult : []
|
||||||
) as TicketQueueSummary[] | undefined
|
|
||||||
const agents = useQuery(api.users.listAgents, { tenantId }) as { _id: string; name: string }[] | undefined
|
const agents = useQuery(api.users.listAgents, { tenantId }) as { _id: string; name: string }[] | undefined
|
||||||
const ticketsRaw = useQuery(
|
const ticketsArgs = convexUserId
|
||||||
api.tickets.list,
|
|
||||||
convexUserId
|
|
||||||
? {
|
? {
|
||||||
tenantId,
|
tenantId,
|
||||||
viewerId: convexUserId as Id<"users">,
|
viewerId: convexUserId as Id<"users">,
|
||||||
|
|
@ -83,10 +80,13 @@ export function TicketsView({ initialFilters }: TicketsViewProps = {}) {
|
||||||
assigneeId: filters.assigneeId ? (filters.assigneeId as unknown as Id<"users">) : undefined,
|
assigneeId: filters.assigneeId ? (filters.assigneeId as unknown as Id<"users">) : undefined,
|
||||||
search: filters.search || undefined,
|
search: filters.search || undefined,
|
||||||
}
|
}
|
||||||
: "skip"
|
: undefined
|
||||||
)
|
const ticketsRaw = useQuery(convexUserId ? api.tickets.list : "skip", ticketsArgs)
|
||||||
|
|
||||||
const tickets = useMemo(() => mapTicketsFromServerList((ticketsRaw ?? []) as unknown[]), [ticketsRaw])
|
const tickets = useMemo(
|
||||||
|
() => mapTicketsFromServerList(Array.isArray(ticketsRaw) ? (ticketsRaw as unknown[]) : []),
|
||||||
|
[ticketsRaw]
|
||||||
|
)
|
||||||
const [companies, setCompanies] = useState<string[]>([])
|
const [companies, setCompanies] = useState<string[]>([])
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let aborted = false
|
let aborted = false
|
||||||
|
|
@ -190,7 +190,7 @@ export function TicketsView({ initialFilters }: TicketsViewProps = {}) {
|
||||||
<div className="flex flex-col gap-6 px-4 lg:px-6">
|
<div className="flex flex-col gap-6 px-4 lg:px-6">
|
||||||
<TicketsFilters
|
<TicketsFilters
|
||||||
onChange={setFilters}
|
onChange={setFilters}
|
||||||
queues={(queues ?? []).map((q) => q.name)}
|
queues={queues.map((q) => q.name)}
|
||||||
companies={companies}
|
companies={companies}
|
||||||
assignees={(agents ?? []).map((a) => ({ id: a._id, name: a.name }))}
|
assignees={(agents ?? []).map((a) => ({ id: a._id, name: a.name }))}
|
||||||
initialState={mergedInitialFilters}
|
initialState={mergedInitialFilters}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue