Set up repository with legacy .NET Framework 4.8 source (OLD/), new .NET 10 Blazor solution (NEW/), OpenSpec specifications, documentation, and project configuration.
10 KiB
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.