Initial commit: IT Nexus Web-App

This commit is contained in:
2026-06-01 20:49:07 +02:00
commit 8023765e6c
387 changed files with 106900 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
<Window x:Class="ITNexusAgent.UI.AdminLoginDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="IT Nexus Admin-Login"
Width="360" Height="230"
WindowStartupLocation="CenterOwner"
ResizeMode="NoResize" Background="White" FontFamily="Segoe UI">
<StackPanel Margin="24">
<TextBlock Text="Admin-Anmeldung" FontSize="14" FontWeight="Bold"
Foreground="#1F2937" Margin="0,0,0,16"/>
<TextBlock Text="Benutzername" FontSize="11" Foreground="#6B7280" Margin="0,0,0,4"/>
<TextBox x:Name="UsernameBox" Height="32" Padding="8,4" FontSize="12"
BorderBrush="#D1D5DB" Margin="0,0,0,10"/>
<TextBlock Text="Passwort" FontSize="11" Foreground="#6B7280" Margin="0,0,0,4"/>
<PasswordBox x:Name="PasswordBox" Height="32" Padding="8,4" FontSize="12"
BorderBrush="#D1D5DB" Margin="0,0,0,4"
KeyDown="PasswordBox_KeyDown"/>
<TextBlock x:Name="ErrorLabel" FontSize="11" Foreground="#EF4444"
Margin="0,4,0,0" Visibility="Collapsed"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,14,0,0">
<Button Content="Abbrechen" Width="80" Height="32" Margin="0,0,8,0"
Click="Cancel_Click" Background="#F9FAFB" BorderBrush="#D1D5DB" FontSize="12"/>
<Button x:Name="LoginButton" Content="Anmelden" Width="90" Height="32"
Click="Login_Click" Background="#0078D4" Foreground="White"
BorderThickness="0" FontSize="12" FontWeight="SemiBold"/>
</StackPanel>
</StackPanel>
</Window>

View File

@@ -0,0 +1,65 @@
using Newtonsoft.Json;
using System.Net.Http;
using System.Text;
using System.Windows;
using System.Windows.Input;
namespace ITNexusAgent.UI;
public partial class AdminLoginDialog : Window
{
public string Token { get; private set; } = "";
public string Username { get; private set; } = "";
private readonly string _serverUrl;
public AdminLoginDialog(string serverUrl)
{
InitializeComponent();
_serverUrl = serverUrl.TrimEnd('/');
}
private async void Login_Click(object sender, RoutedEventArgs e)
{
var username = UsernameBox.Text.Trim();
var password = PasswordBox.Password;
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password)) return;
LoginButton.IsEnabled = false;
ErrorLabel.Visibility = Visibility.Collapsed;
try
{
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(10) };
var body = JsonConvert.SerializeObject(new { username, password });
var resp = await http.PostAsync($"{_serverUrl}/api/auth/login",
new StringContent(body, Encoding.UTF8, "application/json"));
var json = await resp.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeAnonymousType(json, new { token = "" });
if (resp.IsSuccessStatusCode && !string.IsNullOrEmpty(result?.token))
{
Token = result.token;
Username = username;
DialogResult = true;
}
else
{
ErrorLabel.Text = "Ungültige Anmeldedaten";
ErrorLabel.Visibility = Visibility.Visible;
}
}
catch
{
ErrorLabel.Text = "Verbindung fehlgeschlagen";
ErrorLabel.Visibility = Visibility.Visible;
}
finally { LoginButton.IsEnabled = true; }
}
private void PasswordBox_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == Key.Enter) Login_Click(sender, new RoutedEventArgs());
}
private void Cancel_Click(object sender, RoutedEventArgs e) => DialogResult = false;
}

View File

@@ -0,0 +1,510 @@
<Window x:Class="ITNexusAgent.UI.DashboardWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="IT Nexus Agent"
Icon="pack://application:,,,/icon.ico"
Width="560" Height="640"
WindowStartupLocation="CenterScreen"
ResizeMode="CanResize"
StateChanged="Window_StateChanged"
Background="Transparent"
FontFamily="Segoe UI"
WindowStyle="None"
AllowsTransparency="True"
MinWidth="480" MinHeight="540">
<Window.Resources>
<Style x:Key="Card" TargetType="Border">
<Setter Property="Background" Value="#1C1C1F"/>
<Setter Property="CornerRadius" Value="12"/>
<Setter Property="Padding" Value="16,14"/>
<Setter Property="Margin" Value="0,0,0,10"/>
</Style>
<Style x:Key="BtnAction" TargetType="Button">
<Setter Property="Background" Value="#2A2A2E"/>
<Setter Property="Foreground" Value="#E4E4E7"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="FontFamily" Value="Segoe UI"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="Bd" Background="{TemplateBinding Background}" CornerRadius="9"
BorderThickness="1" BorderBrush="#333337">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Bd" Property="Background" Value="#35353A"/>
<Setter TargetName="Bd" Property="BorderBrush" Value="#4A4A50"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Bd" Property="Background" Value="#1A1A1D"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="BtnPrimary" TargetType="Button" BasedOn="{StaticResource BtnAction}">
<Setter Property="Background" Value="#1D4ED8"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="Bd" Background="{TemplateBinding Background}" CornerRadius="9" BorderThickness="0">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Bd" Property="Background" Value="#2563EB"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Bd" Property="Background" Value="#1E40AF"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="BtnWarn" TargetType="Button" BasedOn="{StaticResource BtnAction}">
<Setter Property="Background" Value="#27180A"/>
<Setter Property="Foreground" Value="#F97316"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="Bd" Background="{TemplateBinding Background}" CornerRadius="9"
BorderThickness="1" BorderBrush="#3D2510">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Bd" Property="Background" Value="#352010"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="BtnGhost" TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="#52525B"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="FontSize" Value="11"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<TextBlock x:Name="T" Text="{TemplateBinding Content}"
Foreground="{TemplateBinding Foreground}"
FontSize="{TemplateBinding FontSize}"
HorizontalAlignment="Center"/>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="T" Property="Foreground" Value="#71717A"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="BtnAdminAction" TargetType="Button" BasedOn="{StaticResource BtnAction}">
<Setter Property="FontSize" Value="11"/>
</Style>
</Window.Resources>
<!-- Outer container -->
<Grid>
<Border x:Name="OuterBorder" Background="#111113" CornerRadius="14" BorderBrush="#2A2A2E" BorderThickness="1"
Margin="16">
<Border.Effect>
<DropShadowEffect Color="Black" Opacity="0.4" BlurRadius="20" ShadowDepth="0"/>
</Border.Effect>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- ═══ TITLEBAR (draggable) ═══ -->
<Border x:Name="TitleBarBorder" Grid.Row="0" Background="#18181B" CornerRadius="14,14,0,0"
MouseLeftButtonDown="TitleBar_MouseDown">
<Grid Margin="18,14,18,14">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel VerticalAlignment="Center">
<TextBlock Text="IT Nexus Agent" FontSize="15" FontWeight="Bold"
Foreground="#FAFAFA"/>
<TextBlock x:Name="HostnameLabel" FontSize="11" Foreground="#52525B"
Margin="0,2,0,0"/>
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center">
<!-- Status -->
<StackPanel Orientation="Horizontal" Margin="0,0,20,0" VerticalAlignment="Center">
<Ellipse x:Name="StatusDot" Width="8" Height="8" Margin="0,0,7,0" VerticalAlignment="Center"/>
<TextBlock x:Name="StatusLabel" FontSize="11" FontWeight="SemiBold" VerticalAlignment="Center"/>
</StackPanel>
<!-- Minimieren -->
<Button Width="32" Height="32" Click="Minimize_Click" ToolTip="Minimieren">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border x:Name="Bd" Background="Transparent" CornerRadius="6">
<TextBlock Text="&#xE921;" FontFamily="Segoe MDL2 Assets"
FontSize="9" Foreground="#52525B"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Bd" Property="Background" Value="#2A2A2E"/>
<Setter Property="Foreground" Value="#A1A1AA"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
<!-- Maximieren -->
<Button Width="32" Height="32" Click="Maximize_Click" ToolTip="Maximieren">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border x:Name="Bd" Background="Transparent" CornerRadius="6">
<TextBlock x:Name="MaxIcon" Text="&#xE922;" FontFamily="Segoe MDL2 Assets"
FontSize="9" Foreground="#52525B"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Bd" Property="Background" Value="#2A2A2E"/>
<Setter TargetName="MaxIcon" Property="Foreground" Value="#A1A1AA"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
<!-- Schließen -->
<Button Width="32" Height="32" Click="Close_Click" ToolTip="Schließen">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border x:Name="Bd" Background="Transparent" CornerRadius="6">
<TextBlock Text="&#xE711;" FontFamily="Segoe MDL2 Assets"
FontSize="9" Foreground="#52525B"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Bd" Property="Background" Value="#C0392B"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
</StackPanel>
</Grid>
</Border>
<!-- ═══ CONTENT ═══ -->
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Hidden"
Background="#111113">
<StackPanel Margin="14,12,14,4">
<!-- Metric Cards -->
<Grid Margin="0,0,0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="8"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="8"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Background="#1C1C1F" CornerRadius="12" Padding="14,12">
<StackPanel>
<TextBlock Text="CPU" FontSize="10" Foreground="#52525B"
FontWeight="SemiBold" Margin="0,0,0,6"/>
<TextBlock x:Name="CpuLabel" FontSize="24" FontWeight="Bold"
Foreground="#FAFAFA" Margin="0,0,0,8"/>
<ProgressBar x:Name="CpuBar" Height="3" BorderThickness="0"
Background="#2A2A2E" Minimum="0" Maximum="100"/>
</StackPanel>
</Border>
<Border Grid.Column="2" Background="#1C1C1F" CornerRadius="12" Padding="14,12">
<StackPanel>
<TextBlock Text="RAM" FontSize="10" Foreground="#52525B"
FontWeight="SemiBold" Margin="0,0,0,6"/>
<TextBlock x:Name="RamLabel" FontSize="14" FontWeight="Bold"
Foreground="#FAFAFA" Margin="0,0,0,8" TextWrapping="Wrap"/>
<ProgressBar x:Name="RamBar" Height="3" BorderThickness="0"
Background="#2A2A2E" Minimum="0" Maximum="100"/>
</StackPanel>
</Border>
<Border Grid.Column="4" Background="#1C1C1F" CornerRadius="12" Padding="14,12">
<StackPanel>
<TextBlock Text="DISK" FontSize="10" Foreground="#52525B"
FontWeight="SemiBold" Margin="0,0,0,6"/>
<TextBlock x:Name="DiskLabel" FontSize="14" FontWeight="Bold"
Foreground="#FAFAFA" Margin="0,0,0,8" TextWrapping="Wrap"/>
<ProgressBar x:Name="DiskBar" Height="3" BorderThickness="0"
Background="#2A2A2E" Minimum="0" Maximum="100"/>
</StackPanel>
</Border>
</Grid>
<!-- Status Card -->
<Border Background="#1C1C1F" CornerRadius="12" Padding="16,14" Margin="0,0,0,10">
<StackPanel>
<Grid Margin="0,0,0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="28"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="&#xE72E;" FontFamily="Segoe MDL2 Assets"
FontSize="13" Foreground="#3F3F46" VerticalAlignment="Center"/>
<TextBlock Grid.Column="1" Text="BitLocker" FontSize="12"
Foreground="#A1A1AA" VerticalAlignment="Center"/>
<TextBlock x:Name="BitlockerLabel" Grid.Column="2" FontSize="12"
FontWeight="SemiBold" VerticalAlignment="Center"/>
</Grid>
<Rectangle Height="1" Fill="#27272A" Margin="0,0,0,10"/>
<Grid Margin="0,0,0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="28"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="&#xEA18;" FontFamily="Segoe MDL2 Assets"
FontSize="13" Foreground="#3F3F46" VerticalAlignment="Center"/>
<TextBlock Grid.Column="1" Text="Microsoft Defender" FontSize="12"
Foreground="#A1A1AA" VerticalAlignment="Center"/>
<TextBlock x:Name="DefenderLabel" Grid.Column="2" FontSize="12"
FontWeight="SemiBold" VerticalAlignment="Center"/>
</Grid>
<Rectangle Height="1" Fill="#27272A" Margin="0,0,0,10"/>
<Grid Margin="0,0,0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="28"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="&#xE895;" FontFamily="Segoe MDL2 Assets"
FontSize="13" Foreground="#3F3F46" VerticalAlignment="Center"/>
<TextBlock Grid.Column="1" Text="Windows Updates" FontSize="12"
Foreground="#A1A1AA" VerticalAlignment="Center"/>
<TextBlock x:Name="UpdatesLabel" Grid.Column="2" FontSize="12"
FontWeight="SemiBold" VerticalAlignment="Center"/>
</Grid>
<Rectangle Height="1" Fill="#27272A" Margin="0,0,0,10"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="28"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="&#xE9D9;" FontFamily="Segoe MDL2 Assets"
FontSize="13" Foreground="#3F3F46" VerticalAlignment="Center"/>
<TextBlock Grid.Column="1" Text="Seriennummer" FontSize="12"
Foreground="#A1A1AA" VerticalAlignment="Center"/>
<TextBlock x:Name="SerialLabel" Grid.Column="2" FontSize="11"
Foreground="#52525B" VerticalAlignment="Center"
FontFamily="Consolas"/>
</Grid>
</StackPanel>
</Border>
<!-- Installierte Software -->
<Border Background="#1C1C1F" CornerRadius="12" Padding="16,14" Margin="0,0,0,10">
<StackPanel>
<Grid Margin="0,0,0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="INSTALLIERTE SOFTWARE" FontSize="10" FontWeight="SemiBold"
Foreground="#52525B" VerticalAlignment="Center"/>
<TextBlock x:Name="SoftwareCountLabel" Grid.Column="1" FontSize="10"
Foreground="#3F3F46" VerticalAlignment="Center"/>
</Grid>
<Border Background="#27272A" CornerRadius="8" BorderBrush="#3F3F46"
BorderThickness="1" Padding="10,0" Margin="0,0,0,8">
<TextBox x:Name="SoftwareSearch" Background="Transparent"
Foreground="#71717A" BorderThickness="0" FontSize="12"
Height="32" VerticalContentAlignment="Center"
CaretBrush="White"
TextChanged="SoftwareSearch_TextChanged"/>
</Border>
<ListBox x:Name="SoftwareList" MaxHeight="200" Background="Transparent"
BorderThickness="0" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Foreground" Value="#A1A1AA"/>
<Setter Property="FontSize" Value="11"/>
<Setter Property="Padding" Value="4,3"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border x:Name="Bd" Background="{TemplateBinding Background}"
CornerRadius="4" Padding="6,3">
<TextBlock Text="{TemplateBinding Content}"
Foreground="{TemplateBinding Foreground}"
FontSize="11" TextTrimming="CharacterEllipsis"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Bd" Property="Background" Value="#27272A"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Bd" Property="Background" Value="#1D4ED820"/>
<Setter Property="Foreground" Value="#FAFAFA"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</StackPanel>
</Border>
<!-- Letzter Check-in -->
<TextBlock x:Name="LastCheckinLabel" FontSize="10" Foreground="#3F3F46"
HorizontalAlignment="Center" Margin="0,0,0,10"/>
<!-- Action Buttons -->
<Button x:Name="WebButton" Height="40" Margin="0,0,0,8"
Content="IT Nexus Web öffnen"
Click="WebButton_Click" Style="{StaticResource BtnPrimary}"
FontSize="12" FontWeight="SemiBold"/>
<Grid Margin="0,0,0,8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="8"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Height="38"
Content="Nachricht senden"
Click="MessageButton_Click"
Style="{StaticResource BtnAction}"
FontSize="11"/>
<Button Grid.Column="2" Height="38"
Content="Neustart anfordern"
Click="RebootButton_Click"
Style="{StaticResource BtnWarn}"
FontSize="11"/>
</Grid>
<!-- Admin -->
<Border Background="#1C1C1F" CornerRadius="12" Padding="14,12" Margin="0,0,0,4">
<StackPanel>
<Button x:Name="AdminLoginButton" Content="Admin-Modus"
Height="26" Click="AdminLoginButton_Click"
Style="{StaticResource BtnGhost}"/>
<StackPanel x:Name="AdminPanel" Visibility="Collapsed" Margin="0,12,0,0">
<TextBlock x:Name="AdminUserLabel" FontSize="11" Foreground="#3B82F6"
FontWeight="SemiBold" Margin="0,0,0,10"
HorizontalAlignment="Center"/>
<UniformGrid Columns="2" Rows="2">
<Button Content="Sofort Check-in" Height="34" Margin="0,0,4,4"
Click="AdminCheckin_Click" Style="{StaticResource BtnAdminAction}"/>
<Button Content="Updates prüfen" Height="34" Margin="4,0,0,4"
Click="AdminUpdates_Click" Style="{StaticResource BtnAdminAction}"/>
<Button Content="Agent-Log" Height="34" Margin="0,0,4,0"
Click="AdminLog_Click" Style="{StaticResource BtnAdminAction}"/>
<Button Content="Neu starten" Height="34" Margin="4,0,0,0"
Click="AdminRestart_Click" Style="{StaticResource BtnAdminAction}"/>
</UniformGrid>
</StackPanel>
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
<!-- ═══ FOOTER ═══ -->
<Border Grid.Row="2" Background="#18181B" CornerRadius="0,0,14,14"
Padding="0,10">
<TextBlock x:Name="VersionLabel" FontSize="10" Foreground="#3F3F46"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</Grid>
</Border>
<!-- ═══ ADMIN LOGIN MODAL OVERLAY ═══ -->
<Border x:Name="AdminOverlay" Visibility="Collapsed"
Background="#80000000" CornerRadius="14">
<Border Background="#1C1C1F" CornerRadius="12" Padding="28,24"
Width="320" VerticalAlignment="Center" HorizontalAlignment="Center">
<Border.Effect>
<DropShadowEffect Color="Black" Opacity="0.6" BlurRadius="30" ShadowDepth="0"/>
</Border.Effect>
<StackPanel>
<TextBlock Text="Admin-Anmeldung" FontSize="15" FontWeight="Bold"
Foreground="#FAFAFA" Margin="0,0,0,4"/>
<TextBlock Text="IT Nexus Zugangsdaten eingeben"
FontSize="11" Foreground="#52525B" Margin="0,0,0,20"/>
<TextBlock Text="Benutzername" FontSize="11" Foreground="#71717A" Margin="0,0,0,6"/>
<Border Background="#27272A" CornerRadius="8" BorderBrush="#3F3F46"
BorderThickness="1" Padding="12,0" Margin="0,0,0,12">
<TextBox x:Name="AdminUsernameBox" Background="Transparent"
Foreground="#FAFAFA" BorderThickness="0" FontSize="13"
Height="36" VerticalContentAlignment="Center"
CaretBrush="White"/>
</Border>
<TextBlock Text="Passwort" FontSize="11" Foreground="#71717A" Margin="0,0,0,6"/>
<Border Background="#27272A" CornerRadius="8" BorderBrush="#3F3F46"
BorderThickness="1" Padding="12,0" Margin="0,0,0,6">
<PasswordBox x:Name="AdminPasswordBox" Background="Transparent"
Foreground="#FAFAFA" BorderThickness="0" FontSize="13"
Height="36" VerticalContentAlignment="Center"
KeyDown="AdminPasswordBox_KeyDown"/>
</Border>
<TextBlock x:Name="AdminErrorLabel" FontSize="11" Foreground="#EF4444"
Margin="0,4,0,0" Visibility="Collapsed"/>
<Grid Margin="0,20,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="8"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Content="Abbrechen" Height="38"
Click="AdminCancelModal_Click"
Style="{StaticResource BtnAction}" FontSize="12"/>
<Button x:Name="AdminLoginBtn" Grid.Column="2" Content="Anmelden" Height="38"
Click="AdminLoginModal_Click"
Style="{StaticResource BtnPrimary}" FontSize="12" FontWeight="SemiBold"/>
</Grid>
</StackPanel>
</Border>
</Border>
</Grid>
</Window>

View File

@@ -0,0 +1,503 @@
using ITNexusAgent.Models;
using ITNexusAgent.Services;
using Newtonsoft.Json;
using System.Diagnostics;
using System.Windows;
using System.Windows.Threading;
using WinForms = System.Windows.Forms;
using DrawingIcon = System.Drawing.Icon;
using DrawingSystemIcons = System.Drawing.SystemIcons;
using MessageBox = System.Windows.MessageBox;
using SolidColorBrush = System.Windows.Media.SolidColorBrush;
using MediaColor = System.Windows.Media.Color;
using ProgressBar = System.Windows.Controls.ProgressBar;
namespace ITNexusAgent.UI;
public partial class DashboardWindow : Window
{
private const string StatusPath = @"C:\ProgramData\IT Nexus Agent\status.json";
private const string LogPath = @"C:\ProgramData\IT Nexus Agent\agent.log";
private readonly DispatcherTimer _timer;
private AgentConfig? _config;
private string? _adminToken;
private WinForms.NotifyIcon? _trayIcon;
private List<string> _allSoftware = [];
private bool _softwarePlaceholderActive = true;
public DashboardWindow()
{
InitializeComponent();
try { _config = AgentConfig.Load(@"C:\ProgramData\IT Nexus Agent\config.json"); }
catch { }
InitTrayIcon();
InitSoftwareSearch();
VersionLabel.Text = $"IT Nexus Agent v2.0.0 · Cereda Systems GmbH";
_timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(5) };
_timer.Tick += (_, _) => RefreshStatus();
_timer.Start();
RefreshStatus();
}
private void InitSoftwareSearch()
{
SoftwareSearch.Text = "Software suchen...";
SoftwareSearch.Foreground = new SolidColorBrush(MediaColor.FromRgb(82, 82, 91));
SoftwareSearch.GotFocus += (_, _) =>
{
if (_softwarePlaceholderActive)
{
_softwarePlaceholderActive = false;
SoftwareSearch.Text = "";
SoftwareSearch.Foreground = new SolidColorBrush(MediaColor.FromRgb(250, 250, 250));
}
};
SoftwareSearch.LostFocus += (_, _) =>
{
if (string.IsNullOrWhiteSpace(SoftwareSearch.Text))
{
_softwarePlaceholderActive = true;
SoftwareSearch.Text = "Software suchen...";
SoftwareSearch.Foreground = new SolidColorBrush(MediaColor.FromRgb(82, 82, 91));
ApplySoftwareFilter("");
}
};
}
private void RefreshStatus()
{
try
{
if (!File.Exists(StatusPath))
{
SetOffline("Noch kein Check-in");
return;
}
var json = File.ReadAllText(StatusPath);
var status = JsonConvert.DeserializeObject<StatusCache>(json);
if (status == null) return;
HostnameLabel.Text = $"{status.Hostname} · {status.LastUser}";
var ago = DateTime.Now - status.LastCheckin;
var agoText = ago.TotalSeconds < 60
? $"vor {(int)ago.TotalSeconds} Sekunden"
: ago.TotalMinutes < 60
? $"vor {(int)ago.TotalMinutes} Minuten"
: $"vor {(int)ago.TotalHours} Stunden";
if (status.Online && ago.TotalMinutes < 3)
{
StatusDot.Fill = new SolidColorBrush(MediaColor.FromRgb(34, 197, 94));
StatusLabel.Text = "Online";
}
else
{
StatusDot.Fill = new SolidColorBrush(MediaColor.FromRgb(239, 68, 68));
StatusLabel.Text = "Offline";
}
// Metrics
var cpu = status.CpuUsage ?? 0;
CpuLabel.Text = $"{cpu:0}%";
CpuBar.Value = cpu;
SetBarColor(CpuBar, cpu);
if (status.RamTotal > 0)
{
var ramPct = (status.RamUsed ?? 0) / status.RamTotal.Value * 100;
RamLabel.Text = $"{status.RamUsed:0.0}/{status.RamTotal:0} GB";
RamBar.Value = ramPct;
SetBarColor(RamBar, ramPct);
}
if (status.DiskTotal > 0)
{
var diskUsed = status.DiskTotal.Value - (status.DiskFree ?? 0);
var diskPct = diskUsed / status.DiskTotal.Value * 100;
DiskLabel.Text = $"{diskUsed:0}/{status.DiskTotal:0} GB";
DiskBar.Value = diskPct;
SetBarColor(DiskBar, diskPct);
}
// Security
BitlockerLabel.Text = status.BitlockerStatus switch
{
"encrypted" => "✓ Verschlüsselt",
"off" => "✗ Nicht aktiv",
_ => "? Unbekannt"
};
BitlockerLabel.Foreground = status.BitlockerStatus == "encrypted"
? new SolidColorBrush(MediaColor.FromRgb(22, 163, 74))
: new SolidColorBrush(MediaColor.FromRgb(239, 68, 68));
DefenderLabel.Text = status.DefenderEnabled
? status.DefenderSignaturesAge <= 3
? "✓ Aktiv · Signaturen aktuell"
: $"⚠ Aktiv · Signaturen {status.DefenderSignaturesAge}d alt"
: "✗ Inaktiv";
DefenderLabel.Foreground = status.DefenderEnabled
? new SolidColorBrush(MediaColor.FromRgb(22, 163, 74))
: new SolidColorBrush(MediaColor.FromRgb(239, 68, 68));
UpdatesLabel.Text = status.PendingUpdates == 0 ? "✓ Aktuell" : $"⚠ {status.PendingUpdates} ausstehend";
UpdatesLabel.Foreground = status.PendingUpdates == 0
? new SolidColorBrush(MediaColor.FromRgb(22, 163, 74))
: new SolidColorBrush(MediaColor.FromRgb(245, 158, 11));
SerialLabel.Text = string.IsNullOrEmpty(status.HardwareSerial) ? "—" : status.HardwareSerial;
LastCheckinLabel.Text = $"Letzter Check-in: {agoText}";
// Software-Liste nur aktualisieren wenn sich die Anzahl geändert hat
if (status.InstalledSoftware.Count != _allSoftware.Count ||
(status.InstalledSoftware.Count > 0 && _allSoftware.Count == 0))
{
_allSoftware = status.InstalledSoftware;
var filterText = _softwarePlaceholderActive ? "" : SoftwareSearch.Text;
ApplySoftwareFilter(filterText);
}
}
catch { SetOffline("Fehler beim Laden"); }
}
private void ApplySoftwareFilter(string filter)
{
var filtered = string.IsNullOrWhiteSpace(filter)
? _allSoftware
: _allSoftware.Where(s => s.Contains(filter, StringComparison.OrdinalIgnoreCase)).ToList();
SoftwareList.ItemsSource = filtered;
SoftwareCountLabel.Text = _allSoftware.Count > 0
? $"{filtered.Count} / {_allSoftware.Count}"
: "";
}
private void SoftwareSearch_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
if (_softwarePlaceholderActive) return;
ApplySoftwareFilter(SoftwareSearch.Text);
}
private void SetOffline(string reason)
{
StatusDot.Fill = new SolidColorBrush(MediaColor.FromRgb(156, 163, 175));
StatusLabel.Text = reason;
}
private static void SetBarColor(System.Windows.Controls.ProgressBar bar, double pct)
{
var color = pct > 90 ? MediaColor.FromRgb(239, 68, 68)
: pct > 70 ? MediaColor.FromRgb(245, 158, 11)
: MediaColor.FromRgb(59, 130, 246);
bar.Foreground = new SolidColorBrush(color);
}
private void WebButton_Click(object sender, RoutedEventArgs e)
{
var url = _config?.ServerUrl ?? "https://it-nexus.cereda-systems.de";
Process.Start(new ProcessStartInfo(url) { UseShellExecute = true });
}
private async void MessageButton_Click(object sender, RoutedEventArgs e)
{
var dlg = new MessageDialog();
if (dlg.ShowDialog() != true || string.IsNullOrWhiteSpace(dlg.Message)) return;
try
{
if (_config != null)
{
var api = new ApiService(_config.ServerUrl, _config.AgentKey);
await api.SendMessageAsync(Environment.MachineName, dlg.Message);
MessageBox.Show("Nachricht wurde gesendet.", "IT Nexus", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
catch
{
MessageBox.Show("Fehler beim Senden.", "IT Nexus", MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
private async void RebootButton_Click(object sender, RoutedEventArgs e)
{
var result = MessageBox.Show(
"Möchtest du einen Neustart bei der IT anfordern?\nDein Rechner wird nicht sofort neu gestartet.",
"Neustart anfordern", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result != MessageBoxResult.Yes) return;
try
{
if (_config != null)
{
var api = new ApiService(_config.ServerUrl, _config.AgentKey);
await api.SendRebootRequestAsync(Environment.MachineName);
MessageBox.Show("Neustart wurde angefordert. Die IT wird sich darum kümmern.",
"IT Nexus", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
catch { }
}
private void AdminLoginButton_Click(object sender, RoutedEventArgs e)
{
if (_adminToken != null)
{
_adminToken = null;
AdminPanel.Visibility = Visibility.Collapsed;
AdminLoginButton.Content = "Admin-Modus";
return;
}
AdminUsernameBox.Text = "";
AdminPasswordBox.Password = "";
AdminErrorLabel.Visibility = Visibility.Collapsed;
AdminOverlay.Visibility = Visibility.Visible;
AdminUsernameBox.Focus();
}
private void AdminCancelModal_Click(object sender, RoutedEventArgs e)
=> AdminOverlay.Visibility = Visibility.Collapsed;
private async void AdminLoginModal_Click(object sender, RoutedEventArgs e)
{
var username = AdminUsernameBox.Text.Trim();
var password = AdminPasswordBox.Password;
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password)) return;
AdminLoginBtn.IsEnabled = false;
AdminErrorLabel.Visibility = Visibility.Collapsed;
try
{
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(10) };
var body = Newtonsoft.Json.JsonConvert.SerializeObject(new { username, password });
var resp = await http.PostAsync($"{_config?.ServerUrl?.TrimEnd('/')}/api/auth/login",
new StringContent(body, Encoding.UTF8, "application/json"));
var json = await resp.Content.ReadAsStringAsync();
var result = Newtonsoft.Json.JsonConvert.DeserializeAnonymousType(json, new { token = "" });
if (resp.IsSuccessStatusCode && !string.IsNullOrEmpty(result?.token))
{
_adminToken = result.token;
AdminOverlay.Visibility = Visibility.Collapsed;
if (_pendingExit)
{
_pendingExit = false;
DoExit();
return;
}
AdminPanel.Visibility = Visibility.Visible;
AdminUserLabel.Text = $"Angemeldet als {username}";
AdminLoginButton.Content = "Admin-Modus beenden";
}
else
{
AdminErrorLabel.Text = "Ungültige Anmeldedaten";
AdminErrorLabel.Visibility = Visibility.Visible;
}
}
catch
{
AdminErrorLabel.Text = "Verbindung fehlgeschlagen";
AdminErrorLabel.Visibility = Visibility.Visible;
}
finally { AdminLoginBtn.IsEnabled = true; }
}
private void AdminPasswordBox_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Enter)
AdminLoginModal_Click(sender, new RoutedEventArgs());
}
private void AdminCheckin_Click(object sender, RoutedEventArgs e)
{
try
{
Process.Start(new ProcessStartInfo("net.exe", "stop \"IT Nexus Agent\"")
{ CreateNoWindow = true, UseShellExecute = false })?.WaitForExit();
Process.Start(new ProcessStartInfo("net.exe", "start \"IT Nexus Agent\"")
{ CreateNoWindow = true, UseShellExecute = false });
MessageBox.Show("Agent wird neu gestartet — Check-in in ~5 Sekunden.",
"IT Nexus", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
MessageBox.Show($"Fehler: {ex.Message}", "IT Nexus", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void AdminUpdates_Click(object sender, RoutedEventArgs e)
{
Process.Start(new ProcessStartInfo("UsoClient.exe", "StartScan") { CreateNoWindow = true });
MessageBox.Show("Update-Scan gestartet.", "IT Nexus", MessageBoxButton.OK, MessageBoxImage.Information);
}
private void AdminLog_Click(object sender, RoutedEventArgs e)
{
var dlg = new LogWindow();
dlg.Show();
}
private void AdminRestart_Click(object sender, RoutedEventArgs e)
{
try
{
Process.Start(new ProcessStartInfo("net.exe", "stop \"IT Nexus Agent\"")
{ CreateNoWindow = true, UseShellExecute = false })?.WaitForExit();
Process.Start(new ProcessStartInfo("net.exe", "start \"IT Nexus Agent\"")
{ CreateNoWindow = true, UseShellExecute = false });
MessageBox.Show("Agent-Service wird neu gestartet.", "IT Nexus", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
MessageBox.Show($"Fehler: {ex.Message}", "IT Nexus", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void InitTrayIcon()
{
try
{
var iconPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "icon.ico");
var icon = File.Exists(iconPath)
? new DrawingIcon(iconPath)
: DrawingSystemIcons.Application;
_trayIcon = new WinForms.NotifyIcon
{
Icon = icon,
Text = "IT Nexus Agent",
Visible = true
};
var menu = new WinForms.ContextMenuStrip();
menu.Items.Add("Dashboard öffnen", null, (s, e) => ShowDashboard());
menu.Items.Add("-");
menu.Items.Add("IT Nexus Web", null, (s, e) =>
{
var url = _config?.ServerUrl ?? "https://it-nexus.cereda-systems.de";
Process.Start(new ProcessStartInfo(url) { UseShellExecute = true });
});
menu.Items.Add("-");
menu.Items.Add("Beenden", null, (s, e) => ExitWithAuth());
_trayIcon.ContextMenuStrip = menu;
_trayIcon.DoubleClick += (s, e) => ShowDashboard();
}
catch { }
}
private void ShowDashboard()
{
Show();
WindowState = WindowState.Normal;
Activate();
Focus();
}
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
e.Cancel = true;
Hide();
}
private void ExitWithAuth()
{
Dispatcher.Invoke(() =>
{
if (_adminToken != null)
{
DoExit();
return;
}
AdminUsernameBox.Text = "";
AdminPasswordBox.Password = "";
AdminErrorLabel.Visibility = Visibility.Collapsed;
AdminOverlay.Visibility = Visibility.Visible;
ShowDashboard();
_pendingExit = true;
AdminUsernameBox.Focus();
});
}
private bool _pendingExit = false;
private void DoExit()
{
_trayIcon!.Visible = false;
System.Windows.Application.Current.Shutdown();
}
private void TitleBar_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
DragMove();
}
private void Close_Click(object sender, RoutedEventArgs e) => Close();
private bool _isMaximized = false;
private double _restoreLeft, _restoreTop, _restoreWidth, _restoreHeight;
private void Minimize_Click(object sender, RoutedEventArgs e)
=> WindowState = WindowState.Minimized;
private void Maximize_Click(object sender, RoutedEventArgs e)
{
if (_isMaximized)
RestoreWindow();
else
MaximizeWindow();
}
private void MaximizeWindow()
{
_restoreLeft = Left; _restoreTop = Top;
_restoreWidth = Width; _restoreHeight = Height;
var wa = SystemParameters.WorkArea;
Left = wa.Left; Top = wa.Top;
Width = wa.Width; Height = wa.Height;
_isMaximized = true;
OuterBorder.CornerRadius = new CornerRadius(0);
OuterBorder.Margin = new Thickness(0);
OuterBorder.Effect = null;
TitleBarBorder.CornerRadius = new CornerRadius(0);
}
private void RestoreWindow()
{
Left = _restoreLeft; Top = _restoreTop;
Width = _restoreWidth; Height = _restoreHeight;
_isMaximized = false;
OuterBorder.CornerRadius = new CornerRadius(14);
OuterBorder.Margin = new Thickness(16);
OuterBorder.Effect = new System.Windows.Media.Effects.DropShadowEffect
{
Color = System.Windows.Media.Colors.Black,
Opacity = 0.4, BlurRadius = 20, ShadowDepth = 0
};
TitleBarBorder.CornerRadius = new CornerRadius(14, 14, 0, 0);
}
private void Window_StateChanged(object sender, EventArgs e) { }
protected override void OnClosed(EventArgs e)
{
_timer.Stop();
base.OnClosed(e);
}
}

View File

@@ -0,0 +1,31 @@
<Window x:Class="ITNexusAgent.UI.LogWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="IT Nexus Agent Log"
Width="700" Height="500"
WindowStartupLocation="CenterScreen"
Background="#1E1E2E" FontFamily="Consolas">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="40"/>
</Grid.RowDefinitions>
<TextBox x:Name="LogText" Grid.Row="0"
Background="#1E1E2E" Foreground="#CDD6F4"
FontSize="11" IsReadOnly="True"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto"
BorderThickness="0" Padding="12"
TextWrapping="NoWrap"/>
<Border Grid.Row="1" Background="#181825" Padding="12,0">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Button Content="Aktualisieren" Height="26" Padding="10,0"
Click="Refresh_Click" Background="#313244" Foreground="#CDD6F4"
BorderThickness="0" FontFamily="Segoe UI" FontSize="11" Cursor="Hand"/>
<Button Content="Log leeren" Height="26" Padding="10,0" Margin="8,0,0,0"
Click="Clear_Click" Background="#313244" Foreground="#F38BA8"
BorderThickness="0" FontFamily="Segoe UI" FontSize="11" Cursor="Hand"/>
</StackPanel>
</Border>
</Grid>
</Window>

View File

@@ -0,0 +1,40 @@
using System.Windows;
using MessageBox = System.Windows.MessageBox;
namespace ITNexusAgent.UI;
public partial class LogWindow : Window
{
private const string LogPath = @"C:\ProgramData\IT Nexus Agent\agent.log";
public LogWindow()
{
InitializeComponent();
LoadLog();
}
private void LoadLog()
{
try
{
if (File.Exists(LogPath))
{
var lines = File.ReadAllLines(LogPath);
LogText.Text = string.Join(Environment.NewLine, lines.TakeLast(500));
LogText.ScrollToEnd();
}
else LogText.Text = "Log-Datei nicht gefunden.";
}
catch (Exception ex) { LogText.Text = $"Fehler: {ex.Message}"; }
}
private void Refresh_Click(object sender, RoutedEventArgs e) => LoadLog();
private void Clear_Click(object sender, RoutedEventArgs e)
{
var r = MessageBox.Show("Log wirklich löschen?", "IT Nexus",
MessageBoxButton.YesNo, MessageBoxImage.Question);
if (r != MessageBoxResult.Yes) return;
try { File.WriteAllText(LogPath, ""); LoadLog(); } catch { }
}
}

View File

@@ -0,0 +1,22 @@
<Window x:Class="ITNexusAgent.UI.MessageDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Nachricht an IT senden"
Width="400" Height="220"
WindowStartupLocation="CenterOwner"
ResizeMode="NoResize" Background="White" FontFamily="Segoe UI">
<StackPanel Margin="20">
<TextBlock Text="Nachricht an das IT-Team" FontSize="13" FontWeight="SemiBold"
Foreground="#1F2937" Margin="0,0,0,12"/>
<TextBox x:Name="MessageBox" Height="80" TextWrapping="Wrap" AcceptsReturn="True"
Padding="8" FontSize="12" BorderBrush="#D1D5DB"
VerticalScrollBarVisibility="Auto"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,14,0,0">
<Button Content="Abbrechen" Width="80" Height="32" Margin="0,0,8,0"
Click="Cancel_Click" Background="#F9FAFB" BorderBrush="#D1D5DB" FontSize="12"/>
<Button Content="Senden" Width="80" Height="32"
Click="Send_Click" Background="#0078D4" Foreground="White"
BorderThickness="0" FontSize="12" FontWeight="SemiBold"/>
</StackPanel>
</StackPanel>
</Window>

View File

@@ -0,0 +1,18 @@
using System.Windows;
namespace ITNexusAgent.UI;
public partial class MessageDialog : Window
{
public string Message { get; private set; } = "";
public MessageDialog() => InitializeComponent();
private void Send_Click(object sender, RoutedEventArgs e)
{
Message = MessageBox.Text.Trim();
DialogResult = true;
}
private void Cancel_Click(object sender, RoutedEventArgs e) => DialogResult = false;
}

View File

@@ -0,0 +1,108 @@
<Window x:Class="ITNexusAgent.UI.NotificationWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="IT Nexus Mitteilung"
Width="500" Height="Auto"
SizeToContent="Height"
WindowStartupLocation="CenterScreen"
ResizeMode="NoResize"
WindowStyle="None"
AllowsTransparency="True"
Background="Transparent"
Topmost="True"
FontFamily="Segoe UI">
<Window.Resources>
<Style x:Key="ConfirmBtn" TargetType="Button">
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Height" Value="46"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="Bg" CornerRadius="8"
Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Bg" Property="Opacity" Value="0.85"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Bg" Property="Opacity" Value="0.7"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<!-- Äußerer Rahmen mit Schatten + farbigem Top-Border -->
<Border CornerRadius="12" Background="#1A1D2E"
BorderBrush="#2D3250" BorderThickness="1">
<Border.Effect>
<DropShadowEffect Color="Black" BlurRadius="32" ShadowDepth="8" Opacity="0.7"/>
</Border.Effect>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="4"/> <!-- Farbige Top-Linie -->
<RowDefinition Height="Auto"/> <!-- Header: Icon + Typ + Titel -->
<RowDefinition Height="Auto"/> <!-- Nachricht -->
<RowDefinition Height="Auto"/> <!-- Meta -->
<RowDefinition Height="Auto"/> <!-- Button -->
</Grid.RowDefinitions>
<!-- Farbige Linie oben (wie Web: border-top) -->
<Border Grid.Row="0" x:Name="TopAccent" CornerRadius="12,12,0,0"/>
<!-- Header -->
<StackPanel Grid.Row="1" Orientation="Horizontal"
Margin="24,20,24,16" VerticalAlignment="Center">
<!-- Icon-Box -->
<Border x:Name="IconBox" Width="44" Height="44" CornerRadius="10"
VerticalAlignment="Top" Margin="0,0,14,0">
<TextBlock x:Name="IconText" FontSize="22"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<!-- Typ + Titel -->
<StackPanel VerticalAlignment="Center">
<TextBlock x:Name="TypeLabel" FontSize="10" FontWeight="Bold"
TextOptions.TextFormattingMode="Display"/>
<TextBlock x:Name="TitleText" FontSize="17" FontWeight="Bold"
Foreground="White" TextWrapping="Wrap" MaxWidth="370"
Margin="0,3,0,0" LineHeight="22"/>
</StackPanel>
</StackPanel>
<!-- Nachricht -->
<Border Grid.Row="2" Margin="24,0,24,16"
Background="#252840" CornerRadius="8"
BorderBrush="#3D4270" BorderThickness="1" Padding="14,12">
<TextBlock x:Name="MessageText" FontSize="13" Foreground="#B0B8D1"
TextWrapping="Wrap" LineHeight="20"/>
</Border>
<!-- Meta -->
<StackPanel Grid.Row="3" Orientation="Horizontal"
Margin="24,0,24,20" Opacity="0.55">
<TextBlock x:Name="MetaText" FontSize="11" Foreground="#8B9BBF"/>
</StackPanel>
<!-- Button + Hinweis -->
<StackPanel Grid.Row="4" Margin="24,0,24,24">
<Button x:Name="ConfirmButton" Style="{StaticResource ConfirmBtn}"
Click="ConfirmButton_Click">
<TextBlock Text="✓ Gelesen und bestätigt" FontSize="13" FontWeight="Bold"/>
</Button>
<TextBlock Text="Du musst diese Nachricht bestätigen um fortzufahren."
FontSize="10" Foreground="#555E7A"
HorizontalAlignment="Center" Margin="0,8,0,0"/>
</StackPanel>
</Grid>
</Border>
</Window>

View File

@@ -0,0 +1,59 @@
using ITNexusAgent.Models;
using ITNexusAgent.Services;
using System.Windows;
using System.Windows.Media;
namespace ITNexusAgent.UI;
public partial class NotificationWindow : Window
{
private readonly Announcement _ann;
private readonly ApiService? _api;
public NotificationWindow(Announcement ann)
{
InitializeComponent();
_ann = ann;
var (hex, label, icon) = ann.Type switch
{
"warning" => ("#DC3545", "WICHTIGE WARNUNG", "⚠️"),
"maintenance" => ("#E67E22", "WARTUNGSANKÜNDIGUNG", "🔧"),
_ => ("#5865F2", "INFORMATION", ""),
};
var color = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(hex);
var brush = new SolidColorBrush(color);
var iconBg = new SolidColorBrush(System.Windows.Media.Color.FromArgb(40, color.R, color.G, color.B));
TopAccent.Background = brush;
IconBox.Background = iconBg;
IconBox.BorderBrush = new SolidColorBrush(System.Windows.Media.Color.FromArgb(80, color.R, color.G, color.B));
IconBox.BorderThickness = new Thickness(1);
IconText.Text = icon;
TypeLabel.Text = label;
TypeLabel.Foreground = brush;
TitleText.Text = ann.Title;
MessageText.Text = ann.Message;
ConfirmButton.Background = brush;
MetaText.Text = $"IT Nexus Mitteilung";
try
{
var cfg = AgentConfig.Load(@"C:\ProgramData\IT Nexus Agent\config.json");
_api = new ApiService(cfg.ServerUrl, cfg.AgentKey);
}
catch { }
}
private async void ConfirmButton_Click(object sender, RoutedEventArgs e)
{
try
{
if (_api != null)
await _api.AckAnnouncementAsync(_ann.Id, Environment.MachineName);
}
catch { }
Close();
}
}