Repository: kestra-io/kestra
Issue: label dropdown hide execution button
Labels: bug
frontend

### Expected Behavior

Could it be a simple text box ?

### Actual Behaviour

<img width="751" alt="Screenshot 2023-06-27 at 10 30 14" src="https://github.com/kestra-io/kestra/assets/46634684/46690267-af15-425b-8eeb-1117658f9e62">


### Steps To Reproduce

_No response_

### Environment Information

- Kestra Version:
- Operating System (OS / Docker / Kubernetes):
- Java Version (If not docker):


### Example flow

_No response_

Issue comments:

Pull request review comments:

Patch that resolved the issue:
diff --git a/ui/src/components/flows/FlowRun.vue b/ui/src/components/flows/FlowRun.vue
index 726a8fa656..8111b5c150 100644
--- a/ui/src/components/flows/FlowRun.vue
+++ b/ui/src/components/flows/FlowRun.vue
@@ -82,8 +82,8 @@
             <el-form-item
                 :label="$t('execution labels')"
             >
-                <label-filter
-                    v-model:model-value="executionLabels"
+                <label-input
+                    v-model:labels="executionLabels"
                 />
             </el-form-item>
             <div class="bottom-buttons">
@@ -99,6 +99,9 @@
                         <el-button :icon="Flash" class="flow-run-trigger-button" @click="onSubmit($refs.form)" type="primary" :disabled="flow.disabled || haveBadLabels">
                             {{ $t('launch execution') }}
                         </el-button>
+                        <el-text v-if="haveBadLabels" type="danger" size="small">
+                            {{ $t('wrong labels') }}
+                        </el-text>
                     </el-form-item>
                 </div>
             </div>
@@ -115,11 +118,11 @@
     import {mapState} from "vuex";
     import {executeTask} from "../../utils/submitTask"
     import Editor from "../../components/inputs/Editor.vue";
-    import LabelFilter from "../../components/labels/LabelFilter.vue";
+    import LabelInput from "../../components/labels/LabelInput.vue";
     import {pageFromRoute} from "../../utils/eventsRouter";
 
     export default {
-        components: {Editor, LabelFilter},
+        components: {Editor, LabelInput},
         props: {
             redirect: {
                 type: Boolean,
@@ -169,13 +172,10 @@
             ...mapState("core", ["guidedProperties"]),
             ...mapState("execution", ["execution"]),
             haveBadLabels() {
-                return this.executionLabels.some(label => label.split(":").length !== 2)
+                return this.executionLabels.some(label => (label.key && !label.value) || (!label.key && label.value));
             }
         },
         methods: {
-            isBadLabel(tag) {
-                return tag.split(":").length !== 2
-            },
             fillInputsFromExecution(){
                 const nonEmptyInputNames = Object.keys(this.execution.inputs);
                 this.inputs = Object.fromEntries(
@@ -211,6 +211,8 @@
                             id: this.flow.id,
                             namespace: this.flow.namespace,
                             labels: this.executionLabels
+                                .filter(label => label.key && label.value)
+                                .map(label => `${label.key}:${label.value}`)
                         })
                         this.$emit("executionTrigger");
                     });
@@ -273,19 +275,6 @@
 
                 return true;
             },
-            handleClose(label) {
-                this.executionLabels.splice(this.executionLabels.indexOf(label), 1)
-            },
-            showInput() {
-                this.inputVisible = true;
-            },
-            handleInputConfirm() {
-                if (this.inputNewLabel) {
-                    this.executionLabels.push(this.inputNewLabel)
-                }
-                this.inputVisible = false
-                this.inputNewLabel = ""
-            }
         },
         watch: {
             guidedProperties: {
diff --git a/ui/src/components/labels/LabelInput.vue b/ui/src/components/labels/LabelInput.vue
new file mode 100644
index 0000000000..519a86e1e9
--- /dev/null
+++ b/ui/src/components/labels/LabelInput.vue
@@ -0,0 +1,68 @@
+<template>
+    <div class="d-flex w-100 mb-2" v-for="(label, index) in locals" :key="index">
+        <div class="flex-grow-1 d-flex align-items-center">
+            <el-input
+                class="form-control me-2"
+                :placeholder="$t('key')"
+                v-model="label.key"
+                @update:model-value="update(index, $event, 'key')"
+            />
+            <el-input
+                class="form-control me-2"
+                :placeholder="$t('value')"
+                v-model="label.value"
+                @update:model-value="update(index, $event, 'value')"
+            />
+        </div>
+        <div class="flex-shrink-1">
+            <el-button-group class="d-flex">
+                <el-button :icon="Plus" @click="addItem" />
+                <el-button :icon="Minus" @click="removeItem(index)" :disabled="index === 0 && locals.length === 1" />
+            </el-button-group>
+        </div>
+    </div>
+</template>
+
+<script setup>
+    import Plus from "vue-material-design-icons/Plus.vue";
+    import Minus from "vue-material-design-icons/Minus.vue";
+</script>
+
+
+<script>
+    export default {
+        props: {
+            labels: {
+                type: Array,
+                required: true
+            }
+        },
+        data() {
+            return {
+                locals: []
+            }
+        },
+        emits: ["update:labels"],
+        created() {
+            if (this.labels.length === 0) {
+                this.addItem();
+            } else {
+                this.locals = this.labels
+            }
+        },
+        methods: {
+            addItem() {
+                this.locals.push({key: null, value: null});
+                this.$emit("update:labels", this.locals);
+            },
+            removeItem(index) {
+                this.locals.splice(index, 1);
+                this.$emit("update:labels", this.locals);
+            },
+            update(index, value, prop) {
+                this.locals[index][prop] = value;
+                this.$emit("update:labels", this.locals);
+            },
+        },
+    }
+</script>
\ No newline at end of file
diff --git a/ui/src/translations.json b/ui/src/translations.json
index 5e08e2fe1a..a1542a1c93 100644
--- a/ui/src/translations.json
+++ b/ui/src/translations.json
@@ -262,6 +262,7 @@
     "labels": "Labels",
     "label filter placeholder": "Label as 'key:value'",
     "execution labels": "Execution labels",
+    "wrong labels": "Empty key or value is not allowed in labels",
     "feeds": {
       "title": "What's new at Kestra"
     },
@@ -719,8 +720,9 @@
     "disabled flow desc": "Ce flow est désactivé, veuillez le réactiver pour l'exécuter.",
     "label": "Label",
     "labels": "Labels",
-    "label filter placeholder": "Label as 'key:value'",
+    "label filter placeholder": "Label au format 'clé:valeur'",
     "execution labels": "Labels d'exécution",
+    "wrong labels": "Clé ou valeur vide détecté dans les labels",
     "feeds": {
       "title": "Quoi de neuf sur Kestra"
     },


Test patch:
diff --git a/core/src/test/java/io/kestra/core/schedulers/SchedulerConditionTest.java b/core/src/test/java/io/kestra/core/schedulers/SchedulerConditionTest.java
index c445713315..afbc9f8232 100644
--- a/core/src/test/java/io/kestra/core/schedulers/SchedulerConditionTest.java
+++ b/core/src/test/java/io/kestra/core/schedulers/SchedulerConditionTest.java
@@ -51,7 +51,7 @@ private static Flow createScheduleFlow() {
         return createFlow(Collections.singletonList(schedule));
     }
 
-    @RetryingTest(5)
+    @RetryingTest(10)
     void schedule() throws Exception {
         // mock flow listeners
         FlowListeners flowListenersServiceSpy = spy(this.flowListenersService);