feat: remove Administration workspace — keep only Verwaltung + Suche
- Delete all ops page components (ReviewQueue, AIMonitoring, Governance, SourceMonitoring, ActivityTimeline, SignalPipeline) - Remove OPERATIONS workspace from AppShell config, nav order, path detection - Remove all /ops/* routes from App.tsx - Remove WorkspaceType.OPERATIONS from allowedWorkspaces in authService, sessionStore, permissions - Keep MarketIntelligence page (already moved to /supply/market-intelligence) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
export { useProperties, useProperty, usePropertyDetail } from './useProperties'
|
||||
export { useMatches, useMatchesByNeed, useMatchesByProperty, useApproveMatch, useMatchDetail } from './useMatches'
|
||||
export { useFutureSignals, useFutureSignalsByProperty, useVerifySignal } from './useFutureSignals'
|
||||
export { useNeeds, useNeed, useNeedProfiles } from './useNeeds'
|
||||
export { useUnifiedResults } from './useUnifiedResults'
|
||||
export { useReviewQueue, useApproveReviewItem, useRejectReviewItem } from './useReviewQueue'
|
||||
@@ -0,0 +1,37 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { aiMonitoringService } from '../services/aiMonitoringService'
|
||||
import type { AIMonitoringFilters } from '../provider/IAIMonitoringProvider'
|
||||
import type { ReviewStatus } from '../domain/enums'
|
||||
|
||||
const QK = 'aiOutputs'
|
||||
const STALE = 30_000
|
||||
|
||||
export function useAIOutputs(filters?: AIMonitoringFilters) {
|
||||
return useQuery({
|
||||
queryKey: [QK, filters ?? {}],
|
||||
queryFn: () => aiMonitoringService.getOutputs(filters),
|
||||
staleTime: STALE,
|
||||
select: (res) => res.data ?? [],
|
||||
})
|
||||
}
|
||||
|
||||
export function useAIOutput(id: string | null) {
|
||||
return useQuery({
|
||||
queryKey: [QK, 'detail', id],
|
||||
queryFn: () => aiMonitoringService.getOutput(id!),
|
||||
staleTime: STALE,
|
||||
enabled: !!id,
|
||||
select: (res) => res.data ?? null,
|
||||
})
|
||||
}
|
||||
|
||||
export function useUpdateAIOutputReviewStatus() {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: ({ id, status }: { id: string; status: ReviewStatus }) =>
|
||||
aiMonitoringService.updateReviewStatus(id, status),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: [QK] })
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { sourceService } from '../services/sourceService'
|
||||
import type { SourceFilters, SourceStatus, TermsStatus } from '../domain/dataSource'
|
||||
|
||||
const STALE_SOURCES = 30_000
|
||||
|
||||
export function useDataSources(filters?: SourceFilters) {
|
||||
return useQuery({
|
||||
queryKey: ['data-sources', filters ?? {}],
|
||||
queryFn: () => sourceService.getSources(filters),
|
||||
staleTime: STALE_SOURCES,
|
||||
select: (res) => res.data ?? [],
|
||||
})
|
||||
}
|
||||
|
||||
export function useDataSource(id: string | null) {
|
||||
return useQuery({
|
||||
queryKey: ['data-source', id],
|
||||
queryFn: () => sourceService.getSource(id!),
|
||||
enabled: id !== null,
|
||||
staleTime: STALE_SOURCES,
|
||||
select: (res) => res.data ?? null,
|
||||
})
|
||||
}
|
||||
|
||||
export function useConnectorRuns(sourceId: string | null) {
|
||||
return useQuery({
|
||||
queryKey: ['connector-runs', sourceId],
|
||||
queryFn: () => sourceService.getConnectorRuns(sourceId!),
|
||||
enabled: sourceId !== null,
|
||||
staleTime: STALE_SOURCES,
|
||||
select: (res) => res.data ?? [],
|
||||
})
|
||||
}
|
||||
|
||||
export function useTriggerMockRun() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (sourceId: string) => sourceService.triggerMockRun(sourceId),
|
||||
onSuccess: (_data, sourceId) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['data-sources'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['data-source', sourceId] })
|
||||
queryClient.invalidateQueries({ queryKey: ['connector-runs', sourceId] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useUpdateSourceStatus() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: ({ id, status }: { id: string; status: SourceStatus }) =>
|
||||
sourceService.updateSourceStatus(id, status),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['data-sources'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['data-source'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useMarkTermsStatus() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: ({ id, termsStatus }: { id: string; termsStatus: TermsStatus }) =>
|
||||
sourceService.markTermsStatus(id, termsStatus),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['data-sources'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['data-source'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { futureSignalService } from '../services/futureSignalService'
|
||||
import type { ReviewStatus } from '../domain/enums'
|
||||
import { STALE_SIGNALS } from '../lib/constants'
|
||||
|
||||
export function useFutureSignals() {
|
||||
return useQuery({
|
||||
queryKey: ['futureSignals'],
|
||||
queryFn: () => futureSignalService.getAll(),
|
||||
staleTime: STALE_SIGNALS,
|
||||
select: (res) => res.data ?? [],
|
||||
})
|
||||
}
|
||||
|
||||
export function useFutureSignal(id: string) {
|
||||
return useQuery({
|
||||
queryKey: ['futureSignal', id],
|
||||
queryFn: () => futureSignalService.getById(id),
|
||||
staleTime: STALE_SIGNALS,
|
||||
enabled: !!id,
|
||||
select: (res) => res.data ?? null,
|
||||
})
|
||||
}
|
||||
|
||||
export function useFutureSignalsByProperty(propertyId: string) {
|
||||
return useQuery({
|
||||
queryKey: ['futureSignals', 'property', propertyId],
|
||||
queryFn: () => futureSignalService.getByProperty(propertyId),
|
||||
staleTime: STALE_SIGNALS,
|
||||
enabled: Boolean(propertyId),
|
||||
select: (res) => res.data ?? [],
|
||||
})
|
||||
}
|
||||
|
||||
export function useVerifySignal() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (signalId: string) => futureSignalService.verify(signalId, 'admin@ideal-sharing.ch'),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['futureSignals'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useUpdateSignalReviewStatus() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: ({ id, status }: { id: string; status: ReviewStatus }) =>
|
||||
futureSignalService.updateReviewStatus(id, status),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['futureSignals'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['futureSignal'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -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),
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { matchService } from '../services/matchService'
|
||||
import { STALE_MATCHES } from '../lib/constants'
|
||||
|
||||
export function useMatches() {
|
||||
return useQuery({
|
||||
queryKey: ['matches'],
|
||||
queryFn: () => matchService.getAll(),
|
||||
staleTime: STALE_MATCHES,
|
||||
select: (res) => res.data ?? [],
|
||||
})
|
||||
}
|
||||
|
||||
export function useMatchesByNeed(needId: string) {
|
||||
return useQuery({
|
||||
queryKey: ['matches', 'need', needId],
|
||||
queryFn: () => matchService.getByNeed(needId),
|
||||
staleTime: STALE_MATCHES,
|
||||
enabled: Boolean(needId),
|
||||
select: (res) => res.data ?? [],
|
||||
})
|
||||
}
|
||||
|
||||
export function useMatchesByProperty(propertyId: string) {
|
||||
return useQuery({
|
||||
queryKey: ['matches', 'property', propertyId],
|
||||
queryFn: () => matchService.getByProperty(propertyId),
|
||||
staleTime: STALE_MATCHES,
|
||||
enabled: Boolean(propertyId),
|
||||
select: (res) => res.data ?? [],
|
||||
})
|
||||
}
|
||||
|
||||
export function useApproveMatch() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (matchId: string) => matchService.approve(matchId, 'current-user'),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['matches'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useMatchDetail(id: string) {
|
||||
return useQuery({
|
||||
queryKey: ['match', id],
|
||||
queryFn: () => matchService.getById(id),
|
||||
staleTime: STALE_MATCHES,
|
||||
enabled: Boolean(id),
|
||||
select: (res) => res.data ?? null,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { needService } from '../services/needService'
|
||||
|
||||
export function useNeeds() {
|
||||
return useQuery({
|
||||
queryKey: ['needs'],
|
||||
queryFn: () => needService.getAll(),
|
||||
select: (res) => res.data ?? [],
|
||||
})
|
||||
}
|
||||
|
||||
export function useNeed(id: string) {
|
||||
return useQuery({
|
||||
queryKey: ['need', id],
|
||||
queryFn: () => needService.getById(id),
|
||||
enabled: Boolean(id),
|
||||
select: (res) => res.data ?? null,
|
||||
})
|
||||
}
|
||||
|
||||
export const useNeedProfiles = useNeeds
|
||||
@@ -0,0 +1,66 @@
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { propertyService } from '../services/propertyService'
|
||||
import { matchService } from '../services/matchService'
|
||||
import { futureSignalService } from '../services/futureSignalService'
|
||||
import type { AssetType, ResultType } from '../domain/enums'
|
||||
import { STALE_PROPERTIES, STALE_MATCHES, STALE_SIGNALS } from '../lib/constants'
|
||||
|
||||
interface PropertyFilter {
|
||||
assetType?: AssetType
|
||||
resultType?: ResultType
|
||||
city?: string
|
||||
minAreaSqm?: number
|
||||
maxRentPerSqm?: number
|
||||
organizationId?: string
|
||||
}
|
||||
|
||||
export function useProperties(filter?: PropertyFilter) {
|
||||
return useQuery({
|
||||
queryKey: ['properties', filter ?? {}],
|
||||
queryFn: () => propertyService.getAll(filter),
|
||||
staleTime: STALE_PROPERTIES,
|
||||
select: (res) => res.data ?? [],
|
||||
})
|
||||
}
|
||||
|
||||
export function useProperty(id: string) {
|
||||
return useQuery({
|
||||
queryKey: ['property', id],
|
||||
queryFn: () => propertyService.getById(id),
|
||||
staleTime: STALE_PROPERTIES,
|
||||
enabled: Boolean(id),
|
||||
select: (res) => res.data ?? null,
|
||||
})
|
||||
}
|
||||
|
||||
export const usePropertyDetail = useProperty
|
||||
|
||||
export function usePropertyById(id: string | null) {
|
||||
return useQuery({
|
||||
queryKey: ['property', id],
|
||||
queryFn: () => propertyService.getById(id!),
|
||||
enabled: !!id,
|
||||
staleTime: STALE_PROPERTIES,
|
||||
select: (res) => res.data ?? null,
|
||||
})
|
||||
}
|
||||
|
||||
export function usePropertyMatches(propertyId: string | null) {
|
||||
return useQuery({
|
||||
queryKey: ['property-matches', propertyId],
|
||||
queryFn: () => matchService.getMatchesForProperty(propertyId!),
|
||||
enabled: !!propertyId,
|
||||
staleTime: STALE_MATCHES,
|
||||
select: (res) => res.data ?? [],
|
||||
})
|
||||
}
|
||||
|
||||
export function usePropertySignals(propertyId: string | null) {
|
||||
return useQuery({
|
||||
queryKey: ['property-signals', propertyId],
|
||||
queryFn: () => futureSignalService.getSignalsForProperty(propertyId!),
|
||||
enabled: !!propertyId,
|
||||
staleTime: STALE_SIGNALS,
|
||||
select: (res) => res.data ?? [],
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { reviewService } from '../services/reviewService'
|
||||
import { useSessionStore } from '../stores/sessionStore'
|
||||
import type { ReviewFilters } from '../provider/IReviewProvider'
|
||||
import type { ReviewTaskStatus } from '../domain/review'
|
||||
|
||||
const STALE_REVIEW = 30_000
|
||||
const QK = 'reviewQueue'
|
||||
|
||||
export function useReviewQueue(filters?: ReviewFilters) {
|
||||
return useQuery({
|
||||
queryKey: [QK, filters ?? {}],
|
||||
queryFn: () => reviewService.getTasks(filters),
|
||||
staleTime: STALE_REVIEW,
|
||||
select: (res) => res.data ?? [],
|
||||
})
|
||||
}
|
||||
|
||||
export function useReviewTask(id: string | null) {
|
||||
return useQuery({
|
||||
queryKey: [QK, 'task', id],
|
||||
queryFn: () => reviewService.getTask(id!),
|
||||
staleTime: STALE_REVIEW,
|
||||
enabled: !!id,
|
||||
select: (res) => res.data ?? null,
|
||||
})
|
||||
}
|
||||
|
||||
export function useUpdateReviewStatus() {
|
||||
const queryClient = useQueryClient()
|
||||
const { currentUser } = useSessionStore.getState()
|
||||
const userId = currentUser?.email ?? 'unknown'
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, status, note }: { id: string; status: ReviewTaskStatus; note?: string }) =>
|
||||
reviewService.updateStatus(id, status, userId, note),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: [QK] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useAssignReviewTask() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: ({ id, assignTo }: { id: string; assignTo: string }) =>
|
||||
reviewService.assign(id, assignTo),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: [QK] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useAddReviewNote() {
|
||||
const queryClient = useQueryClient()
|
||||
const { currentUser } = useSessionStore.getState()
|
||||
const userId = currentUser?.email ?? 'unknown'
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, content }: { id: string; content: string }) =>
|
||||
reviewService.addNote(id, { content, createdBy: userId, createdAt: new Date().toISOString() }),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: [QK] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Legacy exports
|
||||
export function useApproveReviewItem() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: ({ id, notes }: { id: string; notes?: string }) =>
|
||||
reviewService.approve(id, 'current-user', notes),
|
||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: [QK] }),
|
||||
})
|
||||
}
|
||||
|
||||
export function useRejectReviewItem() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: ({ id, notes }: { id: string; notes?: string }) =>
|
||||
reviewService.reject(id, 'current-user', notes),
|
||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: [QK] }),
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { shortlistService } from '../services/shortlistService'
|
||||
import type { CreateShortlistInput, UpdateShortlistInput, ShortlistItemInput } from '../domain/shortlist'
|
||||
|
||||
export function useShortlists() {
|
||||
return useQuery({
|
||||
queryKey: ['shortlists'],
|
||||
queryFn: () => shortlistService.getAll(),
|
||||
select: (res) => res.data ?? [],
|
||||
})
|
||||
}
|
||||
|
||||
export function useShortlist(id: string) {
|
||||
return useQuery({
|
||||
queryKey: ['shortlist', id],
|
||||
queryFn: () => shortlistService.getById(id),
|
||||
enabled: !!id,
|
||||
select: (res) => res.data ?? null,
|
||||
})
|
||||
}
|
||||
|
||||
export function useCreateShortlist() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (input: CreateShortlistInput) => shortlistService.create(input),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['shortlists'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useAddToShortlist() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: ({ shortlistId, item }: { shortlistId: string; item: ShortlistItemInput }) =>
|
||||
shortlistService.addItem(shortlistId, item),
|
||||
onSuccess: (_data, { shortlistId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['shortlists'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['shortlist', shortlistId] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useRemoveFromShortlist() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: ({ shortlistId, resultId }: { shortlistId: string; resultId: string }) =>
|
||||
shortlistService.removeItem(shortlistId, resultId),
|
||||
onSuccess: (_data, { shortlistId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['shortlists'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['shortlist', shortlistId] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useUpdateShortlist() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: string; data: UpdateShortlistInput }) =>
|
||||
shortlistService.update(id, data),
|
||||
onSuccess: (_data, { id }) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['shortlists'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['shortlist', id] })
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { signalPipelineService } from '../services/signalPipelineService'
|
||||
import type { GateType } from '../domain/signalPipeline'
|
||||
|
||||
const STALE_PIPELINE = 15_000
|
||||
|
||||
export function useSignalPipelineState(signalId: string | null) {
|
||||
return useQuery({
|
||||
queryKey: ['signal-pipeline', signalId],
|
||||
queryFn: () => signalPipelineService.getPipelineState(signalId!),
|
||||
enabled: signalId !== null,
|
||||
staleTime: STALE_PIPELINE,
|
||||
select: (res) => res.data ?? null,
|
||||
})
|
||||
}
|
||||
|
||||
export function useSignalAuditTrail(signalId: string | null) {
|
||||
return useQuery({
|
||||
queryKey: ['signal-audit-trail', signalId],
|
||||
queryFn: () => signalPipelineService.getAuditTrail(signalId!),
|
||||
enabled: signalId !== null,
|
||||
staleTime: STALE_PIPELINE,
|
||||
select: (res) => res.data ?? [],
|
||||
})
|
||||
}
|
||||
|
||||
export function useEvaluateGate() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: ({ signalId, gateType }: { signalId: string; gateType: GateType }) =>
|
||||
signalPipelineService.evaluateGate(signalId, gateType),
|
||||
onSuccess: (_data, { signalId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['signal-pipeline', signalId] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function usePublishToFutureAvailability() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (signalId: string) => signalPipelineService.publishToFutureAvailability(signalId),
|
||||
onSuccess: (_data, signalId) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['signal-pipeline', signalId] })
|
||||
queryClient.invalidateQueries({ queryKey: ['signal-audit-trail', signalId] })
|
||||
queryClient.invalidateQueries({ queryKey: ['market-signals'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { dashboardService } from '../services/dashboardService'
|
||||
import type { DashboardData } from '../domain/dashboard'
|
||||
|
||||
export function useSupplyDashboard() {
|
||||
return useQuery<DashboardData>({
|
||||
queryKey: ['supply', 'dashboard'],
|
||||
queryFn: () => dashboardService.getDashboardData(),
|
||||
staleTime: 30_000,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import { useMemo } from 'react'
|
||||
import { useMatches, useMatchesByNeed } from './useMatches'
|
||||
import { useProperties } from './useProperties'
|
||||
import { useFutureSignals } from './useFutureSignals'
|
||||
import type {
|
||||
UnifiedMatchResult,
|
||||
VerifiedPortfolioResult,
|
||||
ExternalMarketResult,
|
||||
FutureAvailabilityResult,
|
||||
} from '../domain/unifiedResult'
|
||||
|
||||
export function useUnifiedResults(needId?: string) {
|
||||
const allMatchesQuery = useMatches()
|
||||
const needMatchesQuery = useMatchesByNeed(needId ?? '')
|
||||
const propertiesQuery = useProperties()
|
||||
const signalsQuery = useFutureSignals()
|
||||
|
||||
const matchesQuery = needId ? needMatchesQuery : allMatchesQuery
|
||||
|
||||
const isLoading =
|
||||
matchesQuery.isLoading || propertiesQuery.isLoading || signalsQuery.isLoading
|
||||
const error = matchesQuery.error ?? propertiesQuery.error ?? signalsQuery.error
|
||||
|
||||
const matches = matchesQuery.data ?? []
|
||||
const properties = propertiesQuery.data ?? []
|
||||
const signals = signalsQuery.data ?? []
|
||||
|
||||
const data = useMemo((): UnifiedMatchResult[] => {
|
||||
return matches
|
||||
.flatMap((match): UnifiedMatchResult[] => {
|
||||
const rt = match.resultType ?? 'VERIFIED_PORTFOLIO'
|
||||
|
||||
if (rt === 'FUTURE_AVAILABILITY') {
|
||||
const refId = match.resultId ?? match.propertyId
|
||||
const signal = signals.find(
|
||||
s => s.id === refId || s.propertyId === refId,
|
||||
)
|
||||
if (!signal) return []
|
||||
const result: FutureAvailabilityResult = {
|
||||
matchId: match.id,
|
||||
needId: match.needId,
|
||||
matchScore: match.matchScore,
|
||||
resultType: 'FUTURE_AVAILABILITY',
|
||||
signal,
|
||||
match,
|
||||
}
|
||||
return [result]
|
||||
}
|
||||
|
||||
const property = properties.find(p => p.id === (match.resultId ?? match.propertyId))
|
||||
if (!property) return []
|
||||
|
||||
if (rt === 'EXTERNAL_MARKET') {
|
||||
const result: ExternalMarketResult = {
|
||||
matchId: match.id,
|
||||
needId: match.needId,
|
||||
matchScore: match.matchScore,
|
||||
resultType: 'EXTERNAL_MARKET',
|
||||
property,
|
||||
match,
|
||||
}
|
||||
return [result]
|
||||
}
|
||||
|
||||
const result: VerifiedPortfolioResult = {
|
||||
matchId: match.id,
|
||||
needId: match.needId,
|
||||
matchScore: match.matchScore,
|
||||
resultType: 'VERIFIED_PORTFOLIO',
|
||||
property,
|
||||
match,
|
||||
}
|
||||
return [result]
|
||||
})
|
||||
.sort((a, b) => b.matchScore - a.matchScore)
|
||||
}, [matches, properties, signals])
|
||||
|
||||
return { data, isLoading, error }
|
||||
}
|
||||
Reference in New Issue
Block a user