This is an automated email from the ASF dual-hosted git repository.

marat pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-karavan.git

commit be1aeed6dd10ab80124354a1981aae78d7390026
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Fri Feb 2 15:32:44 2024 -0500

    Wizard  #1097
---
 .../main/webui/src/project/beans/BeanWizard.tsx    | 173 ++++++++++++++++++---
 1 file changed, 148 insertions(+), 25 deletions(-)

diff --git 
a/karavan-web/karavan-app/src/main/webui/src/project/beans/BeanWizard.tsx 
b/karavan-web/karavan-app/src/main/webui/src/project/beans/BeanWizard.tsx
index 1adc5d0f..55bbc040 100644
--- a/karavan-web/karavan-app/src/main/webui/src/project/beans/BeanWizard.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/project/beans/BeanWizard.tsx
@@ -16,54 +16,132 @@
  */
 import React, {useEffect, useMemo, useState} from 'react';
 import {
-    Badge,
     capitalize,
     Flex,
-    Form, FormGroup,
+    Form, FormGroup, FormHelperText, HelperText, HelperTextItem,
     Modal,
     ModalVariant,
     Radio, Text, TextInput,
     Wizard,
-    WizardHeader,
     WizardStep
 } from '@patternfly/react-core';
 import {KaravanApi} from "../../api/KaravanApi";
 import {RegistryBeanDefinition} from "karavan-core/lib/model/CamelDefinition";
 import {CodeUtils} from "../../util/CodeUtils";
-import {ProjectFile, ProjectType} from "../../api/ProjectModels";
-import {useWizardStore} from "../../api/ProjectStore";
+import {ProjectFile} from "../../api/ProjectModels";
+import {useFilesStore, useFileStore, useProjectStore, useWizardStore} from 
"../../api/ProjectStore";
 import {shallow} from "zustand/shallow";
+import ExclamationCircleIcon from 
'@patternfly/react-icons/dist/esm/icons/exclamation-circle-icon';
+import {useForm} from "react-hook-form";
+import {yupResolver} from "@hookform/resolvers/yup";
+import * as yup from "yup";
 import {ProjectService} from "../../api/ProjectService";
+import {EventBus} from "../../designer/utils/EventBus";
+import {useResponseErrorHandler} from 
"../../shared/error/UseResponseErrorHandler";
+import {Beans, Integration} from 
"karavan-core/lib/model/IntegrationDefinition";
+import {CamelDefinitionYaml} from "karavan-core/lib/api/CamelDefinitionYaml";
 
+const CAMEL_YAML_EXT = ".camel.yaml";
+const EMPTY_BEAN = "empty";
 const BEAN_TEMPLATE_SUFFIX_FILENAME = "-bean-template.camel.yaml";
 
 export function BeanWizard() {
 
+    const formValidationSchema = yup.object().shape({
+        filename: yup
+            .string()
+            .matches(/^[a-zA-Z0-9_\-.]+$/, 'Incorrect filename')
+            .required("File name is required"),
+    });
+
+    const {
+        register,
+        setError,
+        handleSubmit,
+        formState: { errors },
+        reset
+    } = useForm({
+        resolver: yupResolver(formValidationSchema),
+        mode: "onChange",
+        defaultValues: {filename: ''}
+    });
+
+    const responseToFormErrorFields = new Map<string, string>([
+        ["filename", "filename"]
+    ]);
+
+    const [project] = useProjectStore((s) => [s.project], shallow);
+    const [operation, setFile, designerTab] = useFileStore((s) => 
[s.operation, s.setFile, s.designerTab], shallow);
+    const [files] = useFilesStore((s) => [s.files], shallow);
     const [showWizard, setShowWizard] = useWizardStore((s) => [s.showWizard, 
s.setShowWizard], shallow)
-    const [files, setFiles] = useState<ProjectFile[]>([]);
+    const [templateFiles, setTemplateFiles] = useState<ProjectFile[]>([]);
     const [templateNames, setTemplateNames] = useState<string[]>([]);
     const [templateName, setTemplateName] = useState<string>('');
+    const [templateBeanName, setTemplateBeanName] = useState<string>('');
+    const [bean, setBean] = useState<RegistryBeanDefinition | undefined>();
+    const [filename, setFilename] = useState<string>('');
     const [beanName, setBeanName] = useState<string>('');
 
+    const [globalErrors, registerResponseErrors, resetGlobalErrors] = 
useResponseErrorHandler(
+        responseToFormErrorFields,
+        setError
+    );
+
+    function handleOnFormSubmitSuccess (file: ProjectFile) {
+        const message = "File successfully created.";
+        EventBus.sendAlert( "Success", message, "success");
+        ProjectService.refreshProjectData(file.projectId);
+        setFile('select', file, designerTab);
+        setShowWizard(false);
+    }
+
+    function handleFormSubmit() {
+        console.log("!!!", bean)
+        let code = '{}';
+        if (bean !== undefined && templateName !== EMPTY_BEAN) {
+            const i = Integration.createNew("temp");
+            i.spec.flows?.push(new Beans({beans: [bean]}))
+            code = CamelDefinitionYaml.integrationToYaml(i);
+        }
+        const fullFileName = filename + CAMEL_YAML_EXT;
+        const file = new ProjectFile(fullFileName, project.projectId, code, 
Date.now());
+        return ProjectService.createFile(file)
+            .then(() => handleOnFormSubmitSuccess(file))
+            .catch((error) => registerResponseErrors(error));
+    }
+
     useEffect(() => {
         if (showWizard) {
+            console.log("useEffect", "celan")
+            reset({filename: ''})
+            setFilename('')
+            setTemplateName('');
+            setTemplateBeanName('');
+            setBean(undefined);
             KaravanApi.getBeanTemplatesFiles(files => {
                 const templateNames = files.map(file => 
file.name.replace(BEAN_TEMPLATE_SUFFIX_FILENAME, ''));
-                setFiles(prevState => {
+                setTemplateFiles(prevState => {
                     return [...files];
                 });
                 setTemplateNames(prevState => {
                     return [...templateNames];
                 });
-                setTemplateName('');
-                setBeanName('');
             });
         }
     }, [showWizard]);
 
+    useEffect(() => {
+        setBeanName(templateBeanName);
+        getBeans.filter(b=> b.name === templateBeanName).forEach(b => {
+            Object.getOwnPropertyNames(b.properties).forEach(prop => {
+                setBean(new RegistryBeanDefinition({...b}))
+            })
+        });
+    }, [templateBeanName]);
+
 
     function getRegistryBeanDefinitions():RegistryBeanDefinition[] {
-        const fs = files
+        const fs = templateFiles
             .filter(f => f.name === 
templateName.concat(BEAN_TEMPLATE_SUFFIX_FILENAME));
         return CodeUtils.getBeans(fs);
     }
@@ -73,45 +151,90 @@ export function BeanWizard() {
     return (
         <Modal title={"Bean"} onClose={_ => setShowWizard(false)}
                variant={ModalVariant.medium} isOpen={showWizard} 
onEscapePress={() => setShowWizard(false)}>
-            <Wizard height={600} title="Bean configuration" onClose={() => 
setShowWizard(false)}>
+            <Wizard height={600} onClose={() => setShowWizard(false)} 
onSubmit={event => handleFormSubmit()}>
                 <WizardStep name={"Type"} id="type"
-                            footer={{ isNextDisabled: 
!templateNames.includes(templateName) }}
+                            footer={{ isNextDisabled: 
!templateNames.includes(templateName) && templateName !== EMPTY_BEAN }}
                 >
                     <Flex direction={{default:"column"}} 
gap={{default:'gapLg'}}>
-                        {templateNames.map(n => <Radio id={n} 
label={capitalize(n)} name={n} isChecked={n === templateName}
+                        <Radio key={EMPTY_BEAN} id={EMPTY_BEAN} 
label={capitalize(EMPTY_BEAN)} name={EMPTY_BEAN} isChecked={EMPTY_BEAN === 
templateName} onChange={_ => setTemplateName(EMPTY_BEAN)} />
+                        {templateNames.map(n => <Radio key={n} id={n} 
label={capitalize(n)} name={n} isChecked={n === templateName}
                                                        onChange={_ => 
setTemplateName(n)} />)}
                     </Flex>
                 </WizardStep>
                 <WizardStep name={"Template"} id="template"
+                            isHidden={templateName === EMPTY_BEAN}
                             isDisabled={templateName.length == 0}
-                            footer={{ isNextDisabled: !getBeans.map(b=> 
b.name).includes(beanName) }}
+                            footer={{ isNextDisabled: !getBeans.map(b=> 
b.name).includes(templateBeanName) }}
                 >
                     <Flex direction={{default:"column"}} 
gap={{default:'gapLg'}}>
-                    {getBeans.map(b => <Radio id={b.name} label={b.name} 
name={b.name} isChecked={b.name === beanName}
-                                               onChange={_ => 
setBeanName(b.name)} />)}
+                    {getBeans.map(b => <Radio key={b.name} id={b.name} 
label={b.name} name={b.name} isChecked={b.name === templateBeanName}
+                                               onChange={_ => 
setTemplateBeanName(b.name)} />)}
                     </Flex>
                 </WizardStep>
                 <WizardStep name="Properties" id="properties"
-                            isDisabled={templateName.length == 0 || 
beanName.length == 0}
-                            footer={{ nextButtonText: 'Add bean' }}
+                            isHidden={templateName === EMPTY_BEAN}
+                            isDisabled={templateName.length == 0 || 
templateBeanName.length == 0}
                 >
-                    <Form>
-                        {getBeans.filter(b=> b.name === beanName).map(b => (
-                           <>
+                    <Form autoComplete="off">
+                        <FormGroup key={"beanName"} label={"Name"} 
fieldId={"beanName"}>
+                            <TextInput
+                                value={beanName}
+                                id={"beanName"}
+                                aria-describedby={'beanName'}
+                                onChange={(_, value) => setBeanName(value)}
+                            />
+                        </FormGroup>
+                        <FormGroup label="Properties:" fieldId="properties"/>
+                        {getBeans.filter(b=> b.name === 
templateBeanName).map(b => (
+                           <div key={b.name}>
                                
{Object.getOwnPropertyNames(b.properties).map(prop => (
-                                   <FormGroup label={prop} fieldId={prop}>
+                                   <FormGroup key={prop} label={prop} 
fieldId={prop}>
                                        <TextInput
-                                           value={b.properties[prop]}
+                                           value={bean?.properties[prop] || ''}
                                            id={prop}
                                            aria-describedby={prop}
-                                           onChange={(_, value) => {}}
+                                           onChange={(_, value) => {
+                                               const b = new 
RegistryBeanDefinition({...bean});
+                                               b.properties[prop] = value;
+                                               setBean(b);
+                                           }}
                                        />
                                    </FormGroup>
                                ))}
-                           </>
+                           </div>
                         ))}
                     </Form>
                 </WizardStep>
+                <WizardStep name={"File"} id={"file"}
+                            footer={{ nextButtonText: 'Save', onNext: 
handleSubmit(handleFormSubmit) }}
+                            isDisabled={(templateName.length == 0 || 
templateBeanName.length == 0) && templateName !== EMPTY_BEAN}
+                >
+                    <Form autoComplete="off">
+                        <FormGroup label="Filename" fieldId="filename" 
isRequired>
+                            <TextInput className="text-field" type="text" 
id="filename"
+                                       aria-label="filename"
+                                       value={filename}
+                                       
customIcon={<Text>{CAMEL_YAML_EXT}</Text>}
+                                       validated={!!errors.filename ? 'error' 
: 'default'}
+                                       {...register('filename')}
+                                       // validated={!!errors.name ? 'error' : 
'default'}
+                                       onChange={(e, value) => {
+                                           setFilename(value);
+                                           register('filename').onChange(e);
+                                       }}
+                            />
+                            {!!errors.filename && (
+                                <FormHelperText>
+                                    <HelperText>
+                                        <HelperTextItem 
icon={<ExclamationCircleIcon />} variant={"error"}>
+                                            {errors?.filename?.message}
+                                        </HelperTextItem>
+                                    </HelperText>
+                                </FormHelperText>
+                            )}
+                        </FormGroup>
+                    </Form>
+                </WizardStep>
             </Wizard>
         </Modal>
     )

Reply via email to