5ef8b9d69d
New domain types: - shortlist.ts: Shortlist, ShortlistItem, CreateShortlistInput - review.ts: ReviewQueueItem, ReviewPriority, ReviewQueueStatus New mock data: - shortlists.ts: 2 sample shortlists - reviewQueue.ts: 3 pending review items New providers (interface + mockup): - IShortlistProvider / MockupShortlistProvider (with 150ms latency) - IReviewProvider / MockupReviewProvider (priority-sorted, with latency) - IDashboardProvider / MockupDashboardProvider (computed from all mock data) New services: - reviewService: getQueue, approve, reject, assign - shortlistService: CRUD + addItem/removeItem - authService: wraps sessionStore, swappable for real auth - dashboardService: getStats(organizationId?) Enhancements: - types.ts: typed ServiceErrorCode + ServiceError, Pagination alias - aiService.ts: openRouterAIService stub (throws ai_generation_failed) - lib/mockUtils.ts: mockDelay(ms) utility New hooks: - useUnifiedResults(needId?): joins matches+properties+signals → UnifiedMatchResult[] - useReviewQueue: + useApproveReviewItem + useRejectReviewItem - useMatchDetail, usePropertyDetail, useNeedProfiles aliases Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
49 lines
2.0 KiB
TypeScript
49 lines
2.0 KiB
TypeScript
import type { IDashboardProvider, DashboardStats } from './IDashboardProvider'
|
|
import { mockProperties } from '../mock-data/properties'
|
|
import { mockMatches } from '../mock-data/matches'
|
|
import { mockNeeds } from '../mock-data/needs'
|
|
import { mockFutureSignals } from '../mock-data/futureSignals'
|
|
import { mockReviewQueue } from '../mock-data/reviewQueue'
|
|
import { mockDelay } from '../lib/mockUtils'
|
|
|
|
export const MockupDashboardProvider: IDashboardProvider = {
|
|
async getStats(organizationId?) {
|
|
await mockDelay()
|
|
|
|
let props = mockProperties
|
|
let matches = mockMatches
|
|
let needs = mockNeeds
|
|
let signals = mockFutureSignals
|
|
let queue = mockReviewQueue
|
|
|
|
if (organizationId) {
|
|
props = props.filter(p => p.organizationId === organizationId)
|
|
matches = matches.filter(m => m.organizationId === organizationId)
|
|
needs = needs.filter(n => n.organizationId === organizationId)
|
|
signals = signals.filter(s => s.organizationId === organizationId)
|
|
queue = queue.filter(r => r.organizationId === organizationId)
|
|
}
|
|
|
|
const avgScore =
|
|
matches.length > 0
|
|
? matches.reduce((sum, m) => sum + m.matchScore, 0) / matches.length
|
|
: 0
|
|
|
|
const stats: DashboardStats = {
|
|
totalProperties: props.length,
|
|
verifiedProperties: props.filter(p => p.resultType === 'VERIFIED_PORTFOLIO').length,
|
|
externalMarketProperties: props.filter(p => p.resultType === 'EXTERNAL_MARKET').length,
|
|
futureSignalProperties: props.filter(p => p.resultType === 'FUTURE_AVAILABILITY').length,
|
|
totalMatches: matches.length,
|
|
pendingReviews: queue.filter(r => r.status === 'PENDING').length,
|
|
approvedMatches: matches.filter(m => m.isApproved === true).length,
|
|
activeNeeds: needs.length,
|
|
totalSignals: signals.length,
|
|
verifiedSignals: signals.filter(s => s.isVerified).length,
|
|
averageMatchScore: Math.round(avgScore),
|
|
highConfidenceMatches: matches.filter(m => m.confidenceLevel >= 0.75).length,
|
|
}
|
|
return stats
|
|
},
|
|
}
|