Repository: kestra-io/kestra (java)

Issue:
UI - Executions Status Filter does not update in Execution Col Chart
### Describe the issue

- Reproduce - In Execution tab, change the filter 'Status' to specific status option
- Outcome - the Column chart does not update, shows all statuses
![Screenshot 2024-07-22 at 1 37 44 PM](https://github.com/user-attachments/assets/eeec1de9-83d2-4549-b592-1d9f103a106e)


### Environment

- Kestra Version: 0.17.13
- Operating System (OS/Docker/Kubernetes): Kestra Cloud
- Java Version (if you don't run kestra in Docker): n/a


Pull request:
feat(): filter state on dashboard and graph on execution list
close #4397

Patch:
diff --git a/core/src/main/java/io/kestra/core/models/collectors/ExecutionUsage.java b/core/src/main/java/io/kestra/core/models/collectors/ExecutionUsage.java
index 0e7c8986c..22c430ee7 100644
--- a/core/src/main/java/io/kestra/core/models/collectors/ExecutionUsage.java
+++ b/core/src/main/java/io/kestra/core/models/collectors/ExecutionUsage.java
@@ -8,7 +8,6 @@ import lombok.Getter;
 import lombok.experimental.SuperBuilder;
 import lombok.extern.jackson.Jacksonized;
 
-import java.time.ZoneId;
 import java.time.ZonedDateTime;
 import java.util.List;
 
@@ -36,8 +35,8 @@ public class ExecutionUsage {
                 from,
                 to,
                 DateUtils.GroupType.DAY,
-                true
-            );
+                null,
+                true);
         } catch (UnsupportedOperationException ignored) {
 
         }
@@ -51,8 +50,8 @@ public class ExecutionUsage {
                 from,
                 to,
                 DateUtils.GroupType.DAY,
-                false
-            ))
+                null,
+                false))
             .dailyTaskRunsCount(dailyTaskRunsCount)
             .build();
     }
diff --git a/core/src/main/java/io/kestra/core/repositories/ExecutionRepositoryInterface.java b/core/src/main/java/io/kestra/core/repositories/ExecutionRepositoryInterface.java
index 11267bb34..340a875ba 100644
--- a/core/src/main/java/io/kestra/core/repositories/ExecutionRepositoryInterface.java
+++ b/core/src/main/java/io/kestra/core/repositories/ExecutionRepositoryInterface.java
@@ -8,18 +8,18 @@ import io.kestra.core.models.executions.statistics.Flow;
 import io.kestra.core.models.flows.State;
 import io.kestra.core.utils.DateUtils;
 import io.micronaut.data.model.Pageable;
+import jakarta.annotation.Nullable;
+import jakarta.validation.constraints.NotNull;
 import lombok.Getter;
 import lombok.NoArgsConstructor;
 import lombok.experimental.SuperBuilder;
+import reactor.core.publisher.Flux;
 
 import java.time.ZonedDateTime;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.function.Function;
-import jakarta.annotation.Nullable;
-import jakarta.validation.constraints.NotNull;
-import reactor.core.publisher.Flux;
 
 public interface ExecutionRepositoryInterface extends SaveRepositoryInterface<Execution> {
     Boolean isTaskRunEnabled();
@@ -122,6 +122,7 @@ public interface ExecutionRepositoryInterface extends SaveRepositoryInterface<Ex
         @Nullable ZonedDateTime startDate,
         @Nullable ZonedDateTime endDate,
         @Nullable DateUtils.GroupType groupBy,
+        List<State.Type> state,
         boolean isTaskRun
     );
 
diff --git a/jdbc/src/main/java/io/kestra/jdbc/repository/AbstractJdbcExecutionRepository.java b/jdbc/src/main/java/io/kestra/jdbc/repository/AbstractJdbcExecutionRepository.java
index e65ed16a3..238044df2 100644
--- a/jdbc/src/main/java/io/kestra/jdbc/repository/AbstractJdbcExecutionRepository.java
+++ b/jdbc/src/main/java/io/kestra/jdbc/repository/AbstractJdbcExecutionRepository.java
@@ -326,7 +326,8 @@ public abstract class AbstractJdbcExecutionRepository extends AbstractJdbcReposi
             null,
             startDate,
             endDate,
-            groupBy
+            groupBy,
+            null
         );
 
         return dailyStatisticsQueryMapRecord(
@@ -348,6 +349,7 @@ public abstract class AbstractJdbcExecutionRepository extends AbstractJdbcReposi
         @Nullable ZonedDateTime startDate,
         @Nullable ZonedDateTime endDate,
         @Nullable DateUtils.GroupType groupBy,
+        @Nullable List<State.Type> states,
         boolean isTaskRun
     ) {
         if (isTaskRun) {
@@ -365,7 +367,8 @@ public abstract class AbstractJdbcExecutionRepository extends AbstractJdbcReposi
             null,
             startDate,
             endDate,
-            groupBy
+            groupBy,
+            states
         );
 
         return dailyStatisticsQueryMapRecord(
@@ -414,7 +417,8 @@ public abstract class AbstractJdbcExecutionRepository extends AbstractJdbcReposi
         List<FlowFilter> flows,
         @Nullable ZonedDateTime startDate,
         @Nullable ZonedDateTime endDate,
-        @Nullable DateUtils.GroupType groupBy
+        @Nullable DateUtils.GroupType groupBy,
+        @Nullable List<State.Type> state
     ) {
         return dailyStatisticsQuery(
             this.defaultFilter(),
@@ -425,7 +429,8 @@ public abstract class AbstractJdbcExecutionRepository extends AbstractJdbcReposi
             flows,
             startDate,
             endDate,
-            groupBy
+            groupBy,
+            state
         );
     }
 
@@ -438,7 +443,8 @@ public abstract class AbstractJdbcExecutionRepository extends AbstractJdbcReposi
         List<FlowFilter> flows,
         @Nullable ZonedDateTime startDate,
         @Nullable ZonedDateTime endDate,
-        @Nullable DateUtils.GroupType groupBy
+        @Nullable DateUtils.GroupType groupBy,
+        @Nullable List<State.Type> state
     ) {
         return dailyStatisticsQuery(
             this.defaultFilter(tenantId),
@@ -449,7 +455,8 @@ public abstract class AbstractJdbcExecutionRepository extends AbstractJdbcReposi
             flows,
             startDate,
             endDate,
-            groupBy
+            groupBy,
+            state
         );
     }
 
@@ -462,7 +469,8 @@ public abstract class AbstractJdbcExecutionRepository extends AbstractJdbcReposi
         List<FlowFilter> flows,
         @Nullable ZonedDateTime startDate,
         @Nullable ZonedDateTime endDate,
-        @Nullable DateUtils.GroupType groupBy
+        @Nullable DateUtils.GroupType groupBy,
+        @Nullable List<State.Type> state
     ) {
         ZonedDateTime finalStartDate = startDate == null ? ZonedDateTime.now().minusDays(30) : startDate;
         ZonedDateTime finalEndDate = endDate == null ? ZonedDateTime.now() : endDate;
@@ -491,6 +499,10 @@ public abstract class AbstractJdbcExecutionRepository extends AbstractJdbcReposi
 
                 select = filteringQuery(select, namespace, flowId, flows, query, null, null, null);
 
+                if (state != null) {
+                    select = select.and(this.statesFilter(state));
+                }
+
                 List<Field<?>> groupFields = new ArrayList<>(fields);
 
                 groupFields.addAll(dateFields);
@@ -583,6 +595,7 @@ public abstract class AbstractJdbcExecutionRepository extends AbstractJdbcReposi
             flows,
             startDate,
             endDate,
+            null,
             null
         );
 
diff --git a/ui/src/components/home/Home.vue b/ui/src/components/home/Home.vue
index 407219a1d..35a4726c5 100644
--- a/ui/src/components/home/Home.vue
+++ b/ui/src/components/home/Home.vue
@@ -22,6 +22,23 @@
                         @update:model-value="onNamespaceSelect"
                     />
                 </el-form-item>
+                <el-form-item>
+                    <el-select
+                        :model-value="state"
+                        @update:model-value="onStateSelect"
+                        clearable
+                        filterable
+                        multiple
+                        :placeholder="$t('state')"
+                    >
+                        <el-option
+                            v-for="item in State.allStates()"
+                            :key="item.key"
+                            :label="item.key"
+                            :value="item.key"
+                        />
+                    </el-select>
+                </el-form-item>
                 <el-form-item>
                     <date-filter
                         @update:is-relative="onDateFilterTypeChange"
@@ -156,6 +173,7 @@
     import TopNavBar from "../layout/TopNavBar.vue";
     import DateFilter from "../executions/date-select/DateFilter.vue";
     import HomeStartup from "override/mixins/homeStartup"
+    import State from "../../utils/state";
 
     export default {
         mixins: [RouteContext, RestoreUrl, HomeStartup],
@@ -217,7 +235,8 @@
                 namespacesStats: undefined,
                 namespaceRestricted: !!this.namespace,
                 refreshDates: false,
-                canAutoRefresh: false
+                canAutoRefresh: false,
+                state: []
             };
         },
         methods: {
@@ -340,7 +359,19 @@
                 }
 
                 this.$router.push({query: query}).then(this.load);
-            }
+            },
+            onStateSelect(state) {
+                this.state = state;
+                if (state && state.length > 0) {
+                    this.$router.push({query: {...this.$route.query, state: state}});
+                } else {
+                    let query = {...this.$route.query}
+                    delete query["state"]
+                    this.$router.push({query: query});
+                }
+
+                this.load(this.onDataLoaded);
+            },
         },
         computed: {
             ...mapState("stat", ["daily", "dailyGroupByFlow"]),
diff --git a/ui/src/mixins/dataTableActions.js b/ui/src/mixins/dataTableActions.js
index c77c30355..e95ad62b9 100644
--- a/ui/src/mixins/dataTableActions.js
+++ b/ui/src/mixins/dataTableActions.js
@@ -74,7 +74,7 @@ export default {
             let query = {...this.$route.query};
 
             for (const [key, value] of Object.entries(values)) {
-                if (value === undefined || value === "" || value === null) {
+                if (value === undefined || value === "" || value === null || value.length === 0) {
                     delete query[key]
                 } else {
                     query[key] = value;
diff --git a/webserver/src/main/java/io/kestra/webserver/controllers/api/StatsController.java b/webserver/src/main/java/io/kestra/webserver/controllers/api/StatsController.java
index 84ab002fa..61c86e2d2 100644
--- a/webserver/src/main/java/io/kestra/webserver/controllers/api/StatsController.java
+++ b/webserver/src/main/java/io/kestra/webserver/controllers/api/StatsController.java
@@ -3,6 +3,7 @@ package io.kestra.webserver.controllers.api;
 import io.kestra.core.models.executions.Execution;
 import io.kestra.core.models.executions.statistics.DailyExecutionStatistics;
 import io.kestra.core.models.executions.statistics.LogStatistics;
+import io.kestra.core.models.flows.State;
 import io.kestra.core.repositories.ExecutionRepositoryInterface;
 import io.kestra.core.repositories.LogRepositoryInterface;
 import io.kestra.core.tenant.TenantService;
@@ -51,8 +52,8 @@ public class StatsController {
             statisticRequest.startDate() != null ? statisticRequest.startDate().withZoneSameInstant(ZoneId.systemDefault()) : null,
             statisticRequest.endDate() != null ? statisticRequest.endDate().withZoneSameInstant(ZoneId.systemDefault()) : null,
             null,
-            false
-        );
+            statisticRequest.state(),
+            false);
     }
 
     @ExecuteOn(TaskExecutors.IO)
@@ -67,8 +68,8 @@ public class StatsController {
             statisticRequest.startDate() != null ? statisticRequest.startDate().withZoneSameInstant(ZoneId.systemDefault()) : null,
             statisticRequest.endDate() != null ? statisticRequest.endDate().withZoneSameInstant(ZoneId.systemDefault()) : null,
             null,
-            true
-        );
+            statisticRequest.state(),
+            true);
     }
 
     @ExecuteOn(TaskExecutors.IO)
@@ -121,7 +122,8 @@ public class StatsController {
         @Parameter(description = "A namespace filter prefix") @Nullable String namespace,
         @Parameter(description = "A flow id filter") @Nullable String flowId,
         @Parameter(description = "The start datetime, default to now") @Nullable @Format("yyyy-MM-dd'T'HH:mm[:ss][.SSS][XXX]") ZonedDateTime startDate,
-        @Parameter(description = "The end datetime, default to now") @Nullable @Format("yyyy-MM-dd'T'HH:mm[:ss][.SSS][XXX]")ZonedDateTime endDate
+        @Parameter(description = "The end datetime, default to now") @Nullable @Format("yyyy-MM-dd'T'HH:mm[:ss][.SSS][XXX]")ZonedDateTime endDate,
+        @Parameter(description = "the state of the execution") @Nullable List<State.Type> state
     ) {}
 
     @Introspected


Test patch:
diff --git a/core/src/test/java/io/kestra/core/repositories/AbstractExecutionRepositoryTest.java b/core/src/test/java/io/kestra/core/repositories/AbstractExecutionRepositoryTest.java
index b9e107f7c..61a84f592 100644
--- a/core/src/test/java/io/kestra/core/repositories/AbstractExecutionRepositoryTest.java
+++ b/core/src/test/java/io/kestra/core/repositories/AbstractExecutionRepositoryTest.java
@@ -427,8 +427,8 @@ public abstract class AbstractExecutionRepositoryTest {
             ZonedDateTime.now().minusDays(10),
             ZonedDateTime.now(),
             null,
-            false
-        );
+            null,
+            false);
 
         assertThat(result.size(), is(11));
         assertThat(result.get(10).getExecutionCounts().size(), is(11));
@@ -456,8 +456,8 @@ public abstract class AbstractExecutionRepositoryTest {
             ZonedDateTime.now().minusDays(10),
             ZonedDateTime.now(),
             null,
-            true
-        );
+            null,
+            true);
 
         assertThat(result.size(), is(11));
         assertThat(result.get(10).getExecutionCounts().size(), is(11));