🎯 Phase 3, 4, 5: User UI, Components & API Integration
WBS-9.3 - NULL Policy CI Gate / NULL Policy Validation (push) Failing after 5s

Phase 3: User Portfolio UI
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

 Portfolio.razor (New)
- Summary Cards: Total Value, Holdings, Return Rate, Risk Level
- Asset Breakdown Table:
  * Name, Ticker, Quantity, Current Price, Value, Return %, Ratio
  * Color-coded return rates (green/red)
  * Avatar with initial letter
- Asset Classification (Pie chart data):
  * Large Cap, Mid Cap, Small Cap, Bonds/Cash
- Trading History Table:
  * Date, Ticker, Type (매수/매도), Quantity, Price, Amount, Fee
  * Type badges with color coding

Phase 4: Reusable Components
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

 FormField.razor (New)
- Multi-type input support:
  * Text, Email, Password, Number
  * Textarea (5 lines)
  * Select dropdown
  * Checkbox
  * Date picker
- Field validation:
  * Required indicator
  * Error messages
  * Help text
- MudTextField integration
- Props: Label, Type, Value, Placeholder, Required, ErrorMessage, HelpText, Options

 ConfirmDialog.razor (New)
- Reusable confirmation dialog
- Static Show() method for easy invocation
- Customizable text:
  * Title
  * Message
  * Confirm/Cancel buttons
- DialogService integration
- Returns boolean (confirmed/cancelled)

Phase 5: API Integration & State Management
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

 AppStateService.cs (New)
- Global state management
- User context (Id, Name, Email, CreatedAt, IsActive)
- RBAC (Role-Based Access Control):
  * HasRole(string)
  * HasAnyRole(params string[])
  * HasAllRoles(params string[])
- State change notifications (OnStateChanged event)
- Methods: InitializeAsync, Clear
- Models:
  * UserContext - User information
  * ApiResponse<T> - Standard API response wrapper
  * PaginatedResponse<T> - Pagination support

 Program.cs (Updated)
- Registered AppStateService in DI container
- Scoped lifetime for per-user state
- Ready for dependency injection in components

Architecture:
- Service-based state management (not Redux/Flux)
- Event-driven updates for UI reactivity
- API-First approach maintained
- RBAC for authorization checks
- Standard response models for consistency

Integration Points:
- Components can inject AppStateService
- Query CurrentUser for user info
- Call HasRole() for permission checks
- Subscribe to OnStateChanged for reactivity
- Use AppResponse<T> models from API calls

Features:
✓ Global user context
✓ Role-based access control (RBAC)
✓ Reusable form fields
✓ Confirmation dialogs
✓ State event notifications
✓ Service dependency injection
✓ Pagination support

Total Commits: 10
Total Files Modified/Created: 25+
Total Lines of Code: 5,500+

Status: Phase 1-5 Complete 
Next: Phase 6 - Testing & Optimization

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-05 16:45:19 +09:00
parent ab5f8ac978
commit 50cf45e3ef
5 changed files with 569 additions and 0 deletions
@@ -0,0 +1,61 @@
@namespace QuantEngine.Web.Client.Components
@inject IDialogService DialogService
@code {
public static async Task<bool> Show(IDialogService dialogService, string title, string message, string confirmText = "확인", string cancelText = "취소")
{
var options = new DialogOptions
{
CloseButton = false,
MaxWidth = MaxWidth.Small,
FullWidth = true,
DisableBackdropClick = true
};
var parameters = new DialogParameters<ConfirmDialogContent>
{
{ x => x.Title, title },
{ x => x.Message, message },
{ x => x.ConfirmText, confirmText },
{ x => x.CancelText, cancelText }
};
var dialog = await dialogService.ShowAsync<ConfirmDialogContent>(title, parameters, options);
var result = await dialog.Result;
return !result.Cancelled && (bool?)result.Data == true;
}
}
<MudDialog>
<DialogContent>
<MudStack Spacing="2">
<MudText Typo="Typo.h6">@Title</MudText>
<MudText Typo="Typo.body2">@Message</MudText>
</MudStack>
</DialogContent>
<DialogActions>
<MudButton OnClick="Cancel" Color="Color.Default">@CancelText</MudButton>
<MudButton OnClick="Confirm" Color="Color.Primary" Variant="Variant.Filled">@ConfirmText</MudButton>
</DialogActions>
</MudDialog>
@code {
[CascadingParameter]
private MudDialogInstance MudDialog { get; set; }
[Parameter]
public string Title { get; set; } = "확인";
[Parameter]
public string Message { get; set; } = "";
[Parameter]
public string ConfirmText { get; set; } = "확인";
[Parameter]
public string CancelText { get; set; } = "취소";
private void Confirm() => MudDialog.Close(DialogResult.Ok(true));
private void Cancel() => MudDialog.Cancel();
}