1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-21 14:29:40 +02:00

fix(app/edge-stack): edge stack create form validation (#343)

This commit is contained in:
LP B 2025-01-24 17:02:52 +01:00 committed by GitHub
parent 3ca5ab180f
commit b753371700
6 changed files with 38 additions and 43 deletions

View file

@ -29,13 +29,16 @@ export function CreateForm() {
const [webhookId] = useState(() => createWebhookId()); const [webhookId] = useState(() => createWebhookId());
const [templateParams, setTemplateParams] = useTemplateParams(); const [templateParams, setTemplateParams] = useTemplateParams();
const templateQuery = useTemplate(templateParams.type, templateParams.id); const templateQuery = useTemplate(
templateParams.templateType,
templateParams.templateId
);
const validation = useValidation(templateQuery); const validation = useValidation(templateQuery);
const mutation = useCreate({ const mutation = useCreate({
webhookId, webhookId,
template: templateQuery.customTemplate || templateQuery.appTemplate, template: templateQuery.customTemplate || templateQuery.appTemplate,
templateType: templateParams.type, templateType: templateParams.templateType,
}); });
const initialValues = useInitialValues(templateQuery, templateParams); const initialValues = useInitialValues(templateQuery, templateParams);
@ -53,6 +56,7 @@ export function CreateForm() {
initialValues={initialValues} initialValues={initialValues}
onSubmit={mutation.onSubmit} onSubmit={mutation.onSubmit}
validationSchema={validation} validationSchema={validation}
validateOnMount
> >
<InnerForm <InnerForm
webhookId={webhookId} webhookId={webhookId}
@ -118,8 +122,8 @@ function useInitialValues(
customTemplate: CustomTemplate | undefined; customTemplate: CustomTemplate | undefined;
}, },
templateParams: { templateParams: {
id: number | undefined; templateId: number | undefined;
type: 'app' | 'custom' | undefined; templateType: 'app' | 'custom' | undefined;
} }
) { ) {
const template = templateQuery.customTemplate || templateQuery.appTemplate; const template = templateQuery.customTemplate || templateQuery.appTemplate;
@ -139,7 +143,7 @@ function useInitialValues(
staggerConfig: staggerConfig:
templateQuery.customTemplate?.EdgeSettings?.StaggerConfig ?? templateQuery.customTemplate?.EdgeSettings?.StaggerConfig ??
getDefaultStaggerConfig(), getDefaultStaggerConfig(),
method: templateParams.id ? 'template' : 'editor', method: templateParams.templateId ? 'template' : 'editor',
git: toGitFormModel( git: toGitFormModel(
templateQuery.customTemplate?.GitConfig, templateQuery.customTemplate?.GitConfig,
parseAutoUpdateResponse() parseAutoUpdateResponse()
@ -149,19 +153,19 @@ function useInitialValues(
getDefaultRelativePathModel(), getDefaultRelativePathModel(),
enableWebhook: false, enableWebhook: false,
fileContent: '', fileContent: '',
templateValues: getTemplateValues(templateParams.type, template), templateValues: getTemplateValues(templateParams.templateType, template),
useManifestNamespaces: false, useManifestNamespaces: false,
}), }),
[ [
templateQuery.customTemplate, templateQuery.customTemplate,
templateParams.id, templateParams.templateId,
templateParams.type, templateParams.templateType,
template, template,
] ]
); );
if ( if (
templateParams.id && templateParams.templateId &&
!templateQuery.customTemplate && !templateQuery.customTemplate &&
!templateQuery.appTemplate !templateQuery.appTemplate
) { ) {

View file

@ -17,7 +17,11 @@ import { useCurrentUser } from '@/react/hooks/useUser';
import { relativePathValidation } from '@/react/portainer/gitops/RelativePathFieldset/validation'; import { relativePathValidation } from '@/react/portainer/gitops/RelativePathFieldset/validation';
import { CustomTemplate } from '@/react/portainer/templates/custom-templates/types'; import { CustomTemplate } from '@/react/portainer/templates/custom-templates/types';
import { TemplateViewModel } from '@/react/portainer/templates/app-templates/view-model'; import { TemplateViewModel } from '@/react/portainer/templates/app-templates/view-model';
import { DeployMethod, GitFormModel } from '@/react/portainer/gitops/types'; import {
DeployMethod,
GitFormModel,
RelativePathModel,
} from '@/react/portainer/gitops/types';
import { EnvironmentType } from '@/react/portainer/environments/types'; import { EnvironmentType } from '@/react/portainer/environments/types';
import { envVarValidation } from '@@/form-components/EnvironmentVariablesFieldset'; import { envVarValidation } from '@@/form-components/EnvironmentVariablesFieldset';
@ -133,7 +137,10 @@ export function useValidation({
); );
}, },
}) as SchemaOf<GitFormModel>, }) as SchemaOf<GitFormModel>,
relativePath: relativePathValidation(), relativePath: mixed().when('method', {
is: 'repository',
then: () => relativePathValidation(),
}) as SchemaOf<RelativePathModel>,
useManifestNamespaces: boolean().default(false), useManifestNamespaces: boolean().default(false),
}) })
), ),

View file

@ -28,8 +28,8 @@ const buildMethods = [editor, upload, git, edgeStackTemplate] as const;
interface Props { interface Props {
webhookId: string; webhookId: string;
onChangeTemplate: (change: { onChangeTemplate: (change: {
type: 'app' | 'custom' | undefined; templateType: 'app' | 'custom' | undefined;
id: number | undefined; templateId: number | undefined;
}) => void; }) => void;
} }
@ -76,8 +76,8 @@ export function DockerComposeForm({ webhookId, onChangeTemplate }: Props) {
values.templateValues values.templateValues
); );
onChangeTemplate({ onChangeTemplate({
id: templateValues.templateId, templateId: templateValues.templateId,
type: templateValues.type, templateType: templateValues.type,
}); });
setValues((values) => ({ setValues((values) => ({
...values, ...values,
@ -145,6 +145,7 @@ export function DockerComposeForm({ webhookId, onChangeTemplate }: Props) {
<FormSection title="Advanced configurations"> <FormSection title="Advanced configurations">
<RelativePathFieldset <RelativePathFieldset
values={values.relativePath} values={values.relativePath}
errors={errors.relativePath}
gitModel={values.git} gitModel={values.git}
onChange={(relativePath) => onChange={(relativePath) =>
setValues((values) => ({ setValues((values) => ({

View file

@ -29,11 +29,11 @@ export function InnerForm({
webhookId: string; webhookId: string;
isLoading: boolean; isLoading: boolean;
onChangeTemplate: ({ onChangeTemplate: ({
type, templateType,
id, templateId,
}: { }: {
type: 'app' | 'custom' | undefined; templateType: 'app' | 'custom' | undefined;
id: number | undefined; templateId: number | undefined;
}) => void; }) => void;
}) { }) {
const { values, setFieldValue, errors, setValues, setFieldError, isValid } = const { values, setFieldValue, errors, setValues, setFieldError, isValid } =
@ -128,6 +128,7 @@ export function InnerForm({
<StaggerFieldset <StaggerFieldset
isEdit={false} isEdit={false}
values={values.staggerConfig} values={values.staggerConfig}
errors={errors.staggerConfig}
onChange={(newStaggerValues) => onChange={(newStaggerValues) =>
setFieldValue('staggerConfig', newStaggerValues) setFieldValue('staggerConfig', newStaggerValues)
} }

View file

@ -1,15 +1,14 @@
import { useParamsState } from '@/react/hooks/useParamState'; import { useParamsState } from '@/react/hooks/useParamState';
export function useTemplateParams() { export function useTemplateParams() {
const [{ id, type }, setTemplateParams] = useParamsState( const [{ templateId, templateType }, setTemplateParams] = useParamsState(
['templateId', 'templateType'],
(params) => ({ (params) => ({
id: parseTemplateId(params.templateId), templateId: parseTemplateId(params.templateId),
type: parseTemplateType(params.templateType), templateType: parseTemplateType(params.templateType),
}) })
); );
return [{ id, type }, setTemplateParams] as const; return [{ templateId, templateType }, setTemplateParams] as const;
} }
function parseTemplateId(param?: string) { function parseTemplateId(param?: string) {

View file

@ -22,32 +22,15 @@ export function useParamState<T>(
/** Use this when you need to use/update multiple params at once. */ /** Use this when you need to use/update multiple params at once. */
export function useParamsState<T extends Record<string, unknown>>( export function useParamsState<T extends Record<string, unknown>>(
params: string[],
parseParams: (params: Record<string, string | undefined>) => T parseParams: (params: Record<string, string | undefined>) => T
) { ) {
const { params: stateParams } = useCurrentStateAndParams(); const { params: stateParams } = useCurrentStateAndParams();
const router = useRouter(); const router = useRouter();
const state = parseParams( const state = parseParams(stateParams);
params.reduce(
(acc, param) => {
acc[param] = stateParams[param];
return acc;
},
{} as Record<string, string | undefined>
)
);
function setState(newState: Partial<T>) { function setState(newState: Partial<T>) {
const newStateParams = Object.entries(newState).reduce( router.stateService.go('.', newState, { reload: false });
(acc, [key, value]) => {
acc[key] = value;
return acc;
},
{} as Record<string, unknown>
);
router.stateService.go('.', newStateParams, { reload: false });
} }
return [state, setState] as const; return [state, setState] as const;