Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion BootstrapBlazor.Extensions.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18
VisualStudioVersion = 18.0.10828.68 main
VisualStudioVersion = 18.0.10828.68
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "components", "components", "{FF1089BE-C704-4374-B629-C57C08E1798F}"
EndProject
Expand Down Expand Up @@ -200,6 +200,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.OpcDa", "sr
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestOpcDa", "test\UnitTestOpcDa\UnitTestOpcDa.csproj", "{835C8BA9-A9CC-4EA0-9002-34A20F8B2E86}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Tasks.Dashboard", "src\components\BootstrapBlazor.Tasks.Dashboard\BootstrapBlazor.Tasks.Dashboard.csproj", "{30C57119-C564-401C-AE3A-6203E2733E1A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -546,6 +548,10 @@ Global
{835C8BA9-A9CC-4EA0-9002-34A20F8B2E86}.Debug|Any CPU.Build.0 = Debug|Any CPU
{835C8BA9-A9CC-4EA0-9002-34A20F8B2E86}.Release|Any CPU.ActiveCfg = Release|Any CPU
{835C8BA9-A9CC-4EA0-9002-34A20F8B2E86}.Release|Any CPU.Build.0 = Release|Any CPU
{30C57119-C564-401C-AE3A-6203E2733E1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{30C57119-C564-401C-AE3A-6203E2733E1A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{30C57119-C564-401C-AE3A-6203E2733E1A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{30C57119-C564-401C-AE3A-6203E2733E1A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -640,6 +646,7 @@ Global
{98373A64-E224-4715-AE02-A8C6DAFF3338} = {B6A98ADE-D26A-4D0B-8978-AB7AC915F5AE}
{01007B10-7C3C-4136-83FF-981CA39AD3D4} = {7B29E81D-92DE-46C8-8EDC-1B48C8F12BC2}
{835C8BA9-A9CC-4EA0-9002-34A20F8B2E86} = {B6A98ADE-D26A-4D0B-8978-AB7AC915F5AE}
{30C57119-C564-401C-AE3A-6203E2733E1A} = {FF1089BE-C704-4374-B629-C57C08E1798F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D5EB1960-6F30-4CE1-B375-EAE1F787D6FF}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">

<PropertyGroup>
<Version>9.0.0</Version>
</PropertyGroup>

<PropertyGroup>
<PackageTags>Bootstrap Blazor WebAssembly wasm UI Components Task Dashboard</PackageTags>
<Description>Bootstrap UI components extensions of Task</Description>
</PropertyGroup>

<ItemGroup>
<Content Remove="Locales\en-US.json" />
<Content Remove="Locales\zh-CN.json" />
<EmbeddedResource Include="Locales\en-US.json" />
<EmbeddedResource Include="Locales\zh-CN.json" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="BootstrapBlazor" Version="9.0.0" />
<PackageReference Include="Longbow.Tasks" Version="9.0.2" />
</ItemGroup>

<ItemGroup>
<Using Include="BootstrapBlazor.Components" />
<Using Include="Longbow.Tasks" />
</ItemGroup>

</Project>
26 changes: 26 additions & 0 deletions src/components/BootstrapBlazor.Tasks.Dashboard/Locales/en-US.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"BootstrapBlazor.Components.Tasks.TaskDashboard": {
"Name": "Name",
"Status": "Status",
"NextRuntime": "NextRuntime",
"LastRuntime": "LastRuntime",
"LastRunResult": "LastRunResult",
"Exception": "Exception",
"Operation": "Operation",
"SchedulerStatus.Running": "Running",
"SchedulerStatus.Ready": "Ready",
"SchedulerStatus.Disabled": "Disabled",
"TriggerResult.Running": "Running",
"TriggerResult.Success": "Success",
"TriggerResult.Cancelled": "Cancelled",
"TriggerResult.Timeout": "Timeout",
"TriggerResult.Error": "Error",
"ButtonPause": "Pause",
"ButtonRun": "Run",
"ButtonLog": "Log",
"LogDialogTitle": "{0} - Log(Latest 20 Items)",
"LogDialogConsoleHeaderText": "Action Log",
"ExceptionDialogTitle": "{0} - Exception Detail",
"None": "None"
}
}
26 changes: 26 additions & 0 deletions src/components/BootstrapBlazor.Tasks.Dashboard/Locales/zh-CN.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"BootstrapBlazor.Components.Tasks.TaskDashboard": {
"Name": "名称",
"Status": "调度状态",
"NextRuntime": "下次运行时间",
"LastRuntime": "上次运行时间",
"LastRunResult": "上次结果",
"Exception": "异常",
"Operation": "操作",
"SchedulerStatus.Running": "运行中",
"SchedulerStatus.Ready": "已停止",
"SchedulerStatus.Disabled": "禁用",
"TriggerResult.Running": "运行",
"TriggerResult.Success": "成功",
"TriggerResult.Cancelled": "取消",
"TriggerResult.Timeout": "超时",
"TriggerResult.Error": "故障",
"ButtonPause": "暂停",
"ButtonRun": "运行",
"ButtonLog": "日志",
"LogDialogTitle": "{0} - 日志窗口(最新 20 条)",
"LogDialogConsoleHeaderText": "执行日志",
"ExceptionDialogTitle": "{0} - 异常明细",
"None": "无"
}
}
59 changes: 59 additions & 0 deletions src/components/BootstrapBlazor.Tasks.Dashboard/TaskDashboard.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
@namespace BootstrapBlazor.Components.Tasks
@inherits BootstrapComponentBase

<div @attributes="AdditionalAttributes" class="@ClassString">
<Table TItem="IScheduler" Items="@_schedulers" IsBordered="true" IsStriped="true">
<TableColumns>
<TableTemplateColumn Text="@Localizer["Name"]">
<Template Context="v">
@v.Row.Name
</Template>
</TableTemplateColumn>
<TableTemplateColumn Text="@Localizer["Status"]">
<Template Context="v">
<Tag Color="GetStatusColor(v.Row.Status)" Icon="@GetStatusIcon(v.Row.Status)">@FormatStatus(v.Row.Status)</Tag>
</Template>
</TableTemplateColumn>
<TableTemplateColumn Text="@Localizer["LastRuntime"]">
<Template Context="v">
@FormatDateTime(v.Row.LastRuntime)
</Template>
</TableTemplateColumn>
<TableTemplateColumn Text="@Localizer["NextRuntime"]">
<Template Context="v">
@FormatDateTime(v.Row.NextRuntime)
</Template>
</TableTemplateColumn>
<TableTemplateColumn Text="@Localizer["LastRunResult"]">
<Template Context="v">
<Tag Color="GetResultColor(v.Row.LastRunResult)">@FormatResult(v.Row.LastRunResult)</Tag>
</Template>
</TableTemplateColumn>
<TableTemplateColumn Text="@Localizer["Exception"]">
<Template Context="v">
@if (v.Row.Exception != null)
{
<Button Color="Color.Danger" Size="Size.ExtraSmall" Icon="fa-solid fa-exclamation-circle"
OnClickWithoutRender="() => OnShowException(v.Row, v.Row.Exception)"></Button>
}
else
{
<Tag Color="Color.Success">@Localizer["None"]</Tag>
}
</Template>
</TableTemplateColumn>
<TableTemplateColumn Text="@Localizer["Operation"]" Width="170">
<Template Context="v">
<div class="btn-group">
<Button Size="Size.ExtraSmall" Color="Color.Warning" Icon="fa-solid fa-pause-circle"
Text="@Localizer["ButtonPause"]" OnClick="() => OnPause(v.Row)" IsDisabled="OnCheckPauseTaskStatus(v.Row)"></Button>
<Button Size="Size.ExtraSmall" Color="Color.Success" Icon="fa-solid fa-play-circle"
Text="@Localizer["ButtonRun"]" OnClick="() => OnRun(v.Row)" IsDisabled="OnCheckRunTaskStatus(v.Row)"></Button>
<Button Size="Size.ExtraSmall" Color="Color.Info" Icon="fa-solid fa-info-circle"
Text="@Localizer["ButtonLog"]" OnClick="() => OnLog(v.Row)"></Button>
</div>
</Template>
</TableTemplateColumn>
</TableColumns>
</Table>
</div>
122 changes: 122 additions & 0 deletions src/components/BootstrapBlazor.Tasks.Dashboard/TaskDashboard.razor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/

using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization;

namespace BootstrapBlazor.Components.Tasks;

/// <summary>
/// Task Dashboard Component
/// </summary>
public partial class TaskDashboard
{
[Inject, NotNull]
private IStringLocalizer<TaskDashboard>? Localizer { get; set; }

[Inject, NotNull]
private DialogService? DialogService { get; set; }

private string? ClassString => CssBuilder.Default("bb-tasks-dashboard")
.AddClassFromAttributes(AdditionalAttributes)
.Build();

private IEnumerable<IScheduler> _schedulers = [];

/// <summary>
/// <inheritdoc/>
/// </summary>
protected override void OnParametersSet()
{
base.OnParametersSet();

_schedulers = TaskServicesManager.ToList();
}

private static Color GetStatusColor(SchedulerStatus status) => status switch
{
SchedulerStatus.Running => Color.Success,
SchedulerStatus.Ready => Color.Primary,
_ => Color.Danger
};

private string FormatStatus(SchedulerStatus status) => status switch
{
SchedulerStatus.Running => Localizer["SchedulerStatus.Running"],
SchedulerStatus.Ready => Localizer["SchedulerStatus.Ready"],
_ => Localizer["SchedulerStatus.Disabled"]
};

private static string GetStatusIcon(SchedulerStatus status) => status switch
{
SchedulerStatus.Running => "fa-solid fa-play-circle",
SchedulerStatus.Ready => "fa-solid fa-stop-circle",
_ => "fa-solid fa-times-circle"
};

private static Color GetResultColor(TriggerResult result) => result switch
{
TriggerResult.Running => Color.Primary,
TriggerResult.Success => Color.Success,
TriggerResult.Cancelled => Color.Dark,
TriggerResult.Timeout => Color.Warning,
_ => Color.Danger,
};

private string FormatResult(TriggerResult result) => result switch
{
TriggerResult.Running => Localizer["TriggerResult.Running"],
TriggerResult.Success => Localizer["TriggerResult.Success"],
TriggerResult.Cancelled => Localizer["TriggerResult.Timeout"],
Comment on lines +67 to +71
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Cancelled result is mapped to Timeout localization key.

Using the Timeout key for Cancelled may mislead users. Please add a separate localization key for Cancelled.

Suggested implementation:

        TriggerResult.Cancelled => Localizer["TriggerResult.Cancelled"],

You must also add the "TriggerResult.Cancelled" key to your localization resource files (e.g., .resx or JSON files) with the appropriate value for each supported language.

TriggerResult.Timeout => Localizer["TriggerResult.Timeout"],
_ => Localizer["TriggerResult.Error"],
};

private static Task OnPause(IScheduler scheduler)
{
scheduler.Status = SchedulerStatus.Ready;
return Task.CompletedTask;
}

private static Task OnRun(IScheduler scheduler)
{
scheduler.Status = SchedulerStatus.Running;
return Task.CompletedTask;
}

private async Task OnLog(IScheduler scheduler)
{
var option = new DialogOption()
{
Class = "modal-dialog-task-log",
Title = Localizer["LogDialogTitle", scheduler.Name],
Component = BootstrapDynamicComponent.CreateComponent<TaskInfo>(new Dictionary<string, object?>
{
[nameof(TaskInfo.Scheduler)] = scheduler,
[nameof(TaskInfo.HeaderText)] = Localizer["LogDialogConsoleHeaderText"].Value
})
};
await DialogService.Show(option);
}

private static bool OnCheckPauseTaskStatus(IScheduler model) => model.Status != SchedulerStatus.Running;

private static bool OnCheckRunTaskStatus(IScheduler model) => model.Status == SchedulerStatus.Running;

private static string? FormatDateTime(DateTimeOffset? dateTime) => dateTime?.ToString("yyyy-MM-dd HH:mm:ss");

private async Task OnShowException(IScheduler scheduler, Exception ex)
{
var option = new DialogOption()
{
Class = "modal-dialog-task-ex",
IsScrolling = true,
Title = Localizer["ExceptionDialogTitle", scheduler.Name],
BodyTemplate = RenderException(ex)
};
await DialogService.Show(option);
}

private static RenderFragment RenderException(Exception ex) => builder => builder.AddContent(0, ex.FormatMarkupString());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.my-component {
border: 2px dashed red;
padding: 1em;
margin: 1em 0;
background-image: url('background.png');
Comment on lines +1 to +5
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: CSS class .my-component is defined but not used.

If the class is not needed, please remove it to keep the stylesheet clean.

}
3 changes: 3 additions & 0 deletions src/components/BootstrapBlazor.Tasks.Dashboard/TaskInfo.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@namespace BootstrapBlazor.Components.Tasks

<BootstrapBlazor.Components.Console Items="@Messages" Height="280" ShowAutoScroll="true" HeaderText="@HeaderText" />
Loading