diff --git a/ui/src/pages/AppsTab/AppDeploymentPage/ConfigTabs/ResourcesTab.tsx b/ui/src/pages/AppsTab/AppDeploymentPage/ConfigTabs/ResourcesTab.tsx new file mode 100644 index 00000000..10ed0d4c --- /dev/null +++ b/ui/src/pages/AppsTab/AppDeploymentPage/ConfigTabs/ResourcesTab.tsx @@ -0,0 +1,244 @@ +import { useState } from 'react'; +import { Select, Input, Toggle } from '@cameleer/design-system'; +import type { ResourcesFormState } from '../hooks/useDeploymentPageState'; +import styles from '../AppDeploymentPage.module.css'; + +interface Props { + value: ResourcesFormState; + onChange: (next: ResourcesFormState) => void; + disabled?: boolean; + isProd?: boolean; +} + +export function ResourcesTab({ value, onChange, disabled, isProd = false }: Props) { + const [newPort, setNewPort] = useState(''); + const [newNetwork, setNewNetwork] = useState(''); + + const update = (key: K, v: ResourcesFormState[K]) => + onChange({ ...value, [key]: v }); + + function addPort() { + const p = parseInt(newPort); + if (p && !value.ports.includes(p)) { + onChange({ ...value, ports: [...value.ports, p] }); + setNewPort(''); + } + } + + function removePort(port: number) { + if (!disabled) update('ports', value.ports.filter((x) => x !== port)); + } + + function addNetwork() { + const v = newNetwork.trim(); + if (v && !value.extraNetworks.includes(v)) { + onChange({ ...value, extraNetworks: [...value.extraNetworks, v] }); + setNewNetwork(''); + } + } + + function removeNetwork(network: string) { + if (!disabled) update('extraNetworks', value.extraNetworks.filter((x) => x !== network)); + } + + return ( +
+ Runtime Type + update('customArgs', e.target.value)} + placeholder="-Xmx256m -Dfoo=bar" + className={styles.inputLg} + /> + + {value.runtimeType === 'native' + ? 'Arguments passed to the native binary' + : 'Additional JVM arguments appended to the start command'} + +
+ + Memory Limit +
+ update('memoryLimit', e.target.value)} + className={styles.inputLg} + placeholder="e.g. 512" + /> + MB +
+ + Memory Reserve +
+
+ update('memoryReserve', e.target.value)} + placeholder="e.g. 256" + className={styles.inputLg} + /> + MB +
+ {!isProd && ( + Available in production environments only + )} +
+ + CPU Request + update('cpuRequest', e.target.value)} + className={styles.inputLg} + placeholder="e.g. 500 millicores" + /> + + CPU Limit +
+ update('cpuLimit', e.target.value)} + placeholder="e.g. 1000" + className={styles.inputLg} + /> + millicores +
+ + Exposed Ports +
+ {value.ports.map((p) => ( + + {p} + + + ))} + setNewPort(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter') { + e.preventDefault(); + addPort(); + } + }} + /> +
+ + App Port + update('appPort', e.target.value)} + className={styles.inputLg} + placeholder="e.g. 8080" + /> + + Replicas + update('replicas', e.target.value)} + className={styles.inputSm} + type="number" + placeholder="1" + /> + + Deploy Strategy + setNewNetwork(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter') { + e.preventDefault(); + addNetwork(); + } + }} + /> + + + Additional Docker networks to join (e.g., monitoring, prometheus) + + + + ); +}