# Web UI Specification Delta ## Purpose This document captures ADDED and MODIFIED requirements for the Blazor WebAssembly user interface specific to the .NET 10 migration. It supplements the base specification at `openspec/specs/web-ui/spec.md`. ## ADDED Requirements ### Requirement: Blazor WebAssembly Hosting Model The system SHALL use Blazor WebAssembly (WASM) as the client-side hosting model. #### Business Rules - The client application SHALL run entirely in the browser via WebAssembly - The application SHALL communicate with the server exclusively via HTTP APIs and SignalR - The application SHALL NOT use server-side Blazor (SignalR for DOM updates) - Initial load SHALL include the .NET runtime and application assemblies - Subsequent navigation SHALL NOT require page reloads #### Scenario: Initial application load - **WHEN** a user navigates to the application URL - **THEN** the browser downloads the .NET WASM runtime - **AND** the Blazor application initializes in the browser - **AND** subsequent interactions do not trigger server-side rendering --- ### Requirement: JWT Token Authentication The system SHALL use JWT tokens for API authentication stored in browser localStorage. #### Inputs - Login credentials (username, password) - LDAP authentication endpoint #### Outputs - JWT token stored in localStorage - Claims extracted for AuthenticationState #### Business Rules - Tokens SHALL be stored in browser localStorage via JS interop - Tokens SHALL be automatically attached to outgoing HTTP requests - Token expiration SHALL trigger re-authentication prompt - Logout SHALL remove token from localStorage and clear auth state #### Scenario: Login stores JWT token - **WHEN** user submits valid credentials - **THEN** the API returns a JWT token - **AND** the token is stored in localStorage under key "authToken" - **AND** AuthStateProvider parses claims from the token - **AND** subsequent API requests include Authorization header #### Scenario: Token expiration - **WHEN** a stored token has expired - **AND** the user attempts an API call - **THEN** the API returns 401 Unauthorized - **AND** the user is redirected to the login page --- ### Requirement: Custom AuthenticationStateProvider The system SHALL implement a custom AuthenticationStateProvider for JWT-based authentication state. #### Business Rules - AuthStateProvider SHALL parse JWT claims without server round-trip - Claims SHALL include username, roles, and expiration - State changes SHALL notify Blazor components via NotifyAuthenticationStateChanged - Invalid/expired tokens SHALL result in anonymous state #### Scenario: Parse claims from JWT - **WHEN** AuthStateProvider initializes with a stored token - **THEN** it parses the token payload (base64-decoded JSON) - **AND** extracts claims into ClaimsPrincipal - **AND** sets authentication type to "jwt" --- ### Requirement: SignalR Auto-Reconnect The system SHALL implement automatic reconnection for SignalR connections with exponential backoff. #### Reconnection Schedule | Attempt | Delay | |---------|-------| | 1 | 0 seconds | | 2 | 2 seconds | | 3 | 5 seconds | | 4 | 10 seconds | | 5 | 30 seconds | #### Business Rules - SignalR client SHALL use WithAutomaticReconnect configuration - Reconnection attempts SHALL follow exponential backoff schedule - UI SHALL indicate connection state during reconnection - Events received during reconnection SHALL be delivered after reconnect #### Scenario: Network interruption recovery - **WHEN** the SignalR connection is lost - **THEN** the client attempts reconnection per the backoff schedule - **AND** logs reconnection attempts to console - **AND** upon successful reconnection, resumes receiving events --- ### Requirement: File Download via JS Interop The system SHALL use JavaScript interop for triggering browser file downloads. #### Business Rules - Excel result files SHALL be downloaded via JS interop function - Template files SHALL be downloaded via direct URL navigation - Downloaded files SHALL prompt browser save dialog - File names SHALL be specified by the server response headers #### JavaScript Functions | Function | Purpose | |----------|---------| | `downloadFileFromStream` | Download file from DotNetStreamReference | | `downloadFileFromUrl` | Download file from URL with filename | #### Scenario: Download search results - **WHEN** user clicks Download Results button - **THEN** API request fetches file as stream - **AND** JS interop triggers browser download dialog - **AND** file is saved with name "Search_{id}_Results.xlsx" --- ### Requirement: Radzen Component Library Integration The system SHALL use Radzen Blazor (free tier) for UI components. #### Service Registration - DialogService SHALL be registered for confirmation dialogs - NotificationService SHALL be registered for toast notifications - RadzenComponents SHALL be registered via AddRadzenComponents() #### CSS and JavaScript - Radzen CSS SHALL be included in index.html - No additional Radzen JavaScript required for core components #### Scenario: Register Radzen services - **WHEN** the application starts - **THEN** DialogService is available for injection - **AND** NotificationService is available for injection - **AND** Radzen component styles are applied --- ### Requirement: Async-First Service Design The system SHALL use async methods for all service operations. #### Business Rules - All HTTP client calls SHALL use async methods (GetFromJsonAsync, PostAsJsonAsync) - All service interfaces SHALL return Task or Task - Cancellation tokens SHALL be accepted on all service methods - UI SHALL remain responsive during API calls #### Scenario: Async API call with loading state - **WHEN** user triggers a data load operation - **THEN** loading indicator displays immediately - **AND** API call executes asynchronously - **AND** UI updates when data arrives - **AND** loading indicator hides --- ### Requirement: ILogger Client-Side Logging The system SHALL use Microsoft.Extensions.Logging for client-side logging. #### Business Rules - All services SHALL accept ILogger via constructor injection - Log levels SHALL be configurable in Program.cs - Logs SHALL output to browser console in development - Error logs SHALL include exception details #### Log Levels by Category | Category | Minimum Level | |----------|---------------| | Default | Information | | Microsoft.AspNetCore | Warning | | SignalR | Debug (dev) / Information (prod) | --- ### Requirement: Same-Window Navigation The system SHALL use same-window navigation for all internal links (no popups or new tabs). #### Business Rules - All internal navigation SHALL use NavigationManager.NavigateTo - Search detail links SHALL NOT open new windows - Queue links SHALL NOT open new tabs - Only external links MAY open new tabs (if any exist) #### Scenario: Navigate to search detail - **WHEN** user clicks View button on search grid row - **THEN** NavigationManager.NavigateTo("/search/{id}") is called - **AND** current window navigates to search detail - **AND** no new window or tab opens --- ## MODIFIED Requirements ### Requirement: Clear Data Confirmation The system SHALL display confirmation dialogs before clearing filter data. #### Inputs - User clicks Clear Data button on any filter panel #### Outputs - Confirmation dialog with OK/Cancel buttons - Data cleared only if user confirms #### Business Rules - DialogService.Confirm SHALL be used for confirmation dialogs - Dialog title SHALL be "Confirm Clear" - Dialog message SHALL be "Are you sure you want to clear all items?" - Cancel SHALL leave data unchanged #### Scenario: Clear filter with confirmation - **WHEN** user clicks Clear Data button - **THEN** confirmation dialog appears - **AND** if user clicks OK, filter list is cleared - **AND** if user clicks Cancel, filter list is unchanged --- ### Requirement: Operator Filter Display The system SHALL display Operator filter entries with AddressNumber, UserID, and FullName properties. #### Display Format | Column | Property | Width | |--------|----------|-------| | Address Number | AddressNumber | 120px | | User ID | UserID | 100px | | Full Name | FullName | Auto | #### Business Rules - Autocomplete dropdown SHALL display all three properties - Format: "{AddressNumber} - {UserID} - {FullName}" - Grid SHALL display all three properties in separate columns #### Scenario: Select operator from autocomplete - **WHEN** user types in operator autocomplete - **THEN** dropdown shows results with format "12345 - JSMITH - John Smith" - **AND** selecting adds entry with all three properties to grid --- ### Requirement: Error Handling Without Custom Error Pages The system SHALL use Blazor's built-in error handling instead of custom error pages. #### Business Rules - ErrorBoundary component SHALL wrap page content in MainLayout - Unhandled exceptions SHALL display inline error message - Error recovery SHALL be automatic on next navigation - Development mode SHALL show exception details - Production mode SHALL show generic error message #### Scenario: Unhandled exception in component - **WHEN** an unhandled exception occurs in a Blazor component - **THEN** ErrorBoundary catches the exception - **AND** error content displays instead of the faulted component - **AND** other components remain functional - **AND** navigating away recovers the error boundary --- ## Migration Notes | Legacy Pattern | New Pattern | Status | |----------------|-------------|--------| | Kendo UI Grid | RadzenDataGrid | ADDED | | Kendo DatePicker | RadzenDatePicker | ADDED | | Kendo ComboBox | RadzenAutoComplete | ADDED | | jQuery AJAX | HttpClient | ADDED | | Forms Authentication | JWT with localStorage | MODIFIED | | SignalR (jQuery) | SignalR (.NET client) | ADDED | | New window navigation | Same-window navigation | MODIFIED | | Custom error pages | ErrorBoundary | MODIFIED | | Clear without confirm | Clear with DialogService.Confirm | MODIFIED | --- ## Open Questions None - all design decisions resolved per best practice recommendations.