diff --git a/src/activities/Elsa.Activities.Http/Activities/HttpEndpoint/HttpEndpoint.cs b/src/activities/Elsa.Activities.Http/Activities/HttpEndpoint/HttpEndpoint.cs index e943dd5e5..b47e61434 100644 --- a/src/activities/Elsa.Activities.Http/Activities/HttpEndpoint/HttpEndpoint.cs +++ b/src/activities/Elsa.Activities.Http/Activities/HttpEndpoint/HttpEndpoint.cs @@ -57,7 +57,7 @@ namespace Elsa.Activities.Http /// The to parse the received request content into if is set to true. /// If not set, the content will be parse into a default type, depending on the parser associated with the received content-type header. /// - [ActivityProperty] + [ActivityProperty(Category = PropertyCategories.Advanced)] public Type? TargetType { get; set; } protected override async ValueTask OnExecuteAsync(ActivityExecutionContext context) => context.WorkflowExecutionContext.IsFirstPass ? await ExecuteInternalAsync(context.CancellationToken) : Suspend(); diff --git a/src/activities/Elsa.Activities.Telnyx/Activities/AnswerCall.cs b/src/activities/Elsa.Activities.Telnyx/Activities/AnswerCall.cs index 09e11c8ac..e456e80c3 100644 --- a/src/activities/Elsa.Activities.Telnyx/Activities/AnswerCall.cs +++ b/src/activities/Elsa.Activities.Telnyx/Activities/AnswerCall.cs @@ -25,22 +25,22 @@ namespace Elsa.Activities.Telnyx.Activities _telnyxClient = telnyxClient; } - [ActivityProperty(Label = "Call Control ID", Hint = "Unique identifier and token for controlling the call")] + [ActivityProperty(Label = "Call Control ID", Hint = "Unique identifier and token for controlling the call", Category = PropertyCategories.Advanced)] public string CallControlId { get; set; } = default!; - [ActivityProperty(Label = "Billing Group ID", Hint = "Use this field to set the Billing Group ID for the call. Must be a valid and existing Billing Group ID.")] + [ActivityProperty(Label = "Billing Group ID", Hint = "Use this field to set the Billing Group ID for the call. Must be a valid and existing Billing Group ID.", Category = PropertyCategories.Advanced)] public string? BillingGroupId { get; set; } - [ActivityProperty(Label = "Client State", Hint = "Use this field to add state to every subsequent webhook. It must be a valid Base-64 encoded string.")] + [ActivityProperty(Label = "Client State", Hint = "Use this field to add state to every subsequent webhook. It must be a valid Base-64 encoded string.", Category = PropertyCategories.Advanced)] public string? ClientState { get; set; } - [ActivityProperty(Label = "Command ID", Hint = "Use this field to avoid duplicate commands. Telnyx will ignore commands with the same Command ID.")] + [ActivityProperty(Label = "Command ID", Hint = "Use this field to avoid duplicate commands. Telnyx will ignore commands with the same Command ID.", Category = PropertyCategories.Advanced)] public string? CommandId { get; set; } - [ActivityProperty(Label = "Webhook URL", Hint = "Use this field to override the URL for which Telnyx will send subsequent webhooks to for this call.")] + [ActivityProperty(Label = "Webhook URL", Hint = "Use this field to override the URL for which Telnyx will send subsequent webhooks to for this call.", Category = PropertyCategories.Advanced)] public string? WebhookUrl { get; set; } - [ActivityProperty(Label = "Webhook URL Method", Hint = "HTTP request type used for Webhook URL", UIHint = ActivityPropertyUIHints.Dropdown, Options = new[] { "GET", "POST" })] + [ActivityProperty(Label = "Webhook URL Method", Hint = "HTTP request type used for Webhook URL", UIHint = ActivityPropertyUIHints.Dropdown, Options = new[] { "GET", "POST" }, Category = PropertyCategories.Advanced)] public string? WebhookUrlMethod { get; set; } protected override async ValueTask OnExecuteAsync(ActivityExecutionContext context) diff --git a/src/activities/Elsa.Activities.Telnyx/Activities/GatherUsingAudio.cs b/src/activities/Elsa.Activities.Telnyx/Activities/GatherUsingAudio.cs index 5229a3ef7..1f0973bac 100644 --- a/src/activities/Elsa.Activities.Telnyx/Activities/GatherUsingAudio.cs +++ b/src/activities/Elsa.Activities.Telnyx/Activities/GatherUsingAudio.cs @@ -5,6 +5,7 @@ using Elsa.Activities.Telnyx.Client.Models; using Elsa.Activities.Telnyx.Client.Services; using Elsa.ActivityResults; using Elsa.Attributes; +using Elsa.Design; using Elsa.Exceptions; using Elsa.Services; using Elsa.Services.Models; @@ -27,25 +28,25 @@ namespace Elsa.Activities.Telnyx.Activities _telnyxClient = telnyxClient; } - [ActivityProperty(Label = "Call Control ID", Hint = "Unique identifier and token for controlling the call")] + [ActivityProperty(Label = "Call Control ID", Hint = "Unique identifier and token for controlling the call", Category = PropertyCategories.Advanced)] public string CallControlId { get; set; } = default!; [ActivityProperty(Label = "Audio URL", Hint = "The URL of a file to be played back at the beginning of each prompt. The URL can point to either a WAV or MP3 file.")] public Uri AudioUrl { get; set; } = default!; - [ActivityProperty(Label = "Client State", Hint = "Use this field to add state to every subsequent webhook. It must be a valid Base-64 encoded string.")] + [ActivityProperty(Label = "Client State", Hint = "Use this field to add state to every subsequent webhook. It must be a valid Base-64 encoded string.", Category = PropertyCategories.Advanced)] public string? ClientState { get; set; } - [ActivityProperty(Label = "Command ID", Hint = "Use this field to avoid duplicate commands. Telnyx will ignore commands with the same Command ID.")] + [ActivityProperty(Label = "Command ID", Hint = "Use this field to avoid duplicate commands. Telnyx will ignore commands with the same Command ID.", Category = PropertyCategories.Advanced)] public string? CommandId { get; set; } - [ActivityProperty(Label = "Inter Digit Timeout", Hint = "The number of milliseconds to wait for input between digits.")] + [ActivityProperty(Label = "Inter Digit Timeout", Hint = "The number of milliseconds to wait for input between digits.", Category = PropertyCategories.Advanced)] public int? InterDigitTimeoutMillis { get; set; } = 5000; [ActivityProperty(Label = "Invalid Audio Url", Hint = "The URL of a file to play when digits don't match the Valid Digits setting or the number of digits is not between Min and Max. The URL can point to either a WAV or MP3 file.")] public Uri? InvalidAudioUrl { get; set; } - [ActivityProperty(Label = "Valid Digits", Hint = "A list of all digits accepted as valid.")] + [ActivityProperty(Label = "Valid Digits", Hint = "A list of all digits accepted as valid.", Category = PropertyCategories.Advanced)] public string? ValidDigits { get; set; } = "0123456789#*"; [ActivityProperty(Label = "Minimum Digits", Hint = "The minimum number of digits to fetch. This parameter has a minimum value of 1.")] @@ -60,7 +61,7 @@ namespace Elsa.Activities.Telnyx.Activities [ActivityProperty(Label = "Terminating Digit", Hint = "The digit used to terminate input if fewer than `maximum_digits` digits have been gathered.")] public string? TerminatingDigit { get; set; } = "#"; - [ActivityProperty(Label = "Timeout", Hint = "The number of milliseconds to wait for a DTMF response after file playback ends before a replaying the sound file.")] + [ActivityProperty(Label = "Timeout", Hint = "The number of milliseconds to wait for a DTMF response after file playback ends before a replaying the sound file.", Category = PropertyCategories.Advanced)] public int? TimeoutMillis { get; set; } = 60000; protected override async ValueTask OnExecuteAsync(ActivityExecutionContext context) diff --git a/src/activities/Elsa.Activities.Telnyx/Activities/HangupCall.cs b/src/activities/Elsa.Activities.Telnyx/Activities/HangupCall.cs index 316081dcf..03cb212fd 100644 --- a/src/activities/Elsa.Activities.Telnyx/Activities/HangupCall.cs +++ b/src/activities/Elsa.Activities.Telnyx/Activities/HangupCall.cs @@ -4,6 +4,7 @@ using Elsa.Activities.Telnyx.Client.Models; using Elsa.Activities.Telnyx.Client.Services; using Elsa.ActivityResults; using Elsa.Attributes; +using Elsa.Design; using Elsa.Exceptions; using Elsa.Services; using Elsa.Services.Models; @@ -26,13 +27,13 @@ namespace Elsa.Activities.Telnyx.Activities _telnyxClient = telnyxClient; } - [ActivityProperty(Label = "Call Control ID", Hint = "Unique identifier and token for controlling the call")] + [ActivityProperty(Label = "Call Control ID", Hint = "Unique identifier and token for controlling the call", Category = PropertyCategories.Advanced)] public string CallControlId { get; set; } = default!; - [ActivityProperty(Label = "Client State", Hint = "Use this field to add state to every subsequent webhook. It must be a valid Base-64 encoded string.")] + [ActivityProperty(Label = "Client State", Hint = "Use this field to add state to every subsequent webhook. It must be a valid Base-64 encoded string.", Category = PropertyCategories.Advanced)] public string? ClientState { get; set; } - [ActivityProperty(Label = "Command ID", Hint = "Use this field to avoid duplicate commands. Telnyx will ignore commands with the same Command ID.")] + [ActivityProperty(Label = "Command ID", Hint = "Use this field to avoid duplicate commands. Telnyx will ignore commands with the same Command ID.", Category = PropertyCategories.Advanced)] public string? CommandId { get; set; } protected override async ValueTask OnExecuteAsync(ActivityExecutionContext context) diff --git a/src/activities/Elsa.Activities.Telnyx/Activities/TransferCall.cs b/src/activities/Elsa.Activities.Telnyx/Activities/TransferCall.cs index 2743bd6f6..34d238790 100644 --- a/src/activities/Elsa.Activities.Telnyx/Activities/TransferCall.cs +++ b/src/activities/Elsa.Activities.Telnyx/Activities/TransferCall.cs @@ -29,7 +29,7 @@ namespace Elsa.Activities.Telnyx.Activities _telnyxClient = telnyxClient; } - [ActivityProperty(Label = "Call Control ID", Hint = "Unique identifier and token for controlling the call.")] + [ActivityProperty(Label = "Call Control ID", Hint = "Unique identifier and token for controlling the call.", Category = PropertyCategories.Advanced)] public string CallControlId { get; set; } = default!; [ActivityProperty(Label = "To", Hint = "The DID or SIP URI to dial out and bridge to the given call.")] @@ -48,40 +48,40 @@ namespace Elsa.Activities.Telnyx.Activities Options = new[] { "disabled", "detect", "detect_beep", "detect_words", "greeting_end" })] public string? AnsweringMachineDetection { get; set; } - [ActivityProperty(Label = "Answering Machine Detection Configuration", Hint = "Optional configuration parameters to modify answering machine detection performance.")] + [ActivityProperty(Label = "Answering Machine Detection Configuration", Hint = "Optional configuration parameters to modify answering machine detection performance.", Category = PropertyCategories.Advanced)] public AnsweringMachineConfig? AnsweringMachineDetectionConfig { get; set; } - [ActivityProperty(Label = "Command ID", Hint = "Use this field to avoid duplicate commands. Telnyx will ignore commands with the same Command ID.")] + [ActivityProperty(Label = "Command ID", Hint = "Use this field to avoid duplicate commands. Telnyx will ignore commands with the same Command ID.", Category = PropertyCategories.Advanced)] public string? CommandId { get; set; } [ActivityProperty(Label = "Audio URL", Hint = "Audio URL to be played back when the transfer destination answers before bridging the call. The URL can point to either a WAV or MP3 file.")] public Uri? AudioUrl { get; set; } - [ActivityProperty(Label = "Client State", Hint = "Use this field to add state to every subsequent webhook. It must be a valid Base-64 encoded string.")] + [ActivityProperty(Label = "Client State", Hint = "Use this field to add state to every subsequent webhook. It must be a valid Base-64 encoded string.", Category = PropertyCategories.Advanced)] public string? ClientState { get; set; } - [ActivityProperty(Label = "Target Leg Client State", Hint = "Use this field to add state to every subsequent webhook for the new leg. It must be a valid Base-64 encoded string.")] + [ActivityProperty(Label = "Target Leg Client State", Hint = "Use this field to add state to every subsequent webhook for the new leg. It must be a valid Base-64 encoded string.", Category = PropertyCategories.Advanced)] public string? TargetLegClientState { get; set; } - [ActivityProperty(Label = "Custom Headers", Hint = "Custom headers to be added to the SIP INVITE.")] + [ActivityProperty(Label = "Custom Headers", Hint = "Custom headers to be added to the SIP INVITE.", Category = PropertyCategories.Advanced)] public IList
? CustomHeaders { get; set; } - [ActivityProperty(Label = "SIP Authentication Username", Hint = "SIP Authentication username used for SIP challenges.")] + [ActivityProperty(Label = "SIP Authentication Username", Hint = "SIP Authentication username used for SIP challenges.", Category = "SIP Authentication")] public string? SipAuthUsername { get; set; } - [ActivityProperty(Label = "SIP Authentication Password", Hint = "SIP Authentication password used for SIP challenges.")] + [ActivityProperty(Label = "SIP Authentication Password", Hint = "SIP Authentication password used for SIP challenges.", Category = "SIP Authentication")] public string? SipAuthPassword { get; set; } - [ActivityProperty(Label = "Time Limit", Hint = "Sets the maximum duration of a Call Control Leg in seconds.")] + [ActivityProperty(Label = "Time Limit", Hint = "Sets the maximum duration of a Call Control Leg in seconds.", Category = PropertyCategories.Advanced)] public int? TimeLimitSecs { get; set; } - [ActivityProperty(Label = "Timeout", Hint = "The number of seconds that Telnyx will wait for the call to be answered by the destination to which it is being transferred.")] + [ActivityProperty(Label = "Timeout", Hint = "The number of seconds that Telnyx will wait for the call to be answered by the destination to which it is being transferred.", Category = PropertyCategories.Advanced)] public int? TimeoutSecs { get; set; } - [ActivityProperty(Label = "Webhook URL", Hint = "Use this field to override the URL for which Telnyx will send subsequent webhooks to for this call.")] + [ActivityProperty(Label = "Webhook URL", Hint = "Use this field to override the URL for which Telnyx will send subsequent webhooks to for this call.", Category = PropertyCategories.Advanced)] public string? WebhookUrl { get; set; } - [ActivityProperty(Label = "Webhook URL Method", Hint = "HTTP request type used for Webhook URL", UIHint = ActivityPropertyUIHints.Dropdown, Options = new[] { "GET", "POST" })] + [ActivityProperty(Label = "Webhook URL Method", Hint = "HTTP request type used for Webhook URL", UIHint = ActivityPropertyUIHints.Dropdown, Options = new[] { "GET", "POST" }, Category = PropertyCategories.Advanced)] public string? WebhookUrlMethod { get; set; } protected override async ValueTask OnExecuteAsync(ActivityExecutionContext context) diff --git a/src/clients/Elsa.Client/Models/ActivityPropertyDescriptor.cs b/src/clients/Elsa.Client/Models/ActivityPropertyDescriptor.cs index a79aff616..e60562800 100644 --- a/src/clients/Elsa.Client/Models/ActivityPropertyDescriptor.cs +++ b/src/clients/Elsa.Client/Models/ActivityPropertyDescriptor.cs @@ -11,5 +11,6 @@ namespace Elsa.Client.Models [DataMember(Order = 3)] public string? Label { get; set; } [DataMember(Order = 4)] public string? Hint { get; set; } [DataMember(Order = 5)] public JToken? Options { get; set; } + [DataMember(Order = 6)] public string? Category { get; set; } } } \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Attributes/ActivityPropertyAttribute.cs b/src/core/Elsa.Abstractions/Attributes/ActivityPropertyAttribute.cs index 85dc398f0..549e05765 100644 --- a/src/core/Elsa.Abstractions/Attributes/ActivityPropertyAttribute.cs +++ b/src/core/Elsa.Abstractions/Attributes/ActivityPropertyAttribute.cs @@ -34,5 +34,10 @@ namespace Elsa.Attributes /// The name of a static method that provides options. /// public Type? OptionsProvider { get; set; } + + /// + /// A category to group this property with. + /// + public string? Category { get; set; } } } \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Design/PropertyCategories.cs b/src/core/Elsa.Abstractions/Design/PropertyCategories.cs new file mode 100644 index 000000000..a55e3a138 --- /dev/null +++ b/src/core/Elsa.Abstractions/Design/PropertyCategories.cs @@ -0,0 +1,7 @@ +namespace Elsa.Design +{ + public static class PropertyCategories + { + public const string Advanced = "Advanced"; + } +} \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Metadata/ActivityPropertyDescriptor.cs b/src/core/Elsa.Abstractions/Metadata/ActivityPropertyDescriptor.cs index d67aa39af..dbcc9cbe3 100644 --- a/src/core/Elsa.Abstractions/Metadata/ActivityPropertyDescriptor.cs +++ b/src/core/Elsa.Abstractions/Metadata/ActivityPropertyDescriptor.cs @@ -6,13 +6,14 @@ namespace Elsa.Metadata { } - public ActivityPropertyDescriptor(string name, string uiHint, string label, string? hint, object? options) + public ActivityPropertyDescriptor(string name, string uiHint, string label, string? hint, object? options, string category) { Name = name; UIHint = uiHint; Label = label; Hint = hint; Options = options; + Category = category; } public string Name { get; set; } = default!; @@ -20,5 +21,6 @@ namespace Elsa.Metadata public string Label { get; set; } = default!; public string? Hint { get; set; } public object? Options { get; set; } + public string? Category { get; set; } } } \ No newline at end of file diff --git a/src/core/Elsa.Core/Metadata/TypedActivityTypeDescriber.cs b/src/core/Elsa.Core/Metadata/TypedActivityTypeDescriber.cs index 799f45500..974d97981 100644 --- a/src/core/Elsa.Core/Metadata/TypedActivityTypeDescriber.cs +++ b/src/core/Elsa.Core/Metadata/TypedActivityTypeDescriber.cs @@ -58,7 +58,8 @@ namespace Elsa.Metadata _uiHintResolver.GetUIHint(propertyInfo), activityPropertyAttribute.Label ?? propertyInfo.Name.Humanize(LetterCasing.Title), activityPropertyAttribute.Hint, - _optionsResolver.GetOptions(propertyInfo) + _optionsResolver.GetOptions(propertyInfo), + activityPropertyAttribute.Category ); } } diff --git a/src/dashboards/blazor/ElsaDashboard.Shared/Surrogates/ActivityPropertyDescriptorSurrogate.cs b/src/dashboards/blazor/ElsaDashboard.Shared/Surrogates/ActivityPropertyDescriptorSurrogate.cs index a590f7ba2..a946e4743 100644 --- a/src/dashboards/blazor/ElsaDashboard.Shared/Surrogates/ActivityPropertyDescriptorSurrogate.cs +++ b/src/dashboards/blazor/ElsaDashboard.Shared/Surrogates/ActivityPropertyDescriptorSurrogate.cs @@ -1,8 +1,6 @@ using Elsa.Client.Models; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using NodaTime; -using NodaTime.Serialization.JsonNet; using ProtoBuf; namespace ElsaDashboard.Shared.Surrogates @@ -10,17 +8,6 @@ namespace ElsaDashboard.Shared.Surrogates [ProtoContract(IgnoreListHandling = true)] public class ActivityPropertyDescriptorSurrogate { - private static readonly JsonSerializerSettings SerializerSettings; - - static ActivityPropertyDescriptorSurrogate() - { - SerializerSettings = new JsonSerializerSettings().ConfigureForNodaTime(DateTimeZoneProviders.Tzdb); - } - - public ActivityPropertyDescriptorSurrogate() - { - } - public ActivityPropertyDescriptorSurrogate(ActivityPropertyDescriptor value) { Name = value.Name; @@ -28,6 +15,7 @@ namespace ElsaDashboard.Shared.Surrogates Label = value.Label; Hint = value.Hint; Options = value.Options?.ToString(Formatting.None); + Category = value.Category; } [ProtoMember(1)] public string? Name { get; } @@ -35,6 +23,7 @@ namespace ElsaDashboard.Shared.Surrogates [ProtoMember(3)] public string? Label { get; } [ProtoMember(4)] public string? Hint { get; } [ProtoMember(5)] public string? Options { get; } + [ProtoMember(6)] public string? Category { get; } public static implicit operator ActivityPropertyDescriptor?(ActivityPropertyDescriptorSurrogate? surrogate) => surrogate != null @@ -44,7 +33,8 @@ namespace ElsaDashboard.Shared.Surrogates UIHint = surrogate.Type!, Hint = surrogate.Hint, Label = surrogate.Label, - Options = surrogate.Options is null or "" ? default : JToken.Parse(surrogate.Options) + Options = surrogate.Options is null or "" ? default : JToken.Parse(surrogate.Options), + Category = surrogate.Category } : default; diff --git a/src/designer/elsa-workflows-studio/src/components/designers/tree/elsa-designer-tree/elsa-designer-tree.tsx b/src/designer/elsa-workflows-studio/src/components/designers/tree/elsa-designer-tree/elsa-designer-tree.tsx index ad2d707f5..1f243d48c 100644 --- a/src/designer/elsa-workflows-studio/src/components/designers/tree/elsa-designer-tree/elsa-designer-tree.tsx +++ b/src/designer/elsa-workflows-studio/src/components/designers/tree/elsa-designer-tree/elsa-designer-tree.tsx @@ -71,10 +71,15 @@ export class ElsaWorkflowDesigner { const displayContexts: Map = {}; for (const model of activityModels) { + + const description = model.description; + const bodyText = description && description.length > 0 ? description : undefined; + const bodyDisplay = bodyText ?

{bodyText}

: undefined; + const displayContext: ActivityDesignDisplayContext = { activityModel: model, activityIcon: , - bodyDisplay: null, + bodyDisplay: bodyDisplay, outcomes: [...model.outcomes] }; diff --git a/src/designer/elsa-workflows-studio/src/components/editors/monaco/elsa-monaco/elsa-monaco.tsx b/src/designer/elsa-workflows-studio/src/components/editors/monaco/elsa-monaco/elsa-monaco.tsx index 05b894ee3..9bd2130d3 100644 --- a/src/designer/elsa-workflows-studio/src/components/editors/monaco/elsa-monaco/elsa-monaco.tsx +++ b/src/designer/elsa-workflows-studio/src/components/editors/monaco/elsa-monaco/elsa-monaco.tsx @@ -45,7 +45,6 @@ export class ElsaMonaco { filePath: libUri }]); - //debugger; const oldModel = monaco.editor.getModel(libUri); if (oldModel) @@ -90,6 +89,7 @@ export class ElsaMonaco { minimap: { enabled: false }, + automaticLayout: true, lineNumbers: "on", theme: "vs", roundedSelection: true, diff --git a/src/designer/elsa-workflows-studio/src/components/editors/workflow-editor/elsa-activity-editor-modal/elsa-activity-editor-modal.tsx b/src/designer/elsa-workflows-studio/src/components/editors/workflow-editor/elsa-activity-editor-modal/elsa-activity-editor-modal.tsx index 803de6149..dbff0be4b 100644 --- a/src/designer/elsa-workflows-studio/src/components/editors/workflow-editor/elsa-activity-editor-modal/elsa-activity-editor-modal.tsx +++ b/src/designer/elsa-workflows-studio/src/components/editors/workflow-editor/elsa-activity-editor-modal/elsa-activity-editor-modal.tsx @@ -18,12 +18,14 @@ export class ElsaActivityPickerModal { dialog: HTMLElsaModalDialogElement; form: HTMLFormElement; formContext: FormContext; + renderProps: any; componentDidLoad() { eventBus.on(EventTypes.ShowActivityEditor, async (activity: ActivityModel, animate: boolean) => { this.activityModel = {...activity, properties: activity.properties || []}; this.activityDescriptor = state.activityDescriptors.find(x => x.type == activity.type); this.formContext = new FormContext(this.activityModel, newValue => this.activityModel = newValue); + this.selectedTab = 'Properties'; await this.dialog.show(animate); }); } @@ -55,13 +57,45 @@ export class ElsaActivityPickerModal { this.selectedTab = tab; } + componentWillRender(){ + const activityDescriptor = this.activityDescriptor || {displayName: '', type: '', outcomes: [], category: '', traits: 0, browsable: false, properties: [], description: ''}; + const propertyCategories = activityDescriptor.properties.filter(x => x.category).map(x => x.category).distinct(); + const defaultProperties = activityDescriptor.properties.filter(x => !x.category || x.category.length == 0); + let tabs: Array = []; + + if(defaultProperties.length > 0) { + tabs.push('Properties'); + } + + tabs.push('Common'); + tabs.push('Behaviors'); + tabs = [...tabs, ...propertyCategories]; + let selectedTab = this.selectedTab; + + if(tabs.findIndex(x => x === selectedTab) < 0) + selectedTab = tabs[0]; + + const activityModel: ActivityModel = this.activityModel || {type: '', activityId: '', outcomes: [], properties: []}; + + this.renderProps = { + activityDescriptor, + propertyCategories, + defaultProperties, + tabs, + selectedTab, + activityModel + } + } + render() { - const tabs = ['Properties', 'Common', 'Behaviors']; - const selectedTab = this.selectedTab; + const renderProps = this.renderProps; + const activityDescriptor = renderProps.activityDescriptor; + const propertyCategories = renderProps.propertyCategories; + const tabs = renderProps.tabs; + const selectedTab = renderProps.selectedTab; + const activityModel: ActivityModel = renderProps.activityModel; const inactiveClass = 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300'; const selectedClass = 'border-blue-500 text-blue-600'; - const activityModel: ActivityModel = this.activityModel || {type: '', activityId: '', outcomes: [], properties: []}; - const activityDescriptor = this.activityDescriptor || {displayName: '', type: '', outcomes: [], category: '', traits: 0, browsable: false, properties: [], description: ''}; return ( @@ -91,7 +125,7 @@ export class ElsaActivityPickerModal {
- {this.renderSelectedTab(activityModel, activityDescriptor)} + {this.renderSelectedTab(activityModel, activityDescriptor, propertyCategories)}
@@ -119,11 +153,12 @@ export class ElsaActivityPickerModal { ); } - renderSelectedTab(activityModel: ActivityModel, activityDescriptor: ActivityDescriptor) { + renderSelectedTab(activityModel: ActivityModel, activityDescriptor: ActivityDescriptor, categories: Array) { return [ this.renderWorkflowContextTab(activityModel), this.renderCommonTab(activityModel), - this.renderPropertiesTab(activityModel, activityDescriptor) + this.renderPropertiesTab(activityModel, activityDescriptor), + this.renderCategoryTabs(activityModel, activityDescriptor, categories) ]; } @@ -157,7 +192,11 @@ export class ElsaActivityPickerModal { } renderPropertiesTab(activityModel: ActivityModel, activityDescriptor: ActivityDescriptor) { - const propertyDescriptors: Array = activityDescriptor.properties; + const propertyDescriptors: Array = this.renderProps.defaultProperties; + + if(propertyDescriptors.length == 0) + return undefined; + const key = `activity-settings:${activityModel.activityId}`; return ( @@ -167,11 +206,25 @@ export class ElsaActivityPickerModal { ); } + renderCategoryTabs(activityModel: ActivityModel, activityDescriptor: ActivityDescriptor, categories: Array) { + const propertyDescriptors: Array = activityDescriptor.properties; + + return ( + categories.map(category => { + const descriptors = propertyDescriptors.filter(x => x.category == category); + const key = `activity-settings:${activityModel.activityId}:${category}`; + return
+ {descriptors.map(property => this.renderPropertyEditor(activityModel, property))} +
+ }) + ); + } + renderPropertyEditor(activity: ActivityModel, property: ActivityPropertyDescriptor) { return propertyDisplayManager.display(activity, property); } getHiddenClass(tab: string) { - return this.selectedTab == tab ? '' : 'hidden'; + return this.renderProps.selectedTab == tab ? '' : 'hidden'; } } diff --git a/src/designer/elsa-workflows-studio/src/index.html b/src/designer/elsa-workflows-studio/src/index.html index 2b2fad383..c6c56897b 100644 --- a/src/designer/elsa-workflows-studio/src/index.html +++ b/src/designer/elsa-workflows-studio/src/index.html @@ -14,7 +14,7 @@ - + diff --git a/src/designer/elsa-workflows-studio/src/models/domain.ts b/src/designer/elsa-workflows-studio/src/models/domain.ts index 0718ca960..5e0d8558a 100644 --- a/src/designer/elsa-workflows-studio/src/models/domain.ts +++ b/src/designer/elsa-workflows-studio/src/models/domain.ts @@ -1,23 +1,23 @@ import {Map} from '../utils/utils'; export interface WorkflowDefinition { - id?: string, - definitionId?: string, - tenantId?: string, - name?: string, - displayName?: string, - description?: string, - version: number, - variables?: Variables, - customAttributes?: Variables, - contextOptions?: WorkflowContextOptions, - isSingleton?: boolean, - persistenceBehavior?: WorkflowPersistenceBehavior, - deleteCompletedInstances?: boolean, - isPublished?: boolean, - isLatest?: boolean, - activities: Array, - connections: Array + id?: string; + definitionId?: string; + tenantId?: string; + name?: string; + displayName?: string; + description?: string; + version: number; + variables?: Variables; + customAttributes?: Variables; + contextOptions?: WorkflowContextOptions; + isSingleton?: boolean; + persistenceBehavior?: WorkflowPersistenceBehavior; + deleteCompletedInstances?: boolean; + isPublished?: boolean; + isLatest?: boolean; + activities: Array; + connections: Array; } export interface ActivityDefinition { @@ -106,6 +106,7 @@ export interface ActivityPropertyDescriptor { label?: string; hint?: string; options?: any; + category?: string; } export enum ActivityTraits { diff --git a/src/designer/elsa-workflows-studio/src/plugins/if-plugin.tsx b/src/designer/elsa-workflows-studio/src/plugins/if-plugin.tsx index 946224895..d61036a17 100644 --- a/src/designer/elsa-workflows-studio/src/plugins/if-plugin.tsx +++ b/src/designer/elsa-workflows-studio/src/plugins/if-plugin.tsx @@ -17,6 +17,8 @@ export class IfPlugin implements ElsaPlugin { const props = activityModel.properties || []; const condition = props.find(x => x.name == 'Condition') || { expression: '' }; const expression = condition.expression || ''; - context.bodyDisplay =

{expression}

; + const description = activityModel.description; + const bodyText = description && description.length > 0 ? description : expression; + context.bodyDisplay =

{bodyText}

; } } diff --git a/src/designer/elsa-workflows-studio/src/plugins/run-javascript-plugin.tsx b/src/designer/elsa-workflows-studio/src/plugins/run-javascript-plugin.tsx index 7a3540257..8834f2d6b 100644 --- a/src/designer/elsa-workflows-studio/src/plugins/run-javascript-plugin.tsx +++ b/src/designer/elsa-workflows-studio/src/plugins/run-javascript-plugin.tsx @@ -22,7 +22,5 @@ export class RunJavascriptPlugin implements ElsaPlugin { if(context.outcomes.length == 0) context.outcomes = ['Done']; - - context.bodyDisplay = undefined; } }