245 lines
7.7 KiB
TypeScript
245 lines
7.7 KiB
TypeScript
|
|
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 = <K extends keyof ResourcesFormState>(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 (
|
||
|
|
<div className={styles.configGrid}>
|
||
|
|
<span className={styles.configLabel}>Runtime Type</span>
|
||
|
|
<Select
|
||
|
|
disabled={disabled}
|
||
|
|
value={value.runtimeType}
|
||
|
|
onChange={(e) => update('runtimeType', e.target.value)}
|
||
|
|
options={[
|
||
|
|
{ value: 'auto', label: 'Auto (detect from JAR)' },
|
||
|
|
{ value: 'spring-boot', label: 'Spring Boot' },
|
||
|
|
{ value: 'quarkus', label: 'Quarkus' },
|
||
|
|
{ value: 'plain-java', label: 'Plain Java' },
|
||
|
|
{ value: 'native', label: 'Native' },
|
||
|
|
]}
|
||
|
|
/>
|
||
|
|
|
||
|
|
<span className={styles.configLabel}>Custom Arguments</span>
|
||
|
|
<div>
|
||
|
|
<Input
|
||
|
|
disabled={disabled}
|
||
|
|
value={value.customArgs}
|
||
|
|
onChange={(e) => update('customArgs', e.target.value)}
|
||
|
|
placeholder="-Xmx256m -Dfoo=bar"
|
||
|
|
className={styles.inputLg}
|
||
|
|
/>
|
||
|
|
<span className={styles.configHint}>
|
||
|
|
{value.runtimeType === 'native'
|
||
|
|
? 'Arguments passed to the native binary'
|
||
|
|
: 'Additional JVM arguments appended to the start command'}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<span className={styles.configLabel}>Memory Limit</span>
|
||
|
|
<div className={styles.configInline}>
|
||
|
|
<Input
|
||
|
|
disabled={disabled}
|
||
|
|
value={value.memoryLimit}
|
||
|
|
onChange={(e) => update('memoryLimit', e.target.value)}
|
||
|
|
className={styles.inputLg}
|
||
|
|
placeholder="e.g. 512"
|
||
|
|
/>
|
||
|
|
<span className={styles.cellMeta}>MB</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<span className={styles.configLabel}>Memory Reserve</span>
|
||
|
|
<div>
|
||
|
|
<div className={styles.configInline}>
|
||
|
|
<Input
|
||
|
|
disabled={!isProd || disabled}
|
||
|
|
value={value.memoryReserve}
|
||
|
|
onChange={(e) => update('memoryReserve', e.target.value)}
|
||
|
|
placeholder="e.g. 256"
|
||
|
|
className={styles.inputLg}
|
||
|
|
/>
|
||
|
|
<span className={styles.cellMeta}>MB</span>
|
||
|
|
</div>
|
||
|
|
{!isProd && (
|
||
|
|
<span className={styles.configHint}>Available in production environments only</span>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<span className={styles.configLabel}>CPU Request</span>
|
||
|
|
<Input
|
||
|
|
disabled={disabled}
|
||
|
|
value={value.cpuRequest}
|
||
|
|
onChange={(e) => update('cpuRequest', e.target.value)}
|
||
|
|
className={styles.inputLg}
|
||
|
|
placeholder="e.g. 500 millicores"
|
||
|
|
/>
|
||
|
|
|
||
|
|
<span className={styles.configLabel}>CPU Limit</span>
|
||
|
|
<div className={styles.configInline}>
|
||
|
|
<Input
|
||
|
|
disabled={disabled}
|
||
|
|
value={value.cpuLimit}
|
||
|
|
onChange={(e) => update('cpuLimit', e.target.value)}
|
||
|
|
placeholder="e.g. 1000"
|
||
|
|
className={styles.inputLg}
|
||
|
|
/>
|
||
|
|
<span className={styles.cellMeta}>millicores</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<span className={styles.configLabel}>Exposed Ports</span>
|
||
|
|
<div className={styles.portPills}>
|
||
|
|
{value.ports.map((p) => (
|
||
|
|
<span key={p} className={styles.portPill}>
|
||
|
|
{p}
|
||
|
|
<button
|
||
|
|
className={styles.portPillDelete}
|
||
|
|
disabled={disabled}
|
||
|
|
onClick={() => removePort(p)}
|
||
|
|
>
|
||
|
|
×
|
||
|
|
</button>
|
||
|
|
</span>
|
||
|
|
))}
|
||
|
|
<input
|
||
|
|
className={styles.portAddInput}
|
||
|
|
disabled={disabled}
|
||
|
|
placeholder="+ port"
|
||
|
|
value={newPort}
|
||
|
|
onChange={(e) => setNewPort(e.target.value)}
|
||
|
|
onKeyDown={(e) => {
|
||
|
|
if (e.key === 'Enter') {
|
||
|
|
e.preventDefault();
|
||
|
|
addPort();
|
||
|
|
}
|
||
|
|
}}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<span className={styles.configLabel}>App Port</span>
|
||
|
|
<Input
|
||
|
|
disabled={disabled}
|
||
|
|
value={value.appPort}
|
||
|
|
onChange={(e) => update('appPort', e.target.value)}
|
||
|
|
className={styles.inputLg}
|
||
|
|
placeholder="e.g. 8080"
|
||
|
|
/>
|
||
|
|
|
||
|
|
<span className={styles.configLabel}>Replicas</span>
|
||
|
|
<Input
|
||
|
|
disabled={disabled}
|
||
|
|
value={value.replicas}
|
||
|
|
onChange={(e) => update('replicas', e.target.value)}
|
||
|
|
className={styles.inputSm}
|
||
|
|
type="number"
|
||
|
|
placeholder="1"
|
||
|
|
/>
|
||
|
|
|
||
|
|
<span className={styles.configLabel}>Deploy Strategy</span>
|
||
|
|
<Select
|
||
|
|
disabled={disabled}
|
||
|
|
value={value.deployStrategy}
|
||
|
|
onChange={(e) => update('deployStrategy', e.target.value)}
|
||
|
|
options={[
|
||
|
|
{ value: 'blue-green', label: 'Blue/Green' },
|
||
|
|
{ value: 'rolling', label: 'Rolling' },
|
||
|
|
]}
|
||
|
|
/>
|
||
|
|
|
||
|
|
<span className={styles.configLabel}>Strip Path Prefix</span>
|
||
|
|
<div className={styles.configInline}>
|
||
|
|
<Toggle
|
||
|
|
checked={value.stripPrefix}
|
||
|
|
onChange={() => !disabled && update('stripPrefix', !value.stripPrefix)}
|
||
|
|
disabled={disabled}
|
||
|
|
/>
|
||
|
|
<span className={value.stripPrefix ? styles.toggleEnabled : styles.toggleDisabled}>
|
||
|
|
{value.stripPrefix ? 'Enabled' : 'Disabled'}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<span className={styles.configLabel}>SSL Offloading</span>
|
||
|
|
<div className={styles.configInline}>
|
||
|
|
<Toggle
|
||
|
|
checked={value.sslOffloading}
|
||
|
|
onChange={() => !disabled && update('sslOffloading', !value.sslOffloading)}
|
||
|
|
disabled={disabled}
|
||
|
|
/>
|
||
|
|
<span className={value.sslOffloading ? styles.toggleEnabled : styles.toggleDisabled}>
|
||
|
|
{value.sslOffloading ? 'Enabled' : 'Disabled'}
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<span className={styles.configLabel}>Extra Networks</span>
|
||
|
|
<div>
|
||
|
|
<div className={styles.portPills}>
|
||
|
|
{value.extraNetworks.map((n) => (
|
||
|
|
<span key={n} className={styles.portPill}>
|
||
|
|
{n}
|
||
|
|
<button
|
||
|
|
className={styles.portPillDelete}
|
||
|
|
disabled={disabled}
|
||
|
|
onClick={() => removeNetwork(n)}
|
||
|
|
>
|
||
|
|
×
|
||
|
|
</button>
|
||
|
|
</span>
|
||
|
|
))}
|
||
|
|
<input
|
||
|
|
className={styles.portAddInput}
|
||
|
|
disabled={disabled}
|
||
|
|
placeholder="+ network"
|
||
|
|
value={newNetwork}
|
||
|
|
onChange={(e) => setNewNetwork(e.target.value)}
|
||
|
|
onKeyDown={(e) => {
|
||
|
|
if (e.key === 'Enter') {
|
||
|
|
e.preventDefault();
|
||
|
|
addNetwork();
|
||
|
|
}
|
||
|
|
}}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
<span className={styles.configHint}>
|
||
|
|
Additional Docker networks to join (e.g., monitoring, prometheus)
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|