feat: Implement Authentik group synchronization and add confirmation dialogs for service management
This commit is contained in:
27
OTSSignsOrchestrator.Desktop/Views/ConfirmationDialog.axaml
Normal file
27
OTSSignsOrchestrator.Desktop/Views/ConfirmationDialog.axaml
Normal file
@@ -0,0 +1,27 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
x:Class="OTSSignsOrchestrator.Desktop.Views.ConfirmationDialog"
|
||||
Title="Confirm"
|
||||
Width="420" Height="200"
|
||||
MinWidth="320" MinHeight="160"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
CanResize="False"
|
||||
SizeToContent="Height">
|
||||
|
||||
<DockPanel Margin="24">
|
||||
<!-- Buttons -->
|
||||
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal"
|
||||
HorizontalAlignment="Right" Spacing="10" Margin="0,16,0,0">
|
||||
<Button Content="Cancel" Name="CancelButton" Width="90" />
|
||||
<Button Content="Confirm" Name="ConfirmButton" Classes="accent" Width="90" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Message -->
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Name="TitleText" FontSize="16" FontWeight="SemiBold"
|
||||
Foreground="{StaticResource AccentBrush}" />
|
||||
<TextBlock Name="MessageText" FontSize="13" TextWrapping="Wrap"
|
||||
Foreground="{StaticResource TextSecondaryBrush}" />
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
@@ -0,0 +1,50 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
|
||||
namespace OTSSignsOrchestrator.Desktop.Views;
|
||||
|
||||
/// <summary>
|
||||
/// A simple Yes/No confirmation dialog that can be shown modally.
|
||||
/// Use <see cref="ShowAsync"/> for a convenient one-liner.
|
||||
/// </summary>
|
||||
public partial class ConfirmationDialog : Window
|
||||
{
|
||||
public bool Result { get; private set; }
|
||||
|
||||
public ConfirmationDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public ConfirmationDialog(string title, string message) : this()
|
||||
{
|
||||
TitleText.Text = title;
|
||||
MessageText.Text = message;
|
||||
Title = title;
|
||||
|
||||
ConfirmButton.Click += OnConfirmClicked;
|
||||
CancelButton.Click += OnCancelClicked;
|
||||
}
|
||||
|
||||
private void OnConfirmClicked(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
Result = true;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void OnCancelClicked(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
Result = false;
|
||||
Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows a modal confirmation dialog and returns true if the user confirmed.
|
||||
/// </summary>
|
||||
public static async Task<bool> ShowAsync(Window owner, string title, string message)
|
||||
{
|
||||
var dialog = new ConfirmationDialog(title, message);
|
||||
await dialog.ShowDialog(owner);
|
||||
return dialog.Result;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="using:OTSSignsOrchestrator.Desktop.ViewModels"
|
||||
xmlns:dto="using:OTSSignsOrchestrator.Core.Models.DTOs"
|
||||
xmlns:svc="using:OTSSignsOrchestrator.Core.Services"
|
||||
x:Class="OTSSignsOrchestrator.Desktop.Views.InstanceDetailsWindow"
|
||||
x:DataType="vm:InstanceDetailsViewModel"
|
||||
Title="Instance Details"
|
||||
Width="620" Height="740"
|
||||
MinWidth="520" MinHeight="600"
|
||||
Width="620" Height="860"
|
||||
MinWidth="520" MinHeight="700"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
CanResize="True">
|
||||
|
||||
@@ -186,6 +188,52 @@
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- ═══ Stack Services ═══ -->
|
||||
<Border Classes="card">
|
||||
<StackPanel Spacing="8">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8" Margin="0,0,0,4">
|
||||
<Border Width="4" Height="20" CornerRadius="2" Background="#A78BFA" />
|
||||
<TextBlock Text="Stack Services" FontSize="16" FontWeight="SemiBold"
|
||||
Foreground="#A78BFA" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
<TextBlock Text="Force-restart individual services within this stack."
|
||||
FontSize="12" Foreground="{StaticResource TextMutedBrush}" Margin="0,0,0,6"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<!-- Loading indicator -->
|
||||
<TextBlock Text="Loading services..." FontSize="12"
|
||||
Foreground="{StaticResource TextMutedBrush}"
|
||||
IsVisible="{Binding IsLoadingServices}" />
|
||||
|
||||
<!-- Services list -->
|
||||
<ItemsControl ItemsSource="{Binding StackServices}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="svc:ServiceInfo">
|
||||
<Border Background="#232336" CornerRadius="6" Padding="12,10" Margin="0,3"
|
||||
BorderBrush="{StaticResource BorderSubtleBrush}" BorderThickness="1">
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0" Spacing="3">
|
||||
<TextBlock Text="{Binding Name}" FontWeight="SemiBold" FontSize="13" />
|
||||
<TextBlock Text="{Binding Image}" FontSize="11"
|
||||
Foreground="{StaticResource TextMutedBrush}" />
|
||||
<TextBlock Text="{Binding Replicas, StringFormat='Replicas: {0}'}"
|
||||
FontSize="11" Foreground="{StaticResource TextSecondaryBrush}" />
|
||||
</StackPanel>
|
||||
<Button Grid.Column="1" Content="Restart"
|
||||
Command="{Binding $parent[ItemsControl].((vm:InstanceDetailsViewModel)DataContext).RestartServiceCommand}"
|
||||
CommandParameter="{Binding}"
|
||||
IsEnabled="{Binding $parent[ItemsControl].((vm:InstanceDetailsViewModel)DataContext).IsBusy, Converter={x:Static BoolConverters.Not}}"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="12" Padding="10,6"
|
||||
ToolTip.Tip="Force-restart this service" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</DockPanel>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Avalonia.Controls;
|
||||
using OTSSignsOrchestrator.Desktop.ViewModels;
|
||||
|
||||
namespace OTSSignsOrchestrator.Desktop.Views;
|
||||
|
||||
@@ -7,5 +8,15 @@ public partial class InstanceDetailsWindow : Window
|
||||
public InstanceDetailsWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContextChanged += OnDataContextChanged;
|
||||
}
|
||||
|
||||
private void OnDataContextChanged(object? sender, EventArgs e)
|
||||
{
|
||||
if (DataContext is InstanceDetailsViewModel vm)
|
||||
{
|
||||
vm.ConfirmAsync = async (title, message) =>
|
||||
await ConfirmationDialog.ShowAsync(this, title, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="using:OTSSignsOrchestrator.Desktop.ViewModels"
|
||||
xmlns:dto="using:OTSSignsOrchestrator.Core.Models.DTOs"
|
||||
xmlns:svc="using:OTSSignsOrchestrator.Core.Services"
|
||||
x:Class="OTSSignsOrchestrator.Desktop.Views.InstancesView"
|
||||
x:DataType="vm:InstancesViewModel">
|
||||
|
||||
@@ -21,6 +22,9 @@
|
||||
IsEnabled="{Binding !IsBusy}"
|
||||
ToolTip.Tip="View credentials and manage this instance." />
|
||||
<Button Content="Inspect" Command="{Binding InspectInstanceCommand}" />
|
||||
<Button Content="Restart Stack" Command="{Binding RestartStackCommand}"
|
||||
IsEnabled="{Binding !IsBusy}"
|
||||
ToolTip.Tip="Force-restart all services in the selected stack." />
|
||||
<Button Content="Delete" Classes="danger" Command="{Binding DeleteInstanceCommand}" />
|
||||
<Border Width="1" Background="{StaticResource BorderSubtleBrush}" Margin="4,2" />
|
||||
<Button Content="Rotate DB Password" Command="{Binding RotateMySqlPasswordCommand}"
|
||||
@@ -69,16 +73,25 @@
|
||||
Foreground="{StaticResource AccentBrush}" Margin="0,0,0,10" />
|
||||
<ItemsControl ItemsSource="{Binding SelectedServices}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<DataTemplate x:DataType="svc:ServiceInfo">
|
||||
<Border Background="#232336" CornerRadius="6" Padding="12,10" Margin="0,3"
|
||||
BorderBrush="{StaticResource BorderSubtleBrush}" BorderThickness="1">
|
||||
<StackPanel Spacing="3">
|
||||
<TextBlock Text="{Binding Name}" FontWeight="SemiBold" FontSize="13" />
|
||||
<TextBlock Text="{Binding Image}" FontSize="11"
|
||||
Foreground="{StaticResource TextMutedBrush}" />
|
||||
<TextBlock Text="{Binding Replicas, StringFormat='Replicas: {0}'}"
|
||||
FontSize="11" Foreground="{StaticResource TextSecondaryBrush}" />
|
||||
</StackPanel>
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0" Spacing="3">
|
||||
<TextBlock Text="{Binding Name}" FontWeight="SemiBold" FontSize="13" />
|
||||
<TextBlock Text="{Binding Image}" FontSize="11"
|
||||
Foreground="{StaticResource TextMutedBrush}" />
|
||||
<TextBlock Text="{Binding Replicas, StringFormat='Replicas: {0}'}"
|
||||
FontSize="11" Foreground="{StaticResource TextSecondaryBrush}" />
|
||||
</StackPanel>
|
||||
<Button Grid.Column="1" Content="Restart"
|
||||
Command="{Binding $parent[ItemsControl].((vm:InstancesViewModel)DataContext).RestartServiceCommand}"
|
||||
CommandParameter="{Binding}"
|
||||
IsEnabled="{Binding $parent[ItemsControl].((vm:InstancesViewModel)DataContext).IsBusy, Converter={x:Static BoolConverters.Not}}"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="12" Padding="10,6"
|
||||
ToolTip.Tip="Force-restart this service" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
|
||||
@@ -21,7 +21,17 @@ public partial class InstancesView : UserControl
|
||||
_vm = DataContext as InstancesViewModel;
|
||||
|
||||
if (_vm is not null)
|
||||
{
|
||||
_vm.OpenDetailsRequested += OnOpenDetailsRequested;
|
||||
_vm.ConfirmAsync = ShowConfirmationAsync;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> ShowConfirmationAsync(string title, string message)
|
||||
{
|
||||
var owner = TopLevel.GetTopLevel(this) as Window;
|
||||
if (owner is null) return false;
|
||||
return await ConfirmationDialog.ShowAsync(owner, title, message);
|
||||
}
|
||||
|
||||
private async void OnOpenDetailsRequested(InstanceDetailsViewModel detailsVm)
|
||||
|
||||
Reference in New Issue
Block a user