Skip to content

Commit

Permalink
fix(Dashboard): nested tasks details
Browse files Browse the repository at this point in the history
& improved rendering perf

Signed-off-by: Jean-Baptiste Bianchi <[email protected]>
  • Loading branch information
JBBianchi committed Aug 21, 2024
1 parent fa584a8 commit 3296e52
Show file tree
Hide file tree
Showing 10 changed files with 252 additions and 119 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,58 +18,65 @@
@using Synapse.Dashboard.Components.DocumentDetailsStateManagement
@inherits StatefulComponent<DocumentDetails, DocumentDetailsStore, DocumentDetailsState>

<div class="d-flex justify-content-between cursor-pointer @ClassNames" @onclick="async (_) => await ToggleAsync()">
<div class="d-flex justify-content-between cursor-pointer @ClassNames" @onclick="async (_) => await Store.ToggleAsync()">
@if (LabelTemplate != null) {
@LabelTemplate
}
else {
<span class="label">@Label</span>
}
@if (collapse != null)
@if (Store.Collapse != null)
{
<Icon Name="@(isOpen ? IconName.CaretUp : IconName.CaretDown)" />
<Icon Name="@(isExpanded ? IconName.CaretUp : IconName.CaretDown)" />
}
</div>
<Collapse @ref="collapse" OnShowing="Store.LoadReferencedDocumentAsync">
@if (reference == null && Document == null)
{
<p>No document</p>
}
else if (!loaded)
{
<Spinner Class="me-3" Color="SpinnerColor.Primary" Size="SpinnerSize.Small" />
}
else
{
<div class="d-flex justify-content-end mb-2">
<PreferredLanguageSelector PreferedLanguageChange="Store.ToggleTextBasedEditorLanguageAsync" />
</div>
<StandaloneCodeEditor @ref="Store.TextEditor"
ConstructionOptions="Store.StandaloneEditorConstructionOptions"
OnDidInit="Store.OnTextBasedEditorInitAsync"
CssClass="h-300-px" />
}
@if (problemDetails != null)
{
<div class="problems">
<Callout Type="CalloutType.Danger" Heading="@problemDetails.Title">
@problemDetails.Detail
</Callout>
@if (problemDetails.Errors != null && problemDetails.Errors.Any())
{
foreach (KeyValuePair<string, string[]> errorContainer in problemDetails.Errors)
<Collapse @ref="Store.Collapse" OnShowing="Store.LoadReferencedDocumentAsync">
@if (isExpanded) // prevents early rendering when collapsed
{
@if (reference == null && Document == null)
{
<p>No document</p>
}
else if (!loaded)
{
<Spinner Class="me-3" Color="SpinnerColor.Primary" Size="SpinnerSize.Small" />
}
else
{
<div class="d-flex justify-content-end mb-2">
<PreferredLanguageSelector PreferedLanguageChange="Store.ToggleTextBasedEditorLanguageAsync" />
</div>
<StandaloneCodeEditor @ref="Store.TextEditor"
ConstructionOptions="Store.StandaloneEditorConstructionOptions"
OnDidInit="Store.OnTextBasedEditorInitAsync"
CssClass="h-300-px" />
}
@if (problemDetails != null)
{
<div class="problems">
<Callout Type="CalloutType.Danger" Heading="@problemDetails.Title">
@problemDetails.Detail
</Callout>
@if (problemDetails.Errors != null && problemDetails.Errors.Any())
{
<Callout Type="CalloutType.Danger" Heading="@errorContainer.Key">
<ul>
@foreach (string error in errorContainer.Value)
{
<li>@error</li>
}
</ul>
</Callout>
foreach (KeyValuePair<string, string[]> errorContainer in problemDetails.Errors)
{
<Callout Type="CalloutType.Danger" Heading="@errorContainer.Key">
<ul>
@foreach (string error in errorContainer.Value)
{
<li>@error</li>
}
</ul>
</Callout>
}
}
}
</div>
</div>
}
}
else // mocks some content to enable opening animation
{
<div class="h-300-px"></div>
}
</Collapse>
@code {
Expand All @@ -93,64 +100,48 @@
/// </summary>
[Parameter] public object? Document { get; set; }

/// <summary>
/// The <see cref="Collapse" /> reference
/// </summary>
private Collapse collapse = default!;
/// <summary>
/// The state of the <see cref="Collapse" />
/// </summary>
private bool isOpen = false;
bool isExpanded = false;
/// <summary>
/// The internal reference
/// </summary>
private string? reference;
string? reference;
/// <summary>
/// The internal boolean indicating if the resource already loaded
/// </summary>
private bool loaded = false;
bool loaded = false;
/// <summary>
/// The <see cref="ProblemDetails"/> that occurred when trying to save the resource, if any
/// </summary>
private ProblemDetails? problemDetails = null;
ProblemDetails? problemDetails = null;

private object? _documentShadow;
object? __documentShadow;

/// <inheritdoc/>
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync().ConfigureAwait(false);
Store.Reference.Subscribe(value => OnStateChanged(_ => reference = value), token: CancellationTokenSource.Token);
Store.Loaded.Subscribe(value => OnStateChanged(_ => loaded = value), token: CancellationTokenSource.Token);
Store.IsExpanded.Subscribe(value => OnStateChanged(_ => isExpanded = value), token: CancellationTokenSource.Token);
Store.ProblemDetails.Subscribe(value => OnStateChanged(c_mp => problemDetails = value), token: CancellationTokenSource.Token);
}

/// <inheritdoc/>
protected override async Task OnParametersSetAsync()
{
await base.OnParametersSetAsync();
if (Document != _documentShadow)
await this.Store.HideAsync();
if (Document != __documentShadow)
{
Store.SetDocument(Document);
_documentShadow = Document;
__documentShadow = Document;
}
if (reference != Reference)
{
Store.SetReference(Reference);
}
if (collapse != null)
{
isOpen = false;
await collapse.HideAsync();
}
}

async Task ToggleAsync()
{
if (collapse != null)
{
isOpen = !isOpen;
await (isOpen ? collapse.ShowAsync() : collapse.HideAsync());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ public record DocumentDetailsState
/// </summary>
public bool Loaded { get; set; } = false;

/// <summary>
/// Gets/sets a boolean indicating if the logs panel is expanded
/// </summary>
public bool IsExpanded { get; set; } = false;

/// <summary>
/// Gets/sets the <see cref="ProblemDetails"/> type that occurred when trying to save the resource, if any
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// limitations under the License.

using Synapse.Api.Client.Services;
using Synapse.Dashboard.Components.WorkflowInstanceLogsStateManagement;

namespace Synapse.Dashboard.Components.DocumentDetailsStateManagement;

Expand Down Expand Up @@ -70,6 +71,11 @@ IYamlSerializer yamlSerializer
/// </summary>
public StandaloneCodeEditor? TextEditor { get; set; }

/// <summary>
/// Gets/sets the logs <see cref="Collapse"/> panel
/// </summary>
public Collapse? Collapse { get; set; }

#region Selectors
/// <summary>
/// Gets an <see cref="IObservable{T}"/> used to observe <see cref="DocumentDetailsState.Label"/> changes
Expand All @@ -91,6 +97,11 @@ IYamlSerializer yamlSerializer
/// </summary>
public IObservable<bool> Loaded => this.Select(state => state.Loaded).DistinctUntilChanged();

/// <summary>
/// Gets an <see cref="IObservable{T}"/> used to observe <see cref="WorkflowInstanceLogsState.IsExpanded"/> changes
/// </summary>
public IObservable<bool> IsExpanded => this.Select(state => state.IsExpanded).DistinctUntilChanged();

/// <summary>
/// Gets an <see cref="IObservable{T}"/> used to observe <see cref="DocumentDetailsState.ProblemType"/> changes
/// </summary>
Expand Down Expand Up @@ -188,6 +199,37 @@ public void SetDocument(object? document)
#endregion

#region Actions
/// <summary>
/// Toggles the <see cref="Collapse"/> panel
/// </summary>
public async Task ToggleAsync()
{
if (this.Collapse != null)
{
var isExpanded = !this.Get(state => state.IsExpanded);
await (isExpanded ? this.Collapse.ShowAsync() : this.Collapse.HideAsync());
this.Reduce(state => state with
{
IsExpanded = isExpanded
});
}
}

/// <summary>
/// Toggles the <see cref="Collapse"/> panel
/// </summary>
public async Task HideAsync()
{
if (this.Collapse != null)
{
await this.Collapse.HideAsync();
this.Reduce(state => state with
{
IsExpanded = false
});
}
}

/// <summary>
/// Loads the referenced documents
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,32 @@
<DocumentDetails Label='Output' Reference="@TaskInstance.OutputReference" />
}
</div>
<h6 class="pt-3">Executed Tasks</h6>
@if (SubTasks == null || SubTasks.Count() == 0)
{
@("-")
}
else
{
<table class="table table-hover">
<thead>
<tr>
<th>Name</th>
<th class="text-center">Status</th>
<th class="text-center">Start Time</th>
<th class="text-center">End Time</th>
<th class="text-center">Duration</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var task in SubTasks)
{
<TaskInstanceRow TaskInstance="@task" Tasks="@Tasks"/>
}
</tbody>
</table>
}
<h6 class="pt-3">Runs</h6>
@if (TaskInstance.Runs == null || TaskInstance.Runs.Count == 0)
{
Expand Down Expand Up @@ -137,4 +163,13 @@

@code {
[Parameter] public TaskInstance? TaskInstance { get; set; }
[Parameter] public IEnumerable<TaskInstance>? Tasks { get; set; }

IEnumerable<TaskInstance> SubTasks
{
get
{
return TaskInstance == null ? [] : (this.Tasks ?? []).Where(t => t.ParentId == TaskInstance.Id);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
protected override void OnParametersSet()
{
double divider = this.Node.Shape == NodeShape.Circle || this.Node.Shape == NodeShape.Ellipse ? 1 : 2;
double offset = radius * 4;
double offset = radius * 2;
double activeOffset = 0;
double compensatedOffset = 0;
double faultedOffset = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,50 +91,28 @@ IWorkflowGraphBuilder workflowGraphBuilder
(graph, instances) =>
{
var tasks = instances.SelectMany(instance => instance.Status?.Tasks ?? []);
//var allNodes = ((IReadOnlyDictionary<string, IWorkflowNodeViewModel>)graph.AllNodes).Concat((IReadOnlyDictionary<string, IWorkflowNodeViewModel>)graph.AllClusters);
foreach (var kvp in graph.AllNodes)
var allNodes = graph.AllNodes.Values.Concat(graph.AllClusters.Values);
foreach (var node in allNodes)
{
int activeCount, faultedCount;
if (kvp.Key == "start-node")
if (node.Id == "start-node")
{
activeCount = instances.Count(instance => instance.Status == null || instance.Status?.Phase == WorkflowInstanceStatusPhase.Pending || instance.Status?.Phase == WorkflowInstanceStatusPhase.Waiting);
faultedCount = instances.Count(instance => (instance.Status?.Tasks == null || instance.Status.Tasks.Count == 0) && (instance.Status?.Phase == WorkflowInstanceStatusPhase.Cancelled || instance.Status?.Phase == WorkflowInstanceStatusPhase.Faulted));
}
else if (kvp.Key == "end-node")
else if (node.Id == "end-node")
{
activeCount = instances.Count(instance => instance.Status?.Phase == WorkflowInstanceStatusPhase.Completed);
faultedCount = instances.Count(instance => instance.Status?.Tasks?.Count > 0 && (instance.Status?.Phase == WorkflowInstanceStatusPhase.Cancelled || instance.Status?.Phase == WorkflowInstanceStatusPhase.Faulted));
}
else
{
var nodeTasks = tasks.Where(task => Regex.Replace(task.Reference.ToString(), @"/for/\d*", "") == kvp.Key);
var nodeTasks = tasks.Where(task => Regex.Replace(task.Reference.ToString(), @"/for/\d*", "") == node.Id);
activeCount = nodeTasks.Count(task => task.Status == TaskInstanceStatus.Running);
faultedCount = nodeTasks.Count(task => task.Status == TaskInstanceStatus.Faulted || task.Status == TaskInstanceStatus.Cancelled);
}
((IWorkflowNodeViewModel)kvp.Value).OperativeInstancesCount = activeCount;
((IWorkflowNodeViewModel)kvp.Value).FaultedInstancesCount = faultedCount;
}
foreach (var kvp in graph.AllClusters)
{
int activeCount, faultedCount;
if (kvp.Key == "start-node")
{
activeCount = instances.Count(instance => instance.Status?.Phase == WorkflowInstanceStatusPhase.Pending || instance.Status?.Phase == WorkflowInstanceStatusPhase.Waiting);
faultedCount = instances.Count(instance => (instance.Status?.Tasks == null || instance.Status.Tasks.Count == 0) && (instance.Status?.Phase == WorkflowInstanceStatusPhase.Cancelled || instance.Status?.Phase == WorkflowInstanceStatusPhase.Faulted));
}
else if (kvp.Key == "end-node")
{
activeCount = instances.Count(instance => instance.Status?.Phase == WorkflowInstanceStatusPhase.Completed);
faultedCount = instances.Count(instance => instance.Status?.Tasks?.Count > 0 && (instance.Status?.Phase == WorkflowInstanceStatusPhase.Cancelled || instance.Status?.Phase == WorkflowInstanceStatusPhase.Faulted));
}
else
{
var nodeTasks = tasks.Where(task => Regex.Replace(task.Reference.ToString(), @"/for/\d*", "") == kvp.Key);
activeCount = nodeTasks.Count(task => task.Status == TaskInstanceStatus.Running);
faultedCount = nodeTasks.Count(task => task.Status == TaskInstanceStatus.Faulted || task.Status == TaskInstanceStatus.Cancelled);
}
((IWorkflowNodeViewModel)kvp.Value).OperativeInstancesCount = activeCount;
((IWorkflowNodeViewModel)kvp.Value).FaultedInstancesCount = faultedCount;
((IWorkflowNodeViewModel)node).OperativeInstancesCount = activeCount;
((IWorkflowNodeViewModel)node).FaultedInstancesCount = faultedCount;
}
return graph;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
<tr>
<td colspan="999">
<Collapse @ref="collapse">
<TaskInstanceDetails TaskInstance="TaskInstance" />
<TaskInstanceDetails TaskInstance="@TaskInstance" Tasks="@Tasks" />
</Collapse>
</td>
</tr>
Expand All @@ -47,6 +47,7 @@

@code {
[Parameter] public TaskInstance? TaskInstance { get; set; }
[Parameter] public IEnumerable<TaskInstance>? Tasks { get; set; }
bool isOpen = false;
Collapse? collapse;

Expand Down
Loading

0 comments on commit 3296e52

Please sign in to comment.