import { useState } from 'react' import axios from 'axios' import clsx from 'clsx' import { Plus, Edit2, Trash2, Sparkles, X, Save, ChevronDown, ChevronUp, Check } from 'lucide-react' const api = axios.create({ baseURL: '/api' }) interface Indicator { symbol: string; indicator: string; label: string; rationale?: string } interface MarketEvent { id: number; name: string; start_date: string; end_date: string | null level: string; category: string; description: string; market_impact: string affected_assets: string; impact_score: number absorption_pct: number | null; relevant_indicators: string } interface Props { events: MarketEvent[] onRefresh: () => void } const LEVELS = ['long', 'medium', 'short'] as const const LEVEL_LABELS = { long: 'Long Terme', medium: 'Moyen Terme', short: 'Court Terme' } const LEVEL_COLORS = { long: { badge: 'bg-violet-900/60 text-violet-300 border-violet-700/50', dot: 'bg-violet-500' }, medium: { badge: 'bg-blue-900/60 text-blue-300 border-blue-700/50', dot: 'bg-blue-500' }, short: { badge: 'bg-emerald-900/60 text-emerald-300 border-emerald-700/50', dot: 'bg-emerald-500' }, } const CATEGORIES = ['macro', 'monetary', 'geopolitical', 'market', 'fiscal'] const EMPTY_FORM = { name: '', start_date: '', end_date: '', level: 'medium', category: 'macro', description: '', market_impact: '', affected_assets: '', impact_score: 0.5, absorption_pct: '', relevant_indicators: '', } type FormState = typeof EMPTY_FORM function AbsorptionBadge({ pct }: { pct: number | null }) { if (pct === null || pct === undefined) return const color = pct >= 80 ? 'text-red-400' : pct >= 40 ? 'text-yellow-400' : 'text-emerald-400' const label = pct >= 80 ? 'Fully priced' : pct >= 40 ? 'Partial' : 'Not yet' return ( {pct.toFixed(0)}% ({label}) ) } function fmtDate(d: string | null) { if (!d) return '—' return new Date(d).toLocaleDateString('fr-FR', { day: '2-digit', month: 'short', year: '2-digit' }) } function parseIndicators(raw: string | null | undefined): Indicator[] { try { return JSON.parse(raw || '[]') } catch { return [] } } // ── Edit / Add Modal ─────────────────────────────────────────────────────────── function EventModal({ event, onClose, onSaved, }: { event: MarketEvent | null // null = create new onClose: () => void onSaved: () => void }) { const [form, setForm] = useState(event ? { name: event.name, start_date: event.start_date, end_date: event.end_date ?? '', level: event.level, category: event.category, description: event.description, market_impact: event.market_impact, affected_assets: (() => { try { return JSON.parse(event.affected_assets || '[]').join(', ') } catch { return '' } })(), impact_score: event.impact_score, absorption_pct: event.absorption_pct !== null && event.absorption_pct !== undefined ? String(event.absorption_pct) : '', relevant_indicators: (() => { try { const inds = JSON.parse(event.relevant_indicators || '[]') return inds.map((i: Indicator) => i.label).join(', ') } catch { return '' } })(), } : { ...EMPTY_FORM }) const [saving, setSaving] = useState(false) const [enriching, setEnriching] = useState(false) const [enrichResult, setEnrichResult] = useState<{ absorption_pct: number; absorption_rationale: string; relevant_indicators: Indicator[] } | null>(null) const set = (k: keyof FormState, v: string | number) => setForm(f => ({ ...f, [k]: v })) async function save() { setSaving(true) try { const payload = { name: form.name, start_date: form.start_date, end_date: form.end_date || null, level: form.level, category: form.category, description: form.description, market_impact: form.market_impact, affected_assets: form.affected_assets.split(',').map(s => s.trim()).filter(Boolean), impact_score: Number(form.impact_score), absorption_pct: form.absorption_pct ? Number(form.absorption_pct) : null, relevant_indicators: enrichResult?.relevant_indicators ?? [], } if (event) { await api.put(`/timeline/events/${event.id}`, payload) } else { await api.post('/timeline/events', payload) } onSaved() onClose() } finally { setSaving(false) } } async function enrich() { if (!event) return setEnriching(true) try { const res = await api.post(`/timeline/events/${event.id}/ai-enrich`) const d = res.data setEnrichResult(d) set('absorption_pct', String(d.absorption_pct ?? '')) } finally { setEnriching(false) } } const indicators = enrichResult?.relevant_indicators ?? parseIndicators(event?.relevant_indicators) return (
e.stopPropagation()} > {/* Header */}
{event ? `Éditer — ${event.name}` : 'Nouvel événement'}
{/* Name + Level + Category */}
set('name', e.target.value)} className="w-full bg-dark-700 border border-slate-700/40 rounded px-3 py-1.5 text-sm text-white focus:outline-none focus:border-slate-500" />
set('impact_score', e.target.value)} className="w-full bg-dark-700 border border-slate-700/40 rounded px-3 py-1.5 text-sm text-white focus:outline-none" />
{/* Dates */}
set('start_date', e.target.value)} className="w-full bg-dark-700 border border-slate-700/40 rounded px-3 py-1.5 text-sm text-white focus:outline-none" />
set('end_date', e.target.value)} className="w-full bg-dark-700 border border-slate-700/40 rounded px-3 py-1.5 text-sm text-white focus:outline-none" />
{/* Description + Market impact */}