feat: F007 property repository & detail view

- useProperties: added usePropertyById, usePropertyMatches, usePropertySignals hooks
- propertyService.getProperties(), matchService.getMatchesForProperty(),
  futureSignalService.getSignalsForProperty() added
- PropertyFilterBar: asset type chips, availability select, sort select, search
- PropertyTable: sortable columns, row selection highlight, loading skeletons,
  empty/error states, Eye/Plus/Bookmark actions
- PropertyCard: decision-object card with header, primary, matchability,
  data quality and action zones; card states (selected, compareSelected,
  stale, low-confidence)
- PropertyDetailView: 7-tab detail panel (Übersicht, Hard Facts, Soft Factors,
  Matchability, Datenqualität, Quelle, Signale) with low-quality banner,
  missing field UX, data update CTA
- PropertyDetailSkeleton: loading state
- Properties page: two-panel layout (table + sliding detail panel)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Sutter
2026-05-15 16:53:51 +02:00
parent 7b7be0b436
commit 3cd2e83b25
12 changed files with 1303 additions and 354 deletions
+33 -1
View File
@@ -1,7 +1,9 @@
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 } from '../lib/constants'
import { STALE_PROPERTIES, STALE_MATCHES, STALE_SIGNALS } from '../lib/constants'
interface PropertyFilter {
assetType?: AssetType
@@ -32,3 +34,33 @@ export function useProperty(id: string) {
}
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 ?? [],
})
}