import { useState } from 'react'; import { File } from 'lucide-react'; import { Env } from './models/Env'; import { Environment } from './componets/env/Environment'; import { Content } from './componets/content/Content'; import { FileChooser } from './componets/FileChooser'; import { Config } from './models/Config'; class AppState { private constructor( public config: Config = new Config(), ) {} static readonly Instance = new AppState(); public loadConfig(cfg: Config) { this.config = cfg; } public async saveEnv(env: Env): Promise { // Simulate async save with 1 second delay return await new Promise((resolve) => { setTimeout(() => { console.log('Saved env:', env.name); resolve(0); }, 1000); }); } } function App() { const [config, setConfig] = useState(() => AppState.Instance.config); const [envs, setEnvs] = useState(() => AppState.Instance.config.envs); const [selectedEnv, setSelectedEnv] = useState(0); // Ensure selectedEnv is always valid const validSelectedEnv = Math.min(selectedEnv, Math.max(0, envs.length - 1)); const currentEnv = envs[validSelectedEnv]; async function handleEnvChanged(env: Env) { // Optimistic update - update React state immediately setEnvs((prevEnvs) => { const newEnvs = [...prevEnvs]; const idx = newEnvs.findIndex((x) => x.id === env.id); if (idx > -1) { newEnvs[idx] = env; } return newEnvs; }); // Also update config.envs and template to keep them in sync setConfig((prevConfig) => { const newConfig = new Config(); newConfig.envs = prevConfig.envs.map((e) => (e.id === env.id ? env : e)); newConfig.template = prevConfig.template; newConfig.updateTemplateFromEnv(env); return newConfig; }); // Fire off async save in background (no need to wait) AppState.Instance.saveEnv(env); } function handleEnvSelected(idx: number) { setSelectedEnv(idx); } function handleEnvAdded(env: Env): number { const newIdx = envs.length; setEnvs((prevEnvs) => [...prevEnvs, env]); setConfig((prevConfig) => { const newConfig = new Config(); newConfig.envs = [...prevConfig.envs, env]; newConfig.template = prevConfig.template; return newConfig; }); return newIdx; } function handleEnvRemoved(envId: number) { setEnvs((prevEnvs) => prevEnvs.filter((e) => e.id !== envId)); setConfig((prevConfig) => { const newConfig = new Config(); newConfig.envs = prevConfig.envs.filter((e) => e.id !== envId); newConfig.template = prevConfig.template; return newConfig; }); } function handleTemplateSaved(newContent: string) { setConfig((prevConfig) => { const newConfig = new Config(); newConfig.envs = prevConfig.envs; newConfig.setTemplate(newContent); return newConfig; }); } return (
{/* Header */}
{ AppState.Instance.loadConfig(x); setEnvs(x.envs); setConfig(x); }} config={config} />
{envs.length > 0 ? (
{/* Environment Panel - Fixed 4/12 on all large screens */}
await handleEnvChanged(e)} onSelected={handleEnvSelected} onAdd={handleEnvAdded} onRemove={handleEnvRemoved} />
{/* Content Panel - Maximum width (8/12 → 9/12 on 2xl) */}
) : ( /* Empty State */

No Configuration Loaded

Create a new configuration or upload an existing XML file to get started

)}
); } export default App;