feat(12-01): add Base64ToImageSourceConverter, localization keys, and ClientLogoPreview property

- Base64ToImageSourceConverter converts data URI strings to BitmapImage with null-safe error handling
- Registered converter in App.xaml as Base64ToImageConverter global resource
- Added 9 localization keys (EN+FR) for logo UI labels in Settings and Profile dialogs
- Added ClientLogoPreview string property to ProfileManagementViewModel with FormatLogoPreview helper
- Updated OnSelectedProfileChanged, BrowseClientLogoAsync, ClearClientLogoAsync, AutoPullClientLogoAsync
- 17 tests pass (6 converter + 11 profile VM logo tests)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dev
2026-04-08 15:18:38 +02:00
parent 0bc0babaf8
commit 6a4cd8ab56
7 changed files with 201 additions and 0 deletions

View File

@@ -32,6 +32,13 @@ public partial class ProfileManagementViewModel : ObservableObject
[ObservableProperty]
private string _validationMessage = string.Empty;
private string? _clientLogoPreview;
public string? ClientLogoPreview
{
get => _clientLogoPreview;
private set { _clientLogoPreview = value; OnPropertyChanged(); }
}
public ObservableCollection<TenantProfile> Profiles { get; } = new();
public IAsyncRelayCommand AddCommand { get; }
@@ -81,6 +88,7 @@ public partial class ProfileManagementViewModel : ObservableObject
partial void OnSelectedProfileChanged(TenantProfile? value)
{
ClientLogoPreview = FormatLogoPreview(value?.ClientLogo);
BrowseClientLogoCommand.NotifyCanExecuteChanged();
ClearClientLogoCommand.NotifyCanExecuteChanged();
AutoPullClientLogoCommand.NotifyCanExecuteChanged();
@@ -88,6 +96,9 @@ public partial class ProfileManagementViewModel : ObservableObject
DeleteCommand.NotifyCanExecuteChanged();
}
private static string? FormatLogoPreview(LogoData? logo)
=> logo is not null ? $"data:{logo.MimeType};base64,{logo.Base64}" : null;
private void NotifyCommandsCanExecuteChanged()
{
AddCommand.NotifyCanExecuteChanged();
@@ -172,6 +183,7 @@ public partial class ProfileManagementViewModel : ObservableObject
{
var logo = await _brandingService.ImportLogoAsync(dialog.FileName);
SelectedProfile.ClientLogo = logo;
ClientLogoPreview = FormatLogoPreview(logo);
await _profileService.UpdateProfileAsync(SelectedProfile);
ValidationMessage = string.Empty;
}
@@ -188,6 +200,7 @@ public partial class ProfileManagementViewModel : ObservableObject
try
{
SelectedProfile.ClientLogo = null;
ClientLogoPreview = null;
await _profileService.UpdateProfileAsync(SelectedProfile);
ValidationMessage = string.Empty;
}
@@ -229,6 +242,7 @@ public partial class ProfileManagementViewModel : ObservableObject
var logo = await _brandingService.ImportLogoFromBytesAsync(bytes);
SelectedProfile.ClientLogo = logo;
ClientLogoPreview = FormatLogoPreview(logo);
await _profileService.UpdateProfileAsync(SelectedProfile);
ValidationMessage = "Client logo pulled from Entra branding.";
}