101 Commits

Author SHA1 Message Date
80791a8cc9 Add company detail page. 2025-11-15 05:32:46 +00:00
d5fbb55425 Add companies list. 2025-11-15 04:48:11 +00:00
7ee042bdec Add consoles pages. 2025-11-15 04:13:24 +00:00
e9221ac130 Return the keyboard styling back. 2025-11-15 04:12:13 +00:00
87291d9dd8 Fix variable naming. 2025-11-15 02:48:40 +00:00
ce1c089fb0 Reorganize project structure. 2025-11-15 02:46:54 +00:00
5d249f435e Reorganize project structure. 2025-11-15 02:46:53 +00:00
3e4677b084 Add machine view. 2025-11-15 02:38:47 +00:00
b7c94312fc Add missing computers list page. 2025-11-15 02:36:01 +00:00
61ebf7b503 Add computer list. 2025-11-15 01:09:30 +00:00
b18396f8d8 Add computers page. 2025-11-14 20:59:12 +00:00
4f1aee302b Fix routes. 2025-11-14 20:58:41 +00:00
7ede62514f Add sidebar. 2025-11-14 19:00:01 +00:00
955c2f9654 Add CORS policy. 2025-11-14 16:46:42 +00:00
4a5708b910 Move enums to Data project. 2025-11-14 16:31:35 +00:00
5bffbc342e Show news on application load. 2025-11-14 15:18:30 +00:00
392c69350f Send type in news DTO. 2025-11-14 15:18:09 +00:00
2bb07845e1 Do not send text from news controller. 2025-11-14 15:17:50 +00:00
1053617622 Fix provisional endpoint for API client. 2025-11-14 15:17:25 +00:00
30b60c0e96 Move framework setting to each project. 2025-11-14 15:12:15 +00:00
14596c5499 Add Kiota client. 2025-11-14 13:33:27 +00:00
9847c987b5 Update copyright year. 2025-11-14 05:08:14 +00:00
6f0de86be4 Add custom route constraints for char and ulong parameters 2025-11-14 05:04:03 +00:00
37d8df4b7d Add properties to enable compiler-generated files output in project configuration 2025-11-14 05:03:51 +00:00
860fd99b00 Fix route parameter in ResolutionsByGpuController for resolutionId 2025-11-14 05:02:44 +00:00
2ba7d86e71 Refactor CompaniesController to use DTOs for machine and company operations 2025-11-14 05:02:01 +00:00
37403e5e53 Add Directory.Build.targets file for project configuration 2025-11-14 04:54:07 +00:00
2066e211e1 Refactor SoftwareVariantDto properties for improved readability 2025-11-14 04:51:39 +00:00
ebc611e5d2 Refactor MachineDto properties for improved readability and add JsonIgnore attributes for collections 2025-11-14 04:51:24 +00:00
5e3be9cbb0 Add LicenseDto and update LicensesController to use DTO for license operations 2025-11-14 04:51:12 +00:00
9d146eb151 Add Iso31661NumericDto and update Iso31661NumericController to use DTO for numeric operations 2025-11-14 04:50:54 +00:00
52b6145a8b Add Iso4217Dto and update Iso4217Controller to use DTO for currency operations 2025-11-14 04:50:39 +00:00
09ced17903 Add InstructionSetExtensionDto and update InstructionSetExtensionsController to use DTO for extension operations 2025-11-14 04:50:28 +00:00
356674f51f Add InstructionSetDto and update InstructionSetsController to use DTO for instruction set operations 2025-11-14 04:50:12 +00:00
cd4aa5767d Add CompanyLogoDto and update CompanyLogosController to use DTO for logo retrieval and creation 2025-11-14 04:49:39 +00:00
cf24356030 Add initial project structure and configuration files for Marechai.App 2025-11-14 01:50:56 +00:00
f85dc22bf6 Update target framework to net10.0 and adjust project properties 2025-11-14 01:50:32 +00:00
795f5ba27d Migrate solution file to XML format. 2025-11-14 01:50:03 +00:00
059417f0dc Update default values in MarechaiContext to use explicit boolean and enum types 2025-11-14 00:24:35 +00:00
5b4a1b42e0 Add migration to update annotations. 2025-11-14 00:17:47 +00:00
e9a2a68e49 Refactor MachinesController to improve route clarity by adding route parameters and updating service dependencies 2025-11-13 22:20:03 +00:00
7c29302153 Refactor controllers to improve route clarity and consistency by adding route parameters and updating method signatures 2025-11-13 22:17:59 +00:00
76bebc68b7 Refactor controllers to use route parameters for UpdateAsync methods and improve consistency in model retrieval 2025-11-13 21:31:49 +00:00
4a2d46f3b0 Refactor controllers to use [FromBody] attribute for DTO parameters in UpdateAsync methods 2025-11-13 21:24:43 +00:00
e9da9c7a3f Refactor controllers to use [FromBody] attribute for DTO parameters and update return types for consistency 2025-11-13 21:22:49 +00:00
b81c628f07 Refactor CurrencyPeggingController routes and method signatures for improved clarity and consistency 2025-11-13 21:04:43 +00:00
6fc709a271 Refactor CurrencyInflationController routes and method signatures for improved clarity and consistency 2025-11-13 21:03:14 +00:00
507e5686e4 Refactor ConsolesController routes and method signatures for improved clarity and consistency 2025-11-13 21:01:22 +00:00
27e5616da8 Refactor ComputersController routes and method signatures for improved clarity and consistency 2025-11-13 21:00:36 +00:00
464f52878b Refactor CompanyLogosController routes and method signatures for improved clarity and consistency 2025-11-13 20:57:06 +00:00
f6214e6d14 Refactor CompaniesController routes and method signatures for improved clarity and consistency 2025-11-13 20:51:05 +00:00
baaf571505 Refactor CompaniesBySoftwareVersionController routes and method signatures for improved clarity and consistency 2025-11-13 20:46:34 +00:00
8d6c382754 Refactor CompaniesBySoftwareVariantController routes and method signatures for improved clarity and consistency 2025-11-13 20:44:57 +00:00
e171b8ddd8 Refactor CompaniesBySoftwareFamilyController routes and method signatures for improved clarity and consistency 2025-11-13 20:38:59 +00:00
6981075c4a Refactor CompaniesByMagazineController routes and method signatures for improved clarity and consistency 2025-11-13 20:37:16 +00:00
bed72102e8 Refactor CompaniesByDocumentController routes and method signatures for improved clarity and consistency 2025-11-13 20:35:47 +00:00
d301315fbf Refactor CompaniesByBookController routes and method signatures for improved clarity and consistency 2025-11-13 20:33:41 +00:00
79f0d2632b Refactor BooksController routes and method signatures for improved clarity and consistency 2025-11-13 20:22:33 +00:00
0ba1a24b4e Refactor BookScansController routes and method signatures for improved clarity and consistency 2025-11-13 20:19:02 +00:00
583f20ff99 Add 401 response type to Delete and Create actions in multiple controllers for improved security handling 2025-11-13 19:28:21 +00:00
1826c70883 Add 404 response type to Delete and Update actions in multiple controllers for improved error handling 2025-11-13 19:10:08 +00:00
34d76fd646 Refactor BookScansController and Iso31661NumericController methods to use synchronous Task return types for improved readability 2025-11-13 19:00:38 +00:00
fc6238aef1 Refactor controller methods to return ActionResult for better error handling 2025-11-13 18:52:45 +00:00
505ace535f Remove unnecessary localization dependencies from CompaniesController and NewsController constructors 2025-11-13 18:28:14 +00:00
a715d936eb Refactor controller methods to use synchronous Task return types for improved readability 2025-11-13 18:27:00 +00:00
e4c2837ad9 Automatically convert services into controllers. 2025-11-13 18:22:44 +00:00
36520596da Add BooksByMachineController with CRUD operations for managing book-machine associations 2025-11-13 17:34:47 +00:00
764e058f79 Add BooksByMachineFamilyController with CRUD operations 2025-11-13 17:29:30 +00:00
349b396588 Add authentication controller. 2025-11-13 17:04:00 +00:00
d0e5725ae0 Update DTO properties to support nullable types and add required validation attributes 2025-11-13 16:46:56 +00:00
0bbf821489 Add JsonPropertyName attributes to DTO properties for serialization 2025-11-13 16:19:14 +00:00
10017850f8 Refactor viewmodels to use DTOs instead of viewmodels 2025-11-13 15:21:11 +00:00
d9239f39c0 Refactor viewmodels to use DTOs instead of viewmodels 2025-11-13 15:21:10 +00:00
f445006e46 Refactor viewmodels to use new namespace structure 2025-11-13 15:16:41 +00:00
3520e49b25 Move viewmodels to different folders.
Yes, separating big renames into smaller commits.
2025-11-13 15:08:01 +00:00
268c8fab07 Move viewmodels to separate library. 2025-11-13 15:07:20 +00:00
e9e3ef2ab0 Move viewmodels to separate library. 2025-11-13 15:07:11 +00:00
6962be93a4 Add token authentication. 2025-11-13 14:36:14 +00:00
be83594ea9 Configure controllers. 2025-11-13 14:27:00 +00:00
b1f32e6f13 Moved startup code to server. 2025-11-13 05:16:58 +00:00
c73c4d839a Moved startup code to server. 2025-11-13 05:16:49 +00:00
d7c61b2fdd Copy appsettings to server. 2025-11-13 04:56:47 +00:00
a942d40849 Add server skeleton. 2025-11-13 04:55:13 +00:00
6c01b2128f Update Aaru.CommonTypes to 5.4.1. 2025-11-13 04:26:47 +00:00
e14bfba354 Move to centralized package management. 2025-11-13 04:25:11 +00:00
d2c71d350d Add Directory.Build.props file. 2025-11-13 04:15:47 +00:00
eb7c0a6858 Remove copilot upgrade assessment file. 2025-11-13 04:07:05 +00:00
8f6d334af4 Major refactor and cleanup. 2025-11-13 04:05:35 +00:00
1d67081792 fix: Update CurrenciesPegging and CurrenciesInflation columns to NOT NULL with foreign key constraints 2025-11-13 03:10:14 +00:00
f304448fdb fix: Ensure correct charset and collation for foreign key compatibility in BookScans migration 2025-11-13 03:10:02 +00:00
88307edc9a fix: Make migration for IssueNumber column idempotent in MagazineIssues table 2025-11-13 03:09:48 +00:00
51d0809536 fix: Update foreign key constraints handling in migration for CurrenciesPegging and CurrenciesInflation 2025-11-13 03:09:36 +00:00
8e07b6587b Update Pomelo.EntityFrameworkCore.MySql to version 9.0.0 2025-11-13 03:08:48 +00:00
4f2435fcbd Update connection string. 2025-11-13 03:08:14 +00:00
3fac917422 docs: Update migration progress - all tasks completed successfully 2025-11-13 02:02:50 +00:00
096865dc3b chore: Complete .NET modernization - upgrade to .NET 9, update dependencies, fix security vulnerabilities, and implement local credential encryption
Changes:
- Upgrade both projects from .NET 5.0 to .NET 9.0
- Update Entity Framework Core packages to 9.0.11
- Update SkiaSharp to 3.119.1 (fixes CVE security vulnerability)
- Remove deprecated Microsoft.ApplicationInsights.AspNetCore
- Implement local credential encryption using Data Protection API
- Add CredentialEncryptor helper for DPAPI integration
- Add ConnectionStringManager for secure connection string handling
- Update Startup.cs to register credential encryption services
- Remove Application Insights configuration from _Host.cshtml

All changes maintain backward compatibility with existing plaintext credentials
while providing optional encryption for production deployments.
2025-11-13 02:02:14 +00:00
29ec7571fe Pre-upgrade checkpoint: Save current state before .NET 5 to .NET 8 migration 2025-11-13 01:40:51 +00:00
fc893ee08b Added more items to ignore 2025-11-13 01:36:52 +00:00
cf7f830aad Updated .editorconfig. 2025-11-13 01:22:01 +00:00
8213e4ad80 Updated Rider project files. 2025-11-13 01:21:28 +00:00
e974a77e46 Add security headers middleware. 2025-11-13 01:20:40 +00:00
1426 changed files with 768094 additions and 76501 deletions

View 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"
}
}

File diff suppressed because one or more lines are too long

View 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

View 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

File diff suppressed because it is too large Load Diff

2
.gitignore vendored
View File

@@ -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

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ContentModelUserStore">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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
View 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
View File

@@ -0,0 +1 @@
<Project></Project>

37
Directory.Packages.props Normal file
View 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>

Binary file not shown.

23
Marechai.App/App.xaml Normal file
View 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>

202
Marechai.App/App.xaml.cs Normal file
View File

@@ -0,0 +1,202 @@
using System.Net.Http;
using Marechai.App.Presentation.ViewModels;
using Marechai.App.Presentation.Views;
using Marechai.App.Services;
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<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>())
])
]));
}
}

View 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

View 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

View 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 |

View 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

View 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;

View 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;
}
}
}

View File

@@ -0,0 +1,82 @@
<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;
</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>

View File

@@ -0,0 +1,6 @@
namespace Marechai.App.Models;
public record AppConfig
{
public string? Environment { get; init; }
}

View File

@@ -0,0 +1,3 @@
namespace Marechai.App.Models;
public record Entity(string Name);

View 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>

View 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>

View 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");

View 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)
{
}
}

View 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);
}
}

View 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.

View File

@@ -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>

View 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>

View 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

View 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();
}
}

View 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>

View 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();
}
}

View 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;
}

View File

@@ -0,0 +1,3 @@
var UnoAppManifest = {
displayName: "Marechai.App"
}

View 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": "/"
}

View File

@@ -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"
}
}
]
}

View 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>

View 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>

View 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>

View 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();
}
}

View File

@@ -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": ""
}
}

View 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>

View 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>

View File

@@ -0,0 +1,11 @@
using Microsoft.UI.Xaml.Controls;
namespace Marechai.App.Presentation.Components;
public sealed partial class Sidebar : UserControl
{
public Sidebar()
{
InitializeComponent();
}
}

View 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();
}

View File

@@ -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();
}

View 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();
}

View File

@@ -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; }
}

View File

@@ -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; }
}

View 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;
}

View File

@@ -0,0 +1,373 @@
#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 Marechai.Data;
using Uno.Extensions.Navigation;
namespace Marechai.App.Presentation.ViewModels;
public partial class CompanyDetailViewModel : ObservableObject
{
private readonly CompanyDetailService _companyDetailService;
private readonly IStringLocalizer _localizer;
private readonly ILogger<CompanyDetailViewModel> _logger;
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 bool _hasError;
[ObservableProperty]
private bool _isDataLoaded;
[ObservableProperty]
private bool _isLoading;
[ObservableProperty]
private CompanyDto? _soldToCompany;
public CompanyDetailViewModel(CompanyDetailService companyDetailService, IStringLocalizer localizer,
ILogger<CompanyDetailViewModel> logger, INavigator navigator)
{
_companyDetailService = companyDetailService;
_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;
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 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;
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 sold-to company if applicable
if(Company.SoldToId != null)
{
int soldToId = UntypedNodeExtractor.ExtractInt(Company.SoldToId);
if(soldToId > 0) SoldToCompany = await _companyDetailService.GetSoldToCompanyAsync(soldToId);
}
// 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;
}

View 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;
}

View 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;
}
}
}

View 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;
}

View 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;
}
}
}

View 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;
}

View 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 ?? ""));
}
}

View 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;
}
}
}

View File

@@ -0,0 +1,3 @@
namespace Marechai.App.Presentation.ViewModels;
public record SecondViewModel(Entity Entity) {}

View 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
}

View 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>

View 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
}
}

View File

@@ -0,0 +1,273 @@
<?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}">
<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">
<!-- 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"
Spacing="8">
<TextBlock Text="Country"
FontSize="14"
FontWeight="SemiBold"
Foreground="{ThemeResource SystemBaseMediumColor}" />
<TextBlock Text="{Binding Company.Country}"
FontSize="14" />
</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>

View 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);
}
}
}

View 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="&#xE72B;"
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="&#xE731;"
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="&#xE787;"
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="&#xE72A;"
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>

View 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);
}
}
}

View 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>

View 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);
}
}
}

View 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="&#xE72B;"
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="&#xE731;"
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="&#xE787;"
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="&#xE72A;"
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>

View 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);
}
}
}

View 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>

View 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);
}
}
}

View 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="&#xE72B;"
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>

View 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);
}
}
}

View 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>

View 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;
}
}
}

View 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>

View 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.
}
}
}

View 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>

View File

@@ -0,0 +1,11 @@
using Microsoft.UI.Xaml.Controls;
namespace Marechai.App.Presentation.Views;
public sealed partial class SecondPage : Page
{
public SecondPage()
{
InitializeComponent();
}
}

View 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>

View 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;
}

View 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

View 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

View File

@@ -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

View 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&lt;global::Marechai.App.Models.BookDto&gt;</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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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&lt;global::Marechai.App.Models.CompanyByBookDto&gt;</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

View File

@@ -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&lt;global::Marechai.App.Models.BookByMachineFamilyDto&gt;</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

View File

@@ -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&lt;global::Marechai.App.Models.BookByMachineDto&gt;</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

View File

@@ -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&lt;global::Marechai.App.Models.PersonByBookDto&gt;</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

View File

@@ -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.Scans
{
/// <summary>
/// Builds and executes requests for operations under \books\{book-id}\scans
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class ScansRequestBuilder : BaseRequestBuilder
{
/// <summary>
/// Instantiates a new <see cref="global::Marechai.App.Books.Item.Scans.ScansRequestBuilder"/> 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 ScansRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/books/{book%2Did}/scans", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.App.Books.Item.Scans.ScansRequestBuilder"/> 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 ScansRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/books/{book%2Did}/scans", rawUrl)
{
}
/// <returns>A List&lt;Guid&gt;</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<Guid?>?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<List<Guid?>> 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.SendPrimitiveCollectionAsync<Guid?>(requestInfo, 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.Scans.ScansRequestBuilder"/></returns>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
public global::Marechai.App.Books.Item.Scans.ScansRequestBuilder WithUrl(string rawUrl)
{
return new global::Marechai.App.Books.Item.Scans.ScansRequestBuilder(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 ScansRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
}
}
#pragma warning restore CS0618

View File

@@ -0,0 +1,91 @@
// <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.Synopsis
{
/// <summary>
/// Builds and executes requests for operations under \books\{book-id}\synopsis
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class SynopsisRequestBuilder : BaseRequestBuilder
{
/// <summary>
/// Instantiates a new <see cref="global::Marechai.App.Books.Item.Synopsis.SynopsisRequestBuilder"/> 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 SynopsisRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/books/{book%2Did}/synopsis", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.App.Books.Item.Synopsis.SynopsisRequestBuilder"/> 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 SynopsisRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/books/{book%2Did}/synopsis", 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>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<string?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<string> 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.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 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.Synopsis.SynopsisRequestBuilder"/></returns>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
public global::Marechai.App.Books.Item.Synopsis.SynopsisRequestBuilder WithUrl(string rawUrl)
{
return new global::Marechai.App.Books.Item.Synopsis.SynopsisRequestBuilder(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 SynopsisRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
}
}
#pragma warning restore CS0618

Some files were not shown because too many files have changed in this diff Show More