mirror of
https://github.com/claunia/marechai.git
synced 2025-12-16 19:14:25 +00:00
Compare commits
106 Commits
dependabot
...
4d30530ef0
| Author | SHA1 | Date | |
|---|---|---|---|
|
4d30530ef0
|
|||
|
e0689684e1
|
|||
|
cfdef93787
|
|||
|
c6cac9e04a
|
|||
|
e2f86b76db
|
|||
|
80791a8cc9
|
|||
|
d5fbb55425
|
|||
|
7ee042bdec
|
|||
|
e9221ac130
|
|||
|
87291d9dd8
|
|||
|
ce1c089fb0
|
|||
|
5d249f435e
|
|||
|
3e4677b084
|
|||
|
b7c94312fc
|
|||
|
61ebf7b503
|
|||
|
b18396f8d8
|
|||
|
4f1aee302b
|
|||
|
7ede62514f
|
|||
|
955c2f9654
|
|||
|
4a5708b910
|
|||
|
5bffbc342e
|
|||
|
392c69350f
|
|||
|
2bb07845e1
|
|||
|
1053617622
|
|||
|
30b60c0e96
|
|||
|
14596c5499
|
|||
|
9847c987b5
|
|||
|
6f0de86be4
|
|||
|
37d8df4b7d
|
|||
|
860fd99b00
|
|||
|
2ba7d86e71
|
|||
|
37403e5e53
|
|||
|
2066e211e1
|
|||
|
ebc611e5d2
|
|||
|
5e3be9cbb0
|
|||
|
9d146eb151
|
|||
|
52b6145a8b
|
|||
|
09ced17903
|
|||
|
356674f51f
|
|||
|
cd4aa5767d
|
|||
|
cf24356030
|
|||
|
f85dc22bf6
|
|||
|
795f5ba27d
|
|||
|
059417f0dc
|
|||
|
5b4a1b42e0
|
|||
|
e9a2a68e49
|
|||
|
7c29302153
|
|||
|
76bebc68b7
|
|||
|
4a2d46f3b0
|
|||
|
e9da9c7a3f
|
|||
|
b81c628f07
|
|||
|
6fc709a271
|
|||
|
507e5686e4
|
|||
|
27e5616da8
|
|||
|
464f52878b
|
|||
|
f6214e6d14
|
|||
|
baaf571505
|
|||
|
8d6c382754
|
|||
|
e171b8ddd8
|
|||
|
6981075c4a
|
|||
|
bed72102e8
|
|||
|
d301315fbf
|
|||
|
79f0d2632b
|
|||
|
0ba1a24b4e
|
|||
|
583f20ff99
|
|||
|
1826c70883
|
|||
|
34d76fd646
|
|||
|
fc6238aef1
|
|||
|
505ace535f
|
|||
|
a715d936eb
|
|||
|
e4c2837ad9
|
|||
|
36520596da
|
|||
|
764e058f79
|
|||
|
349b396588
|
|||
|
d0e5725ae0
|
|||
|
0bbf821489
|
|||
|
10017850f8
|
|||
|
d9239f39c0
|
|||
|
f445006e46
|
|||
|
3520e49b25
|
|||
|
268c8fab07
|
|||
|
e9e3ef2ab0
|
|||
|
6962be93a4
|
|||
|
be83594ea9
|
|||
|
b1f32e6f13
|
|||
|
c73c4d839a
|
|||
|
d7c61b2fdd
|
|||
|
a942d40849
|
|||
|
6c01b2128f
|
|||
|
e14bfba354
|
|||
|
d2c71d350d
|
|||
|
eb7c0a6858
|
|||
|
8f6d334af4
|
|||
|
1d67081792
|
|||
|
f304448fdb
|
|||
|
88307edc9a
|
|||
|
51d0809536
|
|||
|
8e07b6587b
|
|||
|
4f2435fcbd
|
|||
|
3fac917422
|
|||
|
096865dc3b
|
|||
|
29ec7571fe
|
|||
|
fc893ee08b
|
|||
|
cf7f830aad
|
|||
|
8213e4ad80
|
|||
|
e974a77e46
|
39
.appmod/.appcat/assessment-config.json
Normal file
39
.appmod/.appcat/assessment-config.json
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
You can edit this file to configure the application assessment. Please note that any changes saved to this file will be applied the next time you run the assessment.
|
||||
|
||||
The configurable AppCAT arguments
|
||||
|
||||
- target: target Azure compute service to run the apps on. Choosing "Any" if you haven't decided which one to use and later you can choose and compare on the assessment report.
|
||||
| Target | Description |
|
||||
|-----------------------------------|--------------------------------------------------------------------|
|
||||
| Any | Discover issues for all supported targets here. |
|
||||
| AKS.Windows | Best practices for Azure Kubernetes Service (Windows). |
|
||||
| AKS.Linux | Best practices for Azure Kubernetes Service (Linux). |
|
||||
| AppService.Windows | Best practices for Azure App Service (Windows). |
|
||||
| AppService.Linux | Best practices for Azure App Service (Linux). |
|
||||
| AppServiceContainer.Windows | Best practices for Azure App Service Container (Windows). |
|
||||
| AppServiceContainer.Linux | Best practices for Azure App Service Container (Linux). |
|
||||
| AppServiceManagedInstance.Windows | Best practices for Azure App Service Managed Instance (Windows). |
|
||||
| ACA | Best practices for Azure Container Apps. |
|
||||
|
||||
Two examples on how to configure properly.
|
||||
|
||||
Example one: you'd like to migrate your apps to Azure but haven't decided on the target compute service yet.
|
||||
{
|
||||
"appcat": {
|
||||
"target": "Any"
|
||||
}
|
||||
}
|
||||
|
||||
Example two: you'd like to migrate your apps to App Service Linux and want to understand what are the issues to be fixed.
|
||||
{
|
||||
"appcat": {
|
||||
"target": "AppService.Linux"
|
||||
}
|
||||
}
|
||||
*/
|
||||
{
|
||||
"appcat": {
|
||||
"target": "Any"
|
||||
}
|
||||
}
|
||||
601577
.appmod/.appcat/output_20251113_014740.json
Normal file
601577
.appmod/.appcat/output_20251113_014740.json
Normal file
File diff suppressed because one or more lines are too long
68
.appmod/.migration/plan.md
Normal file
68
.appmod/.migration/plan.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# .NET Modernization Plan: Local MariaDB Application
|
||||
|
||||
## Overview
|
||||
Complete modernization of Marechai application from .NET 5 to .NET 9 with dependency updates and local credential security.
|
||||
|
||||
## Goals
|
||||
1. Upgrade from .NET 5.0 to .NET 9.0
|
||||
2. Update all NuGet packages to latest compatible versions
|
||||
3. Fix security vulnerabilities (SkiaSharp CVE)
|
||||
4. Remove deprecated dependencies (Application Insights)
|
||||
5. Implement secure local credential storage using Data Protection API
|
||||
6. Maintain all MariaDB functionality and local-only architecture
|
||||
|
||||
## Projects
|
||||
- **Marechai.Database.csproj** - Class library (net5.0 → net9.0)
|
||||
- **Marechai.csproj** - ASP.NET Core app (net5.0 → net9.0)
|
||||
|
||||
## Migration Tasks
|
||||
|
||||
### Task 1: Upgrade .NET Framework
|
||||
- Update Marechai.Database.csproj target framework from net5.0 to net9.0
|
||||
- Update Marechai.csproj target framework from net5.0 to net9.0
|
||||
- Verify framework migration compatibility
|
||||
|
||||
### Task 2: Update Critical NuGet Packages
|
||||
|
||||
#### Marechai.Database.csproj
|
||||
- Microsoft.AspNetCore.Identity.EntityFrameworkCore: 5.0.1 → 9.0.11
|
||||
- Microsoft.EntityFrameworkCore.Design: 5.0.1 → 9.0.11
|
||||
- Microsoft.EntityFrameworkCore.Proxies: 5.0.1 → 9.0.11
|
||||
|
||||
#### Marechai.csproj
|
||||
- Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore: 5.0.1 → 9.0.11
|
||||
- Microsoft.AspNetCore.Identity.EntityFrameworkCore: 5.0.1 → 9.0.11
|
||||
- Microsoft.AspNetCore.Identity.UI: 5.0.1 → 9.0.11
|
||||
- Microsoft.EntityFrameworkCore.Proxies: 5.0.1 → 9.0.11
|
||||
- Microsoft.EntityFrameworkCore.Tools: 5.0.1 → 9.0.11
|
||||
- Microsoft.VisualStudio.Web.CodeGeneration.Design: 5.0.1 → 9.0.0
|
||||
- SkiaSharp: 2.80.2 → 3.119.1
|
||||
- SkiaSharp.NativeAssets.Linux: 2.80.2 → 3.119.1
|
||||
|
||||
### Task 3: Remove Deprecated Packages
|
||||
- Microsoft.ApplicationInsights.AspNetCore: 2.16.0 (deprecated)
|
||||
|
||||
### Task 4: Implement Local Credential Encryption
|
||||
- Add Data Protection API configuration in appsettings.json
|
||||
- Create credential encryption utility for DPAPI integration
|
||||
- Update connection string handling in Program.cs
|
||||
- Add encrypted credential support in Startup.cs
|
||||
- Ensure backward compatibility with existing deployments
|
||||
|
||||
## Execution Order
|
||||
1. Upgrade .NET framework versions
|
||||
2. Update NuGet packages
|
||||
3. Fix security vulnerabilities
|
||||
4. Remove deprecated dependencies
|
||||
5. Implement credential encryption
|
||||
6. Comprehensive testing and validation
|
||||
7. Commit all changes
|
||||
|
||||
## Success Criteria
|
||||
- ✅ Both projects target .NET 9.0
|
||||
- ✅ All recommended packages updated
|
||||
- ✅ SkiaSharp security vulnerability fixed
|
||||
- ✅ Application Insights deprecated package removed
|
||||
- ✅ Credentials can be securely stored locally
|
||||
- ✅ Build succeeds with no errors
|
||||
- ✅ All changes committed to git
|
||||
87
.appmod/.migration/progress.md
Normal file
87
.appmod/.migration/progress.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# Migration Progress Tracking
|
||||
|
||||
## Current Status: COMPLETED ✅
|
||||
|
||||
### Completed Steps
|
||||
- ✅ Branch created for .NET 5.0 → .NET 9.0 migration
|
||||
- ✅ Migration plan and progress files created
|
||||
- ✅ Marechai.Database.csproj upgraded to net9.0
|
||||
- ✅ Marechai.csproj upgraded to net9.0
|
||||
- ✅ Entity Framework Core packages updated to 9.0.11
|
||||
- ✅ SkiaSharp security vulnerability fixed (2.80.2 → 3.119.1)
|
||||
- ✅ Deprecated Application Insights removed
|
||||
- ✅ Credential encryption implemented via Data Protection API
|
||||
- ✅ Build successful with no errors
|
||||
- ✅ All changes committed to git
|
||||
|
||||
## Detailed Task Status
|
||||
|
||||
### Framework Upgrade
|
||||
- [x] Marechai.Database.csproj: net5.0 → net9.0
|
||||
- [x] Marechai.csproj: net5.0 → net9.0
|
||||
|
||||
### Package Updates - Marechai.Database.csproj
|
||||
- [x] Microsoft.AspNetCore.Identity.EntityFrameworkCore: 5.0.1 → 9.0.11
|
||||
- [x] Microsoft.EntityFrameworkCore.Design: 5.0.1 → 9.0.11
|
||||
- [x] Microsoft.EntityFrameworkCore.Proxies: 5.0.1 → 9.0.11
|
||||
|
||||
### Package Updates - Marechai.csproj
|
||||
- [x] Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore: 5.0.1 → 9.0.11
|
||||
- [x] Microsoft.AspNetCore.Identity.EntityFrameworkCore: 5.0.1 → 9.0.11
|
||||
- [x] Microsoft.AspNetCore.Identity.UI: 5.0.1 → 9.0.11
|
||||
- [x] Microsoft.EntityFrameworkCore.Proxies: 5.0.1 → 9.0.11
|
||||
- [x] Microsoft.EntityFrameworkCore.Tools: 5.0.1 → 9.0.11
|
||||
- [x] Microsoft.VisualStudio.Web.CodeGeneration.Design: 5.0.1 → 9.0.0
|
||||
- [x] SkiaSharp: 2.80.2 → 3.119.1
|
||||
- [x] SkiaSharp.NativeAssets.Linux: 2.80.2 → 3.119.1
|
||||
|
||||
### Deprecated Packages
|
||||
- [x] Removed Microsoft.ApplicationInsights.AspNetCore: 2.16.0
|
||||
|
||||
### Credential Encryption
|
||||
- [x] Added DPAPI configuration via Data Protection API
|
||||
- [x] Created CredentialEncryptor helper class
|
||||
- [x] Created ConnectionStringManager for connection string handling
|
||||
- [x] Updated Startup.cs with credential encryption services
|
||||
- [x] Implemented secure credential handling with fallback support
|
||||
|
||||
## Modernization Summary
|
||||
|
||||
### What Was Accomplished
|
||||
1. **Framework Upgrade**: Both projects successfully migrated from .NET 5.0 to .NET 9.0
|
||||
2. **Package Updates**: All critical NuGet packages updated to latest compatible versions
|
||||
3. **Security Fixes**: SkiaSharp CVE vulnerability fixed with upgrade to 3.119.1
|
||||
4. **Deprecated Dependencies**: Removed Application Insights (2.16.0) which was deprecated
|
||||
5. **Local Credential Encryption**: Implemented ASP.NET Core Data Protection API for secure credential storage
|
||||
6. **Backward Compatibility**: Existing plaintext credentials continue to work
|
||||
|
||||
### Files Modified
|
||||
- Marechai.Database/Marechai.Database.csproj
|
||||
- Marechai/Marechai.csproj
|
||||
- Marechai/Startup.cs
|
||||
- Marechai/Pages/_Host.cshtml
|
||||
|
||||
### Files Created
|
||||
- Marechai/Helpers/CredentialEncryptor.cs
|
||||
- Marechai/Helpers/ConnectionStringManager.cs
|
||||
- Marechai/CREDENTIAL_ENCRYPTION_GUIDE.md
|
||||
- .appmod/.migration/plan.md
|
||||
- .appmod/.migration/progress.md
|
||||
|
||||
## Build Status
|
||||
✅ **Build Successful** - No compilation errors
|
||||
|
||||
## Architecture Notes
|
||||
- All credential encryption is LOCAL-ONLY using Data Protection API (DPAPI)
|
||||
- No cloud services or Azure dependencies required
|
||||
- MariaDB database remains local and unchanged
|
||||
- Backward compatible with existing configurations
|
||||
- Graceful fallback from encrypted to plaintext credentials
|
||||
|
||||
## Next Steps
|
||||
1. Test the application with existing MariaDB connection
|
||||
2. Deploy to production environment
|
||||
3. (Optional) Encrypt connection strings in production using the provided guide
|
||||
|
||||
## Last Updated
|
||||
2024-11-13 01:58:16
|
||||
2101
.editorconfig
2101
.editorconfig
File diff suppressed because it is too large
Load Diff
2
.gitignore
vendored
2
.gitignore
vendored
@@ -442,6 +442,8 @@ $RECYCLE.BIN/
|
||||
|
||||
|
||||
# Assets
|
||||
Marechai/originals
|
||||
Marechai/wwwroot/assets/photos
|
||||
Marechai/wwwroot/assets/**/*.jpeg
|
||||
Marechai/wwwroot/assets/**/*.svg
|
||||
Marechai/wwwroot/assets/**/*.png
|
||||
|
||||
3
.idea/.idea.Marechai/.idea/indexLayout.xml
generated
3
.idea/.idea.Marechai/.idea/indexLayout.xml
generated
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ContentModelUserStore">
|
||||
<component name="UserContentModel">
|
||||
<attachedFolders />
|
||||
<explicitIncludes />
|
||||
<explicitExcludes />
|
||||
</component>
|
||||
|
||||
8
.idea/.idea.Marechai/.idea/modules.xml
generated
8
.idea/.idea.Marechai/.idea/modules.xml
generated
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/.idea.Marechai/.idea/riderModule.iml" filepath="$PROJECT_DIR$/.idea/.idea.Marechai/.idea/riderModule.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
14
.idea/.idea.Marechai/riderModule.iml
generated
14
.idea/.idea.Marechai/riderModule.iml
generated
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="RIDER_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$/../.." />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="R User Library" level="project" />
|
||||
<orderEntry type="library" name="R Skeletons" level="application" />
|
||||
<orderEntry type="library" name="bootstrap" level="application" />
|
||||
<orderEntry type="library" name="all" level="application" />
|
||||
<orderEntry type="library" name="jquery-3.3.1.slim" level="application" />
|
||||
<orderEntry type="library" name="popper.js" level="application" />
|
||||
<orderEntry type="library" name="analytics" level="application" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -1,27 +1,34 @@
|
||||
# Contributing
|
||||
|
||||
## Commit signature
|
||||
|
||||
For security reason we require all commits to be cryptographically signed.
|
||||
This section explains how to setup the development environment for that purpose.
|
||||
|
||||
### Visual Studio and Visual Studio Code for Windows
|
||||
You need to install Git for Windows. It is available as a component of Visual Studio, or separately in https://gitforwindows.org.
|
||||
|
||||
You need to install Git for Windows. It is available as a component of Visual Studio, or separately
|
||||
in https://gitforwindows.org.
|
||||
|
||||
You also need to install Gpg4win from https://www.gpg4win.org. Ensure to select the Kleopatra component.
|
||||
|
||||
Once you have them installed, open Kleopatra and generate a new key pair, of OpenPGP type, following the instructions [here](https://www.gpg4win.org/doc/en/gpg4win-compendium_12.html).
|
||||
Once you have them installed, open Kleopatra and generate a new key pair, of OpenPGP type, following the
|
||||
instructions [here](https://www.gpg4win.org/doc/en/gpg4win-compendium_12.html).
|
||||
Save aside the fingerprint, you'll need it later.
|
||||
|
||||
Now go to environment variables (in the properties of your computer) and add this to the path:
|
||||
`C:\Program Files\Git\usr\bin`
|
||||
|
||||
Finally, open Git Bash, and write the following commands if you want all git commits to be signed:
|
||||
|
||||
```bash
|
||||
git config --global commit.gpgsign true
|
||||
git config --global user.signingkey <FINGERPRINT>
|
||||
git config --global gpg.program "C:\Program Files (x86)\GnuPG\bin\gpg.exe"
|
||||
```
|
||||
|
||||
or if you want the options to apply only for this project
|
||||
|
||||
```bash
|
||||
cd /DRIVE/PATH_TO_PROJECT
|
||||
git config commit.gpgsign true
|
||||
@@ -29,8 +36,11 @@ git config user.signingkey FINGERPRINT
|
||||
git config gpg.program "C:\Program Files (x86)\GnuPG\bin\gpg.exe"
|
||||
```
|
||||
|
||||
replacing `FINGERPRINT` with the fingerprint you saved from the key generation, `DRIVE` with the drive letter and `PATH_TO_PROJECT` using `/` as path separator.
|
||||
replacing `FINGERPRINT` with the fingerprint you saved from the key generation, `DRIVE` with the drive letter and
|
||||
`PATH_TO_PROJECT` using `/` as path separator.
|
||||
|
||||
Once this is done, every time you commit in VS / VSCode, a message box titled `pinentry-qt` will ask for the passphrase you set up earlier and sign the commit with your key.
|
||||
Once this is done, every time you commit in VS / VSCode, a message box titled `pinentry-qt` will ask for the passphrase
|
||||
you set up earlier and sign the commit with your key.
|
||||
|
||||
For GitHub to recognize your signature you need to follow the steps [here](https://help.github.com/en/github/authenticating-to-github/adding-a-new-gpg-key-to-your-github-account).
|
||||
For GitHub to recognize your signature you need to follow the
|
||||
steps [here](https://help.github.com/en/github/authenticating-to-github/adding-a-new-gpg-key-to-your-github-account).
|
||||
17
Directory.Build.props
Normal file
17
Directory.Build.props
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Company>Canary Islands Computer Museum</Company>
|
||||
<Copyright>Copyright © 2003-2026 Natalia Portillo</Copyright>
|
||||
<Product>Canary Islands Computer Museum Website</Product>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- Common duplicated packages -->
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore"/>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies"/>
|
||||
<PackageReference Include="Packaging.Targets">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
1
Directory.Build.targets
Normal file
1
Directory.Build.targets
Normal file
@@ -0,0 +1 @@
|
||||
<Project></Project>
|
||||
37
Directory.Packages.props
Normal file
37
Directory.Packages.props
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- Duplicated packages (also in Directory.Build.props) -->
|
||||
<PackageVersion Include="Humanizer" Version="2.14.1"/>
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.11"/>
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.11"/>
|
||||
<!-- Unique to Marechai.csproj -->
|
||||
<PackageVersion Include="Blazorise.Bootstrap" Version="0.9.2.4"/>
|
||||
<PackageVersion Include="Blazorise.Icons.FontAwesome" Version="0.9.2.4"/>
|
||||
<PackageVersion Include="Markdig" Version="0.22.1"/>
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="9.0.11"/>
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Identity.UI" Version="9.0.11"/>
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.11"/>
|
||||
<PackageVersion Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="9.0.0"/>
|
||||
<PackageVersion Include="MySql.Data" Version="8.0.22"/>
|
||||
<PackageVersion Include="SkiaSharp" Version="3.119.1"/>
|
||||
<PackageVersion Include="SkiaSharp.NativeAssets.Linux" Version="3.119.1"/>
|
||||
<PackageVersion Include="Svg.Skia" Version="0.4.1"/>
|
||||
<PackageVersion Include="Tewr.Blazor.FileReader" Version="3.0.0.20340"/>
|
||||
<PackageVersion Include="Unclassified.NetRevisionTask" Version="0.3.0"/>
|
||||
<!-- Unique to Marechai.Database.csproj -->
|
||||
<PackageVersion Include="Aaru.CommonTypes" Version="5.4.1"/>
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.11"/>
|
||||
<PackageVersion Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0"/>
|
||||
<PackageVersion Include="Pomelo.EntityFrameworkCore.MySql.Json.Microsoft" Version="9.0.0"/>
|
||||
<!-- Build infrastructure -->
|
||||
<PackageVersion Include="Packaging.Targets" Version="0.1.189"/>
|
||||
<!-- Unique to Marechai.Server.csproj -->
|
||||
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0"/>
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.11"/>
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.11"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
BIN
Documentation/Software Architecture Document.pdf
Normal file
BIN
Documentation/Software Architecture Document.pdf
Normal file
Binary file not shown.
23
Marechai.App/App.xaml
Normal file
23
Marechai.App/App.xaml
Normal file
@@ -0,0 +1,23 @@
|
||||
<Application x:Class="Marechai.App.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Marechai.App.Presentation.Converters">
|
||||
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<!-- Load WinUI resources -->
|
||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||
<!-- Load Uno.UI.Toolkit resources -->
|
||||
<ToolkitResources xmlns="using:Uno.Toolkit.UI" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<!-- Add resources here -->
|
||||
<local:ObjectToVisibilityConverter x:Key="ObjectToVisibilityConverter" />
|
||||
<local:StringToVisibilityConverter x:Key="StringToVisibilityConverter" />
|
||||
<local:ZeroToVisibilityConverter x:Key="ZeroToVisibilityConverter" />
|
||||
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
|
||||
</Application>
|
||||
205
Marechai.App/App.xaml.cs
Normal file
205
Marechai.App/App.xaml.cs
Normal file
@@ -0,0 +1,205 @@
|
||||
using System.Net.Http;
|
||||
using Marechai.App.Presentation.ViewModels;
|
||||
using Marechai.App.Presentation.Views;
|
||||
using Marechai.App.Services;
|
||||
using Marechai.App.Services.Caching;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Uno.Extensions;
|
||||
using Uno.Extensions.Configuration;
|
||||
using Uno.Extensions.Hosting;
|
||||
using Uno.Extensions.Http;
|
||||
using Uno.Extensions.Localization;
|
||||
using Uno.Extensions.Navigation;
|
||||
using Uno.UI;
|
||||
using CompanyDetailViewModel = Marechai.App.Presentation.ViewModels.CompanyDetailViewModel;
|
||||
using ComputersListViewModel = Marechai.App.Presentation.ViewModels.ComputersListViewModel;
|
||||
using ComputersViewModel = Marechai.App.Presentation.ViewModels.ComputersViewModel;
|
||||
using MachineViewViewModel = Marechai.App.Presentation.ViewModels.MachineViewViewModel;
|
||||
using MainViewModel = Marechai.App.Presentation.ViewModels.MainViewModel;
|
||||
using NewsViewModel = Marechai.App.Presentation.ViewModels.NewsViewModel;
|
||||
|
||||
namespace Marechai.App;
|
||||
|
||||
public partial class App : Application
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes the singleton application object. This is the first line of authored code
|
||||
/// executed, and as such is the logical equivalent of main() or WinMain().
|
||||
/// </summary>
|
||||
public App()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected Window? MainWindow { get; private set; }
|
||||
protected IHost? Host { get; private set; }
|
||||
|
||||
protected override async void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
IApplicationBuilder builder = this.CreateBuilder(args)
|
||||
|
||||
// Add navigation support for toolkit controls such as TabBar and NavigationView
|
||||
.UseToolkitNavigation()
|
||||
.Configure(host => host
|
||||
#if DEBUG
|
||||
|
||||
// Switch to Development environment when running in DEBUG
|
||||
.UseEnvironment(Environments.Development)
|
||||
#endif
|
||||
.UseLogging((context, logBuilder) =>
|
||||
{
|
||||
// Configure log levels for different categories of logging
|
||||
logBuilder
|
||||
.SetMinimumLevel(context
|
||||
.HostingEnvironment
|
||||
.IsDevelopment()
|
||||
? LogLevel.Information
|
||||
: LogLevel.Warning)
|
||||
|
||||
// Default filters for core Uno Platform namespaces
|
||||
.CoreLogLevel(LogLevel.Warning);
|
||||
|
||||
// Uno Platform namespace filter groups
|
||||
// Uncomment individual methods to see more detailed logging
|
||||
//// Generic Xaml events
|
||||
//logBuilder.XamlLogLevel(LogLevel.Debug);
|
||||
//// Layout specific messages
|
||||
//logBuilder.XamlLayoutLogLevel(LogLevel.Debug);
|
||||
//// Storage messages
|
||||
//logBuilder.StorageLogLevel(LogLevel.Debug);
|
||||
//// Binding related messages
|
||||
//logBuilder.XamlBindingLogLevel(LogLevel.Debug);
|
||||
//// Binder memory references tracking
|
||||
//logBuilder.BinderMemoryReferenceLogLevel(LogLevel.Debug);
|
||||
//// DevServer and HotReload related
|
||||
//logBuilder.HotReloadCoreLogLevel(LogLevel.Information);
|
||||
//// Debug JS interop
|
||||
//logBuilder.WebAssemblyLogLevel(LogLevel.Debug);
|
||||
},
|
||||
true)
|
||||
.UseSerilog(true, true)
|
||||
.UseConfiguration(configure: configBuilder =>
|
||||
configBuilder.EmbeddedSource<App>()
|
||||
.Section<AppConfig>())
|
||||
|
||||
// Enable localization (see appsettings.json for supported languages)
|
||||
.UseLocalization()
|
||||
.UseHttp((context, services) =>
|
||||
{
|
||||
#if DEBUG
|
||||
|
||||
// DelegatingHandler will be automatically injected
|
||||
services
|
||||
.AddTransient<DelegatingHandler,
|
||||
DebugHttpHandler>();
|
||||
#endif
|
||||
services.AddKiotaClientV2<ApiClient>(context,
|
||||
new EndpointOptions
|
||||
{
|
||||
Url = context.Configuration
|
||||
.GetSection("ApiClient:Url")
|
||||
.Value ??
|
||||
|
||||
// Fallback to a default URL if not configured
|
||||
"https://localhost:5023"
|
||||
});
|
||||
})
|
||||
.ConfigureServices((context, services) =>
|
||||
{
|
||||
// Register application services
|
||||
services.AddSingleton<FlagCache>();
|
||||
services.AddSingleton<CompanyLogoCache>();
|
||||
services.AddSingleton<NewsService>();
|
||||
services.AddSingleton<NewsViewModel>();
|
||||
services.AddSingleton<ComputersService>();
|
||||
services.AddSingleton<ComputersViewModel>();
|
||||
services.AddSingleton<ConsolesService>();
|
||||
services.AddSingleton<ConsolesViewModel>();
|
||||
services.AddSingleton<CompaniesService>();
|
||||
services.AddSingleton<CompaniesViewModel>();
|
||||
services.AddSingleton<CompanyDetailService>();
|
||||
services.AddSingleton<CompanyDetailViewModel>();
|
||||
services.AddSingleton<MachineViewViewModel>();
|
||||
|
||||
services
|
||||
.AddSingleton<IComputersListFilterContext,
|
||||
ComputersListFilterContext>();
|
||||
|
||||
services
|
||||
.AddSingleton<IConsolesListFilterContext,
|
||||
ConsolesListFilterContext>();
|
||||
|
||||
services.AddTransient<ComputersListViewModel>();
|
||||
services.AddTransient<ConsolesListViewModel>();
|
||||
})
|
||||
.UseNavigation(RegisterRoutes));
|
||||
|
||||
MainWindow = builder.Window;
|
||||
|
||||
#if DEBUG
|
||||
MainWindow.UseStudio();
|
||||
#endif
|
||||
MainWindow.SetWindowIcon();
|
||||
|
||||
Host = await builder.NavigateAsync<Shell>();
|
||||
}
|
||||
|
||||
private static void RegisterRoutes(IViewRegistry views, IRouteRegistry routes)
|
||||
{
|
||||
views.Register(new ViewMap(ViewModel: typeof(ShellViewModel)),
|
||||
new ViewMap<MainPage, MainViewModel>(),
|
||||
new ViewMap<NewsPage, NewsViewModel>(),
|
||||
new ViewMap<ComputersPage, ComputersViewModel>(),
|
||||
new ViewMap<ComputersListPage, ComputersListViewModel>(),
|
||||
new ViewMap<ConsolesPage, ConsolesViewModel>(),
|
||||
new ViewMap<ConsolesListPage, ConsolesListViewModel>(),
|
||||
new ViewMap<CompaniesPage, CompaniesViewModel>(),
|
||||
new ViewMap<CompanyDetailPage, CompanyDetailViewModel>(),
|
||||
new ViewMap<MachineViewPage, MachineViewViewModel>(),
|
||||
new DataViewMap<SecondPage, SecondViewModel, Entity>());
|
||||
|
||||
routes.Register(new RouteMap("",
|
||||
views.FindByViewModel<ShellViewModel>(),
|
||||
Nested:
|
||||
[
|
||||
new RouteMap("Main",
|
||||
views.FindByViewModel<MainViewModel>(),
|
||||
true,
|
||||
Nested:
|
||||
[
|
||||
new RouteMap("News",
|
||||
views.FindByViewModel<NewsViewModel>(),
|
||||
true),
|
||||
new RouteMap("computers",
|
||||
views.FindByViewModel<ComputersViewModel>(),
|
||||
Nested:
|
||||
[
|
||||
new RouteMap("list-computers",
|
||||
views.FindByViewModel<
|
||||
ComputersListViewModel>()),
|
||||
new RouteMap("view",
|
||||
views.FindByViewModel<
|
||||
MachineViewViewModel>())
|
||||
]),
|
||||
new RouteMap("consoles",
|
||||
views.FindByViewModel<ConsolesViewModel>(),
|
||||
Nested:
|
||||
[
|
||||
new RouteMap("list-consoles",
|
||||
views.FindByViewModel<
|
||||
ConsolesListViewModel>())
|
||||
]),
|
||||
new RouteMap("companies",
|
||||
views.FindByViewModel<CompaniesViewModel>(),
|
||||
Nested:
|
||||
[
|
||||
new RouteMap("detail",
|
||||
views.FindByViewModel<
|
||||
CompanyDetailViewModel>())
|
||||
]),
|
||||
new RouteMap("Second",
|
||||
views.FindByViewModel<SecondViewModel>())
|
||||
])
|
||||
]));
|
||||
}
|
||||
}
|
||||
42
Marechai.App/Assets/Icons/icon.svg
Normal file
42
Marechai.App/Assets/Icons/icon.svg
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="456"
|
||||
height="456"
|
||||
viewBox="0 0 456 456"
|
||||
version="1.1"
|
||||
id="svg453"
|
||||
sodipodi:docname="icon.svg"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs457" />
|
||||
<sodipodi:namedview
|
||||
id="namedview455"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.8574561"
|
||||
inkscape:cx="228.26919"
|
||||
inkscape:cy="228.26919"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1027"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg453" />
|
||||
<rect
|
||||
x="0"
|
||||
y="0"
|
||||
width="456"
|
||||
height="456"
|
||||
fill="#FFFFFF"
|
||||
id="rect451" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
137
Marechai.App/Assets/Icons/icon_foreground.svg
Normal file
137
Marechai.App/Assets/Icons/icon_foreground.svg
Normal file
@@ -0,0 +1,137 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="450"
|
||||
height="450"
|
||||
viewBox="0 0 50.369617 49.826836"
|
||||
version="1.1"
|
||||
id="svg151"
|
||||
sodipodi:docname="icon_foreground.svg"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview153"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.250876"
|
||||
inkscape:cx="218.64677"
|
||||
inkscape:cy="175.87674"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1027"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g149" />
|
||||
<defs
|
||||
id="defs105">
|
||||
<path
|
||||
id="aj28a0fd1a"
|
||||
d="M 1.738,0.156 3.927,2.323 2.347,3.919 0.101,1.81 Z" />
|
||||
<path
|
||||
id="fdje57jgic"
|
||||
d="M 2.201,0.066 3.855,1.703 1.69,3.894 0.093,2.311 Z" />
|
||||
<path
|
||||
id="6bg72xwlze"
|
||||
d="M 2.398,0.044 3.994,1.624 1.886,3.869 0.232,2.232 Z" />
|
||||
<path
|
||||
id="eaqjnja8wg"
|
||||
d="M 1.736,0.023 3.981,2.132 2.344,3.786 0.156,1.619 Z" />
|
||||
</defs>
|
||||
<g
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
id="g149"
|
||||
transform="translate(-2.9304427e-4,-1.6465461e-4)">
|
||||
<g
|
||||
id="g147">
|
||||
<g
|
||||
id="g145">
|
||||
<path
|
||||
fill="#7a67f8"
|
||||
d="M 34.758,38.865 H 34.746 C 31.892,38.86 29.342,36.882 26.152,33.692 l -6.93,-6.873 2.166,-2.188 6.937,6.88 c 3.075,3.074 4.876,4.272 6.427,4.275 h 0.005 c 1.567,0 3.467,-1.262 6.558,-4.353 l 3.541,-3.587 c 1.784,-1.784 2.57,-3.34 2.408,-4.762 -0.13,-1.156 -0.894,-2.397 -2.401,-3.904 L 44.83,19.146 C 43.202,17.414 41.211,15.483 39.131,14.414 38.745,12.437 37.48,10.881 37.3,10.596 c 3.803,0.559 7.197,3.703 9.758,6.424 2.788,2.794 5.803,7.176 -0.018,12.996 l -3.54,3.588 c -3.251,3.25 -5.844,5.261 -8.742,5.261"
|
||||
id="path107" />
|
||||
<path
|
||||
fill="#f85977"
|
||||
d="m 25.399,28.608 6.492,-6.562 c 3.076,-3.076 4.274,-4.877 4.276,-6.428 0.004,-1.567 -1.257,-3.469 -4.352,-6.563 L 28.228,5.515 C 24.58,1.867 22.369,2.699 19.561,5.507 L 19.528,5.54 c -1.54,1.448 -3.237,3.182 -4.346,5.01 -1.031,0.073 -2.361,0.424 -3.997,1.518 0.906,-3.397 3.737,-6.422 6.216,-8.755 2.794,-2.789 7.177,-5.804 12.997,0.017 l 3.588,3.54 c 3.255,3.256 5.266,5.851 5.26,8.754 -0.005,2.854 -1.982,5.404 -5.172,8.594 l -6.489,6.559 z"
|
||||
id="path109" />
|
||||
<path
|
||||
fill="#159bff"
|
||||
d="M 12.522,38.707 C 8.939,37.946 5.746,34.972 3.308,32.382 2.035,31.106 0.321,29.13 0.042,26.663 c -0.274,-2.414 0.8,-4.795 3.283,-7.278 l 3.542,-3.588 c 3.25,-3.25 5.843,-5.261 8.74,-5.261 h 0.013 c 2.854,0.005 5.404,1.983 8.593,5.172 l 7.046,6.976 -2.165,2.19 -7.053,-6.983 c -3.076,-3.076 -4.876,-4.273 -6.427,-4.276 h -0.006 c -1.566,0 -3.466,1.261 -6.557,4.352 L 5.51,21.555 c -1.784,1.784 -2.57,3.34 -2.409,4.762 0.131,1.156 0.894,2.396 2.402,3.904 l 0.033,0.034 c 1.55,1.649 3.43,3.479 5.401,4.573 0.168,1.739 1.2,3.297 1.585,3.88"
|
||||
id="path111" />
|
||||
<path
|
||||
fill="#67e5ad"
|
||||
d="m 26.32,49.827 c -1.925,0 -4.114,-0.886 -6.557,-3.33 l -3.588,-3.54 C 9.167,35.949 9.151,32.546 16.086,25.61 l 6.802,-6.872 2.193,2.162 -6.812,6.882 c -3.076,3.076 -4.273,4.877 -4.276,6.427 -0.003,1.568 1.258,3.47 4.352,6.563 l 3.588,3.541 c 3.646,3.647 5.858,2.816 8.666,0.008 l 0.034,-0.033 c 1.654,-1.555 3.5,-3.46 4.593,-5.437 1.661,-0.14 2.9,-0.841 3.835,-1.438 -0.8,3.537 -3.738,6.69 -6.302,9.102 -1.62,1.618 -3.777,3.312 -6.439,3.312"
|
||||
id="path113" />
|
||||
<g
|
||||
transform="translate(21.154,18.577)"
|
||||
id="g120">
|
||||
<mask
|
||||
id="8jptpqrneb"
|
||||
fill="#ffffff">
|
||||
<use
|
||||
xlink:href="#aj28a0fd1a"
|
||||
id="use115" />
|
||||
</mask>
|
||||
<path
|
||||
d="M 0.101,1.81 1.738,0.156 3.927,2.323 2.347,3.919 Z"
|
||||
mask="url(#8jptpqrneb)"
|
||||
id="path118" />
|
||||
</g>
|
||||
<g
|
||||
transform="translate(27.404,20.981)"
|
||||
id="g127">
|
||||
<mask
|
||||
id="b2iljpfwbd"
|
||||
fill="#ffffff">
|
||||
<use
|
||||
xlink:href="#fdje57jgic"
|
||||
id="use122" />
|
||||
</mask>
|
||||
<path
|
||||
d="M 2.201,0.066 3.855,1.703 1.69,3.894 0.093,2.311 Z"
|
||||
mask="url(#b2iljpfwbd)"
|
||||
id="path125" />
|
||||
</g>
|
||||
<g
|
||||
transform="translate(18.99,24.587)"
|
||||
id="g134">
|
||||
<mask
|
||||
id="gj70tyfpnf"
|
||||
fill="#ffffff">
|
||||
<use
|
||||
xlink:href="#6bg72xwlze"
|
||||
id="use129" />
|
||||
</mask>
|
||||
<path
|
||||
d="M 1.886,3.869 0.232,2.232 2.398,0.044 3.994,1.624 Z"
|
||||
mask="url(#gj70tyfpnf)"
|
||||
id="path132" />
|
||||
</g>
|
||||
<g
|
||||
transform="translate(25.24,26.99)"
|
||||
id="g141">
|
||||
<mask
|
||||
id="z7vhvduckh"
|
||||
fill="#ffffff">
|
||||
<use
|
||||
xlink:href="#eaqjnja8wg"
|
||||
id="use136" />
|
||||
</mask>
|
||||
<path
|
||||
d="M 3.981,2.132 2.344,3.786 0.156,1.619 1.736,0.023 Z"
|
||||
mask="url(#z7vhvduckh)"
|
||||
id="path139" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.5 KiB |
32
Marechai.App/Assets/SharedAssets.md
Normal file
32
Marechai.App/Assets/SharedAssets.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Shared Assets
|
||||
|
||||
See documentation about assets here: https://github.com/unoplatform/uno/blob/master/doc/articles/features/working-with-assets.md
|
||||
|
||||
## Here is a cheat sheet
|
||||
|
||||
1. Add the image file to the `Assets` directory of a shared project.
|
||||
2. Set the build action to `Content`.
|
||||
3. (Recommended) Provide an asset for various scales/dpi
|
||||
|
||||
### Examples
|
||||
|
||||
```text
|
||||
\Assets\Images\logo.scale-100.png
|
||||
\Assets\Images\logo.scale-200.png
|
||||
\Assets\Images\logo.scale-400.png
|
||||
|
||||
\Assets\Images\scale-100\logo.png
|
||||
\Assets\Images\scale-200\logo.png
|
||||
\Assets\Images\scale-400\logo.png
|
||||
```
|
||||
|
||||
### Table of scales
|
||||
|
||||
| Scale | WinUI | iOS | Android |
|
||||
|-------|:-----------:|:---------------:|:-------:|
|
||||
| `100` | scale-100 | @1x | mdpi |
|
||||
| `125` | scale-125 | N/A | N/A |
|
||||
| `150` | scale-150 | N/A | hdpi |
|
||||
| `200` | scale-200 | @2x | xhdpi |
|
||||
| `300` | scale-300 | @3x | xxhdpi |
|
||||
| `400` | scale-400 | N/A | xxxhdpi |
|
||||
137
Marechai.App/Assets/Splash/splash_screen.svg
Normal file
137
Marechai.App/Assets/Splash/splash_screen.svg
Normal file
@@ -0,0 +1,137 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="450"
|
||||
height="450"
|
||||
viewBox="0 0 50.369617 49.826836"
|
||||
version="1.1"
|
||||
id="svg151"
|
||||
sodipodi:docname="icon_foreground.svg"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview153"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.250876"
|
||||
inkscape:cx="218.64677"
|
||||
inkscape:cy="175.87674"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1027"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g149" />
|
||||
<defs
|
||||
id="defs105">
|
||||
<path
|
||||
id="aj28a0fd1a"
|
||||
d="M 1.738,0.156 3.927,2.323 2.347,3.919 0.101,1.81 Z" />
|
||||
<path
|
||||
id="fdje57jgic"
|
||||
d="M 2.201,0.066 3.855,1.703 1.69,3.894 0.093,2.311 Z" />
|
||||
<path
|
||||
id="6bg72xwlze"
|
||||
d="M 2.398,0.044 3.994,1.624 1.886,3.869 0.232,2.232 Z" />
|
||||
<path
|
||||
id="eaqjnja8wg"
|
||||
d="M 1.736,0.023 3.981,2.132 2.344,3.786 0.156,1.619 Z" />
|
||||
</defs>
|
||||
<g
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
id="g149"
|
||||
transform="translate(-2.9304427e-4,-1.6465461e-4)">
|
||||
<g
|
||||
id="g147">
|
||||
<g
|
||||
id="g145">
|
||||
<path
|
||||
fill="#7a67f8"
|
||||
d="M 34.758,38.865 H 34.746 C 31.892,38.86 29.342,36.882 26.152,33.692 l -6.93,-6.873 2.166,-2.188 6.937,6.88 c 3.075,3.074 4.876,4.272 6.427,4.275 h 0.005 c 1.567,0 3.467,-1.262 6.558,-4.353 l 3.541,-3.587 c 1.784,-1.784 2.57,-3.34 2.408,-4.762 -0.13,-1.156 -0.894,-2.397 -2.401,-3.904 L 44.83,19.146 C 43.202,17.414 41.211,15.483 39.131,14.414 38.745,12.437 37.48,10.881 37.3,10.596 c 3.803,0.559 7.197,3.703 9.758,6.424 2.788,2.794 5.803,7.176 -0.018,12.996 l -3.54,3.588 c -3.251,3.25 -5.844,5.261 -8.742,5.261"
|
||||
id="path107" />
|
||||
<path
|
||||
fill="#f85977"
|
||||
d="m 25.399,28.608 6.492,-6.562 c 3.076,-3.076 4.274,-4.877 4.276,-6.428 0.004,-1.567 -1.257,-3.469 -4.352,-6.563 L 28.228,5.515 C 24.58,1.867 22.369,2.699 19.561,5.507 L 19.528,5.54 c -1.54,1.448 -3.237,3.182 -4.346,5.01 -1.031,0.073 -2.361,0.424 -3.997,1.518 0.906,-3.397 3.737,-6.422 6.216,-8.755 2.794,-2.789 7.177,-5.804 12.997,0.017 l 3.588,3.54 c 3.255,3.256 5.266,5.851 5.26,8.754 -0.005,2.854 -1.982,5.404 -5.172,8.594 l -6.489,6.559 z"
|
||||
id="path109" />
|
||||
<path
|
||||
fill="#159bff"
|
||||
d="M 12.522,38.707 C 8.939,37.946 5.746,34.972 3.308,32.382 2.035,31.106 0.321,29.13 0.042,26.663 c -0.274,-2.414 0.8,-4.795 3.283,-7.278 l 3.542,-3.588 c 3.25,-3.25 5.843,-5.261 8.74,-5.261 h 0.013 c 2.854,0.005 5.404,1.983 8.593,5.172 l 7.046,6.976 -2.165,2.19 -7.053,-6.983 c -3.076,-3.076 -4.876,-4.273 -6.427,-4.276 h -0.006 c -1.566,0 -3.466,1.261 -6.557,4.352 L 5.51,21.555 c -1.784,1.784 -2.57,3.34 -2.409,4.762 0.131,1.156 0.894,2.396 2.402,3.904 l 0.033,0.034 c 1.55,1.649 3.43,3.479 5.401,4.573 0.168,1.739 1.2,3.297 1.585,3.88"
|
||||
id="path111" />
|
||||
<path
|
||||
fill="#67e5ad"
|
||||
d="m 26.32,49.827 c -1.925,0 -4.114,-0.886 -6.557,-3.33 l -3.588,-3.54 C 9.167,35.949 9.151,32.546 16.086,25.61 l 6.802,-6.872 2.193,2.162 -6.812,6.882 c -3.076,3.076 -4.273,4.877 -4.276,6.427 -0.003,1.568 1.258,3.47 4.352,6.563 l 3.588,3.541 c 3.646,3.647 5.858,2.816 8.666,0.008 l 0.034,-0.033 c 1.654,-1.555 3.5,-3.46 4.593,-5.437 1.661,-0.14 2.9,-0.841 3.835,-1.438 -0.8,3.537 -3.738,6.69 -6.302,9.102 -1.62,1.618 -3.777,3.312 -6.439,3.312"
|
||||
id="path113" />
|
||||
<g
|
||||
transform="translate(21.154,18.577)"
|
||||
id="g120">
|
||||
<mask
|
||||
id="8jptpqrneb"
|
||||
fill="#ffffff">
|
||||
<use
|
||||
xlink:href="#aj28a0fd1a"
|
||||
id="use115" />
|
||||
</mask>
|
||||
<path
|
||||
d="M 0.101,1.81 1.738,0.156 3.927,2.323 2.347,3.919 Z"
|
||||
mask="url(#8jptpqrneb)"
|
||||
id="path118" />
|
||||
</g>
|
||||
<g
|
||||
transform="translate(27.404,20.981)"
|
||||
id="g127">
|
||||
<mask
|
||||
id="b2iljpfwbd"
|
||||
fill="#ffffff">
|
||||
<use
|
||||
xlink:href="#fdje57jgic"
|
||||
id="use122" />
|
||||
</mask>
|
||||
<path
|
||||
d="M 2.201,0.066 3.855,1.703 1.69,3.894 0.093,2.311 Z"
|
||||
mask="url(#b2iljpfwbd)"
|
||||
id="path125" />
|
||||
</g>
|
||||
<g
|
||||
transform="translate(18.99,24.587)"
|
||||
id="g134">
|
||||
<mask
|
||||
id="gj70tyfpnf"
|
||||
fill="#ffffff">
|
||||
<use
|
||||
xlink:href="#6bg72xwlze"
|
||||
id="use129" />
|
||||
</mask>
|
||||
<path
|
||||
d="M 1.886,3.869 0.232,2.232 2.398,0.044 3.994,1.624 Z"
|
||||
mask="url(#gj70tyfpnf)"
|
||||
id="path132" />
|
||||
</g>
|
||||
<g
|
||||
transform="translate(25.24,26.99)"
|
||||
id="g141">
|
||||
<mask
|
||||
id="z7vhvduckh"
|
||||
fill="#ffffff">
|
||||
<use
|
||||
xlink:href="#eaqjnja8wg"
|
||||
id="use136" />
|
||||
</mask>
|
||||
<path
|
||||
d="M 3.981,2.132 2.344,3.786 0.156,1.619 1.736,0.023 Z"
|
||||
mask="url(#z7vhvduckh)"
|
||||
id="path139" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.5 KiB |
13
Marechai.App/GlobalUsings.cs
Normal file
13
Marechai.App/GlobalUsings.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
global using System.Collections.Immutable;
|
||||
global using Microsoft.Extensions.DependencyInjection;
|
||||
global using Microsoft.Extensions.Hosting;
|
||||
global using Microsoft.Extensions.Localization;
|
||||
global using Microsoft.Extensions.Logging;
|
||||
global using Microsoft.Extensions.Options;
|
||||
global using Marechai.App.Models;
|
||||
global using Marechai.App.Presentation;
|
||||
global using Marechai.App.Services.Endpoints;
|
||||
global using Uno.Extensions.Http.Kiota;
|
||||
global using ApplicationExecutionState = Windows.ApplicationModel.Activation.ApplicationExecutionState;
|
||||
global using CommunityToolkit.Mvvm.ComponentModel;
|
||||
global using CommunityToolkit.Mvvm.Input;
|
||||
61
Marechai.App/Helpers/UntypedNodeExtractor.cs
Normal file
61
Marechai.App/Helpers/UntypedNodeExtractor.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using Microsoft.Kiota.Abstractions.Serialization;
|
||||
|
||||
namespace Marechai.App.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Helper class for extracting values from Kiota UntypedNode objects.
|
||||
/// </summary>
|
||||
public static class UntypedNodeExtractor
|
||||
{
|
||||
/// <summary>
|
||||
/// Extracts an integer value from an UntypedNode.
|
||||
/// </summary>
|
||||
/// <param name="node">The UntypedNode to extract from. Can be null.</param>
|
||||
/// <returns>The extracted integer value, or 0 if extraction fails.</returns>
|
||||
public static int ExtractInt(UntypedNode? node)
|
||||
{
|
||||
if(node == null) return 0;
|
||||
|
||||
try
|
||||
{
|
||||
// Cast to UntypedInteger to access the Value property
|
||||
if(node is UntypedInteger intNode) return intNode.GetValue();
|
||||
|
||||
// Fallback: try to parse ToString() result
|
||||
var stringValue = node.ToString();
|
||||
|
||||
if(!string.IsNullOrWhiteSpace(stringValue) && int.TryParse(stringValue, out int result)) return result;
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts a long value from an UntypedNode.
|
||||
/// </summary>
|
||||
/// <param name="node">The UntypedNode to extract from. Can be null.</param>
|
||||
/// <returns>The extracted long value, or 0 if extraction fails.</returns>
|
||||
public static long ExtractLong(UntypedNode? node)
|
||||
{
|
||||
if(node == null) return 0;
|
||||
|
||||
try
|
||||
{
|
||||
if(node is UntypedInteger intNode) return intNode.GetValue();
|
||||
|
||||
var stringValue = node.ToString();
|
||||
|
||||
if(!string.IsNullOrWhiteSpace(stringValue) && long.TryParse(stringValue, out long result)) return result;
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
83
Marechai.App/Marechai.App.csproj
Normal file
83
Marechai.App/Marechai.App.csproj
Normal file
@@ -0,0 +1,83 @@
|
||||
<Project Sdk="Uno.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net10.0-android;net10.0-browserwasm;net10.0-desktop</TargetFrameworks>
|
||||
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows')) Or $([MSBuild]::IsOSPlatform('macos'))">$(TargetFrameworks);net10.0-ios</TargetFrameworks>
|
||||
|
||||
<OutputType>Exe</OutputType>
|
||||
<UnoSingleProject>true</UnoSingleProject>
|
||||
|
||||
<!-- Display name -->
|
||||
<ApplicationTitle>Marechai.App</ApplicationTitle>
|
||||
<!-- App Identifier -->
|
||||
<ApplicationId>net.marechai.app</ApplicationId>
|
||||
<!-- Versions -->
|
||||
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>1</ApplicationVersion>
|
||||
<!-- Package Publisher -->
|
||||
<ApplicationPublisher>O=Marechai.App</ApplicationPublisher>
|
||||
<!-- Package Description -->
|
||||
<Description>Marechai.App powered by Uno Platform.</Description>
|
||||
|
||||
<!--
|
||||
UnoFeatures let's you quickly add and manage implicit package references based on the features you want to use.
|
||||
https://aka.platform.uno/singleproject-features
|
||||
-->
|
||||
<UnoFeatures>
|
||||
Lottie;
|
||||
Hosting;
|
||||
Toolkit;
|
||||
Logging;
|
||||
LoggingSerilog;
|
||||
Mvvm;
|
||||
Configuration;
|
||||
HttpKiota;
|
||||
Serialization;
|
||||
Localization;
|
||||
Navigation;
|
||||
ThemeService;
|
||||
SkiaRenderer;
|
||||
Svg;
|
||||
</UnoFeatures>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Marechai.Data\Marechai.Data.csproj"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Humanizer"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Presentation\Views\Shell.xaml.cs">
|
||||
<DependentUpon>Shell.xaml</DependentUpon>
|
||||
<IsDefaultItem>true</IsDefaultItem>
|
||||
</Compile>
|
||||
<Compile Update="Presentation\Views\SecondPage.xaml.cs">
|
||||
<DependentUpon>SecondPage.xaml</DependentUpon>
|
||||
<IsDefaultItem>true</IsDefaultItem>
|
||||
</Compile>
|
||||
<Compile Update="Presentation\Views\ComputersPage.xaml.cs">
|
||||
<DependentUpon>ComputersPage.xaml</DependentUpon>
|
||||
<IsDefaultItem>true</IsDefaultItem>
|
||||
</Compile>
|
||||
<Compile Update="Presentation\Views\MainPage.xaml.cs">
|
||||
<DependentUpon>MainPage.xaml</DependentUpon>
|
||||
<IsDefaultItem>true</IsDefaultItem>
|
||||
</Compile>
|
||||
<Compile Update="Presentation\Views\ComputersListPage.xaml.cs">
|
||||
<DependentUpon>ComputersListPage.xaml</DependentUpon>
|
||||
<IsDefaultItem>true</IsDefaultItem>
|
||||
</Compile>
|
||||
<Compile Update="Presentation\Views\NewsPage.xaml.cs">
|
||||
<DependentUpon>NewsPage.xaml</DependentUpon>
|
||||
<IsDefaultItem>true</IsDefaultItem>
|
||||
</Compile>
|
||||
<Compile Update="Presentation\Views\MachineViewPage.xaml.cs">
|
||||
<DependentUpon>MachineViewPage.xaml</DependentUpon>
|
||||
<IsDefaultItem>true</IsDefaultItem>
|
||||
</Compile>
|
||||
<Compile Update="Presentation\Components\Sidebar.xaml.cs">
|
||||
<DependentUpon>Sidebar.xaml</DependentUpon>
|
||||
<IsDefaultItem>true</IsDefaultItem>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
6
Marechai.App/Models/AppConfig.cs
Normal file
6
Marechai.App/Models/AppConfig.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Marechai.App.Models;
|
||||
|
||||
public record AppConfig
|
||||
{
|
||||
public string? Environment { get; init; }
|
||||
}
|
||||
3
Marechai.App/Models/Entity.cs
Normal file
3
Marechai.App/Models/Entity.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace Marechai.App.Models;
|
||||
|
||||
public record Entity(string Name);
|
||||
31
Marechai.App/Package.appxmanifest
Normal file
31
Marechai.App/Package.appxmanifest
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Package
|
||||
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
IgnorableNamespaces="uap rescap">
|
||||
|
||||
<Identity />
|
||||
<Properties />
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
<Resource Language="x-generate"/>
|
||||
</Resources>
|
||||
|
||||
<Applications>
|
||||
<Application Id="App"
|
||||
Executable="$targetnametoken$.exe"
|
||||
EntryPoint="$targetentrypoint$">
|
||||
<uap:VisualElements />
|
||||
</Application>
|
||||
</Applications>
|
||||
|
||||
<Capabilities>
|
||||
<rescap:Capability Name="runFullTrust" />
|
||||
</Capabilities>
|
||||
</Package>
|
||||
4
Marechai.App/Platforms/Android/AndroidManifest.xml
Normal file
4
Marechai.App/Platforms/Android/AndroidManifest.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<application android:allowBackup="true" android:supportsRtl="true"></application>
|
||||
</manifest>
|
||||
22
Marechai.App/Platforms/Android/Assets/AboutAssets.txt
Normal file
22
Marechai.App/Platforms/Android/Assets/AboutAssets.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
To add cross-platform image assets for your Uno Platform app, use the Assets folder
|
||||
in the shared project instead. Assets in this folder are Android-only assets.
|
||||
|
||||
Any raw assets you want to be deployed with your application can be placed in
|
||||
this directory (and child directories) and given a Build Action of "AndroidAsset".
|
||||
|
||||
These files will be deployed with your package and will be accessible using Android's
|
||||
AssetManager, like this:
|
||||
|
||||
public class ReadAsset : Activity
|
||||
{
|
||||
protected override void OnCreate (Bundle bundle)
|
||||
{
|
||||
base.OnCreate (bundle);
|
||||
|
||||
InputStream input = Assets.Open ("my_asset.txt");
|
||||
}
|
||||
}
|
||||
|
||||
Additionally, some Android functions will automatically load asset files:
|
||||
|
||||
Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");
|
||||
28
Marechai.App/Platforms/Android/Main.Android.cs
Normal file
28
Marechai.App/Platforms/Android/Main.Android.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
|
||||
namespace Marechai.App.Droid;
|
||||
|
||||
[global::Android.App.ApplicationAttribute(
|
||||
Label = "@string/ApplicationName",
|
||||
Icon = "@mipmap/icon",
|
||||
LargeHeap = true,
|
||||
HardwareAccelerated = true,
|
||||
Theme = "@style/Theme.App.Starting"
|
||||
)]
|
||||
public class Application : Microsoft.UI.Xaml.NativeApplication
|
||||
{
|
||||
public Application(IntPtr javaReference, JniHandleOwnership transfer)
|
||||
: base(() => new App(), javaReference, transfer)
|
||||
{
|
||||
}
|
||||
}
|
||||
22
Marechai.App/Platforms/Android/MainActivity.Android.cs
Normal file
22
Marechai.App/Platforms/Android/MainActivity.Android.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Android.App;
|
||||
using Android.Content.PM;
|
||||
using Android.OS;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
|
||||
namespace Marechai.App.Droid;
|
||||
|
||||
[Activity(
|
||||
MainLauncher = true,
|
||||
ConfigurationChanges = global::Uno.UI.ActivityHelper.AllConfigChanges,
|
||||
WindowSoftInputMode = SoftInput.AdjustNothing | SoftInput.StateHidden
|
||||
)]
|
||||
public class MainActivity : Microsoft.UI.Xaml.ApplicationActivity
|
||||
{
|
||||
protected override void OnCreate(Bundle? savedInstanceState)
|
||||
{
|
||||
global::AndroidX.Core.SplashScreen.SplashScreen.InstallSplashScreen(this);
|
||||
|
||||
base.OnCreate(savedInstanceState);
|
||||
}
|
||||
}
|
||||
47
Marechai.App/Platforms/Android/Resources/AboutResources.txt
Normal file
47
Marechai.App/Platforms/Android/Resources/AboutResources.txt
Normal file
@@ -0,0 +1,47 @@
|
||||
To add cross-platform image assets for your Uno Platform app, use the Assets folder
|
||||
in the shared project instead. Resources in this folder are Android-only.
|
||||
|
||||
Images, layout descriptions, binary blobs and string dictionaries can be included
|
||||
in your application as resource files. Various Android APIs are designed to
|
||||
operate on the resource IDs instead of dealing with images, strings or binary blobs
|
||||
directly.
|
||||
|
||||
For example, a sample Android app that contains a user interface layout (main.axml),
|
||||
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
|
||||
would keep its resources in the "Resources" directory of the application:
|
||||
|
||||
Resources/
|
||||
drawable/
|
||||
icon.png
|
||||
|
||||
layout/
|
||||
main.axml
|
||||
|
||||
values/
|
||||
strings.xml
|
||||
|
||||
In order to get the build system to recognize Android resources, set the build action to
|
||||
"AndroidResource". The native Android APIs do not operate directly with filenames, but
|
||||
instead operate on resource IDs. When you compile an Android application that uses resources,
|
||||
the build system will package the resources for distribution and generate a class called "R"
|
||||
(this is an Android convention) that contains the tokens for each one of the resources
|
||||
included. For example, for the above Resources layout, this is what the R class would expose:
|
||||
|
||||
public class R {
|
||||
public class drawable {
|
||||
public const int icon = 0x123;
|
||||
}
|
||||
|
||||
public class layout {
|
||||
public const int main = 0x456;
|
||||
}
|
||||
|
||||
public class strings {
|
||||
public const int first_string = 0xabc;
|
||||
public const int second_string = 0xbcd;
|
||||
}
|
||||
}
|
||||
|
||||
You would then use R.drawable.icon to reference the drawable/icon.png file, or R.layout.main
|
||||
to reference the layout/main.axml file, or R.strings.first_string to reference the first
|
||||
string in the dictionary file values/strings.xml.
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="Hello">Hello World, Click Me!</string>
|
||||
<string name="ApplicationName">Marechai.App</string>
|
||||
</resources>
|
||||
25
Marechai.App/Platforms/Android/Resources/values/Styles.xml
Normal file
25
Marechai.App/Platforms/Android/Resources/values/Styles.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<resources>
|
||||
<style name="AppTheme" parent="Theme.MaterialComponents.Light">
|
||||
<!-- This removes the ActionBar -->
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="android:windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.App.Starting" parent="Theme.SplashScreen">
|
||||
<!-- uno_splash_color and uno_splash_image are generated by Uno.Resizetizer -->
|
||||
<!-- This property is used for the splash screen -->
|
||||
<item name="android:windowSplashScreenBackground">@color/uno_splash_color</item>
|
||||
<item name="android:windowBackground">@drawable/uno_splash_image</item>
|
||||
<item name="android:windowSplashScreenAnimatedIcon">@drawable/uno_splash_image</item>
|
||||
|
||||
<item name="postSplashScreenTheme">@style/AppTheme</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.AppCompat.Translucent">
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
<item name="android:windowAnimationStyle">@android:style/Animation</item>
|
||||
</style>
|
||||
</resources>
|
||||
2
Marechai.App/Platforms/Android/environment.conf
Normal file
2
Marechai.App/Platforms/Android/environment.conf
Normal file
@@ -0,0 +1,2 @@
|
||||
# See this for more details: http://developer.xamarin.com/guides/android/advanced_topics/garbage_collection/
|
||||
MONO_GC_PARAMS=bridge-implementation=new,nursery-size=32m,soft-heap-limit=256m
|
||||
21
Marechai.App/Platforms/Desktop/Program.cs
Normal file
21
Marechai.App/Platforms/Desktop/Program.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using Uno.UI.Hosting;
|
||||
|
||||
namespace Marechai.App;
|
||||
|
||||
internal class Program
|
||||
{
|
||||
[STAThread]
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var host = UnoPlatformHostBuilder.Create()
|
||||
.App(() => new App())
|
||||
.UseX11()
|
||||
.UseLinuxFrameBuffer()
|
||||
.UseMacOS()
|
||||
.UseWin32()
|
||||
.Build();
|
||||
|
||||
host.Run();
|
||||
}
|
||||
}
|
||||
10
Marechai.App/Platforms/WebAssembly/LinkerConfig.xml
Normal file
10
Marechai.App/Platforms/WebAssembly/LinkerConfig.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<linker>
|
||||
<assembly fullname="Marechai.App" />
|
||||
|
||||
<!--
|
||||
Uncomment this section when using JSON.NET
|
||||
<assembly fullname="System.Core">
|
||||
<type fullname="System.Linq.Expressions*" />
|
||||
</assembly>
|
||||
-->
|
||||
</linker>
|
||||
17
Marechai.App/Platforms/WebAssembly/Program.cs
Normal file
17
Marechai.App/Platforms/WebAssembly/Program.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System.Threading.Tasks;
|
||||
using Uno.UI.Hosting;
|
||||
|
||||
namespace Marechai.App;
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
var host = UnoPlatformHostBuilder.Create()
|
||||
.App(() => new App())
|
||||
.UseWebAssembly()
|
||||
.Build();
|
||||
|
||||
await host.RunAsync();
|
||||
}
|
||||
}
|
||||
28
Marechai.App/Platforms/WebAssembly/WasmCSS/Fonts.css
Normal file
28
Marechai.App/Platforms/WebAssembly/WasmCSS/Fonts.css
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
When adding fonts here, make sure to add them using a base64 data uri, otherwise
|
||||
fonts loading are delayed, and text may get displayed incorrectly.
|
||||
*/
|
||||
|
||||
/* https://github.com/unoplatform/uno/issues/3954 */
|
||||
@font-face {
|
||||
font-family: 'Segoe UI';
|
||||
src: local('Segoe UI'), local('-apple-system'), local('BlinkMacSystemFont'), local('Inter'), local('Cantarell'), local('Ubuntu'), local('Roboto'), local('Open Sans'), local('Noto Sans'), local('Helvetica Neue'), local('sans-serif');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
src: url(./Uno.Fonts.Roboto/Fonts/Roboto-Light.ttf) format('truetype');
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
src: url(./Uno.Fonts.Roboto/Fonts/Roboto-Regular.ttf) format('truetype');
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
src: url(./Uno.Fonts.Roboto/Fonts/Roboto-Medium.ttf) format('truetype');
|
||||
font-weight: 500;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
var UnoAppManifest = {
|
||||
displayName: "Marechai.App"
|
||||
}
|
||||
10
Marechai.App/Platforms/WebAssembly/manifest.webmanifest
Normal file
10
Marechai.App/Platforms/WebAssembly/manifest.webmanifest
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"background_color": "#ffffff",
|
||||
"description": "Marechai.App",
|
||||
"display": "standalone",
|
||||
"name": "Marechai.App",
|
||||
"short_name": "Marechai.App",
|
||||
"start_url": "/index.html",
|
||||
"theme_color": "#ffffff",
|
||||
"scope": "/"
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"navigationFallback": {
|
||||
"rewrite": "/index.html",
|
||||
"exclude": [
|
||||
"*.{css,js}",
|
||||
"*.{png}",
|
||||
"*.{c,h,wasm,clr,pdb,dat,txt}"
|
||||
]
|
||||
},
|
||||
"routes": [
|
||||
{
|
||||
"route": "/package_*",
|
||||
"headers": {
|
||||
"cache-control": "public, immutable, max-age=31536000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"route": "/*.ttf",
|
||||
"headers": {
|
||||
"cache-control": "public, immutable, max-age=31536000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"route": "/*",
|
||||
"headers": {
|
||||
"cache-control": "must-revalidate, max-age=3600"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
78
Marechai.App/Platforms/WebAssembly/wwwroot/web.config
Normal file
78
Marechai.App/Platforms/WebAssembly/wwwroot/web.config
Normal file
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<system.web>
|
||||
<customErrors mode="Off"/>
|
||||
</system.web>
|
||||
|
||||
<system.webServer>
|
||||
|
||||
<!-- Disable compression as we're doing it through pre-compressed files -->
|
||||
<urlCompression doStaticCompression="false" doDynamicCompression="false" dynamicCompressionBeforeCache="false" />
|
||||
|
||||
<staticContent>
|
||||
<remove fileExtension=".dll" />
|
||||
<remove fileExtension=".wasm" />
|
||||
<remove fileExtension=".woff" />
|
||||
<remove fileExtension=".woff2" />
|
||||
<mimeMap fileExtension=".wasm" mimeType="application/wasm" />
|
||||
<mimeMap fileExtension=".clr" mimeType="application/octet-stream" />
|
||||
<mimeMap fileExtension=".pdb" mimeType="application/octet-stream" />
|
||||
<mimeMap fileExtension=".woff" mimeType="application/font-woff" />
|
||||
<mimeMap fileExtension=".woff2" mimeType="application/font-woff" />
|
||||
<mimeMap fileExtension=".dat" mimeType="application/octet-stream" />
|
||||
<!-- Required for PWAs -->
|
||||
<mimeMap fileExtension=".json" mimeType="application/octet-stream" />
|
||||
</staticContent>
|
||||
|
||||
<rewrite>
|
||||
<rules>
|
||||
<rule name="Lookup for pre-compressed brotli file" stopProcessing="true">
|
||||
<match url="(.*)$"/>
|
||||
<conditions>
|
||||
<!-- Match brotli requests -->
|
||||
<add input="{HTTP_ACCEPT_ENCODING}" pattern="br" />
|
||||
|
||||
<!-- Match all but pre-compressed files -->
|
||||
<add input="{REQUEST_URI}" pattern="^(?!/_compressed_br/)(.*)$" />
|
||||
|
||||
<!-- Check if the pre-compressed file exists on the disk -->
|
||||
<add input="{DOCUMENT_ROOT}/_compressed_br/{C:0}" matchType="IsFile" negate="false" />
|
||||
</conditions>
|
||||
<action type="Rewrite" url="/_compressed_br{C:0}" />
|
||||
</rule>
|
||||
|
||||
<rule name="Lookup for pre-compressed gzip file" stopProcessing="true">
|
||||
<match url="(.*)$"/>
|
||||
<conditions>
|
||||
<!-- Match gzip requests -->
|
||||
<add input="{HTTP_ACCEPT_ENCODING}" pattern="gzip" />
|
||||
|
||||
<!-- Match all but pre-compressed files -->
|
||||
<add input="{REQUEST_URI}" pattern="^(?!/_compressed_gz/)(.*)$" />
|
||||
|
||||
<!-- Check if the pre-compressed file exists on the disk -->
|
||||
<add input="{DOCUMENT_ROOT}/_compressed_gz/{C:0}" matchType="IsFile" negate="false" />
|
||||
</conditions>
|
||||
<action type="Rewrite" url="/_compressed_gz{C:0}" />
|
||||
</rule>
|
||||
</rules>
|
||||
|
||||
<outboundRules>
|
||||
<rule name="Adjust content encoding for gzip pre-compressed files" enabled="true" stopProcessing="true">
|
||||
<match serverVariable="RESPONSE_CONTENT_ENCODING" pattern="" />
|
||||
<conditions>
|
||||
<add input="{REQUEST_URI}" pattern="/_compressed_gz/.*$" />
|
||||
</conditions>
|
||||
<action type="Rewrite" value="gzip"/>
|
||||
</rule>
|
||||
<rule name="Adjust content encoding for brotli pre-compressed files" enabled="true" stopProcessing="true">
|
||||
<match serverVariable="RESPONSE_CONTENT_ENCODING" pattern="" />
|
||||
<conditions>
|
||||
<add input="{REQUEST_URI}" pattern="/_compressed_br/.*$" />
|
||||
</conditions>
|
||||
<action type="Rewrite" value="br"/>
|
||||
</rule>
|
||||
</outboundRules>
|
||||
</rewrite>
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
6
Marechai.App/Platforms/iOS/Entitlements.plist
Normal file
6
Marechai.App/Platforms/iOS/Entitlements.plist
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
</dict>
|
||||
</plist>
|
||||
43
Marechai.App/Platforms/iOS/Info.plist
Normal file
43
Marechai.App/Platforms/iOS/Info.plist
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIDeviceFamily</key>
|
||||
<array>
|
||||
<integer>1</integer>
|
||||
<integer>2</integer>
|
||||
</array>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>XSAppIconAssets</key>
|
||||
<string>Assets.xcassets/icon.appiconset</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
|
||||
<!--
|
||||
Adjust this to your application's encryption usage.
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
-->
|
||||
</dict>
|
||||
</plist>
|
||||
18
Marechai.App/Platforms/iOS/Main.iOS.cs
Normal file
18
Marechai.App/Platforms/iOS/Main.iOS.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using UIKit;
|
||||
using Uno.UI.Hosting;
|
||||
|
||||
namespace Marechai.App.iOS;
|
||||
|
||||
public class EntryPoint
|
||||
{
|
||||
// This is the main entry point of the application.
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var host = UnoPlatformHostBuilder.Create()
|
||||
.App(() => new App())
|
||||
.UseAppleUIKit()
|
||||
.Build();
|
||||
|
||||
host.Run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"orientation": "portrait",
|
||||
"extent": "full-screen",
|
||||
"minimum-system-version": "7.0",
|
||||
"scale": "2x",
|
||||
"size": "640x960",
|
||||
"idiom": "iphone"
|
||||
},
|
||||
{
|
||||
"orientation": "portrait",
|
||||
"extent": "full-screen",
|
||||
"minimum-system-version": "7.0",
|
||||
"subtype": "retina4",
|
||||
"scale": "2x",
|
||||
"size": "640x1136",
|
||||
"idiom": "iphone"
|
||||
},
|
||||
{
|
||||
"orientation": "portrait",
|
||||
"extent": "full-screen",
|
||||
"minimum-system-version": "7.0",
|
||||
"scale": "1x",
|
||||
"size": "768x1024",
|
||||
"idiom": "ipad"
|
||||
},
|
||||
{
|
||||
"orientation": "landscape",
|
||||
"extent": "full-screen",
|
||||
"minimum-system-version": "7.0",
|
||||
"scale": "1x",
|
||||
"size": "1024x768",
|
||||
"idiom": "ipad"
|
||||
},
|
||||
{
|
||||
"orientation": "portrait",
|
||||
"extent": "full-screen",
|
||||
"minimum-system-version": "7.0",
|
||||
"scale": "2x",
|
||||
"size": "1536x2048",
|
||||
"idiom": "ipad"
|
||||
},
|
||||
{
|
||||
"orientation": "landscape",
|
||||
"extent": "full-screen",
|
||||
"minimum-system-version": "7.0",
|
||||
"scale": "2x",
|
||||
"size": "2048x1536",
|
||||
"idiom": "ipad"
|
||||
}
|
||||
],
|
||||
"properties": {},
|
||||
"info": {
|
||||
"version": 1,
|
||||
"author": ""
|
||||
}
|
||||
}
|
||||
41
Marechai.App/Platforms/iOS/PrivacyInfo.xcprivacy
Normal file
41
Marechai.App/Platforms/iOS/PrivacyInfo.xcprivacy
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<!-- see https://aka.platform/uno/apple-privacy-manifest for more information -->
|
||||
|
||||
<!-- .NET Runtime/BCL -->
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>C617.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>35F9.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>E174.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
|
||||
<!-- NSUserDefaults -->
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>CA92.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
260
Marechai.App/Presentation/Components/Sidebar.xaml
Normal file
260
Marechai.App/Presentation/Components/Sidebar.xaml
Normal file
@@ -0,0 +1,260 @@
|
||||
<?xml version="1.0"
|
||||
encoding="utf-8"?>
|
||||
|
||||
<UserControl x:Class="Marechai.App.Presentation.Components.Sidebar"
|
||||
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:local="using:Marechai.App.Presentation.Converters"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="600"
|
||||
d:DesignWidth="280"
|
||||
Background="{ThemeResource NavigationViewDefaultPaneBackground}">
|
||||
<UserControl.Resources>
|
||||
<local:CollapseExpandIconConverter x:Key="CollapseExpandIconConverter" />
|
||||
<local:CollapseExpandTooltipConverter x:Key="CollapseExpandTooltipConverter" />
|
||||
</UserControl.Resources>
|
||||
|
||||
<!-- Grid container - naturally responds to parent column width -->
|
||||
<Grid RowSpacing="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" /> <RowDefinition Height="*" /> <RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Sidebar Header with Collapse/Expand Button -->
|
||||
<Grid Grid.Row="0"
|
||||
Padding="8,8,8,8"
|
||||
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
|
||||
BorderThickness="0,0,0,1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" /> <ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Sidebar Title - Hidden when collapsed -->
|
||||
<TextBlock Grid.Column="0"
|
||||
Text="Navigation"
|
||||
FontSize="16"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
VerticalAlignment="Center"
|
||||
Padding="4,0,0,0"
|
||||
Visibility="{Binding SidebarContentVisible}" />
|
||||
|
||||
<!-- Collapse/Expand Button - Always visible -->
|
||||
<Button Grid.Column="1"
|
||||
Content="{Binding IsSidebarOpen, Converter={StaticResource CollapseExpandIconConverter}}"
|
||||
Command="{Binding ToggleSidebarCommand}"
|
||||
Background="Transparent"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Padding="8,8,8,8"
|
||||
CornerRadius="4"
|
||||
ToolTipService.ToolTip="{Binding IsSidebarOpen, Converter={StaticResource CollapseExpandTooltipConverter}}"
|
||||
FontSize="14"
|
||||
MinWidth="40"
|
||||
MinHeight="40"
|
||||
HorizontalAlignment="Center" />
|
||||
</Grid>
|
||||
|
||||
<!-- Scrollable Navigation Items - Hidden when collapsed -->
|
||||
<ScrollViewer Grid.Row="1"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
Padding="0"
|
||||
Visibility="{Binding SidebarContentVisible}">
|
||||
<StackPanel Orientation="Vertical"
|
||||
Spacing="0"
|
||||
Padding="0">
|
||||
<!-- News -->
|
||||
<Button Content="{Binding LocalizedStrings[News]}"
|
||||
Command="{Binding NavigateToNewsCommand}"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left"
|
||||
Padding="16,10,16,10"
|
||||
FontSize="13"
|
||||
Background="Transparent"
|
||||
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
BorderThickness="0"
|
||||
CornerRadius="0" />
|
||||
|
||||
<!-- Books -->
|
||||
<Button Content="{Binding LocalizedStrings[Books]}"
|
||||
Command="{Binding NavigateToBooksCommand}"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left"
|
||||
Padding="16,10,16,10"
|
||||
FontSize="13"
|
||||
Background="Transparent"
|
||||
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
BorderThickness="0"
|
||||
CornerRadius="0" />
|
||||
|
||||
<!-- Companies -->
|
||||
<Button Content="{Binding LocalizedStrings[Companies]}"
|
||||
Command="{Binding NavigateToCompaniesCommand}"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left"
|
||||
Padding="16,10,16,10"
|
||||
FontSize="13"
|
||||
Background="Transparent"
|
||||
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
BorderThickness="0"
|
||||
CornerRadius="0" />
|
||||
|
||||
<!-- Computers -->
|
||||
<Button Content="{Binding LocalizedStrings[Computers]}"
|
||||
Command="{Binding NavigateToComputersCommand}"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left"
|
||||
Padding="16,10,16,10"
|
||||
FontSize="13"
|
||||
Background="Transparent"
|
||||
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
BorderThickness="0"
|
||||
CornerRadius="0" />
|
||||
|
||||
<!-- Consoles -->
|
||||
<Button Content="{Binding LocalizedStrings[Consoles]}"
|
||||
Command="{Binding NavigateToConsolesCommand}"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left"
|
||||
Padding="16,10,16,10"
|
||||
FontSize="13"
|
||||
Background="Transparent"
|
||||
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
BorderThickness="0"
|
||||
CornerRadius="0" />
|
||||
|
||||
<!-- Documents -->
|
||||
<Button Content="{Binding LocalizedStrings[Documents]}"
|
||||
Command="{Binding NavigateToDocumentsCommand}"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left"
|
||||
Padding="16,10,16,10"
|
||||
FontSize="13"
|
||||
Background="Transparent"
|
||||
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
BorderThickness="0"
|
||||
CornerRadius="0" />
|
||||
|
||||
<!-- Dumps -->
|
||||
<Button Content="{Binding LocalizedStrings[Dumps]}"
|
||||
Command="{Binding NavigateToDumpsCommand}"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left"
|
||||
Padding="16,10,16,10"
|
||||
FontSize="13"
|
||||
Background="Transparent"
|
||||
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
BorderThickness="0"
|
||||
CornerRadius="0" />
|
||||
|
||||
<!-- Graphical Processing Units -->
|
||||
<Button Content="{Binding LocalizedStrings[GraphicalProcessingUnits]}"
|
||||
Command="{Binding NavigateToGraphicalProcessingUnitsCommand}"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left"
|
||||
Padding="16,10,16,10"
|
||||
FontSize="13"
|
||||
Background="Transparent"
|
||||
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
BorderThickness="0"
|
||||
CornerRadius="0" />
|
||||
|
||||
<!-- Magazines -->
|
||||
<Button Content="{Binding LocalizedStrings[Magazines]}"
|
||||
Command="{Binding NavigateToMagazinesCommand}"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left"
|
||||
Padding="16,10,16,10"
|
||||
FontSize="13"
|
||||
Background="Transparent"
|
||||
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
BorderThickness="0"
|
||||
CornerRadius="0" />
|
||||
|
||||
<!-- People -->
|
||||
<Button Content="{Binding LocalizedStrings[People]}"
|
||||
Command="{Binding NavigateToPeopleCommand}"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left"
|
||||
Padding="16,10,16,10"
|
||||
FontSize="13"
|
||||
Background="Transparent"
|
||||
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
BorderThickness="0"
|
||||
CornerRadius="0" />
|
||||
|
||||
<!-- Processors -->
|
||||
<Button Content="{Binding LocalizedStrings[Processors]}"
|
||||
Command="{Binding NavigateToProcessorsCommand}"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left"
|
||||
Padding="16,10,16,10"
|
||||
FontSize="13"
|
||||
Background="Transparent"
|
||||
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
BorderThickness="0"
|
||||
CornerRadius="0" />
|
||||
|
||||
<!-- Software -->
|
||||
<Button Content="{Binding LocalizedStrings[Software]}"
|
||||
Command="{Binding NavigateToSoftwareCommand}"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left"
|
||||
Padding="16,10,16,10"
|
||||
FontSize="13"
|
||||
Background="Transparent"
|
||||
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
BorderThickness="0"
|
||||
CornerRadius="0" />
|
||||
|
||||
<!-- Sound Synthesizers -->
|
||||
<Button Content="{Binding LocalizedStrings[SoundSynthesizers]}"
|
||||
Command="{Binding NavigateToSoundSynthesizersCommand}"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left"
|
||||
Padding="16,10,16,10"
|
||||
FontSize="13"
|
||||
Background="Transparent"
|
||||
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
BorderThickness="0"
|
||||
CornerRadius="0" />
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- Bottom Fixed Items - Hidden when collapsed -->
|
||||
<Grid Grid.Row="2"
|
||||
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
|
||||
BorderThickness="0,1,0,0"
|
||||
Padding="0"
|
||||
Visibility="{Binding IsSidebarOpen}">
|
||||
<StackPanel Orientation="Vertical"
|
||||
Spacing="0">
|
||||
<!-- Login/Logout -->
|
||||
<Button Content="{Binding LoginLogoutButtonText}"
|
||||
Command="{Binding LoginLogoutCommand}"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left"
|
||||
Padding="16,10,16,10"
|
||||
FontSize="13"
|
||||
Background="Transparent"
|
||||
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
BorderThickness="0"
|
||||
CornerRadius="0" />
|
||||
|
||||
<!-- Settings -->
|
||||
<Button Content="{Binding LocalizedStrings[Settings]}"
|
||||
Command="{Binding NavigateToSettingsCommand}"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left"
|
||||
Padding="16,10,16,10"
|
||||
FontSize="13"
|
||||
Background="Transparent"
|
||||
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
|
||||
BorderThickness="0"
|
||||
CornerRadius="0" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
11
Marechai.App/Presentation/Components/Sidebar.xaml.cs
Normal file
11
Marechai.App/Presentation/Components/Sidebar.xaml.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Marechai.App.Presentation.Components;
|
||||
|
||||
public sealed partial class Sidebar : UserControl
|
||||
{
|
||||
public Sidebar()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
51
Marechai.App/Presentation/Converters/CompanyConverters.cs
Normal file
51
Marechai.App/Presentation/Converters/CompanyConverters.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
|
||||
namespace Marechai.App.Presentation.Converters;
|
||||
|
||||
/// <summary>
|
||||
/// Converts null object to Collapsed visibility
|
||||
/// </summary>
|
||||
public class ObjectToVisibilityConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language) =>
|
||||
value != null ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language) =>
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts empty/null string to Collapsed visibility
|
||||
/// </summary>
|
||||
public class StringToVisibilityConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if(value is string str && !string.IsNullOrWhiteSpace(str)) return Visibility.Visible;
|
||||
|
||||
return Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language) =>
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts zero count to Collapsed visibility, otherwise Visible
|
||||
/// </summary>
|
||||
public class ZeroToVisibilityConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if(value is int count && count > 0) return Visibility.Visible;
|
||||
|
||||
if(value is long longCount && longCount > 0) return Visibility.Visible;
|
||||
|
||||
return Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language) =>
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
|
||||
namespace Marechai.App.Presentation.Converters;
|
||||
|
||||
/// <summary>
|
||||
/// Converts DateTime to formatted foundation date string, returns empty if null
|
||||
/// </summary>
|
||||
public class FoundationDateConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if(value is DateTime dateTime) return dateTime.ToString("MMMM d, yyyy");
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language) =>
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
59
Marechai.App/Presentation/Converters/SidebarConverters.cs
Normal file
59
Marechai.App/Presentation/Converters/SidebarConverters.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
|
||||
namespace Marechai.App.Presentation.Converters;
|
||||
|
||||
/// <summary>
|
||||
/// Converts boolean value to collapse/expand arrow icon
|
||||
/// </summary>
|
||||
public class CollapseExpandIconConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if(value is bool isOpen) return isOpen ? "◄" : "►";
|
||||
|
||||
return "►";
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language) =>
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts boolean value to collapse/expand tooltip text
|
||||
/// </summary>
|
||||
public class CollapseExpandTooltipConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if(value is bool isOpen) return isOpen ? "Collapse Sidebar" : "Expand Sidebar";
|
||||
|
||||
return "Expand Sidebar";
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language) =>
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts boolean value to GridLength for sidebar column width
|
||||
/// </summary>
|
||||
public class SidebarWidthConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if(value is bool isOpen)
|
||||
{
|
||||
// 280 when open, 60 when collapsed (to keep toggle button visible)
|
||||
double width = isOpen ? 280 : 60;
|
||||
|
||||
return new GridLength(width, GridUnitType.Pixel);
|
||||
}
|
||||
|
||||
return new GridLength(280, GridUnitType.Pixel);
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language) =>
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/******************************************************************************
|
||||
// MARECHAI: Master repository of computing history artifacts information
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2003-2026 Natalia Portillo
|
||||
*******************************************************************************/
|
||||
|
||||
namespace Marechai.App.Presentation.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Navigation parameter for the CompanyDetailPage containing both the company ID and the navigation source.
|
||||
/// </summary>
|
||||
public class CompanyDetailNavigationParameter
|
||||
{
|
||||
public required int CompanyId { get; init; }
|
||||
public object? NavigationSource { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/******************************************************************************
|
||||
// MARECHAI: Master repository of computing history artifacts information
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2003-2026 Natalia Portillo
|
||||
*******************************************************************************/
|
||||
|
||||
namespace Marechai.App.Presentation.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Navigation parameter for the MachineViewPage containing both the machine ID and the navigation source.
|
||||
/// </summary>
|
||||
public class MachineViewNavigationParameter
|
||||
{
|
||||
public required int MachineId { get; init; }
|
||||
public object? NavigationSource { get; init; }
|
||||
}
|
||||
195
Marechai.App/Presentation/ViewModels/CompaniesViewModel.cs
Normal file
195
Marechai.App/Presentation/ViewModels/CompaniesViewModel.cs
Normal file
@@ -0,0 +1,195 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Marechai.App.Helpers;
|
||||
using Marechai.App.Presentation.Models;
|
||||
using Marechai.App.Services;
|
||||
using Uno.Extensions.Navigation;
|
||||
|
||||
namespace Marechai.App.Presentation.ViewModels;
|
||||
|
||||
public partial class CompaniesViewModel : ObservableObject
|
||||
{
|
||||
private readonly List<CompanyListItem> _allCompanies = [];
|
||||
private readonly CompaniesService _companiesService;
|
||||
private readonly IStringLocalizer _localizer;
|
||||
private readonly ILogger<CompaniesViewModel> _logger;
|
||||
private readonly INavigator _navigator;
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<CompanyListItem> _companiesList = [];
|
||||
|
||||
[ObservableProperty]
|
||||
private int _companyCount;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _companyCountText = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _errorMessage = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _hasError;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isDataLoaded;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isLoading;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _searchQuery = string.Empty;
|
||||
|
||||
public CompaniesViewModel(CompaniesService companiesService, IStringLocalizer localizer,
|
||||
ILogger<CompaniesViewModel> logger, INavigator navigator)
|
||||
{
|
||||
_companiesService = companiesService;
|
||||
_localizer = localizer;
|
||||
_logger = logger;
|
||||
_navigator = navigator;
|
||||
LoadData = new AsyncRelayCommand(LoadDataAsync);
|
||||
GoBackCommand = new AsyncRelayCommand(GoBackAsync);
|
||||
NavigateToCompanyCommand = new AsyncRelayCommand<CompanyListItem>(NavigateToCompanyAsync);
|
||||
}
|
||||
|
||||
public IAsyncRelayCommand LoadData { get; }
|
||||
public ICommand GoBackCommand { get; }
|
||||
public IAsyncRelayCommand<CompanyListItem> NavigateToCompanyCommand { get; }
|
||||
public string Title { get; } = "Companies";
|
||||
|
||||
partial void OnSearchQueryChanged(string value)
|
||||
{
|
||||
// Automatically filter when SearchQuery changes
|
||||
UpdateFilter(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads companies count and list from the API
|
||||
/// </summary>
|
||||
private async Task LoadDataAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsLoading = true;
|
||||
ErrorMessage = string.Empty;
|
||||
HasError = false;
|
||||
IsDataLoaded = false;
|
||||
CompaniesList.Clear();
|
||||
_allCompanies.Clear();
|
||||
|
||||
// Load companies
|
||||
List<CompanyDto> companies = await _companiesService.GetAllCompaniesAsync();
|
||||
|
||||
// Set count
|
||||
CompanyCount = companies.Count;
|
||||
CompanyCountText = _localizer["Companies in the database"];
|
||||
|
||||
// Build the full list in memory
|
||||
foreach(CompanyDto company in companies)
|
||||
{
|
||||
// Extract id from UntypedNode
|
||||
int companyId = UntypedNodeExtractor.ExtractInt(company.Id);
|
||||
|
||||
// Convert DateTimeOffset? to DateTime?
|
||||
DateTime? foundedDate = company.Founded?.DateTime;
|
||||
|
||||
_allCompanies.Add(new CompanyListItem
|
||||
{
|
||||
Id = companyId,
|
||||
Name = company.Name ?? string.Empty,
|
||||
FoundationDate = foundedDate
|
||||
});
|
||||
}
|
||||
|
||||
// Apply current filter (will show all if SearchQuery is empty)
|
||||
UpdateFilter(SearchQuery);
|
||||
|
||||
if(CompaniesList.Count == 0)
|
||||
{
|
||||
ErrorMessage = _localizer["No companies found"].Value;
|
||||
HasError = true;
|
||||
}
|
||||
else
|
||||
IsDataLoaded = true;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError("Error loading companies data: {Exception}", ex.Message);
|
||||
ErrorMessage = _localizer["Failed to load companies data. Please try again later."].Value;
|
||||
HasError = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles back navigation
|
||||
/// </summary>
|
||||
private async Task GoBackAsync()
|
||||
{
|
||||
await _navigator.NavigateViewModelAsync<MainViewModel>(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigates to company detail view
|
||||
/// </summary>
|
||||
private async Task NavigateToCompanyAsync(CompanyListItem? company)
|
||||
{
|
||||
if(company is null) return;
|
||||
|
||||
_logger.LogInformation("Navigating to company: {CompanyName} (ID: {CompanyId})", company.Name, company.Id);
|
||||
|
||||
// Navigate to company detail view with navigation parameter
|
||||
var navParam = new CompanyDetailNavigationParameter
|
||||
{
|
||||
CompanyId = company.Id,
|
||||
NavigationSource = this
|
||||
};
|
||||
|
||||
await _navigator.NavigateViewModelAsync<CompanyDetailViewModel>(this, data: navParam);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the filtered list based on search query
|
||||
/// </summary>
|
||||
private void UpdateFilter(string? query)
|
||||
{
|
||||
string lowerQuery = string.IsNullOrWhiteSpace(query) ? string.Empty : query.Trim().ToLowerInvariant();
|
||||
|
||||
CompaniesList.Clear();
|
||||
|
||||
if(string.IsNullOrEmpty(lowerQuery))
|
||||
{
|
||||
// No filter, show all companies
|
||||
foreach(CompanyListItem company in _allCompanies) CompaniesList.Add(company);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Filter companies by name (case-insensitive)
|
||||
var filtered = _allCompanies.Where(c => c.Name.Contains(lowerQuery, StringComparison.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
|
||||
foreach(CompanyListItem company in filtered) CompaniesList.Add(company);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Data model for a company in the list
|
||||
/// </summary>
|
||||
public class CompanyListItem
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public DateTime? FoundationDate { get; set; }
|
||||
|
||||
public string FoundationDateDisplay =>
|
||||
FoundationDate.HasValue ? FoundationDate.Value.ToString("MMMM d, yyyy") : string.Empty;
|
||||
}
|
||||
455
Marechai.App/Presentation/ViewModels/CompanyDetailViewModel.cs
Normal file
455
Marechai.App/Presentation/ViewModels/CompanyDetailViewModel.cs
Normal file
@@ -0,0 +1,455 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Marechai.App.Helpers;
|
||||
using Marechai.App.Presentation.Models;
|
||||
using Marechai.App.Services;
|
||||
using Marechai.App.Services.Caching;
|
||||
using Marechai.Data;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using Uno.Extensions.Navigation;
|
||||
|
||||
namespace Marechai.App.Presentation.ViewModels;
|
||||
|
||||
public partial class CompanyDetailViewModel : ObservableObject
|
||||
{
|
||||
private readonly CompanyDetailService _companyDetailService;
|
||||
private readonly FlagCache _flagCache;
|
||||
private readonly IStringLocalizer _localizer;
|
||||
private readonly ILogger<CompanyDetailViewModel> _logger;
|
||||
private readonly CompanyLogoCache _logoCache;
|
||||
private readonly INavigator _navigator;
|
||||
|
||||
[ObservableProperty]
|
||||
private CompanyDto? _company;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _companyId;
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<CompanyDetailMachine> _computers = [];
|
||||
|
||||
[ObservableProperty]
|
||||
private string _computersFilterText = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _consoelsFilterText = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<CompanyDetailMachine> _consoles = [];
|
||||
|
||||
[ObservableProperty]
|
||||
private string _errorMessage = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<CompanyDetailMachine> _filteredComputers = [];
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<CompanyDetailMachine> _filteredConsoles = [];
|
||||
|
||||
[ObservableProperty]
|
||||
private SvgImageSource? _flagImageSource;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _hasError;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isDataLoaded;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isLoading;
|
||||
|
||||
[ObservableProperty]
|
||||
private SvgImageSource? _logoImageSource;
|
||||
|
||||
[ObservableProperty]
|
||||
private CompanyDto? _soldToCompany;
|
||||
|
||||
public CompanyDetailViewModel(CompanyDetailService companyDetailService, FlagCache flagCache,
|
||||
CompanyLogoCache logoCache, IStringLocalizer localizer,
|
||||
ILogger<CompanyDetailViewModel> logger, INavigator navigator)
|
||||
{
|
||||
_companyDetailService = companyDetailService;
|
||||
_flagCache = flagCache;
|
||||
_logoCache = logoCache;
|
||||
_localizer = localizer;
|
||||
_logger = logger;
|
||||
_navigator = navigator;
|
||||
LoadData = new AsyncRelayCommand(LoadDataAsync);
|
||||
GoBackCommand = new AsyncRelayCommand(GoBackAsync);
|
||||
NavigateToMachineCommand = new AsyncRelayCommand<CompanyDetailMachine>(NavigateToMachineAsync);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display text for the company's status
|
||||
/// </summary>
|
||||
public string CompanyStatusDisplay => Company != null ? GetStatusMessage(Company) : string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display text for the company's founded date
|
||||
/// </summary>
|
||||
public string CompanyFoundedDateDisplay => Company != null ? GetFoundedDateDisplay(Company) : string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether flag content is available
|
||||
/// </summary>
|
||||
public bool HasFlagContent => FlagImageSource != null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether logo content is available
|
||||
/// </summary>
|
||||
public bool HasLogoContent => LogoImageSource != null;
|
||||
|
||||
public IAsyncRelayCommand LoadData { get; }
|
||||
public ICommand GoBackCommand { get; }
|
||||
public IAsyncRelayCommand<CompanyDetailMachine> NavigateToMachineCommand { get; }
|
||||
public string Title { get; } = "Company Details";
|
||||
|
||||
partial void OnCompanyChanged(CompanyDto? oldValue, CompanyDto? newValue)
|
||||
{
|
||||
// Notify that computed properties have changed
|
||||
OnPropertyChanged(nameof(CompanyStatusDisplay));
|
||||
OnPropertyChanged(nameof(CompanyFoundedDateDisplay));
|
||||
}
|
||||
|
||||
partial void OnFlagImageSourceChanged(SvgImageSource? oldValue, SvgImageSource? newValue)
|
||||
{
|
||||
// Notify that HasFlagContent has changed
|
||||
OnPropertyChanged(nameof(HasFlagContent));
|
||||
}
|
||||
|
||||
partial void OnLogoImageSourceChanged(SvgImageSource? oldValue, SvgImageSource? newValue)
|
||||
{
|
||||
// Notify that HasLogoContent has changed
|
||||
OnPropertyChanged(nameof(HasLogoContent));
|
||||
}
|
||||
|
||||
partial void OnComputersFilterTextChanged(string value)
|
||||
{
|
||||
FilterComputers(value);
|
||||
}
|
||||
|
||||
partial void OnConsoelsFilterTextChanged(string value)
|
||||
{
|
||||
FilterConsoles(value);
|
||||
}
|
||||
|
||||
private void FilterComputers(string filterText)
|
||||
{
|
||||
ObservableCollection<CompanyDetailMachine> filtered = string.IsNullOrWhiteSpace(filterText)
|
||||
? new ObservableCollection<
|
||||
CompanyDetailMachine>(Computers)
|
||||
: new
|
||||
ObservableCollection<
|
||||
CompanyDetailMachine>(Computers.Where(c =>
|
||||
c.Name.Contains(filterText,
|
||||
StringComparison
|
||||
.OrdinalIgnoreCase)));
|
||||
|
||||
FilteredComputers = filtered;
|
||||
}
|
||||
|
||||
private void FilterConsoles(string filterText)
|
||||
{
|
||||
ObservableCollection<CompanyDetailMachine> filtered = string.IsNullOrWhiteSpace(filterText)
|
||||
? new ObservableCollection<
|
||||
CompanyDetailMachine>(Consoles)
|
||||
: new
|
||||
ObservableCollection<
|
||||
CompanyDetailMachine>(Consoles.Where(c =>
|
||||
c.Name.Contains(filterText,
|
||||
StringComparison
|
||||
.OrdinalIgnoreCase)));
|
||||
|
||||
FilteredConsoles = filtered;
|
||||
}
|
||||
|
||||
private async Task NavigateToMachineAsync(CompanyDetailMachine? machine)
|
||||
{
|
||||
if(machine == null) return;
|
||||
|
||||
var navParam = new MachineViewNavigationParameter
|
||||
{
|
||||
MachineId = machine.Id,
|
||||
NavigationSource = this
|
||||
};
|
||||
|
||||
await _navigator.NavigateViewModelAsync<MachineViewViewModel>(this, data: navParam);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the formatted founding date with unknown handling
|
||||
/// </summary>
|
||||
public string GetFoundedDateDisplay(CompanyDto company)
|
||||
{
|
||||
if(company.Founded is null) return string.Empty;
|
||||
|
||||
DateTime date = company.Founded.Value.DateTime;
|
||||
|
||||
if(company.FoundedMonthIsUnknown ?? false) return $"{date.Year}.";
|
||||
|
||||
if(company.FoundedDayIsUnknown ?? false) return $"{date:Y}.";
|
||||
|
||||
return $"{date:D}.";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the formatted sold/event date with unknown handling
|
||||
/// </summary>
|
||||
public string GetEventDateDisplay(CompanyDto? company, bool monthUnknown = false, bool dayUnknown = false)
|
||||
{
|
||||
if(company?.Sold is null) return _localizer["unknown date"].Value;
|
||||
|
||||
DateTime date = company.Sold.Value.DateTime;
|
||||
|
||||
if(monthUnknown || (company.SoldMonthIsUnknown ?? false)) return $"{date.Year}";
|
||||
|
||||
if(dayUnknown || (company.SoldDayIsUnknown ?? false)) return $"{date:Y}";
|
||||
|
||||
return $"{date:D}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the status message for the company
|
||||
/// </summary>
|
||||
public string GetStatusMessage(CompanyDto company)
|
||||
{
|
||||
return company.Status switch
|
||||
{
|
||||
1 => _localizer["Company is active."].Value,
|
||||
2 => GetSoldStatusMessage(company),
|
||||
3 => GetMergedStatusMessage(company),
|
||||
4 => GetBankruptcyMessage(company),
|
||||
5 => GetDefunctMessage(company),
|
||||
6 => GetRenamedStatusMessage(company),
|
||||
_ => _localizer["Current company status is unknown."].Value
|
||||
};
|
||||
}
|
||||
|
||||
private string GetSoldStatusMessage(CompanyDto company)
|
||||
{
|
||||
if(SoldToCompany != null)
|
||||
{
|
||||
return string.Format(_localizer["Company sold to {0} on {1}."].Value,
|
||||
SoldToCompany.Name,
|
||||
GetEventDateDisplay(company));
|
||||
}
|
||||
|
||||
if(company.Sold != null)
|
||||
{
|
||||
return string.Format(_localizer["Company sold on {0} to an unknown company."].Value,
|
||||
GetEventDateDisplay(company));
|
||||
}
|
||||
|
||||
return SoldToCompany != null
|
||||
? string.Format(_localizer["Company sold to {0} on an unknown date."].Value, SoldToCompany.Name)
|
||||
: _localizer["Company was sold to an unknown company on an unknown date."].Value;
|
||||
}
|
||||
|
||||
private string GetMergedStatusMessage(CompanyDto company)
|
||||
{
|
||||
if(SoldToCompany != null)
|
||||
{
|
||||
return string.Format(_localizer["Company merged on {0} to form {1}."].Value,
|
||||
GetEventDateDisplay(company),
|
||||
SoldToCompany.Name);
|
||||
}
|
||||
|
||||
if(company.Sold != null)
|
||||
{
|
||||
return string.Format(_localizer["Company merged on {0} to form an unknown company."].Value,
|
||||
GetEventDateDisplay(company));
|
||||
}
|
||||
|
||||
return SoldToCompany != null
|
||||
? string.Format(_localizer["Company merged on an unknown date to form {0}."].Value,
|
||||
SoldToCompany.Name)
|
||||
: _localizer["Company merged to form an unknown company on an unknown date."].Value;
|
||||
}
|
||||
|
||||
private string GetBankruptcyMessage(CompanyDto company) => company.Sold != null
|
||||
? string.Format(_localizer
|
||||
["Company declared bankruptcy on {0}."]
|
||||
.Value,
|
||||
GetEventDateDisplay(company))
|
||||
: _localizer
|
||||
["Company declared bankruptcy on an unknown date."]
|
||||
.Value;
|
||||
|
||||
private string GetDefunctMessage(CompanyDto company) => company.Sold != null
|
||||
? string.Format(_localizer
|
||||
["Company ceased operations on {0}."]
|
||||
.Value,
|
||||
GetEventDateDisplay(company))
|
||||
: _localizer
|
||||
["Company ceased operations on an unknown date."]
|
||||
.Value;
|
||||
|
||||
private string GetRenamedStatusMessage(CompanyDto company)
|
||||
{
|
||||
if(SoldToCompany != null)
|
||||
{
|
||||
return string.Format(_localizer["Company renamed to {0} on {1}."].Value,
|
||||
SoldToCompany.Name,
|
||||
GetEventDateDisplay(company));
|
||||
}
|
||||
|
||||
if(company.Sold != null)
|
||||
{
|
||||
return string.Format(_localizer["Company was renamed on {0} to an unknown name."].Value,
|
||||
GetEventDateDisplay(company));
|
||||
}
|
||||
|
||||
return SoldToCompany != null
|
||||
? string.Format(_localizer["Company renamed to {0} on an unknown date."].Value, SoldToCompany.Name)
|
||||
: _localizer["Company renamed to an unknown name on an unknown date."].Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads company details from the API
|
||||
/// </summary>
|
||||
private async Task LoadDataAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsLoading = true;
|
||||
ErrorMessage = string.Empty;
|
||||
HasError = false;
|
||||
IsDataLoaded = false;
|
||||
FlagImageSource = null;
|
||||
LogoImageSource = null;
|
||||
|
||||
if(CompanyId <= 0)
|
||||
{
|
||||
ErrorMessage = _localizer["Invalid company ID."].Value;
|
||||
HasError = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Load company details
|
||||
Company = await _companyDetailService.GetCompanyByIdAsync(CompanyId);
|
||||
|
||||
if(Company is null)
|
||||
{
|
||||
ErrorMessage = _localizer["Company not found."].Value;
|
||||
HasError = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Load flag if country is available
|
||||
if(Company.CountryId is not null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var countryCode = (short)UntypedNodeExtractor.ExtractInt(Company.CountryId);
|
||||
Stream? 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)
|
||||
{
|
||||
int soldToId = UntypedNodeExtractor.ExtractInt(Company.SoldToId);
|
||||
if(soldToId > 0) SoldToCompany = await _companyDetailService.GetSoldToCompanyAsync(soldToId);
|
||||
}
|
||||
|
||||
// Load logo if available
|
||||
if(Company.LastLogo.HasValue)
|
||||
{
|
||||
try
|
||||
{
|
||||
Stream? logoStream = await _logoCache.GetFlagAsync(Company.LastLogo.Value);
|
||||
|
||||
var logoSource = new SvgImageSource();
|
||||
await logoSource.SetSourceAsync(logoStream.AsRandomAccessStream());
|
||||
LogoImageSource = logoSource;
|
||||
|
||||
_logger.LogInformation("Successfully loaded logo for company {CompanyId}", CompanyId);
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError("Failed to load logo for company {CompanyId}: {Exception}", CompanyId, ex.Message);
|
||||
|
||||
// Continue without logo if loading fails
|
||||
}
|
||||
}
|
||||
|
||||
// Load computers and consoles made by this company
|
||||
List<MachineDto> machines = await _companyDetailService.GetComputersByCompanyAsync(CompanyId);
|
||||
Computers.Clear();
|
||||
Consoles.Clear();
|
||||
FilteredComputers.Clear();
|
||||
FilteredConsoles.Clear();
|
||||
|
||||
foreach(MachineDto machine in machines)
|
||||
{
|
||||
int machineId = UntypedNodeExtractor.ExtractInt(machine.Id);
|
||||
|
||||
var machineItem = new CompanyDetailMachine
|
||||
{
|
||||
Id = machineId,
|
||||
Name = machine.Name ?? string.Empty
|
||||
};
|
||||
|
||||
// Categorize by machine type enum
|
||||
if(machine.Type == (int)MachineType.Computer)
|
||||
Computers.Add(machineItem);
|
||||
else if(machine.Type == (int)MachineType.Console) Consoles.Add(machineItem);
|
||||
}
|
||||
|
||||
// Initialize filtered lists
|
||||
FilteredComputers = new ObservableCollection<CompanyDetailMachine>(Computers);
|
||||
FilteredConsoles = new ObservableCollection<CompanyDetailMachine>(Consoles);
|
||||
|
||||
IsDataLoaded = true;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError("Error loading company details: {Exception}", ex.Message);
|
||||
ErrorMessage = _localizer["Failed to load company details. Please try again later."].Value;
|
||||
HasError = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles back navigation
|
||||
/// </summary>
|
||||
private async Task GoBackAsync()
|
||||
{
|
||||
await _navigator.NavigateViewModelAsync<CompaniesViewModel>(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Data model for a machine in the company detail view
|
||||
/// </summary>
|
||||
public class CompanyDetailMachine
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
}
|
||||
251
Marechai.App/Presentation/ViewModels/ComputersListViewModel.cs
Normal file
251
Marechai.App/Presentation/ViewModels/ComputersListViewModel.cs
Normal file
@@ -0,0 +1,251 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Marechai.App.Helpers;
|
||||
using Marechai.App.Presentation.Models;
|
||||
using Marechai.App.Services;
|
||||
using Uno.Extensions.Navigation;
|
||||
|
||||
namespace Marechai.App.Presentation.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// ViewModel for displaying a filtered list of computers
|
||||
/// </summary>
|
||||
public partial class ComputersListViewModel : ObservableObject
|
||||
{
|
||||
private readonly ComputersService _computersService;
|
||||
private readonly IComputersListFilterContext _filterContext;
|
||||
private readonly IStringLocalizer _localizer;
|
||||
private readonly ILogger<ComputersListViewModel> _logger;
|
||||
private readonly INavigator _navigator;
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<ComputerListItem> _computersList = [];
|
||||
|
||||
[ObservableProperty]
|
||||
private string _errorMessage = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _filterDescription = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _hasError;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isDataLoaded;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isLoading;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _pageTitle = string.Empty;
|
||||
|
||||
public ComputersListViewModel(ComputersService computersService, IStringLocalizer localizer,
|
||||
ILogger<ComputersListViewModel> logger, INavigator navigator,
|
||||
IComputersListFilterContext filterContext)
|
||||
{
|
||||
_computersService = computersService;
|
||||
_localizer = localizer;
|
||||
_logger = logger;
|
||||
_navigator = navigator;
|
||||
_filterContext = filterContext;
|
||||
LoadData = new AsyncRelayCommand(LoadDataAsync);
|
||||
GoBackCommand = new AsyncRelayCommand(GoBackAsync);
|
||||
NavigateToComputerCommand = new AsyncRelayCommand<ComputerListItem>(NavigateToComputerAsync);
|
||||
}
|
||||
|
||||
public IAsyncRelayCommand LoadData { get; }
|
||||
public ICommand GoBackCommand { get; }
|
||||
public IAsyncRelayCommand<ComputerListItem> NavigateToComputerCommand { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the filter type
|
||||
/// </summary>
|
||||
public ComputerListFilterType FilterType
|
||||
{
|
||||
get => _filterContext.FilterType;
|
||||
set => _filterContext.FilterType = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the filter value
|
||||
/// </summary>
|
||||
public string FilterValue
|
||||
{
|
||||
get => _filterContext.FilterValue;
|
||||
set => _filterContext.FilterValue = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads computers based on the current filter
|
||||
/// </summary>
|
||||
private async Task LoadDataAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsLoading = true;
|
||||
ErrorMessage = string.Empty;
|
||||
HasError = false;
|
||||
IsDataLoaded = false;
|
||||
ComputersList.Clear();
|
||||
|
||||
_logger.LogInformation("LoadDataAsync called. FilterType={FilterType}, FilterValue={FilterValue}",
|
||||
FilterType,
|
||||
FilterValue);
|
||||
|
||||
// Update title and filter description based on filter type
|
||||
UpdateFilterDescription();
|
||||
|
||||
// Load computers from the API based on the current filter
|
||||
await LoadComputersFromApiAsync();
|
||||
|
||||
_logger.LogInformation("LoadComputersFromApiAsync completed. ComputersList.Count={Count}",
|
||||
ComputersList.Count);
|
||||
|
||||
if(ComputersList.Count == 0)
|
||||
{
|
||||
ErrorMessage = _localizer["No computers found for this filter"].Value;
|
||||
HasError = true;
|
||||
|
||||
_logger.LogWarning("No computers found for filter: {FilterType} {FilterValue}",
|
||||
FilterType,
|
||||
FilterValue);
|
||||
}
|
||||
else
|
||||
IsDataLoaded = true;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error loading computers: {Exception}", ex.Message);
|
||||
ErrorMessage = _localizer["Failed to load computers. Please try again later."].Value;
|
||||
HasError = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the title and filter description based on the current filter
|
||||
/// </summary>
|
||||
private void UpdateFilterDescription()
|
||||
{
|
||||
switch(FilterType)
|
||||
{
|
||||
case ComputerListFilterType.All:
|
||||
PageTitle = _localizer["All Computers"];
|
||||
FilterDescription = _localizer["Browsing all computers in the database"];
|
||||
|
||||
break;
|
||||
|
||||
case ComputerListFilterType.Letter:
|
||||
if(!string.IsNullOrEmpty(FilterValue) && FilterValue.Length == 1)
|
||||
{
|
||||
PageTitle = $"{_localizer["Computers Starting with"]} {FilterValue}";
|
||||
FilterDescription = $"{_localizer["Showing computers that start with"]} {FilterValue}";
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ComputerListFilterType.Year:
|
||||
if(!string.IsNullOrEmpty(FilterValue) && int.TryParse(FilterValue, out int year))
|
||||
{
|
||||
PageTitle = $"{_localizer["Computers from"]} {year}";
|
||||
FilterDescription = $"{_localizer["Showing computers released in"]} {year}";
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads computers from the API based on the current filter
|
||||
/// </summary>
|
||||
private async Task LoadComputersFromApiAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
List<MachineDto> computers = FilterType switch
|
||||
{
|
||||
ComputerListFilterType.Letter when FilterValue.Length == 1 =>
|
||||
await _computersService.GetComputersByLetterAsync(FilterValue[0]),
|
||||
|
||||
ComputerListFilterType.Year when int.TryParse(FilterValue, out int year) =>
|
||||
await _computersService.GetComputersByYearAsync(year),
|
||||
|
||||
_ => await _computersService.GetAllComputersAsync()
|
||||
};
|
||||
|
||||
// Add computers to the list sorted by name
|
||||
foreach(MachineDto computer in computers.OrderBy(c => c.Name))
|
||||
{
|
||||
int year = computer.Introduced?.Year ?? 0;
|
||||
int id = UntypedNodeExtractor.ExtractInt(computer.Id);
|
||||
|
||||
_logger.LogInformation("Computer: {Name}, Introduced: {Introduced}, Year: {Year}, Company: {Company}, ID: {Id}",
|
||||
computer.Name,
|
||||
computer.Introduced,
|
||||
year,
|
||||
computer.Company,
|
||||
id);
|
||||
|
||||
ComputersList.Add(new ComputerListItem
|
||||
{
|
||||
Id = id,
|
||||
Name = computer.Name ?? string.Empty,
|
||||
Year = year,
|
||||
Manufacturer = computer.Company ?? string.Empty
|
||||
});
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error loading computers from API");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigates back to the computers main view
|
||||
/// </summary>
|
||||
private async Task GoBackAsync()
|
||||
{
|
||||
await _navigator.NavigateViewModelAsync<ComputersViewModel>(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigates to the computer detail view
|
||||
/// </summary>
|
||||
private async Task NavigateToComputerAsync(ComputerListItem? computer)
|
||||
{
|
||||
if(computer is null) return;
|
||||
|
||||
_logger.LogInformation("Navigating to computer detail: {ComputerName} (ID: {ComputerId})",
|
||||
computer.Name,
|
||||
computer.Id);
|
||||
|
||||
var navParam = new MachineViewNavigationParameter
|
||||
{
|
||||
MachineId = computer.Id,
|
||||
NavigationSource = this
|
||||
};
|
||||
|
||||
await _navigator.NavigateViewModelAsync<MachineViewViewModel>(this, data: navParam);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Data model for a computer in the list
|
||||
/// </summary>
|
||||
public class ComputerListItem
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public int Year { get; set; }
|
||||
public string Manufacturer { get; set; } = string.Empty;
|
||||
}
|
||||
207
Marechai.App/Presentation/ViewModels/ComputersViewModel.cs
Normal file
207
Marechai.App/Presentation/ViewModels/ComputersViewModel.cs
Normal file
@@ -0,0 +1,207 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Marechai.App.Services;
|
||||
using Uno.Extensions.Navigation;
|
||||
|
||||
namespace Marechai.App.Presentation.ViewModels;
|
||||
|
||||
public partial class ComputersViewModel : ObservableObject
|
||||
{
|
||||
private readonly ComputersService _computersService;
|
||||
private readonly IComputersListFilterContext _filterContext;
|
||||
private readonly IStringLocalizer _localizer;
|
||||
private readonly ILogger<ComputersViewModel> _logger;
|
||||
private readonly INavigator _navigator;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _computerCount;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _computerCountText = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _errorMessage = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _hasError;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isDataLoaded;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isLoading;
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<char> _lettersList = [];
|
||||
|
||||
[ObservableProperty]
|
||||
private int _maximumYear;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _minimumYear;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _yearsGridTitle = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<int> _yearsList = [];
|
||||
|
||||
public ComputersViewModel(ComputersService computersService, IStringLocalizer localizer,
|
||||
ILogger<ComputersViewModel> logger, INavigator navigator,
|
||||
IComputersListFilterContext filterContext)
|
||||
{
|
||||
_computersService = computersService;
|
||||
_localizer = localizer;
|
||||
_logger = logger;
|
||||
_navigator = navigator;
|
||||
_filterContext = filterContext;
|
||||
LoadData = new AsyncRelayCommand(LoadDataAsync);
|
||||
GoBackCommand = new AsyncRelayCommand(GoBackAsync);
|
||||
NavigateByLetterCommand = new AsyncRelayCommand<char>(NavigateByLetterAsync);
|
||||
NavigateByYearCommand = new AsyncRelayCommand<int>(NavigateByYearAsync);
|
||||
NavigateAllComputersCommand = new AsyncRelayCommand(NavigateAllComputersAsync);
|
||||
|
||||
InitializeLetters();
|
||||
}
|
||||
|
||||
public IAsyncRelayCommand LoadData { get; }
|
||||
public ICommand GoBackCommand { get; }
|
||||
public IAsyncRelayCommand<char> NavigateByLetterCommand { get; }
|
||||
public IAsyncRelayCommand<int> NavigateByYearCommand { get; }
|
||||
public IAsyncRelayCommand NavigateAllComputersCommand { get; }
|
||||
public string Title { get; } = "Computers";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the alphabet list (A-Z)
|
||||
/// </summary>
|
||||
private void InitializeLetters()
|
||||
{
|
||||
LettersList.Clear();
|
||||
|
||||
for(var c = 'A'; c <= 'Z'; c++) LettersList.Add(c);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads computers count, minimum and maximum years from the API
|
||||
/// </summary>
|
||||
private async Task LoadDataAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsLoading = true;
|
||||
ErrorMessage = string.Empty;
|
||||
HasError = false;
|
||||
IsDataLoaded = false;
|
||||
YearsList.Clear();
|
||||
|
||||
// Load all data in parallel for better performance
|
||||
Task<int> countTask = _computersService.GetComputersCountAsync();
|
||||
Task<int> minYearTask = _computersService.GetMinimumYearAsync();
|
||||
Task<int> maxYearTask = _computersService.GetMaximumYearAsync();
|
||||
await Task.WhenAll(countTask, minYearTask, maxYearTask);
|
||||
|
||||
ComputerCount = countTask.Result;
|
||||
MinimumYear = minYearTask.Result;
|
||||
MaximumYear = maxYearTask.Result;
|
||||
|
||||
// Update display text
|
||||
ComputerCountText = _localizer["Computers in the database"];
|
||||
|
||||
// Generate years list
|
||||
if(MinimumYear > 0 && MaximumYear > 0)
|
||||
{
|
||||
for(int year = MinimumYear; year <= MaximumYear; year++) YearsList.Add(year);
|
||||
|
||||
YearsGridTitle = $"Browse by Year ({MinimumYear} - {MaximumYear})";
|
||||
}
|
||||
|
||||
if(ComputerCount == 0)
|
||||
{
|
||||
ErrorMessage = _localizer["No computers found"].Value;
|
||||
HasError = true;
|
||||
}
|
||||
else
|
||||
IsDataLoaded = true;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError("Error loading computers data: {Exception}", ex.Message);
|
||||
ErrorMessage = _localizer["Failed to load computers data. Please try again later."].Value;
|
||||
HasError = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles back navigation
|
||||
/// </summary>
|
||||
private async Task GoBackAsync()
|
||||
{
|
||||
await _navigator.NavigateViewModelAsync<MainViewModel>(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigates to computers filtered by letter
|
||||
/// </summary>
|
||||
private async Task NavigateByLetterAsync(char letter)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Navigating to computers by letter: {Letter}", letter);
|
||||
_filterContext.FilterType = ComputerListFilterType.Letter;
|
||||
_filterContext.FilterValue = letter.ToString();
|
||||
await _navigator.NavigateRouteAsync(this, "list-computers");
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError("Error navigating to letter computers: {Exception}", ex.Message);
|
||||
ErrorMessage = _localizer["Failed to navigate. Please try again."].Value;
|
||||
HasError = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigates to computers filtered by year
|
||||
/// </summary>
|
||||
private async Task NavigateByYearAsync(int year)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Navigating to computers by year: {Year}", year);
|
||||
_filterContext.FilterType = ComputerListFilterType.Year;
|
||||
_filterContext.FilterValue = year.ToString();
|
||||
await _navigator.NavigateRouteAsync(this, "list-computers");
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError("Error navigating to year computers: {Exception}", ex.Message);
|
||||
ErrorMessage = _localizer["Failed to navigate. Please try again."].Value;
|
||||
HasError = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigates to all computers view
|
||||
/// </summary>
|
||||
private async Task NavigateAllComputersAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Navigating to all computers");
|
||||
_filterContext.FilterType = ComputerListFilterType.All;
|
||||
_filterContext.FilterValue = string.Empty;
|
||||
await _navigator.NavigateRouteAsync(this, "list-computers");
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError("Error navigating to all computers: {Exception}", ex.Message);
|
||||
ErrorMessage = _localizer["Failed to navigate. Please try again."].Value;
|
||||
HasError = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
249
Marechai.App/Presentation/ViewModels/ConsolesListViewModel.cs
Normal file
249
Marechai.App/Presentation/ViewModels/ConsolesListViewModel.cs
Normal file
@@ -0,0 +1,249 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Marechai.App.Helpers;
|
||||
using Marechai.App.Presentation.Models;
|
||||
using Marechai.App.Services;
|
||||
using Uno.Extensions.Navigation;
|
||||
|
||||
namespace Marechai.App.Presentation.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// ViewModel for displaying a filtered list of consoles
|
||||
/// </summary>
|
||||
public partial class ConsolesListViewModel : ObservableObject
|
||||
{
|
||||
private readonly ConsolesService _consolesService;
|
||||
private readonly IConsolesListFilterContext _filterContext;
|
||||
private readonly IStringLocalizer _localizer;
|
||||
private readonly ILogger<ConsolesListViewModel> _logger;
|
||||
private readonly INavigator _navigator;
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<ConsoleListItem> _consolesList = [];
|
||||
|
||||
[ObservableProperty]
|
||||
private string _errorMessage = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _filterDescription = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _hasError;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isDataLoaded;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isLoading;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _pageTitle = string.Empty;
|
||||
|
||||
public ConsolesListViewModel(ConsolesService consolesService, IStringLocalizer localizer,
|
||||
ILogger<ConsolesListViewModel> logger, INavigator navigator,
|
||||
IConsolesListFilterContext filterContext)
|
||||
{
|
||||
_consolesService = consolesService;
|
||||
_localizer = localizer;
|
||||
_logger = logger;
|
||||
_navigator = navigator;
|
||||
_filterContext = filterContext;
|
||||
LoadData = new AsyncRelayCommand(LoadDataAsync);
|
||||
GoBackCommand = new AsyncRelayCommand(GoBackAsync);
|
||||
NavigateToConsoleCommand = new AsyncRelayCommand<ConsoleListItem>(NavigateToConsoleAsync);
|
||||
}
|
||||
|
||||
public IAsyncRelayCommand LoadData { get; }
|
||||
public ICommand GoBackCommand { get; }
|
||||
public IAsyncRelayCommand<ConsoleListItem> NavigateToConsoleCommand { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the filter type
|
||||
/// </summary>
|
||||
public ConsoleListFilterType FilterType
|
||||
{
|
||||
get => _filterContext.FilterType;
|
||||
set => _filterContext.FilterType = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the filter value
|
||||
/// </summary>
|
||||
public string FilterValue
|
||||
{
|
||||
get => _filterContext.FilterValue;
|
||||
set => _filterContext.FilterValue = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads consoles based on the current filter
|
||||
/// </summary>
|
||||
private async Task LoadDataAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsLoading = true;
|
||||
ErrorMessage = string.Empty;
|
||||
HasError = false;
|
||||
IsDataLoaded = false;
|
||||
ConsolesList.Clear();
|
||||
|
||||
_logger.LogInformation("LoadDataAsync called. FilterType={FilterType}, FilterValue={FilterValue}",
|
||||
FilterType,
|
||||
FilterValue);
|
||||
|
||||
// Update title and filter description based on filter type
|
||||
UpdateFilterDescription();
|
||||
|
||||
// Load consoles from the API based on the current filter
|
||||
await LoadConsolesFromApiAsync();
|
||||
|
||||
_logger.LogInformation("LoadConsolesFromApiAsync completed. ConsolesList.Count={Count}",
|
||||
ConsolesList.Count);
|
||||
|
||||
if(ConsolesList.Count == 0)
|
||||
{
|
||||
ErrorMessage = _localizer["No consoles found for this filter"].Value;
|
||||
HasError = true;
|
||||
|
||||
_logger.LogWarning("No consoles found for filter: {FilterType} {FilterValue}", FilterType, FilterValue);
|
||||
}
|
||||
else
|
||||
IsDataLoaded = true;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error loading consoles: {Exception}", ex.Message);
|
||||
ErrorMessage = _localizer["Failed to load consoles. Please try again later."].Value;
|
||||
HasError = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the title and filter description based on the current filter
|
||||
/// </summary>
|
||||
private void UpdateFilterDescription()
|
||||
{
|
||||
switch(FilterType)
|
||||
{
|
||||
case ConsoleListFilterType.All:
|
||||
PageTitle = _localizer["All Consoles"];
|
||||
FilterDescription = _localizer["Browsing all consoles in the database"];
|
||||
|
||||
break;
|
||||
|
||||
case ConsoleListFilterType.Letter:
|
||||
if(!string.IsNullOrEmpty(FilterValue) && FilterValue.Length == 1)
|
||||
{
|
||||
PageTitle = $"{_localizer["Consoles Starting with"]} {FilterValue}";
|
||||
FilterDescription = $"{_localizer["Showing consoles that start with"]} {FilterValue}";
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ConsoleListFilterType.Year:
|
||||
if(!string.IsNullOrEmpty(FilterValue) && int.TryParse(FilterValue, out int year))
|
||||
{
|
||||
PageTitle = $"{_localizer["Consoles from"]} {year}";
|
||||
FilterDescription = $"{_localizer["Showing consoles released in"]} {year}";
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads consoles from the API based on the current filter
|
||||
/// </summary>
|
||||
private async Task LoadConsolesFromApiAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
List<MachineDto> consoles = FilterType switch
|
||||
{
|
||||
ConsoleListFilterType.Letter when FilterValue.Length == 1 =>
|
||||
await _consolesService.GetConsolesByLetterAsync(FilterValue[0]),
|
||||
|
||||
ConsoleListFilterType.Year when int.TryParse(FilterValue, out int year) =>
|
||||
await _consolesService.GetConsolesByYearAsync(year),
|
||||
|
||||
_ => await _consolesService.GetAllConsolesAsync()
|
||||
};
|
||||
|
||||
// Add consoles to the list sorted by name
|
||||
foreach(MachineDto console in consoles.OrderBy(c => c.Name))
|
||||
{
|
||||
int year = console.Introduced?.Year ?? 0;
|
||||
int id = UntypedNodeExtractor.ExtractInt(console.Id);
|
||||
|
||||
_logger.LogInformation("Console: {Name}, Introduced: {Introduced}, Year: {Year}, Company: {Company}, ID: {Id}",
|
||||
console.Name,
|
||||
console.Introduced,
|
||||
year,
|
||||
console.Company,
|
||||
id);
|
||||
|
||||
ConsolesList.Add(new ConsoleListItem
|
||||
{
|
||||
Id = id,
|
||||
Name = console.Name ?? string.Empty,
|
||||
Year = year,
|
||||
Manufacturer = console.Company ?? string.Empty
|
||||
});
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error loading consoles from API");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigates back to the consoles main view
|
||||
/// </summary>
|
||||
private async Task GoBackAsync()
|
||||
{
|
||||
await _navigator.NavigateViewModelAsync<ConsolesViewModel>(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigates to the console detail view
|
||||
/// </summary>
|
||||
private async Task NavigateToConsoleAsync(ConsoleListItem? console)
|
||||
{
|
||||
if(console is null) return;
|
||||
|
||||
_logger.LogInformation("Navigating to console detail: {ConsoleName} (ID: {ConsoleId})",
|
||||
console.Name,
|
||||
console.Id);
|
||||
|
||||
var navParam = new MachineViewNavigationParameter
|
||||
{
|
||||
MachineId = console.Id,
|
||||
NavigationSource = this
|
||||
};
|
||||
|
||||
await _navigator.NavigateViewModelAsync<MachineViewViewModel>(this, data: navParam);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Data model for a console in the list
|
||||
/// </summary>
|
||||
public class ConsoleListItem
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public int Year { get; set; }
|
||||
public string Manufacturer { get; set; } = string.Empty;
|
||||
}
|
||||
207
Marechai.App/Presentation/ViewModels/ConsolesViewModel.cs
Normal file
207
Marechai.App/Presentation/ViewModels/ConsolesViewModel.cs
Normal file
@@ -0,0 +1,207 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Marechai.App.Services;
|
||||
using Uno.Extensions.Navigation;
|
||||
|
||||
namespace Marechai.App.Presentation.ViewModels;
|
||||
|
||||
public partial class ConsolesViewModel : ObservableObject
|
||||
{
|
||||
private readonly ConsolesService _consolesService;
|
||||
private readonly IConsolesListFilterContext _filterContext;
|
||||
private readonly IStringLocalizer _localizer;
|
||||
private readonly ILogger<ConsolesViewModel> _logger;
|
||||
private readonly INavigator _navigator;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _consoleCount;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _consoleCountText = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _errorMessage = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _hasError;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isDataLoaded;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isLoading;
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<char> _lettersList = [];
|
||||
|
||||
[ObservableProperty]
|
||||
private int _maximumYear;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _minimumYear;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _yearsGridTitle = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<int> _yearsList = [];
|
||||
|
||||
public ConsolesViewModel(ConsolesService consolesService, IStringLocalizer localizer,
|
||||
ILogger<ConsolesViewModel> logger, INavigator navigator,
|
||||
IConsolesListFilterContext filterContext)
|
||||
{
|
||||
_consolesService = consolesService;
|
||||
_localizer = localizer;
|
||||
_logger = logger;
|
||||
_navigator = navigator;
|
||||
_filterContext = filterContext;
|
||||
LoadData = new AsyncRelayCommand(LoadDataAsync);
|
||||
GoBackCommand = new AsyncRelayCommand(GoBackAsync);
|
||||
NavigateByLetterCommand = new AsyncRelayCommand<char>(NavigateByLetterAsync);
|
||||
NavigateByYearCommand = new AsyncRelayCommand<int>(NavigateByYearAsync);
|
||||
NavigateAllConsolesCommand = new AsyncRelayCommand(NavigateAllConsolesAsync);
|
||||
|
||||
InitializeLetters();
|
||||
}
|
||||
|
||||
public IAsyncRelayCommand LoadData { get; }
|
||||
public ICommand GoBackCommand { get; }
|
||||
public IAsyncRelayCommand<char> NavigateByLetterCommand { get; }
|
||||
public IAsyncRelayCommand<int> NavigateByYearCommand { get; }
|
||||
public IAsyncRelayCommand NavigateAllConsolesCommand { get; }
|
||||
public string Title { get; } = "Consoles";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the alphabet list (A-Z)
|
||||
/// </summary>
|
||||
private void InitializeLetters()
|
||||
{
|
||||
LettersList.Clear();
|
||||
|
||||
for(var c = 'A'; c <= 'Z'; c++) LettersList.Add(c);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads consoles count, minimum and maximum years from the API
|
||||
/// </summary>
|
||||
private async Task LoadDataAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsLoading = true;
|
||||
ErrorMessage = string.Empty;
|
||||
HasError = false;
|
||||
IsDataLoaded = false;
|
||||
YearsList.Clear();
|
||||
|
||||
// Load all data in parallel for better performance
|
||||
Task<int> countTask = _consolesService.GetConsolesCountAsync();
|
||||
Task<int> minYearTask = _consolesService.GetMinimumYearAsync();
|
||||
Task<int> maxYearTask = _consolesService.GetMaximumYearAsync();
|
||||
await Task.WhenAll(countTask, minYearTask, maxYearTask);
|
||||
|
||||
ConsoleCount = countTask.Result;
|
||||
MinimumYear = minYearTask.Result;
|
||||
MaximumYear = maxYearTask.Result;
|
||||
|
||||
// Update display text
|
||||
ConsoleCountText = _localizer["Consoles in the database"];
|
||||
|
||||
// Generate years list
|
||||
if(MinimumYear > 0 && MaximumYear > 0)
|
||||
{
|
||||
for(int year = MinimumYear; year <= MaximumYear; year++) YearsList.Add(year);
|
||||
|
||||
YearsGridTitle = $"Browse by Year ({MinimumYear} - {MaximumYear})";
|
||||
}
|
||||
|
||||
if(ConsoleCount == 0)
|
||||
{
|
||||
ErrorMessage = _localizer["No consoles found"].Value;
|
||||
HasError = true;
|
||||
}
|
||||
else
|
||||
IsDataLoaded = true;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError("Error loading consoles data: {Exception}", ex.Message);
|
||||
ErrorMessage = _localizer["Failed to load consoles data. Please try again later."].Value;
|
||||
HasError = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles back navigation
|
||||
/// </summary>
|
||||
private async Task GoBackAsync()
|
||||
{
|
||||
await _navigator.NavigateViewModelAsync<MainViewModel>(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigates to consoles filtered by letter
|
||||
/// </summary>
|
||||
private async Task NavigateByLetterAsync(char letter)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Navigating to consoles by letter: {Letter}", letter);
|
||||
_filterContext.FilterType = ConsoleListFilterType.Letter;
|
||||
_filterContext.FilterValue = letter.ToString();
|
||||
await _navigator.NavigateRouteAsync(this, "list-consoles");
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError("Error navigating to letter consoles: {Exception}", ex.Message);
|
||||
ErrorMessage = _localizer["Failed to navigate. Please try again."].Value;
|
||||
HasError = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigates to consoles filtered by year
|
||||
/// </summary>
|
||||
private async Task NavigateByYearAsync(int year)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Navigating to consoles by year: {Year}", year);
|
||||
_filterContext.FilterType = ConsoleListFilterType.Year;
|
||||
_filterContext.FilterValue = year.ToString();
|
||||
await _navigator.NavigateRouteAsync(this, "list-consoles");
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError("Error navigating to year consoles: {Exception}", ex.Message);
|
||||
ErrorMessage = _localizer["Failed to navigate. Please try again."].Value;
|
||||
HasError = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Navigates to all consoles view
|
||||
/// </summary>
|
||||
private async Task NavigateAllConsolesAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Navigating to all consoles");
|
||||
_filterContext.FilterType = ConsoleListFilterType.All;
|
||||
_filterContext.FilterValue = string.Empty;
|
||||
await _navigator.NavigateRouteAsync(this, "list-consoles");
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError("Error navigating to all consoles: {Exception}", ex.Message);
|
||||
ErrorMessage = _localizer["Failed to navigate. Please try again."].Value;
|
||||
HasError = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
407
Marechai.App/Presentation/ViewModels/MachineViewViewModel.cs
Normal file
407
Marechai.App/Presentation/ViewModels/MachineViewViewModel.cs
Normal file
@@ -0,0 +1,407 @@
|
||||
/******************************************************************************
|
||||
// MARECHAI: Master repository of computing history artifacts information
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2003-2026 Natalia Portillo
|
||||
*******************************************************************************/
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
using Humanizer;
|
||||
using Marechai.App.Helpers;
|
||||
using Marechai.App.Presentation.Models;
|
||||
using Marechai.App.Services;
|
||||
using Marechai.Data;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Uno.Extensions.Navigation;
|
||||
|
||||
namespace Marechai.App.Presentation.ViewModels;
|
||||
|
||||
public partial class MachineViewViewModel : ObservableObject
|
||||
{
|
||||
private readonly ComputersService _computersService;
|
||||
private readonly ILogger<MachineViewViewModel> _logger;
|
||||
private readonly INavigator _navigator;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _companyName = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _errorMessage = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? _familyName;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _hasError;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? _introductionDateDisplay;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isDataLoaded;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isLoading;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isPrototype;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _machineName = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string? _modelName;
|
||||
private object? _navigationSource;
|
||||
|
||||
[ObservableProperty]
|
||||
private Visibility _showFamily = Visibility.Collapsed;
|
||||
|
||||
[ObservableProperty]
|
||||
private Visibility _showFamilyOrModel = Visibility.Collapsed;
|
||||
|
||||
[ObservableProperty]
|
||||
private Visibility _showGpus = Visibility.Collapsed;
|
||||
|
||||
[ObservableProperty]
|
||||
private Visibility _showIntroductionDate = Visibility.Collapsed;
|
||||
|
||||
[ObservableProperty]
|
||||
private Visibility _showMemory = Visibility.Collapsed;
|
||||
|
||||
[ObservableProperty]
|
||||
private Visibility _showModel = Visibility.Collapsed;
|
||||
|
||||
[ObservableProperty]
|
||||
private Visibility _showProcessors = Visibility.Collapsed;
|
||||
|
||||
[ObservableProperty]
|
||||
private Visibility _showSoundSynthesizers = Visibility.Collapsed;
|
||||
|
||||
[ObservableProperty]
|
||||
private Visibility _showStorage = Visibility.Collapsed;
|
||||
|
||||
public MachineViewViewModel(ILogger<MachineViewViewModel> logger, INavigator navigator,
|
||||
ComputersService computersService)
|
||||
{
|
||||
_logger = logger;
|
||||
_navigator = navigator;
|
||||
_computersService = computersService;
|
||||
}
|
||||
|
||||
public ObservableCollection<ProcessorDisplayItem> Processors { get; } = [];
|
||||
public ObservableCollection<MemoryDisplayItem> Memory { get; } = [];
|
||||
public ObservableCollection<GpuDisplayItem> Gpus { get; } = [];
|
||||
public ObservableCollection<SoundSynthesizerDisplayItem> SoundSynthesizers { get; } = [];
|
||||
public ObservableCollection<StorageDisplayItem> Storage { get; } = [];
|
||||
|
||||
[RelayCommand]
|
||||
public async Task GoBack()
|
||||
{
|
||||
// If we came from News, navigate back to News
|
||||
if(_navigationSource is NewsViewModel)
|
||||
{
|
||||
await _navigator.NavigateViewModelAsync<NewsViewModel>(this);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If we came from CompanyDetailViewModel, navigate back to company details
|
||||
if(_navigationSource is CompanyDetailViewModel companyVm)
|
||||
{
|
||||
var navParam = new CompanyDetailNavigationParameter
|
||||
{
|
||||
CompanyId = companyVm.CompanyId
|
||||
};
|
||||
|
||||
await _navigator.NavigateViewModelAsync<CompanyDetailViewModel>(this, data: navParam);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If we came from ConsolesListViewModel, navigate back to consoles list
|
||||
if(_navigationSource is ConsolesListViewModel)
|
||||
{
|
||||
await _navigator.NavigateViewModelAsync<ConsolesListViewModel>(this);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If we came from ComputersListViewModel, navigate back to computers list
|
||||
if(_navigationSource is ComputersListViewModel)
|
||||
{
|
||||
await _navigator.NavigateViewModelAsync<ComputersListViewModel>(this);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, try to go back in the navigation stack
|
||||
await _navigator.GoBack(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the navigation source (where we came from).
|
||||
/// </summary>
|
||||
public void SetNavigationSource(object? source)
|
||||
{
|
||||
_navigationSource = source;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
public Task LoadData()
|
||||
{
|
||||
// Placeholder for retry functionality
|
||||
HasError = false;
|
||||
ErrorMessage = string.Empty;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task LoadMachineAsync(int machineId)
|
||||
{
|
||||
try
|
||||
{
|
||||
IsLoading = true;
|
||||
IsDataLoaded = false;
|
||||
HasError = false;
|
||||
ErrorMessage = string.Empty;
|
||||
Processors.Clear();
|
||||
Memory.Clear();
|
||||
Gpus.Clear();
|
||||
SoundSynthesizers.Clear();
|
||||
Storage.Clear();
|
||||
|
||||
_logger.LogInformation("Loading machine {MachineId}", machineId);
|
||||
|
||||
// Fetch machine data from API
|
||||
MachineDto? machine = await _computersService.GetMachineByIdAsync(machineId);
|
||||
|
||||
if(machine is null)
|
||||
{
|
||||
HasError = true;
|
||||
ErrorMessage = "Machine not found";
|
||||
IsLoading = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Populate basic information
|
||||
MachineName = machine.Name ?? string.Empty;
|
||||
CompanyName = machine.Company ?? string.Empty;
|
||||
FamilyName = machine.FamilyName;
|
||||
ModelName = machine.Model;
|
||||
|
||||
// Check if this is a prototype (year 1000 is used as placeholder for prototypes)
|
||||
IsPrototype = machine.Introduced?.Year == 1000;
|
||||
|
||||
// Set introduction date if available and not a prototype
|
||||
if(machine.Introduced.HasValue && machine.Introduced.Value.Year != 1000)
|
||||
IntroductionDateDisplay = machine.Introduced.Value.ToString("MMMM d, yyyy");
|
||||
|
||||
// Populate processors
|
||||
if(machine.Processors != null)
|
||||
{
|
||||
foreach(ProcessorDto processor in machine.Processors)
|
||||
{
|
||||
var details = new List<string>();
|
||||
int speed = UntypedNodeExtractor.ExtractInt(processor.Speed);
|
||||
int gprSize = UntypedNodeExtractor.ExtractInt(processor.GprSize);
|
||||
int cores = UntypedNodeExtractor.ExtractInt(processor.Cores);
|
||||
|
||||
if(speed > 0) details.Add($"{speed} MHz");
|
||||
if(gprSize > 0) details.Add($"{gprSize} bits");
|
||||
if(cores > 1) details.Add($"{cores} cores");
|
||||
|
||||
Processors.Add(new ProcessorDisplayItem
|
||||
{
|
||||
DisplayName = processor.Name ?? string.Empty,
|
||||
Manufacturer = processor.Company ?? string.Empty,
|
||||
HasDetails = details.Count > 0,
|
||||
DetailsText = string.Join(", ", details)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Populate memory
|
||||
if(machine.Memory != null)
|
||||
{
|
||||
foreach(MemoryDto mem in machine.Memory)
|
||||
{
|
||||
long size = UntypedNodeExtractor.ExtractLong(mem.Size);
|
||||
|
||||
string sizeStr = size > 0
|
||||
? size > 1024 ? $"{size} bytes ({size.Bytes().Humanize()})" : $"{size} bytes"
|
||||
: "Unknown";
|
||||
|
||||
// Get humanized memory usage description
|
||||
string usageDescription = mem.Usage.HasValue
|
||||
? ((MemoryUsage)mem.Usage.Value).Humanize()
|
||||
: "Unknown";
|
||||
|
||||
Memory.Add(new MemoryDisplayItem
|
||||
{
|
||||
SizeDisplay = sizeStr,
|
||||
TypeDisplay = usageDescription
|
||||
});
|
||||
}
|
||||
} // Populate GPUs
|
||||
|
||||
if(machine.Gpus != null)
|
||||
{
|
||||
foreach(GpuDto gpu in machine.Gpus)
|
||||
{
|
||||
Gpus.Add(new GpuDisplayItem
|
||||
{
|
||||
DisplayName = gpu.Name ?? string.Empty,
|
||||
Manufacturer = gpu.Company ?? string.Empty,
|
||||
HasManufacturer = !string.IsNullOrEmpty(gpu.Company)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Populate sound synthesizers
|
||||
if(machine.SoundSynthesizers != null)
|
||||
{
|
||||
foreach(SoundSynthDto synth in machine.SoundSynthesizers)
|
||||
{
|
||||
var details = new List<string>();
|
||||
int voices = UntypedNodeExtractor.ExtractInt(synth.Voices);
|
||||
|
||||
if(voices > 0) details.Add($"{voices} voices");
|
||||
|
||||
SoundSynthesizers.Add(new SoundSynthesizerDisplayItem
|
||||
{
|
||||
DisplayName = synth.Name ?? string.Empty,
|
||||
HasDetails = details.Count > 0,
|
||||
DetailsText = string.Join(", ", details)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Populate storage
|
||||
if(machine.Storage != null)
|
||||
{
|
||||
foreach(StorageDto storage in machine.Storage)
|
||||
{
|
||||
long capacity = UntypedNodeExtractor.ExtractLong(storage.Capacity);
|
||||
|
||||
string displayText = capacity > 0
|
||||
? capacity > 1024
|
||||
? $"{capacity} bytes ({capacity.Bytes().Humanize()})"
|
||||
: $"{capacity} bytes"
|
||||
: "Storage";
|
||||
|
||||
// Get humanized storage type description
|
||||
string typeNote = storage.Type.HasValue ? ((StorageType)storage.Type.Value).Humanize() : "Unknown";
|
||||
|
||||
Storage.Add(new StorageDisplayItem
|
||||
{
|
||||
DisplayText = displayText,
|
||||
TypeNote = typeNote
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
UpdateVisibilities();
|
||||
IsDataLoaded = true;
|
||||
IsLoading = false;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error loading machine {MachineId}", machineId);
|
||||
HasError = true;
|
||||
ErrorMessage = ex.Message;
|
||||
IsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateVisibilities()
|
||||
{
|
||||
ShowIntroductionDate =
|
||||
!string.IsNullOrEmpty(IntroductionDateDisplay) ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
ShowFamily = !string.IsNullOrEmpty(FamilyName) ? Visibility.Visible : Visibility.Collapsed;
|
||||
ShowModel = !string.IsNullOrEmpty(ModelName) ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
ShowFamilyOrModel = ShowFamily == Visibility.Visible || ShowModel == Visibility.Visible
|
||||
? Visibility.Visible
|
||||
: Visibility.Collapsed;
|
||||
|
||||
ShowProcessors = Processors.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
|
||||
ShowMemory = Memory.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
|
||||
ShowGpus = Gpus.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
|
||||
ShowSoundSynthesizers = SoundSynthesizers.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
|
||||
ShowStorage = Storage.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display item for processor information
|
||||
/// </summary>
|
||||
public class ProcessorDisplayItem
|
||||
{
|
||||
public string DisplayName { get; set; } = string.Empty;
|
||||
public string Manufacturer { get; set; } = string.Empty;
|
||||
public bool HasDetails { get; set; }
|
||||
public string DetailsText { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display item for memory information
|
||||
/// </summary>
|
||||
public class MemoryDisplayItem
|
||||
{
|
||||
public string SizeDisplay { get; set; } = string.Empty;
|
||||
public string TypeDisplay { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display item for GPU information
|
||||
/// </summary>
|
||||
public class GpuDisplayItem
|
||||
{
|
||||
public string DisplayName { get; set; } = string.Empty;
|
||||
public string Manufacturer { get; set; } = string.Empty;
|
||||
public bool HasManufacturer { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display item for sound synthesizer information
|
||||
/// </summary>
|
||||
public class SoundSynthesizerDisplayItem
|
||||
{
|
||||
public string DisplayName { get; set; } = string.Empty;
|
||||
public bool HasDetails { get; set; }
|
||||
public string DetailsText { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display item for storage information
|
||||
/// </summary>
|
||||
public class StorageDisplayItem
|
||||
{
|
||||
public string DisplayText { get; set; } = string.Empty;
|
||||
public string TypeNote { get; set; } = string.Empty;
|
||||
}
|
||||
177
Marechai.App/Presentation/ViewModels/MainViewModel.cs
Normal file
177
Marechai.App/Presentation/ViewModels/MainViewModel.cs
Normal file
@@ -0,0 +1,177 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Uno.Extensions.Navigation;
|
||||
|
||||
namespace Marechai.App.Presentation.ViewModels;
|
||||
|
||||
public partial class MainViewModel : ObservableObject
|
||||
{
|
||||
private readonly IStringLocalizer _localizer;
|
||||
private readonly INavigator _navigator;
|
||||
[ObservableProperty]
|
||||
private bool _isSidebarOpen = true;
|
||||
[ObservableProperty]
|
||||
private Dictionary<string, string> _localizedStrings = new();
|
||||
[ObservableProperty]
|
||||
private string _loginLogoutButtonText = "";
|
||||
|
||||
[ObservableProperty]
|
||||
private string? _name;
|
||||
[ObservableProperty]
|
||||
private NewsViewModel? _newsViewModel;
|
||||
[ObservableProperty]
|
||||
private bool _sidebarContentVisible = true;
|
||||
|
||||
public MainViewModel(IStringLocalizer localizer, IOptions<AppConfig> appInfo, INavigator navigator,
|
||||
NewsViewModel newsViewModel)
|
||||
{
|
||||
_navigator = navigator;
|
||||
_localizer = localizer;
|
||||
NewsViewModel = newsViewModel;
|
||||
Title = "Marechai";
|
||||
Title += $" - {localizer["ApplicationName"]}";
|
||||
if(appInfo?.Value?.Environment != null) Title += $" - {appInfo.Value.Environment}";
|
||||
|
||||
GoToSecond = new AsyncRelayCommand(GoToSecondView);
|
||||
|
||||
// Initialize localized strings
|
||||
InitializeLocalizedStrings();
|
||||
|
||||
// Initialize commands
|
||||
NavigateToNewsCommand = new AsyncRelayCommand(NavigateToMainAsync);
|
||||
NavigateToBooksCommand = new AsyncRelayCommand(() => NavigateTo("books"));
|
||||
NavigateToCompaniesCommand = new AsyncRelayCommand(() => NavigateTo("companies"));
|
||||
NavigateToComputersCommand = new AsyncRelayCommand(() => NavigateTo("computers"));
|
||||
NavigateToConsolesCommand = new AsyncRelayCommand(() => NavigateTo("consoles"));
|
||||
NavigateToDocumentsCommand = new AsyncRelayCommand(() => NavigateTo("documents"));
|
||||
NavigateToDumpsCommand = new AsyncRelayCommand(() => NavigateTo("dumps"));
|
||||
NavigateToGraphicalProcessingUnitsCommand = new AsyncRelayCommand(() => NavigateTo("gpus"));
|
||||
NavigateToMagazinesCommand = new AsyncRelayCommand(() => NavigateTo("magazines"));
|
||||
NavigateToPeopleCommand = new AsyncRelayCommand(() => NavigateTo("people"));
|
||||
NavigateToProcessorsCommand = new AsyncRelayCommand(() => NavigateTo("processors"));
|
||||
NavigateToSoftwareCommand = new AsyncRelayCommand(() => NavigateTo("software"));
|
||||
NavigateToSoundSynthesizersCommand = new AsyncRelayCommand(() => NavigateTo("soundsynthesizers"));
|
||||
NavigateToSettingsCommand = new AsyncRelayCommand(() => NavigateTo("settings"));
|
||||
LoginLogoutCommand = new RelayCommand(HandleLoginLogout);
|
||||
ToggleSidebarCommand = new RelayCommand(() => IsSidebarOpen = !IsSidebarOpen);
|
||||
|
||||
UpdateLoginLogoutButtonText();
|
||||
}
|
||||
|
||||
public string? Title { get; }
|
||||
|
||||
public ICommand GoToSecond { get; }
|
||||
|
||||
public ICommand NavigateToNewsCommand { get; }
|
||||
public ICommand NavigateToBooksCommand { get; }
|
||||
public ICommand NavigateToCompaniesCommand { get; }
|
||||
public ICommand NavigateToComputersCommand { get; }
|
||||
public ICommand NavigateToConsolesCommand { get; }
|
||||
public ICommand NavigateToDocumentsCommand { get; }
|
||||
public ICommand NavigateToDumpsCommand { get; }
|
||||
public ICommand NavigateToGraphicalProcessingUnitsCommand { get; }
|
||||
public ICommand NavigateToMagazinesCommand { get; }
|
||||
public ICommand NavigateToPeopleCommand { get; }
|
||||
public ICommand NavigateToProcessorsCommand { get; }
|
||||
public ICommand NavigateToSoftwareCommand { get; }
|
||||
public ICommand NavigateToSoundSynthesizersCommand { get; }
|
||||
public ICommand NavigateToSettingsCommand { get; }
|
||||
public ICommand LoginLogoutCommand { get; }
|
||||
public ICommand ToggleSidebarCommand { get; }
|
||||
|
||||
private void InitializeLocalizedStrings()
|
||||
{
|
||||
LocalizedStrings = new Dictionary<string, string>
|
||||
{
|
||||
{
|
||||
"News", _localizer["News"]
|
||||
},
|
||||
{
|
||||
"Books", _localizer["Books"]
|
||||
},
|
||||
{
|
||||
"Companies", _localizer["Companies"]
|
||||
},
|
||||
{
|
||||
"Computers", _localizer["Computers"]
|
||||
},
|
||||
{
|
||||
"Consoles", _localizer["Consoles"]
|
||||
},
|
||||
{
|
||||
"Documents", _localizer["Documents"]
|
||||
},
|
||||
{
|
||||
"Dumps", _localizer["Dumps"]
|
||||
},
|
||||
{
|
||||
"GraphicalProcessingUnits", _localizer["GraphicalProcessingUnits"]
|
||||
},
|
||||
{
|
||||
"Magazines", _localizer["Magazines"]
|
||||
},
|
||||
{
|
||||
"People", _localizer["People"]
|
||||
},
|
||||
{
|
||||
"Processors", _localizer["Processors"]
|
||||
},
|
||||
{
|
||||
"Software", _localizer["Software"]
|
||||
},
|
||||
{
|
||||
"SoundSynthesizers", _localizer["SoundSynthesizers"]
|
||||
},
|
||||
{
|
||||
"Settings", _localizer["Settings"]
|
||||
},
|
||||
{
|
||||
"Login", _localizer["Login"]
|
||||
},
|
||||
{
|
||||
"Logout", _localizer["Logout"]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void UpdateLoginLogoutButtonText()
|
||||
{
|
||||
// TODO: Check if user is logged in
|
||||
// For now, always show "Login"
|
||||
LoginLogoutButtonText = LocalizedStrings["Login"];
|
||||
}
|
||||
|
||||
private static void HandleLoginLogout()
|
||||
{
|
||||
// TODO: Implement login/logout logic
|
||||
}
|
||||
|
||||
private async Task NavigateTo(string destination)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Navigate within the Main region using relative navigation
|
||||
// The "./" prefix means navigate within the current page's region
|
||||
await _navigator.NavigateRouteAsync(this, $"./{destination}");
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
// Navigation error - fail silently for now
|
||||
// TODO: Add error handling/logging
|
||||
}
|
||||
}
|
||||
|
||||
private async Task NavigateToMainAsync()
|
||||
{
|
||||
// Navigate to News page (the default/home page)
|
||||
await NavigateTo("News");
|
||||
}
|
||||
|
||||
private async Task GoToSecondView()
|
||||
{
|
||||
// Navigate to Second view model providing qualifier and data
|
||||
await _navigator.NavigateViewModelAsync<SecondViewModel>(this, "Second", new Entity(Name ?? ""));
|
||||
}
|
||||
}
|
||||
176
Marechai.App/Presentation/ViewModels/NewsViewModel.cs
Normal file
176
Marechai.App/Presentation/ViewModels/NewsViewModel.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
using Marechai.App.Helpers;
|
||||
using Marechai.App.Presentation.Models;
|
||||
using Marechai.App.Services;
|
||||
using Marechai.Data;
|
||||
using Uno.Extensions.Navigation;
|
||||
|
||||
namespace Marechai.App.Presentation.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper for NewsDto with generated display text
|
||||
/// </summary>
|
||||
public class NewsItemViewModel
|
||||
{
|
||||
public required NewsDto News { get; init; }
|
||||
public required string DisplayText { get; init; }
|
||||
public required IAsyncRelayCommand<NewsDto> NavigateToItemCommand { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if this news item can be navigated to (only computers and consoles)
|
||||
/// </summary>
|
||||
public bool CanNavigateToItem
|
||||
{
|
||||
get
|
||||
{
|
||||
if(News?.Type is null) return false;
|
||||
var type = (NewsType)News.Type.Value;
|
||||
|
||||
return type is NewsType.NewComputerInDb
|
||||
or NewsType.NewConsoleInDb
|
||||
or NewsType.UpdatedComputerInDb
|
||||
or NewsType.UpdatedConsoleInDb
|
||||
or NewsType.NewComputerInCollection
|
||||
or NewsType.NewConsoleInCollection
|
||||
or NewsType.UpdatedComputerInCollection
|
||||
or NewsType.UpdatedConsoleInCollection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public partial class NewsViewModel : ObservableObject
|
||||
{
|
||||
private readonly IStringLocalizer _localizer;
|
||||
private readonly ILogger<NewsViewModel> _logger;
|
||||
private readonly INavigator _navigator;
|
||||
private readonly NewsService _newsService;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _errorMessage = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _hasError;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isLoading;
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<NewsItemViewModel> _newsList = [];
|
||||
|
||||
public NewsViewModel(NewsService newsService, IStringLocalizer localizer, ILogger<NewsViewModel> logger,
|
||||
INavigator navigator)
|
||||
{
|
||||
_newsService = newsService;
|
||||
_localizer = localizer;
|
||||
_logger = logger;
|
||||
_navigator = navigator;
|
||||
LoadNews = new AsyncRelayCommand(LoadNewsAsync);
|
||||
}
|
||||
|
||||
public IAsyncRelayCommand LoadNews { get; }
|
||||
|
||||
[RelayCommand]
|
||||
private async Task NavigateToNewsItem(NewsDto news)
|
||||
{
|
||||
if(news?.Type is null) return;
|
||||
|
||||
var newsType = (NewsType)news.Type.Value;
|
||||
|
||||
// Only navigate for computer and console news items
|
||||
bool isComputerOrConsole = newsType is NewsType.NewComputerInDb
|
||||
or NewsType.NewConsoleInDb
|
||||
or NewsType.UpdatedComputerInDb
|
||||
or NewsType.UpdatedConsoleInDb
|
||||
or NewsType.NewComputerInCollection
|
||||
or NewsType.NewConsoleInCollection
|
||||
or NewsType.UpdatedComputerInCollection
|
||||
or NewsType.UpdatedConsoleInCollection;
|
||||
|
||||
if(!isComputerOrConsole) return;
|
||||
|
||||
// Extract the machine ID from AffectedId
|
||||
if(news.AffectedId is null) return;
|
||||
|
||||
int machineId = UntypedNodeExtractor.ExtractInt(news.AffectedId);
|
||||
|
||||
if(machineId <= 0) return;
|
||||
|
||||
// Navigate to machine view with source information
|
||||
var navParam = new MachineViewNavigationParameter
|
||||
{
|
||||
MachineId = machineId,
|
||||
NavigationSource = this
|
||||
};
|
||||
|
||||
await _navigator.NavigateViewModelAsync<MachineViewViewModel>(this, data: navParam);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to extract int from UntypedNode
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// Generates localized text based on NewsType
|
||||
/// </summary>
|
||||
private string GetLocalizedTextForNewsType(NewsType type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
NewsType.NewComputerInDb => _localizer["New computer in database"].Value,
|
||||
NewsType.NewConsoleInDb => _localizer["New console in database"].Value,
|
||||
NewsType.NewComputerInCollection => _localizer["New computer in collection"].Value,
|
||||
NewsType.NewConsoleInCollection => _localizer["New console in collection"].Value,
|
||||
NewsType.UpdatedComputerInDb => _localizer["Updated computer in database"].Value,
|
||||
NewsType.UpdatedConsoleInDb => _localizer["Updated console in database"].Value,
|
||||
NewsType.UpdatedComputerInCollection => _localizer["Updated computer in collection"].Value,
|
||||
NewsType.UpdatedConsoleInCollection => _localizer["Updated console in collection"].Value,
|
||||
_ => string.Empty
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the latest news from the API
|
||||
/// </summary>
|
||||
private async Task LoadNewsAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsLoading = true;
|
||||
ErrorMessage = string.Empty;
|
||||
HasError = false;
|
||||
NewsList.Clear();
|
||||
|
||||
List<NewsDto> news = await _newsService.GetLatestNewsAsync();
|
||||
|
||||
if(news.Count == 0)
|
||||
{
|
||||
ErrorMessage = _localizer["No news available"].Value;
|
||||
HasError = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach(NewsDto item in news)
|
||||
{
|
||||
NewsList.Add(new NewsItemViewModel
|
||||
{
|
||||
News = item,
|
||||
DisplayText = GetLocalizedTextForNewsType((NewsType)(item.Type ?? 0)),
|
||||
NavigateToItemCommand = NavigateToNewsItemCommand
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
_logger.LogError("Error loading news: {Exception}", ex.Message);
|
||||
ErrorMessage = _localizer["Failed to load news. Please try again later."].Value;
|
||||
HasError = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsLoading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Marechai.App/Presentation/ViewModels/SecondViewModel.cs
Normal file
3
Marechai.App/Presentation/ViewModels/SecondViewModel.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace Marechai.App.Presentation.ViewModels;
|
||||
|
||||
public record SecondViewModel(Entity Entity) {}
|
||||
12
Marechai.App/Presentation/ViewModels/ShellViewModel.cs
Normal file
12
Marechai.App/Presentation/ViewModels/ShellViewModel.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Uno.Extensions.Navigation;
|
||||
|
||||
namespace Marechai.App.Presentation.ViewModels;
|
||||
|
||||
public class ShellViewModel
|
||||
{
|
||||
private readonly INavigator _navigator;
|
||||
|
||||
public ShellViewModel(INavigator navigator) => _navigator = navigator;
|
||||
|
||||
// Add code here to initialize or attach event handlers to singleton services
|
||||
}
|
||||
120
Marechai.App/Presentation/Views/CompaniesPage.xaml
Normal file
120
Marechai.App/Presentation/Views/CompaniesPage.xaml
Normal file
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0"
|
||||
encoding="utf-8"?>
|
||||
|
||||
<Page x:Class="Marechai.App.Presentation.Views.CompaniesPage"
|
||||
x:Name="PageRoot"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:utu="using:Uno.Toolkit.UI"
|
||||
NavigationCacheMode="Required"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<Grid utu:SafeArea.Insets="VisibleBounds">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" /> <RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Header -->
|
||||
<utu:NavigationBar Grid.Row="0"
|
||||
Content="{Binding Path=Title}">
|
||||
<utu:NavigationBar.MainCommand>
|
||||
<AppBarButton Icon="Back"
|
||||
Label="Back"
|
||||
Command="{Binding GoBackCommand}"
|
||||
AutomationProperties.Name="Go back" />
|
||||
</utu:NavigationBar.MainCommand>
|
||||
</utu:NavigationBar>
|
||||
|
||||
<!-- Content -->
|
||||
<ScrollViewer Grid.Row="1">
|
||||
<StackPanel Padding="16"
|
||||
Spacing="16">
|
||||
|
||||
<!-- Company Count Display -->
|
||||
<StackPanel HorizontalAlignment="Center"
|
||||
Spacing="8">
|
||||
<TextBlock Text="{Binding CompanyCountText}"
|
||||
TextAlignment="Center"
|
||||
FontSize="18"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
<TextBlock Text="{Binding CompanyCount}"
|
||||
TextAlignment="Center"
|
||||
FontSize="48"
|
||||
FontWeight="Bold"
|
||||
Foreground="{ThemeResource SystemAccentColor}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Search Box -->
|
||||
<AutoSuggestBox x:Name="CompaniesSearchBox"
|
||||
HorizontalAlignment="Stretch"
|
||||
PlaceholderText="Search companies..."
|
||||
Text="{Binding SearchQuery, Mode=TwoWay}"
|
||||
QuerySubmitted="OnSearchQuerySubmitted"
|
||||
TextChanged="OnSearchTextChanged">
|
||||
<AutoSuggestBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Name}" />
|
||||
</DataTemplate>
|
||||
</AutoSuggestBox.ItemTemplate>
|
||||
</AutoSuggestBox>
|
||||
|
||||
<!-- Loading State -->
|
||||
<StackPanel Visibility="{Binding IsLoading}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Padding="32"
|
||||
Spacing="16">
|
||||
<ProgressRing IsActive="True"
|
||||
IsIndeterminate="True"
|
||||
Height="48"
|
||||
Width="48" />
|
||||
<TextBlock Text="Loading..."
|
||||
TextAlignment="Center"
|
||||
FontSize="14" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Error State -->
|
||||
<StackPanel Visibility="{Binding HasError}"
|
||||
Padding="16"
|
||||
Background="{ThemeResource SystemErrorBackgroundColor}"
|
||||
CornerRadius="8"
|
||||
Spacing="8">
|
||||
<TextBlock Text="Error"
|
||||
FontWeight="Bold"
|
||||
Foreground="{ThemeResource SystemErrorTextForegroundColor}" />
|
||||
<TextBlock Text="{Binding ErrorMessage}"
|
||||
Foreground="{ThemeResource SystemErrorTextForegroundColor}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Companies List -->
|
||||
<StackPanel Visibility="{Binding IsDataLoaded}"
|
||||
Spacing="8">
|
||||
<ItemsRepeater ItemsSource="{Binding CompaniesList}">
|
||||
<ItemsRepeater.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Button HorizontalAlignment="Stretch"
|
||||
Command="{Binding DataContext.NavigateToCompanyCommand, ElementName=PageRoot}"
|
||||
CommandParameter="{Binding}">
|
||||
<StackPanel Spacing="4"
|
||||
Padding="8">
|
||||
<TextBlock Text="{Binding Name}"
|
||||
FontSize="16"
|
||||
FontWeight="SemiBold" />
|
||||
<TextBlock Text="{Binding FoundationDateDisplay}"
|
||||
FontSize="12"
|
||||
Opacity="0.6" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</ItemsRepeater.ItemTemplate>
|
||||
</ItemsRepeater>
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
</Grid>
|
||||
|
||||
</Page>
|
||||
59
Marechai.App/Presentation/Views/CompaniesPage.xaml.cs
Normal file
59
Marechai.App/Presentation/Views/CompaniesPage.xaml.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using Marechai.App.Presentation.ViewModels;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
|
||||
namespace Marechai.App.Presentation.Views;
|
||||
|
||||
public sealed partial class CompaniesPage : Page
|
||||
{
|
||||
public CompaniesPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContextChanged += CompaniesPage_DataContextChanged;
|
||||
Loaded += CompaniesPage_Loaded;
|
||||
}
|
||||
|
||||
private void CompaniesPage_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if(DataContext is not CompaniesViewModel viewModel) return;
|
||||
|
||||
// Trigger data loading
|
||||
_ = viewModel.LoadData.ExecuteAsync(null);
|
||||
}
|
||||
|
||||
private void CompaniesPage_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
|
||||
{
|
||||
if(args.NewValue is CompaniesViewModel viewModel)
|
||||
{
|
||||
// Trigger data loading when data context changes
|
||||
_ = viewModel.LoadData.ExecuteAsync(null);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
if(DataContext is CompaniesViewModel viewModel)
|
||||
{
|
||||
// Trigger data loading when navigating to the page
|
||||
_ = viewModel.LoadData.ExecuteAsync(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSearchTextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
|
||||
{
|
||||
if(args.Reason == AutoSuggestionBoxTextChangeReason.UserInput)
|
||||
{
|
||||
// The two-way binding will automatically update SearchQuery in ViewModel,
|
||||
// which will trigger OnSearchQueryChanged and filter the list
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSearchQuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
|
||||
{
|
||||
// The two-way binding will automatically update SearchQuery in ViewModel,
|
||||
// which will trigger OnSearchQueryChanged and filter the list
|
||||
}
|
||||
}
|
||||
307
Marechai.App/Presentation/Views/CompanyDetailPage.xaml
Normal file
307
Marechai.App/Presentation/Views/CompanyDetailPage.xaml
Normal file
@@ -0,0 +1,307 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Page x:Class="Marechai.App.Presentation.Views.CompanyDetailPage"
|
||||
x:Name="PageRoot"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:utu="using:Uno.Toolkit.UI"
|
||||
NavigationCacheMode="Required"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<Page.Resources>
|
||||
</Page.Resources>
|
||||
|
||||
<Grid utu:SafeArea.Insets="VisibleBounds">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Header -->
|
||||
<utu:NavigationBar Grid.Row="0"
|
||||
Content="{Binding Path=Title}">
|
||||
<utu:NavigationBar.MainCommand>
|
||||
<AppBarButton Icon="Back"
|
||||
Label="Back"
|
||||
Command="{Binding GoBackCommand}"
|
||||
AutomationProperties.Name="Go back" />
|
||||
</utu:NavigationBar.MainCommand>
|
||||
</utu:NavigationBar>
|
||||
|
||||
<!-- Content -->
|
||||
<ScrollViewer Grid.Row="1">
|
||||
<StackPanel Padding="16"
|
||||
Spacing="16">
|
||||
|
||||
<!-- Logo Display (Top Center) -->
|
||||
<Image MaxWidth="96"
|
||||
MaxHeight="96"
|
||||
Stretch="Uniform"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="0,0,0,8"
|
||||
Source="{Binding LogoImageSource}"
|
||||
Visibility="{Binding HasLogoContent, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
|
||||
<!-- Loading State -->
|
||||
<StackPanel Visibility="{Binding IsLoading}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Padding="32"
|
||||
Spacing="16">
|
||||
<ProgressRing IsActive="True"
|
||||
IsIndeterminate="True"
|
||||
Height="48"
|
||||
Width="48" />
|
||||
<TextBlock Text="Loading..."
|
||||
TextAlignment="Center"
|
||||
FontSize="14" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Error State -->
|
||||
<StackPanel Visibility="{Binding HasError}"
|
||||
Padding="16"
|
||||
Background="{ThemeResource SystemErrorBackgroundColor}"
|
||||
CornerRadius="8"
|
||||
Spacing="8">
|
||||
<TextBlock Text="Error"
|
||||
FontWeight="Bold"
|
||||
Foreground="{ThemeResource SystemErrorTextForegroundColor}" />
|
||||
<TextBlock Text="{Binding ErrorMessage}"
|
||||
Foreground="{ThemeResource SystemErrorTextForegroundColor}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Company Details -->
|
||||
<StackPanel Visibility="{Binding IsDataLoaded}"
|
||||
Spacing="16">
|
||||
|
||||
<!-- Company Name -->
|
||||
<TextBlock Text="{Binding Company.Name}"
|
||||
FontSize="28"
|
||||
FontWeight="Bold"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<!-- Company Status -->
|
||||
<StackPanel Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
CornerRadius="8"
|
||||
Padding="12"
|
||||
Spacing="8">
|
||||
<TextBlock Text="Status"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
<TextBlock Text="{Binding CompanyStatusDisplay}"
|
||||
FontSize="14"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Founded Date -->
|
||||
<StackPanel Visibility="{Binding Company.Founded, Converter={StaticResource ObjectToVisibilityConverter}}"
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
CornerRadius="8"
|
||||
Padding="12"
|
||||
Spacing="8">
|
||||
<TextBlock Text="Founded"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
<TextBlock Text="{Binding CompanyFoundedDateDisplay}"
|
||||
FontSize="14" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Legal Name -->
|
||||
<StackPanel Visibility="{Binding Company.LegalName, Converter={StaticResource StringToVisibilityConverter}}"
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
CornerRadius="8"
|
||||
Padding="12"
|
||||
Spacing="8">
|
||||
<TextBlock Text="Legal Name"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
<TextBlock Text="{Binding Company.LegalName}"
|
||||
FontSize="14" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Country -->
|
||||
<StackPanel Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
CornerRadius="8"
|
||||
Padding="12">
|
||||
<Grid ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 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>
|
||||
|
||||
<!-- Address -->
|
||||
<StackPanel Visibility="{Binding Company.Address, Converter={StaticResource StringToVisibilityConverter}}"
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
CornerRadius="8"
|
||||
Padding="12"
|
||||
Spacing="8">
|
||||
<TextBlock Text="Address"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
<TextBlock TextWrapping="Wrap">
|
||||
<Run Text="{Binding Company.Address}" />
|
||||
<Run Text="{Binding Company.City}" />
|
||||
<Run Text="{Binding Company.PostalCode}" />
|
||||
<Run Text="{Binding Company.Province}" />
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Links Section -->
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Text="Links"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
|
||||
<!-- Website -->
|
||||
<HyperlinkButton Visibility="{Binding Company.Website, Converter={StaticResource StringToVisibilityConverter}}"
|
||||
NavigateUri="{Binding Company.Website}">
|
||||
<TextBlock Text="Website" />
|
||||
</HyperlinkButton>
|
||||
|
||||
<!-- Twitter -->
|
||||
<HyperlinkButton Visibility="{Binding Company.Twitter, Converter={StaticResource StringToVisibilityConverter}}"
|
||||
Click="OnTwitterClick">
|
||||
<TextBlock Text="Twitter" />
|
||||
</HyperlinkButton>
|
||||
|
||||
<!-- Facebook -->
|
||||
<HyperlinkButton Visibility="{Binding Company.Facebook, Converter={StaticResource StringToVisibilityConverter}}"
|
||||
Click="OnFacebookClick">
|
||||
<TextBlock Text="Facebook" />
|
||||
</HyperlinkButton>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Computers Section -->
|
||||
<StackPanel Visibility="{Binding Computers.Count, Converter={StaticResource ZeroToVisibilityConverter}}"
|
||||
Spacing="8">
|
||||
<StackPanel Orientation="Horizontal"
|
||||
Spacing="8">
|
||||
<TextBlock Text="Computers"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
<TextBlock Text="{Binding Computers.Count}"
|
||||
FontSize="14"
|
||||
FontWeight="Bold"
|
||||
Foreground="{ThemeResource SystemAccentColor}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Filter Box -->
|
||||
<AutoSuggestBox PlaceholderText="Filter computers..."
|
||||
Text="{Binding ComputersFilterText, Mode=TwoWay}"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{ThemeResource ControlElevationBorderBrush}" />
|
||||
|
||||
<!-- Scrollable Computers List -->
|
||||
<ScrollViewer Height="200"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{ThemeResource ControlElevationBorderBrush}"
|
||||
CornerRadius="8">
|
||||
<ItemsRepeater ItemsSource="{Binding FilteredComputers}"
|
||||
Margin="0">
|
||||
<ItemsRepeater.Layout>
|
||||
<StackLayout Spacing="4" />
|
||||
</ItemsRepeater.Layout>
|
||||
<ItemsRepeater.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Button Command="{Binding DataContext.NavigateToMachineCommand, ElementName=PageRoot}"
|
||||
CommandParameter="{Binding}"
|
||||
Padding="12,8"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left">
|
||||
<TextBlock Text="{Binding Name}"
|
||||
FontSize="12"
|
||||
TextWrapping="Wrap" />
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</ItemsRepeater.ItemTemplate>
|
||||
</ItemsRepeater>
|
||||
</ScrollViewer>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Consoles Section -->
|
||||
<StackPanel Visibility="{Binding Consoles.Count, Converter={StaticResource ZeroToVisibilityConverter}}"
|
||||
Spacing="8">
|
||||
<StackPanel Orientation="Horizontal"
|
||||
Spacing="8">
|
||||
<TextBlock Text="Consoles"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
<TextBlock Text="{Binding Consoles.Count}"
|
||||
FontSize="14"
|
||||
FontWeight="Bold"
|
||||
Foreground="{ThemeResource SystemAccentColor}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Filter Box -->
|
||||
<AutoSuggestBox PlaceholderText="Filter consoles..."
|
||||
Text="{Binding ConsoelsFilterText, Mode=TwoWay}"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{ThemeResource ControlElevationBorderBrush}" />
|
||||
|
||||
<!-- Scrollable Consoles List -->
|
||||
<ScrollViewer Height="200"
|
||||
BorderThickness="1"
|
||||
BorderBrush="{ThemeResource ControlElevationBorderBrush}"
|
||||
CornerRadius="8">
|
||||
<ItemsRepeater ItemsSource="{Binding FilteredConsoles}"
|
||||
Margin="0">
|
||||
<ItemsRepeater.Layout>
|
||||
<StackLayout Spacing="4" />
|
||||
</ItemsRepeater.Layout>
|
||||
<ItemsRepeater.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Button Command="{Binding DataContext.NavigateToMachineCommand, ElementName=PageRoot}"
|
||||
CommandParameter="{Binding}"
|
||||
Padding="12,8"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left">
|
||||
<TextBlock Text="{Binding Name}"
|
||||
FontSize="12"
|
||||
TextWrapping="Wrap" />
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</ItemsRepeater.ItemTemplate>
|
||||
</ItemsRepeater>
|
||||
</ScrollViewer>
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
</Grid>
|
||||
|
||||
</Page>
|
||||
77
Marechai.App/Presentation/Views/CompanyDetailPage.xaml.cs
Normal file
77
Marechai.App/Presentation/Views/CompanyDetailPage.xaml.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using Windows.System;
|
||||
using Marechai.App.Presentation.Models;
|
||||
using Marechai.App.Presentation.ViewModels;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
|
||||
namespace Marechai.App.Presentation.Views;
|
||||
|
||||
public sealed partial class CompanyDetailPage : Page
|
||||
{
|
||||
private object? _navigationSource;
|
||||
private int? _pendingCompanyId;
|
||||
|
||||
public CompanyDetailPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContextChanged += CompanyDetailPage_DataContextChanged;
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
int? companyId = null;
|
||||
|
||||
// Handle both int and CompanyDetailNavigationParameter
|
||||
if(e.Parameter is int intId)
|
||||
companyId = intId;
|
||||
else if(e.Parameter is CompanyDetailNavigationParameter navParam)
|
||||
{
|
||||
companyId = navParam.CompanyId;
|
||||
_navigationSource = navParam.NavigationSource;
|
||||
}
|
||||
|
||||
if(companyId.HasValue)
|
||||
{
|
||||
_pendingCompanyId = companyId;
|
||||
|
||||
if(DataContext is CompanyDetailViewModel viewModel)
|
||||
{
|
||||
viewModel.CompanyId = companyId.Value;
|
||||
_ = viewModel.LoadData.ExecuteAsync(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CompanyDetailPage_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
|
||||
{
|
||||
if(DataContext is CompanyDetailViewModel viewModel && _pendingCompanyId.HasValue)
|
||||
{
|
||||
viewModel.CompanyId = _pendingCompanyId.Value;
|
||||
_ = viewModel.LoadData.ExecuteAsync(null);
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnTwitterClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if(DataContext is CompanyDetailViewModel viewModel && viewModel.Company?.Twitter is not null)
|
||||
{
|
||||
var uri = new Uri($"https://www.twitter.com/{viewModel.Company.Twitter}");
|
||||
await Launcher.LaunchUriAsync(uri);
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnFacebookClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if(DataContext is CompanyDetailViewModel viewModel && viewModel.Company?.Facebook is not null)
|
||||
{
|
||||
var uri = new Uri($"https://www.facebook.com/{viewModel.Company.Facebook}");
|
||||
await Launcher.LaunchUriAsync(uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
261
Marechai.App/Presentation/Views/ComputersListPage.xaml
Normal file
261
Marechai.App/Presentation/Views/ComputersListPage.xaml
Normal file
@@ -0,0 +1,261 @@
|
||||
<?xml version="1.0"
|
||||
encoding="utf-8"?>
|
||||
|
||||
<Page x:Class="Marechai.App.Presentation.Views.ComputersListPage"
|
||||
x:Name="PageRoot"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
NavigationCacheMode="Required"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<Grid RowSpacing="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" /> <RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Header with Back Button and Title -->
|
||||
<Grid Grid.Row="0"
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
|
||||
BorderThickness="0,0,0,1"
|
||||
Padding="16,12,16,12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Back Button -->
|
||||
<Button Grid.Column="0"
|
||||
Command="{Binding GoBackCommand}"
|
||||
Style="{ThemeResource AlternateButtonStyle}"
|
||||
ToolTipService.ToolTip="Go back"
|
||||
Padding="8"
|
||||
MinWidth="44"
|
||||
MinHeight="44"
|
||||
VerticalAlignment="Center">
|
||||
<FontIcon Glyph=""
|
||||
FontSize="16" />
|
||||
</Button>
|
||||
|
||||
<!-- Page Title -->
|
||||
<StackPanel Grid.Column="1"
|
||||
Margin="16,0,0,0"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding PageTitle}"
|
||||
FontSize="20"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextControlForeground}" />
|
||||
<TextBlock Text="{Binding FilterDescription}"
|
||||
FontSize="12"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}"
|
||||
Margin="0,4,0,0" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Main Content -->
|
||||
<Grid Grid.Row="1">
|
||||
|
||||
<!-- Loading State -->
|
||||
<StackPanel Visibility="{Binding IsLoading}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Padding="32"
|
||||
Spacing="16">
|
||||
<ProgressRing IsActive="True"
|
||||
IsIndeterminate="True"
|
||||
Height="64"
|
||||
Width="64"
|
||||
Foreground="{ThemeResource SystemAccentColor}" />
|
||||
<TextBlock Text="Loading computers..."
|
||||
FontSize="14"
|
||||
TextAlignment="Center"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Error State -->
|
||||
<StackPanel Visibility="{Binding HasError}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Padding="24"
|
||||
Spacing="16"
|
||||
MaxWidth="400">
|
||||
<InfoBar IsOpen="True"
|
||||
Severity="Error"
|
||||
Title="Unable to Load Computers"
|
||||
Message="{Binding ErrorMessage}"
|
||||
IsClosable="False" />
|
||||
<Button Content="Retry"
|
||||
Command="{Binding LoadData}"
|
||||
HorizontalAlignment="Center"
|
||||
Style="{ThemeResource AccentButtonStyle}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Computers List -->
|
||||
<Grid Visibility="{Binding IsDataLoaded}">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Disabled">
|
||||
<Grid Padding="8">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" /> <RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Count Header -->
|
||||
<StackPanel Grid.Row="0"
|
||||
Padding="16,12"
|
||||
Orientation="Horizontal"
|
||||
Spacing="4">
|
||||
<TextBlock FontSize="12"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}"
|
||||
Text="RESULTS:" />
|
||||
<TextBlock FontSize="12"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}"
|
||||
Text="{Binding ComputersList.Count}" />
|
||||
<TextBlock FontSize="12"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}"
|
||||
Text="computers" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Computers List -->
|
||||
<ItemsControl Grid.Row="1"
|
||||
ItemsSource="{Binding ComputersList}"
|
||||
Padding="0"
|
||||
Margin="0,8,0,0"
|
||||
HorizontalAlignment="Stretch">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Vertical"
|
||||
Spacing="0"
|
||||
HorizontalAlignment="Stretch" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Button Padding="0"
|
||||
Margin="0,0,0,8"
|
||||
MinHeight="80"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
HorizontalAlignment="Stretch"
|
||||
Command="{Binding DataContext.NavigateToComputerCommand, ElementName=PageRoot}"
|
||||
CommandParameter="{Binding}"
|
||||
Background="Transparent"
|
||||
BorderThickness="0">
|
||||
<Button.Template>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Grid MinHeight="80"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<!-- Shadow effect -->
|
||||
<Border x:Name="ShadowBorder"
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="16,12"
|
||||
Translation="0, 0, 4"
|
||||
VerticalAlignment="Stretch">
|
||||
<Border.Shadow>
|
||||
<ThemeShadow />
|
||||
</Border.Shadow>
|
||||
|
||||
<Grid ColumnSpacing="16">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Computer Info -->
|
||||
<StackPanel Grid.Column="0"
|
||||
Spacing="8"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Stretch">
|
||||
<TextBlock Text="{Binding Name}"
|
||||
FontSize="16"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextControlForeground}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
<Grid ColumnSpacing="16"
|
||||
Height="20"
|
||||
VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel Grid.Column="0"
|
||||
Orientation="Horizontal"
|
||||
Spacing="6"
|
||||
VerticalAlignment="Center">
|
||||
<FontIcon Glyph=""
|
||||
FontSize="14"
|
||||
Foreground="{ThemeResource SystemAccentColor}" />
|
||||
<TextBlock Text="{Binding Manufacturer}"
|
||||
FontSize="13"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
Spacing="6"
|
||||
VerticalAlignment="Center">
|
||||
<FontIcon Glyph=""
|
||||
FontSize="14"
|
||||
Foreground="{ThemeResource SystemAccentColor}" />
|
||||
<TextBlock FontSize="13"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}">
|
||||
<Run Text="{Binding Year}" />
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Navigation Arrow -->
|
||||
<StackPanel Grid.Column="1"
|
||||
VerticalAlignment="Center">
|
||||
<FontIcon Glyph=""
|
||||
FontSize="18"
|
||||
Foreground="{ThemeResource SystemAccentColor}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="PointerOver">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ShadowBorder.Background"
|
||||
Value="{ThemeResource CardBackgroundFillColorSecondaryBrush}" />
|
||||
<Setter Target="ShadowBorder.Translation"
|
||||
Value="0, -2, 8" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ShadowBorder.Background"
|
||||
Value="{ThemeResource CardBackgroundFillColorTertiaryBrush}" />
|
||||
<Setter Target="ShadowBorder.Translation"
|
||||
Value="0, 0, 2" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Button.Template>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
</Page>
|
||||
37
Marechai.App/Presentation/Views/ComputersListPage.xaml.cs
Normal file
37
Marechai.App/Presentation/Views/ComputersListPage.xaml.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using Marechai.App.Presentation.ViewModels;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Marechai.App.Presentation.Views;
|
||||
|
||||
/// <summary>
|
||||
/// Professional list view for displaying computers filtered by letter, year, or all.
|
||||
/// Features responsive layout, modern styling, and smooth navigation.
|
||||
/// </summary>
|
||||
public sealed partial class ComputersListPage : Page
|
||||
{
|
||||
public ComputersListPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
Loaded += ComputersListPage_Loaded;
|
||||
DataContextChanged += ComputersListPage_DataContextChanged;
|
||||
}
|
||||
|
||||
private void ComputersListPage_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
|
||||
{
|
||||
if(DataContext is ComputersListViewModel vm)
|
||||
{
|
||||
// Load data when DataContext is set
|
||||
vm.LoadData.Execute(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void ComputersListPage_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if(DataContext is ComputersListViewModel vm)
|
||||
{
|
||||
// Load data when page is loaded (fallback)
|
||||
vm.LoadData.Execute(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
311
Marechai.App/Presentation/Views/ComputersPage.xaml
Normal file
311
Marechai.App/Presentation/Views/ComputersPage.xaml
Normal file
@@ -0,0 +1,311 @@
|
||||
<?xml version="1.0"
|
||||
encoding="utf-8"?>
|
||||
|
||||
<Page x:Class="Marechai.App.Presentation.Views.ComputersPage"
|
||||
x:Name="PageRoot"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:utu="using:Uno.Toolkit.UI"
|
||||
NavigationCacheMode="Required"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<Grid utu:SafeArea.Insets="VisibleBounds">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" /> <RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Header -->
|
||||
<utu:NavigationBar Grid.Row="0"
|
||||
Content="{Binding Path=Title}">
|
||||
<utu:NavigationBar.MainCommand>
|
||||
<AppBarButton Icon="Back"
|
||||
Label="Back"
|
||||
Command="{Binding GoBackCommand}"
|
||||
AutomationProperties.Name="Go back" />
|
||||
</utu:NavigationBar.MainCommand>
|
||||
</utu:NavigationBar>
|
||||
|
||||
<!-- Content -->
|
||||
<ScrollViewer Grid.Row="1">
|
||||
<StackPanel Padding="16"
|
||||
Spacing="24">
|
||||
|
||||
<!-- Computer Count Display -->
|
||||
<StackPanel HorizontalAlignment="Center"
|
||||
Spacing="8">
|
||||
<TextBlock Text="{Binding ComputerCountText}"
|
||||
TextAlignment="Center"
|
||||
FontSize="18"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
<TextBlock Text="{Binding ComputerCount}"
|
||||
TextAlignment="Center"
|
||||
FontSize="48"
|
||||
FontWeight="Bold"
|
||||
Foreground="{ThemeResource SystemAccentColor}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Loading State -->
|
||||
<StackPanel Visibility="{Binding IsLoading}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Padding="32"
|
||||
Spacing="16">
|
||||
<ProgressRing IsActive="True"
|
||||
IsIndeterminate="True"
|
||||
Height="48"
|
||||
Width="48" />
|
||||
<TextBlock Text="Loading..."
|
||||
TextAlignment="Center"
|
||||
FontSize="14" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Error State -->
|
||||
<StackPanel Visibility="{Binding HasError}"
|
||||
Padding="16"
|
||||
Background="{ThemeResource SystemErrorBackgroundColor}"
|
||||
CornerRadius="8"
|
||||
Spacing="8">
|
||||
<TextBlock Text="Error"
|
||||
FontWeight="Bold"
|
||||
Foreground="{ThemeResource SystemErrorTextForegroundColor}" />
|
||||
<TextBlock Text="{Binding ErrorMessage}"
|
||||
Foreground="{ThemeResource SystemErrorTextForegroundColor}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Main Content (visible when loaded and no error) -->
|
||||
<StackPanel Visibility="{Binding IsDataLoaded}"
|
||||
Spacing="24">
|
||||
|
||||
<!-- Letters Grid Section -->
|
||||
<StackPanel Spacing="12">
|
||||
<TextBlock Text="Browse by Letter"
|
||||
FontSize="16"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
<ItemsRepeater ItemsSource="{Binding LettersList}"
|
||||
Layout="{StaticResource LettersGridLayout}">
|
||||
<ItemsRepeater.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Button Content="{Binding}"
|
||||
Command="{Binding DataContext.NavigateByLetterCommand, ElementName=PageRoot}"
|
||||
CommandParameter="{Binding}"
|
||||
Style="{StaticResource KeyboardKeyButtonStyle}" />
|
||||
</DataTemplate>
|
||||
</ItemsRepeater.ItemTemplate>
|
||||
</ItemsRepeater>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Years Grid Section -->
|
||||
<StackPanel Spacing="12">
|
||||
<TextBlock Text="{Binding YearsGridTitle}"
|
||||
FontSize="16"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
<ItemsRepeater ItemsSource="{Binding YearsList}"
|
||||
Layout="{StaticResource YearsGridLayout}">
|
||||
<ItemsRepeater.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Button Content="{Binding}"
|
||||
Command="{Binding DataContext.NavigateByYearCommand, ElementName=PageRoot}"
|
||||
CommandParameter="{Binding}"
|
||||
Style="{StaticResource KeyboardKeyButtonStyle}" />
|
||||
</DataTemplate>
|
||||
</ItemsRepeater.ItemTemplate>
|
||||
</ItemsRepeater>
|
||||
</StackPanel>
|
||||
|
||||
<!-- All Computers and Search Section -->
|
||||
<StackPanel Spacing="12">
|
||||
<Button Content="All Computers"
|
||||
Padding="16,12"
|
||||
HorizontalAlignment="Stretch"
|
||||
FontSize="16"
|
||||
FontWeight="SemiBold"
|
||||
Command="{Binding NavigateAllComputersCommand}"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
|
||||
<!-- Search Field (placeholder for future implementation) -->
|
||||
<TextBox PlaceholderText="Search computers..."
|
||||
Padding="12"
|
||||
IsEnabled="False"
|
||||
Opacity="0.5" />
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
</Grid>
|
||||
|
||||
<Page.Resources>
|
||||
<!-- Keyboard Key Button Style (revised: more padding, simplified borders to avoid clipping, darker scheme) -->
|
||||
<Style x:Key="KeyboardKeyButtonStyle"
|
||||
TargetType="Button">
|
||||
<!-- Base appearance -->
|
||||
<Setter Property="Foreground"
|
||||
Value="#1A1A1A" />
|
||||
<Setter Property="Background">
|
||||
<Setter.Value>
|
||||
<LinearGradientBrush StartPoint="0,0"
|
||||
EndPoint="0,1">
|
||||
<GradientStop Color="#D6D6D6"
|
||||
Offset="0" />
|
||||
<GradientStop Color="#C2C2C2"
|
||||
Offset="0.55" />
|
||||
<GradientStop Color="#B0B0B0"
|
||||
Offset="1" />
|
||||
</LinearGradientBrush>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="BorderBrush"
|
||||
Value="#7A7A7A" />
|
||||
<Setter Property="BorderThickness"
|
||||
Value="1" />
|
||||
<Setter Property="CornerRadius"
|
||||
Value="6" />
|
||||
<Setter Property="Padding"
|
||||
Value="14,12" /> <!-- Increased vertical padding to prevent cutoff -->
|
||||
<Setter Property="Margin"
|
||||
Value="4" />
|
||||
<Setter Property="FontFamily"
|
||||
Value="Segoe UI" />
|
||||
<Setter Property="FontWeight"
|
||||
Value="SemiBold" />
|
||||
<Setter Property="FontSize"
|
||||
Value="15" />
|
||||
<Setter Property="HorizontalAlignment"
|
||||
Value="Stretch" />
|
||||
<Setter Property="VerticalAlignment"
|
||||
Value="Stretch" />
|
||||
<Setter Property="HorizontalContentAlignment"
|
||||
Value="Center" />
|
||||
<Setter Property="VerticalContentAlignment"
|
||||
Value="Center" />
|
||||
<Setter Property="MinWidth"
|
||||
Value="52" />
|
||||
<Setter Property="MinHeight"
|
||||
Value="52" /> <!-- Larger min height avoids clipping ascenders/descenders -->
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Grid>
|
||||
<!-- Shadow (simple) -->
|
||||
<Border x:Name="Shadow"
|
||||
CornerRadius="6"
|
||||
Background="#33000000"
|
||||
Margin="2,4,4,2" />
|
||||
<!-- Key surface -->
|
||||
<Border x:Name="KeyBorder"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}">
|
||||
<!-- Inner highlight & content -->
|
||||
<Grid>
|
||||
<Border CornerRadius="{TemplateBinding CornerRadius}"
|
||||
BorderBrush="#60FFFFFF"
|
||||
BorderThickness="1,1,0,0" />
|
||||
<Border CornerRadius="{TemplateBinding CornerRadius}"
|
||||
BorderBrush="#30000000"
|
||||
BorderThickness="0,0,1,1" />
|
||||
<ContentPresenter x:Name="ContentPresenter"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Margin="0"
|
||||
TextWrapping="NoWrap" />
|
||||
</Grid>
|
||||
</Border>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="PointerOver">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="KeyBorder.Background">
|
||||
<Setter.Value>
|
||||
<LinearGradientBrush StartPoint="0,0"
|
||||
EndPoint="0,1">
|
||||
<GradientStop Color="#E0E0E0"
|
||||
Offset="0" />
|
||||
<GradientStop Color="#CFCFCF"
|
||||
Offset="0.55" />
|
||||
<GradientStop Color="#BDBDBD"
|
||||
Offset="1" />
|
||||
</LinearGradientBrush>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Target="KeyBorder.BorderBrush"
|
||||
Value="#5F5F5F" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="KeyBorder.Background">
|
||||
<Setter.Value>
|
||||
<LinearGradientBrush StartPoint="0,0"
|
||||
EndPoint="0,1">
|
||||
<GradientStop Color="#9C9C9C"
|
||||
Offset="0" />
|
||||
<GradientStop Color="#A8A8A8"
|
||||
Offset="0.55" />
|
||||
<GradientStop Color="#B4B4B4"
|
||||
Offset="1" />
|
||||
</LinearGradientBrush>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Target="KeyBorder.BorderBrush"
|
||||
Value="#4A4A4A" />
|
||||
<Setter Target="KeyBorder.RenderTransform">
|
||||
<Setter.Value>
|
||||
<TranslateTransform Y="2" />
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Target="Shadow.Opacity"
|
||||
Value="0.15" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="KeyBorder.Opacity"
|
||||
Value="0.45" />
|
||||
<Setter Target="ContentPresenter.Foreground"
|
||||
Value="#777777" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="FocusStates">
|
||||
<VisualState x:Name="Focused">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="KeyBorder.BorderBrush"
|
||||
Value="#3A7AFE" />
|
||||
<Setter Target="KeyBorder.BorderThickness"
|
||||
Value="2" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Unfocused" />
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- Responsive Grid Layouts -->
|
||||
<UniformGridLayout x:Key="LettersGridLayout"
|
||||
ItemsStretch="Fill"
|
||||
MinItemWidth="44"
|
||||
MinItemHeight="44"
|
||||
MaximumRowsOrColumns="13" />
|
||||
|
||||
<UniformGridLayout x:Key="YearsGridLayout"
|
||||
ItemsStretch="Fill"
|
||||
MinItemWidth="54"
|
||||
MinItemHeight="44"
|
||||
MaximumRowsOrColumns="10" />
|
||||
|
||||
</Page.Resources>
|
||||
|
||||
</Page>
|
||||
44
Marechai.App/Presentation/Views/ComputersPage.xaml.cs
Normal file
44
Marechai.App/Presentation/Views/ComputersPage.xaml.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using Marechai.App.Presentation.ViewModels;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
|
||||
namespace Marechai.App.Presentation.Views;
|
||||
|
||||
public sealed partial class ComputersPage : Page
|
||||
{
|
||||
public ComputersPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContextChanged += ComputersPage_DataContextChanged;
|
||||
Loaded += ComputersPage_Loaded;
|
||||
}
|
||||
|
||||
private void ComputersPage_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if(DataContext is not ComputersViewModel viewModel) return;
|
||||
|
||||
// Trigger data loading
|
||||
_ = viewModel.LoadData.ExecuteAsync(null);
|
||||
}
|
||||
|
||||
private void ComputersPage_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
|
||||
{
|
||||
if(args.NewValue is ComputersViewModel viewModel)
|
||||
{
|
||||
// Trigger data loading when data context changes
|
||||
_ = viewModel.LoadData.ExecuteAsync(null);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
if(DataContext is ComputersViewModel viewModel)
|
||||
{
|
||||
// Trigger data loading when navigating to the page
|
||||
_ = viewModel.LoadData.ExecuteAsync(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
261
Marechai.App/Presentation/Views/ConsolesListPage.xaml
Normal file
261
Marechai.App/Presentation/Views/ConsolesListPage.xaml
Normal file
@@ -0,0 +1,261 @@
|
||||
<?xml version="1.0"
|
||||
encoding="utf-8"?>
|
||||
|
||||
<Page x:Class="Marechai.App.Presentation.Views.ConsolesListPage"
|
||||
x:Name="PageRoot"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
NavigationCacheMode="Required"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<Grid RowSpacing="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" /> <RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Header with Back Button and Title -->
|
||||
<Grid Grid.Row="0"
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
|
||||
BorderThickness="0,0,0,1"
|
||||
Padding="16,12,16,12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Back Button -->
|
||||
<Button Grid.Column="0"
|
||||
Command="{Binding GoBackCommand}"
|
||||
Style="{ThemeResource AlternateButtonStyle}"
|
||||
ToolTipService.ToolTip="Go back"
|
||||
Padding="8"
|
||||
MinWidth="44"
|
||||
MinHeight="44"
|
||||
VerticalAlignment="Center">
|
||||
<FontIcon Glyph=""
|
||||
FontSize="16" />
|
||||
</Button>
|
||||
|
||||
<!-- Page Title -->
|
||||
<StackPanel Grid.Column="1"
|
||||
Margin="16,0,0,0"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding PageTitle}"
|
||||
FontSize="20"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextControlForeground}" />
|
||||
<TextBlock Text="{Binding FilterDescription}"
|
||||
FontSize="12"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}"
|
||||
Margin="0,4,0,0" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Main Content -->
|
||||
<Grid Grid.Row="1">
|
||||
|
||||
<!-- Loading State -->
|
||||
<StackPanel Visibility="{Binding IsLoading}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Padding="32"
|
||||
Spacing="16">
|
||||
<ProgressRing IsActive="True"
|
||||
IsIndeterminate="True"
|
||||
Height="64"
|
||||
Width="64"
|
||||
Foreground="{ThemeResource SystemAccentColor}" />
|
||||
<TextBlock Text="Loading consoles..."
|
||||
FontSize="14"
|
||||
TextAlignment="Center"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Error State -->
|
||||
<StackPanel Visibility="{Binding HasError}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Padding="24"
|
||||
Spacing="16"
|
||||
MaxWidth="400">
|
||||
<InfoBar IsOpen="True"
|
||||
Severity="Error"
|
||||
Title="Unable to Load Consoles"
|
||||
Message="{Binding ErrorMessage}"
|
||||
IsClosable="False" />
|
||||
<Button Content="Retry"
|
||||
Command="{Binding LoadData}"
|
||||
HorizontalAlignment="Center"
|
||||
Style="{ThemeResource AccentButtonStyle}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Consoles List -->
|
||||
<Grid Visibility="{Binding IsDataLoaded}">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Disabled">
|
||||
<Grid Padding="8">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" /> <RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Count Header -->
|
||||
<StackPanel Grid.Row="0"
|
||||
Padding="16,12"
|
||||
Orientation="Horizontal"
|
||||
Spacing="4">
|
||||
<TextBlock FontSize="12"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}"
|
||||
Text="RESULTS:" />
|
||||
<TextBlock FontSize="12"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}"
|
||||
Text="{Binding ConsolesList.Count}" />
|
||||
<TextBlock FontSize="12"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}"
|
||||
Text="consoles" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Consoles List -->
|
||||
<ItemsControl Grid.Row="1"
|
||||
ItemsSource="{Binding ConsolesList}"
|
||||
Padding="0"
|
||||
Margin="0,8,0,0"
|
||||
HorizontalAlignment="Stretch">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Vertical"
|
||||
Spacing="0"
|
||||
HorizontalAlignment="Stretch" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Button Padding="0"
|
||||
Margin="0,0,0,8"
|
||||
MinHeight="80"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
HorizontalAlignment="Stretch"
|
||||
Command="{Binding DataContext.NavigateToConsoleCommand, ElementName=PageRoot}"
|
||||
CommandParameter="{Binding}"
|
||||
Background="Transparent"
|
||||
BorderThickness="0">
|
||||
<Button.Template>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Grid MinHeight="80"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<!-- Shadow effect -->
|
||||
<Border x:Name="ShadowBorder"
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="16,12"
|
||||
Translation="0, 0, 4"
|
||||
VerticalAlignment="Stretch">
|
||||
<Border.Shadow>
|
||||
<ThemeShadow />
|
||||
</Border.Shadow>
|
||||
|
||||
<Grid ColumnSpacing="16">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Console Info -->
|
||||
<StackPanel Grid.Column="0"
|
||||
Spacing="8"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Stretch">
|
||||
<TextBlock Text="{Binding Name}"
|
||||
FontSize="16"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextControlForeground}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
<Grid ColumnSpacing="16"
|
||||
Height="20"
|
||||
VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel Grid.Column="0"
|
||||
Orientation="Horizontal"
|
||||
Spacing="6"
|
||||
VerticalAlignment="Center">
|
||||
<FontIcon Glyph=""
|
||||
FontSize="14"
|
||||
Foreground="{ThemeResource SystemAccentColor}" />
|
||||
<TextBlock Text="{Binding Manufacturer}"
|
||||
FontSize="13"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
Spacing="6"
|
||||
VerticalAlignment="Center">
|
||||
<FontIcon Glyph=""
|
||||
FontSize="14"
|
||||
Foreground="{ThemeResource SystemAccentColor}" />
|
||||
<TextBlock FontSize="13"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}">
|
||||
<Run Text="{Binding Year}" />
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Navigation Arrow -->
|
||||
<StackPanel Grid.Column="1"
|
||||
VerticalAlignment="Center">
|
||||
<FontIcon Glyph=""
|
||||
FontSize="18"
|
||||
Foreground="{ThemeResource SystemAccentColor}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="PointerOver">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ShadowBorder.Background"
|
||||
Value="{ThemeResource CardBackgroundFillColorSecondaryBrush}" />
|
||||
<Setter Target="ShadowBorder.Translation"
|
||||
Value="0, -2, 8" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="ShadowBorder.Background"
|
||||
Value="{ThemeResource CardBackgroundFillColorTertiaryBrush}" />
|
||||
<Setter Target="ShadowBorder.Translation"
|
||||
Value="0, 0, 2" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Button.Template>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
</Page>
|
||||
37
Marechai.App/Presentation/Views/ConsolesListPage.xaml.cs
Normal file
37
Marechai.App/Presentation/Views/ConsolesListPage.xaml.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using Marechai.App.Presentation.ViewModels;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Marechai.App.Presentation.Views;
|
||||
|
||||
/// <summary>
|
||||
/// Professional list view for displaying consoles filtered by letter, year, or all.
|
||||
/// Features responsive layout, modern styling, and smooth navigation.
|
||||
/// </summary>
|
||||
public sealed partial class ConsolesListPage : Page
|
||||
{
|
||||
public ConsolesListPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
Loaded += ConsolesListPage_Loaded;
|
||||
DataContextChanged += ConsolesListPage_DataContextChanged;
|
||||
}
|
||||
|
||||
private void ConsolesListPage_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
|
||||
{
|
||||
if(DataContext is ConsolesListViewModel vm)
|
||||
{
|
||||
// Load data when DataContext is set
|
||||
vm.LoadData.Execute(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void ConsolesListPage_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if(DataContext is ConsolesListViewModel vm)
|
||||
{
|
||||
// Load data when page is loaded (fallback)
|
||||
vm.LoadData.Execute(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
297
Marechai.App/Presentation/Views/ConsolesPage.xaml
Normal file
297
Marechai.App/Presentation/Views/ConsolesPage.xaml
Normal file
@@ -0,0 +1,297 @@
|
||||
<?xml version="1.0"
|
||||
encoding="utf-8"?>
|
||||
|
||||
<Page x:Class="Marechai.App.Presentation.Views.ConsolesPage"
|
||||
x:Name="PageRoot"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:utu="using:Uno.Toolkit.UI"
|
||||
NavigationCacheMode="Required"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<Grid utu:SafeArea.Insets="VisibleBounds">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" /> <RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Header -->
|
||||
<utu:NavigationBar Grid.Row="0"
|
||||
Content="{Binding Path=Title}">
|
||||
<utu:NavigationBar.MainCommand>
|
||||
<AppBarButton Icon="Back"
|
||||
Label="Back"
|
||||
Command="{Binding GoBackCommand}"
|
||||
AutomationProperties.Name="Go back" />
|
||||
</utu:NavigationBar.MainCommand>
|
||||
</utu:NavigationBar>
|
||||
|
||||
<!-- Content -->
|
||||
<ScrollViewer Grid.Row="1">
|
||||
<StackPanel Padding="16"
|
||||
Spacing="24">
|
||||
|
||||
<!-- Console Count Display -->
|
||||
<StackPanel HorizontalAlignment="Center"
|
||||
Spacing="8">
|
||||
<TextBlock Text="{Binding ConsoleCountText}"
|
||||
TextAlignment="Center"
|
||||
FontSize="18"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
<TextBlock Text="{Binding ConsoleCount}"
|
||||
TextAlignment="Center"
|
||||
FontSize="48"
|
||||
FontWeight="Bold"
|
||||
Foreground="{ThemeResource SystemAccentColor}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Loading State -->
|
||||
<StackPanel Visibility="{Binding IsLoading}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Padding="32"
|
||||
Spacing="16">
|
||||
<ProgressRing IsActive="True"
|
||||
IsIndeterminate="True"
|
||||
Height="48"
|
||||
Width="48" />
|
||||
<TextBlock Text="Loading..."
|
||||
TextAlignment="Center"
|
||||
FontSize="14" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Error State -->
|
||||
<StackPanel Visibility="{Binding HasError}"
|
||||
Padding="16"
|
||||
Background="{ThemeResource SystemErrorBackgroundColor}"
|
||||
CornerRadius="8"
|
||||
Spacing="8">
|
||||
<TextBlock Text="Error"
|
||||
FontWeight="Bold"
|
||||
Foreground="{ThemeResource SystemErrorTextForegroundColor}" />
|
||||
<TextBlock Text="{Binding ErrorMessage}"
|
||||
Foreground="{ThemeResource SystemErrorTextForegroundColor}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Main Content (visible when loaded and no error) -->
|
||||
<StackPanel Visibility="{Binding IsDataLoaded}"
|
||||
Spacing="24">
|
||||
|
||||
<!-- Letters Grid Section -->
|
||||
<StackPanel Spacing="12">
|
||||
<TextBlock Text="Browse by Letter"
|
||||
FontSize="16"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
<ItemsRepeater ItemsSource="{Binding LettersList}"
|
||||
Layout="{StaticResource LettersGridLayout}">
|
||||
<ItemsRepeater.ItemTemplate></ItemsRepeater.ItemTemplate>
|
||||
</ItemsRepeater>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Years Grid Section -->
|
||||
<StackPanel Spacing="12">
|
||||
<TextBlock Text="{Binding YearsGridTitle}"
|
||||
FontSize="16"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
<ItemsRepeater ItemsSource="{Binding YearsList}"
|
||||
Layout="{StaticResource YearsGridLayout}">
|
||||
<ItemsRepeater.ItemTemplate></ItemsRepeater.ItemTemplate>
|
||||
</ItemsRepeater>
|
||||
</StackPanel>
|
||||
|
||||
<!-- All Consoles and Search Section -->
|
||||
<StackPanel Spacing="12">
|
||||
<Button Content="All Consoles"
|
||||
Padding="16,12"
|
||||
HorizontalAlignment="Stretch"
|
||||
FontSize="16"
|
||||
FontWeight="SemiBold"
|
||||
Command="{Binding NavigateAllConsolesCommand}"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
|
||||
<!-- Search Field (placeholder for future implementation) -->
|
||||
<TextBox PlaceholderText="Search consoles..."
|
||||
Padding="12"
|
||||
IsEnabled="False"
|
||||
Opacity="0.5" />
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
</Grid>
|
||||
|
||||
<Page.Resources>
|
||||
<!-- Keyboard Key Button Style (revised: more padding, simplified borders to avoid clipping, darker scheme) -->
|
||||
<Style x:Key="KeyboardKeyButtonStyle"
|
||||
TargetType="Button">
|
||||
<!-- Base appearance -->
|
||||
<Setter Property="Foreground"
|
||||
Value="#1A1A1A" />
|
||||
<Setter Property="Background">
|
||||
<Setter.Value>
|
||||
<LinearGradientBrush StartPoint="0,0"
|
||||
EndPoint="0,1">
|
||||
<GradientStop Color="#D6D6D6"
|
||||
Offset="0" />
|
||||
<GradientStop Color="#C2C2C2"
|
||||
Offset="0.55" />
|
||||
<GradientStop Color="#B0B0B0"
|
||||
Offset="1" />
|
||||
</LinearGradientBrush>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Property="BorderBrush"
|
||||
Value="#7A7A7A" />
|
||||
<Setter Property="BorderThickness"
|
||||
Value="1" />
|
||||
<Setter Property="CornerRadius"
|
||||
Value="6" />
|
||||
<Setter Property="Padding"
|
||||
Value="14,12" /> <!-- Increased vertical padding to prevent cutoff -->
|
||||
<Setter Property="Margin"
|
||||
Value="4" />
|
||||
<Setter Property="FontFamily"
|
||||
Value="Segoe UI" />
|
||||
<Setter Property="FontWeight"
|
||||
Value="SemiBold" />
|
||||
<Setter Property="FontSize"
|
||||
Value="15" />
|
||||
<Setter Property="HorizontalAlignment"
|
||||
Value="Stretch" />
|
||||
<Setter Property="VerticalAlignment"
|
||||
Value="Stretch" />
|
||||
<Setter Property="HorizontalContentAlignment"
|
||||
Value="Center" />
|
||||
<Setter Property="VerticalContentAlignment"
|
||||
Value="Center" />
|
||||
<Setter Property="MinWidth"
|
||||
Value="52" />
|
||||
<Setter Property="MinHeight"
|
||||
Value="52" /> <!-- Larger min height avoids clipping ascenders/descenders -->
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Grid>
|
||||
<!-- Shadow (simple) -->
|
||||
<Border x:Name="Shadow"
|
||||
CornerRadius="6"
|
||||
Background="#33000000"
|
||||
Margin="2,4,4,2" />
|
||||
<!-- Key surface -->
|
||||
<Border x:Name="KeyBorder"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="{TemplateBinding CornerRadius}">
|
||||
<!-- Inner highlight & content -->
|
||||
<Grid>
|
||||
<Border CornerRadius="{TemplateBinding CornerRadius}"
|
||||
BorderBrush="#60FFFFFF"
|
||||
BorderThickness="1,1,0,0" />
|
||||
<Border CornerRadius="{TemplateBinding CornerRadius}"
|
||||
BorderBrush="#30000000"
|
||||
BorderThickness="0,0,1,1" />
|
||||
<ContentPresenter x:Name="ContentPresenter"
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Margin="0"
|
||||
TextWrapping="NoWrap" />
|
||||
</Grid>
|
||||
</Border>
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="PointerOver">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="KeyBorder.Background">
|
||||
<Setter.Value>
|
||||
<LinearGradientBrush StartPoint="0,0"
|
||||
EndPoint="0,1">
|
||||
<GradientStop Color="#E0E0E0"
|
||||
Offset="0" />
|
||||
<GradientStop Color="#CFCFCF"
|
||||
Offset="0.55" />
|
||||
<GradientStop Color="#BDBDBD"
|
||||
Offset="1" />
|
||||
</LinearGradientBrush>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Target="KeyBorder.BorderBrush"
|
||||
Value="#5F5F5F" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Pressed">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="KeyBorder.Background">
|
||||
<Setter.Value>
|
||||
<LinearGradientBrush StartPoint="0,0"
|
||||
EndPoint="0,1">
|
||||
<GradientStop Color="#9C9C9C"
|
||||
Offset="0" />
|
||||
<GradientStop Color="#A8A8A8"
|
||||
Offset="0.55" />
|
||||
<GradientStop Color="#B4B4B4"
|
||||
Offset="1" />
|
||||
</LinearGradientBrush>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Target="KeyBorder.BorderBrush"
|
||||
Value="#4A4A4A" />
|
||||
<Setter Target="KeyBorder.RenderTransform">
|
||||
<Setter.Value>
|
||||
<TranslateTransform Y="2" />
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
<Setter Target="Shadow.Opacity"
|
||||
Value="0.15" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="KeyBorder.Opacity"
|
||||
Value="0.45" />
|
||||
<Setter Target="ContentPresenter.Foreground"
|
||||
Value="#777777" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
<VisualStateGroup x:Name="FocusStates">
|
||||
<VisualState x:Name="Focused">
|
||||
<VisualState.Setters>
|
||||
<Setter Target="KeyBorder.BorderBrush"
|
||||
Value="#3A7AFE" />
|
||||
<Setter Target="KeyBorder.BorderThickness"
|
||||
Value="2" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Unfocused" />
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- Responsive Grid Layouts -->
|
||||
<UniformGridLayout x:Key="LettersGridLayout"
|
||||
ItemsStretch="Fill"
|
||||
MinItemWidth="44"
|
||||
MinItemHeight="44"
|
||||
MaximumRowsOrColumns="13" />
|
||||
|
||||
<UniformGridLayout x:Key="YearsGridLayout"
|
||||
ItemsStretch="Fill"
|
||||
MinItemWidth="54"
|
||||
MinItemHeight="44"
|
||||
MaximumRowsOrColumns="10" />
|
||||
|
||||
</Page.Resources>
|
||||
|
||||
</Page>
|
||||
44
Marechai.App/Presentation/Views/ConsolesPage.xaml.cs
Normal file
44
Marechai.App/Presentation/Views/ConsolesPage.xaml.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using Marechai.App.Presentation.ViewModels;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
|
||||
namespace Marechai.App.Presentation.Views;
|
||||
|
||||
public sealed partial class ConsolesPage : Page
|
||||
{
|
||||
public ConsolesPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContextChanged += ConsolesPage_DataContextChanged;
|
||||
Loaded += ConsolesPage_Loaded;
|
||||
}
|
||||
|
||||
private void ConsolesPage_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if(DataContext is not ConsolesViewModel viewModel) return;
|
||||
|
||||
// Trigger data loading
|
||||
_ = viewModel.LoadData.ExecuteAsync(null);
|
||||
}
|
||||
|
||||
private void ConsolesPage_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
|
||||
{
|
||||
if(args.NewValue is ConsolesViewModel viewModel)
|
||||
{
|
||||
// Trigger data loading when data context changes
|
||||
_ = viewModel.LoadData.ExecuteAsync(null);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
if(DataContext is ConsolesViewModel viewModel)
|
||||
{
|
||||
// Trigger data loading when navigating to the page
|
||||
_ = viewModel.LoadData.ExecuteAsync(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
385
Marechai.App/Presentation/Views/MachineViewPage.xaml
Normal file
385
Marechai.App/Presentation/Views/MachineViewPage.xaml
Normal file
@@ -0,0 +1,385 @@
|
||||
<?xml version="1.0"
|
||||
encoding="utf-8"?>
|
||||
|
||||
<Page x:Class="Marechai.App.Presentation.Views.MachineViewPage"
|
||||
x:Name="PageRoot"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
NavigationCacheMode="Disabled"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<Grid RowSpacing="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" /> <RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Header with Back Button -->
|
||||
<Grid Grid.Row="0"
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
|
||||
BorderThickness="0,0,0,1"
|
||||
Padding="12,12,16,12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Back Button -->
|
||||
<Button Grid.Column="0"
|
||||
Command="{Binding GoBackCommand}"
|
||||
Style="{ThemeResource AlternateButtonStyle}"
|
||||
ToolTipService.ToolTip="Go back"
|
||||
Padding="8"
|
||||
MinWidth="44"
|
||||
MinHeight="44"
|
||||
VerticalAlignment="Center">
|
||||
<FontIcon Glyph=""
|
||||
FontSize="16" />
|
||||
</Button>
|
||||
|
||||
<!-- Title Section -->
|
||||
<StackPanel Grid.Column="1"
|
||||
Margin="12,0,0,0"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding MachineName}"
|
||||
FontSize="20"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextControlForeground}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
<TextBlock Text="{Binding CompanyName}"
|
||||
FontSize="12"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}"
|
||||
Margin="0,4,0,0" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Main Content -->
|
||||
<Grid Grid.Row="1">
|
||||
|
||||
<!-- Loading State -->
|
||||
<StackPanel Visibility="{Binding IsLoading}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Padding="32"
|
||||
Spacing="16">
|
||||
<ProgressRing IsActive="True"
|
||||
IsIndeterminate="True"
|
||||
Height="64"
|
||||
Width="64"
|
||||
Foreground="{ThemeResource SystemAccentColor}" />
|
||||
<TextBlock Text="Loading machine details..."
|
||||
FontSize="14"
|
||||
TextAlignment="Center"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Error State -->
|
||||
<StackPanel Visibility="{Binding HasError}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Padding="24"
|
||||
Spacing="16"
|
||||
MaxWidth="400">
|
||||
<InfoBar IsOpen="True"
|
||||
Severity="Error"
|
||||
Title="Unable to Load Machine"
|
||||
Message="{Binding ErrorMessage}"
|
||||
IsClosable="False" />
|
||||
<Button Content="Retry"
|
||||
Command="{Binding LoadData}"
|
||||
HorizontalAlignment="Center"
|
||||
Style="{ThemeResource AccentButtonStyle}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Machine Details -->
|
||||
<ScrollViewer Visibility="{Binding IsDataLoaded}"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Disabled">
|
||||
<StackPanel Padding="16"
|
||||
Spacing="24">
|
||||
|
||||
<!-- Prototype Badge -->
|
||||
<StackPanel Visibility="{Binding IsPrototype}">
|
||||
<Border Background="{ThemeResource WarningFillColorTertiaryBrush}"
|
||||
BorderBrush="{ThemeResource WarningBorderColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="16,12">
|
||||
<TextBlock Text="PROTOTYPE"
|
||||
FontSize="14"
|
||||
FontWeight="Bold"
|
||||
Foreground="{ThemeResource WarningForegroundColorDefaultBrush}"
|
||||
TextAlignment="Center" />
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Introduction Date -->
|
||||
<StackPanel Visibility="{Binding ShowIntroductionDate}"
|
||||
Spacing="8">
|
||||
<TextBlock Text="Introduction Date"
|
||||
FontSize="12"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="16,12">
|
||||
<TextBlock Text="{Binding IntroductionDateDisplay}"
|
||||
FontSize="16"
|
||||
Foreground="{ThemeResource TextControlForeground}" />
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Family and Model -->
|
||||
<Grid Visibility="{Binding ShowFamilyOrModel}"
|
||||
ColumnSpacing="16">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Family -->
|
||||
<StackPanel Grid.Column="0"
|
||||
Visibility="{Binding ShowFamily}"
|
||||
Spacing="8">
|
||||
<TextBlock Text="Family"
|
||||
FontSize="12"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="16,12">
|
||||
<TextBlock Text="{Binding FamilyName}"
|
||||
FontSize="14"
|
||||
Foreground="{ThemeResource TextControlForeground}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Model -->
|
||||
<StackPanel Grid.Column="1"
|
||||
Visibility="{Binding ShowModel}"
|
||||
Spacing="8">
|
||||
<TextBlock Text="Model"
|
||||
FontSize="12"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="16,12">
|
||||
<TextBlock Text="{Binding ModelName}"
|
||||
FontSize="14"
|
||||
Foreground="{ThemeResource TextControlForeground}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Processors Section -->
|
||||
<StackPanel Visibility="{Binding ShowProcessors}"
|
||||
Spacing="12">
|
||||
<TextBlock Text="Processors"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextControlForeground}" />
|
||||
<ItemsControl ItemsSource="{Binding Processors}"
|
||||
HorizontalAlignment="Stretch">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Vertical"
|
||||
Spacing="8" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="16,12">
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Text="{Binding DisplayName}"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextControlForeground}" />
|
||||
<TextBlock Text="{Binding Manufacturer}"
|
||||
FontSize="12"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
<TextBlock Text="{Binding DetailsText}"
|
||||
FontSize="12"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}"
|
||||
TextWrapping="Wrap"
|
||||
Visibility="{Binding HasDetails}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Memory Section -->
|
||||
<StackPanel Visibility="{Binding ShowMemory}"
|
||||
Spacing="12">
|
||||
<TextBlock Text="Memory"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextControlForeground}" />
|
||||
<ItemsControl ItemsSource="{Binding Memory}"
|
||||
HorizontalAlignment="Stretch">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Vertical"
|
||||
Spacing="8" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="16,12">
|
||||
<StackPanel Spacing="6">
|
||||
<TextBlock Text="{Binding SizeDisplay}"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextControlForeground}" />
|
||||
<TextBlock Text="{Binding TypeDisplay}"
|
||||
FontSize="12"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
|
||||
<!-- GPUs Section -->
|
||||
<StackPanel Visibility="{Binding ShowGpus}"
|
||||
Spacing="12">
|
||||
<TextBlock Text="Graphics Processing Units"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextControlForeground}" />
|
||||
<ItemsControl ItemsSource="{Binding Gpus}"
|
||||
HorizontalAlignment="Stretch">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Vertical"
|
||||
Spacing="8" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="16,12">
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Text="{Binding DisplayName}"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextControlForeground}" />
|
||||
<TextBlock Text="{Binding Manufacturer}"
|
||||
FontSize="12"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}"
|
||||
Visibility="{Binding HasManufacturer}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Sound Synthesizers Section -->
|
||||
<StackPanel Visibility="{Binding ShowSoundSynthesizers}"
|
||||
Spacing="12">
|
||||
<TextBlock Text="Sound Synthesizers"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextControlForeground}" />
|
||||
<ItemsControl ItemsSource="{Binding SoundSynthesizers}"
|
||||
HorizontalAlignment="Stretch">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Vertical"
|
||||
Spacing="8" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="16,12">
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Text="{Binding DisplayName}"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextControlForeground}" />
|
||||
<TextBlock Text="{Binding DetailsText}"
|
||||
FontSize="12"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}"
|
||||
TextWrapping="Wrap"
|
||||
Visibility="{Binding HasDetails}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Storage Section -->
|
||||
<StackPanel Visibility="{Binding ShowStorage}"
|
||||
Spacing="12">
|
||||
<TextBlock Text="Storage"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextControlForeground}" />
|
||||
<ItemsControl ItemsSource="{Binding Storage}"
|
||||
HorizontalAlignment="Stretch">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Vertical"
|
||||
Spacing="8" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="16,12">
|
||||
<StackPanel Spacing="6">
|
||||
<TextBlock Text="{Binding DisplayText}"
|
||||
FontSize="13"
|
||||
Foreground="{ThemeResource TextControlForeground}"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock Text="{Binding TypeNote}"
|
||||
FontSize="11"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Bottom Spacing -->
|
||||
<Border Height="24" />
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
</Page>
|
||||
82
Marechai.App/Presentation/Views/MachineViewPage.xaml.cs
Normal file
82
Marechai.App/Presentation/Views/MachineViewPage.xaml.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
/******************************************************************************
|
||||
// MARECHAI: Master repository of computing history artifacts information
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2003-2026 Natalia Portillo
|
||||
*******************************************************************************/
|
||||
|
||||
#nullable enable
|
||||
|
||||
using Marechai.App.Presentation.Models;
|
||||
using Marechai.App.Presentation.ViewModels;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
|
||||
namespace Marechai.App.Presentation.Views;
|
||||
|
||||
public sealed partial class MachineViewPage : Page
|
||||
{
|
||||
private object? _navigationSource;
|
||||
private int? _pendingMachineId;
|
||||
|
||||
public MachineViewPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContextChanged += MachineViewPage_DataContextChanged;
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
int? machineId = null;
|
||||
|
||||
// Handle both int and MachineViewNavigationParameter
|
||||
if(e.Parameter is int intId)
|
||||
machineId = intId;
|
||||
else if(e.Parameter is MachineViewNavigationParameter navParam)
|
||||
{
|
||||
machineId = navParam.MachineId;
|
||||
_navigationSource = navParam.NavigationSource;
|
||||
}
|
||||
|
||||
if(machineId.HasValue)
|
||||
{
|
||||
_pendingMachineId = machineId;
|
||||
|
||||
if(DataContext is MachineViewViewModel viewModel)
|
||||
{
|
||||
viewModel.SetNavigationSource(_navigationSource);
|
||||
_ = viewModel.LoadMachineAsync(machineId.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void MachineViewPage_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
|
||||
{
|
||||
if(DataContext is MachineViewViewModel viewModel && _pendingMachineId.HasValue)
|
||||
{
|
||||
viewModel.SetNavigationSource(_navigationSource);
|
||||
_ = viewModel.LoadMachineAsync(_pendingMachineId.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
55
Marechai.App/Presentation/Views/MainPage.xaml
Normal file
55
Marechai.App/Presentation/Views/MainPage.xaml
Normal file
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0"
|
||||
encoding="utf-8"?>
|
||||
|
||||
<Page x:Class="Marechai.App.Presentation.Views.MainPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:utu="using:Uno.Toolkit.UI"
|
||||
xmlns:uen="using:Uno.Extensions.Navigation.UI"
|
||||
xmlns:components="clr-namespace:Marechai.App.Presentation.Components"
|
||||
NavigationCacheMode="Required"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<Grid utu:SafeArea.Insets="VisibleBounds">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition x:Name="SidebarColumn"
|
||||
Width="280" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" /> <RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<Grid x:Name="SidebarWrapper"
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="0"
|
||||
Width="280"
|
||||
HorizontalAlignment="Left">
|
||||
<components:Sidebar x:Name="SidebarPanel"
|
||||
DataContext="{Binding}"
|
||||
VerticalAlignment="Stretch" />
|
||||
</Grid>
|
||||
|
||||
<!-- Header -->
|
||||
<utu:NavigationBar Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Content="{Binding Title}">
|
||||
<utu:NavigationBar.MainCommand>
|
||||
<AppBarButton Icon="GlobalNavigationButton"
|
||||
Command="{Binding ToggleSidebarCommand}"
|
||||
Label="Toggle Sidebar"
|
||||
AutomationProperties.Name="Toggle sidebar visibility" />
|
||||
</utu:NavigationBar.MainCommand>
|
||||
</utu:NavigationBar>
|
||||
|
||||
<!-- Content Region for Navigation -->
|
||||
<ContentControl Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
uen:Region.Attached="True"
|
||||
uen:Region.Name="Main" />
|
||||
</Grid>
|
||||
</Page>
|
||||
98
Marechai.App/Presentation/Views/MainPage.xaml.cs
Normal file
98
Marechai.App/Presentation/Views/MainPage.xaml.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using Marechai.App.Presentation.ViewModels;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Marechai.App.Presentation.Views;
|
||||
|
||||
public sealed partial class MainPage : Page
|
||||
{
|
||||
private PropertyChangedEventHandler _sidebarPropertyChangedHandler;
|
||||
|
||||
public MainPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContextChanged += MainPage_DataContextChanged;
|
||||
Loaded += MainPage_Loaded;
|
||||
}
|
||||
|
||||
private void MainPage_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if(DataContext is not MainViewModel viewModel) return;
|
||||
|
||||
SidebarWrapper.Width = viewModel.IsSidebarOpen ? 280 : 60;
|
||||
|
||||
if(_sidebarPropertyChangedHandler != null) return;
|
||||
|
||||
_sidebarPropertyChangedHandler = (_, propArgs) =>
|
||||
{
|
||||
if(propArgs.PropertyName != nameof(MainViewModel.IsSidebarOpen)) return;
|
||||
|
||||
AnimateSidebarWidth(((MainViewModel)DataContext).IsSidebarOpen);
|
||||
};
|
||||
|
||||
((INotifyPropertyChanged)viewModel).PropertyChanged += _sidebarPropertyChangedHandler;
|
||||
}
|
||||
|
||||
void AnimateSidebarWidth(bool isOpen)
|
||||
{
|
||||
double start = SidebarColumn.Width.Value;
|
||||
double end = isOpen ? 280 : 60;
|
||||
|
||||
if(Math.Abs(start - end) < 0.1) return;
|
||||
|
||||
// If expanding, show content immediately
|
||||
if(isOpen && DataContext is MainViewModel vm) vm.SidebarContentVisible = true;
|
||||
|
||||
const int durationMs = 250;
|
||||
const int fps = 60;
|
||||
var steps = (int)(durationMs / (1000.0 / fps));
|
||||
var currentStep = 0;
|
||||
|
||||
var timer = new DispatcherTimer
|
||||
{
|
||||
Interval = TimeSpan.FromMilliseconds(1000.0 / fps)
|
||||
};
|
||||
|
||||
timer.Tick += (_, _) =>
|
||||
{
|
||||
currentStep++;
|
||||
double t = (double)currentStep / steps;
|
||||
|
||||
// Ease in-out cubic
|
||||
double eased = t < 0.5 ? 4 * t * t * t : 1 - Math.Pow(-2 * t + 2, 3) / 2;
|
||||
double value = start + (end - start) * eased;
|
||||
SidebarColumn.Width = new GridLength(value, GridUnitType.Pixel);
|
||||
SidebarWrapper.Width = value;
|
||||
|
||||
if(currentStep >= steps)
|
||||
{
|
||||
SidebarColumn.Width = new GridLength(end, GridUnitType.Pixel);
|
||||
SidebarWrapper.Width = end;
|
||||
timer.Stop();
|
||||
|
||||
// After collapse animation completes, hide sidebar content
|
||||
if(!isOpen && DataContext is MainViewModel vm) vm.SidebarContentVisible = false;
|
||||
}
|
||||
};
|
||||
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
private void MainPage_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
|
||||
{
|
||||
if(args.NewValue is MainViewModel vm && _sidebarPropertyChangedHandler == null)
|
||||
{
|
||||
SidebarWrapper.Width = vm.IsSidebarOpen ? 280 : 60;
|
||||
|
||||
_sidebarPropertyChangedHandler = (_, propArgs) =>
|
||||
{
|
||||
if(propArgs.PropertyName != nameof(MainViewModel.IsSidebarOpen)) return;
|
||||
AnimateSidebarWidth(vm.IsSidebarOpen);
|
||||
};
|
||||
|
||||
((INotifyPropertyChanged)vm).PropertyChanged += _sidebarPropertyChangedHandler;
|
||||
}
|
||||
}
|
||||
}
|
||||
130
Marechai.App/Presentation/Views/NewsPage.xaml
Normal file
130
Marechai.App/Presentation/Views/NewsPage.xaml
Normal file
@@ -0,0 +1,130 @@
|
||||
<?xml version="1.0"
|
||||
encoding="utf-8"?>
|
||||
|
||||
<Page x:Class="Marechai.App.Presentation.Views.NewsPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:utu="using:Uno.Toolkit.UI"
|
||||
NavigationCacheMode="Required"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<Grid utu:SafeArea.Insets="VisibleBounds">
|
||||
<RefreshContainer x:Name="RefreshContainer"
|
||||
RefreshRequested="RefreshContainer_RefreshRequested">
|
||||
<ScrollViewer>
|
||||
<Grid Padding="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- News Title Section -->
|
||||
<StackPanel Grid.Row="0"
|
||||
Margin="0,0,0,16">
|
||||
<TextBlock Text="Latest News"
|
||||
FontSize="32"
|
||||
FontWeight="Bold"
|
||||
Foreground="{ThemeResource SystemAccentColor}"
|
||||
Margin="0,0,0,8" />
|
||||
<TextBlock Text="Stay updated with the latest additions to the database"
|
||||
FontSize="14"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Loading State -->
|
||||
<StackPanel Grid.Row="2"
|
||||
Visibility="{Binding IsLoading}"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Padding="32"
|
||||
Spacing="16">
|
||||
<ProgressRing IsActive="True"
|
||||
IsIndeterminate="True"
|
||||
Height="48"
|
||||
Width="48"
|
||||
Foreground="{ThemeResource SystemAccentColor}" />
|
||||
<TextBlock Text="Loading latest news..."
|
||||
FontSize="16"
|
||||
TextAlignment="Center"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Error State -->
|
||||
<StackPanel Grid.Row="2"
|
||||
Visibility="{Binding HasError}"
|
||||
VerticalAlignment="Center"
|
||||
Spacing="16"
|
||||
Padding="32">
|
||||
<InfoBar IsOpen="True"
|
||||
Severity="Error"
|
||||
Title="Unable to Load News"
|
||||
Message="{Binding ErrorMessage}"
|
||||
IsClosable="False" />
|
||||
<Button Content="Retry"
|
||||
Command="{Binding LoadNews}"
|
||||
HorizontalAlignment="Center"
|
||||
Style="{ThemeResource AccentButtonStyle}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- News Feed -->
|
||||
<ItemsControl Grid.Row="2"
|
||||
ItemsSource="{Binding NewsList}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Button Margin="0,0,0,12"
|
||||
Padding="0"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
Command="{Binding NavigateToItemCommand}"
|
||||
CommandParameter="{Binding News}">
|
||||
<Border CornerRadius="8"
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
Padding="16">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Date -->
|
||||
<TextBlock Grid.Row="0"
|
||||
Text="{Binding News.Timestamp}"
|
||||
FontSize="12"
|
||||
Foreground="{ThemeResource SystemBaseMediumColor}"
|
||||
Margin="0,0,0,12" />
|
||||
|
||||
<!-- News Title/Text (Localized) -->
|
||||
<TextBlock Grid.Row="1"
|
||||
Text="{Binding DisplayText}"
|
||||
FontSize="16"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource SystemBaseHighColor}"
|
||||
TextWrapping="Wrap"
|
||||
Margin="0,0,0,12" />
|
||||
|
||||
<!-- Item Name -->
|
||||
<TextBlock Grid.Row="2"
|
||||
Text="{Binding News.ItemName}"
|
||||
FontSize="14"
|
||||
Foreground="{ThemeResource SystemAccentColor}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Vertical"
|
||||
Spacing="0" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
</ItemsControl>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</RefreshContainer>
|
||||
</Grid>
|
||||
</Page>
|
||||
73
Marechai.App/Presentation/Views/NewsPage.xaml.cs
Normal file
73
Marechai.App/Presentation/Views/NewsPage.xaml.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using Windows.Foundation;
|
||||
using Marechai.App.Presentation.ViewModels;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
|
||||
namespace Marechai.App.Presentation.Views;
|
||||
|
||||
public sealed partial class NewsPage : Page
|
||||
{
|
||||
private bool _initialNewsLoaded;
|
||||
|
||||
public NewsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContextChanged += NewsPage_DataContextChanged;
|
||||
Loaded += NewsPage_Loaded;
|
||||
}
|
||||
|
||||
private void NewsPage_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if(_initialNewsLoaded) return;
|
||||
|
||||
if(DataContext is NewsViewModel viewModel)
|
||||
{
|
||||
_initialNewsLoaded = true;
|
||||
_ = viewModel.LoadNews.ExecuteAsync(null);
|
||||
DataContextChanged -= NewsPage_DataContextChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void NewsPage_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
|
||||
{
|
||||
if(_initialNewsLoaded) return;
|
||||
|
||||
if(args.NewValue is NewsViewModel viewModel)
|
||||
{
|
||||
_initialNewsLoaded = true;
|
||||
_ = viewModel.LoadNews.ExecuteAsync(null);
|
||||
DataContextChanged -= NewsPage_DataContextChanged;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
if(_initialNewsLoaded) return;
|
||||
|
||||
if(DataContext is NewsViewModel viewModel)
|
||||
{
|
||||
_initialNewsLoaded = true;
|
||||
_ = viewModel.LoadNews.ExecuteAsync(null);
|
||||
DataContextChanged -= NewsPage_DataContextChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private async void RefreshContainer_RefreshRequested(RefreshContainer sender, RefreshRequestedEventArgs args)
|
||||
{
|
||||
// Handle pull-to-refresh
|
||||
using Deferral deferral = args.GetDeferral();
|
||||
|
||||
try
|
||||
{
|
||||
if(DataContext is NewsViewModel viewModel) await viewModel.LoadNews.ExecuteAsync(null);
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
// Swallow to avoid process crash; NewsViewModel already logs errors.
|
||||
}
|
||||
}
|
||||
}
|
||||
22
Marechai.App/Presentation/Views/SecondPage.xaml
Normal file
22
Marechai.App/Presentation/Views/SecondPage.xaml
Normal file
@@ -0,0 +1,22 @@
|
||||
<Page x:Class="Marechai.App.Presentation.Views.SecondPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:utu="using:Uno.Toolkit.UI"
|
||||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
|
||||
<Grid utu:SafeArea.Insets="VisibleBounds">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" /> <RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<utu:NavigationBar Content="Second Page" />
|
||||
<StackPanel Grid.Row="1"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding Entity.Name}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Margin="8" />
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
</Page>
|
||||
11
Marechai.App/Presentation/Views/SecondPage.xaml.cs
Normal file
11
Marechai.App/Presentation/Views/SecondPage.xaml.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Marechai.App.Presentation.Views;
|
||||
|
||||
public sealed partial class SecondPage : Page
|
||||
{
|
||||
public SecondPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
34
Marechai.App/Presentation/Views/Shell.xaml
Normal file
34
Marechai.App/Presentation/Views/Shell.xaml
Normal file
@@ -0,0 +1,34 @@
|
||||
<UserControl x:Class="Marechai.App.Presentation.Views.Shell"
|
||||
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:utu="using:Uno.Toolkit.UI"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="400">
|
||||
<Border Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<utu:ExtendedSplashScreen x:Name="Splash"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch">
|
||||
<utu:ExtendedSplashScreen.LoadingContentTemplate>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="2*" /> <RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ProgressRing IsActive="True"
|
||||
Grid.Row="1"
|
||||
VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Height="100"
|
||||
Width="100" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</utu:ExtendedSplashScreen.LoadingContentTemplate>
|
||||
</utu:ExtendedSplashScreen>
|
||||
</Border>
|
||||
</UserControl>
|
||||
14
Marechai.App/Presentation/Views/Shell.xaml.cs
Normal file
14
Marechai.App/Presentation/Views/Shell.xaml.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Uno.Extensions.Hosting;
|
||||
|
||||
namespace Marechai.App.Presentation.Views;
|
||||
|
||||
public sealed partial class Shell : UserControl, IContentControlProvider
|
||||
{
|
||||
public Shell()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public ContentControl ContentControl => Splash;
|
||||
}
|
||||
64
Marechai.App/Services/Caching/FlagCache.cs
Normal file
64
Marechai.App/Services/Caching/FlagCache.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Marechai.App.Services.Caching;
|
||||
|
||||
public sealed class FlagCache
|
||||
{
|
||||
readonly IConfiguration _configuration;
|
||||
StorageFolder _flagsFolder;
|
||||
|
||||
public FlagCache(IConfiguration configuration)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_ = EnsureFolderExistAsync();
|
||||
}
|
||||
|
||||
async Task EnsureFolderExistAsync()
|
||||
{
|
||||
StorageFolder localFolder = ApplicationData.Current.LocalCacheFolder;
|
||||
_flagsFolder = await localFolder.CreateFolderAsync("flags", CreationCollisionOption.OpenIfExists);
|
||||
}
|
||||
|
||||
public async Task<Stream> GetFlagAsync(short countryCode)
|
||||
{
|
||||
var filename = $"{countryCode:D3}.svg";
|
||||
|
||||
Stream retStream;
|
||||
|
||||
if(await _flagsFolder.TryGetItemAsync(filename) is StorageFile file)
|
||||
{
|
||||
retStream = await file.OpenStreamForReadAsync();
|
||||
|
||||
return retStream;
|
||||
}
|
||||
|
||||
await CacheFlagAsync(countryCode);
|
||||
|
||||
file = await _flagsFolder.GetFileAsync(filename);
|
||||
|
||||
retStream = await file.OpenStreamForReadAsync();
|
||||
|
||||
return retStream;
|
||||
}
|
||||
|
||||
async Task CacheFlagAsync(short countryCode)
|
||||
{
|
||||
var filename = $"{countryCode:D3}.svg";
|
||||
string baseUrl = _configuration.GetSection("ApiClient:Url").Value;
|
||||
string flagUrl = baseUrl + $"/assets/flags/countries/{filename}";
|
||||
using var httpClient = new HttpClient();
|
||||
using HttpResponseMessage response = await httpClient.GetAsync(flagUrl);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
using Stream stream = await response.Content.ReadAsStreamAsync();
|
||||
StorageFile file = await _flagsFolder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);
|
||||
|
||||
using Stream fileStream = await file.OpenStreamForWriteAsync();
|
||||
await stream.CopyToAsync(fileStream);
|
||||
}
|
||||
}
|
||||
64
Marechai.App/Services/Caching/LogoCache.cs
Normal file
64
Marechai.App/Services/Caching/LogoCache.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Marechai.App.Services.Caching;
|
||||
|
||||
public sealed class CompanyLogoCache
|
||||
{
|
||||
readonly IConfiguration _configuration;
|
||||
StorageFolder _flagsFolder;
|
||||
|
||||
public CompanyLogoCache(IConfiguration configuration)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_ = EnsureFolderExistAsync();
|
||||
}
|
||||
|
||||
async Task EnsureFolderExistAsync()
|
||||
{
|
||||
StorageFolder localFolder = ApplicationData.Current.LocalCacheFolder;
|
||||
_flagsFolder = await localFolder.CreateFolderAsync("logos", CreationCollisionOption.OpenIfExists);
|
||||
}
|
||||
|
||||
public async Task<Stream> GetFlagAsync(Guid companyLogoId)
|
||||
{
|
||||
var filename = $"{companyLogoId}.svg";
|
||||
|
||||
Stream retStream;
|
||||
|
||||
if(await _flagsFolder.TryGetItemAsync(filename) is StorageFile file)
|
||||
{
|
||||
retStream = await file.OpenStreamForReadAsync();
|
||||
|
||||
return retStream;
|
||||
}
|
||||
|
||||
await CacheFlagAsync(companyLogoId);
|
||||
|
||||
file = await _flagsFolder.GetFileAsync(filename);
|
||||
|
||||
retStream = await file.OpenStreamForReadAsync();
|
||||
|
||||
return retStream;
|
||||
}
|
||||
|
||||
async Task CacheFlagAsync(Guid companyLogoId)
|
||||
{
|
||||
var filename = $"{companyLogoId}.svg";
|
||||
string baseUrl = _configuration.GetSection("ApiClient:Url").Value;
|
||||
string flagUrl = baseUrl + $"/assets/logos/{filename}";
|
||||
using var httpClient = new HttpClient();
|
||||
using HttpResponseMessage response = await httpClient.GetAsync(flagUrl);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
using Stream stream = await response.Content.ReadAsStreamAsync();
|
||||
StorageFile file = await _flagsFolder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);
|
||||
|
||||
using Stream fileStream = await file.OpenStreamForWriteAsync();
|
||||
await stream.CopyToAsync(fileStream);
|
||||
}
|
||||
}
|
||||
288
Marechai.App/Services/Client/ApiClient.cs
Normal file
288
Marechai.App/Services/Client/ApiClient.cs
Normal file
@@ -0,0 +1,288 @@
|
||||
// <auto-generated/>
|
||||
#pragma warning disable CS0618
|
||||
using Marechai.App.Auth;
|
||||
using Marechai.App.Books;
|
||||
using Marechai.App.BrowserTests;
|
||||
using Marechai.App.Companies;
|
||||
using Marechai.App.Computers;
|
||||
using Marechai.App.Consoles;
|
||||
using Marechai.App.Countries;
|
||||
using Marechai.App.Currencies;
|
||||
using Marechai.App.Documents;
|
||||
using Marechai.App.Dumps;
|
||||
using Marechai.App.Gpus;
|
||||
using Marechai.App.InstructionSetExtensions;
|
||||
using Marechai.App.InstructionSetExtensionsByProcessor;
|
||||
using Marechai.App.InstructionSets;
|
||||
using Marechai.App.Iso31661Numeric;
|
||||
using Marechai.App.Iso4217;
|
||||
using Marechai.App.Licenses;
|
||||
using Marechai.App.MachineFamilies;
|
||||
using Marechai.App.Machines;
|
||||
using Marechai.App.Magazines;
|
||||
using Marechai.App.MagazinesByMachine;
|
||||
using Marechai.App.MagazinesByMachineFamily;
|
||||
using Marechai.App.Medias;
|
||||
using Marechai.App.MemoriesByMachine;
|
||||
using Marechai.App.News;
|
||||
using Marechai.App.People;
|
||||
using Marechai.App.PeopleByBook;
|
||||
using Marechai.App.PeopleByDocument;
|
||||
using Marechai.App.PeopleByMagazine;
|
||||
using Marechai.App.Processor;
|
||||
using Marechai.App.Processors;
|
||||
using Marechai.App.ProcessorsByMachine;
|
||||
using Marechai.App.Resolutions;
|
||||
using Marechai.App.ResolutionsByGpu;
|
||||
using Marechai.App.ResolutionsByScreen;
|
||||
using Marechai.App.Screens;
|
||||
using Marechai.App.ScreensByMachine;
|
||||
using Marechai.App.Software;
|
||||
using Marechai.App.SoundSynths;
|
||||
using Marechai.App.SoundSynthsByMachine;
|
||||
using Marechai.App.StorageByMachine;
|
||||
using Microsoft.Kiota.Abstractions.Extensions;
|
||||
using Microsoft.Kiota.Abstractions;
|
||||
using Microsoft.Kiota.Serialization.Form;
|
||||
using Microsoft.Kiota.Serialization.Json;
|
||||
using Microsoft.Kiota.Serialization.Multipart;
|
||||
using Microsoft.Kiota.Serialization.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
namespace Marechai.App
|
||||
{
|
||||
/// <summary>
|
||||
/// The main entry point of the SDK, exposes the configuration and the fluent API.
|
||||
/// </summary>
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
public partial class ApiClient : BaseRequestBuilder
|
||||
{
|
||||
/// <summary>The auth property</summary>
|
||||
public global::Marechai.App.Auth.AuthRequestBuilder Auth
|
||||
{
|
||||
get => new global::Marechai.App.Auth.AuthRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The books property</summary>
|
||||
public global::Marechai.App.Books.BooksRequestBuilder Books
|
||||
{
|
||||
get => new global::Marechai.App.Books.BooksRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The browserTests property</summary>
|
||||
public global::Marechai.App.BrowserTests.BrowserTestsRequestBuilder BrowserTests
|
||||
{
|
||||
get => new global::Marechai.App.BrowserTests.BrowserTestsRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The companies property</summary>
|
||||
public global::Marechai.App.Companies.CompaniesRequestBuilder Companies
|
||||
{
|
||||
get => new global::Marechai.App.Companies.CompaniesRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The computers property</summary>
|
||||
public global::Marechai.App.Computers.ComputersRequestBuilder Computers
|
||||
{
|
||||
get => new global::Marechai.App.Computers.ComputersRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The consoles property</summary>
|
||||
public global::Marechai.App.Consoles.ConsolesRequestBuilder Consoles
|
||||
{
|
||||
get => new global::Marechai.App.Consoles.ConsolesRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The countries property</summary>
|
||||
public global::Marechai.App.Countries.CountriesRequestBuilder Countries
|
||||
{
|
||||
get => new global::Marechai.App.Countries.CountriesRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The currencies property</summary>
|
||||
public global::Marechai.App.Currencies.CurrenciesRequestBuilder Currencies
|
||||
{
|
||||
get => new global::Marechai.App.Currencies.CurrenciesRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The documents property</summary>
|
||||
public global::Marechai.App.Documents.DocumentsRequestBuilder Documents
|
||||
{
|
||||
get => new global::Marechai.App.Documents.DocumentsRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The dumps property</summary>
|
||||
public global::Marechai.App.Dumps.DumpsRequestBuilder Dumps
|
||||
{
|
||||
get => new global::Marechai.App.Dumps.DumpsRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The gpus property</summary>
|
||||
public global::Marechai.App.Gpus.GpusRequestBuilder Gpus
|
||||
{
|
||||
get => new global::Marechai.App.Gpus.GpusRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The instructionSetExtensions property</summary>
|
||||
public global::Marechai.App.InstructionSetExtensions.InstructionSetExtensionsRequestBuilder InstructionSetExtensions
|
||||
{
|
||||
get => new global::Marechai.App.InstructionSetExtensions.InstructionSetExtensionsRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The instructionSetExtensionsByProcessor property</summary>
|
||||
public global::Marechai.App.InstructionSetExtensionsByProcessor.InstructionSetExtensionsByProcessorRequestBuilder InstructionSetExtensionsByProcessor
|
||||
{
|
||||
get => new global::Marechai.App.InstructionSetExtensionsByProcessor.InstructionSetExtensionsByProcessorRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The instructionSets property</summary>
|
||||
public global::Marechai.App.InstructionSets.InstructionSetsRequestBuilder InstructionSets
|
||||
{
|
||||
get => new global::Marechai.App.InstructionSets.InstructionSetsRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The iso31661Numeric property</summary>
|
||||
public global::Marechai.App.Iso31661Numeric.Iso31661NumericRequestBuilder Iso31661Numeric
|
||||
{
|
||||
get => new global::Marechai.App.Iso31661Numeric.Iso31661NumericRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The iso4217 property</summary>
|
||||
public global::Marechai.App.Iso4217.Iso4217RequestBuilder Iso4217
|
||||
{
|
||||
get => new global::Marechai.App.Iso4217.Iso4217RequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The licenses property</summary>
|
||||
public global::Marechai.App.Licenses.LicensesRequestBuilder Licenses
|
||||
{
|
||||
get => new global::Marechai.App.Licenses.LicensesRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The machineFamilies property</summary>
|
||||
public global::Marechai.App.MachineFamilies.MachineFamiliesRequestBuilder MachineFamilies
|
||||
{
|
||||
get => new global::Marechai.App.MachineFamilies.MachineFamiliesRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The machines property</summary>
|
||||
public global::Marechai.App.Machines.MachinesRequestBuilder Machines
|
||||
{
|
||||
get => new global::Marechai.App.Machines.MachinesRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The magazines property</summary>
|
||||
public global::Marechai.App.Magazines.MagazinesRequestBuilder Magazines
|
||||
{
|
||||
get => new global::Marechai.App.Magazines.MagazinesRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The magazinesByMachine property</summary>
|
||||
public global::Marechai.App.MagazinesByMachine.MagazinesByMachineRequestBuilder MagazinesByMachine
|
||||
{
|
||||
get => new global::Marechai.App.MagazinesByMachine.MagazinesByMachineRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The magazinesByMachineFamily property</summary>
|
||||
public global::Marechai.App.MagazinesByMachineFamily.MagazinesByMachineFamilyRequestBuilder MagazinesByMachineFamily
|
||||
{
|
||||
get => new global::Marechai.App.MagazinesByMachineFamily.MagazinesByMachineFamilyRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The medias property</summary>
|
||||
public global::Marechai.App.Medias.MediasRequestBuilder Medias
|
||||
{
|
||||
get => new global::Marechai.App.Medias.MediasRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The memoriesByMachine property</summary>
|
||||
public global::Marechai.App.MemoriesByMachine.MemoriesByMachineRequestBuilder MemoriesByMachine
|
||||
{
|
||||
get => new global::Marechai.App.MemoriesByMachine.MemoriesByMachineRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The news property</summary>
|
||||
public global::Marechai.App.News.NewsRequestBuilder News
|
||||
{
|
||||
get => new global::Marechai.App.News.NewsRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The people property</summary>
|
||||
public global::Marechai.App.People.PeopleRequestBuilder People
|
||||
{
|
||||
get => new global::Marechai.App.People.PeopleRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The peopleByBook property</summary>
|
||||
public global::Marechai.App.PeopleByBook.PeopleByBookRequestBuilder PeopleByBook
|
||||
{
|
||||
get => new global::Marechai.App.PeopleByBook.PeopleByBookRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The peopleByDocument property</summary>
|
||||
public global::Marechai.App.PeopleByDocument.PeopleByDocumentRequestBuilder PeopleByDocument
|
||||
{
|
||||
get => new global::Marechai.App.PeopleByDocument.PeopleByDocumentRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The peopleByMagazine property</summary>
|
||||
public global::Marechai.App.PeopleByMagazine.PeopleByMagazineRequestBuilder PeopleByMagazine
|
||||
{
|
||||
get => new global::Marechai.App.PeopleByMagazine.PeopleByMagazineRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The processor property</summary>
|
||||
public global::Marechai.App.Processor.ProcessorRequestBuilder Processor
|
||||
{
|
||||
get => new global::Marechai.App.Processor.ProcessorRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The processors property</summary>
|
||||
public global::Marechai.App.Processors.ProcessorsRequestBuilder Processors
|
||||
{
|
||||
get => new global::Marechai.App.Processors.ProcessorsRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The processorsByMachine property</summary>
|
||||
public global::Marechai.App.ProcessorsByMachine.ProcessorsByMachineRequestBuilder ProcessorsByMachine
|
||||
{
|
||||
get => new global::Marechai.App.ProcessorsByMachine.ProcessorsByMachineRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The resolutions property</summary>
|
||||
public global::Marechai.App.Resolutions.ResolutionsRequestBuilder Resolutions
|
||||
{
|
||||
get => new global::Marechai.App.Resolutions.ResolutionsRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The resolutionsByGpu property</summary>
|
||||
public global::Marechai.App.ResolutionsByGpu.ResolutionsByGpuRequestBuilder ResolutionsByGpu
|
||||
{
|
||||
get => new global::Marechai.App.ResolutionsByGpu.ResolutionsByGpuRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The resolutionsByScreen property</summary>
|
||||
public global::Marechai.App.ResolutionsByScreen.ResolutionsByScreenRequestBuilder ResolutionsByScreen
|
||||
{
|
||||
get => new global::Marechai.App.ResolutionsByScreen.ResolutionsByScreenRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The screens property</summary>
|
||||
public global::Marechai.App.Screens.ScreensRequestBuilder Screens
|
||||
{
|
||||
get => new global::Marechai.App.Screens.ScreensRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The screensByMachine property</summary>
|
||||
public global::Marechai.App.ScreensByMachine.ScreensByMachineRequestBuilder ScreensByMachine
|
||||
{
|
||||
get => new global::Marechai.App.ScreensByMachine.ScreensByMachineRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The software property</summary>
|
||||
public global::Marechai.App.Software.SoftwareRequestBuilder Software
|
||||
{
|
||||
get => new global::Marechai.App.Software.SoftwareRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The soundSynths property</summary>
|
||||
public global::Marechai.App.SoundSynths.SoundSynthsRequestBuilder SoundSynths
|
||||
{
|
||||
get => new global::Marechai.App.SoundSynths.SoundSynthsRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The soundSynthsByMachine property</summary>
|
||||
public global::Marechai.App.SoundSynthsByMachine.SoundSynthsByMachineRequestBuilder SoundSynthsByMachine
|
||||
{
|
||||
get => new global::Marechai.App.SoundSynthsByMachine.SoundSynthsByMachineRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The storageByMachine property</summary>
|
||||
public global::Marechai.App.StorageByMachine.StorageByMachineRequestBuilder StorageByMachine
|
||||
{
|
||||
get => new global::Marechai.App.StorageByMachine.StorageByMachineRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="global::Marechai.App.ApiClient"/> and sets the default values.
|
||||
/// </summary>
|
||||
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
|
||||
public ApiClient(IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}", new Dictionary<string, object>())
|
||||
{
|
||||
ApiClientBuilder.RegisterDefaultSerializer<JsonSerializationWriterFactory>();
|
||||
ApiClientBuilder.RegisterDefaultSerializer<TextSerializationWriterFactory>();
|
||||
ApiClientBuilder.RegisterDefaultSerializer<FormSerializationWriterFactory>();
|
||||
ApiClientBuilder.RegisterDefaultSerializer<MultipartSerializationWriterFactory>();
|
||||
ApiClientBuilder.RegisterDefaultDeserializer<JsonParseNodeFactory>();
|
||||
ApiClientBuilder.RegisterDefaultDeserializer<TextParseNodeFactory>();
|
||||
ApiClientBuilder.RegisterDefaultDeserializer<FormParseNodeFactory>();
|
||||
if (string.IsNullOrEmpty(RequestAdapter.BaseUrl))
|
||||
{
|
||||
RequestAdapter.BaseUrl = "https://localhost:7163";
|
||||
}
|
||||
PathParameters.TryAdd("baseurl", RequestAdapter.BaseUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
41
Marechai.App/Services/Client/Auth/AuthRequestBuilder.cs
Normal file
41
Marechai.App/Services/Client/Auth/AuthRequestBuilder.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
// <auto-generated/>
|
||||
#pragma warning disable CS0618
|
||||
using Marechai.App.Auth.Login;
|
||||
using Microsoft.Kiota.Abstractions.Extensions;
|
||||
using Microsoft.Kiota.Abstractions;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
namespace Marechai.App.Auth
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds and executes requests for operations under \auth
|
||||
/// </summary>
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
public partial class AuthRequestBuilder : BaseRequestBuilder
|
||||
{
|
||||
/// <summary>The login property</summary>
|
||||
public global::Marechai.App.Auth.Login.LoginRequestBuilder Login
|
||||
{
|
||||
get => new global::Marechai.App.Auth.Login.LoginRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="global::Marechai.App.Auth.AuthRequestBuilder"/> and sets the default values.
|
||||
/// </summary>
|
||||
/// <param name="pathParameters">Path parameters for the request</param>
|
||||
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
|
||||
public AuthRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/auth", pathParameters)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="global::Marechai.App.Auth.AuthRequestBuilder"/> and sets the default values.
|
||||
/// </summary>
|
||||
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
|
||||
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
|
||||
public AuthRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/auth", rawUrl)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
@@ -0,0 +1,98 @@
|
||||
// <auto-generated/>
|
||||
#pragma warning disable CS0618
|
||||
using Marechai.App.Models;
|
||||
using Microsoft.Kiota.Abstractions.Extensions;
|
||||
using Microsoft.Kiota.Abstractions.Serialization;
|
||||
using Microsoft.Kiota.Abstractions;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System;
|
||||
namespace Marechai.App.Auth.Login
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds and executes requests for operations under \auth\login
|
||||
/// </summary>
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
public partial class LoginRequestBuilder : BaseRequestBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="global::Marechai.App.Auth.Login.LoginRequestBuilder"/> and sets the default values.
|
||||
/// </summary>
|
||||
/// <param name="pathParameters">Path parameters for the request</param>
|
||||
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
|
||||
public LoginRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/auth/login", pathParameters)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="global::Marechai.App.Auth.Login.LoginRequestBuilder"/> and sets the default values.
|
||||
/// </summary>
|
||||
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
|
||||
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
|
||||
public LoginRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/auth/login", rawUrl)
|
||||
{
|
||||
}
|
||||
/// <returns>A <see cref="global::Marechai.App.Models.AuthResponse"/></returns>
|
||||
/// <param name="body">The request body</param>
|
||||
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 400 status code</exception>
|
||||
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 401 status code</exception>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public async Task<global::Marechai.App.Models.AuthResponse?> PostAsync(global::Marechai.App.Models.AuthRequest body, Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public async Task<global::Marechai.App.Models.AuthResponse> PostAsync(global::Marechai.App.Models.AuthRequest body, Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#endif
|
||||
if(ReferenceEquals(body, null)) throw new ArgumentNullException(nameof(body));
|
||||
var requestInfo = ToPostRequestInformation(body, requestConfiguration);
|
||||
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
|
||||
{
|
||||
{ "400", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
{ "401", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
};
|
||||
return await RequestAdapter.SendAsync<global::Marechai.App.Models.AuthResponse>(requestInfo, global::Marechai.App.Models.AuthResponse.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
/// <returns>A <see cref="RequestInformation"/></returns>
|
||||
/// <param name="body">The request body</param>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public RequestInformation ToPostRequestInformation(global::Marechai.App.Models.AuthRequest body, Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public RequestInformation ToPostRequestInformation(global::Marechai.App.Models.AuthRequest body, Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
|
||||
{
|
||||
#endif
|
||||
if(ReferenceEquals(body, null)) throw new ArgumentNullException(nameof(body));
|
||||
var requestInfo = new RequestInformation(Method.POST, UrlTemplate, PathParameters);
|
||||
requestInfo.Configure(requestConfiguration);
|
||||
requestInfo.Headers.TryAdd("Accept", "application/json");
|
||||
requestInfo.SetContentFromParsable(RequestAdapter, "application/json", body);
|
||||
return requestInfo;
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="global::Marechai.App.Auth.Login.LoginRequestBuilder"/></returns>
|
||||
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
|
||||
public global::Marechai.App.Auth.Login.LoginRequestBuilder WithUrl(string rawUrl)
|
||||
{
|
||||
return new global::Marechai.App.Auth.Login.LoginRequestBuilder(rawUrl, RequestAdapter);
|
||||
}
|
||||
/// <summary>
|
||||
/// Configuration for the request such as headers, query parameters, and middleware options.
|
||||
/// </summary>
|
||||
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
public partial class LoginRequestBuilderPostRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
181
Marechai.App/Services/Client/Books/BooksRequestBuilder.cs
Normal file
181
Marechai.App/Services/Client/Books/BooksRequestBuilder.cs
Normal file
@@ -0,0 +1,181 @@
|
||||
// <auto-generated/>
|
||||
#pragma warning disable CS0618
|
||||
using Marechai.App.Books.Companies;
|
||||
using Marechai.App.Books.Item;
|
||||
using Marechai.App.Books.Scans;
|
||||
using Marechai.App.Models;
|
||||
using Microsoft.Kiota.Abstractions.Extensions;
|
||||
using Microsoft.Kiota.Abstractions.Serialization;
|
||||
using Microsoft.Kiota.Abstractions;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System;
|
||||
namespace Marechai.App.Books
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds and executes requests for operations under \books
|
||||
/// </summary>
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
public partial class BooksRequestBuilder : BaseRequestBuilder
|
||||
{
|
||||
/// <summary>The companies property</summary>
|
||||
public global::Marechai.App.Books.Companies.CompaniesRequestBuilder Companies
|
||||
{
|
||||
get => new global::Marechai.App.Books.Companies.CompaniesRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The scans property</summary>
|
||||
public global::Marechai.App.Books.Scans.ScansRequestBuilder Scans
|
||||
{
|
||||
get => new global::Marechai.App.Books.Scans.ScansRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>Gets an item from the Marechai.App.books.item collection</summary>
|
||||
/// <param name="position">Unique identifier of the item</param>
|
||||
/// <returns>A <see cref="global::Marechai.App.Books.Item.BookItemRequestBuilder"/></returns>
|
||||
public global::Marechai.App.Books.Item.BookItemRequestBuilder this[long position]
|
||||
{
|
||||
get
|
||||
{
|
||||
var urlTplParams = new Dictionary<string, object>(PathParameters);
|
||||
urlTplParams.Add("book%2Did", position);
|
||||
return new global::Marechai.App.Books.Item.BookItemRequestBuilder(urlTplParams, RequestAdapter);
|
||||
}
|
||||
}
|
||||
/// <summary>Gets an item from the Marechai.App.books.item collection</summary>
|
||||
/// <param name="position">Unique identifier of the item</param>
|
||||
/// <returns>A <see cref="global::Marechai.App.Books.Item.BookItemRequestBuilder"/></returns>
|
||||
[Obsolete("This indexer is deprecated and will be removed in the next major version. Use the one with the typed parameter instead.")]
|
||||
public global::Marechai.App.Books.Item.BookItemRequestBuilder this[string position]
|
||||
{
|
||||
get
|
||||
{
|
||||
var urlTplParams = new Dictionary<string, object>(PathParameters);
|
||||
if (!string.IsNullOrWhiteSpace(position)) urlTplParams.Add("book%2Did", position);
|
||||
return new global::Marechai.App.Books.Item.BookItemRequestBuilder(urlTplParams, RequestAdapter);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="global::Marechai.App.Books.BooksRequestBuilder"/> and sets the default values.
|
||||
/// </summary>
|
||||
/// <param name="pathParameters">Path parameters for the request</param>
|
||||
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
|
||||
public BooksRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/books", pathParameters)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="global::Marechai.App.Books.BooksRequestBuilder"/> and sets the default values.
|
||||
/// </summary>
|
||||
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
|
||||
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
|
||||
public BooksRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/books", rawUrl)
|
||||
{
|
||||
}
|
||||
/// <returns>A List<global::Marechai.App.Models.BookDto></returns>
|
||||
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 400 status code</exception>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public async Task<List<global::Marechai.App.Models.BookDto>?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public async Task<List<global::Marechai.App.Models.BookDto>> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#endif
|
||||
var requestInfo = ToGetRequestInformation(requestConfiguration);
|
||||
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
|
||||
{
|
||||
{ "400", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
};
|
||||
var collectionResult = await RequestAdapter.SendCollectionAsync<global::Marechai.App.Models.BookDto>(requestInfo, global::Marechai.App.Models.BookDto.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
|
||||
return collectionResult?.AsList();
|
||||
}
|
||||
/// <returns>A <see cref="UntypedNode"/></returns>
|
||||
/// <param name="body">The request body</param>
|
||||
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 400 status code</exception>
|
||||
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 401 status code</exception>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public async Task<UntypedNode?> PostAsync(global::Marechai.App.Models.BookDto body, Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public async Task<UntypedNode> PostAsync(global::Marechai.App.Models.BookDto body, Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#endif
|
||||
if(ReferenceEquals(body, null)) throw new ArgumentNullException(nameof(body));
|
||||
var requestInfo = ToPostRequestInformation(body, requestConfiguration);
|
||||
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
|
||||
{
|
||||
{ "400", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
{ "401", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
};
|
||||
return await RequestAdapter.SendAsync<UntypedNode>(requestInfo, UntypedNode.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
/// <returns>A <see cref="RequestInformation"/></returns>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
|
||||
{
|
||||
#endif
|
||||
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
|
||||
requestInfo.Configure(requestConfiguration);
|
||||
requestInfo.Headers.TryAdd("Accept", "text/plain;q=0.9");
|
||||
return requestInfo;
|
||||
}
|
||||
/// <returns>A <see cref="RequestInformation"/></returns>
|
||||
/// <param name="body">The request body</param>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public RequestInformation ToPostRequestInformation(global::Marechai.App.Models.BookDto body, Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public RequestInformation ToPostRequestInformation(global::Marechai.App.Models.BookDto body, Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
|
||||
{
|
||||
#endif
|
||||
if(ReferenceEquals(body, null)) throw new ArgumentNullException(nameof(body));
|
||||
var requestInfo = new RequestInformation(Method.POST, UrlTemplate, PathParameters);
|
||||
requestInfo.Configure(requestConfiguration);
|
||||
requestInfo.Headers.TryAdd("Accept", "text/plain;q=0.9");
|
||||
requestInfo.SetContentFromParsable(RequestAdapter, "application/json", body);
|
||||
return requestInfo;
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="global::Marechai.App.Books.BooksRequestBuilder"/></returns>
|
||||
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
|
||||
public global::Marechai.App.Books.BooksRequestBuilder WithUrl(string rawUrl)
|
||||
{
|
||||
return new global::Marechai.App.Books.BooksRequestBuilder(rawUrl, RequestAdapter);
|
||||
}
|
||||
/// <summary>
|
||||
/// Configuration for the request such as headers, query parameters, and middleware options.
|
||||
/// </summary>
|
||||
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
public partial class BooksRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Configuration for the request such as headers, query parameters, and middleware options.
|
||||
/// </summary>
|
||||
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
public partial class BooksRequestBuilderPostRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
@@ -0,0 +1,124 @@
|
||||
// <auto-generated/>
|
||||
#pragma warning disable CS0618
|
||||
using Marechai.App.Books.Companies.Item;
|
||||
using Marechai.App.Models;
|
||||
using Microsoft.Kiota.Abstractions.Extensions;
|
||||
using Microsoft.Kiota.Abstractions.Serialization;
|
||||
using Microsoft.Kiota.Abstractions;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System;
|
||||
namespace Marechai.App.Books.Companies
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds and executes requests for operations under \books\companies
|
||||
/// </summary>
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
public partial class CompaniesRequestBuilder : BaseRequestBuilder
|
||||
{
|
||||
/// <summary>Gets an item from the Marechai.App.books.companies.item collection</summary>
|
||||
/// <param name="position">Unique identifier of the item</param>
|
||||
/// <returns>A <see cref="global::Marechai.App.Books.Companies.Item.CompaniesItemRequestBuilder"/></returns>
|
||||
public global::Marechai.App.Books.Companies.Item.CompaniesItemRequestBuilder this[long position]
|
||||
{
|
||||
get
|
||||
{
|
||||
var urlTplParams = new Dictionary<string, object>(PathParameters);
|
||||
urlTplParams.Add("id", position);
|
||||
return new global::Marechai.App.Books.Companies.Item.CompaniesItemRequestBuilder(urlTplParams, RequestAdapter);
|
||||
}
|
||||
}
|
||||
/// <summary>Gets an item from the Marechai.App.books.companies.item collection</summary>
|
||||
/// <param name="position">Unique identifier of the item</param>
|
||||
/// <returns>A <see cref="global::Marechai.App.Books.Companies.Item.CompaniesItemRequestBuilder"/></returns>
|
||||
[Obsolete("This indexer is deprecated and will be removed in the next major version. Use the one with the typed parameter instead.")]
|
||||
public global::Marechai.App.Books.Companies.Item.CompaniesItemRequestBuilder this[string position]
|
||||
{
|
||||
get
|
||||
{
|
||||
var urlTplParams = new Dictionary<string, object>(PathParameters);
|
||||
if (!string.IsNullOrWhiteSpace(position)) urlTplParams.Add("id", position);
|
||||
return new global::Marechai.App.Books.Companies.Item.CompaniesItemRequestBuilder(urlTplParams, RequestAdapter);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="global::Marechai.App.Books.Companies.CompaniesRequestBuilder"/> and sets the default values.
|
||||
/// </summary>
|
||||
/// <param name="pathParameters">Path parameters for the request</param>
|
||||
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
|
||||
public CompaniesRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/books/companies", pathParameters)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="global::Marechai.App.Books.Companies.CompaniesRequestBuilder"/> and sets the default values.
|
||||
/// </summary>
|
||||
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
|
||||
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
|
||||
public CompaniesRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/books/companies", rawUrl)
|
||||
{
|
||||
}
|
||||
/// <returns>A <see cref="UntypedNode"/></returns>
|
||||
/// <param name="body">The request body</param>
|
||||
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 400 status code</exception>
|
||||
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 401 status code</exception>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public async Task<UntypedNode?> PostAsync(global::Marechai.App.Models.CompanyByBookDto body, Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public async Task<UntypedNode> PostAsync(global::Marechai.App.Models.CompanyByBookDto body, Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#endif
|
||||
if(ReferenceEquals(body, null)) throw new ArgumentNullException(nameof(body));
|
||||
var requestInfo = ToPostRequestInformation(body, requestConfiguration);
|
||||
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
|
||||
{
|
||||
{ "400", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
{ "401", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
};
|
||||
return await RequestAdapter.SendAsync<UntypedNode>(requestInfo, UntypedNode.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
/// <returns>A <see cref="RequestInformation"/></returns>
|
||||
/// <param name="body">The request body</param>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public RequestInformation ToPostRequestInformation(global::Marechai.App.Models.CompanyByBookDto body, Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public RequestInformation ToPostRequestInformation(global::Marechai.App.Models.CompanyByBookDto body, Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
|
||||
{
|
||||
#endif
|
||||
if(ReferenceEquals(body, null)) throw new ArgumentNullException(nameof(body));
|
||||
var requestInfo = new RequestInformation(Method.POST, UrlTemplate, PathParameters);
|
||||
requestInfo.Configure(requestConfiguration);
|
||||
requestInfo.Headers.TryAdd("Accept", "text/plain;q=0.9");
|
||||
requestInfo.SetContentFromParsable(RequestAdapter, "application/json", body);
|
||||
return requestInfo;
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="global::Marechai.App.Books.Companies.CompaniesRequestBuilder"/></returns>
|
||||
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
|
||||
public global::Marechai.App.Books.Companies.CompaniesRequestBuilder WithUrl(string rawUrl)
|
||||
{
|
||||
return new global::Marechai.App.Books.Companies.CompaniesRequestBuilder(rawUrl, RequestAdapter);
|
||||
}
|
||||
/// <summary>
|
||||
/// Configuration for the request such as headers, query parameters, and middleware options.
|
||||
/// </summary>
|
||||
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
public partial class CompaniesRequestBuilderPostRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
@@ -0,0 +1,95 @@
|
||||
// <auto-generated/>
|
||||
#pragma warning disable CS0618
|
||||
using Marechai.App.Models;
|
||||
using Microsoft.Kiota.Abstractions.Extensions;
|
||||
using Microsoft.Kiota.Abstractions.Serialization;
|
||||
using Microsoft.Kiota.Abstractions;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System;
|
||||
namespace Marechai.App.Books.Companies.Item
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds and executes requests for operations under \books\companies\{id}
|
||||
/// </summary>
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
public partial class CompaniesItemRequestBuilder : BaseRequestBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="global::Marechai.App.Books.Companies.Item.CompaniesItemRequestBuilder"/> and sets the default values.
|
||||
/// </summary>
|
||||
/// <param name="pathParameters">Path parameters for the request</param>
|
||||
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
|
||||
public CompaniesItemRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/books/companies/{id}", pathParameters)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="global::Marechai.App.Books.Companies.Item.CompaniesItemRequestBuilder"/> and sets the default values.
|
||||
/// </summary>
|
||||
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
|
||||
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
|
||||
public CompaniesItemRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/books/companies/{id}", rawUrl)
|
||||
{
|
||||
}
|
||||
/// <returns>A <see cref="string"/></returns>
|
||||
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 400 status code</exception>
|
||||
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 401 status code</exception>
|
||||
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 404 status code</exception>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public async Task<string?> DeleteAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public async Task<string> DeleteAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#endif
|
||||
var requestInfo = ToDeleteRequestInformation(requestConfiguration);
|
||||
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
|
||||
{
|
||||
{ "400", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
{ "401", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
{ "404", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
};
|
||||
return await RequestAdapter.SendPrimitiveAsync<string>(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
/// <returns>A <see cref="RequestInformation"/></returns>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public RequestInformation ToDeleteRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public RequestInformation ToDeleteRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
|
||||
{
|
||||
#endif
|
||||
var requestInfo = new RequestInformation(Method.DELETE, UrlTemplate, PathParameters);
|
||||
requestInfo.Configure(requestConfiguration);
|
||||
requestInfo.Headers.TryAdd("Accept", "application/json, text/plain;q=0.9");
|
||||
return requestInfo;
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="global::Marechai.App.Books.Companies.Item.CompaniesItemRequestBuilder"/></returns>
|
||||
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
|
||||
public global::Marechai.App.Books.Companies.Item.CompaniesItemRequestBuilder WithUrl(string rawUrl)
|
||||
{
|
||||
return new global::Marechai.App.Books.Companies.Item.CompaniesItemRequestBuilder(rawUrl, RequestAdapter);
|
||||
}
|
||||
/// <summary>
|
||||
/// Configuration for the request such as headers, query parameters, and middleware options.
|
||||
/// </summary>
|
||||
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
public partial class CompaniesItemRequestBuilderDeleteRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
@@ -0,0 +1,228 @@
|
||||
// <auto-generated/>
|
||||
#pragma warning disable CS0618
|
||||
using Marechai.App.Books.Item.Companies;
|
||||
using Marechai.App.Books.Item.MachineFamilies;
|
||||
using Marechai.App.Books.Item.Machines;
|
||||
using Marechai.App.Books.Item.People;
|
||||
using Marechai.App.Books.Item.Scans;
|
||||
using Marechai.App.Books.Item.Synopsis;
|
||||
using Marechai.App.Models;
|
||||
using Microsoft.Kiota.Abstractions.Extensions;
|
||||
using Microsoft.Kiota.Abstractions.Serialization;
|
||||
using Microsoft.Kiota.Abstractions;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System;
|
||||
namespace Marechai.App.Books.Item
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds and executes requests for operations under \books\{book-id}
|
||||
/// </summary>
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
public partial class BookItemRequestBuilder : BaseRequestBuilder
|
||||
{
|
||||
/// <summary>The companies property</summary>
|
||||
public global::Marechai.App.Books.Item.Companies.CompaniesRequestBuilder Companies
|
||||
{
|
||||
get => new global::Marechai.App.Books.Item.Companies.CompaniesRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The machineFamilies property</summary>
|
||||
public global::Marechai.App.Books.Item.MachineFamilies.MachineFamiliesRequestBuilder MachineFamilies
|
||||
{
|
||||
get => new global::Marechai.App.Books.Item.MachineFamilies.MachineFamiliesRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The machines property</summary>
|
||||
public global::Marechai.App.Books.Item.Machines.MachinesRequestBuilder Machines
|
||||
{
|
||||
get => new global::Marechai.App.Books.Item.Machines.MachinesRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The people property</summary>
|
||||
public global::Marechai.App.Books.Item.People.PeopleRequestBuilder People
|
||||
{
|
||||
get => new global::Marechai.App.Books.Item.People.PeopleRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The scans property</summary>
|
||||
public global::Marechai.App.Books.Item.Scans.ScansRequestBuilder Scans
|
||||
{
|
||||
get => new global::Marechai.App.Books.Item.Scans.ScansRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>The synopsis property</summary>
|
||||
public global::Marechai.App.Books.Item.Synopsis.SynopsisRequestBuilder Synopsis
|
||||
{
|
||||
get => new global::Marechai.App.Books.Item.Synopsis.SynopsisRequestBuilder(PathParameters, RequestAdapter);
|
||||
}
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="global::Marechai.App.Books.Item.BookItemRequestBuilder"/> and sets the default values.
|
||||
/// </summary>
|
||||
/// <param name="pathParameters">Path parameters for the request</param>
|
||||
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
|
||||
public BookItemRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/books/{book%2Did}", pathParameters)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="global::Marechai.App.Books.Item.BookItemRequestBuilder"/> and sets the default values.
|
||||
/// </summary>
|
||||
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
|
||||
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
|
||||
public BookItemRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/books/{book%2Did}", rawUrl)
|
||||
{
|
||||
}
|
||||
/// <returns>A <see cref="string"/></returns>
|
||||
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 400 status code</exception>
|
||||
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 401 status code</exception>
|
||||
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 404 status code</exception>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public async Task<string?> DeleteAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public async Task<string> DeleteAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#endif
|
||||
var requestInfo = ToDeleteRequestInformation(requestConfiguration);
|
||||
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
|
||||
{
|
||||
{ "400", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
{ "401", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
{ "404", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
};
|
||||
return await RequestAdapter.SendPrimitiveAsync<string>(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
/// <returns>A <see cref="global::Marechai.App.Models.BookDto"/></returns>
|
||||
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 400 status code</exception>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public async Task<global::Marechai.App.Models.BookDto?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public async Task<global::Marechai.App.Models.BookDto> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#endif
|
||||
var requestInfo = ToGetRequestInformation(requestConfiguration);
|
||||
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
|
||||
{
|
||||
{ "400", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
};
|
||||
return await RequestAdapter.SendAsync<global::Marechai.App.Models.BookDto>(requestInfo, global::Marechai.App.Models.BookDto.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
/// <returns>A <see cref="string"/></returns>
|
||||
/// <param name="body">The request body</param>
|
||||
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 400 status code</exception>
|
||||
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 401 status code</exception>
|
||||
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 404 status code</exception>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public async Task<string?> PutAsync(global::Marechai.App.Models.BookDto body, Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public async Task<string> PutAsync(global::Marechai.App.Models.BookDto body, Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#endif
|
||||
if(ReferenceEquals(body, null)) throw new ArgumentNullException(nameof(body));
|
||||
var requestInfo = ToPutRequestInformation(body, requestConfiguration);
|
||||
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
|
||||
{
|
||||
{ "400", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
{ "401", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
{ "404", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
};
|
||||
return await RequestAdapter.SendPrimitiveAsync<string>(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
/// <returns>A <see cref="RequestInformation"/></returns>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public RequestInformation ToDeleteRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public RequestInformation ToDeleteRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
|
||||
{
|
||||
#endif
|
||||
var requestInfo = new RequestInformation(Method.DELETE, UrlTemplate, PathParameters);
|
||||
requestInfo.Configure(requestConfiguration);
|
||||
requestInfo.Headers.TryAdd("Accept", "application/json, text/plain;q=0.9");
|
||||
return requestInfo;
|
||||
}
|
||||
/// <returns>A <see cref="RequestInformation"/></returns>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
|
||||
{
|
||||
#endif
|
||||
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
|
||||
requestInfo.Configure(requestConfiguration);
|
||||
requestInfo.Headers.TryAdd("Accept", "application/json, text/plain;q=0.9");
|
||||
return requestInfo;
|
||||
}
|
||||
/// <returns>A <see cref="RequestInformation"/></returns>
|
||||
/// <param name="body">The request body</param>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public RequestInformation ToPutRequestInformation(global::Marechai.App.Models.BookDto body, Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public RequestInformation ToPutRequestInformation(global::Marechai.App.Models.BookDto body, Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
|
||||
{
|
||||
#endif
|
||||
if(ReferenceEquals(body, null)) throw new ArgumentNullException(nameof(body));
|
||||
var requestInfo = new RequestInformation(Method.PUT, UrlTemplate, PathParameters);
|
||||
requestInfo.Configure(requestConfiguration);
|
||||
requestInfo.Headers.TryAdd("Accept", "application/json, text/plain;q=0.9");
|
||||
requestInfo.SetContentFromParsable(RequestAdapter, "application/json", body);
|
||||
return requestInfo;
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="global::Marechai.App.Books.Item.BookItemRequestBuilder"/></returns>
|
||||
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
|
||||
public global::Marechai.App.Books.Item.BookItemRequestBuilder WithUrl(string rawUrl)
|
||||
{
|
||||
return new global::Marechai.App.Books.Item.BookItemRequestBuilder(rawUrl, RequestAdapter);
|
||||
}
|
||||
/// <summary>
|
||||
/// Configuration for the request such as headers, query parameters, and middleware options.
|
||||
/// </summary>
|
||||
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
public partial class BookItemRequestBuilderDeleteRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Configuration for the request such as headers, query parameters, and middleware options.
|
||||
/// </summary>
|
||||
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
public partial class BookItemRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Configuration for the request such as headers, query parameters, and middleware options.
|
||||
/// </summary>
|
||||
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
public partial class BookItemRequestBuilderPutRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
@@ -0,0 +1,92 @@
|
||||
// <auto-generated/>
|
||||
#pragma warning disable CS0618
|
||||
using Marechai.App.Models;
|
||||
using Microsoft.Kiota.Abstractions.Extensions;
|
||||
using Microsoft.Kiota.Abstractions.Serialization;
|
||||
using Microsoft.Kiota.Abstractions;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System;
|
||||
namespace Marechai.App.Books.Item.Companies
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds and executes requests for operations under \books\{book-id}\companies
|
||||
/// </summary>
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
public partial class CompaniesRequestBuilder : BaseRequestBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="global::Marechai.App.Books.Item.Companies.CompaniesRequestBuilder"/> and sets the default values.
|
||||
/// </summary>
|
||||
/// <param name="pathParameters">Path parameters for the request</param>
|
||||
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
|
||||
public CompaniesRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/books/{book%2Did}/companies", pathParameters)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="global::Marechai.App.Books.Item.Companies.CompaniesRequestBuilder"/> and sets the default values.
|
||||
/// </summary>
|
||||
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
|
||||
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
|
||||
public CompaniesRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/books/{book%2Did}/companies", rawUrl)
|
||||
{
|
||||
}
|
||||
/// <returns>A List<global::Marechai.App.Models.CompanyByBookDto></returns>
|
||||
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 400 status code</exception>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public async Task<List<global::Marechai.App.Models.CompanyByBookDto>?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public async Task<List<global::Marechai.App.Models.CompanyByBookDto>> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#endif
|
||||
var requestInfo = ToGetRequestInformation(requestConfiguration);
|
||||
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
|
||||
{
|
||||
{ "400", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
};
|
||||
var collectionResult = await RequestAdapter.SendCollectionAsync<global::Marechai.App.Models.CompanyByBookDto>(requestInfo, global::Marechai.App.Models.CompanyByBookDto.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
|
||||
return collectionResult?.AsList();
|
||||
}
|
||||
/// <returns>A <see cref="RequestInformation"/></returns>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
|
||||
{
|
||||
#endif
|
||||
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
|
||||
requestInfo.Configure(requestConfiguration);
|
||||
requestInfo.Headers.TryAdd("Accept", "text/plain;q=0.9");
|
||||
return requestInfo;
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="global::Marechai.App.Books.Item.Companies.CompaniesRequestBuilder"/></returns>
|
||||
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
|
||||
public global::Marechai.App.Books.Item.Companies.CompaniesRequestBuilder WithUrl(string rawUrl)
|
||||
{
|
||||
return new global::Marechai.App.Books.Item.Companies.CompaniesRequestBuilder(rawUrl, RequestAdapter);
|
||||
}
|
||||
/// <summary>
|
||||
/// Configuration for the request such as headers, query parameters, and middleware options.
|
||||
/// </summary>
|
||||
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
public partial class CompaniesRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
@@ -0,0 +1,92 @@
|
||||
// <auto-generated/>
|
||||
#pragma warning disable CS0618
|
||||
using Marechai.App.Models;
|
||||
using Microsoft.Kiota.Abstractions.Extensions;
|
||||
using Microsoft.Kiota.Abstractions.Serialization;
|
||||
using Microsoft.Kiota.Abstractions;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System;
|
||||
namespace Marechai.App.Books.Item.MachineFamilies
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds and executes requests for operations under \books\{book-id}\machine-families
|
||||
/// </summary>
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
public partial class MachineFamiliesRequestBuilder : BaseRequestBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="global::Marechai.App.Books.Item.MachineFamilies.MachineFamiliesRequestBuilder"/> and sets the default values.
|
||||
/// </summary>
|
||||
/// <param name="pathParameters">Path parameters for the request</param>
|
||||
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
|
||||
public MachineFamiliesRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/books/{book%2Did}/machine-families", pathParameters)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="global::Marechai.App.Books.Item.MachineFamilies.MachineFamiliesRequestBuilder"/> and sets the default values.
|
||||
/// </summary>
|
||||
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
|
||||
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
|
||||
public MachineFamiliesRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/books/{book%2Did}/machine-families", rawUrl)
|
||||
{
|
||||
}
|
||||
/// <returns>A List<global::Marechai.App.Models.BookByMachineFamilyDto></returns>
|
||||
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 400 status code</exception>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public async Task<List<global::Marechai.App.Models.BookByMachineFamilyDto>?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public async Task<List<global::Marechai.App.Models.BookByMachineFamilyDto>> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#endif
|
||||
var requestInfo = ToGetRequestInformation(requestConfiguration);
|
||||
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
|
||||
{
|
||||
{ "400", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
};
|
||||
var collectionResult = await RequestAdapter.SendCollectionAsync<global::Marechai.App.Models.BookByMachineFamilyDto>(requestInfo, global::Marechai.App.Models.BookByMachineFamilyDto.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
|
||||
return collectionResult?.AsList();
|
||||
}
|
||||
/// <returns>A <see cref="RequestInformation"/></returns>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
|
||||
{
|
||||
#endif
|
||||
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
|
||||
requestInfo.Configure(requestConfiguration);
|
||||
requestInfo.Headers.TryAdd("Accept", "text/plain;q=0.9");
|
||||
return requestInfo;
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="global::Marechai.App.Books.Item.MachineFamilies.MachineFamiliesRequestBuilder"/></returns>
|
||||
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
|
||||
public global::Marechai.App.Books.Item.MachineFamilies.MachineFamiliesRequestBuilder WithUrl(string rawUrl)
|
||||
{
|
||||
return new global::Marechai.App.Books.Item.MachineFamilies.MachineFamiliesRequestBuilder(rawUrl, RequestAdapter);
|
||||
}
|
||||
/// <summary>
|
||||
/// Configuration for the request such as headers, query parameters, and middleware options.
|
||||
/// </summary>
|
||||
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
public partial class MachineFamiliesRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
@@ -0,0 +1,92 @@
|
||||
// <auto-generated/>
|
||||
#pragma warning disable CS0618
|
||||
using Marechai.App.Models;
|
||||
using Microsoft.Kiota.Abstractions.Extensions;
|
||||
using Microsoft.Kiota.Abstractions.Serialization;
|
||||
using Microsoft.Kiota.Abstractions;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System;
|
||||
namespace Marechai.App.Books.Item.Machines
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds and executes requests for operations under \books\{book-id}\machines
|
||||
/// </summary>
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
public partial class MachinesRequestBuilder : BaseRequestBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="global::Marechai.App.Books.Item.Machines.MachinesRequestBuilder"/> and sets the default values.
|
||||
/// </summary>
|
||||
/// <param name="pathParameters">Path parameters for the request</param>
|
||||
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
|
||||
public MachinesRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/books/{book%2Did}/machines", pathParameters)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="global::Marechai.App.Books.Item.Machines.MachinesRequestBuilder"/> and sets the default values.
|
||||
/// </summary>
|
||||
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
|
||||
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
|
||||
public MachinesRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/books/{book%2Did}/machines", rawUrl)
|
||||
{
|
||||
}
|
||||
/// <returns>A List<global::Marechai.App.Models.BookByMachineDto></returns>
|
||||
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 400 status code</exception>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public async Task<List<global::Marechai.App.Models.BookByMachineDto>?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public async Task<List<global::Marechai.App.Models.BookByMachineDto>> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#endif
|
||||
var requestInfo = ToGetRequestInformation(requestConfiguration);
|
||||
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
|
||||
{
|
||||
{ "400", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
};
|
||||
var collectionResult = await RequestAdapter.SendCollectionAsync<global::Marechai.App.Models.BookByMachineDto>(requestInfo, global::Marechai.App.Models.BookByMachineDto.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
|
||||
return collectionResult?.AsList();
|
||||
}
|
||||
/// <returns>A <see cref="RequestInformation"/></returns>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
|
||||
{
|
||||
#endif
|
||||
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
|
||||
requestInfo.Configure(requestConfiguration);
|
||||
requestInfo.Headers.TryAdd("Accept", "text/plain;q=0.9");
|
||||
return requestInfo;
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="global::Marechai.App.Books.Item.Machines.MachinesRequestBuilder"/></returns>
|
||||
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
|
||||
public global::Marechai.App.Books.Item.Machines.MachinesRequestBuilder WithUrl(string rawUrl)
|
||||
{
|
||||
return new global::Marechai.App.Books.Item.Machines.MachinesRequestBuilder(rawUrl, RequestAdapter);
|
||||
}
|
||||
/// <summary>
|
||||
/// Configuration for the request such as headers, query parameters, and middleware options.
|
||||
/// </summary>
|
||||
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
public partial class MachinesRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
@@ -0,0 +1,92 @@
|
||||
// <auto-generated/>
|
||||
#pragma warning disable CS0618
|
||||
using Marechai.App.Models;
|
||||
using Microsoft.Kiota.Abstractions.Extensions;
|
||||
using Microsoft.Kiota.Abstractions.Serialization;
|
||||
using Microsoft.Kiota.Abstractions;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System;
|
||||
namespace Marechai.App.Books.Item.People
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds and executes requests for operations under \books\{book-id}\people
|
||||
/// </summary>
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
public partial class PeopleRequestBuilder : BaseRequestBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="global::Marechai.App.Books.Item.People.PeopleRequestBuilder"/> and sets the default values.
|
||||
/// </summary>
|
||||
/// <param name="pathParameters">Path parameters for the request</param>
|
||||
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
|
||||
public PeopleRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/books/{book%2Did}/people", pathParameters)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="global::Marechai.App.Books.Item.People.PeopleRequestBuilder"/> and sets the default values.
|
||||
/// </summary>
|
||||
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
|
||||
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
|
||||
public PeopleRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/books/{book%2Did}/people", rawUrl)
|
||||
{
|
||||
}
|
||||
/// <returns>A List<global::Marechai.App.Models.PersonByBookDto></returns>
|
||||
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
/// <exception cref="global::Marechai.App.Models.ProblemDetails">When receiving a 400 status code</exception>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public async Task<List<global::Marechai.App.Models.PersonByBookDto>?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public async Task<List<global::Marechai.App.Models.PersonByBookDto>> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#endif
|
||||
var requestInfo = ToGetRequestInformation(requestConfiguration);
|
||||
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
|
||||
{
|
||||
{ "400", global::Marechai.App.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
};
|
||||
var collectionResult = await RequestAdapter.SendCollectionAsync<global::Marechai.App.Models.PersonByBookDto>(requestInfo, global::Marechai.App.Models.PersonByBookDto.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
|
||||
return collectionResult?.AsList();
|
||||
}
|
||||
/// <returns>A <see cref="RequestInformation"/></returns>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
|
||||
{
|
||||
#endif
|
||||
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
|
||||
requestInfo.Configure(requestConfiguration);
|
||||
requestInfo.Headers.TryAdd("Accept", "text/plain;q=0.9");
|
||||
return requestInfo;
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="global::Marechai.App.Books.Item.People.PeopleRequestBuilder"/></returns>
|
||||
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
|
||||
public global::Marechai.App.Books.Item.People.PeopleRequestBuilder WithUrl(string rawUrl)
|
||||
{
|
||||
return new global::Marechai.App.Books.Item.People.PeopleRequestBuilder(rawUrl, RequestAdapter);
|
||||
}
|
||||
/// <summary>
|
||||
/// Configuration for the request such as headers, query parameters, and middleware options.
|
||||
/// </summary>
|
||||
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
public partial class PeopleRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user