From cd3fb844edaa47ce4e8ce355adb2fbfd1736c0c6 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Fri, 11 Jan 2019 16:40:45 +0100 Subject: [PATCH] Incremental work on track and trace --- .../Drivers/SetVariableDriver.cs | 4 +- .../Extensions/WorkflowExtensions.cs | 8 +++ .../Models/ActivityExecutionContext.cs | 5 -- .../Elsa.Abstractions/Models/ActivityFault.cs | 12 ---- src/core/Elsa.Abstractions/Models/LogEntry.cs | 22 ++++--- src/core/Elsa.Core/ActivityInvoker.cs | 24 +++++--- .../Results/TriggerEndpointsResult.cs | 2 - .../Tokenizers/WorkflowTokenizer.cs | 13 ++-- .../Services/IActivityShapeFactory.cs | 1 + .../Assets/scripts/elsa/log-entry.ts | 10 +++ .../scripts/elsa/workflow-designer-config.ts | 8 +-- .../Assets/scripts/elsa/workflow-designer.ts | 61 ++++++++++--------- .../Assets/styles/workflow-designer.scss | 8 +++ .../Elsa.Web.Components.csproj | 2 +- .../Services/ActivityShapeFactory.cs | 1 + .../ViewComponents/WorkflowDesigner.cs | 22 +++---- .../Views/Activity.Design.cshtml | 40 +++++++++--- .../WorkflowDesigner/Default.cshtml | 8 ++- .../Controllers/WorkflowsController.cs | 2 +- .../ViewComponents/WorkflowEditor.cs | 13 ++++ .../Components/WorkflowEditor/Default.cshtml} | 36 +++++++---- .../Views/Shared/WorkflowEditor.cshtml | 14 ----- .../Views/Workflows/Details.cshtml | 2 +- .../Views/Workflows/Edit.cshtml | 2 +- .../Views/Workflows/New.cshtml | 4 +- .../Views/_ViewImports.cshtml | 1 + .../Elsa.Web.BootstrapTheme.csproj | 6 +- .../Elsa.Web.Management/Workflows/Edit.cshtml | 2 - .../Elsa.Web.Management/Workflows/New.cshtml | 2 - 29 files changed, 196 insertions(+), 139 deletions(-) delete mode 100644 src/core/Elsa.Abstractions/Models/ActivityFault.cs create mode 100644 src/web/modules/Elsa.Web.Components/Assets/scripts/elsa/log-entry.ts create mode 100644 src/web/modules/Elsa.Web.Management/ViewComponents/WorkflowEditor.cs rename src/web/{themes/Elsa.Web.BootstrapTheme/Views/Elsa.Web.Management/Shared/WorkflowEditor.cshtml => modules/Elsa.Web.Management/Views/Shared/Components/WorkflowEditor/Default.cshtml} (53%) delete mode 100644 src/web/modules/Elsa.Web.Management/Views/Shared/WorkflowEditor.cshtml delete mode 100644 src/web/themes/Elsa.Web.BootstrapTheme/Views/Elsa.Web.Management/Workflows/Edit.cshtml delete mode 100644 src/web/themes/Elsa.Web.BootstrapTheme/Views/Elsa.Web.Management/Workflows/New.cshtml diff --git a/src/activities/Elsa.Activities.Primitives/Drivers/SetVariableDriver.cs b/src/activities/Elsa.Activities.Primitives/Drivers/SetVariableDriver.cs index 011389763..00dcab149 100644 --- a/src/activities/Elsa.Activities.Primitives/Drivers/SetVariableDriver.cs +++ b/src/activities/Elsa.Activities.Primitives/Drivers/SetVariableDriver.cs @@ -19,8 +19,8 @@ namespace Elsa.Activities.Primitives.Drivers protected override async Task OnExecuteAsync(SetVariable activity, WorkflowExecutionContext workflowContext, CancellationToken cancellationToken) { - var value = await expressionEvaluator.EvaluateAsync(activity.ValueExpression, workflowContext, cancellationToken); - workflowContext.CurrentScope.SetVariable(activity.VariableName, value); + //var value = await expressionEvaluator.EvaluateAsync(activity.ValueExpression, workflowContext, cancellationToken); + //workflowContext.CurrentScope.SetVariable(activity.VariableName, value); return TriggerEndpoint("Done"); } } diff --git a/src/core/Elsa.Abstractions/Extensions/WorkflowExtensions.cs b/src/core/Elsa.Abstractions/Extensions/WorkflowExtensions.cs index d05318bd0..fbadc223d 100644 --- a/src/core/Elsa.Abstractions/Extensions/WorkflowExtensions.cs +++ b/src/core/Elsa.Abstractions/Extensions/WorkflowExtensions.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using Elsa.Models; +using NodaTime; namespace Elsa.Extensions { @@ -28,5 +29,12 @@ namespace Elsa.Extensions return query; } + + public static LogEntry AddLogEntry(this Workflow workflow, string activityId, Instant instant, string message, bool faulted = false) + { + var entry = new LogEntry(activityId, instant, message, faulted); + workflow.ExecutionLog.Add(entry); + return entry; + } } } \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Models/ActivityExecutionContext.cs b/src/core/Elsa.Abstractions/Models/ActivityExecutionContext.cs index c82d6f786..98aac6fd7 100644 --- a/src/core/Elsa.Abstractions/Models/ActivityExecutionContext.cs +++ b/src/core/Elsa.Abstractions/Models/ActivityExecutionContext.cs @@ -5,13 +5,8 @@ public ActivityExecutionContext(IActivity activity) { Activity = activity; - LogEntry = new LogEntry - { - ActivityId = activity.Id - }; } public IActivity Activity { get; } - public LogEntry LogEntry { get; set; } } } diff --git a/src/core/Elsa.Abstractions/Models/ActivityFault.cs b/src/core/Elsa.Abstractions/Models/ActivityFault.cs deleted file mode 100644 index ebce4ecaa..000000000 --- a/src/core/Elsa.Abstractions/Models/ActivityFault.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Elsa.Models -{ - public class ActivityFault - { - public ActivityFault(string message) - { - Message = message; - } - - public string Message { get; set; } - } -} \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Models/LogEntry.cs b/src/core/Elsa.Abstractions/Models/LogEntry.cs index 924e4dcfc..26f6af9c1 100644 --- a/src/core/Elsa.Abstractions/Models/LogEntry.cs +++ b/src/core/Elsa.Abstractions/Models/LogEntry.cs @@ -1,16 +1,24 @@ -using System.Collections.Generic; using NodaTime; namespace Elsa.Models { public class LogEntry { + public LogEntry() + { + } + + public LogEntry(string activityId, Instant timestamp, string message, bool faulted = false) + { + ActivityId = activityId; + Timestamp = timestamp; + Message = message; + Faulted = faulted; + } + public string ActivityId { get; set; } - public Instant? ExecutedAt { get; set; } - public Instant? HaltedAt { get; set; } - public Instant? ResumedAt { get; set; } - public Instant? CompletedAt { get; set; } - public IList TriggeredEndpoints { get; set; } - public ActivityFault Fault { get; set; } + public Instant Timestamp { get; set; } + public bool Faulted { get; set; } + public string Message { get; set; } } } \ No newline at end of file diff --git a/src/core/Elsa.Core/ActivityInvoker.cs b/src/core/Elsa.Core/ActivityInvoker.cs index ecb0cb64d..d02445b39 100644 --- a/src/core/Elsa.Core/ActivityInvoker.cs +++ b/src/core/Elsa.Core/ActivityInvoker.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Elsa.Exceptions; +using Elsa.Extensions; using Elsa.Models; using Elsa.Results; using Microsoft.Extensions.Logging; @@ -28,9 +29,10 @@ namespace Elsa { return await InvokeAsync(workflowContext, activity, (context, driver) => { - workflowContext.Workflow.ExecutionLog.Add(context.LogEntry); - context.LogEntry.ExecutedAt = clock.GetCurrentInstant(); - return driver.ExecuteAsync(context, workflowContext, cancellationToken); + workflowContext.Workflow.AddLogEntry(activity.Id, clock.GetCurrentInstant(), "Executing"); + var result = driver.ExecuteAsync(context, workflowContext, cancellationToken); + workflowContext.Workflow.AddLogEntry(activity.Id, clock.GetCurrentInstant(), "Executed"); + return result; }); } @@ -38,8 +40,10 @@ namespace Elsa { return await InvokeAsync(workflowContext, activity, (context, driver) => { - context.LogEntry.ResumedAt = clock.GetCurrentInstant(); - return driver.ResumeAsync(context, workflowContext, cancellationToken); + workflowContext.Workflow.AddLogEntry(activity.Id, clock.GetCurrentInstant(), "Resuming"); + var result = driver.ResumeAsync(context, workflowContext, cancellationToken); + workflowContext.Workflow.AddLogEntry(activity.Id, clock.GetCurrentInstant(), "Resumed"); + return result; }); } @@ -47,8 +51,10 @@ namespace Elsa { return await InvokeAsync(workflowContext, activity, (context, driver) => { - context.LogEntry.HaltedAt = clock.GetCurrentInstant(); - return driver.HaltedAsync(context, workflowContext, cancellationToken); + workflowContext.Workflow.AddLogEntry(activity.Id, clock.GetCurrentInstant(), "Halting"); + var result = driver.HaltedAsync(context, workflowContext, cancellationToken); + workflowContext.Workflow.AddLogEntry(activity.Id, clock.GetCurrentInstant(), "Halted"); + return result; }); } @@ -70,8 +76,8 @@ namespace Elsa catch (Exception e) { logger.LogError(e, "Error while invoking activity {ActivityId} of workflow {WorkflowId}", activity.Id, workflowContext.Workflow.Metadata.Id); - activityContext.LogEntry.Fault = new ActivityFault(e.Message); - return new FaultWorkflowResult(activityContext.LogEntry.Fault.Message, clock.GetCurrentInstant()); + workflowContext.Workflow.AddLogEntry(activity.Id, clock.GetCurrentInstant(), e.Message, true); + return new FaultWorkflowResult(e, clock.GetCurrentInstant()); } } } diff --git a/src/core/Elsa.Core/Results/TriggerEndpointsResult.cs b/src/core/Elsa.Core/Results/TriggerEndpointsResult.cs index a9142bf5f..05d0b29a0 100644 --- a/src/core/Elsa.Core/Results/TriggerEndpointsResult.cs +++ b/src/core/Elsa.Core/Results/TriggerEndpointsResult.cs @@ -20,8 +20,6 @@ namespace Elsa.Results { var currentActivity = workflowContext.CurrentActivity; - workflowContext.CurrentLogEntry.TriggeredEndpoints = EndpointNames.ToList(); - foreach (var endpointName in EndpointNames) { workflowContext.ScheduleNextActivities(workflowContext, new SourceEndpoint(currentActivity, endpointName)); diff --git a/src/core/Elsa.Core/Serialization/Tokenizers/WorkflowTokenizer.cs b/src/core/Elsa.Core/Serialization/Tokenizers/WorkflowTokenizer.cs index 082a9cdf1..5a3e8d8f3 100644 --- a/src/core/Elsa.Core/Serialization/Tokenizers/WorkflowTokenizer.cs +++ b/src/core/Elsa.Core/Serialization/Tokenizers/WorkflowTokenizer.cs @@ -21,6 +21,7 @@ namespace Elsa.Serialization.Tokenizers private readonly ITokenizerInvoker tokenizerInvoker; private readonly IActivityLibrary activityLibrary; private readonly IClock clock; + private readonly JsonSerializerSettings _serializerSettings; private readonly JsonSerializer jsonSerializer; public WorkflowTokenizer(ITokenizerInvoker tokenizerInvoker, IActivityLibrary activityLibrary, IClock clock) @@ -29,14 +30,14 @@ namespace Elsa.Serialization.Tokenizers this.activityLibrary = activityLibrary; this.clock = clock; - var settings = new JsonSerializerSettings + _serializerSettings = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() } .ConfigureForNodaTime(DateTimeZoneProviders.Tzdb); - settings.Converters.Add(new StringEnumConverter()); - jsonSerializer = JsonSerializer.Create(settings); + _serializerSettings.Converters.Add(new StringEnumConverter()); + jsonSerializer = JsonSerializer.Create(_serializerSettings); } public async Task TokenizeWorkflowAsync(Workflow value, CancellationToken cancellationToken) @@ -266,11 +267,7 @@ namespace Elsa.Serialization.Tokenizers { var models = (JArray) token["executionLog"] ?? new JArray(); - foreach (var model in models) - { - var entry = JsonConvert.DeserializeObject(model.ToString(), new JsonSerializerSettings().ConfigureForNodaTime(DateTimeZoneProviders.Tzdb)); - yield return entry; - } + return JsonConvert.DeserializeObject>(models.ToString(), _serializerSettings); } private IEnumerable DeserializeConnections(JToken token, IDictionary activityDictionary) diff --git a/src/web/modules/Elsa.Web.Abstractions/Services/IActivityShapeFactory.cs b/src/web/modules/Elsa.Web.Abstractions/Services/IActivityShapeFactory.cs index 7f3fd8451..1fd9f9700 100644 --- a/src/web/modules/Elsa.Web.Abstractions/Services/IActivityShapeFactory.cs +++ b/src/web/modules/Elsa.Web.Abstractions/Services/IActivityShapeFactory.cs @@ -1,5 +1,6 @@ using System.Threading; using System.Threading.Tasks; +using Elsa.Models; using OrchardCore.DisplayManagement; namespace Elsa.Web.Services diff --git a/src/web/modules/Elsa.Web.Components/Assets/scripts/elsa/log-entry.ts b/src/web/modules/Elsa.Web.Components/Assets/scripts/elsa/log-entry.ts new file mode 100644 index 000000000..3ac8efa7b --- /dev/null +++ b/src/web/modules/Elsa.Web.Components/Assets/scripts/elsa/log-entry.ts @@ -0,0 +1,10 @@ +/// +/// +namespace Elsa { + export interface LogEntry { + activityId: string; + timestamp: Date; + faulted: boolean; + message: string; + } +} \ No newline at end of file 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 88fae5752..0c280dd7f 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 @@ -6,8 +6,6 @@ namespace Elsa { return jsPlumb.getInstance({ Anchor: "Continuous", DragOptions: {cursor: 'pointer', zIndex: 2000}, - //EndpointStyle: [{fillStyle: '#225588'}], - //Endpoints: [["Dot", {radius: 7}], ["Blank"]], ConnectionOverlays: [ ["Arrow", {width: 12, length: 12, location: -5}], ], @@ -15,9 +13,9 @@ namespace Elsa { }); } - static getSourceEndpointOptions(activityId: any, endpointName: any): any { - const fill = '#7da7f2'; - const stroke = '#7da7f2'; + static getSourceEndpointOptions(activityId: any, endpointName: any, hasExecuted: boolean, hasFaulted: boolean): any { + const fill = hasFaulted ? '#d23c3c' : hasExecuted? '#6faa44' : '#7da7f2'; + const stroke = fill; return { endpoint: 'Dot', anchor: 'Continuous', 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 dce1b8c2a..e72410945 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 @@ -6,11 +6,14 @@ /// /// /// +/// namespace Elsa { export class WorkflowDesigner { 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; @@ -18,6 +21,8 @@ namespace Elsa { constructor(containerElement: HTMLElement) { 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); @@ -137,7 +142,7 @@ namespace Elsa { }; private showContextMenu = (menu: JQuery, e: JQuery.Event) => { - if (e.isDefaultPrevented()) + if (e.isDefaultPrevented() || !this.isDefinition) return; e.preventDefault(); @@ -169,23 +174,25 @@ namespace Elsa { const activityId: string = activityElement.data('activity-id'); const activityEndpoints: any[] = activityElement.data('activity-endpoints'); - this.plumber.draggable(activityElement, { - containment: true, - start: (args: any) => { - this.dragStart = {left: args.e.screenX, top: args.e.screenY}; - }, - stop: (args: any) => { - this.hasDragged = this.dragStart.left !== args.e.screenX || this.dragStart.top !== args.e.screenY; + if(this.isDefinition) { + this.plumber.draggable(activityElement, { + containment: true, + start: (args: any) => { + this.dragStart = {left: args.e.screenX, top: args.e.screenY}; + }, + stop: (args: any) => { + this.hasDragged = this.dragStart.left !== args.e.screenX || this.dragStart.top !== args.e.screenY; - if (!this.hasDragged) { - return; + if (!this.hasDragged) { + return; + } + + this.updateActivityModel(activityElement, activity => { + this.setActivityPosition(activity, args.pos[0], args.pos[1]); + }); } - - this.updateActivityModel(activityElement, activity => { - this.setActivityPosition(activity, args.pos[0], args.pos[1]); - }); - } - }); + }); + } this.plumber.makeTarget(activityElement, { dropOptions: {hoverClass: 'hover'}, @@ -209,23 +216,17 @@ namespace Elsa { private restoreConnections = (activityElement: HTMLElement) => { const activity: IActivity = $(activityElement).data('activity-model'); const connections: IConnection[] = $(this.canvasContainer).data('workflow-connections'); - const plumber = this.plumber; const activityConnections = connections.filter(x => x.target.activityId == activity.id || x.source.activityId == activity.id); - for (let connection of activityConnections) { - const sourceEndpointUuid: string = `${connection.source.activityId}-${connection.source.name}`; - const sourceEndpoint: Endpoint = plumber.getEndpoint(sourceEndpointUuid); - const destinationElementId: string = `activity-${connection.target.activityId}`; - - plumber.connect({ - source: sourceEndpoint, - target: destinationElementId - }); - } + this.createConnectionsInternal(activityConnections); }; private createConnections = () => { const connections: IConnection[] = $(this.canvasContainer).data('workflow-connections'); + this.createConnectionsInternal(connections); + }; + + private createConnectionsInternal = (connections: IConnection[]) => { const plumber = this.plumber; for (let connection of connections) { @@ -241,8 +242,12 @@ namespace Elsa { }; private addSourceEndpoints = (activityElement: HTMLElement, activityId: string, endpoints: any[]) => { + const $activityElement = $(activityElement); + for (let endpoint of endpoints) { - const sourceEndpointOptions: any = WorkflowDesignerConfig.getSourceEndpointOptions(activityId, endpoint.name); + const hasExecuted: boolean = $activityElement.data('activity-executed'); + const hasFaulted: boolean = $activityElement.data('activity-faulted'); + const sourceEndpointOptions: any = WorkflowDesignerConfig.getSourceEndpointOptions(activityId, endpoint.name, hasExecuted, hasFaulted); 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 65f1c0fae..f89be4f26 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 @@ -30,12 +30,20 @@ -o-transition: -o-box-shadow 0.15s ease-in; transition: box-shadow 0.15s ease-in; + &.activity-executed { + border: 1px solid #6faa44; + } + &.activity-start, &.activity-blocking { background-color: #f1f1f1; color: #3d3d3d; border: 1px solid #5a8fee; } + &.activity-faulted { + border: 1px solid #d23c3c; + } + header { h4 { font-size: 1.4em; diff --git a/src/web/modules/Elsa.Web.Components/Elsa.Web.Components.csproj b/src/web/modules/Elsa.Web.Components/Elsa.Web.Components.csproj index bb2a788d7..11bb95820 100644 --- a/src/web/modules/Elsa.Web.Components/Elsa.Web.Components.csproj +++ b/src/web/modules/Elsa.Web.Components/Elsa.Web.Components.csproj @@ -34,4 +34,4 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/web/modules/Elsa.Web.Components/Services/ActivityShapeFactory.cs b/src/web/modules/Elsa.Web.Components/Services/ActivityShapeFactory.cs index 579e594e2..1b84ee455 100644 --- a/src/web/modules/Elsa.Web.Components/Services/ActivityShapeFactory.cs +++ b/src/web/modules/Elsa.Web.Components/Services/ActivityShapeFactory.cs @@ -2,6 +2,7 @@ using System; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Elsa.Models; using Elsa.Web.Components.Models; using Elsa.Web.Services; using OrchardCore.DisplayManagement; diff --git a/src/web/modules/Elsa.Web.Components/ViewComponents/WorkflowDesigner.cs b/src/web/modules/Elsa.Web.Components/ViewComponents/WorkflowDesigner.cs index d8f81d46b..945958eda 100644 --- a/src/web/modules/Elsa.Web.Components/ViewComponents/WorkflowDesigner.cs +++ b/src/web/modules/Elsa.Web.Components/ViewComponents/WorkflowDesigner.cs @@ -12,34 +12,34 @@ namespace Elsa.Web.Components.ViewComponents { public class WorkflowDesigner : ViewComponent { - private readonly IWorkflowStore workflowStore; private readonly IActivityShapeFactory activityShapeFactory; - public WorkflowDesigner( - IWorkflowStore workflowStore, - IActivityShapeFactory activityShapeFactory) + public WorkflowDesigner( IActivityShapeFactory activityShapeFactory) { - this.workflowStore = workflowStore; this.activityShapeFactory = activityShapeFactory; } public async Task InvokeAsync(Workflow workflow, CancellationToken cancellationToken) { - var activities = workflow.Activities; - var activityShapes = await BuildActivityShapesAsync(activities, cancellationToken); + var activityShapes = await BuildActivityShapesAsync(workflow, cancellationToken); var viewModel = new WorkflowDesignerViewModel(workflow, activityShapes); return View(viewModel); } - private async Task> BuildActivityShapesAsync(IEnumerable activities, CancellationToken cancellationToken) + private async Task> BuildActivityShapesAsync(Workflow workflow, CancellationToken cancellationToken) { - return await Task.WhenAll(activities.Select((x, i) => BuildActivityShapeAsync(x, cancellationToken))); + return await Task.WhenAll(workflow.Activities.Select((x, i) => BuildActivityShapeAsync(workflow, x, cancellationToken))); } - private async Task BuildActivityShapeAsync(IActivity activity, CancellationToken cancellationToken) + private async Task BuildActivityShapeAsync(Workflow workflow, IActivity activity, CancellationToken cancellationToken) { - return await activityShapeFactory.BuildDesignShapeAsync(activity, cancellationToken); + var shape = (dynamic)await activityShapeFactory.BuildDesignShapeAsync(activity, cancellationToken); + var logEntries = workflow.ExecutionLog.Where(x => x.ActivityId == activity.Id).OrderByDescending(x => x.Timestamp).ToList(); + + shape.LogEntries = logEntries; + + return shape; } } } \ No newline at end of file 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 3fdbefd33..bb500ba38 100644 --- a/src/web/modules/Elsa.Web.Components/Views/Activity.Design.cshtml +++ b/src/web/modules/Elsa.Web.Components/Views/Activity.Design.cshtml @@ -6,26 +6,46 @@ @using Newtonsoft.Json.Serialization @model dynamic @{ - var activity = (IActivity)Model.Activity; + var activity = (IActivity) Model.Activity; var descriptor = (ActivityDescriptor) Model.ActivityDescriptor; var id = activity.Id; - var endpoints = (IList)Model.Endpoints; + var endpoints = (IList) Model.Endpoints; var serializerSettings = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }; var activityJson = JsonConvert.SerializeObject(activity, serializerSettings); var activityEndpoints = JsonConvert.SerializeObject(endpoints, serializerSettings); - var designer = (ActivityDesignerMetadata)Model.Designer; - var x = designer.X; - var y = designer.Y; + 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 cssClasses = new List { "activity" }; + var left = designer.X; + var top = designer.Y; + + if (hasExecuted) + { + cssClasses.Add("activity-executed"); + } + + if (hasFaulted) + { + cssClasses.Add("activity-faulted"); + } + + var cssClass = string.Join(" ", cssClasses); } -
+ data-activity-endpoints="@activityEndpoints" + data-activity-executed="@hasExecuted.ToString().ToLower()" + data-activity-faulted="@hasFaulted.ToString().ToLower()" + data-activity-log="@logEntriesJson"> @await DisplayAsync(Model.Content)