mirror of
https://github.com/claunia/marechai.git
synced 2025-12-16 19:14:25 +00:00
Add login page.
This commit is contained in:
@@ -17,6 +17,10 @@
|
|||||||
<local:ObjectToVisibilityConverter x:Key="ObjectToVisibilityConverter" />
|
<local:ObjectToVisibilityConverter x:Key="ObjectToVisibilityConverter" />
|
||||||
<local:StringToVisibilityConverter x:Key="StringToVisibilityConverter" />
|
<local:StringToVisibilityConverter x:Key="StringToVisibilityConverter" />
|
||||||
<local:ZeroToVisibilityConverter x:Key="ZeroToVisibilityConverter" />
|
<local:ZeroToVisibilityConverter x:Key="ZeroToVisibilityConverter" />
|
||||||
|
<local:InvertBoolConverter x:Key="InvertBoolConverter" />
|
||||||
|
<local:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
|
||||||
|
<local:InvertBoolToVisibilityConverter x:Key="InvertBoolToVisibilityConverter" />
|
||||||
|
<local:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
|
||||||
|
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</Application.Resources>
|
</Application.Resources>
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ using ProcessorsListViewModel = Marechai.App.Presentation.ViewModels.ProcessorsL
|
|||||||
using SettingsViewModel = Marechai.App.Presentation.ViewModels.SettingsViewModel;
|
using SettingsViewModel = Marechai.App.Presentation.ViewModels.SettingsViewModel;
|
||||||
using SoundSynthDetailViewModel = Marechai.App.Presentation.ViewModels.SoundSynthDetailViewModel;
|
using SoundSynthDetailViewModel = Marechai.App.Presentation.ViewModels.SoundSynthDetailViewModel;
|
||||||
using SoundSynthsListViewModel = Marechai.App.Presentation.ViewModels.SoundSynthsListViewModel;
|
using SoundSynthsListViewModel = Marechai.App.Presentation.ViewModels.SoundSynthsListViewModel;
|
||||||
|
using LoginViewModel = Marechai.App.Presentation.ViewModels.LoginViewModel;
|
||||||
|
|
||||||
namespace Marechai.App;
|
namespace Marechai.App;
|
||||||
|
|
||||||
@@ -164,6 +165,7 @@ public partial class App : Application
|
|||||||
services.AddTransient<SoundSynthsListViewModel>();
|
services.AddTransient<SoundSynthsListViewModel>();
|
||||||
services.AddTransient<SoundSynthDetailViewModel>();
|
services.AddTransient<SoundSynthDetailViewModel>();
|
||||||
services.AddTransient<SettingsViewModel>();
|
services.AddTransient<SettingsViewModel>();
|
||||||
|
services.AddTransient<LoginViewModel>();
|
||||||
})
|
})
|
||||||
.UseNavigation(RegisterRoutes));
|
.UseNavigation(RegisterRoutes));
|
||||||
|
|
||||||
@@ -181,6 +183,7 @@ public partial class App : Application
|
|||||||
{
|
{
|
||||||
views.Register(new ViewMap(ViewModel: typeof(ShellViewModel)),
|
views.Register(new ViewMap(ViewModel: typeof(ShellViewModel)),
|
||||||
new ViewMap<MainPage, MainViewModel>(),
|
new ViewMap<MainPage, MainViewModel>(),
|
||||||
|
new ViewMap<LoginPage, LoginViewModel>(),
|
||||||
new ViewMap<NewsPage, NewsViewModel>(),
|
new ViewMap<NewsPage, NewsViewModel>(),
|
||||||
new ViewMap<ComputersPage, ComputersViewModel>(),
|
new ViewMap<ComputersPage, ComputersViewModel>(),
|
||||||
new ViewMap<ComputersListPage, ComputersListViewModel>(),
|
new ViewMap<ComputersListPage, ComputersListViewModel>(),
|
||||||
@@ -203,6 +206,7 @@ public partial class App : Application
|
|||||||
views.FindByViewModel<ShellViewModel>(),
|
views.FindByViewModel<ShellViewModel>(),
|
||||||
Nested:
|
Nested:
|
||||||
[
|
[
|
||||||
|
new RouteMap("Login", views.FindByViewModel<LoginViewModel>()),
|
||||||
new RouteMap("Main",
|
new RouteMap("Main",
|
||||||
views.FindByViewModel<MainViewModel>(),
|
views.FindByViewModel<MainViewModel>(),
|
||||||
true,
|
true,
|
||||||
|
|||||||
93
Marechai.App/Presentation/ViewModels/LoginViewModel.cs
Normal file
93
Marechai.App/Presentation/ViewModels/LoginViewModel.cs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
#nullable enable
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Uno.Extensions.Authentication;
|
||||||
|
using Uno.Extensions.Navigation;
|
||||||
|
|
||||||
|
namespace Marechai.App.Presentation.ViewModels;
|
||||||
|
|
||||||
|
public partial class LoginViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private readonly IAuthenticationService _authService;
|
||||||
|
private readonly INavigator _navigator;
|
||||||
|
private readonly IStringLocalizer _stringLocalizer;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _email = string.Empty;
|
||||||
|
[ObservableProperty]
|
||||||
|
private string? _errorMessage;
|
||||||
|
[ObservableProperty]
|
||||||
|
private bool _isLoggingIn;
|
||||||
|
[ObservableProperty]
|
||||||
|
private string _password = string.Empty;
|
||||||
|
|
||||||
|
public LoginViewModel(INavigator navigator, IAuthenticationService authService, IStringLocalizer stringLocalizer)
|
||||||
|
{
|
||||||
|
_navigator = navigator;
|
||||||
|
_authService = authService;
|
||||||
|
_stringLocalizer = stringLocalizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private async Task LoginAsync()
|
||||||
|
{
|
||||||
|
// Clear previous error
|
||||||
|
ErrorMessage = null;
|
||||||
|
|
||||||
|
// Validate inputs
|
||||||
|
if(string.IsNullOrWhiteSpace(Email))
|
||||||
|
{
|
||||||
|
ErrorMessage = _stringLocalizer["LoginPage.Error.EmailRequired"];
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(string.IsNullOrWhiteSpace(Password))
|
||||||
|
{
|
||||||
|
ErrorMessage = _stringLocalizer["LoginPage.Error.PasswordRequired"];
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IsLoggingIn = true;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var credentials = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["Email"] = Email,
|
||||||
|
["Password"] = Password
|
||||||
|
};
|
||||||
|
|
||||||
|
bool success = await _authService.LoginAsync(null, credentials, null, CancellationToken.None);
|
||||||
|
|
||||||
|
if(success)
|
||||||
|
{
|
||||||
|
// Navigate back to main page on successful login
|
||||||
|
await _navigator.NavigateRouteAsync(this, "/Main");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Check if there's an error message in credentials
|
||||||
|
if(credentials.TryGetValue("error", out string? error))
|
||||||
|
ErrorMessage = error;
|
||||||
|
else
|
||||||
|
ErrorMessage = _stringLocalizer["LoginPage.Error.LoginFailed"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
ErrorMessage = ex.Message;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsLoggingIn = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private void ClearError() => ErrorMessage = null;
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using Marechai.App.Services;
|
using Marechai.App.Services;
|
||||||
|
using Uno.Extensions.Authentication;
|
||||||
using Uno.Extensions.Navigation;
|
using Uno.Extensions.Navigation;
|
||||||
using Uno.Extensions.Toolkit;
|
using Uno.Extensions.Toolkit;
|
||||||
|
|
||||||
@@ -10,6 +12,7 @@ namespace Marechai.App.Presentation.ViewModels;
|
|||||||
|
|
||||||
public partial class MainViewModel : ObservableObject
|
public partial class MainViewModel : ObservableObject
|
||||||
{
|
{
|
||||||
|
private readonly IAuthenticationService _authService;
|
||||||
private readonly IStringLocalizer _localizer;
|
private readonly IStringLocalizer _localizer;
|
||||||
private readonly INavigator _navigator;
|
private readonly INavigator _navigator;
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
@@ -27,10 +30,12 @@ public partial class MainViewModel : ObservableObject
|
|||||||
private bool _sidebarContentVisible = true;
|
private bool _sidebarContentVisible = true;
|
||||||
|
|
||||||
public MainViewModel(IStringLocalizer localizer, IOptions<AppConfig> appInfo, INavigator navigator,
|
public MainViewModel(IStringLocalizer localizer, IOptions<AppConfig> appInfo, INavigator navigator,
|
||||||
NewsViewModel newsViewModel, IColorThemeService colorThemeService, IThemeService themeService)
|
NewsViewModel newsViewModel, IColorThemeService colorThemeService, IThemeService themeService,
|
||||||
|
IAuthenticationService authService)
|
||||||
{
|
{
|
||||||
_navigator = navigator;
|
_navigator = navigator;
|
||||||
_localizer = localizer;
|
_localizer = localizer;
|
||||||
|
_authService = authService;
|
||||||
NewsViewModel = newsViewModel;
|
NewsViewModel = newsViewModel;
|
||||||
Title = "Marechai";
|
Title = "Marechai";
|
||||||
Title += $" - {localizer["ApplicationName"]}";
|
Title += $" - {localizer["ApplicationName"]}";
|
||||||
@@ -62,6 +67,9 @@ public partial class MainViewModel : ObservableObject
|
|||||||
LoginLogoutCommand = new RelayCommand(HandleLoginLogout);
|
LoginLogoutCommand = new RelayCommand(HandleLoginLogout);
|
||||||
ToggleSidebarCommand = new RelayCommand(() => IsSidebarOpen = !IsSidebarOpen);
|
ToggleSidebarCommand = new RelayCommand(() => IsSidebarOpen = !IsSidebarOpen);
|
||||||
|
|
||||||
|
// Subscribe to authentication events
|
||||||
|
_authService.LoggedOut += OnLoggedOut;
|
||||||
|
|
||||||
UpdateLoginLogoutButtonText();
|
UpdateLoginLogoutButtonText();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,16 +165,39 @@ public partial class MainViewModel : ObservableObject
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateLoginLogoutButtonText()
|
private async void UpdateLoginLogoutButtonText()
|
||||||
{
|
{
|
||||||
// TODO: Check if user is logged in
|
bool isAuthenticated = await _authService.IsAuthenticated(CancellationToken.None);
|
||||||
// For now, always show "Login"
|
LoginLogoutButtonText = isAuthenticated ? LocalizedStrings["Logout"] : LocalizedStrings["Login"];
|
||||||
LoginLogoutButtonText = LocalizedStrings["Login"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void HandleLoginLogout()
|
private void OnLoggedOut(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
// TODO: Implement login/logout logic
|
// Update button text when user logs out
|
||||||
|
UpdateLoginLogoutButtonText();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RefreshAuthenticationState()
|
||||||
|
{
|
||||||
|
// Public method to refresh authentication state (called after login)
|
||||||
|
UpdateLoginLogoutButtonText();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void HandleLoginLogout()
|
||||||
|
{
|
||||||
|
bool isAuthenticated = await _authService.IsAuthenticated(CancellationToken.None);
|
||||||
|
|
||||||
|
if(isAuthenticated)
|
||||||
|
{
|
||||||
|
// Logout
|
||||||
|
await _authService.LogoutAsync(null, CancellationToken.None);
|
||||||
|
UpdateLoginLogoutButtonText();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Navigate to login page - use absolute path starting from root
|
||||||
|
await _navigator.NavigateRouteAsync(this, "/Login");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task NavigateTo(string destination)
|
private async Task NavigateTo(string destination)
|
||||||
|
|||||||
@@ -8,5 +8,6 @@ public class ShellViewModel
|
|||||||
|
|
||||||
public ShellViewModel(INavigator navigator) => _navigator = navigator;
|
public ShellViewModel(INavigator navigator) => _navigator = navigator;
|
||||||
|
|
||||||
// Add code here to initialize or attach event handlers to singleton services
|
// Users can browse the app without authentication
|
||||||
|
// Login is available from the sidebar when needed
|
||||||
}
|
}
|
||||||
209
Marechai.App/Presentation/Views/LoginPage.xaml
Normal file
209
Marechai.App/Presentation/Views/LoginPage.xaml
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
<?xml version="1.0"
|
||||||
|
encoding="utf-8"?>
|
||||||
|
|
||||||
|
<Page x:Class="Marechai.App.Presentation.Views.LoginPage"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:vm="using:Marechai.App.Presentation.ViewModels"
|
||||||
|
xmlns:utu="using:Uno.Toolkit.UI"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
|
||||||
|
d:DataContext="{d:DesignInstance vm:LoginViewModel}">
|
||||||
|
|
||||||
|
<Grid utu:SafeArea.Insets="VisibleBounds">
|
||||||
|
<!-- Center content on screen -->
|
||||||
|
<Grid HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
MaxWidth="480"
|
||||||
|
Padding="32">
|
||||||
|
|
||||||
|
<!-- Login Card with Mac OS 9 styling -->
|
||||||
|
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||||
|
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||||
|
BorderThickness="2"
|
||||||
|
CornerRadius="8"
|
||||||
|
Padding="0"
|
||||||
|
Translation="0,0,16">
|
||||||
|
<Border.Shadow>
|
||||||
|
<ThemeShadow />
|
||||||
|
</Border.Shadow>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" /> <RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!-- Header with Mac OS 9 Title Bar Style -->
|
||||||
|
<Border Grid.Row="0"
|
||||||
|
Background="{ThemeResource AccentFillColorDefaultBrush}"
|
||||||
|
Padding="16,12"
|
||||||
|
CornerRadius="6,6,0,0">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<!-- Icon -->
|
||||||
|
<FontIcon Grid.Column="0"
|
||||||
|
Glyph=""
|
||||||
|
FontSize="24"
|
||||||
|
Foreground="White"
|
||||||
|
Margin="0,0,12,0"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
|
||||||
|
<!-- Title -->
|
||||||
|
<TextBlock Grid.Column="1"
|
||||||
|
x:Uid="LoginPage_Title"
|
||||||
|
Text="Sign In to Marechai"
|
||||||
|
Style="{StaticResource TitleTextBlockStyle}"
|
||||||
|
Foreground="White"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Login Form Content -->
|
||||||
|
<ScrollViewer Grid.Row="1"
|
||||||
|
VerticalScrollBarVisibility="Auto">
|
||||||
|
<StackPanel Padding="32"
|
||||||
|
Spacing="24">
|
||||||
|
|
||||||
|
<!-- Welcome Message -->
|
||||||
|
<TextBlock x:Uid="LoginPage_WelcomeMessage"
|
||||||
|
Text="Welcome back! Please sign in to continue."
|
||||||
|
Style="{StaticResource BodyTextBlockStyle}"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
TextAlignment="Center" />
|
||||||
|
|
||||||
|
<!-- Email Input -->
|
||||||
|
<StackPanel Spacing="8">
|
||||||
|
<TextBlock x:Uid="LoginPage_EmailLabel"
|
||||||
|
Text="Email Address"
|
||||||
|
Style="{StaticResource BodyStrongTextBlockStyle}" />
|
||||||
|
<TextBox x:Name="EmailTextBox"
|
||||||
|
x:Uid="LoginPage_EmailTextBox"
|
||||||
|
Text="{Binding Email, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
PlaceholderText="Enter your email"
|
||||||
|
InputScope="EmailSmtpAddress"
|
||||||
|
IsSpellCheckEnabled="False"
|
||||||
|
AutomationProperties.Name="Email address"
|
||||||
|
KeyDown="OnEmailKeyDown" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- Password Input -->
|
||||||
|
<StackPanel Spacing="8">
|
||||||
|
<TextBlock x:Uid="LoginPage_PasswordLabel"
|
||||||
|
Text="Password"
|
||||||
|
Style="{StaticResource BodyStrongTextBlockStyle}" />
|
||||||
|
<PasswordBox x:Name="PasswordBox"
|
||||||
|
x:Uid="LoginPage_PasswordBox"
|
||||||
|
Password="{Binding Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
PlaceholderText="Enter your password"
|
||||||
|
AutomationProperties.Name="Password"
|
||||||
|
KeyDown="OnPasswordKeyDown" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- Error Message -->
|
||||||
|
<Border x:Name="ErrorBorder"
|
||||||
|
Background="#FFF4E6"
|
||||||
|
BorderBrush="#FFB84D"
|
||||||
|
BorderThickness="2"
|
||||||
|
CornerRadius="4"
|
||||||
|
Padding="12"
|
||||||
|
Visibility="{Binding ErrorMessage, Converter={StaticResource NullToVisibilityConverter}}">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<!-- Warning Icon -->
|
||||||
|
<FontIcon Grid.Column="0"
|
||||||
|
Glyph=""
|
||||||
|
Foreground="#D97706"
|
||||||
|
FontSize="20"
|
||||||
|
Margin="0,0,8,0"
|
||||||
|
VerticalAlignment="Top" />
|
||||||
|
|
||||||
|
<!-- Error Text -->
|
||||||
|
<TextBlock Grid.Column="1"
|
||||||
|
Text="{Binding ErrorMessage}"
|
||||||
|
Foreground="#92400E"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
|
||||||
|
<!-- Close Button -->
|
||||||
|
<Button Grid.Column="2"
|
||||||
|
Command="{Binding ClearErrorCommand}"
|
||||||
|
Background="Transparent"
|
||||||
|
BorderThickness="0"
|
||||||
|
Padding="8"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
AutomationProperties.Name="Close error message">
|
||||||
|
<FontIcon Glyph=""
|
||||||
|
FontSize="12"
|
||||||
|
Foreground="#92400E" />
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Login Button -->
|
||||||
|
<Button x:Name="LoginButton"
|
||||||
|
x:Uid="LoginPage_LoginButton"
|
||||||
|
Command="{Binding LoginCommand}"
|
||||||
|
IsEnabled="{Binding IsLoggingIn, Converter={StaticResource InvertBoolConverter}}"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Padding="16,12"
|
||||||
|
Style="{StaticResource AccentButtonStyle}"
|
||||||
|
AutomationProperties.Name="Sign in button">
|
||||||
|
<Button.Content>
|
||||||
|
<Grid>
|
||||||
|
<TextBlock x:Uid="LoginPage_LoginButtonText"
|
||||||
|
Text="Sign In"
|
||||||
|
Visibility="{Binding IsLoggingIn, Converter={StaticResource InvertBoolToVisibilityConverter}}" />
|
||||||
|
<StackPanel Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Spacing="8"
|
||||||
|
Visibility="{Binding IsLoggingIn, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||||
|
<ProgressRing IsActive="True"
|
||||||
|
Width="20"
|
||||||
|
Height="20"
|
||||||
|
Foreground="White" />
|
||||||
|
<TextBlock x:Uid="LoginPage_SigningInText"
|
||||||
|
Text="Signing in..."
|
||||||
|
Foreground="White" />
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Button.Content>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<!-- Additional Options -->
|
||||||
|
<StackPanel Spacing="8"
|
||||||
|
HorizontalAlignment="Center">
|
||||||
|
<HyperlinkButton x:Uid="LoginPage_ForgotPasswordLink"
|
||||||
|
Content="Forgot your password?"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Visibility="Collapsed" />
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Spacing="4"
|
||||||
|
Visibility="Collapsed">
|
||||||
|
<TextBlock x:Uid="LoginPage_NoAccountText"
|
||||||
|
Text="Don't have an account?"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
<HyperlinkButton x:Uid="LoginPage_SignUpLink"
|
||||||
|
Content="Sign up"
|
||||||
|
Padding="4,0" />
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Page>
|
||||||
33
Marechai.App/Presentation/Views/LoginPage.xaml.cs
Normal file
33
Marechai.App/Presentation/Views/LoginPage.xaml.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using Windows.System;
|
||||||
|
using Marechai.App.Presentation.ViewModels;
|
||||||
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Input;
|
||||||
|
|
||||||
|
namespace Marechai.App.Presentation.Views;
|
||||||
|
|
||||||
|
public sealed partial class LoginPage : Page
|
||||||
|
{
|
||||||
|
public LoginPage() => InitializeComponent();
|
||||||
|
|
||||||
|
private void OnEmailKeyDown(object sender, KeyRoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if(e.Key == VirtualKey.Enter)
|
||||||
|
{
|
||||||
|
// Move focus to password field
|
||||||
|
PasswordBox.Focus(FocusState.Keyboard);
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPasswordKeyDown(object sender, KeyRoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if(e.Key == VirtualKey.Enter)
|
||||||
|
{
|
||||||
|
// Trigger login when Enter is pressed
|
||||||
|
if(DataContext is LoginViewModel viewModel && LoginButton.IsEnabled) viewModel.LoginCommand.Execute(null);
|
||||||
|
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -224,4 +224,51 @@
|
|||||||
<data name="SettingsPage_DesignSystemChange_Notice.Text" xml:space="preserve">
|
<data name="SettingsPage_DesignSystemChange_Notice.Text" xml:space="preserve">
|
||||||
<value>Design system changes will be applied the next time you start the application. Color theme changes are applied immediately.</value>
|
<value>Design system changes will be applied the next time you start the application. Color theme changes are applied immediately.</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
|
<!-- Login Page -->
|
||||||
|
<data name="LoginPage_Title.Text" xml:space="preserve">
|
||||||
|
<value>Sign In to Marechai</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_WelcomeMessage.Text" xml:space="preserve">
|
||||||
|
<value>Welcome back! Please sign in to continue.</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_EmailLabel.Text" xml:space="preserve">
|
||||||
|
<value>Email Address</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_EmailTextBox.PlaceholderText" xml:space="preserve">
|
||||||
|
<value>Enter your email</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_PasswordLabel.Text" xml:space="preserve">
|
||||||
|
<value>Password</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_PasswordBox.PlaceholderText" xml:space="preserve">
|
||||||
|
<value>Enter your password</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_LoginButton.Content" xml:space="preserve">
|
||||||
|
<value>Sign In</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_LoginButtonText.Text" xml:space="preserve">
|
||||||
|
<value>Sign In</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_SigningInText.Text" xml:space="preserve">
|
||||||
|
<value>Signing in...</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_ForgotPasswordLink.Content" xml:space="preserve">
|
||||||
|
<value>Forgot your password?</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_NoAccountText.Text" xml:space="preserve">
|
||||||
|
<value>Don't have an account?</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_SignUpLink.Content" xml:space="preserve">
|
||||||
|
<value>Sign up</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage.Error.EmailRequired" xml:space="preserve">
|
||||||
|
<value>Email address is required.</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage.Error.PasswordRequired" xml:space="preserve">
|
||||||
|
<value>Password is required.</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage.Error.LoginFailed" xml:space="preserve">
|
||||||
|
<value>Login failed. Please check your credentials and try again.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -224,4 +224,51 @@
|
|||||||
<data name="SettingsPage_DesignSystemChange_Notice.Text" xml:space="preserve">
|
<data name="SettingsPage_DesignSystemChange_Notice.Text" xml:space="preserve">
|
||||||
<value>Los cambios del sistema de diseño se aplicarán la próxima vez que inicie la aplicación. Los cambios de tema de color se aplican inmediatamente.</value>
|
<value>Los cambios del sistema de diseño se aplicarán la próxima vez que inicie la aplicación. Los cambios de tema de color se aplican inmediatamente.</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
|
<!-- Login Page -->
|
||||||
|
<data name="LoginPage_Title.Text" xml:space="preserve">
|
||||||
|
<value>Iniciar Sesión en Marechai</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_WelcomeMessage.Text" xml:space="preserve">
|
||||||
|
<value>¡Bienvenido de nuevo! Por favor, inicia sesión para continuar.</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_EmailLabel.Text" xml:space="preserve">
|
||||||
|
<value>Dirección de Correo Electrónico</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_EmailTextBox.PlaceholderText" xml:space="preserve">
|
||||||
|
<value>Ingrese su correo electrónico</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_PasswordLabel.Text" xml:space="preserve">
|
||||||
|
<value>Contraseña</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_PasswordBox.PlaceholderText" xml:space="preserve">
|
||||||
|
<value>Ingrese su contraseña</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_LoginButton.Content" xml:space="preserve">
|
||||||
|
<value>Iniciar Sesión</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_LoginButtonText.Text" xml:space="preserve">
|
||||||
|
<value>Iniciar Sesión</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_SigningInText.Text" xml:space="preserve">
|
||||||
|
<value>Iniciando sesión...</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_ForgotPasswordLink.Content" xml:space="preserve">
|
||||||
|
<value>¿Olvidó su contraseña?</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_NoAccountText.Text" xml:space="preserve">
|
||||||
|
<value>¿No tiene una cuenta?</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_SignUpLink.Content" xml:space="preserve">
|
||||||
|
<value>Registrarse</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage.Error.EmailRequired" xml:space="preserve">
|
||||||
|
<value>La dirección de correo electrónico es obligatoria.</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage.Error.PasswordRequired" xml:space="preserve">
|
||||||
|
<value>La contraseña es obligatoria.</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage.Error.LoginFailed" xml:space="preserve">
|
||||||
|
<value>Error al iniciar sesión. Por favor, verifique sus credenciales e intente nuevamente.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -222,6 +222,53 @@
|
|||||||
<value>Système de Conception</value>
|
<value>Système de Conception</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SettingsPage_DesignSystemChange_Notice.Text" xml:space="preserve">
|
<data name="SettingsPage_DesignSystemChange_Notice.Text" xml:space="preserve">
|
||||||
<value>Les modifications du système de conception seront appliquées au prochain démarrage de l'application. Les modifications du thème de couleur sont appliquées immédiatement.</value>
|
<value>Les modifications du système de conception seront appliquées la prochaine fois que vous démarrerez l'application. Les modifications du thème de couleur sont appliquées immédiatement.</value>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
<!-- Login Page -->
|
||||||
|
<data name="LoginPage_Title.Text" xml:space="preserve">
|
||||||
|
<value>Se Connecter à Marechai</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_WelcomeMessage.Text" xml:space="preserve">
|
||||||
|
<value>Bienvenue ! Veuillez vous connecter pour continuer.</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_EmailLabel.Text" xml:space="preserve">
|
||||||
|
<value>Adresse E-mail</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_EmailTextBox.PlaceholderText" xml:space="preserve">
|
||||||
|
<value>Entrez votre e-mail</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_PasswordLabel.Text" xml:space="preserve">
|
||||||
|
<value>Mot de Passe</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_PasswordBox.PlaceholderText" xml:space="preserve">
|
||||||
|
<value>Entrez votre mot de passe</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_LoginButton.Content" xml:space="preserve">
|
||||||
|
<value>Se Connecter</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_LoginButtonText.Text" xml:space="preserve">
|
||||||
|
<value>Se Connecter</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_SigningInText.Text" xml:space="preserve">
|
||||||
|
<value>Connexion en cours...</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_ForgotPasswordLink.Content" xml:space="preserve">
|
||||||
|
<value>Mot de passe oublié ?</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_NoAccountText.Text" xml:space="preserve">
|
||||||
|
<value>Vous n'avez pas de compte ?</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_SignUpLink.Content" xml:space="preserve">
|
||||||
|
<value>S'inscrire</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage.Error.EmailRequired" xml:space="preserve">
|
||||||
|
<value>L'adresse e-mail est requise.</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage.Error.PasswordRequired" xml:space="preserve">
|
||||||
|
<value>Le mot de passe est requis.</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage.Error.LoginFailed" xml:space="preserve">
|
||||||
|
<value>Échec de la connexion. Veuillez vérifier vos identifiants et réessayer.</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -222,6 +222,53 @@
|
|||||||
<value>Sistema de Design</value>
|
<value>Sistema de Design</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SettingsPage_DesignSystemChange_Notice.Text" xml:space="preserve">
|
<data name="SettingsPage_DesignSystemChange_Notice.Text" xml:space="preserve">
|
||||||
<value>As alterações do sistema de design serão aplicadas na próxima vez que você iniciar o aplicativo. As alterações de tema de cor são aplicadas imediatamente.</value>
|
<value>As alterações no sistema de design serão aplicadas na próxima vez que você iniciar o aplicativo. As alterações no tema de cores são aplicadas imediatamente.</value>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
<!-- Login Page -->
|
||||||
|
<data name="LoginPage_Title.Text" xml:space="preserve">
|
||||||
|
<value>Entrar no Marechai</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_WelcomeMessage.Text" xml:space="preserve">
|
||||||
|
<value>Bem-vindo de volta! Por favor, faça login para continuar.</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_EmailLabel.Text" xml:space="preserve">
|
||||||
|
<value>Endereço de E-mail</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_EmailTextBox.PlaceholderText" xml:space="preserve">
|
||||||
|
<value>Digite seu e-mail</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_PasswordLabel.Text" xml:space="preserve">
|
||||||
|
<value>Senha</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_PasswordBox.PlaceholderText" xml:space="preserve">
|
||||||
|
<value>Digite sua senha</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_LoginButton.Content" xml:space="preserve">
|
||||||
|
<value>Entrar</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_LoginButtonText.Text" xml:space="preserve">
|
||||||
|
<value>Entrar</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_SigningInText.Text" xml:space="preserve">
|
||||||
|
<value>Entrando...</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_ForgotPasswordLink.Content" xml:space="preserve">
|
||||||
|
<value>Esqueceu sua senha?</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_NoAccountText.Text" xml:space="preserve">
|
||||||
|
<value>Não tem uma conta?</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage_SignUpLink.Content" xml:space="preserve">
|
||||||
|
<value>Cadastre-se</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage.Error.EmailRequired" xml:space="preserve">
|
||||||
|
<value>O endereço de e-mail é obrigatório.</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage.Error.PasswordRequired" xml:space="preserve">
|
||||||
|
<value>A senha é obrigatória.</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoginPage.Error.LoginFailed" xml:space="preserve">
|
||||||
|
<value>Falha no login. Por favor, verifique suas credenciais e tente novamente.</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
Reference in New Issue
Block a user