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 4c48d656c082abc94d1edb8c2c812b28b4dca9a8
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Mon Apr 1 20:23:24 2024 -0400

    Improvements
---
 .../org/apache/camel/karavan/code/CodeService.java |  46 +++++++-
 .../webui/src/util/ModalDeleteConfirmation.tsx     |  35 ++++++
 karavan-app/src/main/webui/src/util/form-util.css  |  12 ++
 .../src/main/webui/src/util/useFormUtil.tsx        | 130 +++++++++++++++++++++
 4 files changed, 222 insertions(+), 1 deletion(-)

diff --git 
a/karavan-app/src/main/java/org/apache/camel/karavan/code/CodeService.java 
b/karavan-app/src/main/java/org/apache/camel/karavan/code/CodeService.java
index 03a646d6..b4290c01 100644
--- a/karavan-app/src/main/java/org/apache/camel/karavan/code/CodeService.java
+++ b/karavan-app/src/main/java/org/apache/camel/karavan/code/CodeService.java
@@ -48,6 +48,13 @@ import org.yaml.snakeyaml.Yaml;
 import org.yaml.snakeyaml.constructor.SafeConstructor;
 
 import java.io.*;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.time.Instant;
 import java.util.*;
 import java.util.stream.Collectors;
@@ -169,7 +176,7 @@ public class CodeService {
     public String getTemplateText(String fileName) {
         try {
             List<ProjectFile> files = 
karavanCacheService.getProjectFiles(Project.Type.templates.name());
-            // replaceAll("\r\n", "\n")) has been add to eliminate the impact 
of editing the template files from windows machine. 
+            // replaceAll("\r\n", "\n")) has been add to eliminate the impact 
of editing the template files from windows machine.
             return files.stream().filter(f -> 
f.getName().equalsIgnoreCase(fileName))
                     .map(file-> file.getCode().replaceAll("\r\n", 
"\n")).findFirst().orElse(null);
         } catch (Exception e) {
@@ -393,6 +400,43 @@ public class CodeService {
         return vertx.fileSystem().readFileBlocking(fileName).toString();
     }
 
+    public List<String> listResources(String resourceFolder, boolean 
onlyFolders) {
+        List<String> filePaths = new ArrayList<>();
+        try {
+            URI uri = CodeService.class.getResource(resourceFolder).toURI();
+            Path myPath;
+            FileSystem fileSystem = null;
+            if (uri.getScheme().equals("jar")) {
+                fileSystem = FileSystems.newFileSystem(uri, 
Collections.emptyMap());
+                myPath = fileSystem.getPath(resourceFolder);
+            } else {
+                myPath = Paths.get(uri);
+            }
+
+            if (onlyFolders) {
+                // Use Files.walk to list and collect directory paths
+                filePaths = Files.walk(myPath, 10)
+                        .filter(Files::isDirectory)
+                        .map(path -> path.getFileName().toString())
+                        .collect(Collectors.toList());
+            } else {
+                // Use Files.walk to list and collect file paths
+                filePaths = Files.walk(myPath, 1)
+                        .filter(Files::isRegularFile)
+                        .map(path -> path.getFileName().toString())
+                        .collect(Collectors.toList());
+            }
+
+            // Close the file system if opened
+            if (fileSystem != null) {
+                fileSystem.close();
+            }
+        } catch (URISyntaxException | IOException e) {
+            e.printStackTrace();
+        }
+        return filePaths;
+    }
+
     public String getFileString(String fullName) {
         return vertx.fileSystem().readFileBlocking(fullName).toString();
     }
diff --git a/karavan-app/src/main/webui/src/util/ModalDeleteConfirmation.tsx 
b/karavan-app/src/main/webui/src/util/ModalDeleteConfirmation.tsx
new file mode 100644
index 00000000..ec4d0079
--- /dev/null
+++ b/karavan-app/src/main/webui/src/util/ModalDeleteConfirmation.tsx
@@ -0,0 +1,35 @@
+import React from 'react';
+import {
+    Button,
+    Modal,
+    ModalVariant
+} from '@patternfly/react-core';
+import '../designer/karavan.css';
+
+interface Props {
+    content: string | React.JSX.Element
+    isOpen: boolean
+    onClose: () => void
+    onConfirm: () => void
+    title?: string
+}
+
+export function ModalDeleteConfirmation(props: Props) {
+
+    const {title, isOpen, onClose, onConfirm, content} = props;
+    return (
+            <Modal
+                title={title ? title : 'Confirmation'}
+                variant={ModalVariant.small}
+                isOpen={isOpen}
+                onClose={() => onClose()}
+                actions={[
+                    <Button key="confirm" variant="danger" onClick={e => 
onConfirm()}>Confirm</Button>,
+                    <Button key="cancel" variant="link"
+                            onClick={e => onClose()}>Cancel</Button>
+                ]}
+                onEscapePress={e => onClose()}>
+                {content}
+            </Modal>
+    )
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/util/form-util.css 
b/karavan-app/src/main/webui/src/util/form-util.css
new file mode 100644
index 00000000..47c4819f
--- /dev/null
+++ b/karavan-app/src/main/webui/src/util/form-util.css
@@ -0,0 +1,12 @@
+.pf-v5-c-modal-box .text-field-with-prefix {
+    gap: 0;
+}
+
+.pf-v5-c-modal-box .text-field-with-prefix .text-field-prefix {
+    margin-top: auto;
+    margin-bottom: auto;
+}
+
+.pf-v5-c-modal-box .text-field-with-prefix 
.pf-v5-c-text-input-group__text-input {
+    padding-left: 0;
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/util/useFormUtil.tsx 
b/karavan-app/src/main/webui/src/util/useFormUtil.tsx
new file mode 100644
index 00000000..fb7903d8
--- /dev/null
+++ b/karavan-app/src/main/webui/src/util/useFormUtil.tsx
@@ -0,0 +1,130 @@
+import React from 'react';
+import {FieldError, UseFormReturn} from "react-hook-form";
+import {
+    Flex,
+    FormGroup,
+    FormHelperText,
+    FormSelect,
+    FormSelectOption,
+    HelperText,
+    HelperTextItem, Switch, Text,
+    TextInput, TextInputGroup, TextInputGroupMain, TextVariants
+} from "@patternfly/react-core";
+import "./form-util.css"
+
+export function useFormUtil(formContext: UseFormReturn<any>) {
+
+    function getHelper(error: FieldError | undefined) {
+        if (error) {
+            return (
+                <FormHelperText>
+                    <HelperText>
+                        <HelperTextItem variant={'error'}>
+                            {error.message}
+                        </HelperTextItem>
+                    </HelperText>
+                </FormHelperText>
+            )
+        }
+    }
+
+    function getTextField(fieldName: string, label: string, validate?: 
((value: string, formValues: any) => boolean | string) | Record<string, (value: 
string, formValues: any) => boolean | string>) {
+        const {setValue, getValues, register, formState: {errors}} = 
formContext;
+        return (
+            <FormGroup label={label} fieldId={fieldName} isRequired>
+                <TextInput className="text-field" type="text" id={fieldName}
+                           value={getValues()[fieldName]}
+                           validated={!!errors[fieldName] ? 'error' : 
'default'}
+                           {...register(fieldName, {required: "Required 
field", validate: validate})}
+                           onChange={(e, v) => {
+                               setValue(fieldName, v, {shouldValidate: true});
+                           }}
+                />
+                {getHelper((errors as any)[fieldName])}
+            </FormGroup>
+        )
+    }
+
+    function getTextFieldPrefix(fieldName: string, label: string, prefix: 
string,
+                                required: boolean,
+                                validate?: ((value: string, formValues: any) 
=> boolean | string) | Record<string, (value: string, formValues: any) => 
boolean | string>) {
+        const {setValue, getValues, register, formState: {errors}} = 
formContext;
+        return (
+            <FormGroup label={label} fieldId={fieldName} isRequired>
+                <TextInputGroup>
+                    <TextInputGroupMain className="text-field-with-prefix" 
type="text" id={fieldName}
+                                        value={getValues()[fieldName]}
+                        // validated={!!errors[fieldName] ? 'error' : 
'default'}
+                                        {...register(fieldName, {required: 
(required ? "Required field" : false), validate: validate})}
+                                        onChange={(e, v) => {
+                                            setValue(fieldName, v, 
{shouldValidate: true});
+                                        }}
+                    >
+                        <Text className='text-field-prefix' 
component={TextVariants.p}>{prefix}</Text>
+                    </TextInputGroupMain>
+                </TextInputGroup>
+                {getHelper((errors as any)[fieldName])}
+            </FormGroup>
+        )
+    }
+
+    function getFormSelect(fieldName: string, label: string, options: [string, 
string][]) {
+        const {register, watch, setValue, formState: {errors}} = formContext;
+        return (
+            <FormGroup label={label} fieldId={fieldName} isRequired>
+                <FormSelect
+                    ouiaId={fieldName}
+                    validated={!!errors[fieldName] ? 'error' : 'default'}
+                    value={watch(fieldName)}
+                    {...register(fieldName, {required: "Required field"})}
+                    onChange={(e, v) => {
+                        setValue(fieldName, v, {shouldValidate: true});
+                    }}
+                    name={fieldName}
+
+                >
+                    <FormSelectOption key='placeholder' value={undefined} 
label='Select one' isDisabled/>
+                    {options.map((option, index) => (
+                        <FormSelectOption key={index} value={option[0]} 
label={option[1]}/>
+                    ))}
+                </FormSelect>
+                {getHelper((errors as any)[fieldName])}
+            </FormGroup>
+        )
+    }
+
+    function getSwitches(fieldName: string, label: string, options: [string, 
string][]) {
+        const {watch, register, getValues, setValue, formState: {errors}} = 
formContext;
+        return (
+            <FormGroup label={label} fieldId={fieldName} isRequired 
{...register(fieldName)}>
+                <Flex direction={{default: 'column'}}>
+                {options.map((option, index) => {
+                    const key = option[0];
+                    const label = option[0];
+                    return (<Switch
+                        id={key}
+                        label={label}
+                        labelOff={label}
+                        isChecked={watch(fieldName) !== undefined && 
watch(fieldName).includes(key)}
+                        onChange={(e, v) => {
+                            const vals: string[] = watch(fieldName);
+                            const idx = vals.findIndex(x => x === key);
+                            if (idx > -1 && !v) {
+                                vals.splice(idx, 1);
+                                setValue(fieldName, [...vals]);
+                            } else if (idx === -1 && v) {
+                                vals.push(key);
+                                setValue(fieldName, [...vals]);
+                            }
+                        }}
+                        ouiaId={option[0]}
+                    />)
+                })}
+                </Flex>
+            </FormGroup>
+        )
+    }
+
+    return {getFormSelect, getTextField, getSwitches, getTextFieldPrefix}
+}
+

Reply via email to