feat: F021 market intelligence & signal discovery
Signal domain types, mock data (8 signals), provider interface + MockupMarketIntelligenceProvider, marketIntelligenceService, TanStack Query hooks, 10 ops components (SignalInbox, MarketSignalCard, MarketSignalFilterBar, MarketSignalSkeleton, MarketSignalEmptyState, SignalConfidenceBadge, SourceReliabilityBadge, SensitivityWarningPanel, SignalConversionPanel, SignalEvidenceList), MarketIntelligence page wired into AppShell nav and App router. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { marketIntelligenceService } from '../services/marketIntelligenceService'
|
||||
import { reviewService } from '../services/reviewService'
|
||||
import type { MarketSignalFilters, SignalProcessingStatus } from '../domain/marketSignal'
|
||||
|
||||
const STALE_SIGNALS = 30_000
|
||||
|
||||
export function useMarketSignals(filters?: MarketSignalFilters) {
|
||||
return useQuery({
|
||||
queryKey: ['market-signals', filters ?? {}],
|
||||
queryFn: () => marketIntelligenceService.getSignals(filters),
|
||||
staleTime: STALE_SIGNALS,
|
||||
select: (res) => res.data ?? [],
|
||||
})
|
||||
}
|
||||
|
||||
export function useMarketSignalDetail(id: string | null) {
|
||||
return useQuery({
|
||||
queryKey: ['market-signal', id],
|
||||
queryFn: () => marketIntelligenceService.getSignalDetail(id!),
|
||||
enabled: id !== null,
|
||||
staleTime: STALE_SIGNALS,
|
||||
select: (res) => res.data ?? null,
|
||||
})
|
||||
}
|
||||
|
||||
export function useUpdateSignalStatus() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: ({ id, status }: { id: string; status: SignalProcessingStatus }) =>
|
||||
marketIntelligenceService.updateSignalStatus(id, status),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['market-signals'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['market-signal'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useConvertToFutureSignal() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => marketIntelligenceService.convertToFutureSignal(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['market-signals'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['market-signal'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useLinkSignalToEntity() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
id,
|
||||
entityType,
|
||||
entityId,
|
||||
}: {
|
||||
id: string
|
||||
entityType: 'property' | 'need'
|
||||
entityId: string
|
||||
}) => marketIntelligenceService.linkSignalToEntity(id, entityType, entityId),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['market-signals'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['market-signal'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useCreateReviewTask() {
|
||||
return useMutation({
|
||||
mutationFn: (signalId: string) => reviewService.createReviewTask(signalId),
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user