fix(edge/stacks): load template [EE-7109] (#11848)

pull/11579/merge
Chaim Lev-Ari 2024-06-16 07:54:00 +03:00 committed by GitHub
parent a28bd349ae
commit 0f5988af49
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 126 additions and 60 deletions

View File

@ -120,10 +120,10 @@ function useTemplate(
id: number | undefined id: number | undefined
) { ) {
const customTemplateQuery = useCustomTemplate(id, { const customTemplateQuery = useCustomTemplate(id, {
enabled: !!id && type === 'custom', enabled: type === 'custom',
}); });
const appTemplateQuery = useAppTemplate(id, { const appTemplateQuery = useAppTemplate(id, {
enabled: !!id && type === 'app', enabled: type === 'app',
}); });
return { return {

View File

@ -13,12 +13,12 @@ import {
edgeStackTemplate, edgeStackTemplate,
upload, upload,
} from '@@/BoxSelector/common-options/build-methods'; } from '@@/BoxSelector/common-options/build-methods';
import { WebEditorForm } from '@@/WebEditorForm';
import { FileUploadForm } from '@@/form-components/FileUpload'; import { FileUploadForm } from '@@/form-components/FileUpload';
import { TemplateFieldset } from './TemplateFieldset/TemplateFieldset'; import { TemplateFieldset } from './TemplateFieldset/TemplateFieldset';
import { useRenderTemplate } from './useRenderTemplate'; import { useRenderTemplate } from './useRenderTemplate';
import { DockerFormValues } from './types'; import { DockerFormValues } from './types';
import { DockerContentField } from './DockerContentField';
const buildMethods = [editor, upload, git, edgeStackTemplate] as const; const buildMethods = [editor, upload, git, edgeStackTemplate] as const;
@ -78,26 +78,12 @@ export function DockerComposeForm({
{(method === editor.value || {(method === editor.value ||
(method === edgeStackTemplate.value && template)) && ( (method === edgeStackTemplate.value && template)) && (
<WebEditorForm <DockerContentField
id="stack-creation-editor"
value={values.fileContent} value={values.fileContent}
onChange={(value) => handleChange({ fileContent: value })} onChange={(value) => handleChange({ fileContent: value })}
yaml
placeholder="Define or paste the content of your docker compose file here"
error={errors?.fileContent}
readonly={method === edgeStackTemplate.value && !!template?.GitConfig} readonly={method === edgeStackTemplate.value && !!template?.GitConfig}
data-cy="stack-creation-editor" error={errors?.fileContent}
> />
You can get more information about Compose file format in the{' '}
<a
href="https://docs.docker.com/compose/compose-file/"
target="_blank"
rel="noreferrer"
>
official documentation
</a>
.
</WebEditorForm>
)} )}
{method === upload.value && ( {method === upload.value && (

View File

@ -0,0 +1,36 @@
import { WebEditorForm } from '@@/WebEditorForm';
export function DockerContentField({
error,
onChange,
readonly,
value,
}: {
value: string;
onChange: (value: string) => void;
error?: string;
readonly?: boolean;
}) {
return (
<WebEditorForm
id="stack-creation-editor"
value={value}
onChange={onChange}
yaml
placeholder="Define or paste the content of your docker compose file here"
error={error}
readonly={readonly}
data-cy="stack-creation-editor"
>
You can get more information about Compose file format in the{' '}
<a
href="https://docs.docker.com/compose/compose-file/"
target="_blank"
rel="noreferrer"
>
official documentation
</a>
.
</WebEditorForm>
);
}

View File

@ -24,7 +24,7 @@ export function TemplateSelector({
) => void; ) => void;
error?: string; error?: string;
}) { }) {
const { options, getTemplate } = useOptions(); const { options, getTemplate, selectedValue } = useOptions(value);
return ( return (
<FormControl label="Template" inputId="template_selector" errors={error}> <FormControl label="Template" inputId="template_selector" errors={error}>
@ -32,10 +32,8 @@ export function TemplateSelector({
inputId="template_selector" inputId="template_selector"
formatGroupLabel={GroupLabel} formatGroupLabel={GroupLabel}
placeholder="Select an Edge stack template" placeholder="Select an Edge stack template"
value={{ options={options}
templateId: value.templateId, value={selectedValue}
type: value.type,
}}
onChange={(value) => { onChange={(value) => {
if (!value) { if (!value) {
onChange(undefined, undefined); onChange(undefined, undefined);
@ -48,14 +46,19 @@ export function TemplateSelector({
} }
onChange(getTemplate({ type, id: templateId }), type); onChange(getTemplate({ type, id: templateId }), type);
}} }}
options={options}
data-cy="edge-stacks-create-template-selector" data-cy="edge-stacks-create-template-selector"
/> />
</FormControl> </FormControl>
); );
} }
function useOptions() { interface Option {
label: string;
templateId?: number;
type: 'app' | 'custom';
}
function useOptions(value: SelectedTemplateValue) {
const customTemplatesQuery = useCustomTemplates({ const customTemplatesQuery = useCustomTemplates({
params: { params: {
edge: true, edge: true,
@ -71,43 +74,75 @@ function useOptions() {
), ),
}); });
const appTemplateOptions: Array<Option> = useMemo(
() =>
appTemplatesQuery.data?.map(
(template) =>
({
label: `${template.Title} - ${template.Description}`,
templateId: template.Id,
type: 'app',
}) satisfies Option
) || [],
[appTemplatesQuery.data]
);
const customTemplateOptions: Array<Option> = useMemo(
() =>
customTemplatesQuery.data && customTemplatesQuery.data.length > 0
? customTemplatesQuery.data.map(
(template) =>
({
label: `${template.Title} - ${template.Description}`,
templateId: template.Id,
type: 'custom' as 'app' | 'custom',
}) satisfies Option
)
: [
{
label: 'No edge custom templates available',
templateId: undefined,
type: 'custom' as 'app' | 'custom',
} satisfies Option,
],
[customTemplatesQuery.data]
);
const options = useMemo( const options = useMemo(
() => () =>
[ [
{ {
label: 'Edge App Templates', label: 'Edge App Templates',
options: options: appTemplateOptions,
appTemplatesQuery.data?.map((template) => ({
label: `${template.Title} - ${template.Description}`,
templateId: template.Id,
type: 'app' as 'app' | 'custom',
})) || [],
}, },
{ {
label: 'Edge Custom Templates', label: 'Edge Custom Templates',
options: options: customTemplateOptions,
customTemplatesQuery.data && customTemplatesQuery.data.length > 0
? customTemplatesQuery.data.map((template) => ({
label: `${template.Title} - ${template.Description}`,
templateId: template.Id,
type: 'custom' as 'app' | 'custom',
}))
: [
{
label: 'No edge custom templates available',
templateId: undefined,
type: undefined,
},
],
}, },
] as const, ] as const,
[appTemplatesQuery.data, customTemplatesQuery.data] [appTemplateOptions, customTemplateOptions]
); );
return { options, getTemplate }; const selectedValue: Option | undefined = useMemo(() => {
if (!value.templateId) {
return undefined;
}
if (value.type === 'app') {
return appTemplateOptions.find(
(template) => template.templateId === value.templateId
);
}
return customTemplateOptions.find(
(template) => template.templateId === value.templateId
);
}, [value.templateId, value.type, customTemplateOptions, appTemplateOptions]);
return { options, getTemplate, selectedValue };
function getTemplate({ type, id }: { type: 'app' | 'custom'; id: number }) { function getTemplate({ type, id }: { type: 'app' | 'custom'; id: number }) {
if (type === 'app') { if (type === 'app') {

View File

@ -16,13 +16,18 @@ export function useRenderTemplate(
templateValues: DockerFormValues['templateValues'], templateValues: DockerFormValues['templateValues'],
setValues: (values: SetStateAction<DockerFormValues>) => void setValues: (values: SetStateAction<DockerFormValues>) => void
) { ) {
const templateQuery = useCustomTemplate(templateValues.templateId); const templateQuery = useCustomTemplate(templateValues.templateId, {
enabled: templateValues.type === 'custom',
});
const template = templateQuery.data; const template = templateQuery.data;
const templateFileQuery = useCustomTemplateFile( const templateFileQuery = useCustomTemplateFile(
templateValues.templateId, templateValues.templateId,
!!template?.GitConfig !!template?.GitConfig,
{
enabled: templateValues.type === 'custom',
}
); );
const [renderedFile, setRenderedFile] = useState<string>(''); const [renderedFile, setRenderedFile] = useState<string>('');

View File

@ -31,7 +31,7 @@ export function useAppTemplates<T = Array<TemplateViewModel>>({
export function useAppTemplate( export function useAppTemplate(
id: AppTemplate['id'] | undefined, id: AppTemplate['id'] | undefined,
{ enabled = true }: { enabled?: boolean } = {} { enabled }: { enabled?: boolean } = {}
) { ) {
const templateListQuery = useAppTemplates({ enabled: !!id && enabled }); const templateListQuery = useAppTemplates({ enabled: !!id && enabled });
@ -39,7 +39,7 @@ export function useAppTemplate(
return { return {
data: template, data: template,
isLoading: templateListQuery.isLoading, isLoading: templateListQuery.isInitialLoading,
error: templateListQuery.error, error: templateListQuery.error,
}; };
} }

View File

@ -19,7 +19,7 @@ export async function getCustomTemplate(id: CustomTemplate['Id']) {
export function useCustomTemplate( export function useCustomTemplate(
id?: CustomTemplate['Id'], id?: CustomTemplate['Id'],
{ enabled = true }: { enabled?: boolean } = {} { enabled }: { enabled?: boolean } = {}
) { ) {
return useQuery(queryKeys.item(id!), () => getCustomTemplate(id!), { return useQuery(queryKeys.item(id!), () => getCustomTemplate(id!), {
...withGlobalError('Unable to retrieve custom template'), ...withGlobalError('Unable to retrieve custom template'),

View File

@ -12,13 +12,17 @@ type CustomTemplateFileContent = {
FileContent: string; FileContent: string;
}; };
export function useCustomTemplateFile(id?: CustomTemplate['Id'], git = false) { export function useCustomTemplateFile(
id?: CustomTemplate['Id'],
git = false,
{ enabled }: { enabled?: boolean } = {}
) {
return useQuery( return useQuery(
id ? queryKeys.file(id, { git }) : [], queryKeys.file(id!, { git }),
() => getCustomTemplateFile({ id: id!, git }), () => getCustomTemplateFile({ id: id!, git }),
{ {
...withGlobalError('Failed to get custom template file'), ...withGlobalError('Failed to get custom template file'),
enabled: !!id, enabled: !!id && enabled,
// there's nothing to do with a new file content, so we're disabling refetch // there's nothing to do with a new file content, so we're disabling refetch
refetchOnReconnect: false, refetchOnReconnect: false,
refetchOnWindowFocus: false, refetchOnWindowFocus: false,