diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index cc632cd..3ef7685 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,67 +1,450 @@ # GitHub Copilot Instructions — Fixiy +## Project Overview + +Fixiy is a MAUI Blazor Hybrid application with a shared codebase. + +### Solution Structure + +* `Fixiy.Shared` contains: + + * Business logic + * Razor pages + * Shared components + * DTOs + * Interfaces + * Service abstractions + +* `Fixiy.Maui` is the MAUI host application. + +* `Fixiy.Web` is the ASP.NET Core host application. + +The Shared project is the source of truth. Avoid duplicating logic between hosts. + +--- + +## Agent Behaviour + +* Before making changes, inspect the existing implementation and follow established patterns. +* Prefer extending existing functionality over creating new implementations. +* Make the smallest change necessary to satisfy the requirement. +* Never rewrite large portions of working code unless explicitly requested. +* Search for existing services, DTOs, components, pages and interfaces before creating new ones. +* Never create duplicate functionality. +* Do not rename public classes, interfaces, routes, pages or services without verifying all usages. +* When requirements are unclear, ask for clarification instead of making assumptions. +* Preserve the existing architecture. +* Do not move files between projects unless explicitly requested. +* Do not generate placeholder implementations containing TODO comments unless specifically requested. +* Always check whether a change affects both MAUI and Web hosts. + +--- + ## Language and Style -- **C# 12+** with `enable` and `enable` throughout. Never disable these per-file without strong justification. -- Use **file-scoped namespaces** (`namespace Fixiy.Shared.Services;`) not block-scoped. -- Prefer `record` types for immutable DTOs. -- Use `async`/`await` consistently — never `.Result` or `.Wait()` on Tasks. -- Remove unused `using` directives. +* Use C# 12+. +* Always keep: + +```xml +enable +enable +``` + +* Never disable nullable reference types without strong justification. +* Use file-scoped namespaces: + +```csharp +namespace Fixiy.Shared.Services; +``` + +* Never use block-scoped namespaces. +* Prefer `record` types for immutable DTOs. +* Use expression-bodied members when they improve readability. +* Use collection expressions when appropriate. +* Use `async`/`await` consistently. +* Never use `.Result`, `.Wait()` or `.GetAwaiter().GetResult()`. +* Remove unused `using` directives. +* One type per file. +* File names must match type names. +* Constants use PascalCase. +* Do not use Hungarian notation. +* Omit the `private` modifier on fields. + +--- ## Blazor Component Conventions -- All shared components live in `Fixiy.Shared/Components/`. Do not create components in `Fixiy.Maui` or `Fixiy.Web`. -- Pages go in `Fixiy.Shared/Components/Pages/`, layout in `Layout/`, reusable elements in `SingleElements/`. -- Scoped CSS goes in a matching `.razor.css` file alongside the component. -- Inject services via `@inject` in `.razor` files — Blazor components do not support constructor injection. -- Prefer `[Parameter]` for component inputs; avoid cascading parameters unless genuinely needed across deep trees. +### Component Locations + +Shared components must live inside: + +```text +Fixiy.Shared/Components/ +``` + +Never create reusable components in: + +```text +Fixiy.Maui +Fixiy.Web +``` + +### Structure + +Pages: + +```text +Fixiy.Shared/Components/Pages/ +``` + +Layouts: + +```text +Fixiy.Shared/Components/Layout/ +``` + +Reusable elements: + +```text +Fixiy.Shared/Components/SingleElements/ +``` + +### Styling + +* Scoped styles must use matching `.razor.css` files. +* Keep component-specific styling inside scoped CSS whenever possible. + +### Dependency Injection + +Use: + +```razor +@inject IService Service +``` + +Do not attempt constructor injection in Razor components. + +### Parameters + +* Prefer `[Parameter]`. +* Avoid `CascadingParameter` unless truly necessary across multiple levels. + +### Code Organization + +* Keep `@code` blocks small. +* Extract complex logic into: + + * `.razor.cs` + * Service classes + * ViewModels + +--- ## Render Mode Rules -- **Always use `InteractiveRenderSettings.*` properties** (`@InteractiveServer`, `@InteractiveAuto`, `@InteractiveWebAssembly`) in shared components — never hard-coded `RenderMode.*` constants. -- `Fixiy.Maui/MauiProgram.cs` calls `ConfigureBlazorHybridRenderModes()` which sets all three properties to `null`. This is intentional — MAUI Blazor Hybrid runs components statically. -- Do not work around the null render modes in shared code. Components that need interactivity must tolerate `null` render mode in MAUI context. +Always use: + +```razor +@InteractiveServer +@InteractiveAuto +@InteractiveWebAssembly +``` + +through: + +```csharp +InteractiveRenderSettings +``` + +Never use: + +```csharp +RenderMode.Server +RenderMode.Auto +RenderMode.WebAssembly +``` + +directly. + +### Important + +`Fixiy.Maui/MauiProgram.cs` calls: + +```csharp +ConfigureBlazorHybridRenderModes() +``` + +which intentionally sets render modes to `null`. + +This is required for MAUI Blazor Hybrid. + +Do not: + +* Replace null render modes +* Add workarounds +* Introduce MAUI-specific render mode hacks + +All shared components must tolerate null render modes. + +--- + +## MAUI Blazor Hybrid Rules + +* Navigation must remain compatible with iOS swipe-back gestures. + +* Prefer native MAUI navigation for secondary pages. + +* Use PushAsync for stack navigation. + +* Do not introduce JavaScript solutions when a MAUI or Blazor solution exists. + +* Shared components must work identically in: + + * MAUI Hybrid + * ASP.NET Core Server + * WebAssembly + +* Never introduce host-specific code into shared components. + +--- + +## MVVM Guidelines + +* Business logic must not live inside Razor pages. +* Complex state management belongs in services or ViewModels. +* UI components should focus on presentation. +* Keep ViewModels platform-independent. +* Avoid code duplication between ViewModels and services. + +--- ## IFormFactor Platform Abstraction -- Platform-specific device info must be accessed via `IFormFactor` injection (`Fixiy.Shared/Interfaces/IFormFactor.cs`). -- **Never call `DeviceInfo`, `Connectivity`, `FileSystem`, or any `Microsoft.Maui.*` API from `Fixiy.Shared`.** -- When adding new platform capabilities: define interface in `Fixiy.Shared/Interfaces/` → implement in both `Fixiy.Maui/Services/` and `Fixiy.Web/Services/` → register in both DI roots. +Platform-specific functionality must be accessed through: + +```csharp +IFormFactor +``` + +located in: + +```text +Fixiy.Shared/Interfaces/ +``` + +### Forbidden in Shared + +Never directly access: + +```csharp +DeviceInfo +Connectivity +FileSystem +Permissions +Launcher +Preferences +SecureStorage +Microsoft.Maui.* +``` + +from: + +```text +Fixiy.Shared +``` + +### Adding New Platform Features + +Always follow this process: + +1. Create interface in: + +```text +Fixiy.Shared/Interfaces/ +``` + +2. Implement in: + +```text +Fixiy.Maui/Services/ +``` + +3. Implement in: + +```text +Fixiy.Web/Services/ +``` + +4. Register in both DI roots. + +Do not skip any step. + +--- + +## Dependency Injection + +### Service Registration + +Always verify registrations in both hosts. + +### Lifetimes + +#### MAUI + +Use: + +```csharp +Singleton +``` + +for platform services. + +#### Web + +Use: + +```csharp +Scoped +``` + +for platform services. + +### Interfaces + +Interfaces must not assume any specific lifetime. + +### Injection + +Prefer: + +```csharp +IService +``` + +over concrete implementations. + +Never inject a concrete type when an abstraction exists. + +--- ## IntegryApiClient Registration -- Register with the same `appToken` and `useLoginAzienda: true` in both hosts. -- MAUI: `.UseIntegry(appToken, useLoginAzienda: true)` on the `MauiAppBuilder` chain. -- Web: `builder.Services.UseIntegry(appToken, useLoginAzienda: true)` in `Program.cs`. -- One registration per DI root — do not call it twice. +Use the same configuration in both hosts. -## Service Registration Lifetimes +### MAUI -- `Fixiy.Maui` registers platform services as **`Singleton`** — the MAUI app has a single long-lived process. -- `Fixiy.Web` registers platform services as **`Scoped`** — ASP.NET Core server-side per-request scope. -- Interfaces must not assume a specific lifetime; implementations may. +```csharp +.UseIntegry(appToken, useLoginAzienda: true) +``` + +### Web + +```csharp +builder.Services.UseIntegry( + appToken, + useLoginAzienda: true) +``` + +### Rules + +* One registration per host. +* Never register twice. +* Always keep app tokens aligned. +* Always keep versions aligned. + +--- + +## Data Access Guidelines + +### Entity Framework + +* Prefer async APIs. +* Use `AsNoTracking()` for read-only queries. +* Avoid loading entire tables into memory. +* Avoid N+1 query patterns. +* Prefer projections over loading full entities when possible. + +### Performance + +* Minimize allocations. +* Reuse existing services. +* Avoid unnecessary LINQ enumerations. +* Prefer server-side filtering. + +--- + +## Error Handling + +* Use exceptions only for exceptional situations. +* Validate inputs early. +* Log meaningful information. +* Never swallow exceptions silently. +* Preserve stack traces. + +Do not write: + +```csharp +catch (Exception) +{ +} +``` + +--- ## Maintenance Matrix -| Change | Cascades to | -|--------|-------------| -| Add/modify `Fixiy.Shared/Interfaces/*.cs` | Both `Fixiy.Maui/Services/` and `Fixiy.Web/Services/` implementations + DI registration in both hosts | -| Modify `InteractiveRenderSettings.cs` | `MauiProgram.ConfigureBlazorHybridRenderModes()` — must null every new property | -| Add a new shared page | `Fixiy.Shared/Components/Pages/` → nav link in `NavMenu.razor` if user-accessible | -| Add a NuGet package | Target `.csproj` file; check private NuGet feed has it | -| Change IntegryApiClient version | All three `.csproj` files (`Fixiy.Maui`, `Fixiy.Shared`, `Fixiy.Web`) | -| Modify `_Imports.razor` | Verify no MAUI-only or web-only namespaces are introduced | -| Update app token | `Fixiy.Maui/MauiProgram.cs` `AppToken` const + `Fixiy.Web/Program.cs` `appToken` const | +| Change | Cascades To | +| ----------------------------------------- | ----------------------------------------------------------------- | +| Add/modify `Fixiy.Shared/Interfaces/*.cs` | Update MAUI implementation + Web implementation + DI registration | +| Modify `InteractiveRenderSettings.cs` | Update `ConfigureBlazorHybridRenderModes()` | +| Add shared page | Add navigation entry if user accessible | +| Add shared service | Verify registration in both hosts | +| Add platform capability | Interface + MAUI implementation + Web implementation | +| Add NuGet package | Verify target project and feed availability | +| Change IntegryApiClient version | Update all project references | +| Modify `_Imports.razor` | Ensure no host-specific namespaces are introduced | +| Update app token | Update both hosts | -## Test Conventions +--- -- No test projects are currently configured. When adding tests, use **xUnit** with **bUnit** for Razor component testing. -- Test projects should reference `Fixiy.Shared` and provide mock implementations of `IFormFactor`. -- Use `Moq` or `NSubstitute` for mocking; do not create manual stubs unless the interface is trivial. +## Testing Conventions -## Code Style Notes +Current solution has no dedicated test projects. -- Omit the `private` modifier on fields — it is the default in C#. -- Constants: `PascalCase` for class-level `const`, no Hungarian notation. -- One type per file; file name matches type name. -- Keep `@code` blocks in `.razor` files focused — extract complex logic to a `*.razor.cs` code-behind or a separate service class. +When adding tests: + +### Frameworks + +Use: + +* xUnit +* bUnit +* Moq or NSubstitute + +### Requirements + +* Test projects should reference `Fixiy.Shared`. +* Mock `IFormFactor`. +* Avoid manual stubs unless interfaces are trivial. + +--- + +## Code Review Checklist + +Before completing a task verify: + +* Existing patterns were followed. +* No duplicate services were created. +* No duplicate DTOs were created. +* No duplicate pages were created. +* No duplicate components were created. +* Shared code contains no MAUI-specific APIs. +* DI registrations are correct. +* Both hosts were updated when required. +* Nullable reference types remain enabled. +* Async APIs are used correctly. +* Render mode rules were respected. +* Navigation compatibility remains intact. +* No unnecessary files were added. +* No working code was rewritten unnecessarily. +* The solution architecture remains consistent.