Repository: kestra-io/kestra (java)

Issue:
Add support of input type EMAIL 
### Feature description

Sometimes the input is not just plain string but email. Implementing email validation regex every time can be tedious — we can add a new EMAIL input type to help with that use case. 

```yaml
id: myflow
namespace: company.team
inputs:
  - id: myemail
    type: EMAIL
    defaults: hello@kestra.io
tasks:
  - id: hello
    type: io.kestra.plugin.core.log.Log
    message: Hello {{ inputs.myemail }}
```

trying to enter sth like "Arbitrary string" would not be allowed as this input must be a valid email.

Pull request:
implemented a new input type email with validation
<!-- Thanks for submitting a Pull Request to Kestra. To help us review your contribution, please follow the guidelines below:

- Make sure that your commits follow the [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) specification e.g. `feat(ui): add a new navigation menu item` or `fix(core): fix a bug in the core model` or `docs: update the README.md`. This will help us automatically generate the changelog.
- The title should briefly summarize the proposed changes.
- Provide a short overview of the change and the value it adds.
- Share a flow example to help the reviewer understand and QA the change.
- Use "closes" to automatically close an issue. For example, `closes #1234` will close issue #1234. -->

### What changes are being made and why?

<!-- Please include a brief summary of the changes included in this PR e.g. closes #1234. -->
closes #5477
A new input type of email has been added to speed the process of making email inputs with the required regex pattern
---

### How the changes have been QAed?

<!-- Include example code that shows how this PR has been QAed. The code should present a complete yet easily reproducible flow.
Note that this is not a replacement for unit tests but rather a way to demonstrate how the changes work in a real-life scenario, as the end-user would experience them.

Remove this section if this change applies to all flows or to the documentation only. -->

The changes have been tested by creating a new flow with an input of type email. Furthermore the flow has been executed with a valid email and an invalid email

```yaml
id: myflow
namespace: company.team
inputs:
  - id: myemail
    type: EMAIL
    defaults: hello@kestra.io
tasks:
  - id: hello
    type: io.kestra.plugin.core.log.Log
    message: Hello World! 

```

---

### Setup Instructions

<!--If there are any setup requirements like API keys or trial accounts, kindly include brief bullet-points-description outlining the setup process below.

- [External System Documentation](URL)
- Steps to set up the necessary resources

If there are no setup requirements, you can remove this section.

Thank you for your contribution. ❤️  -->

No additional setup is required. 


Patch:
diff --git a/core/src/main/java/io/kestra/core/models/flows/Input.java b/core/src/main/java/io/kestra/core/models/flows/Input.java
index 14d31187a..611df131b 100644
--- a/core/src/main/java/io/kestra/core/models/flows/Input.java
+++ b/core/src/main/java/io/kestra/core/models/flows/Input.java
@@ -41,7 +41,8 @@ import lombok.experimental.SuperBuilder;
     @JsonSubTypes.Type(value = TimeInput.class, name = "TIME"),
     @JsonSubTypes.Type(value = URIInput.class, name = "URI"),
     @JsonSubTypes.Type(value = MultiselectInput.class, name = "MULTISELECT"),
-    @JsonSubTypes.Type(value = YamlInput.class, name = "YAML")
+    @JsonSubTypes.Type(value = YamlInput.class, name = "YAML"),
+    @JsonSubTypes.Type(value = EmailInput.class, name = "EMAIL"),
 })
 @JsonInclude(JsonInclude.Include.NON_DEFAULT)
 public abstract class Input<T> implements Data {
diff --git a/core/src/main/java/io/kestra/core/models/flows/Type.java b/core/src/main/java/io/kestra/core/models/flows/Type.java
index e5a4e2735..4e8641525 100644
--- a/core/src/main/java/io/kestra/core/models/flows/Type.java
+++ b/core/src/main/java/io/kestra/core/models/flows/Type.java
@@ -24,7 +24,8 @@ public enum Type {
     SECRET(SecretInput.class.getName()),
     ARRAY(ArrayInput.class.getName()),
     MULTISELECT(MultiselectInput.class.getName()),
-    YAML(YamlInput.class.getName());
+    YAML(YamlInput.class.getName()),
+    EMAIL(EmailInput.class.getName());
 
     private final String clsName;
 
diff --git a/core/src/main/java/io/kestra/core/models/flows/input/EmailInput.java b/core/src/main/java/io/kestra/core/models/flows/input/EmailInput.java
new file mode 100644
index 000000000..bfb18e437
--- /dev/null
+++ b/core/src/main/java/io/kestra/core/models/flows/input/EmailInput.java
@@ -0,0 +1,25 @@
+package io.kestra.core.models.flows.input;
+
+import io.kestra.core.models.flows.Input;
+import io.kestra.core.models.validations.ManualConstraintViolation;
+import jakarta.validation.ConstraintViolationException;
+
+import java.util.regex.Pattern;
+
+public class EmailInput extends Input<String> {
+
+    private static final String EMAIL_PATTERN = "^$|^[a-zA-Z0-9_!#$%&’*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$";
+
+    @Override
+    public void validate(String input) throws ConstraintViolationException {
+        if(!Pattern.matches(EMAIL_PATTERN, input)){
+            throw ManualConstraintViolation.toConstraintViolationException(
+                "The input must be a valid email",
+                this,
+                EmailInput.class,
+                getId(),
+                input
+            );
+        }
+    }
+}
diff --git a/core/src/main/java/io/kestra/core/runners/FlowInputOutput.java b/core/src/main/java/io/kestra/core/runners/FlowInputOutput.java
index b31b1f38b..05d7418bc 100644
--- a/core/src/main/java/io/kestra/core/runners/FlowInputOutput.java
+++ b/core/src/main/java/io/kestra/core/runners/FlowInputOutput.java
@@ -393,7 +393,7 @@ public class FlowInputOutput {
     private Object parseType(Execution execution, Type type, String id, Type elementType, Object current) throws Exception {
         try {
             return switch (type) {
-                case SELECT, ENUM, STRING -> current;
+                case SELECT, ENUM, STRING, EMAIL -> current;
                 case SECRET -> {
                     if (secretKey.isEmpty()) {
                         throw new Exception("Unable to use a `SECRET` input/output as encryption is not configured");
@@ -401,7 +401,8 @@ public class FlowInputOutput {
                     yield EncryptionService.encrypt(secretKey.get(), (String) current);
                 }
                 case INT -> current instanceof Integer ? current : Integer.valueOf((String) current);
-                case FLOAT -> current instanceof Float ? current : Float.valueOf((String) current);
+                // Assuming that after the render we must have a double/int, so we can safely use its toString representation
+                case FLOAT -> current instanceof Float ? current : Float.valueOf(current.toString());
                 case BOOLEAN -> current instanceof Boolean ? current : Boolean.valueOf((String) current);
                 case DATETIME -> Instant.parse(((String) current));
                 case DATE -> LocalDate.parse(((String) current));
diff --git a/ui/src/components/executions/ForEachStatus.vue b/ui/src/components/executions/ForEachStatus.vue
index 1b321495f..d855eeaa3 100644
--- a/ui/src/components/executions/ForEachStatus.vue
+++ b/ui/src/components/executions/ForEachStatus.vue
@@ -19,7 +19,7 @@
             </router-link>
             <div v-for="state in State.allStates()" :key="state.key">
                 <router-link :to="goToExecutionsList(state.key)" class="el-button count-button" v-if="localSubflowStatus[state.key] >= 0">
-                    {{ capitalizeFirstLetter(state.key) }}
+                    {{ capitalizeFirstLetter(getStateToBeDisplayed(state.key)) }}
                     <span class="counter">{{ localSubflowStatus[state.key] }}</span>
                     <div class="dot rounded-5" :class="`bg-${state.colorClass}`" />
                 </router-link>
@@ -29,6 +29,7 @@
 </template>
 <script>
     import {cssVariable} from "@kestra-io/ui-libs/src/utils/global";
+    import {stateDisplayValues} from "../../utils/constants";
     import State from "../../utils/state";
     import throttle from "lodash/throttle"
 
@@ -79,6 +80,13 @@
             capitalizeFirstLetter(str) {
                 return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
             },
+            getStateToBeDisplayed(str){
+                if(str === State.RUNNING){
+                    return stateDisplayValues.INPROGRESS;
+                }else{
+                    return str;
+                }
+            },
             goToExecutionsList(state) {
                 const queries = {
                     triggerExecutionId: this.executionId,
diff --git a/ui/src/components/inputs/InputsForm.vue b/ui/src/components/inputs/InputsForm.vue
index 8c9078c39..c6acb15a0 100644
--- a/ui/src/components/inputs/InputsForm.vue
+++ b/ui/src/components/inputs/InputsForm.vue
@@ -11,7 +11,7 @@
                 :full-height="false"
                 :input="true"
                 :navbar="false"
-                v-if="input.type === 'STRING' || input.type === 'URI'"
+                v-if="input.type === 'STRING' || input.type === 'URI' || input.type === 'EMAIL'"
                 v-model="inputs[input.id]"
                 @update:model-value="onChange"
                 @confirm="onSubmit"
diff --git a/ui/src/utils/constants.js b/ui/src/utils/constants.js
index 217ab86ef..7bbd1dfbf 100644
--- a/ui/src/utils/constants.js
+++ b/ui/src/utils/constants.js
@@ -47,3 +47,7 @@ export const executeFlowBehaviours = {
     NEW_TAB: "new tab"
 }
 
+export const stateDisplayValues = {
+    INPROGRESS: "IN-PROGRESS"
+}
+


Test patch:
diff --git a/webserver/src/test/java/io/kestra/webserver/controllers/api/PluginControllerTest.java b/webserver/src/test/java/io/kestra/webserver/controllers/api/PluginControllerTest.java
index 8ec1e1ced..6878c1ce2 100644
--- a/webserver/src/test/java/io/kestra/webserver/controllers/api/PluginControllerTest.java
+++ b/webserver/src/test/java/io/kestra/webserver/controllers/api/PluginControllerTest.java
@@ -213,7 +213,7 @@ class PluginControllerTest {
                 Argument.listOf(InputType.class)
             );
 
-            assertThat(doc.size(), is(17));
+            assertThat(doc.size(), is(18));
         });
     }