feat: F016 future availability workspace

2-panel workspace at /supply/future-availability: filterable signal list with FutureSignalCard (type/sensitivity/review badges + mandatory disclaimer) and collapsible detail panel with full evidence, review workflow (IN_REVIEW→APPROVED/REJECTED), shortlist action, and review task creation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Sutter
2026-05-16 21:19:52 +02:00
parent 65130efca7
commit 2e68638246
13 changed files with 767 additions and 216 deletions
+5 -1
View File
@@ -1,11 +1,14 @@
import type { FutureSignal } from '../domain/futureSignal'
import type { SignalType } from '../domain/enums'
import type { SignalType, ReviewStatus } from '../domain/enums'
export interface FutureSignalFilters {
signalType?: SignalType
minProbability?: number
organizationId?: string
isVerified?: boolean
sensitivityLevel?: string
reviewStatus?: ReviewStatus
maxTimeHorizonMonths?: number
}
export interface IFutureSignalProvider {
@@ -13,4 +16,5 @@ export interface IFutureSignalProvider {
getById(id: string): Promise<FutureSignal | null>
getByProperty(propertyId: string): Promise<FutureSignal[]>
verify(id: string, verifiedBy: string): Promise<FutureSignal>
updateReviewStatus(id: string, status: ReviewStatus): Promise<FutureSignal>
}
@@ -1,5 +1,6 @@
import type { IFutureSignalProvider, FutureSignalFilters } from './IFutureSignalProvider'
import type { FutureSignal } from '../domain/futureSignal'
import type { ReviewStatus } from '../domain/enums'
import { mockFutureSignals } from '../mock-data/futureSignals'
const store: FutureSignal[] = [...mockFutureSignals]
@@ -11,6 +12,9 @@ export const MockupFutureSignalProvider: IFutureSignalProvider = {
if (filters?.minProbability !== undefined) results = results.filter(s => s.probability >= filters.minProbability!)
if (filters?.organizationId) results = results.filter(s => s.organizationId === filters.organizationId)
if (filters?.isVerified !== undefined) results = results.filter(s => s.isVerified === filters.isVerified)
if (filters?.sensitivityLevel) results = results.filter(s => s.sensitivityLevel === filters.sensitivityLevel)
if (filters?.reviewStatus) results = results.filter(s => (s.reviewStatus ?? 'UNREVIEWED') === filters.reviewStatus)
if (filters?.maxTimeHorizonMonths !== undefined) results = results.filter(s => s.timeHorizonMonths <= filters.maxTimeHorizonMonths!)
return results.sort((a, b) => b.probability - a.probability)
},
async getById(id) {
@@ -24,4 +28,9 @@ export const MockupFutureSignalProvider: IFutureSignalProvider = {
store[idx] = { ...store[idx], isVerified: true, verifiedBy, verifiedAt: new Date().toISOString(), updatedAt: new Date().toISOString() }
return store[idx]
},
async updateReviewStatus(id, status: ReviewStatus) {
const idx = store.findIndex(s => s.id === id)
store[idx] = { ...store[idx], reviewStatus: status, updatedAt: new Date().toISOString() }
return store[idx]
},
}