Files
breakpilot-lehrer/studio-v2/app/alerts-b2b/_components/HitDetailModal.tsx
Benjamin Admin 0b37c5e692 [split-required] Split website + studio-v2 monoliths (Phase 3 continued)
Website (14 monoliths split):
- compliance/page.tsx (1,519 → 9), docs/audit (1,262 → 20)
- quality (1,231 → 16), alerts (1,203 → 10), docs (1,202 → 11)
- i18n.ts (1,173 → 8 language files)
- unity-bridge (1,094 → 12), backlog (1,087 → 6)
- training (1,066 → 8), rag (1,063 → 8)
- Deleted index_original.ts (4,899 LOC dead backup)

Studio-v2 (5 monoliths split):
- meet/page.tsx (1,481 → 9), messages (1,166 → 9)
- AlertsB2BContext.tsx (1,165 → 5 modules)
- alerts-b2b/page.tsx (1,019 → 6), korrektur/archiv (1,001 → 6)

All existing imports preserved. Zero new TypeScript errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-24 17:52:36 +02:00

149 lines
6.4 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import { useState } from 'react'
import { useTheme } from '@/lib/ThemeContext'
import type { B2BHit } from '@/lib/AlertsB2BContext'
import { getImportanceLabelColor, getDecisionLabelColor, formatDeadline } from '@/lib/AlertsB2BContext'
import { DecisionTraceModal } from './DecisionTraceModal'
export function HitDetailModal({
hit,
onClose,
onFeedback
}: {
hit: B2BHit
onClose: () => void
onFeedback: (feedback: 'relevant' | 'irrelevant') => void
}) {
const { isDark } = useTheme()
const [showTrace, setShowTrace] = useState(false)
return (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
<div className="absolute inset-0 bg-black/50 backdrop-blur-sm" onClick={onClose} />
<div className={`relative w-full max-w-2xl rounded-3xl border p-8 max-h-[90vh] overflow-y-auto ${
isDark ? 'bg-slate-900 border-white/20' : 'bg-white border-slate-200 shadow-2xl'
}`}>
{/* Header */}
<div className="flex items-start justify-between mb-4">
<div className="flex items-center gap-3 flex-wrap">
<span className={`px-3 py-1 rounded-full text-xs font-medium border ${getImportanceLabelColor(hit.importanceLabel, isDark)}`}>
{hit.importanceLabel}
</span>
<span className={`px-2 py-1 rounded-lg text-xs ${getDecisionLabelColor(hit.decisionLabel, isDark)}`}>
{hit.decisionLabel === 'relevant' ? 'Relevant' : hit.decisionLabel === 'needs_review' ? 'Pruefung noetig' : 'Irrelevant'}
</span>
<span className={`text-xs ${isDark ? 'text-white/40' : 'text-slate-400'}`}>
Score: {hit.importanceScore}
</span>
</div>
<button onClick={onClose} className={`p-2 rounded-lg ${isDark ? 'hover:bg-white/10' : 'hover:bg-slate-100'}`}>
<svg className={`w-5 h-5 ${isDark ? 'text-white/60' : 'text-slate-400'}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
{/* Title */}
<h2 className={`text-xl font-bold mb-4 ${isDark ? 'text-white' : 'text-slate-900'}`}>
{hit.title}
</h2>
{/* Metadata */}
<div className={`flex flex-wrap gap-4 mb-4 text-sm ${isDark ? 'text-white/60' : 'text-slate-500'}`}>
{hit.buyerGuess && (
<span className="flex items-center gap-1">
<span>🏛</span> {hit.buyerGuess}
</span>
)}
{hit.countryGuess && (
<span className="flex items-center gap-1">
<span>🌍</span> {hit.countryGuess}
</span>
)}
{hit.deadlineGuess && (
<span className={`flex items-center gap-1 ${
formatDeadline(hit.deadlineGuess).includes('Heute') || formatDeadline(hit.deadlineGuess).includes('Morgen')
? 'text-red-500 font-medium'
: ''
}`}>
<span>📅</span> Frist: {formatDeadline(hit.deadlineGuess)}
</span>
)}
</div>
{/* Snippet */}
<div className={`rounded-xl p-4 mb-6 ${isDark ? 'bg-white/5' : 'bg-slate-50'}`}>
<p className={isDark ? 'text-white/80' : 'text-slate-600'}>
{hit.snippet}
</p>
</div>
{/* Source Info */}
<div className={`flex items-center gap-4 mb-6 text-sm ${isDark ? 'text-white/40' : 'text-slate-400'}`}>
<span className={`px-2 py-1 rounded ${isDark ? 'bg-white/10' : 'bg-slate-100'}`}>
{hit.sourceType === 'email' ? '📧 Email' : '📡 RSS'}
</span>
<a
href={hit.originalUrl}
target="_blank"
rel="noopener noreferrer"
className={`hover:underline ${isDark ? 'text-blue-400' : 'text-blue-600'}`}
>
Original ansehen
</a>
</div>
{/* Actions */}
<div className="flex items-center justify-between pt-4 border-t border-white/10">
{/* Feedback */}
<div className="flex items-center gap-2">
<span className={`text-sm ${isDark ? 'text-white/60' : 'text-slate-500'}`}>War das relevant?</span>
<button
onClick={() => onFeedback('relevant')}
className={`p-2 rounded-lg transition-all ${
hit.userFeedback === 'relevant'
? 'bg-green-500/20 text-green-400'
: isDark ? 'hover:bg-white/10 text-white/40' : 'hover:bg-slate-100 text-slate-400'
}`}
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M14 10h4.764a2 2 0 011.789 2.894l-3.5 7A2 2 0 0115.263 21h-4.017c-.163 0-.326-.02-.485-.06L7 20m7-10V5a2 2 0 00-2-2h-.095c-.5 0-.905.405-.905.905 0 .714-.211 1.412-.608 2.006L7 11v9m7-10h-2M7 20H5a2 2 0 01-2-2v-6a2 2 0 012-2h2.5" />
</svg>
</button>
<button
onClick={() => onFeedback('irrelevant')}
className={`p-2 rounded-lg transition-all ${
hit.userFeedback === 'irrelevant'
? 'bg-red-500/20 text-red-400'
: isDark ? 'hover:bg-white/10 text-white/40' : 'hover:bg-slate-100 text-slate-400'
}`}
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 14H5.236a2 2 0 01-1.789-2.894l3.5-7A2 2 0 018.736 3h4.018a2 2 0 01.485.06l3.76.94m-7 10v5a2 2 0 002 2h.096c.5 0 .905-.405.905-.904 0-.715.211-1.413.608-2.008L17 13V4m-7 10h2m5-10h2a2 2 0 012 2v6a2 2 0 01-2 2h-2.5" />
</svg>
</button>
</div>
{/* Decision Trace */}
<button
onClick={() => setShowTrace(true)}
className={`px-4 py-2 rounded-lg text-sm transition-all ${
isDark
? 'bg-white/10 text-white/80 hover:bg-white/20'
: 'bg-slate-100 text-slate-700 hover:bg-slate-200'
}`}
>
🔍 Decision Trace
</button>
</div>
</div>
{/* Decision Trace Modal */}
{showTrace && (
<DecisionTraceModal hit={hit} onClose={() => setShowTrace(false)} />
)}
</div>
)
}