Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / frontend/src/tools/slatematch/scopeQueries.ts |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 720d47fae476b0d38477ae4401697d372b1ea2bc |
| Branch | feature/opportunity-to-outcome |
| Path or archive member | frontend/src/tools/slatematch/scopeQueries.ts |
| Original SHA-256 | 67f848a0ad2df78627de2a53dca5d2c553cf913b6b8f0e9c5b4954cfc27e7739 |
| Git blob SHA | 91ea6226aabf25526f8bfb65be231b6904185a0d |
| Reading-copy SHA-256 | 67f848a0ad2df78627de2a53dca5d2c553cf913b6b8f0e9c5b4954cfc27e7739 |
Recovered source transcript
Download complete source text ↓122 lines · 4.2 KB
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { scopeApi, uploadDocument } from './scopeApi'
// Extraction runs OUT OF BAND, in cmd/opportunity-extract on a schedule, so a document sits at 'pending' for
// a while after a successful upload. The documents query therefore polls while anything is still pending and
// stops when nothing is, which is the difference between a screen that shows extraction finishing and one a
// user has to reload to believe.
const PENDING = new Set(['pending', 'running'])
export function useDocuments(oppId: string) {
return useQuery({
queryKey: ['slatematch', 'documents', oppId],
queryFn: () => scopeApi.documents(oppId),
enabled: !!oppId,
refetchInterval: (query) => {
const docs = query.state.data?.documents ?? []
return docs.some((d) => PENDING.has(d.extraction_status)) ? 4000 : false
},
})
}
export function useScope(oppId: string, status?: string) {
return useQuery({
queryKey: ['slatematch', 'scope', oppId, status ?? ''],
queryFn: () => scopeApi.scope(oppId, status),
enabled: !!oppId,
})
}
export function useDrafts(oppId: string) {
return useQuery({
queryKey: ['slatematch', 'drafts', oppId],
queryFn: () => scopeApi.drafts(oppId),
enabled: !!oppId,
})
}
// A review action can change the scope list AND the drafts derived from it, so both namespaces are
// invalidated. Being conservative here is cheap; a stale review queue after an accept is not.
function useInvalidateScope(oppId: string) {
const qc = useQueryClient()
return () => {
void qc.invalidateQueries({ queryKey: ['slatematch', 'scope', oppId] })
void qc.invalidateQueries({ queryKey: ['slatematch', 'drafts', oppId] })
}
}
export function useReviewScopeItem(oppId: string) {
const inv = useInvalidateScope(oppId)
return useMutation({
mutationFn: (v: { itemId: string; patch: Record<string, unknown> }) =>
scopeApi.review(oppId, v.itemId, v.patch),
onSuccess: () => inv(),
})
}
export function useAddManualScope(oppId: string) {
const inv = useInvalidateScope(oppId)
return useMutation({
mutationFn: (v: { scope_type: string; title: string; description: string }) => scopeApi.addManual(oppId, v),
onSuccess: () => inv(),
})
}
export function useSplitScopeItem(oppId: string) {
const inv = useInvalidateScope(oppId)
return useMutation({
mutationFn: (v: { itemId: string; parts: Array<{ scope_type?: string; title: string; description?: string }> }) =>
scopeApi.split(oppId, v.itemId, v.parts),
onSuccess: () => inv(),
})
}
export function useMergeScopeItems(oppId: string) {
const inv = useInvalidateScope(oppId)
return useMutation({
mutationFn: (v: { itemId: string; absorb_item_ids: string[]; title: string; description?: string }) =>
scopeApi.merge(oppId, v.itemId, {
absorb_item_ids: v.absorb_item_ids,
title: v.title,
description: v.description,
}),
onSuccess: () => inv(),
})
}
export function useGenerateDrafts(oppId: string) {
const inv = useInvalidateScope(oppId)
return useMutation({ mutationFn: () => scopeApi.generateDrafts(oppId), onSuccess: () => inv() })
}
export function usePatchDraft(oppId: string) {
const inv = useInvalidateScope(oppId)
return useMutation({
mutationFn: (v: { draftId: string; patch: Record<string, unknown> }) => scopeApi.patchDraft(v.draftId, v.patch),
onSuccess: () => inv(),
})
}
export function useApproveDraft(oppId: string) {
const inv = useInvalidateScope(oppId)
return useMutation({ mutationFn: (draftId: string) => scopeApi.approveDraft(draftId), onSuccess: () => inv() })
}
export function useRejectDraft(oppId: string) {
const inv = useInvalidateScope(oppId)
return useMutation({
mutationFn: (v: { draftId: string; reason: string }) => scopeApi.rejectDraft(v.draftId, v.reason),
onSuccess: () => inv(),
})
}
export function useUploadDocument(oppId: string) {
const qc = useQueryClient()
return useMutation({
mutationFn: (v: { file: File; documentType: string; onProgress: (p: number) => void }) =>
uploadDocument(oppId, v.file, v.documentType, v.onProgress),
onSuccess: () => void qc.invalidateQueries({ queryKey: ['slatematch', 'documents', oppId] }),
})
}