diff --git a/ui/src/pages/AppsTab/AppDeploymentPage/AppDeploymentPage.module.css b/ui/src/pages/AppsTab/AppDeploymentPage/AppDeploymentPage.module.css index e887b96e..4b050e0e 100644 --- a/ui/src/pages/AppsTab/AppDeploymentPage/AppDeploymentPage.module.css +++ b/ui/src/pages/AppsTab/AppDeploymentPage/AppDeploymentPage.module.css @@ -6,6 +6,25 @@ min-height: 100%; } +/* Tabs + content grouped together with no internal gap */ +.tabGroup { + display: flex; + flex-direction: column; + flex: 1 1 auto; + min-height: 0; +} + +/* The tab-content card sits flush against the Tabs strip — no gap */ +.tabContent { + border: 1px solid var(--border); + border-top: none; + border-radius: 0 0 6px 6px; + padding: 16px; + background: var(--bg-surface); + flex: 1 1 auto; + min-height: 0; +} + .section { border: 1px solid var(--border); border-radius: 6px; @@ -246,6 +265,17 @@ /* HistoryDisclosure */ .historyRow { margin-top: 16px; } +/* Environment pill (Identity section) */ +.envPill { + display: inline-block; + padding: 2px 10px; + border-radius: 10px; + font-size: 12px; + font-weight: 500; + color: var(--text-inverse, white); + width: max-content; +} + /* Env vars list */ .envVarsList { display: flex; diff --git a/ui/src/pages/AppsTab/AppDeploymentPage/ConfigTabs/MonitoringTab.tsx b/ui/src/pages/AppsTab/AppDeploymentPage/ConfigTabs/MonitoringTab.tsx index 2001981a..61b60d69 100644 --- a/ui/src/pages/AppsTab/AppDeploymentPage/ConfigTabs/MonitoringTab.tsx +++ b/ui/src/pages/AppsTab/AppDeploymentPage/ConfigTabs/MonitoringTab.tsx @@ -42,6 +42,27 @@ export function MonitoringTab({ value, onChange, disabled }: Props) { ]} /> + Max Payload Size +
+ update('payloadSize', e.target.value)} + className={styles.inputMd} + placeholder="e.g. 4" + /> + {value.metricsEnabled ? 'Enabled' : 'Disabled'} + Interval + update('metricsInterval', e.target.value)} + className={styles.inputXs} + placeholder="60" + /> + s
Sampling Rate @@ -90,6 +120,30 @@ export function MonitoringTab({ value, onChange, disabled }: Props) { {value.compressSuccess ? 'Enabled' : 'Disabled'} + + Replay +
+ !disabled && update('replayEnabled', !value.replayEnabled)} + disabled={disabled} + /> + + {value.replayEnabled ? 'Enabled' : 'Disabled'} + +
+ + Route Control +
+ !disabled && update('routeControlEnabled', !value.routeControlEnabled)} + disabled={disabled} + /> + + {value.routeControlEnabled ? 'Enabled' : 'Disabled'} + +
); } diff --git a/ui/src/pages/AppsTab/AppDeploymentPage/ConfigTabs/VariablesTab.tsx b/ui/src/pages/AppsTab/AppDeploymentPage/ConfigTabs/VariablesTab.tsx index b29884ad..c1edc0dd 100644 --- a/ui/src/pages/AppsTab/AppDeploymentPage/ConfigTabs/VariablesTab.tsx +++ b/ui/src/pages/AppsTab/AppDeploymentPage/ConfigTabs/VariablesTab.tsx @@ -1,6 +1,5 @@ -import { Button, Input } from '@cameleer/design-system'; +import { EnvEditor } from '../../../../components/EnvEditor'; import type { VariablesFormState } from '../hooks/useDeploymentPageState'; -import styles from '../AppDeploymentPage.module.css'; interface Props { value: VariablesFormState; @@ -9,52 +8,11 @@ interface Props { } export function VariablesTab({ value, onChange, disabled }: Props) { - function updateRow(index: number, field: 'key' | 'value', v: string) { - const next = value.envVars.map((row, i) => (i === index ? { ...row, [field]: v } : row)); - onChange({ envVars: next }); - } - - function removeRow(index: number) { - onChange({ envVars: value.envVars.filter((_, i) => i !== index) }); - } - - function addRow() { - onChange({ envVars: [...value.envVars, { key: '', value: '' }] }); - } - return ( -
-
- {value.envVars.map((row, i) => ( -
- updateRow(i, 'key', e.target.value)} - placeholder="KEY" - /> - updateRow(i, 'value', e.target.value)} - placeholder="value" - /> - -
- ))} -
-
- -
-
+ onChange({ envVars: entries })} + disabled={disabled} + /> ); } diff --git a/ui/src/pages/AppsTab/AppDeploymentPage/IdentitySection.tsx b/ui/src/pages/AppsTab/AppDeploymentPage/IdentitySection.tsx index d4567634..2176b3da 100644 --- a/ui/src/pages/AppsTab/AppDeploymentPage/IdentitySection.tsx +++ b/ui/src/pages/AppsTab/AppDeploymentPage/IdentitySection.tsx @@ -1,7 +1,8 @@ import { useRef } from 'react'; -import { SectionHeader, Input, MonoText, Button, Badge } from '@cameleer/design-system'; +import { SectionHeader, Input, MonoText, Button } from '@cameleer/design-system'; import type { App, AppVersion } from '../../../api/queries/admin/apps'; import type { Environment } from '../../../api/queries/admin/environments'; +import { envColorVar } from '../../../components/env-colors'; import styles from './AppDeploymentPage.module.css'; function slugify(name: string): string { @@ -63,7 +64,13 @@ export function IdentitySection({ {slug || '...'} Environment - + + {environment.displayName} + External URL {externalUrl} diff --git a/ui/src/pages/AppsTab/AppDeploymentPage/hooks/useDeploymentPageState.ts b/ui/src/pages/AppsTab/AppDeploymentPage/hooks/useDeploymentPageState.ts index f6b0f6ee..1e8b7dcb 100644 --- a/ui/src/pages/AppsTab/AppDeploymentPage/hooks/useDeploymentPageState.ts +++ b/ui/src/pages/AppsTab/AppDeploymentPage/hooks/useDeploymentPageState.ts @@ -6,11 +6,16 @@ import type { App } from '../../../../api/queries/admin/apps'; export interface MonitoringFormState { engineLevel: string; payloadCaptureMode: string; + payloadSize: string; + payloadUnit: string; applicationLogLevel: string; agentLogLevel: string; metricsEnabled: boolean; + metricsInterval: string; samplingRate: string; compressSuccess: boolean; + replayEnabled: boolean; + routeControlEnabled: boolean; } export interface ResourcesFormState { @@ -48,11 +53,16 @@ const defaultForm: DeploymentPageFormState = { monitoring: { engineLevel: 'REGULAR', payloadCaptureMode: 'BOTH', + payloadSize: '4', + payloadUnit: 'KB', applicationLogLevel: 'INFO', agentLogLevel: 'INFO', metricsEnabled: true, + metricsInterval: '60', samplingRate: '1.0', compressSuccess: false, + replayEnabled: true, + routeControlEnabled: true, }, resources: { memoryLimit: '512', memoryReserve: '', cpuRequest: '500', cpuLimit: '', @@ -80,11 +90,16 @@ export function useDeploymentPageState( monitoring: { engineLevel: (agentConfig?.engineLevel as string) ?? defaultForm.monitoring.engineLevel, payloadCaptureMode: (agentConfig?.payloadCaptureMode as string) ?? defaultForm.monitoring.payloadCaptureMode, + payloadSize: defaultForm.monitoring.payloadSize, + payloadUnit: defaultForm.monitoring.payloadUnit, applicationLogLevel: (agentConfig?.applicationLogLevel as string) ?? defaultForm.monitoring.applicationLogLevel, agentLogLevel: (agentConfig?.agentLogLevel as string) ?? defaultForm.monitoring.agentLogLevel, metricsEnabled: agentConfig?.metricsEnabled ?? defaultForm.monitoring.metricsEnabled, + metricsInterval: defaultForm.monitoring.metricsInterval, samplingRate: agentConfig?.samplingRate !== undefined ? String(agentConfig.samplingRate) : defaultForm.monitoring.samplingRate, compressSuccess: agentConfig?.compressSuccess ?? defaultForm.monitoring.compressSuccess, + replayEnabled: defaultForm.monitoring.replayEnabled, + routeControlEnabled: defaultForm.monitoring.routeControlEnabled, }, resources: { memoryLimit: String(merged.memoryLimitMb ?? defaultForm.resources.memoryLimit), diff --git a/ui/src/pages/AppsTab/AppDeploymentPage/index.tsx b/ui/src/pages/AppsTab/AppDeploymentPage/index.tsx index d4b612d3..3ae36f6d 100644 --- a/ui/src/pages/AppsTab/AppDeploymentPage/index.tsx +++ b/ui/src/pages/AppsTab/AppDeploymentPage/index.tsx @@ -328,11 +328,16 @@ export default function AppDeploymentPage() { monitoring: { engineLevel: (a.engineLevel as string) ?? prev.monitoring.engineLevel, payloadCaptureMode: (a.payloadCaptureMode as string) ?? prev.monitoring.payloadCaptureMode, + payloadSize: prev.monitoring.payloadSize, + payloadUnit: prev.monitoring.payloadUnit, applicationLogLevel: (a.applicationLogLevel as string) ?? prev.monitoring.applicationLogLevel, agentLogLevel: (a.agentLogLevel as string) ?? prev.monitoring.agentLogLevel, metricsEnabled: (a.metricsEnabled as boolean) ?? prev.monitoring.metricsEnabled, + metricsInterval: prev.monitoring.metricsInterval, samplingRate: a.samplingRate !== undefined ? String(a.samplingRate) : prev.monitoring.samplingRate, compressSuccess: (a.compressSuccess as boolean) ?? prev.monitoring.compressSuccess, + replayEnabled: prev.monitoring.replayEnabled, + routeControlEnabled: prev.monitoring.routeControlEnabled, }, resources: { memoryLimit: c.memoryLimitMb !== undefined ? String(c.memoryLimitMb) : prev.resources.memoryLimit, @@ -431,13 +436,14 @@ export default function AppDeploymentPage() { )} {/* ── Config tabs ── */} +
setTab(v as TabKey)} /> -
+
{tab === 'monitoring' && ( )}
+
{/* ── Stop confirmation dialog ── */}