'use client'; import { useState } from 'react'; import { useApp } from '@/lib/context'; import { resolveKey } from '@/lib/localization'; import type { ChartConfig } from '@/lib/types'; type DraftChart = Omit; interface Props { initial?: ChartConfig; onSave: (draft: DraftChart) => void; onClose: () => void; } const inputCls = 'w-full bg-gray-800 border border-gray-600 rounded px-3 py-1.5 text-white text-sm focus:outline-none focus:border-indigo-500'; /** * Normalizes a comma-separated list of user tokens (localized names or raw keys) * to raw item_keys. Unknown tokens are kept as-is. */ function normalizeList(raw: string, reverseMap: Map): string[] | null { const arr = raw .split(',') .map((x) => x.trim()) .filter(Boolean); if (arr.length === 0) return null; return arr.map((t) => resolveKey(t, reverseMap)); } export default function ChartEditor({ initial, onSave, onClose }: Props) { const { reverseMap } = useApp(); const [title, setTitle] = useState(initial?.title ?? ''); const [chartType, setChartType] = useState( initial?.chart_type ?? 'signals', ); const [vizType, setVizType] = useState(initial?.viz_type ?? 'line'); const [signalType, setSignalType] = useState( initial?.signal_type ?? 'both', ); const [combinators, setCombinators] = useState((initial?.filter_combinators ?? []).join(', ')); const [whitelist, setWhitelist] = useState((initial?.filter_items ?? []).join(', ')); const [blacklist, setBlacklist] = useState((initial?.filter_items_exclude ?? []).join(', ')); const [useRegex, setUseRegex] = useState(initial?.filter_items_regex ?? false); const [orderBy, setOrderBy] = useState(initial?.order_by ?? 'value_asc'); const [seriesLimit, setSeriesLimit] = useState(initial?.series_limit ?? 20); const [yMin, setYMin] = useState(initial?.y_min?.toString() ?? ''); const [yMax, setYMax] = useState(initial?.y_max?.toString() ?? ''); const [yScale, setYScale] = useState(initial?.y_scale ?? 'linear'); const [width, setWidth] = useState(initial?.width ?? 2); const [height, setHeight] = useState(initial?.height ?? 4); function splitCombinators(): string[] | null { const arr = combinators .split(',') .map((x) => x.trim()) .filter(Boolean); return arr.length > 0 ? arr : null; } function handleSave() { if (!title.trim()) return; // Regex mode: store pattern exactly as typed — server expands via matchKeys at query time. // Non-regex mode: resolve each comma-token (localized name or raw key) to raw key. const filter_items = useRegex ? whitelist.trim() ? [whitelist.trim()] : null : normalizeList(whitelist, reverseMap); const filter_items_exclude = useRegex ? blacklist.trim() ? [blacklist.trim()] : null : normalizeList(blacklist, reverseMap); onSave({ title: title.trim(), chart_type: chartType, viz_type: chartType === 'divider' ? 'line' : vizType, signal_type: signalType, pos_x: initial?.pos_x ?? 0, pos_y: initial?.pos_y ?? 0, width, height, filter_combinators: chartType === 'divider' ? null : splitCombinators(), filter_items: chartType === 'divider' ? null : filter_items, filter_items_exclude: chartType === 'divider' ? null : filter_items_exclude, filter_items_regex: useRegex, order_by: orderBy, series_limit: seriesLimit, y_min: yMin !== '' ? parseFloat(yMin) : null, y_max: yMax !== '' ? parseFloat(yMax) : null, y_scale: yScale, }); } const isSignals = chartType === 'signals'; const isDivider = chartType === 'divider'; return (
e.stopPropagation()} >

{initial ? 'Edit Chart' : 'New Chart'}

setTitle(e.target.value)} className={`${inputCls} mb-3`} /> {!isDivider && ( <> )} {isSignals && ( <> setSeriesLimit(Number(e.target.value))} className={`${inputCls} mb-3`} /> setCombinators(e.target.value)} placeholder="nauvis, nauvis-orbit" className={`${inputCls} mb-3`} />
setWhitelist(e.target.value)} placeholder={useRegex ? 'wissen.*|Iron Plate' : 'Iron Plate, copper-plate'} className={`${inputCls} mb-1`} />

Whitelist — localized names or item keys accepted (empty = all)

setBlacklist(e.target.value)} placeholder={useRegex ? 'Holz|stone' : 'Wood, stone'} className={`${inputCls} mb-1`} />

Blacklist — localized names or item keys accepted

)} {!isDivider && ( <>
setYMin(e.target.value)} placeholder="auto" className={inputCls} />
setYMax(e.target.value)} placeholder="auto" className={inputCls} />
)}
setWidth(Number(e.target.value))} className={inputCls} />
setHeight(Number(e.target.value))} className={inputCls} />
); }