From b946bc32a69094663df7d59da4f3eb262dce2046 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Fri, 11 Jan 2019 22:46:01 +0100 Subject: [PATCH] Implemented logging --- .../scripts/elsa/workflow-designer-config.ts | 12 ++-- .../Assets/scripts/elsa/workflow-designer.ts | 9 ++- .../Assets/styles/workflow-designer.scss | 46 ++++++++++-- .../Services/ActivityShapeFactory.cs | 4 ++ .../ViewComponents/WorkflowDesigner.cs | 9 ++- .../Views/Activity.Design.cshtml | 71 ++++++++++++++----- 6 files changed, 116 insertions(+), 35 deletions(-) diff --git a/src/web/modules/Elsa.Web.Components/Assets/scripts/elsa/workflow-designer-config.ts b/src/web/modules/Elsa.Web.Components/Assets/scripts/elsa/workflow-designer-config.ts index 0c280dd7f..96c591d3b 100644 --- a/src/web/modules/Elsa.Web.Components/Assets/scripts/elsa/workflow-designer-config.ts +++ b/src/web/modules/Elsa.Web.Components/Assets/scripts/elsa/workflow-designer-config.ts @@ -13,8 +13,8 @@ namespace Elsa { }); } - static getSourceEndpointOptions(activityId: any, endpointName: any, hasExecuted: boolean, hasFaulted: boolean): any { - const fill = hasFaulted ? '#d23c3c' : hasExecuted? '#6faa44' : '#7da7f2'; + static getSourceEndpointOptions(activityId: any, endpointName: any, hasExecuted: boolean, hasFaulted: boolean, isBlocking: boolean): any { + const fill = isBlocking ? '#7da7f2' : hasFaulted ? '#d23c3c' : hasExecuted? '#6faa44' : '#7da7f2'; const stroke = fill; return { endpoint: 'Dot', @@ -29,19 +29,19 @@ namespace Elsa { connector: ['Flowchart', {stub: [40, 60], gap: 0, cornerRadius: 5, alwaysRespectStubs: true}], connectorStyle: { strokeWidth: 2, - stroke: '#999999', + stroke: !isBlocking && (hasFaulted || hasExecuted) ? fill : '#999999', joinstyle: 'round', outlineStroke: 'white', - outlineWidth: 2 + outlineWidth: 1 }, hoverPaintStyle: { fill: stroke, stroke: fill }, - connectorHoverStyle: { + connectorHoverStyle: isBlocking || hasFaulted || hasExecuted ? null : { strokeWidth: 3, stroke: stroke, - outlineWidth: 5, + outlineWidth: 1, outlineStroke: 'white' }, connectorOverlays: [['Label', {location: [3, -1.5], cssClass: 'endpointSourceLabel'}]], diff --git a/src/web/modules/Elsa.Web.Components/Assets/scripts/elsa/workflow-designer.ts b/src/web/modules/Elsa.Web.Components/Assets/scripts/elsa/workflow-designer.ts index e72410945..191d263a3 100644 --- a/src/web/modules/Elsa.Web.Components/Assets/scripts/elsa/workflow-designer.ts +++ b/src/web/modules/Elsa.Web.Components/Assets/scripts/elsa/workflow-designer.ts @@ -13,7 +13,6 @@ namespace Elsa { private readonly container: JQuery; private readonly canvasContainer: JQuery; private readonly isDefinition: boolean; - private readonly executionLog: Array; private readonly plumber: any; private dragStart: { left: number; top: number }; private hasDragged: boolean; @@ -22,7 +21,6 @@ namespace Elsa { this.container = $(containerElement); this.canvasContainer = this.container.find('.workflow-canvas'); this.isDefinition = this.canvasContainer.data('workflow-is-definition'); - this.executionLog = this.canvasContainer.data('workflow-execution-log'); this.plumber = WorkflowDesignerConfig.createJsPlumbInstance(this.canvasContainer[0]); this.initializeNodes(); this.container.on('contextmenu', '.workflow-canvas', this.onCanvasContextMenu); @@ -31,6 +29,10 @@ namespace Elsa { this.canvasContainer.on('click', '.activity .context-menu .dropdown-item', this.onActivityContextMenuItemClick); this.canvasContainer.on('click', '.canvas-context-menu .dropdown-item', this.onCanvasContextMenuItemClick); $(document).on('click', 'body', this.hideContextMenu); + + this.canvasContainer.find('[data-toggle="popover"]').popover({ + container: '.workflow-canvas' + }) } public getWorkflow = (): IWorkflow => { @@ -247,7 +249,8 @@ namespace Elsa { for (let endpoint of endpoints) { const hasExecuted: boolean = $activityElement.data('activity-executed'); const hasFaulted: boolean = $activityElement.data('activity-faulted'); - const sourceEndpointOptions: any = WorkflowDesignerConfig.getSourceEndpointOptions(activityId, endpoint.name, hasExecuted, hasFaulted); + const isBlocking: boolean = $activityElement.data('activity-blocking'); + const sourceEndpointOptions: any = WorkflowDesignerConfig.getSourceEndpointOptions(activityId, endpoint.name, hasExecuted, hasFaulted, isBlocking); this.plumber.addEndpoint(activityElement, { connectorOverlays: [['Label', { label: endpoint.name, diff --git a/src/web/modules/Elsa.Web.Components/Assets/styles/workflow-designer.scss b/src/web/modules/Elsa.Web.Components/Assets/styles/workflow-designer.scss index f89be4f26..ddd9d999e 100644 --- a/src/web/modules/Elsa.Web.Components/Assets/styles/workflow-designer.scss +++ b/src/web/modules/Elsa.Web.Components/Assets/styles/workflow-designer.scss @@ -6,7 +6,7 @@ .activity { display: none; - border: 1px solid #5a8fee; + border: 1px solid #cccccc; box-shadow: 2px 2px 19px #aaa; -o-box-shadow: 2px 2px 9px #aaa; -webkit-box-shadow: 2px 2px 9px #aaa; @@ -30,18 +30,49 @@ -o-transition: -o-box-shadow 0.15s ease-in; transition: box-shadow 0.15s ease-in; - &.activity-executed { - border: 1px solid #6faa44; + .activity-status { + position: absolute; + right: 4px; + bottom: 4px; + cursor: pointer; + i { + + font-size: 2em; + } } - &.activity-start, &.activity-blocking { - background-color: #f1f1f1; - color: #3d3d3d; + &.activity-definition { border: 1px solid #5a8fee; } + + &.activity-executed { + border: 1px solid #6faa44; + + .activity-status { + i { + color: #6faa44; + } + } + } + + &.activity-blocking { + border: 1px solid #5a8fee; + + .activity-status { + i { + color: #5a8fee; + } + } + } &.activity-faulted { border: 1px solid #d23c3c; + + .activity-status { + i { + color: #d23c3c; + } + } } header { @@ -53,6 +84,9 @@ } } } + + + } .jtk-connector { diff --git a/src/web/modules/Elsa.Web.Components/Services/ActivityShapeFactory.cs b/src/web/modules/Elsa.Web.Components/Services/ActivityShapeFactory.cs index 1b84ee455..e434fbcfc 100644 --- a/src/web/modules/Elsa.Web.Components/Services/ActivityShapeFactory.cs +++ b/src/web/modules/Elsa.Web.Components/Services/ActivityShapeFactory.cs @@ -30,6 +30,10 @@ namespace Elsa.Web.Components.Services shape.ActivityDescriptor = descriptor; shape.Activity = activity; shape.Designer = designerMetadata; + shape.IsBlocking = false; + shape.HasExecuted = false; + shape.HasFaulted = false; + shape.WorkflowIsDefinition = true; return shape; } diff --git a/src/web/modules/Elsa.Web.Components/ViewComponents/WorkflowDesigner.cs b/src/web/modules/Elsa.Web.Components/ViewComponents/WorkflowDesigner.cs index 945958eda..3f9fe022d 100644 --- a/src/web/modules/Elsa.Web.Components/ViewComponents/WorkflowDesigner.cs +++ b/src/web/modules/Elsa.Web.Components/ViewComponents/WorkflowDesigner.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using System.Xml.Linq; +using Elsa.Extensions; using Elsa.Models; using Elsa.Persistence; using Elsa.Web.Components.ViewModels; @@ -35,9 +37,14 @@ namespace Elsa.Web.Components.ViewComponents private async Task BuildActivityShapeAsync(Workflow workflow, IActivity activity, CancellationToken cancellationToken) { var shape = (dynamic)await activityShapeFactory.BuildDesignShapeAsync(activity, cancellationToken); - var logEntries = workflow.ExecutionLog.Where(x => x.ActivityId == activity.Id).OrderByDescending(x => x.Timestamp).ToList(); + var logEntries = workflow.ExecutionLog.Where(x => x.ActivityId == activity.Id).OrderBy(x => x.Timestamp).ToList(); shape.LogEntries = logEntries; + shape.BlockingActivities = workflow.BlockingActivities; + shape.HasExecuted = logEntries.Any(); + shape.HasFaulted = logEntries.Any(x => x.Faulted); + shape.IsBlocking = workflow.BlockingActivities.Any(x => x.Id == activity.Id); + shape.WorkflowIsDefinition = workflow.IsDefinition(); return shape; } diff --git a/src/web/modules/Elsa.Web.Components/Views/Activity.Design.cshtml b/src/web/modules/Elsa.Web.Components/Views/Activity.Design.cshtml index bb500ba38..d4b8599e5 100644 --- a/src/web/modules/Elsa.Web.Components/Views/Activity.Design.cshtml +++ b/src/web/modules/Elsa.Web.Components/Views/Activity.Design.cshtml @@ -1,6 +1,7 @@ @using Elsa @using Elsa.Models @using Elsa.Web.Components.Models +@using Microsoft.AspNetCore.Html @using Microsoft.Extensions.Localization @using Newtonsoft.Json @using Newtonsoft.Json.Serialization @@ -14,28 +15,41 @@ var activityJson = JsonConvert.SerializeObject(activity, serializerSettings); var activityEndpoints = JsonConvert.SerializeObject(endpoints, serializerSettings); var designer = (ActivityDesignerMetadata) Model.Designer; - var logEntries = Model.LogEntries != null ? (ICollection)Model.LogEntries : new List(); - var logEntriesJson = JsonConvert.SerializeObject(logEntries, serializerSettings); - var hasFaulted = logEntries.Any(x => x.Faulted); - var hasExecuted = logEntries.Any(); + var hasFaulted = (bool)Model.HasFaulted; + var hasExecuted = (bool)Model.HasExecuted; + var isBlocking = (bool)Model.IsBlocking; + var isDefinition = (bool)Model.WorkflowIsDefinition; + var isInstance = !isDefinition; var cssClasses = new List { "activity" }; var left = designer.X; var top = designer.Y; - if (hasExecuted) + if (isInstance) { - cssClasses.Add("activity-executed"); + if (hasExecuted) + { + cssClasses.Add("activity-executed"); + } + + if (hasFaulted) + { + cssClasses.Add("activity-faulted"); + } + + if (isBlocking) + { + cssClasses.Add("activity-blocking"); + } + } + else + { + cssClasses.Add("activity-definition"); } - if (hasFaulted) - { - cssClasses.Add("activity-faulted"); - } - - var cssClass = string.Join(" ", cssClasses); + var activityClass = string.Join(" ", cssClasses); }
+ data-activity-blocking="@isBlocking.ToString().ToLower()" + data-activity-faulted="@hasFaulted.ToString().ToLower()"> @await DisplayAsync(Model.Content) - + + @if (isDefinition) + { + + } + + @if (isInstance) + { + var title = hasFaulted ? T["Faulted"] : isBlocking ? T["Awaiting Input"] : T["Executed"]; + var statusClass = hasFaulted ? "fas fa-exclamation-circle" : isBlocking ? "fas fa-hourglass-half" : "fas fa-check-circle"; + var logEntries = Model.LogEntries != null ? (ICollection) Model.LogEntries : new List(); + var logEntriesText = new HtmlString(string.Join("
", logEntries.Select(x => $"{x.Timestamp}: {x.Message}"))); + +
+ @if (hasFaulted || isBlocking || hasExecuted) + { + + } +
+ }
\ No newline at end of file