import { Box, Button, Chip, CircularProgress, Divider, IconButton, LinearProgress, Paper, Typography } from '@mui/material' import { X } from 'lucide-react' import { useState } from 'react' import { SignalTypeBadge } from './SignalTypeBadge' import { SensitivityBadge } from './SensitivityBadge' import { SignalReviewStatusBadge } from './SignalReviewStatusBadge' import { FutureSignalDisclaimer } from './FutureSignalDisclaimer' import { useUpdateSignalReviewStatus } from '../../hooks/useFutureSignals' import { useCreateReviewTask } from '../../hooks/useReviewQueue' import { useShortlistStore } from '../../stores/shortlistStore' import { useToastStore } from '../../stores/toastStore' import { ReviewStatus } from '../../domain/enums' import type { FutureSignal } from '../../domain/futureSignal' import { SOURCE_TYPE_LABELS } from '../../lib/constants' import { DS_ACCENT, DS_SLATE } from '../../lib/ds' const CREDIBILITY_META: Record = { HIGH: { label: 'Hoch', color: DS_ACCENT.success.main }, MEDIUM: { label: 'Mittel', color: DS_ACCENT.warning.main }, LOW: { label: 'Niedrig', color: DS_ACCENT.danger.main }, } function BarRow({ label, value }: { label: string; value: number }) { const color = value >= 0.75 ? '#1a7a4a' : value >= 0.55 ? '#d97706' : '#c0392b' return ( {label} {Math.round(value * 100)}% ) } interface Props { signal: FutureSignal onClose: () => void } export function FutureSignalDetailPanel({ signal, onClose }: Props) { const updateStatus = useUpdateSignalReviewStatus() const createReviewTask = useCreateReviewTask() const { openAddDialog } = useShortlistStore() const showToast = useToastStore((s) => s.showToast) const [reviewTaskSent, setReviewTaskSent] = useState(false) const reviewStatus = signal.reviewStatus ?? ReviewStatus.UNREVIEWED const isRejected = reviewStatus === ReviewStatus.REJECTED const isApproved = reviewStatus === ReviewStatus.APPROVED const STATUS_TOAST: Record = { IN_REVIEW: 'Signal zur Prüfung markiert.', APPROVED: 'Signal genehmigt.', REJECTED: 'Signal abgelehnt.', FLAGGED: 'Signal markiert.', } async function handleStatus(status: typeof ReviewStatus[keyof typeof ReviewStatus]) { try { await updateStatus.mutateAsync({ id: signal.id, status }) showToast(STATUS_TOAST[status] ?? 'Status aktualisiert.') } catch { showToast('Statusänderung fehlgeschlagen.', 'error') } } function handleSendReview() { createReviewTask.mutate(signal.id, { onSuccess: () => { setReviewTaskSent(true) showToast('Prüfungsaufgabe erstellt.') }, }) } function handleShortlist() { openAddDialog({ resultId: signal.id, resultType: 'FUTURE_AVAILABILITY', title: signal.title ?? signal.companyName ?? signal.locationHint, matchScore: Math.round(signal.confidenceScore * 100), confidenceScore: signal.confidenceScore, sourceLabel: SOURCE_TYPE_LABELS[signal.source.type] ?? signal.source.type, addedBy: 'admin@ideal-sharing.ch', }) } const credMeta = CREDIBILITY_META[signal.source.credibility] ?? { label: signal.source.credibility, color: DS_SLATE[500] } return ( {/* Header */} {signal.title ?? signal.companyName ?? signal.locationHint} {(signal.title || signal.companyName) && ( {signal.locationHint} )} {signal.isVerified && ( )} {/* Body */} {/* Probability + confidence */} {/* Meta */} Zeithorizont ~{signal.timeHorizonMonths} Monate {signal.areaSqmEstimate && ( Geschätzte Fläche ~{signal.areaSqmEstimate.toLocaleString('de-CH')} m² )} {signal.companyName && ( Unternehmen {signal.companyName} )} {signal.riskLevel && ( Risikoniveau {signal.riskLevel} )} {/* Source */} Quelle Typ {SOURCE_TYPE_LABELS[signal.source.type] ?? signal.source.type} Glaubwürdigkeit {signal.source.publishedAt && ( Veröffentlicht {signal.source.publishedAt} )} {/* Evidence */} {signal.evidence?.summary && ( <> Evidenz {signal.evidence.summary} )} {/* Market indicator */} {signal.marketIndicator && ( <> Marktindikator {signal.marketIndicator} )} {/* Actions */} Aktionen {reviewStatus === ReviewStatus.UNREVIEWED && ( )} {!isRejected && !reviewTaskSent && ( )} {(reviewStatus === ReviewStatus.IN_REVIEW || reviewStatus === ReviewStatus.FLAGGED) && !isApproved && ( )} {!isRejected && ( )} ) }