From 984e6860ad1ea9c4bb65e787101eaa418ea89888 Mon Sep 17 00:00:00 2001 From: OpenSquared Date: Thu, 25 Jun 2026 21:32:02 +0200 Subject: [PATCH] feat: event category selector in detail panel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Always-visible 'Catégorie IA' block in EventDetail (view + edit mode) - Dropdown lists all EventCategories; selecting one instantly saves sub_type and updates category type via PUT (no need to enter full edit mode) - Shows matched category description + default impacts preview (ticker chips with direction arrows and sensitivity %) - Edit mode: category dropdown also auto-sets the category type field Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/pages/MarketEvents.tsx | 106 +++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 3 deletions(-) diff --git a/frontend/src/pages/MarketEvents.tsx b/frontend/src/pages/MarketEvents.tsx index 9291cd6..48adb9f 100644 --- a/frontend/src/pages/MarketEvents.tsx +++ b/frontend/src/pages/MarketEvents.tsx @@ -308,6 +308,8 @@ function EventDetail({ const [editing, setEditing] = useState(false) const [editData, setEditData] = useState>({}) const [saving, setSaving] = useState(false) + const [allCats, setAllCats] = useState([]) + const [catSaving, setCatSaving] = useState(false) const loadDetail = useCallback(async () => { const r = await fetch(`/api/market-events/${event.id}`) @@ -318,6 +320,34 @@ function EventDetail({ }, [event.id]) useEffect(() => { loadDetail() }, [loadDetail]) + useEffect(() => { + fetch('/api/impact/categories') + .then(r => r.json()) + .then(d => setAllCats(Array.isArray(d) ? d : [])) + .catch(() => {}) + }, []) + + // Quick-save sub_type without entering full edit mode + const assignCategory = async (catName: string) => { + const cat = allCats.find(c => c.name === catName) + setCatSaving(true) + const payload = { + ...detail, + sub_type: catName, + category: cat?.type ?? detail.category, + source_refs: detail.source_refs ?? [], + } + const r = await fetch(`/api/market-events/${event.id}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload), + }) + setCatSaving(false) + if (r.ok) { + setDetail(d => ({ ...d, sub_type: catName, category: cat?.type ?? d.category })) + onUpdated({ ...detail, sub_type: catName, category: cat?.type ?? detail.category }) + } + } const evaluate = async (force = false) => { setEval(true) @@ -377,7 +407,6 @@ function EventDetail({ )}
- {detail.sub_type && {detail.sub_type}} {detail.level} {detail.start_date} {detail.end_date && → {detail.end_date}} @@ -419,6 +448,77 @@ function EventDetail({ )}
+ {/* ── Catégorie IA ─────────────────────────────────────────────── */} + {(() => { + const currentSub = editing ? (editData.sub_type ?? detail.sub_type ?? '') : (detail.sub_type ?? '') + const matchedCat = allCats.find(c => c.name === currentSub) + const nDefaults = matchedCat?.default_impacts?.length ?? 0 + + return ( +
+
+ Catégorie IA + {matchedCat && ( + + {nDefaults} impact{nDefaults !== 1 ? 's' : ''} par défaut + + )} +
+ {editing ? ( + + ) : ( +
+ + {catSaving && } +
+ )} + {matchedCat?.description && ( +
{matchedCat.description}
+ )} + {matchedCat && nDefaults > 0 && ( +
+ {matchedCat.default_impacts.slice(0, 8).map((di: DefaultImpact) => ( + + {di.typical_direction === 'bullish' ? '↑' : di.typical_direction === 'bearish' ? '↓' : '–'} {di.instrument_id} + {(di.sensitivity * 100).toFixed(0)}% + + ))} +
+ )} +
+ ) + })()} + {/* Description */} {editing ? (