Improve frontend panel de control
This commit is contained in:
@@ -94,14 +94,15 @@ function translateKey(
|
||||
if (key.startsWith('reasoning.')) {
|
||||
// Remove 'reasoning.' prefix and use reasoning namespace
|
||||
const translationKey = key.substring('reasoning.'.length);
|
||||
return tReasoning(translationKey, { ...processedParams, defaultValue: key });
|
||||
// Use i18next-icu for interpolation with {variable} syntax
|
||||
return String(tReasoning(translationKey, processedParams));
|
||||
} else if (key.startsWith('action_queue.') || key.startsWith('dashboard.') || key.startsWith('health.')) {
|
||||
// Use dashboard namespace
|
||||
return tDashboard(key, { ...processedParams, defaultValue: key });
|
||||
return String(tDashboard(key, processedParams));
|
||||
}
|
||||
|
||||
// Default to dashboard
|
||||
return tDashboard(key, { ...processedParams, defaultValue: key });
|
||||
return String(tDashboard(key, processedParams));
|
||||
}
|
||||
|
||||
function ActionItemCard({
|
||||
|
||||
@@ -55,7 +55,10 @@ function TimelineItemCard({
|
||||
translationKey = key.substring('production.'.length);
|
||||
namespace = 'production';
|
||||
}
|
||||
return { reasoning: t(translationKey, { ...params, ns: namespace, defaultValue: item.reasoning || '' }) };
|
||||
// Use i18next-icu for interpolation with {variable} syntax
|
||||
const fullKey = `${namespace}:${translationKey}`;
|
||||
const result = t(fullKey, params);
|
||||
return { reasoning: String(result || item.reasoning || '') };
|
||||
} else if (item.reasoning_data) {
|
||||
return formatBatchAction(item.reasoning_data);
|
||||
}
|
||||
@@ -112,11 +115,15 @@ function TimelineItemCard({
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
<span className="text-sm font-medium" style={{ color: 'var(--text-primary)' }}>
|
||||
{item.status_i18n
|
||||
? t(item.status_i18n.key, {
|
||||
...item.status_i18n.params,
|
||||
ns: item.status_i18n.key.startsWith('production.') ? 'production' : 'reasoning',
|
||||
defaultValue: item.statusText || 'Status'
|
||||
})
|
||||
? (() => {
|
||||
const statusKey = item.status_i18n.key;
|
||||
const statusNamespace = statusKey.startsWith('production.') ? 'production' : 'reasoning';
|
||||
const statusTranslationKey = statusKey.startsWith('production.')
|
||||
? statusKey.substring('production.'.length)
|
||||
: (statusKey.startsWith('reasoning.') ? statusKey.substring('reasoning.'.length) : statusKey);
|
||||
const fullStatusKey = `${statusNamespace}:${statusTranslationKey}`;
|
||||
return String(t(fullStatusKey, item.status_i18n.params) || item.statusText || 'Status');
|
||||
})()
|
||||
: item.statusText || 'Status'}
|
||||
</span>
|
||||
{item.status === 'IN_PROGRESS' && (
|
||||
|
||||
@@ -35,12 +35,19 @@ export const KeyValueEditor: React.FC<KeyValueEditorProps> = ({
|
||||
const [showRawJson, setShowRawJson] = useState(false);
|
||||
const [rawJson, setRawJson] = useState('');
|
||||
const [jsonError, setJsonError] = useState<string | null>(null);
|
||||
const [isInitialized, setIsInitialized] = useState(false);
|
||||
|
||||
// Initialize pairs from value
|
||||
// Initialize pairs from value only on first mount or when value changes from external source
|
||||
useEffect(() => {
|
||||
// Skip if already initialized with internal changes
|
||||
if (isInitialized && pairs.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
setPairs([]);
|
||||
setRawJson('{}');
|
||||
setIsInitialized(true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -51,6 +58,7 @@ export const KeyValueEditor: React.FC<KeyValueEditorProps> = ({
|
||||
if (value.trim() === '') {
|
||||
setPairs([]);
|
||||
setRawJson('{}');
|
||||
setIsInitialized(true);
|
||||
return;
|
||||
}
|
||||
jsonObj = JSON.parse(value);
|
||||
@@ -58,23 +66,28 @@ export const KeyValueEditor: React.FC<KeyValueEditorProps> = ({
|
||||
jsonObj = value;
|
||||
}
|
||||
|
||||
const newPairs: KeyValuePair[] = Object.entries(jsonObj).map(([key, val], index) => ({
|
||||
id: `pair-${Date.now()}-${index}`,
|
||||
key,
|
||||
value: String(val),
|
||||
type: detectType(val)
|
||||
}));
|
||||
// Only update if there's actual data
|
||||
if (Object.keys(jsonObj).length > 0) {
|
||||
const newPairs: KeyValuePair[] = Object.entries(jsonObj).map(([key, val], index) => ({
|
||||
id: `pair-${Date.now()}-${index}`,
|
||||
key,
|
||||
value: String(val),
|
||||
type: detectType(val)
|
||||
}));
|
||||
|
||||
setPairs(newPairs);
|
||||
setRawJson(JSON.stringify(jsonObj, null, 2));
|
||||
setJsonError(null);
|
||||
setPairs(newPairs);
|
||||
setRawJson(JSON.stringify(jsonObj, null, 2));
|
||||
setJsonError(null);
|
||||
}
|
||||
setIsInitialized(true);
|
||||
} catch (error) {
|
||||
// If parsing fails, treat as empty
|
||||
setPairs([]);
|
||||
setRawJson(typeof value === 'string' ? value : '{}');
|
||||
setJsonError('Invalid JSON');
|
||||
setIsInitialized(true);
|
||||
}
|
||||
}, [value]);
|
||||
}, [value, isInitialized, pairs.length]);
|
||||
|
||||
const detectType = (value: any): 'string' | 'number' | 'boolean' => {
|
||||
if (typeof value === 'boolean') return 'boolean';
|
||||
@@ -112,7 +125,8 @@ export const KeyValueEditor: React.FC<KeyValueEditorProps> = ({
|
||||
};
|
||||
const newPairs = [...pairs, newPair];
|
||||
setPairs(newPairs);
|
||||
onChange?.(pairsToJson(newPairs));
|
||||
// Don't call onChange yet - wait for user to fill in the key
|
||||
// onChange will be called when they type in the key/value
|
||||
};
|
||||
|
||||
const handleRemovePair = (id: string) => {
|
||||
|
||||
Reference in New Issue
Block a user