diff --git a/src/Microsoft.Tye.Hosting/Dashboard/Pages/Index.razor b/src/Microsoft.Tye.Hosting/Dashboard/Pages/Index.razor
index 53a9bd47..635c0ed1 100644
--- a/src/Microsoft.Tye.Hosting/Dashboard/Pages/Index.razor
+++ b/src/Microsoft.Tye.Hosting/Dashboard/Pages/Index.razor
@@ -88,8 +88,6 @@
{
_subscriptions.Add(a.ReplicaEvents.Subscribe(OnReplicaChanged));
}
-
- base.OnInitialized();
}
private void OnReplicaChanged(ReplicaEvent replicaEvent)
diff --git a/src/Microsoft.Tye.Hosting/Dashboard/Pages/ServiceDetails.razor b/src/Microsoft.Tye.Hosting/Dashboard/Pages/ServiceDetails.razor
index f82e7edf..875912c5 100644
--- a/src/Microsoft.Tye.Hosting/Dashboard/Pages/ServiceDetails.razor
+++ b/src/Microsoft.Tye.Hosting/Dashboard/Pages/ServiceDetails.razor
@@ -18,6 +18,10 @@ else
case ServiceType.Project:
break;
}
+
+
+
+
}
@code {
diff --git a/src/Microsoft.Tye.Hosting/Dashboard/Shared/MetricsDisplay.razor b/src/Microsoft.Tye.Hosting/Dashboard/Shared/MetricsDisplay.razor
new file mode 100644
index 00000000..16ca28d5
--- /dev/null
+++ b/src/Microsoft.Tye.Hosting/Dashboard/Shared/MetricsDisplay.razor
@@ -0,0 +1,74 @@
+@implements IDisposable
+@using System.Threading;
+
+Metrics
+
+
+
+
+
+
+
+
+
+
+ | Metric |
+ Value |
+
+
+ @if (replica is object)
+ {
+
+ @foreach (var metric in replica.Metrics.OrderBy(kvp => kvp.Key))
+ {
+
+ | @metric.Key |
+ @metric.Value |
+
+ }
+
+ }
+ else
+ {
+
+ }
+
+
+
+@code {
+ ReplicaStatus? replica;
+ Timer? timer;
+
+ [Parameter] public Service Service { get; set; } = default!;
+
+ protected override void OnParametersSet()
+ {
+ replica = Service?.Replicas.FirstOrDefault().Value;
+ OnReplicaChanged(replica);
+ }
+
+ void OnReplicaChanged(ReplicaStatus? replica)
+ {
+ this.replica = replica;
+ if (this.replica == null && timer is object)
+ {
+ timer.Dispose();
+ timer = null;
+ }
+
+ if (this.replica != null)
+ {
+ timer ??= new Timer(Timer_Tick, state: null, 1000, 1000);
+ }
+ }
+
+ void Timer_Tick(object? _)
+ {
+ _ = InvokeAsync(() => StateHasChanged());
+ }
+
+ void IDisposable.Dispose()
+ {
+ timer?.Dispose();
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Tye.Hosting/Dashboard/Shared/ReplicaSelector.razor b/src/Microsoft.Tye.Hosting/Dashboard/Shared/ReplicaSelector.razor
new file mode 100644
index 00000000..bebe0a36
--- /dev/null
+++ b/src/Microsoft.Tye.Hosting/Dashboard/Shared/ReplicaSelector.razor
@@ -0,0 +1,55 @@
+@implements IDisposable
+
+
+
+
+@code {
+ IDisposable? subscription;
+ string? selected;
+
+ [Parameter] public Service Service { get; set; } = default!;
+
+ [Parameter] public EventCallback ReplicaChanged { get; set; }
+
+ protected override void OnParametersSet()
+ {
+ subscription?.Dispose();
+ subscription = Service.ReplicaEvents.Subscribe(OnReplicasChanged);
+ }
+
+ void OnReplicasChanged(ReplicaEvent @event)
+ {
+ _ = InvokeAsync(() =>
+ {
+ if (@event.State == ReplicaState.Stopped && @event.Replica.Name == selected)
+ {
+ selected = null;
+ }
+
+ StateHasChanged();
+ });
+ }
+
+ async Task ReplicaSelected(ChangeEventArgs e)
+ {
+ selected = (string?)e.Value;
+ ReplicaStatus? replica = null;
+ if (selected is string)
+ {
+ Service.Replicas.TryGetValue(selected, out replica);
+ }
+
+ await ReplicaChanged.InvokeAsync(replica);
+ }
+
+ void IDisposable.Dispose()
+ {
+ subscription?.Dispose();
+ }
+}
+
diff --git a/src/Microsoft.Tye.Hosting/Model/ReplicaStatus.cs b/src/Microsoft.Tye.Hosting/Model/ReplicaStatus.cs
index 3578ecb4..eee5a084 100644
--- a/src/Microsoft.Tye.Hosting/Model/ReplicaStatus.cs
+++ b/src/Microsoft.Tye.Hosting/Model/ReplicaStatus.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System.Collections.Concurrent;
using System.Collections.Generic;
namespace Microsoft.Tye.Hosting.Model
@@ -22,7 +23,7 @@ namespace Microsoft.Tye.Hosting.Model
public Dictionary