From 15f890abdafed348682442b6c02a5f38ee880041 Mon Sep 17 00:00:00 2001 From: sokol Date: Wed, 18 Feb 2026 17:09:32 +0300 Subject: [PATCH] feat: add manual template editor with view/edit modes --- src/App.tsx | 11 ++- src/componets/content/ConfigTemplate.tsx | 118 +++++++++++++++++++++++ src/componets/content/Content.tsx | 17 +--- 3 files changed, 131 insertions(+), 15 deletions(-) create mode 100644 src/componets/content/ConfigTemplate.tsx diff --git a/src/App.tsx b/src/App.tsx index eee5c48..4d7ec74 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -90,6 +90,15 @@ function App() { }); } + function handleTemplateSaved(newContent: string) { + setConfig(prevConfig => { + const newConfig = new Config(); + newConfig.envs = prevConfig.envs; + newConfig.addTemplate(newContent); + return newConfig; + }); + } + return ( <>
@@ -111,7 +120,7 @@ function App() { onRemove={handleEnvRemoved} />
- +
) : diff --git a/src/componets/content/ConfigTemplate.tsx b/src/componets/content/ConfigTemplate.tsx new file mode 100644 index 0000000..d0a8249 --- /dev/null +++ b/src/componets/content/ConfigTemplate.tsx @@ -0,0 +1,118 @@ +import { useState } from "react"; +import Highlight from 'react-highlight'; +import 'highlight.js/styles/far.css'; +import { Config } from "../../models/Config"; + +interface ConfigTemplateProps { + config: Config; + onSaved: (newContent: string) => void; +} + +export function ConfigTemplate(props: ConfigTemplateProps) { + const [mode, setMode] = useState<'view' | 'edit'>('view'); + const [draftContent, setDraftContent] = useState(props.config.template.content); + const [originalContent, setOriginalContent] = useState(props.config.template.content); + const [jsonError, setJsonError] = useState(null); + + // Sync draft when config changes (in view mode) + if (mode === 'view') { + setDraftContent(props.config.template.content); + } + + function handleEdit() { + setOriginalContent(props.config.template.content); + setDraftContent(props.config.template.content); + setJsonError(null); + setMode('edit'); + } + + function handleRevert() { + setDraftContent(originalContent); + setJsonError(null); + setMode('view'); + } + + function handleSave() { + // Validate JSON before saving + try { + JSON.parse(draftContent); + setJsonError(null); + props.onSaved(draftContent); + setMode('view'); + } catch (e) { + setJsonError((e as Error).message); + } + } + + function handleDraftChange(value: string) { + setDraftContent(value); + // Validate JSON on every change + try { + if (value.trim()) { + JSON.parse(value); + setJsonError(null); + } else { + setJsonError(null); + } + } catch (e) { + setJsonError((e as Error).message); + } + } + + const isValidJson = jsonError === null; + + return ( +
+ {mode === 'view' ? ( + <> +
+ +
+ + {props.config.template.content || "{}"} + + + ) : ( + <> +
+ + + + {isValidJson ? 'Valid JSON' : 'Invalid JSON'} + +
+ {jsonError && ( +
+ {jsonError} +
+ )} +