feat: multi-format env var editor for deployment config
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 1m12s
CI / docker (push) Successful in 1m32s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 38s

Replace simple key-value rows with EnvEditor component that supports
editing variables as Table, Properties, YAML, or .env format.
Switching views converts data seamlessly. Includes file import
(drag-and-drop .properties/.yaml/.env) with auto-detect and merge.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-11 08:16:09 +02:00
parent e9ce828e10
commit 1a45235e30
6 changed files with 485 additions and 36 deletions

View File

@@ -17,6 +17,7 @@ import {
useToast,
} from '@cameleer/design-system';
import type { Column } from '@cameleer/design-system';
import { EnvEditor } from '../../components/EnvEditor';
import { useEnvironmentStore } from '../../api/environment-store';
import { useEnvironments } from '../../api/queries/admin/environments';
import {
@@ -366,19 +367,7 @@ function CreateAppView({ environments, selectedEnv }: { environments: Environmen
{configTab === 'variables' && (
<div className={sectionStyles.section}>
<SectionHeader>Variables</SectionHeader>
{envVars.map((v, i) => (
<div key={i} className={styles.envVarRow}>
<Input disabled={busy} value={v.key} onChange={(e) => {
const next = [...envVars]; next[i] = { ...v, key: e.target.value }; setEnvVars(next);
}} className={styles.envVarKey} placeholder="KEY" />
<Input disabled={busy} value={v.value} onChange={(e) => {
const next = [...envVars]; next[i] = { ...v, value: e.target.value }; setEnvVars(next);
}} className={styles.envVarValue} placeholder="value" />
<button className={styles.envVarDelete} disabled={busy}
onClick={() => !busy && setEnvVars(envVars.filter((_, j) => j !== i))}>&times;</button>
</div>
))}
<Button size="sm" variant="secondary" disabled={busy} onClick={() => setEnvVars([...envVars, { key: '', value: '' }])}>+ Add Variable</Button>
<EnvEditor value={envVars} onChange={setEnvVars} disabled={busy} />
</div>
)}
@@ -1015,22 +1004,7 @@ function ConfigSubTab({ app, environment }: { app: App; environment?: Environmen
{configTab === 'variables' && (
<div className={sectionStyles.section}>
<SectionHeader>Variables</SectionHeader>
{envVars.map((v, i) => (
<div key={i} className={styles.envVarRow}>
<Input disabled={!editing} value={v.key} onChange={(e) => {
const next = [...envVars]; next[i] = { ...v, key: e.target.value }; setEnvVars(next);
}} className={styles.envVarKey} placeholder="KEY" />
<Input disabled={!editing} value={v.value} onChange={(e) => {
const next = [...envVars]; next[i] = { ...v, value: e.target.value }; setEnvVars(next);
}} className={styles.envVarValue} placeholder="value" />
<button className={styles.envVarDelete} disabled={!editing}
onClick={() => editing && setEnvVars(envVars.filter((_, j) => j !== i))}>&times;</button>
</div>
))}
{editing && (
<Button size="sm" variant="secondary" onClick={() => setEnvVars([...envVars, { key: '', value: '' }])}>+ Add Variable</Button>
)}
{envVars.length === 0 && !editing && <EmptyState title="No variables" description="No environment variables configured." />}
<EnvEditor value={envVars} onChange={setEnvVars} disabled={!editing} />
</div>
)}