import { useEffect, useState } from 'react'; import { useParams } from 'react-router'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { Settings2, FileText, Save } from 'lucide-react'; import { api } from '@/lib/api'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs'; interface ConfigFile { index: number; path: string; parser: string; editableKeys: string[] | null; } interface ConfigEntry { key: string; value: string; } interface ConfigDetail { path: string; parser: string; editableKeys: string[] | null; entries: ConfigEntry[]; raw: string; } function mergeConfigEntries( entries: ConfigEntry[], editableKeys: string[] | null, ): ConfigEntry[] { if (!editableKeys || editableKeys.length === 0) return entries; const existing = new Map(entries.map((entry) => [entry.key, entry])); const merged = [...entries]; for (const key of editableKeys) { if (!existing.has(key)) { merged.push({ key, value: '' }); } } return merged; } export function ConfigPage() { const { orgId, serverId } = useParams(); const queryClient = useQueryClient(); const { data: configsData } = useQuery({ queryKey: ['configs', orgId, serverId], queryFn: () => api.get<{ configs: ConfigFile[] }>( `/organizations/${orgId}/servers/${serverId}/config`, ), }); const configs = configsData?.configs ?? []; if (configs.length === 0) { return (

No config files available for this game

); } return ( {configs.map((cf) => ( {cf.path.split('/').pop()} ))} {configs.map((cf) => ( ))} ); } function ConfigEditor({ orgId, serverId, configIndex, configFile, }: { orgId: string; serverId: string; configIndex: number; configFile: ConfigFile; }) { const queryClient = useQueryClient(); const { data: detail } = useQuery({ queryKey: ['config-detail', orgId, serverId, configIndex], queryFn: () => api.get( `/organizations/${orgId}/servers/${serverId}/config/${configIndex}`, ), }); const [entries, setEntries] = useState([]); useEffect(() => { if (!detail) return; setEntries(mergeConfigEntries(detail.entries, configFile.editableKeys)); }, [detail, configFile.editableKeys]); const saveMutation = useMutation({ mutationFn: (data: { entries: ConfigEntry[] }) => api.put( `/organizations/${orgId}/servers/${serverId}/config/${configIndex}`, data, ), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['config-detail', orgId, serverId, configIndex], }); }, }); const updateEntry = (key: string, value: string) => { setEntries((prev) => prev.map((e) => (e.key === key ? { ...e, value } : e)), ); }; return (
{configFile.path} {configFile.parser} {configFile.editableKeys ? `${configFile.editableKeys.length} allowed additions, plus existing keys` : 'All detected keys editable'}
{entries.length === 0 ? (

{detail ? 'No entries found. The server may need to be started first to generate config files.' : 'Loading...'}

) : (
{entries.map((entry) => (
updateEntry(entry.key, e.target.value)} className="font-mono text-sm" />
))}
)} {saveMutation.isSuccess && (

Config saved successfully

)} {saveMutation.isError && (

Failed to save config

)}
); }