diff --git a/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-definitions/components/browser.tsx b/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-definitions/components/browser.tsx index b3bd10a4c..68fc6e5d8 100644 --- a/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-definitions/components/browser.tsx +++ b/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-definitions/components/browser.tsx @@ -2,7 +2,8 @@ import {Component, Event, EventEmitter, h, Host, Method, Prop, State, Watch} fro import {OrderBy, PagedList, VersionOptions} from '../../../models'; import {Container} from 'typedi'; import {DeleteIcon, EditIcon, PublishIcon, UnPublishIcon} from '../../../components/icons/tooling'; -import {Filter, FilterProps} from './filter'; +import { Search } from "./search"; +import { Filter, FilterProps } from './filter'; import {PagerData} from '../../../components/shared/pager/pager'; import {updateSelectedWorkflowDefinitions} from '../services/utils'; import {WorkflowDefinitionSummary} from "../models/entities"; @@ -43,6 +44,7 @@ export class WorkflowDefinitionBrowser { @State() private orderBy?: WorkflowDefinitionsOrderBy; @State() private labels?: string[]; @State() private selectAllChecked: boolean; + @State() private searchTerm?: string; async componentWillLoad() { const persistedRequest = getRequest(); @@ -60,6 +62,13 @@ export class WorkflowDefinitionBrowser { this.newWorkflowDefinitionSelected.emit(); }; + private onSearch = async (term: string) => { + this.searchTerm = term; + this.resetPagination(); + await this.loadWorkflowDefinitions(); + }; + + private async onPublishClick(e: MouseEvent, workflowDefinition: WorkflowDefinitionSummary) { await this.api.publish(workflowDefinition); await this.loadWorkflowDefinitions(); @@ -146,6 +155,7 @@ export class WorkflowDefinitionBrowser { const materializerName = 'Json'; const request: ListWorkflowDefinitionsRequest = { + searchTerm: this.searchTerm, materializerName, page: this.currentPage, pageSize: this.currentPageSize, @@ -252,6 +262,8 @@ export class WorkflowDefinitionBrowser {

Workflow Definitions

+ +
diff --git a/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-definitions/components/filter.tsx b/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-definitions/components/filter.tsx index cbf71549a..0fa0571fa 100644 --- a/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-definitions/components/filter.tsx +++ b/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-definitions/components/filter.tsx @@ -40,8 +40,6 @@ export const Filter: FunctionalComponent = ({ pageSizeFilter, order
 
- - diff --git a/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-definitions/components/search.tsx b/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-definitions/components/search.tsx new file mode 100644 index 000000000..b7adb454b --- /dev/null +++ b/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-definitions/components/search.tsx @@ -0,0 +1,49 @@ +import {FunctionalComponent, h} from "@stencil/core"; +import {debounce} from 'lodash'; + +export interface SearchProps { + onSearch: (term: string) => void; +} + +export const Search: FunctionalComponent = ({onSearch}) => { + + const onSubmit = (e: Event) => { + e.preventDefault(); + const form = e.target as HTMLFormElement; + const formData = new FormData(form); + const searchTerm: FormDataEntryValue = formData.get('searchTerm').toString(); + + onSearch(searchTerm); + } + + const onSearchDebounced = debounce(onSearch, 200); + + const onKeyUp = (e: KeyboardEvent) => { + const term = (e.target as HTMLInputElement).value; + onSearchDebounced(term); + }; + + return
+
+
+
+ +
+
+ + + +
+ +
+ +
+
+
; +} diff --git a/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-definitions/services/api.ts b/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-definitions/services/api.ts index dafe37aa5..74826ea3c 100644 --- a/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-definitions/services/api.ts +++ b/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-definitions/services/api.ts @@ -91,6 +91,8 @@ export class WorkflowDefinitionsApi { if (!!request.label) queryString.label = request.label; + if (!!request.searchTerm) queryString.searchTerm = request.searchTerm; + const queryStringText = serializeQueryString(queryString); const httpClient = await this.getHttpClient(); const response = await httpClient.get>(`workflow-definitions${queryStringText}`); @@ -255,6 +257,7 @@ export enum WorkflowDefinitionsOrderBy { } export interface ListWorkflowDefinitionsRequest { + searchTerm?: string; page?: number; pageSize?: number; definitionIds?: Array; diff --git a/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-instances/components/browser.tsx b/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-instances/components/browser.tsx index 277e9eedd..b1c544c89 100644 --- a/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-instances/components/browser.tsx +++ b/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-instances/components/browser.tsx @@ -57,7 +57,7 @@ export class WorkflowInstanceBrowser { if (persistedRequest) { // TODO: Persist search term, need to bind the value to the input - // this.searchTerm = persistedRequest.searchTerm + //this.searchTerm = persistedRequest.searchTerm this.currentPage = persistedRequest.page this.currentPageSize = persistedRequest.pageSize this.orderBy = persistedRequest.orderBy diff --git a/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-instances/components/filter.tsx b/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-instances/components/filter.tsx index 6c5553bb1..ef483dcf5 100644 --- a/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-instances/components/filter.tsx +++ b/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-instances/components/filter.tsx @@ -53,9 +53,11 @@ export const Filter: FunctionalComponent = ({pageSizeFilter, workfl
 
+ + diff --git a/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-instances/services/workflow-instances-api.ts b/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-instances/services/workflow-instances-api.ts index 287ba52a2..cf8e1faf5 100644 --- a/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-instances/services/workflow-instances-api.ts +++ b/src/designer/designer_packages/elsa-workflows-designer/src/modules/workflow-instances/services/workflow-instances-api.ts @@ -114,4 +114,4 @@ export interface GetWorkflowJournalRequest { workflowInstanceId: string; page?: number; pageSize?: number; -} \ No newline at end of file +} diff --git a/src/modules/Elsa.Dapper/Extensions/ParameterizedQueryBuilderExtensions.cs b/src/modules/Elsa.Dapper/Extensions/ParameterizedQueryBuilderExtensions.cs index d9b313d52..bac9afa63 100644 --- a/src/modules/Elsa.Dapper/Extensions/ParameterizedQueryBuilderExtensions.cs +++ b/src/modules/Elsa.Dapper/Extensions/ParameterizedQueryBuilderExtensions.cs @@ -96,6 +96,22 @@ public static class ParameterizedQueryBuilderExtensions return query; } + /// + /// Appends an search for workflowdfefinitions to the search + /// + /// The query. + /// The search term. + public static ParameterizedQuery AndWorkflowDefinitionSearchTerm(this ParameterizedQuery query, string? searchTerm) + { + if (string.IsNullOrWhiteSpace(searchTerm)) return query; + + var searchTermLike = $"%{searchTerm}%"; + query.Sql.AppendLine("and (Name like @SearchTermLike or Description like @SearchTermLike or Id = @SearchTerm or DefinitionId = @SearchTerm)"); + query.Parameters.Add("@SearchTerm", searchTerm); + query.Parameters.Add("@SearchTermLike", searchTermLike); + return query; + } + /// /// Appends an AND clause to the query if the value is not null. /// @@ -153,8 +169,11 @@ public static class ParameterizedQueryBuilderExtensions public static ParameterizedQuery AndWorkflowInstanceSearchTerm(this ParameterizedQuery query, string? searchTerm) { if (string.IsNullOrWhiteSpace(searchTerm)) return query; - query.Sql.AppendLine("and (Name like @SearchTerm or ID like @SearchTerm or DefinitionId like @SearchTerm or CorrelationId like @SearchTerm)"); + + var searchTermLike = $"%{searchTerm}%"; + query.Sql.AppendLine("and (Name like @SearchTermLike or ID = @SearchTerm or DefinitionId = @SearchTerm or DefinitionVersionId = @SearchTerm or CorrelationId = @SearchTerm)"); query.Parameters.Add("@SearchTerm", searchTerm); + query.Parameters.Add("@SearchTermLike", searchTermLike); return query; } diff --git a/src/modules/Elsa.Dapper/Modules/Management/Services/DapperWorkflowDefinitionStore.cs b/src/modules/Elsa.Dapper/Modules/Management/Services/DapperWorkflowDefinitionStore.cs index 57ac5f676..90802f584 100644 --- a/src/modules/Elsa.Dapper/Modules/Management/Services/DapperWorkflowDefinitionStore.cs +++ b/src/modules/Elsa.Dapper/Modules/Management/Services/DapperWorkflowDefinitionStore.cs @@ -152,7 +152,8 @@ public class DapperWorkflowDefinitionStore : IWorkflowDefinitionStore .Equals(nameof(WorkflowDefinition.MaterializerName), filter.MaterializerName) .Equals(nameof(WorkflowDefinition.Name), filter.Name) .In(nameof(WorkflowDefinition.Name), filter.Names) - .Equals(nameof(WorkflowDefinition.Options.UsableAsActivity), filter.UsableAsActivity); + .Equals(nameof(WorkflowDefinition.Options.UsableAsActivity), filter.UsableAsActivity) + .AndWorkflowDefinitionSearchTerm(filter.SearchTerm); } private Page Map(Page source) => new(Map(source.Items).ToList(), source.TotalCount); diff --git a/src/modules/Elsa.Dapper/Modules/Management/Services/DapperWorkflowInstanceStore.cs b/src/modules/Elsa.Dapper/Modules/Management/Services/DapperWorkflowInstanceStore.cs index 994a4382b..1d0020017 100644 --- a/src/modules/Elsa.Dapper/Modules/Management/Services/DapperWorkflowInstanceStore.cs +++ b/src/modules/Elsa.Dapper/Modules/Management/Services/DapperWorkflowInstanceStore.cs @@ -136,8 +136,7 @@ public class DapperWorkflowInstanceStore : IWorkflowInstanceStore .Equals(nameof(WorkflowInstance.Name), filter.Version) .Equals(nameof(WorkflowInstance.CorrelationId), filter.CorrelationId) .In(nameof(WorkflowInstance.CorrelationId), filter.CorrelationIds) - .AndWorkflowInstanceSearchTerm(filter.SearchTerm) - ; + .AndWorkflowInstanceSearchTerm(filter.SearchTerm); } private async Task> MapAsync(Page source) diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Management/WorkflowDefinitionStore.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Management/WorkflowDefinitionStore.cs index 9e5944489..c6d5822b8 100644 --- a/src/modules/Elsa.EntityFrameworkCore/Modules/Management/WorkflowDefinitionStore.cs +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Management/WorkflowDefinitionStore.cs @@ -183,7 +183,7 @@ public class EFCoreWorkflowDefinitionStore : IWorkflowDefinitionStore if (filter.Name != null) queryable = queryable.Where(x => x.Name == filter.Name); if (filter.Names != null) queryable = queryable.Where(x => filter.Names.Contains(x.Name!)); if (filter.UsableAsActivity != null) queryable = queryable.Where(x => EF.Property(x, "UsableAsActivity") == filter.UsableAsActivity); - + if (!string.IsNullOrWhiteSpace(filter.SearchTerm)) queryable = queryable.Where(x => x.Name.Contains(filter.SearchTerm) || x.Description.Contains(filter.SearchTerm) || x.Id == filter.SearchTerm || x.DefinitionId == filter.SearchTerm); return queryable; } diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/List/Endpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/List/Endpoint.cs index b89577e99..9c69a3aaf 100644 --- a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/List/Endpoint.cs +++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/List/Endpoint.cs @@ -39,6 +39,10 @@ internal class List : ElsaEndpoint var versionOptions = request.VersionOptions != null ? VersionOptions.FromString(request.VersionOptions) : default(VersionOptions?); var splitIds = request.DefinitionIds ?? Array.Empty(); + if (!string.IsNullOrWhiteSpace(request.SearchTerm)) { + return new WorkflowDefinitionFilter { SearchTerm = request.SearchTerm, VersionOptions = versionOptions }; + } + return splitIds.Any() ? new WorkflowDefinitionFilter { DefinitionIds = splitIds, VersionOptions = versionOptions } : new WorkflowDefinitionFilter { MaterializerName = request.MaterializerName, VersionOptions = versionOptions }; diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/List/Models.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/List/Models.cs index 92e092fd7..17f0ec584 100644 --- a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/List/Models.cs +++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/List/Models.cs @@ -15,6 +15,7 @@ public class Request public int? PageSize { get; set; } public OrderByWorkflowDefinition? OrderBy { get; set; } public OrderDirection? OrderDirection { get; set; } + public string? SearchTerm { get; set; } } public class Response diff --git a/src/modules/Elsa.Workflows.Management/Filters/WorkflowDefinitionFilter.cs b/src/modules/Elsa.Workflows.Management/Filters/WorkflowDefinitionFilter.cs index 66addfe1b..407328999 100644 --- a/src/modules/Elsa.Workflows.Management/Filters/WorkflowDefinitionFilter.cs +++ b/src/modules/Elsa.Workflows.Management/Filters/WorkflowDefinitionFilter.cs @@ -40,7 +40,12 @@ public class WorkflowDefinitionFilter /// Filter by the name of the workflow definition. /// public string? Name { get; set; } - + + /// + /// Filter by the name or id of the workflow definition. + /// + public string? SearchTerm { get; set; } + /// /// Filter by the names of the workflow definitions. /// @@ -73,7 +78,8 @@ public class WorkflowDefinitionFilter if (filter.Name != null) queryable = queryable.Where(x => x.Name == filter.Name); if (filter.Names != null) queryable = queryable.Where(x => filter.Names.Contains(x.Name!)); if (filter.UsableAsActivity != null) queryable = queryable.Where(x => x.Options.UsableAsActivity == filter.UsableAsActivity); - + if (!string.IsNullOrWhiteSpace(filter.SearchTerm)) queryable = queryable.Where(x => x.Name.Contains(filter.SearchTerm) || x.Description.Contains(filter.SearchTerm) || x.Id == filter.SearchTerm || x.DefinitionId == filter.SearchTerm); + return queryable; } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Filters/WorkflowInstanceFilter.cs b/src/modules/Elsa.Workflows.Management/Filters/WorkflowInstanceFilter.cs index 06c551790..171a1e3dd 100644 --- a/src/modules/Elsa.Workflows.Management/Filters/WorkflowInstanceFilter.cs +++ b/src/modules/Elsa.Workflows.Management/Filters/WorkflowInstanceFilter.cs @@ -94,9 +94,10 @@ public class WorkflowInstanceFilter query = from instance in query where instance.Name!.Contains(searchTerm) - || instance.Id.Contains(searchTerm) - || instance.DefinitionId.Contains(searchTerm) - || instance.CorrelationId!.Contains(searchTerm) + || instance.DefinitionVersionId.Equals(searchTerm) + || instance.Id.Equals(searchTerm) + || instance.DefinitionId.Equals(searchTerm) + || instance.CorrelationId!.Equals(searchTerm) select instance; }