refactor: complete application rewrite with modern UI
This commit is contained in:
@@ -1,157 +1,148 @@
|
||||
import { useState } from "react";
|
||||
import { Env } from "../../models/Env";
|
||||
import Highlight from 'react-highlight'
|
||||
import 'highlight.js/styles/far.css'
|
||||
import { Builder } from "../../builders";
|
||||
import { Config } from "../../models/Config";
|
||||
import { ConfigTemplate } from "./ConfigTemplate";
|
||||
import { useState } from 'react';
|
||||
import { Tabs, TabPanel, CodeBlock } from '../../components/ui';
|
||||
import { Env } from '../../models/Env';
|
||||
import { Config } from '../../models/Config';
|
||||
import { ConfigTemplateEditor } from './ConfigTemplate';
|
||||
import { Builder } from '../../builders';
|
||||
|
||||
|
||||
export function Content(props: { config: Config, env: Env, onTemplateSaved: (newContent: string) => void }) {
|
||||
const [selectTab, setTab] = useState(ContentType.Env);
|
||||
|
||||
// Validate placeholders for warning badge
|
||||
const missingPlaceholders = props.config.validatePlaceholders();
|
||||
const hasValidationWarnings = missingPlaceholders.length > 0;
|
||||
|
||||
return (
|
||||
<>
|
||||
<ContentTabs onSelected={(id) => setTab(id)} selectedTab={selectTab} hasValidationWarnings={hasValidationWarnings} />
|
||||
<div className="">
|
||||
{selectTab == ContentType.Env ? (<ContentParams env={props.env} />) : ""}
|
||||
{selectTab == ContentType.Json ? (<ConfigTemplate config={props.config} onSaved={props.onTemplateSaved} />) : ""}
|
||||
{selectTab == ContentType.Raw ? (<ContentRaw config={props.config} env={props.env} />) : ""}
|
||||
{selectTab == ContentType.Test ? (<ContentTest config={props.config} env={props.env} />) : ""}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
interface ContentProps {
|
||||
config: Config;
|
||||
env: Env;
|
||||
onTemplateSaved: (newContent: string) => void;
|
||||
}
|
||||
|
||||
enum ContentType {
|
||||
Env = 0,
|
||||
Json = 1,
|
||||
Raw = 2,
|
||||
Test = 3
|
||||
export function Content({ config, env, onTemplateSaved }: ContentProps) {
|
||||
const [activeTab, setActiveTab] = useState('env');
|
||||
|
||||
// Validate placeholders for warning badge
|
||||
const missingPlaceholders = config.validatePlaceholders();
|
||||
const hasValidationWarnings = missingPlaceholders.length > 0;
|
||||
|
||||
const tabs: Array<{ id: string; label: string; badge?: string | number; badgeVariant?: 'warning' | 'danger' }> = [
|
||||
{ id: 'env', label: 'Env' },
|
||||
{
|
||||
id: 'template',
|
||||
label: 'Content Template',
|
||||
badge: hasValidationWarnings ? '!' : undefined,
|
||||
badgeVariant: hasValidationWarnings ? 'warning' : undefined,
|
||||
},
|
||||
{ id: 'raw', label: 'Raw Template' },
|
||||
{ id: 'test', label: 'Test-filled' },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-xl shadow-lg border border-slate-200 overflow-hidden">
|
||||
<Tabs tabs={tabs} activeTab={activeTab} onChange={setActiveTab} />
|
||||
|
||||
<div className="p-4">
|
||||
<TabPanel isActive={activeTab === 'env'}>
|
||||
<ContentParams env={env} />
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel isActive={activeTab === 'template'}>
|
||||
<ConfigTemplateEditor config={config} onSaved={onTemplateSaved} />
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel isActive={activeTab === 'raw'}>
|
||||
<ContentRaw config={config} env={env} />
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel isActive={activeTab === 'test'}>
|
||||
<ContentTest config={config} env={env} />
|
||||
</TabPanel>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ContentTabs(props: { onSelected: (id: ContentType) => void, selectedTab: ContentType, hasValidationWarnings: boolean }) {
|
||||
function clickHandler(type: ContentType) {
|
||||
props.onSelected(type);
|
||||
}
|
||||
|
||||
function isActive(type: ContentType): string {
|
||||
return type == props.selectedTab ? " active" : " ";
|
||||
}
|
||||
|
||||
return (
|
||||
<ul className="nav nav-pills nav-fill">
|
||||
<li className="nav-item">
|
||||
<a className={"nav-link" + isActive(ContentType.Env)} aria-current="page" href="#" onClick={() => clickHandler(ContentType.Env)}>Env</a>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<a className={"nav-link" + isActive(ContentType.Json)} href="#" onClick={() => clickHandler(ContentType.Json)} >
|
||||
Content Template
|
||||
{props.hasValidationWarnings && (
|
||||
<span className="badge bg-warning text-dark ms-1">!</span>
|
||||
)}
|
||||
</a>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<a className={"nav-link" + isActive(ContentType.Raw)} href="#" onClick={() => clickHandler(ContentType.Raw)}>Raw template</a>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<a className={"nav-link" + isActive(ContentType.Test)} href="#" onClick={() => clickHandler(ContentType.Test)}>Test-filled template</a>
|
||||
</li>
|
||||
</ul>
|
||||
)
|
||||
function ContentParams({ env }: { env: Env }) {
|
||||
const xml = Builder.getEnv(env).build();
|
||||
|
||||
return (
|
||||
<div className="animate-fade-in">
|
||||
<CodeBlock code={xml} language="xml" maxHeight="500px" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ContentRaw(props: { config: Config, env: Env }) {
|
||||
const envsXml = Builder.getEnvs(props.config.envs);
|
||||
const templateContent = props.config.template.content;
|
||||
function ContentRaw({ config }: { config: Config; env: Env }) {
|
||||
const envsXml = Builder.getEnvs(config.envs);
|
||||
const templateContent = config.template.content;
|
||||
|
||||
const xml = `<engine>
|
||||
const xml = `<engine>
|
||||
${envsXml}
|
||||
<template>
|
||||
${templateContent}
|
||||
</template>
|
||||
</engine>`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Highlight className="language-xml">
|
||||
{xml}
|
||||
</Highlight>
|
||||
</>
|
||||
)
|
||||
return (
|
||||
<div className="animate-fade-in">
|
||||
<CodeBlock code={xml} language="xml" maxHeight="500px" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ContentTest(props: { config: Config, env: Env }) {
|
||||
const [selectedEnvId, setSelectedEnvId] = useState(props.env.id);
|
||||
const selectedEnv = props.config.envs.find(e => e.id === selectedEnvId) ?? props.env;
|
||||
function ContentTest({ config, env }: { config: Config; env: Env }) {
|
||||
const [selectedEnvId, setSelectedEnvId] = useState(env.id ?? 0);
|
||||
const selectedEnv = config.envs.find(e => e.id === selectedEnvId) ?? env;
|
||||
|
||||
const filledTemplate = fillTemplate(props.config, selectedEnv);
|
||||
const filledTemplate = fillTemplate(config, selectedEnv);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-2">
|
||||
<label className="form-label">Select Environment:</label>
|
||||
<select
|
||||
className="form-select w-auto d-inline-block"
|
||||
value={selectedEnvId}
|
||||
onChange={(e) => setSelectedEnvId(Number(e.target.value))}
|
||||
>
|
||||
{props.config.envs.map(env => (
|
||||
<option key={env.id} value={env.id}>{env.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<Highlight className="language-json">
|
||||
{filledTemplate}
|
||||
</Highlight>
|
||||
</>
|
||||
)
|
||||
const selectOptions = config.envs.map((e) => ({
|
||||
value: e.id ?? 0,
|
||||
label: e.name ?? 'Unknown',
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className="animate-fade-in space-y-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="text-sm font-medium text-slate-700">Select Environment:</label>
|
||||
<select
|
||||
className="px-3 py-1.5 border border-slate-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
value={selectedEnvId}
|
||||
onChange={(e) => setSelectedEnvId(Number(e.target.value))}
|
||||
>
|
||||
{selectOptions.map((opt) => (
|
||||
<option key={opt.value} value={opt.value}>{opt.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<CodeBlock code={filledTemplate} language="json" maxHeight="500px" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function fillTemplate(config: Config, env: Env): string {
|
||||
const defaultEnv = config.envs.find(e => e.name === "DEFAULT");
|
||||
const paramMap = new Map<string, string>();
|
||||
const defaultEnv = config.envs.find((e) => e.name === 'DEFAULT');
|
||||
const paramMap = new Map<string, string>();
|
||||
|
||||
// First, load DEFAULT values as fallback
|
||||
if (defaultEnv) {
|
||||
for (const param of defaultEnv.params) {
|
||||
if (param.name && param.value !== undefined) {
|
||||
paramMap.set(param.name, param.value);
|
||||
}
|
||||
}
|
||||
// Load DEFAULT values first
|
||||
if (defaultEnv) {
|
||||
for (const param of defaultEnv.params) {
|
||||
if (param.name && param.value !== undefined) {
|
||||
paramMap.set(param.name, param.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Then, override with selected environment values (precedence)
|
||||
for (const param of env.params) {
|
||||
if (param.name && param.value !== undefined) {
|
||||
paramMap.set(param.name, param.value);
|
||||
}
|
||||
// Override with selected environment values
|
||||
for (const param of env.params) {
|
||||
if (param.name && param.value !== undefined) {
|
||||
paramMap.set(param.name, param.value);
|
||||
}
|
||||
}
|
||||
|
||||
let filledTemplate = config.template.content;
|
||||
const placeholderRegex = /@(\w+)@/g;
|
||||
let filledTemplate = config.template.content;
|
||||
const placeholderRegex = /@(\w+)@/g;
|
||||
|
||||
filledTemplate = filledTemplate.replace(placeholderRegex, (_, paramName) => {
|
||||
if (paramName === Config.ENV_NAME_PARAM) {
|
||||
return env.name ?? "--NO-VALUE--";
|
||||
}
|
||||
return paramMap.get(paramName) ?? "--NO-VALUE--";
|
||||
});
|
||||
filledTemplate = filledTemplate.replace(placeholderRegex, (_, paramName) => {
|
||||
if (paramName === Config.ENV_NAME_PARAM) {
|
||||
return env.name ?? '--NO-VALUE--';
|
||||
}
|
||||
return paramMap.get(paramName) ?? '--NO-VALUE--';
|
||||
});
|
||||
|
||||
return filledTemplate;
|
||||
return filledTemplate;
|
||||
}
|
||||
|
||||
function ContentParams(props: { env: Env }) {
|
||||
const bldr = Builder.getEnv(props.env);
|
||||
|
||||
return (
|
||||
<Highlight className="language-xml">
|
||||
{bldr.build()}
|
||||
</Highlight>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user