diff --git a/src/Aspire.Dashboard/Components/Controls/AspireMenuButton.razor b/src/Aspire.Dashboard/Components/Controls/AspireMenuButton.razor index ad2f9bf8b5..43d4768e14 100644 --- a/src/Aspire.Dashboard/Components/Controls/AspireMenuButton.razor +++ b/src/Aspire.Dashboard/Components/Controls/AspireMenuButton.razor @@ -2,25 +2,37 @@ @using Aspire.Dashboard.Model @using Microsoft.FluentUI.AspNetCore.Components.DesignTokens @inherits FluentComponentBase -@typeparam TItem -
- - @if (string.IsNullOrWhiteSpace(Text)) + + @if (string.IsNullOrWhiteSpace(Text)) + { + + } + else + { + @Text + + } + + + + @foreach (var item in Items) + { + @if (item.IsDivider) { - + } else { - @Text - - } - - - - @foreach (TItem command in Items) - { - @ItemText(command) + + @item.Text + @if (item.Icon != null) + { + + + + } + } - -
+ } + diff --git a/src/Aspire.Dashboard/Components/Controls/AspireMenuButton.razor.cs b/src/Aspire.Dashboard/Components/Controls/AspireMenuButton.razor.cs index 5dd588d418..470fd5b4d9 100644 --- a/src/Aspire.Dashboard/Components/Controls/AspireMenuButton.razor.cs +++ b/src/Aspire.Dashboard/Components/Controls/AspireMenuButton.razor.cs @@ -1,21 +1,20 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Aspire.Dashboard.Model; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; using Microsoft.FluentUI.AspNetCore.Components; namespace Aspire.Dashboard.Components; -public partial class AspireMenuButton : FluentComponentBase +public partial class AspireMenuButton : FluentComponentBase { private static readonly Icon s_defaultIcon = new Icons.Regular.Size24.ChevronDown(); private bool _visible; private Icon? _icon; - private readonly string _buttonId = Identifier.NewId(); - [Parameter] public string? Text { get; set; } @@ -23,16 +22,12 @@ public partial class AspireMenuButton : FluentComponentBase public Icon? Icon { get; set; } [Parameter] - public required IList Items { get; set; } - - [Parameter] - public required Func ItemText { get; set; } + public required IList Items { get; set; } [Parameter] public Appearance? ButtonAppearance { get; set; } - [Parameter] - public EventCallback OnItemClicked { get; set; } + public string MenuButtonId { get; } = Identifier.NewId(); protected override void OnParametersSet() { @@ -44,11 +39,11 @@ private void ToggleMenu() _visible = !_visible; } - private async Task HandleItemClicked(TItem item) + private async Task HandleItemClicked(MenuButtonItem item) { - if (item is not null) + if (item.OnClick is {} onClick) { - await OnItemClicked.InvokeAsync(item); + await onClick(); } _visible = false; } diff --git a/src/Aspire.Dashboard/Components/Controls/ResourceActions.razor b/src/Aspire.Dashboard/Components/Controls/ResourceActions.razor new file mode 100644 index 0000000000..fe765ef725 --- /dev/null +++ b/src/Aspire.Dashboard/Components/Controls/ResourceActions.razor @@ -0,0 +1,23 @@ +@namespace Aspire.Dashboard.Components +@using Aspire.Dashboard.Model +@using Microsoft.FluentUI.AspNetCore.Components + +@foreach (var highlightedCommand in Commands.Where(c => c.IsHighlighted)) +{ + + @if (!string.IsNullOrEmpty(highlightedCommand.IconName) && CommandViewModel.ResolveIconName(highlightedCommand.IconName) is { } icon) + { + + } + else + { + @highlightedCommand.DisplayName + } + +} + + diff --git a/src/Aspire.Dashboard/Components/Controls/ResourceActions.razor.cs b/src/Aspire.Dashboard/Components/Controls/ResourceActions.razor.cs new file mode 100644 index 0000000000..9ad8e812c3 --- /dev/null +++ b/src/Aspire.Dashboard/Components/Controls/ResourceActions.razor.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Aspire.Dashboard.Model; +using Microsoft.AspNetCore.Components; +using Microsoft.Extensions.Localization; +using Microsoft.FluentUI.AspNetCore.Components; + +namespace Aspire.Dashboard.Components; + +public partial class ResourceActions : ComponentBase +{ + private static readonly Icon s_viewDetailsIcon = new Icons.Regular.Size20.Info(); + private static readonly Icon s_consoleLogsIcon = new Icons.Regular.Size20.SlideText(); + + private AspireMenuButton? _menuButton; + + [Inject] + public required IStringLocalizer Loc { get; set; } + + [Parameter] + public required IList Commands { get; set; } + + [Parameter] + public required EventCallback CommandSelected { get; set; } + + [Parameter] + public required EventCallback OnViewDetails { get; set; } + + [Parameter] + public required EventCallback OnConsoleLogs { get; set; } + + private readonly List _menuItems = new(); + + protected override void OnParametersSet() + { + _menuItems.Clear(); + + _menuItems.Add(new MenuButtonItem + { + Text = Loc[nameof(Resources.Resources.ResourceActionViewDetailsText)], + Icon = s_viewDetailsIcon, + OnClick = () => OnViewDetails.InvokeAsync(_menuButton?.MenuButtonId) + }); + _menuItems.Add(new MenuButtonItem + { + Text = Loc[nameof(Resources.Resources.ResourceActionConsoleLogsText)], + Icon = s_consoleLogsIcon, + OnClick = OnConsoleLogs.InvokeAsync + }); + + var menuCommands = Commands.Where(c => !c.IsHighlighted).ToList(); + if (menuCommands.Count > 0) + { + _menuItems.Add(new MenuButtonItem { IsDivider = true }); + + foreach (var command in menuCommands) + { + var icon = (!string.IsNullOrEmpty(command.IconName) && CommandViewModel.ResolveIconName(command.IconName) is { } i) ? i : null; + + _menuItems.Add(new MenuButtonItem + { + Text = command.DisplayName, + Tooltip = command.DisplayDescription, + Icon = icon, + OnClick = () => CommandSelected.InvokeAsync(command) + }); + } + } + } +} diff --git a/src/Aspire.Dashboard/Components/Controls/ResourceCommands.razor b/src/Aspire.Dashboard/Components/Controls/ResourceCommands.razor deleted file mode 100644 index 9bdc24116f..0000000000 --- a/src/Aspire.Dashboard/Components/Controls/ResourceCommands.razor +++ /dev/null @@ -1,10 +0,0 @@ -@namespace Aspire.Dashboard.Components -@using Aspire.Dashboard.Model - - - diff --git a/src/Aspire.Dashboard/Components/Controls/ResourceCommands.razor.cs b/src/Aspire.Dashboard/Components/Controls/ResourceCommands.razor.cs deleted file mode 100644 index 9f65fa5947..0000000000 --- a/src/Aspire.Dashboard/Components/Controls/ResourceCommands.razor.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Aspire.Dashboard.Model; -using Microsoft.AspNetCore.Components; - -namespace Aspire.Dashboard.Components; - -public partial class ResourceCommands : ComponentBase -{ - [Parameter] - public required IList Commands { get; set; } - - [Parameter] - public EventCallback CommandSelected { get; set; } -} diff --git a/src/Aspire.Dashboard/Components/Controls/ResourceDetails.razor b/src/Aspire.Dashboard/Components/Controls/ResourceDetails.razor index d36d8bf433..4a3be10d60 100644 --- a/src/Aspire.Dashboard/Components/Controls/ResourceDetails.razor +++ b/src/Aspire.Dashboard/Components/Controls/ResourceDetails.razor @@ -7,7 +7,7 @@
- View logs + @Loc[Resources.ResourceDetailsViewConsoleLogs] @if (ShowSpecOnlyToggle) { diff --git a/src/Aspire.Dashboard/Components/Pages/Resources.razor b/src/Aspire.Dashboard/Components/Pages/Resources.razor index 227c78e5d9..b167a23ccd 100644 --- a/src/Aspire.Dashboard/Components/Pages/Resources.razor +++ b/src/Aspire.Dashboard/Components/Pages/Resources.razor @@ -73,9 +73,6 @@ SelectedValue="@SelectedResource" ViewKey="ResourcesList"> - @{ - var gridTemplateColumns = HasResourcesWithCommands ? "1fr 1.5fr 1.25fr 1.5fr 2.5fr 2.5fr 1fr 1fr 1fr" : "1fr 1.5fr 1.25fr 1.5fr 2.5fr 2.5fr 1fr 1fr"; - } - - @ControlsStringsLoc[ControlsStrings.ViewAction] - - - @{ - var id = $"details-button-{context.Uid}"; - } -
- @ControlsStringsLoc[nameof(ControlsStrings.ViewAction)] -
-
- -
- + +
+
diff --git a/src/Aspire.Dashboard/Components/Pages/Resources.razor.cs b/src/Aspire.Dashboard/Components/Pages/Resources.razor.cs index 1210907ee9..f4719162ab 100644 --- a/src/Aspire.Dashboard/Components/Pages/Resources.razor.cs +++ b/src/Aspire.Dashboard/Components/Pages/Resources.razor.cs @@ -27,9 +27,7 @@ public partial class Resources : ComponentBase, IAsyncDisposable private const string StartTimeColumn = nameof(StartTimeColumn); private const string SourceColumn = nameof(SourceColumn); private const string EndpointsColumn = nameof(EndpointsColumn); - private const string LogsColumn = nameof(LogsColumn); - private const string DetailsColumn = nameof(DetailsColumn); - private const string CommandsColumn = nameof(CommandsColumn); + private const string ActionsColumn = nameof(ActionsColumn); private Subscription? _logsSubscription; private Dictionary? _applicationUnviewedErrorCounts; @@ -139,8 +137,6 @@ static bool UnionWithKeys(ConcurrentDictionary left, ConcurrentDic } } - private bool HasResourcesWithCommands => _resourceByName.Any(r => r.Value.Commands.Any()); - private IQueryable? FilteredResources => _resourceByName.Values.Where(Filter).OrderBy(e => e.ResourceType).ThenBy(e => e.Name).AsQueryable(); private readonly GridSort _nameSort = GridSort.ByAscending(p => p.Name); @@ -156,9 +152,7 @@ protected override async Task OnInitializedAsync() new GridColumn(Name: StartTimeColumn, DesktopWidth: "1.5fr"), new GridColumn(Name: SourceColumn, DesktopWidth: "2.5fr"), new GridColumn(Name: EndpointsColumn, DesktopWidth: "2.5fr", MobileWidth: "2fr"), - new GridColumn(Name: LogsColumn, DesktopWidth: "1fr"), - new GridColumn(Name: DetailsColumn, DesktopWidth: "1fr", MobileWidth: "1fr"), - new GridColumn(Name: CommandsColumn, DesktopWidth: "1fr", IsVisible: () => HasResourcesWithCommands) + new GridColumn(Name: ActionsColumn, DesktopWidth: "1.5fr", MobileWidth: "1fr") ], DimensionManager); _applicationUnviewedErrorCounts = TelemetryRepository.GetApplicationUnviewedErrorLogsCount(); diff --git a/src/Aspire.Dashboard/Model/MenuButtonItem.cs b/src/Aspire.Dashboard/Model/MenuButtonItem.cs new file mode 100644 index 0000000000..14eaecb22d --- /dev/null +++ b/src/Aspire.Dashboard/Model/MenuButtonItem.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.FluentUI.AspNetCore.Components; + +namespace Aspire.Dashboard.Model; + +public class MenuButtonItem +{ + public bool IsDivider { get; set; } + public string? Text { get; set; } + public string? Tooltip { get; set; } + public Icon? Icon { get; set; } + public Func? OnClick { get; set; } +} diff --git a/src/Aspire.Dashboard/Model/ResourceViewModel.cs b/src/Aspire.Dashboard/Model/ResourceViewModel.cs index 00dec27bb9..36905c7785 100644 --- a/src/Aspire.Dashboard/Model/ResourceViewModel.cs +++ b/src/Aspire.Dashboard/Model/ResourceViewModel.cs @@ -1,11 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Concurrent; using System.Collections.Frozen; using System.Collections.Immutable; using System.Diagnostics; using Aspire.Dashboard.Extensions; using Google.Protobuf.WellKnownTypes; +using Microsoft.FluentUI.AspNetCore.Components; namespace Aspire.Dashboard.Model; @@ -60,20 +62,50 @@ public static string GetResourceName(ResourceViewModel resource, IDictionary s_iconCache = new(); + public string CommandType { get; } public string DisplayName { get; } + public string? DisplayDescription { get; } public string? ConfirmationMessage { get; } public Value? Parameter { get; } + public bool IsHighlighted { get; } + public string? IconName { get; } - public CommandViewModel(string commandType, string displayName, string? confirmationMessage, Value? parameter) + public CommandViewModel(string commandType, string displayName, string? displayDescription, string? confirmationMessage, Value? parameter, bool isHighlighted, string? iconName) { ArgumentException.ThrowIfNullOrWhiteSpace(commandType); ArgumentException.ThrowIfNullOrWhiteSpace(displayName); CommandType = commandType; DisplayName = displayName; + DisplayDescription = displayDescription; ConfirmationMessage = confirmationMessage; Parameter = parameter; + IsHighlighted = isHighlighted; + IconName = iconName; + } + + public static CustomIcon? ResolveIconName(string iconName) + { + // Icons.GetInstance isn't efficent. Cache icon lookup. + return s_iconCache.GetOrAdd(iconName, static name => + { + try + { + return Icons.GetInstance(new IconInfo + { + Name = name, + Variant = IconVariant.Regular, + Size = IconSize.Size20 + }); + } + catch + { + // Icon name couldn't be found. + return null; + } + }); } } diff --git a/src/Aspire.Dashboard/ResourceService/Partials.cs b/src/Aspire.Dashboard/ResourceService/Partials.cs index 39206ecc51..9f894fd5b0 100644 --- a/src/Aspire.Dashboard/ResourceService/Partials.cs +++ b/src/Aspire.Dashboard/ResourceService/Partials.cs @@ -58,7 +58,7 @@ ImmutableArray GetUrls() ImmutableArray GetCommands() { return Commands - .Select(c => new CommandViewModel(c.CommandType, c.DisplayName, c.ConfirmationMessage, c.Parameter)) + .Select(c => new CommandViewModel(c.CommandType, c.DisplayName, c.HasDisplayDescription ? c.DisplayDescription : null, c.ConfirmationMessage, c.Parameter, c.IsHighlighted, c.HasIconName ? c.IconName : null)) .ToImmutableArray(); } diff --git a/src/Aspire.Dashboard/Resources/Resources.Designer.cs b/src/Aspire.Dashboard/Resources/Resources.Designer.cs index 1210ef12c6..ade01090fc 100644 --- a/src/Aspire.Dashboard/Resources/Resources.Designer.cs +++ b/src/Aspire.Dashboard/Resources/Resources.Designer.cs @@ -60,6 +60,24 @@ internal Resources() { } } + /// + /// Looks up a localized string similar to Console logs. + /// + public static string ResourceActionConsoleLogsText { + get { + return ResourceManager.GetString("ResourceActionConsoleLogsText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to View details. + /// + public static string ResourceActionViewDetailsText { + get { + return ResourceManager.GetString("ResourceActionViewDetailsText", resourceCulture); + } + } + /// /// Looks up a localized string similar to "{0}" command failed. /// @@ -106,20 +124,20 @@ public static string ResourceDetailsProxyUrl { } /// - /// Looks up a localized string similar to Commands. + /// Looks up a localized string similar to View console logs. /// - public static string ResourcesCommandsColumnHeader { + public static string ResourceDetailsViewConsoleLogs { get { - return ResourceManager.GetString("ResourcesCommandsColumnHeader", resourceCulture); + return ResourceManager.GetString("ResourceDetailsViewConsoleLogs", resourceCulture); } } /// - /// Looks up a localized string similar to Details. + /// Looks up a localized string similar to Actions. /// - public static string ResourcesDetailsColumnHeader { + public static string ResourcesActionsColumnHeader { get { - return ResourceManager.GetString("ResourcesDetailsColumnHeader", resourceCulture); + return ResourceManager.GetString("ResourcesActionsColumnHeader", resourceCulture); } } @@ -285,15 +303,6 @@ public static string ResourcesHeader { } } - /// - /// Looks up a localized string similar to Logs. - /// - public static string ResourcesLogsColumnHeader { - get { - return ResourceManager.GetString("ResourcesLogsColumnHeader", resourceCulture); - } - } - /// /// Looks up a localized string similar to No environment variables. /// diff --git a/src/Aspire.Dashboard/Resources/Resources.resx b/src/Aspire.Dashboard/Resources/Resources.resx index 3fb2e0461d..29b1a4789b 100644 --- a/src/Aspire.Dashboard/Resources/Resources.resx +++ b/src/Aspire.Dashboard/Resources/Resources.resx @@ -1,17 +1,17 @@ - @@ -140,15 +140,9 @@ State - - Logs - Type - - Details - Start time @@ -226,7 +220,16 @@ View Logs - - Commands + + Actions + + + Console logs + + + View details + + + View console logs - + \ No newline at end of file diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.cs.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.cs.xlf index 135048e555..2f47e1ad30 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.cs.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.cs.xlf @@ -2,6 +2,16 @@ + + Console logs + Console logs + + + + View details + View details + + "{0}" command failed Provedení příkazu "{0}" se nezdařilo @@ -27,14 +37,14 @@ Adresa URL proxy serveru - - Commands - Příkazy + + View console logs + View console logs - - Details - Podrobnosti + + Actions + Actions @@ -117,11 +127,6 @@ Prostředí - - Logs - Protokoly - - Source Zdroj diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.de.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.de.xlf index c1783addf4..7b1c36a5ac 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.de.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.de.xlf @@ -2,6 +2,16 @@ + + Console logs + Console logs + + + + View details + View details + + "{0}" command failed Befehl "{0}" fehlgeschlagen @@ -27,14 +37,14 @@ Proxy-URL - - Commands - Befehle + + View console logs + View console logs - - Details - Details + + Actions + Actions @@ -117,11 +127,6 @@ Umgebung - - Logs - Protokolle - - Source Quelle diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.es.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.es.xlf index 2df056960c..48cca31b1e 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.es.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.es.xlf @@ -2,6 +2,16 @@ + + Console logs + Console logs + + + + View details + View details + + "{0}" command failed Error del comando "{0}" @@ -27,14 +37,14 @@ URL del proxy - - Commands - Comandos + + View console logs + View console logs - - Details - Detalles + + Actions + Actions @@ -117,11 +127,6 @@ Entorno - - Logs - Registros - - Source Origen diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.fr.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.fr.xlf index 2b17871bab..265cde3252 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.fr.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.fr.xlf @@ -2,6 +2,16 @@ + + Console logs + Console logs + + + + View details + View details + + "{0}" command failed Échec de la commande « {0} » @@ -27,14 +37,14 @@ URL du proxy - - Commands - Commandes + + View console logs + View console logs - - Details - Détails + + Actions + Actions @@ -117,11 +127,6 @@ Environnement - - Logs - Journaux - - Source Source diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.it.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.it.xlf index 0be3b0c7d0..be434f752a 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.it.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.it.xlf @@ -2,6 +2,16 @@ + + Console logs + Console logs + + + + View details + View details + + "{0}" command failed Comando "{0}" non riuscito @@ -27,14 +37,14 @@ URL proxy - - Commands - Comandi + + View console logs + View console logs - - Details - Dettagli + + Actions + Actions @@ -117,11 +127,6 @@ Ambiente - - Logs - Log - - Source Origine diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.ja.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.ja.xlf index 0c84f0c9b2..587ef7feb6 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.ja.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.ja.xlf @@ -2,6 +2,16 @@ + + Console logs + Console logs + + + + View details + View details + + "{0}" command failed "{0}" コマンドが失敗しました @@ -27,14 +37,14 @@ プロキシ URL - - Commands - コマンド + + View console logs + View console logs - - Details - 詳細 + + Actions + Actions @@ -117,11 +127,6 @@ 環境 - - Logs - ログ - - Source ソース diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.ko.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.ko.xlf index fdef712896..fd7309ebb7 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.ko.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.ko.xlf @@ -2,6 +2,16 @@ + + Console logs + Console logs + + + + View details + View details + + "{0}" command failed "{0}" 명령이 실패했습니다. @@ -27,14 +37,14 @@ 프록시 URL - - Commands - 명령 + + View console logs + View console logs - - Details - 세부 정보 + + Actions + Actions @@ -117,11 +127,6 @@ 환경 - - Logs - 로그 - - Source 원본 diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.pl.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.pl.xlf index 9ef1788fc5..6c03742335 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.pl.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.pl.xlf @@ -2,6 +2,16 @@ + + Console logs + Console logs + + + + View details + View details + + "{0}" command failed Niepowodzenie polecenia „{0}” @@ -27,14 +37,14 @@ Adres URL serwera proxy - - Commands - Polecenia + + View console logs + View console logs - - Details - Szczegóły + + Actions + Actions @@ -117,11 +127,6 @@ Środowisko - - Logs - Dzienniki - - Source Źródło diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.pt-BR.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.pt-BR.xlf index eae4670310..bcf3a79a9e 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.pt-BR.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.pt-BR.xlf @@ -2,6 +2,16 @@ + + Console logs + Console logs + + + + View details + View details + + "{0}" command failed O comando “{0}” falhou @@ -27,14 +37,14 @@ URL do Proxy - - Commands - Comandos + + View console logs + View console logs - - Details - Detalhes + + Actions + Actions @@ -117,11 +127,6 @@ Ambiente - - Logs - Logs - - Source Origem diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.ru.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.ru.xlf index bc5d693788..b7d808fcec 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.ru.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.ru.xlf @@ -2,6 +2,16 @@ + + Console logs + Console logs + + + + View details + View details + + "{0}" command failed Сбой команды "{0}" @@ -27,14 +37,14 @@ URL-адрес прокси-сервера - - Commands - Команды + + View console logs + View console logs - - Details - Сведения + + Actions + Actions @@ -117,11 +127,6 @@ Среда - - Logs - Журналы - - Source Источник diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.tr.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.tr.xlf index aee3f3a237..325c7817f9 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.tr.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.tr.xlf @@ -2,6 +2,16 @@ + + Console logs + Console logs + + + + View details + View details + + "{0}" command failed "{0}" komutu başarısız oldu @@ -27,14 +37,14 @@ Ara sunucu URL'si - - Commands - Komutlar + + View console logs + View console logs - - Details - Ayrıntılar + + Actions + Actions @@ -117,11 +127,6 @@ Ortam - - Logs - Günlükler - - Source Kaynak diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.zh-Hans.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.zh-Hans.xlf index 78fa9b791f..5385d454f1 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.zh-Hans.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.zh-Hans.xlf @@ -2,6 +2,16 @@ + + Console logs + Console logs + + + + View details + View details + + "{0}" command failed “{0}”命令失败 @@ -27,14 +37,14 @@ 代理 URL - - Commands - 命令 + + View console logs + View console logs - - Details - 详细信息 + + Actions + Actions @@ -117,11 +127,6 @@ 环境 - - Logs - 日志 - - Source diff --git a/src/Aspire.Dashboard/Resources/xlf/Resources.zh-Hant.xlf b/src/Aspire.Dashboard/Resources/xlf/Resources.zh-Hant.xlf index 848b6cc0ff..81cb162ee9 100644 --- a/src/Aspire.Dashboard/Resources/xlf/Resources.zh-Hant.xlf +++ b/src/Aspire.Dashboard/Resources/xlf/Resources.zh-Hant.xlf @@ -2,6 +2,16 @@ + + Console logs + Console logs + + + + View details + View details + + "{0}" command failed "{0}" 命令失敗 @@ -27,14 +37,14 @@ Proxy URL - - Commands - 命令 + + View console logs + View console logs - - Details - 詳細資料 + + Actions + Actions @@ -117,11 +127,6 @@ 環境 - - Logs - 記錄 - - Source 來源 diff --git a/src/Aspire.Hosting/Dashboard/DashboardService.cs b/src/Aspire.Hosting/Dashboard/DashboardService.cs index b57d519b1c..a410a75d71 100644 --- a/src/Aspire.Hosting/Dashboard/DashboardService.cs +++ b/src/Aspire.Hosting/Dashboard/DashboardService.cs @@ -25,8 +25,6 @@ internal sealed partial class DashboardService(DashboardServiceData serviceData, // with IHostApplicationLifetime.ApplicationStopping to ensure eager cancellation // of pending connections during shutdown. - // TODO implement command handling - [GeneratedRegex("""^(?.+?)\.?AppHost$""", RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.CultureInvariant)] private static partial Regex ApplicationNameRegex(); diff --git a/src/Aspire.Hosting/Dashboard/proto/Partials.cs b/src/Aspire.Hosting/Dashboard/proto/Partials.cs index 4be6ee4c44..5da844c539 100644 --- a/src/Aspire.Hosting/Dashboard/proto/Partials.cs +++ b/src/Aspire.Hosting/Dashboard/proto/Partials.cs @@ -40,6 +40,47 @@ public static Resource FromSnapshot(ResourceSnapshot snapshot) resource.Properties.Add(new ResourceProperty { Name = property.Name, Value = property.Value }); } + // Disable start/stop/restart commands until host/DCP infrastructure is ready. + /* + if (snapshot.ResourceType is KnownResourceTypes.Project or KnownResourceTypes.Container or KnownResourceTypes.Executable) + { + if (snapshot.State is "Exited" or "Finished" or "FailedToStart") + { + resource.Commands.Add(new ResourceCommand + { + CommandType = "Start", + ConfirmationMessage = "ConfirmationMessage!", + DisplayName = "Start", + DisplayDescription = "Start resource", + IsHighlighted = true, + IconName = "Play" + }); + } + else + { + resource.Commands.Add(new ResourceCommand + { + CommandType = "Stop", + ConfirmationMessage = "ConfirmationMessage!", + DisplayName = "Stop", + DisplayDescription = "Stop resource", + IsHighlighted = true, + IconName = "Stop" + }); + } + + resource.Commands.Add(new ResourceCommand + { + CommandType = "Restart", + ConfirmationMessage = "ConfirmationMessage!", + DisplayName = "Restart", + DisplayDescription = "Restart resource", + IsHighlighted = false, + IconName = "ArrowCounterclockwise" + }); + } + */ + return resource; } } diff --git a/src/Aspire.Hosting/Dashboard/proto/resource_service.proto b/src/Aspire.Hosting/Dashboard/proto/resource_service.proto index 80305d952d..37ac0759e7 100644 --- a/src/Aspire.Hosting/Dashboard/proto/resource_service.proto +++ b/src/Aspire.Hosting/Dashboard/proto/resource_service.proto @@ -38,6 +38,14 @@ message ResourceCommand { // Clients must return any value provided by the server when invoking // the command. optional google.protobuf.Value parameter = 4; + // A flag that indicates whether the command is highlighted in the UI. + bool is_highlighted = 5; + // Optional icon name. This name should be a valid FluentUI icon name. + // https://aka.ms/fluentui-system-icons + optional string icon_name = 6; + // Optional description of the command, to be shown in the UI. + // Could be used as a tooltip. May be localized. + optional string display_description = 7; } // Represents a request to execute a command. diff --git a/tests/Aspire.Workload.Tests/WorkloadTestsBase.cs b/tests/Aspire.Workload.Tests/WorkloadTestsBase.cs index 0e608b8012..7bfa342224 100644 --- a/tests/Aspire.Workload.Tests/WorkloadTestsBase.cs +++ b/tests/Aspire.Workload.Tests/WorkloadTestsBase.cs @@ -49,7 +49,7 @@ protected static async Task CheckDashboardHasResourcesAsync(IPage testOutput.WriteLine($"Waiting for resources to appear on the dashboard"); await Task.Delay(500); - Dictionary expectedRowsTable = expectedResources.ToDictionary(r => r.Name); + var expectedRowsTable = expectedResources.ToDictionary(r => r.Name); HashSet foundNames = []; List foundRows = []; @@ -61,7 +61,7 @@ protected static async Task CheckDashboardHasResourcesAsync(IPage await Task.Delay(500); // _testOutput.WriteLine($"Checking for rows again"); - ILocator rowsLocator = dashboardPage.Locator("//fluent-data-grid-row[@class='hover resource-row']"); + var rowsLocator = dashboardPage.Locator("//fluent-data-grid-row[@class='hover resource-row']"); var allRows = await rowsLocator.AllAsync(); // _testOutput.WriteLine($"found rows#: {allRows.Count}"); if (allRows.Count == 0) @@ -70,14 +70,13 @@ protected static async Task CheckDashboardHasResourcesAsync(IPage continue; } - foreach (ILocator rowLoc in allRows) + foreach (var rowLoc in allRows) { // get the cells - IReadOnlyList cellLocs = await rowLoc.Locator("//fluent-data-grid-cell[@role='gridcell']").AllAsync(); - Assert.Equal(8, cellLocs.Count); + var cellLocs = await rowLoc.Locator("//fluent-data-grid-cell[@role='gridcell']").AllAsync(); // is the resource name expected? - string resourceName = await cellLocs[1].InnerTextAsync(); + var resourceName = await cellLocs[1].InnerTextAsync(); if (!expectedRowsTable.TryGetValue(resourceName, out var expectedRow)) { Assert.Fail($"Row with unknown name found: {resourceName}"); @@ -87,11 +86,9 @@ protected static async Task CheckDashboardHasResourcesAsync(IPage continue; } - string resourceNameInCell = await cellLocs[1].InnerTextAsync().ConfigureAwait(false); - resourceNameInCell.Trim(); - AssertEqual(expectedRow.Name, resourceNameInCell, $"Name for {resourceName}"); + AssertEqual(expectedRow.Name, resourceName, $"Name for {resourceName}"); - string actualState = await cellLocs[2].InnerTextAsync().ConfigureAwait(false); + var actualState = await cellLocs[2].InnerTextAsync().ConfigureAwait(false); actualState = actualState.Trim(); if (expectedRow.State != actualState && actualState != "Finished" && !actualState.Contains("failed", StringComparison.OrdinalIgnoreCase)) { @@ -102,7 +99,7 @@ protected static async Task CheckDashboardHasResourcesAsync(IPage // Match endpoints - int matchingEndpoints = 0; + var matchingEndpoints = 0; var expectedEndpoints = expectedRow.Endpoints; var endpointsFound =