mirror of
https://github.com/portainer/portainer.git
synced 2025-07-24 15:59:41 +02:00
refactor(app): migrate env var form section [EE-6232] (#10499)
* refactor(app): migrate env var form section [EE-6232] * allow undoing delete in inputlist
This commit is contained in:
parent
6228314e3c
commit
488393007f
16 changed files with 274 additions and 209 deletions
|
@ -0,0 +1,59 @@
|
|||
import { FormError } from '../FormError';
|
||||
import { InputLabeled } from '../Input/InputLabeled';
|
||||
import { ItemProps } from '../InputList';
|
||||
|
||||
import { EnvVar } from './types';
|
||||
|
||||
export function EnvironmentVariableItem({
|
||||
item,
|
||||
onChange,
|
||||
disabled,
|
||||
error,
|
||||
readOnly,
|
||||
index,
|
||||
}: ItemProps<EnvVar>) {
|
||||
return (
|
||||
<div className="relative flex w-full flex-col">
|
||||
<div className="flex w-full items-start gap-2">
|
||||
<div className="w-1/2">
|
||||
<InputLabeled
|
||||
className="w-full"
|
||||
label="name"
|
||||
required
|
||||
value={item.name}
|
||||
onChange={(e) => handleChange({ name: e.target.value })}
|
||||
disabled={disabled}
|
||||
needsDeletion={item.needsDeletion}
|
||||
readOnly={readOnly}
|
||||
placeholder="e.g. FOO"
|
||||
size="small"
|
||||
id={`env-name${index}`}
|
||||
/>
|
||||
{error && (
|
||||
<div>
|
||||
<FormError className="mt-1 !mb-0">
|
||||
{Object.values(error)[0]}
|
||||
</FormError>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<InputLabeled
|
||||
className="w-1/2"
|
||||
label="value"
|
||||
value={item.value}
|
||||
onChange={(e) => handleChange({ value: e.target.value })}
|
||||
disabled={disabled}
|
||||
needsDeletion={item.needsDeletion}
|
||||
readOnly={readOnly}
|
||||
placeholder="e.g. bar"
|
||||
size="small"
|
||||
id={`env-value${index}`}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
function handleChange(partial: Partial<EnvVar>) {
|
||||
onChange({ ...item, ...partial });
|
||||
}
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
import { useState } from 'react';
|
||||
import { array, object, SchemaOf, string } from 'yup';
|
||||
import { array, boolean, object, SchemaOf, string } from 'yup';
|
||||
|
||||
import { ArrayError } from '../InputList/InputList';
|
||||
import { buildUniquenessTest } from '../validate-unique';
|
||||
|
||||
import { AdvancedMode } from './AdvancedMode';
|
||||
import { SimpleMode } from './SimpleMode';
|
||||
|
@ -11,21 +12,24 @@ export function EnvironmentVariablesFieldset({
|
|||
onChange,
|
||||
values,
|
||||
errors,
|
||||
canUndoDelete,
|
||||
}: {
|
||||
values: Value;
|
||||
onChange(value: Value): void;
|
||||
errors?: ArrayError<Value>;
|
||||
canUndoDelete?: boolean;
|
||||
}) {
|
||||
const [simpleMode, setSimpleMode] = useState(true);
|
||||
|
||||
return (
|
||||
<div className="col-sm-12">
|
||||
<>
|
||||
{simpleMode ? (
|
||||
<SimpleMode
|
||||
onAdvancedModeClick={() => setSimpleMode(false)}
|
||||
onChange={onChange}
|
||||
value={values}
|
||||
errors={errors}
|
||||
canUndoDelete={canUndoDelete}
|
||||
/>
|
||||
) : (
|
||||
<AdvancedMode
|
||||
|
@ -34,7 +38,7 @@ export function EnvironmentVariablesFieldset({
|
|||
value={values}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -43,6 +47,14 @@ export function envVarValidation(): SchemaOf<Value> {
|
|||
object({
|
||||
name: string().required('Name is required'),
|
||||
value: string().default(''),
|
||||
needsDeletion: boolean().default(false),
|
||||
})
|
||||
).test(
|
||||
'unique',
|
||||
'This environment variable is already defined.',
|
||||
buildUniquenessTest(
|
||||
() => 'This environment variable is already defined.',
|
||||
'name'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -34,11 +34,13 @@ export function EnvironmentVariablesPanel({
|
|||
</div>
|
||||
)}
|
||||
|
||||
<EnvironmentVariablesFieldset
|
||||
values={values}
|
||||
onChange={onChange}
|
||||
errors={errors}
|
||||
/>
|
||||
<div className="col-sm-12">
|
||||
<EnvironmentVariablesFieldset
|
||||
values={values}
|
||||
onChange={onChange}
|
||||
errors={errors}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{showHelpMessage && (
|
||||
<div className="col-sm-12">
|
||||
|
|
|
@ -7,24 +7,24 @@ import { Button } from '@@/buttons';
|
|||
import { TextTip } from '@@/Tip/TextTip';
|
||||
import { FileUploadField } from '@@/form-components/FileUpload';
|
||||
import { InputList } from '@@/form-components/InputList';
|
||||
import { ArrayError, ItemProps } from '@@/form-components/InputList/InputList';
|
||||
import { InputLabeled } from '@@/form-components/Input/InputLabeled';
|
||||
import { ArrayError } from '@@/form-components/InputList/InputList';
|
||||
|
||||
import { FormError } from '../FormError';
|
||||
|
||||
import { type EnvVar, type Value } from './types';
|
||||
import type { Value } from './types';
|
||||
import { parseDotEnvFile } from './utils';
|
||||
import { EnvironmentVariableItem } from './EnvironmentVariableItem';
|
||||
|
||||
export function SimpleMode({
|
||||
value,
|
||||
onChange,
|
||||
onAdvancedModeClick,
|
||||
errors,
|
||||
canUndoDelete,
|
||||
}: {
|
||||
value: Value;
|
||||
onChange: (value: Value) => void;
|
||||
onAdvancedModeClick: () => void;
|
||||
errors?: ArrayError<Value>;
|
||||
canUndoDelete?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
|
@ -47,13 +47,17 @@ export function SimpleMode({
|
|||
onChange={onChange}
|
||||
value={value}
|
||||
isAddButtonHidden
|
||||
item={Item}
|
||||
item={EnvironmentVariableItem}
|
||||
errors={errors}
|
||||
canUndoDelete={canUndoDelete}
|
||||
/>
|
||||
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
onClick={() => onChange([...value, { name: '', value: '' }])}
|
||||
onClick={() =>
|
||||
onChange([...value, { name: '', value: '', needsDeletion: false }])
|
||||
}
|
||||
className="!ml-0"
|
||||
color="default"
|
||||
icon={Plus}
|
||||
>
|
||||
|
@ -66,54 +70,6 @@ export function SimpleMode({
|
|||
);
|
||||
}
|
||||
|
||||
function Item({
|
||||
item,
|
||||
onChange,
|
||||
disabled,
|
||||
error,
|
||||
readOnly,
|
||||
index,
|
||||
}: ItemProps<EnvVar>) {
|
||||
return (
|
||||
<div className="relative flex w-full flex-col">
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<InputLabeled
|
||||
className="w-1/2"
|
||||
label="name"
|
||||
value={item.name}
|
||||
onChange={(e) => handleChange({ name: e.target.value })}
|
||||
disabled={disabled}
|
||||
readOnly={readOnly}
|
||||
placeholder="e.g. FOO"
|
||||
size="small"
|
||||
id={`env-name${index}`}
|
||||
/>
|
||||
<InputLabeled
|
||||
className="w-1/2"
|
||||
label="value"
|
||||
value={item.value}
|
||||
onChange={(e) => handleChange({ value: e.target.value })}
|
||||
disabled={disabled}
|
||||
readOnly={readOnly}
|
||||
placeholder="e.g. bar"
|
||||
size="small"
|
||||
id={`env-value${index}`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{!!error && (
|
||||
<div className="absolute -bottom-5">
|
||||
<FormError className="m-0">{Object.values(error)[0]}</FormError>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
function handleChange(partial: Partial<EnvVar>) {
|
||||
onChange({ ...item, ...partial });
|
||||
}
|
||||
}
|
||||
|
||||
function FileEnv({ onChooseFile }: { onChooseFile: (file: Value) => void }) {
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
export interface EnvVar {
|
||||
name: string;
|
||||
value?: string;
|
||||
needsDeletion?: boolean;
|
||||
}
|
||||
|
||||
export type Value = Array<EnvVar>;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue