import { useEffect, useMemo, useState } from 'react'; import axios from 'axios'; import { ChevronLeft, ChevronRight, Pencil, Plus, RefreshCw, Search, Trash2, X } from 'lucide-react'; import { APP_ENDPOINTS } from '../../../config/env'; import { LucentSelect } from '../../../components/lucent/LucentSelect'; import type { PlatformSettings, SystemSettingItem } from '../types'; import { LucentIconButton } from '../../../components/lucent/LucentIconButton'; import { useLucentPrompt } from '../../../components/lucent/LucentPromptProvider'; interface PlatformSettingsPageProps { isZh: boolean; } interface SystemSettingsResponse { items: SystemSettingItem[]; } interface SettingDraft { key: string; name: string; category: string; description: string; value_type: string; value: string; is_public: boolean; sort_order: string; } const emptyDraft: SettingDraft = { key: '', name: '', category: 'general', description: '', value_type: 'string', value: '', is_public: false, sort_order: '100', }; function toDraft(item?: SystemSettingItem): SettingDraft { if (!item) return emptyDraft; return { key: item.key, name: item.name, category: item.category, description: item.description, value_type: item.value_type, value: item.value_type === 'json' ? JSON.stringify(item.value, null, 2) : String(item.value ?? ''), is_public: item.is_public, sort_order: String(item.sort_order ?? 100), }; } function parseValue(draft: SettingDraft) { if (draft.value_type === 'integer') return Number.parseInt(draft.value || '0', 10) || 0; if (draft.value_type === 'float') return Number.parseFloat(draft.value || '0') || 0; if (draft.value_type === 'boolean') return ['1', 'true', 'yes', 'on'].includes(draft.value.trim().toLowerCase()); if (draft.value_type === 'json') return JSON.parse(draft.value || 'null'); return draft.value; } function displayValue(item: SystemSettingItem) { if (item.value_type === 'json') return JSON.stringify(item.value); if (item.value_type === 'boolean') return String(Boolean(item.value)); return String(item.value ?? ''); } function normalizePageSize(value: unknown, fallback = 10) { const parsed = Number(value); if (!Number.isFinite(parsed) || parsed <= 0) return fallback; return Math.max(1, Math.min(100, Math.floor(parsed))); } export function PlatformSettingsPage({ isZh }: PlatformSettingsPageProps) { const { notify, confirm } = useLucentPrompt(); const [items, setItems] = useState([]); const [loading, setLoading] = useState(false); const [saving, setSaving] = useState(false); const [search, setSearch] = useState(''); const [showEditor, setShowEditor] = useState(false); const [editingKey, setEditingKey] = useState(''); const [draft, setDraft] = useState(emptyDraft); const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(10); const loadRows = async () => { setLoading(true); try { const res = await axios.get(`${APP_ENDPOINTS.apiBase}/platform/system-settings`); setItems(Array.isArray(res.data?.items) ? res.data.items : []); } catch (error: any) { notify(error?.response?.data?.detail || (isZh ? '读取系统参数失败。' : 'Failed to load system settings.'), { tone: 'error' }); } finally { setLoading(false); } }; const refreshSnapshot = async () => { try { const res = await axios.get(`${APP_ENDPOINTS.apiBase}/platform/settings`); setPageSize(normalizePageSize(res.data?.page_size, 10)); } catch { // ignore snapshot refresh failures } }; useEffect(() => { setPage(1); void (async () => { await Promise.allSettled([loadRows(), refreshSnapshot()]); })(); }, []); useEffect(() => { setPage(1); }, [search]); const filtered = useMemo(() => { const keyword = search.trim().toLowerCase(); if (!keyword) return items; return items.filter((item) => [item.key, item.name, item.category, item.description].some((value) => String(value || '').toLowerCase().includes(keyword)), ); }, [items, search]); useEffect(() => { const maxPage = Math.max(1, Math.ceil(filtered.length / pageSize)); if (page > maxPage) setPage(maxPage); }, [filtered.length, page, pageSize]); const pageCount = Math.max(1, Math.ceil(filtered.length / pageSize)); const pagedItems = useMemo(() => filtered.slice((page - 1) * pageSize, page * pageSize), [filtered, page, pageSize]); return (

{isZh ? '系统参数' : 'System Settings'}

{isZh ? '参数修改后会立即同步到运行时,无需重启平台。' : 'Setting changes are applied to the runtime immediately without restarting the platform.'}

setSearch(event.target.value)} placeholder={isZh ? '搜索参数...' : 'Search settings...'} aria-label={isZh ? '搜索参数' : 'Search settings'} />
{pagedItems.map((item) => ( ))}
{isZh ? '参数键' : 'Key'} {isZh ? '名称' : 'Name'} {isZh ? '当前值' : 'Value'} {isZh ? '类型' : 'Type'} {isZh ? '分类' : 'Category'} {isZh ? '描述' : 'Description'} {isZh ? '操作' : 'Actions'}
{item.key}
{item.is_public ?
{isZh ? '前端可访问' : 'Public'}
: null}
{item.name} {displayValue(item)} {item.value_type} {item.category} {item.description}
{ setEditingKey(item.key); setDraft(toDraft(item)); setShowEditor(true); }} tooltip={isZh ? '编辑' : 'Edit'} aria-label={isZh ? '编辑' : 'Edit'} > { void (async () => { const ok = await confirm({ title: isZh ? '删除系统参数' : 'Delete System Setting', message: `${isZh ? '确认删除' : 'Delete'} ${item.key}?`, tone: 'warning', }); if (!ok) return; try { await axios.delete(`${APP_ENDPOINTS.apiBase}/platform/system-settings/${encodeURIComponent(item.key)}`); await loadRows(); await refreshSnapshot(); } catch (error: any) { notify(error?.response?.data?.detail || (isZh ? '删除失败。' : 'Delete failed.'), { tone: 'error' }); } })(); }} tooltip={isZh ? '删除' : 'Delete'} aria-label={isZh ? '删除' : 'Delete'} >
{filtered.length === 0 ?
{isZh ? '暂无系统参数。' : 'No system settings.'}
: null}
{isZh ? `第 ${page} / ${pageCount} 页,共 ${filtered.length} 条` : `Page ${page} / ${pageCount}, ${filtered.length} rows`}
setPage((value) => Math.max(1, value - 1))} tooltip={isZh ? '上一页' : 'Previous'} aria-label={isZh ? '上一页' : 'Previous'} > = pageCount} onClick={() => setPage((value) => Math.min(pageCount, value + 1))} tooltip={isZh ? '下一页' : 'Next'} aria-label={isZh ? '下一页' : 'Next'} >
{showEditor ? (
setShowEditor(false)}>
event.stopPropagation()}>

{editingKey ? (isZh ? '编辑参数' : 'Edit Setting') : (isZh ? '新增参数' : 'Create Setting')}

setShowEditor(false)} tooltip={isZh ? '关闭' : 'Close'} aria-label={isZh ? '关闭' : 'Close'}>
setDraft((prev) => ({ ...prev, key: event.target.value }))} disabled={Boolean(editingKey)} /> setDraft((prev) => ({ ...prev, name: event.target.value }))} /> setDraft((prev) => ({ ...prev, value_type: event.target.value }))}> {draft.value_type === 'json' ? (