Redesign Client Settings Into Tabs

Reorganizes the settings screen into General, Mode & Connectivity, and Data Sync tabs while keeping the shared bottom action bar.
feature/centralized-offline-canteen
SYED MUSTUFA AHMED NAQVI 2026-06-02 14:32:01 +05:00
parent ca7e388989
commit 3e91cfaba8
2 changed files with 300 additions and 251 deletions

View File

@ -222,11 +222,12 @@ public partial class ClientSettingsViewModel : ObservableObject
_configService.SetScanIntervalMinutes(minutes);
_configService.SetScanIntervalSeconds(seconds);
_configService.SetAdminCardId(AdminCardId?.Trim() ?? string.Empty);
FileLogger.Info(
"ClientSettings",
$"Settings saved. AdminCardId={AdminCardId?.Trim()}, SiteId={SiteId?.Trim()}, DeviceId={DeviceId?.Trim()}");
_configService.SetCentralServerBaseUrl(BackendBaseUrl.Trim());
_configService.SetDeviceId(DeviceId?.Trim() ?? string.Empty);
DeviceId = _configService.GetDeviceId();
FileLogger.Info(
"ClientSettings",
$"Settings saved. AdminCardId={AdminCardId?.Trim()}, SiteId={SiteId?.Trim()}, DeviceId={DeviceId}");
var site = SiteId?.Trim() ?? string.Empty;
_configService.SetSiteId(
string.IsNullOrEmpty(site)

View File

@ -72,275 +72,323 @@
<Setter Property="Padding" Value="20"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
</Style>
<Style x:Key="SettingsTabItemStyle" TargetType="TabItem">
<Setter Property="Foreground" Value="{StaticResource MutedText}"/>
<Setter Property="FontSize" Value="15"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Padding" Value="18,10"/>
<Setter Property="Margin" Value="0,0,8,0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Border x:Name="TabBorder"
Background="Transparent"
BorderBrush="Transparent"
BorderThickness="0,0,0,3"
Padding="{TemplateBinding Padding}">
<ContentPresenter ContentSource="Header"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="TabBorder" Property="Background" Value="#F7FAFC"/>
<Setter Property="Foreground" Value="{StaticResource PrimaryText}"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="TabBorder" Property="Background" Value="#E6F4F3"/>
<Setter TargetName="TabBorder" Property="BorderBrush" Value="{StaticResource PrimaryAccent}"/>
<Setter Property="Foreground" Value="{StaticResource PrimaryAccent}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid Background="{StaticResource AppBackground}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Padding="32,24,32,16">
<Grid HorizontalAlignment="Stretch"
Width="{Binding ViewportWidth, RelativeSource={RelativeSource AncestorType=ScrollViewer}}"
MinWidth="320">
<StackPanel HorizontalAlignment="Stretch">
<!-- Page header -->
<StackPanel Orientation="Horizontal" Margin="0,0,0,8">
<Border Width="48" Height="48" Background="#E6F4F3" CornerRadius="24" Margin="0,0,16,0">
<Viewbox Margin="10">
<Canvas Width="24" Height="24">
<Path Data="M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11.03L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.26C16.04,5.86 15.48,5.51 14.87,5.23L14.5,2.58C14.46,2.34 14.25,2.17 14,2.17H10C9.75,2.17 9.54,2.34 9.5,2.58L9.13,5.23C8.52,5.51 7.96,5.86 7.44,6.26L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.22,8.95 2.27,9.22 2.46,9.37L4.57,11.03C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.22,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.74C7.96,18.14 8.52,18.49 9.13,18.77L9.5,21.42C9.54,21.66 9.75,21.83 10,21.83H14C14.25,21.83 14.46,21.66 14.5,21.42L14.87,18.77C15.48,18.49 16.04,18.14 16.56,17.74L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z"
Fill="{StaticResource PrimaryAccent}" Stretch="Uniform"/>
</Canvas>
</Viewbox>
</Border>
<StackPanel VerticalAlignment="Center">
<TextBlock Text="Settings" FontSize="32" FontWeight="Bold" Foreground="{StaticResource PrimaryText}"/>
<TextBlock Text="Configure scanning, access, connectivity, and sync settings."
FontSize="15" Foreground="{StaticResource MutedText}" Margin="0,4,0,0"/>
</StackPanel>
</StackPanel>
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="32,24,32,12">
<Border Width="48" Height="48" Background="#E6F4F3" CornerRadius="24" Margin="0,0,16,0">
<Viewbox Margin="10">
<Canvas Width="24" Height="24">
<Path Data="M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11.03L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.26C16.04,5.86 15.48,5.51 14.87,5.23L14.5,2.58C14.46,2.34 14.25,2.17 14,2.17H10C9.75,2.17 9.54,2.34 9.5,2.58L9.13,5.23C8.52,5.51 7.96,5.86 7.44,6.26L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.22,8.95 2.27,9.22 2.46,9.37L4.57,11.03C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.22,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.74C7.96,18.14 8.52,18.49 9.13,18.77L9.5,21.42C9.54,21.66 9.75,21.83 10,21.83H14C14.25,21.83 14.46,21.66 14.5,21.42L14.87,18.77C15.48,18.49 16.04,18.14 16.56,17.74L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z"
Fill="{StaticResource PrimaryAccent}" Stretch="Uniform"/>
</Canvas>
</Viewbox>
</Border>
<StackPanel VerticalAlignment="Center">
<TextBlock Text="Settings" FontSize="32" FontWeight="Bold" Foreground="{StaticResource PrimaryText}"/>
<TextBlock Text="Configure scanning, access, connectivity, and sync settings."
FontSize="15" Foreground="{StaticResource MutedText}" Margin="0,4,0,0"/>
</StackPanel>
</StackPanel>
<!-- 1. Scan Interval -->
<Border Style="{StaticResource SectionCardStyle}">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,12">
<Border Width="36" Height="36" Background="#E6F4F3" CornerRadius="18" Margin="0,0,12,0">
<TextBlock Text="&#xE121;" FontFamily="Segoe MDL2 Assets" FontSize="16"
Foreground="{StaticResource PrimaryAccent}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<StackPanel VerticalAlignment="Center">
<TextBlock Text="Scan Interval" FontSize="20" FontWeight="Bold" Foreground="{StaticResource PrimaryText}"/>
<TextBlock Text="Set the minimum time between scans to prevent duplicates."
FontSize="14" Foreground="{StaticResource MutedText}"/>
</StackPanel>
</StackPanel>
<Grid Margin="0,8,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/><ColumnDefinition Width="12"/>
<ColumnDefinition Width="*"/><ColumnDefinition Width="12"/>
<ColumnDefinition Width="*"/><ColumnDefinition Width="12"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Text="Days (0365)" FontSize="13" Foreground="{StaticResource MutedText}" Margin="0,0,0,4"/>
<TextBox Text="{Binding ScanIntervalDays, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource ModernTextBoxStyle}"/>
</StackPanel>
<StackPanel Grid.Column="2">
<TextBlock Text="Hours (023)" FontSize="13" Foreground="{StaticResource MutedText}" Margin="0,0,0,4"/>
<TextBox Text="{Binding ScanIntervalHours, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource ModernTextBoxStyle}"/>
</StackPanel>
<StackPanel Grid.Column="4">
<TextBlock Text="Minutes (059)" FontSize="13" Foreground="{StaticResource MutedText}" Margin="0,0,0,4"/>
<TextBox Text="{Binding ScanIntervalMinutes, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource ModernTextBoxStyle}"/>
</StackPanel>
<StackPanel Grid.Column="6">
<TextBlock Text="Seconds (059)" FontSize="13" Foreground="{StaticResource MutedText}" Margin="0,0,0,4"/>
<TextBox Text="{Binding ScanIntervalSeconds, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource ModernTextBoxStyle}"/>
</StackPanel>
</Grid>
<StackPanel Orientation="Horizontal" Margin="0,12,0,0">
<TextBlock Text="&#xE946;" FontFamily="Segoe MDL2 Assets" FontSize="14"
Foreground="{StaticResource MutedText}" Margin="0,2,8,0"/>
<TextBlock Text="The minimum time between scans to prevent duplicates. At least 1 second required."
FontSize="13" Foreground="{StaticResource MutedText}" TextWrapping="Wrap"/>
</StackPanel>
</StackPanel>
</Border>
<TabControl Grid.Row="1"
Margin="32,0,32,0"
Background="Transparent"
BorderThickness="0"
ItemContainerStyle="{StaticResource SettingsTabItemStyle}">
<TabControl.Resources>
<Style TargetType="TabPanel">
<Setter Property="Margin" Value="0,0,0,16"/>
</Style>
</TabControl.Resources>
<!-- 2. Admin Access -->
<Border Style="{StaticResource SectionCardStyle}">
<TabItem Header="General">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,12">
<Border Width="36" Height="36" Background="#E6F4F3" CornerRadius="18" Margin="0,0,12,0">
<TextBlock Text="&#xE77B;" FontFamily="Segoe MDL2 Assets" FontSize="16"
Foreground="{StaticResource PrimaryAccent}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<TextBlock Text="Admin Access" FontSize="20" FontWeight="Bold" Foreground="{StaticResource PrimaryText}" VerticalAlignment="Center"/>
</StackPanel>
<TextBlock Text="Admin Employee ID" FontSize="14" FontWeight="SemiBold" Foreground="{StaticResource PrimaryText}"/>
<TextBox Text="{Binding AdminCardId, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource ModernTextBoxStyle}" Margin="0,8,0,4"/>
<TextBlock Text="Card ID with admin access to settings." FontSize="13" Foreground="{StaticResource MutedText}"/>
</StackPanel>
</Border>
<!-- 3. Application Mode & Connectivity -->
<Border Style="{StaticResource SectionCardStyle}">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,12">
<Border Width="36" Height="36" Background="#E6F4F3" CornerRadius="18" Margin="0,0,12,0">
<TextBlock Text="&#xE968;" FontFamily="Segoe MDL2 Assets" FontSize="16"
Foreground="{StaticResource PrimaryAccent}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<StackPanel VerticalAlignment="Center">
<TextBlock Text="Application Mode &amp; Connectivity" FontSize="20" FontWeight="Bold" Foreground="{StaticResource PrimaryText}"/>
<TextBlock Text="Configure how this application connects to servers."
FontSize="14" Foreground="{StaticResource MutedText}"/>
<Border Style="{StaticResource SectionCardStyle}">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,12">
<Border Width="36" Height="36" Background="#E6F4F3" CornerRadius="18" Margin="0,0,12,0">
<TextBlock Text="&#xE121;" FontFamily="Segoe MDL2 Assets" FontSize="16"
Foreground="{StaticResource PrimaryAccent}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<StackPanel VerticalAlignment="Center">
<TextBlock Text="Scan Interval" FontSize="20" FontWeight="Bold" Foreground="{StaticResource PrimaryText}"/>
<TextBlock Text="Set the minimum time between scans to prevent duplicates."
FontSize="14" Foreground="{StaticResource MutedText}"/>
</StackPanel>
</StackPanel>
<Grid Margin="0,8,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/><ColumnDefinition Width="12"/>
<ColumnDefinition Width="*"/><ColumnDefinition Width="12"/>
<ColumnDefinition Width="*"/><ColumnDefinition Width="12"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Text="Days (0-365)" FontSize="13" Foreground="{StaticResource MutedText}" Margin="0,0,0,4"/>
<TextBox Text="{Binding ScanIntervalDays, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource ModernTextBoxStyle}"/>
</StackPanel>
<StackPanel Grid.Column="2">
<TextBlock Text="Hours (0-23)" FontSize="13" Foreground="{StaticResource MutedText}" Margin="0,0,0,4"/>
<TextBox Text="{Binding ScanIntervalHours, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource ModernTextBoxStyle}"/>
</StackPanel>
<StackPanel Grid.Column="4">
<TextBlock Text="Minutes (0-59)" FontSize="13" Foreground="{StaticResource MutedText}" Margin="0,0,0,4"/>
<TextBox Text="{Binding ScanIntervalMinutes, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource ModernTextBoxStyle}"/>
</StackPanel>
<StackPanel Grid.Column="6">
<TextBlock Text="Seconds (0-59)" FontSize="13" Foreground="{StaticResource MutedText}" Margin="0,0,0,4"/>
<TextBox Text="{Binding ScanIntervalSeconds, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource ModernTextBoxStyle}"/>
</StackPanel>
</Grid>
<StackPanel Orientation="Horizontal" Margin="0,12,0,0">
<TextBlock Text="&#xE946;" FontFamily="Segoe MDL2 Assets" FontSize="14"
Foreground="{StaticResource MutedText}" Margin="0,2,8,0"/>
<TextBlock Text="The minimum time between scans to prevent duplicates. At least 1 second required."
FontSize="13" Foreground="{StaticResource MutedText}" TextWrapping="Wrap"/>
</StackPanel>
</StackPanel>
</StackPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="24"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="24"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Row="0" Grid.Column="0">
<TextBlock Text="Application Mode" FontSize="14" FontWeight="SemiBold" Foreground="{StaticResource PrimaryText}"/>
<TextBox Text="{Binding ApplicationModeDisplay}" IsReadOnly="True" IsTabStop="False"
</Border>
<Border Style="{StaticResource SectionCardStyle}">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,12">
<Border Width="36" Height="36" Background="#E6F4F3" CornerRadius="18" Margin="0,0,12,0">
<TextBlock Text="&#xE77B;" FontFamily="Segoe MDL2 Assets" FontSize="16"
Foreground="{StaticResource PrimaryAccent}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<TextBlock Text="Admin Access" FontSize="20" FontWeight="Bold" Foreground="{StaticResource PrimaryText}" VerticalAlignment="Center"/>
</StackPanel>
<TextBlock Text="Admin Employee ID" FontSize="14" FontWeight="SemiBold" Foreground="{StaticResource PrimaryText}"/>
<TextBox Text="{Binding AdminCardId, UpdateSourceTrigger=PropertyChanged}"
IsReadOnly="True"
IsTabStop="False"
Style="{StaticResource ModernTextBoxStyle}" Margin="0,8,0,4" Background="#F8FAFC"/>
<TextBlock Text="Client: this PC sends scans to the central backend server."
FontSize="12" Foreground="{StaticResource MutedText}" TextWrapping="Wrap"/>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="2">
<TextBlock Text="Backend Server Base URL" FontSize="14" FontWeight="SemiBold" Foreground="{StaticResource PrimaryText}"/>
<TextBox Text="{Binding BackendBaseUrl, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource ModernTextBoxStyle}" Margin="0,8,0,4"/>
<TextBlock Text="Example: http://192.168.1.10:5000" FontSize="12" Foreground="{StaticResource MutedText}"/>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="4">
<TextBlock Text="Device ID" FontSize="14" FontWeight="SemiBold" Foreground="{StaticResource PrimaryText}"/>
<TextBox Text="{Binding DeviceId, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource ModernTextBoxStyle}" Margin="0,8,0,4"/>
<TextBlock Text="Identifies this scanner PC in scan records." FontSize="12" Foreground="{StaticResource MutedText}"/>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Margin="0,16,0,0">
<TextBlock Text="Site ID" FontSize="14" FontWeight="SemiBold" Foreground="{StaticResource PrimaryText}"/>
<TextBox Text="{Binding SiteId, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource ModernTextBoxStyle}" Margin="0,8,0,4" MaxWidth="480" HorizontalAlignment="Left"/>
<TextBlock Text="Canteen location site code sent with each scan." FontSize="12" Foreground="{StaticResource MutedText}"/>
</StackPanel>
</Grid>
<Border Margin="0,16,0,0" Padding="12" CornerRadius="8" Background="#EDF2F7">
<StackPanel Orientation="Horizontal">
<Ellipse Width="10" Height="10" Margin="0,4,10,0" VerticalAlignment="Top">
<Ellipse.Style>
<Style TargetType="Ellipse">
<Setter Property="Fill" Value="{StaticResource ErrorBrush}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsBackendOnline}" Value="True">
<Setter Property="Fill" Value="{StaticResource SuccessBrush}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
<TextBlock Text="{Binding BackendHealthDisplay}" FontSize="13" Foreground="{StaticResource PrimaryText}" TextWrapping="Wrap"/>
<TextBlock Text="Card ID with admin access to settings." FontSize="13" Foreground="{StaticResource MutedText}"/>
</StackPanel>
</Border>
</StackPanel>
</Border>
</ScrollViewer>
</TabItem>
<!-- 4. Data Sync -->
<Border Style="{StaticResource SectionCardStyle}" Margin="0,0,0,8">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,16">
<Border Width="36" Height="36" Background="#E6F4F3" CornerRadius="18" Margin="0,0,12,0">
<TextBlock Text="&#xE895;" FontFamily="Segoe MDL2 Assets" FontSize="16"
Foreground="{StaticResource PrimaryAccent}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<StackPanel VerticalAlignment="Center">
<TextBlock Text="Data Sync" FontSize="20" FontWeight="Bold" Foreground="{StaticResource PrimaryText}"/>
<TextBlock Text="Keep your offline data up to date and send transactions to production."
FontSize="14" Foreground="{StaticResource MutedText}"/>
<TabItem Header="Mode &amp; Connectivity">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<Border Style="{StaticResource SectionCardStyle}">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,12">
<Border Width="36" Height="36" Background="#E6F4F3" CornerRadius="18" Margin="0,0,12,0">
<TextBlock Text="&#xE968;" FontFamily="Segoe MDL2 Assets" FontSize="16"
Foreground="{StaticResource PrimaryAccent}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<StackPanel VerticalAlignment="Center">
<TextBlock Text="Application Mode &amp; Connectivity" FontSize="20" FontWeight="Bold" Foreground="{StaticResource PrimaryText}"/>
<TextBlock Text="Configure how this application connects to servers."
FontSize="14" Foreground="{StaticResource MutedText}"/>
</StackPanel>
</StackPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="24"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="24"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Row="0" Grid.Column="0">
<TextBlock Text="Application Mode" FontSize="14" FontWeight="SemiBold" Foreground="{StaticResource PrimaryText}"/>
<TextBox Text="{Binding ApplicationModeDisplay}" IsReadOnly="True" IsTabStop="False"
Style="{StaticResource ModernTextBoxStyle}" Margin="0,8,0,4" Background="#F8FAFC"/>
<TextBlock Text="Client: this PC sends scans to the central backend server."
FontSize="12" Foreground="{StaticResource MutedText}" TextWrapping="Wrap"/>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="2">
<TextBlock Text="Backend Server Base URL" FontSize="14" FontWeight="SemiBold" Foreground="{StaticResource PrimaryText}"/>
<TextBox Text="{Binding BackendBaseUrl, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource ModernTextBoxStyle}" Margin="0,8,0,4"/>
<TextBlock Text="Example: http://192.168.1.10:5000" FontSize="12" Foreground="{StaticResource MutedText}"/>
</StackPanel>
<StackPanel Grid.Row="0" Grid.Column="4">
<TextBlock Text="Device ID" FontSize="14" FontWeight="SemiBold" Foreground="{StaticResource PrimaryText}"/>
<TextBox Text="{Binding DeviceId, UpdateSourceTrigger=PropertyChanged}"
IsReadOnly="True"
IsTabStop="False"
Style="{StaticResource ModernTextBoxStyle}" Margin="0,8,0,4" Background="#F8FAFC"/>
<TextBlock Text="Identifies this scanner PC in scan records." FontSize="12" Foreground="{StaticResource MutedText}"/>
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Margin="0,16,0,0">
<TextBlock Text="Site ID" FontSize="14" FontWeight="SemiBold" Foreground="{StaticResource PrimaryText}"/>
<TextBox Text="{Binding SiteId, UpdateSourceTrigger=PropertyChanged}"
IsReadOnly="True"
IsTabStop="False"
Style="{StaticResource ModernTextBoxStyle}" Margin="0,8,0,4" MaxWidth="480" HorizontalAlignment="Left" Background="#F8FAFC"/>
<TextBlock Text="Canteen location site code sent with each scan." FontSize="12" Foreground="{StaticResource MutedText}"/>
</StackPanel>
</Grid>
<Border Margin="0,16,0,0" Padding="12" CornerRadius="8" Background="#EDF2F7">
<StackPanel Orientation="Horizontal">
<Ellipse Width="10" Height="10" Margin="0,4,10,0" VerticalAlignment="Top">
<Ellipse.Style>
<Style TargetType="Ellipse">
<Setter Property="Fill" Value="{StaticResource ErrorBrush}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsBackendOnline}" Value="True">
<Setter Property="Fill" Value="{StaticResource SuccessBrush}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
<TextBlock Text="{Binding BackendHealthDisplay}" FontSize="13" Foreground="{StaticResource PrimaryText}" TextWrapping="Wrap"/>
</StackPanel>
</Border>
</StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/><ColumnDefinition Width="16"/><ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Download Offline Data -->
<Border Grid.Column="0" Style="{StaticResource SyncSubCardStyle}">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,8">
<TextBlock Text="&#xE896;" FontFamily="Segoe MDL2 Assets" FontSize="18"
Foreground="{StaticResource PrimaryAccent}" Margin="0,0,8,0"/>
<TextBlock Text="Download Offline Data" FontSize="17" FontWeight="SemiBold" Foreground="{StaticResource PrimaryText}"/>
</StackPanel>
<TextBlock Text="Download employee RFID tags, meal schedules, and menu data for offline scanning."
FontSize="13" Foreground="{StaticResource MutedText}" TextWrapping="Wrap" Margin="0,0,0,16"/>
<Button Content="{Binding SyncCacheButtonText}"
Command="{Binding SyncEmployeeAndMenuCacheNowCommand}"
Style="{StaticResource PrimaryButtonStyle}"
HorizontalAlignment="Left"
IsEnabled="{Binding CanSyncCacheNow}"/>
</StackPanel>
</Border>
<!-- Post Data to Production -->
<Border Grid.Column="2" Style="{StaticResource SyncSubCardStyle}">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,8">
<TextBlock Text="&#xE898;" FontFamily="Segoe MDL2 Assets" FontSize="18"
Foreground="{StaticResource OrangeAccent}" Margin="0,0,8,0"/>
<TextBlock Text="Post Data to Production" FontSize="17" FontWeight="SemiBold" Foreground="{StaticResource PrimaryText}"/>
</StackPanel>
<TextBlock Text="Post captured scan and transaction data to the production server."
FontSize="13" Foreground="{StaticResource MutedText}" TextWrapping="Wrap" Margin="0,0,0,16"/>
<Button Content="{Binding PostButtonText}"
Command="{Binding PostDataNowCommand}"
Style="{StaticResource PrimaryButtonStyle}"
HorizontalAlignment="Left"
IsEnabled="{Binding CanPostNow}"/>
</StackPanel>
</Border>
</Grid>
<TextBlock Text="{Binding LastEmployeeRfidCacheSyncDisplay, StringFormat=Last employee RFID cache sync: {0}}"
FontSize="13" Foreground="{StaticResource MutedText}" Margin="0,16,0,4"/>
<TextBlock Text="{Binding LastMealMenuCacheSyncDisplay, StringFormat=Last meal/menu cache sync: {0}}"
FontSize="13" Foreground="{StaticResource MutedText}"/>
</StackPanel>
</Border>
</Border>
</ScrollViewer>
</TabItem>
<!-- Status message -->
<Border Padding="14" CornerRadius="8" Margin="0,0,0,8">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#dcfce7"/>
<Setter Property="BorderBrush" Value="#86efac"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding SaveMessage}" Value="">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsError}" Value="True">
<Setter Property="Background" Value="#fee2e2"/>
<Setter Property="BorderBrush" Value="#fca5a5"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="{Binding SaveMessage}" FontSize="15" FontWeight="SemiBold" TextWrapping="Wrap">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource SuccessBrush}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsError}" Value="True">
<Setter Property="Foreground" Value="{StaticResource ErrorBrush}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Border>
</StackPanel>
</Grid>
</ScrollViewer>
<TabItem Header="Data Sync">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<Border Style="{StaticResource SectionCardStyle}" Margin="0,0,0,8">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,16">
<Border Width="36" Height="36" Background="#E6F4F3" CornerRadius="18" Margin="0,0,12,0">
<TextBlock Text="&#xE895;" FontFamily="Segoe MDL2 Assets" FontSize="16"
Foreground="{StaticResource PrimaryAccent}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<StackPanel VerticalAlignment="Center">
<TextBlock Text="Data Sync" FontSize="20" FontWeight="Bold" Foreground="{StaticResource PrimaryText}"/>
<TextBlock Text="Keep your offline data up to date and send transactions to production."
FontSize="14" Foreground="{StaticResource MutedText}"/>
</StackPanel>
</StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/><ColumnDefinition Width="16"/><ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Style="{StaticResource SyncSubCardStyle}">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,8">
<TextBlock Text="&#xE896;" FontFamily="Segoe MDL2 Assets" FontSize="18"
Foreground="{StaticResource PrimaryAccent}" Margin="0,0,8,0"/>
<TextBlock Text="Download Offline Data" FontSize="17" FontWeight="SemiBold" Foreground="{StaticResource PrimaryText}"/>
</StackPanel>
<TextBlock Text="Download employee RFID tags, meal schedules, and menu data for offline scanning."
FontSize="13" Foreground="{StaticResource MutedText}" TextWrapping="Wrap" Margin="0,0,0,16"/>
<Button Content="{Binding SyncCacheButtonText}"
Command="{Binding SyncEmployeeAndMenuCacheNowCommand}"
Style="{StaticResource PrimaryButtonStyle}"
HorizontalAlignment="Left"
IsEnabled="{Binding CanSyncCacheNow}"/>
</StackPanel>
</Border>
<Border Grid.Column="2" Style="{StaticResource SyncSubCardStyle}">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,8">
<TextBlock Text="&#xE898;" FontFamily="Segoe MDL2 Assets" FontSize="18"
Foreground="{StaticResource OrangeAccent}" Margin="0,0,8,0"/>
<TextBlock Text="Post Data to Production" FontSize="17" FontWeight="SemiBold" Foreground="{StaticResource PrimaryText}"/>
</StackPanel>
<TextBlock Text="Post captured scan and transaction data to the production server."
FontSize="13" Foreground="{StaticResource MutedText}" TextWrapping="Wrap" Margin="0,0,0,16"/>
<Button Content="{Binding PostButtonText}"
Command="{Binding PostDataNowCommand}"
Style="{StaticResource PrimaryButtonStyle}"
HorizontalAlignment="Left"
IsEnabled="{Binding CanPostNow}"/>
</StackPanel>
</Border>
</Grid>
<TextBlock Text="{Binding LastEmployeeRfidCacheSyncDisplay, StringFormat=Last employee RFID cache sync: {0}}"
FontSize="13" Foreground="{StaticResource MutedText}" Margin="0,16,0,4"/>
<TextBlock Text="{Binding LastMealMenuCacheSyncDisplay, StringFormat=Last meal/menu cache sync: {0}}"
FontSize="13" Foreground="{StaticResource MutedText}"/>
</StackPanel>
</Border>
</ScrollViewer>
</TabItem>
</TabControl>
<!-- Sticky bottom action bar (full width) -->
<Border Grid.Row="1" Background="#F9FBFB" BorderBrush="{StaticResource BorderBrush}" BorderThickness="0,1,0,0" Padding="32,16">
<Border Grid.Row="2" Padding="14" CornerRadius="8" Margin="32,8,32,12">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#dcfce7"/>
<Setter Property="BorderBrush" Value="#86efac"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding SaveMessage}" Value="">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsError}" Value="True">
<Setter Property="Background" Value="#fee2e2"/>
<Setter Property="BorderBrush" Value="#fca5a5"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="{Binding SaveMessage}" FontSize="15" FontWeight="SemiBold" TextWrapping="Wrap">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource SuccessBrush}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsError}" Value="True">
<Setter Property="Foreground" Value="{StaticResource ErrorBrush}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Border>
<Border Grid.Row="3" Background="#F9FBFB" BorderBrush="{StaticResource BorderBrush}" BorderThickness="0,1,0,0" Padding="32,16">
<Grid HorizontalAlignment="Stretch">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button Command="{Binding OpenMealSchedulesCommand}" Style="{StaticResource OutlineButtonStyle}" MinWidth="160" Margin="0,0,12,0">