mirror of
https://github.com/claunia/marechai.git
synced 2025-12-16 19:14:25 +00:00
Show country flag in company detail page.
This commit is contained in:
@@ -2,6 +2,7 @@ using System.Net.Http;
|
|||||||
using Marechai.App.Presentation.ViewModels;
|
using Marechai.App.Presentation.ViewModels;
|
||||||
using Marechai.App.Presentation.Views;
|
using Marechai.App.Presentation.Views;
|
||||||
using Marechai.App.Services;
|
using Marechai.App.Services;
|
||||||
|
using Marechai.App.Services.Caching;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Uno.Extensions;
|
using Uno.Extensions;
|
||||||
using Uno.Extensions.Configuration;
|
using Uno.Extensions.Configuration;
|
||||||
@@ -106,6 +107,7 @@ public partial class App : Application
|
|||||||
.ConfigureServices((context, services) =>
|
.ConfigureServices((context, services) =>
|
||||||
{
|
{
|
||||||
// Register application services
|
// Register application services
|
||||||
|
services.AddSingleton<FlagCache>();
|
||||||
services.AddSingleton<NewsService>();
|
services.AddSingleton<NewsService>();
|
||||||
services.AddSingleton<NewsViewModel>();
|
services.AddSingleton<NewsViewModel>();
|
||||||
services.AddSingleton<ComputersService>();
|
services.AddSingleton<ComputersService>();
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
Navigation;
|
Navigation;
|
||||||
ThemeService;
|
ThemeService;
|
||||||
SkiaRenderer;
|
SkiaRenderer;
|
||||||
|
Svg;
|
||||||
</UnoFeatures>
|
</UnoFeatures>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -3,20 +3,25 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using Marechai.App.Helpers;
|
using Marechai.App.Helpers;
|
||||||
using Marechai.App.Presentation.Models;
|
using Marechai.App.Presentation.Models;
|
||||||
using Marechai.App.Services;
|
using Marechai.App.Services;
|
||||||
|
using Marechai.App.Services.Caching;
|
||||||
using Marechai.Data;
|
using Marechai.Data;
|
||||||
|
using Microsoft.UI.Xaml.Media.Imaging;
|
||||||
using Uno.Extensions.Navigation;
|
using Uno.Extensions.Navigation;
|
||||||
|
using Windows.Storage.Streams;
|
||||||
|
|
||||||
namespace Marechai.App.Presentation.ViewModels;
|
namespace Marechai.App.Presentation.ViewModels;
|
||||||
|
|
||||||
public partial class CompanyDetailViewModel : ObservableObject
|
public partial class CompanyDetailViewModel : ObservableObject
|
||||||
{
|
{
|
||||||
private readonly CompanyDetailService _companyDetailService;
|
private readonly CompanyDetailService _companyDetailService;
|
||||||
|
private readonly FlagCache _flagCache;
|
||||||
private readonly IStringLocalizer _localizer;
|
private readonly IStringLocalizer _localizer;
|
||||||
private readonly ILogger<CompanyDetailViewModel> _logger;
|
private readonly ILogger<CompanyDetailViewModel> _logger;
|
||||||
private readonly INavigator _navigator;
|
private readonly INavigator _navigator;
|
||||||
@@ -57,13 +62,20 @@ public partial class CompanyDetailViewModel : ObservableObject
|
|||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private bool _isLoading;
|
private bool _isLoading;
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
private SvgImageSource? _flagImageSource;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
private CompanyDto? _soldToCompany;
|
private CompanyDto? _soldToCompany;
|
||||||
|
|
||||||
public CompanyDetailViewModel(CompanyDetailService companyDetailService, IStringLocalizer localizer,
|
public CompanyDetailViewModel(CompanyDetailService companyDetailService,
|
||||||
ILogger<CompanyDetailViewModel> logger, INavigator navigator)
|
FlagCache flagCache,
|
||||||
|
IStringLocalizer localizer,
|
||||||
|
ILogger<CompanyDetailViewModel> logger,
|
||||||
|
INavigator navigator)
|
||||||
{
|
{
|
||||||
_companyDetailService = companyDetailService;
|
_companyDetailService = companyDetailService;
|
||||||
|
_flagCache = flagCache;
|
||||||
_localizer = localizer;
|
_localizer = localizer;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_navigator = navigator;
|
_navigator = navigator;
|
||||||
@@ -82,6 +94,11 @@ public partial class CompanyDetailViewModel : ObservableObject
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string CompanyFoundedDateDisplay => Company != null ? GetFoundedDateDisplay(Company) : string.Empty;
|
public string CompanyFoundedDateDisplay => Company != null ? GetFoundedDateDisplay(Company) : string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets whether flag content is available
|
||||||
|
/// </summary>
|
||||||
|
public bool HasFlagContent => FlagImageSource != null;
|
||||||
|
|
||||||
public IAsyncRelayCommand LoadData { get; }
|
public IAsyncRelayCommand LoadData { get; }
|
||||||
public ICommand GoBackCommand { get; }
|
public ICommand GoBackCommand { get; }
|
||||||
public IAsyncRelayCommand<CompanyDetailMachine> NavigateToMachineCommand { get; }
|
public IAsyncRelayCommand<CompanyDetailMachine> NavigateToMachineCommand { get; }
|
||||||
@@ -94,6 +111,12 @@ public partial class CompanyDetailViewModel : ObservableObject
|
|||||||
OnPropertyChanged(nameof(CompanyFoundedDateDisplay));
|
OnPropertyChanged(nameof(CompanyFoundedDateDisplay));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
partial void OnFlagImageSourceChanged(SvgImageSource? oldValue, SvgImageSource? newValue)
|
||||||
|
{
|
||||||
|
// Notify that HasFlagContent has changed
|
||||||
|
OnPropertyChanged(nameof(HasFlagContent));
|
||||||
|
}
|
||||||
|
|
||||||
partial void OnComputersFilterTextChanged(string value)
|
partial void OnComputersFilterTextChanged(string value)
|
||||||
{
|
{
|
||||||
FilterComputers(value);
|
FilterComputers(value);
|
||||||
@@ -286,6 +309,7 @@ public partial class CompanyDetailViewModel : ObservableObject
|
|||||||
ErrorMessage = string.Empty;
|
ErrorMessage = string.Empty;
|
||||||
HasError = false;
|
HasError = false;
|
||||||
IsDataLoaded = false;
|
IsDataLoaded = false;
|
||||||
|
FlagImageSource = null;
|
||||||
|
|
||||||
if(CompanyId <= 0)
|
if(CompanyId <= 0)
|
||||||
{
|
{
|
||||||
@@ -306,7 +330,28 @@ public partial class CompanyDetailViewModel : ObservableObject
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load sold-to company if applicable
|
// Load flag if country is available
|
||||||
|
if(Company.CountryId is not null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
short countryCode = (short)UntypedNodeExtractor.ExtractInt(Company.CountryId);
|
||||||
|
var flagStream = await _flagCache.GetFlagAsync(countryCode);
|
||||||
|
|
||||||
|
var flagSource = new SvgImageSource();
|
||||||
|
await flagSource.SetSourceAsync(flagStream.AsRandomAccessStream());
|
||||||
|
FlagImageSource = flagSource;
|
||||||
|
|
||||||
|
_logger.LogInformation("Successfully loaded flag for country code {CountryCode}",
|
||||||
|
countryCode);
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError("Failed to load flag for country {CountryId}: {Exception}",
|
||||||
|
Company.CountryId, ex.Message);
|
||||||
|
// Continue without flag if loading fails
|
||||||
|
}
|
||||||
|
}
|
||||||
if(Company.SoldToId != null)
|
if(Company.SoldToId != null)
|
||||||
{
|
{
|
||||||
int soldToId = UntypedNodeExtractor.ExtractInt(Company.SoldToId);
|
int soldToId = UntypedNodeExtractor.ExtractInt(Company.SoldToId);
|
||||||
|
|||||||
@@ -7,6 +7,9 @@
|
|||||||
NavigationCacheMode="Required"
|
NavigationCacheMode="Required"
|
||||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||||
|
|
||||||
|
<Page.Resources>
|
||||||
|
</Page.Resources>
|
||||||
|
|
||||||
<Grid utu:SafeArea.Insets="VisibleBounds">
|
<Grid utu:SafeArea.Insets="VisibleBounds">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
@@ -113,14 +116,35 @@
|
|||||||
<!-- Country -->
|
<!-- Country -->
|
||||||
<StackPanel Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
<StackPanel Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||||
CornerRadius="8"
|
CornerRadius="8"
|
||||||
Padding="12"
|
Padding="12">
|
||||||
Spacing="8">
|
<Grid ColumnSpacing="12">
|
||||||
<TextBlock Text="Country"
|
<Grid.ColumnDefinitions>
|
||||||
FontSize="14"
|
<ColumnDefinition Width="*" />
|
||||||
FontWeight="SemiBold"
|
<ColumnDefinition Width="Auto" />
|
||||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock Text="{Binding Company.Country}"
|
|
||||||
FontSize="14" />
|
<!-- Country Name and Label -->
|
||||||
|
<StackPanel Grid.Column="0"
|
||||||
|
Spacing="4">
|
||||||
|
<TextBlock Text="Country"
|
||||||
|
FontSize="14"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||||
|
<TextBlock Text="{Binding Company.Country}"
|
||||||
|
FontSize="14"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- Country Flag -->
|
||||||
|
<Image Grid.Column="1"
|
||||||
|
Width="48"
|
||||||
|
Height="32"
|
||||||
|
Stretch="UniformToFill"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Source="{Binding FlagImageSource}"
|
||||||
|
Visibility="{Binding HasFlagContent, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||||
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<!-- Address -->
|
<!-- Address -->
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ public sealed class FlagCache
|
|||||||
|
|
||||||
public async Task<Stream> GetFlagAsync(short countryCode)
|
public async Task<Stream> GetFlagAsync(short countryCode)
|
||||||
{
|
{
|
||||||
var filename = $"{countryCode}.svg";
|
var filename = $"{countryCode:D3}.svg";
|
||||||
|
|
||||||
Stream retStream;
|
Stream retStream;
|
||||||
|
|
||||||
@@ -48,9 +48,9 @@ public sealed class FlagCache
|
|||||||
|
|
||||||
async Task CacheFlagAsync(short countryCode)
|
async Task CacheFlagAsync(short countryCode)
|
||||||
{
|
{
|
||||||
var filename = $"{countryCode}.svg";
|
var filename = $"{countryCode:D3}.svg";
|
||||||
string baseUrl = _configuration.GetSection("ApiClient:Url").Value;
|
string baseUrl = _configuration.GetSection("ApiClient:Url").Value;
|
||||||
string flagUrl = baseUrl + $"/assets/flags/{filename}";
|
string flagUrl = baseUrl + $"/assets/flags/countries/{filename}";
|
||||||
using var httpClient = new HttpClient();
|
using var httpClient = new HttpClient();
|
||||||
using HttpResponseMessage response = await httpClient.GetAsync(flagUrl);
|
using HttpResponseMessage response = await httpClient.GetAsync(flagUrl);
|
||||||
response.EnsureSuccessStatusCode();
|
response.EnsureSuccessStatusCode();
|
||||||
|
|||||||
Reference in New Issue
Block a user