--- phase: 19-app-registration-removal plan: "02" subsystem: ViewModels/UI tags: [app-registration, viewmodel, xaml, localization, unit-tests] dependency_graph: requires: [19-01] provides: [register-app-ui, remove-app-ui, fallback-instructions-panel] affects: [ProfileManagementViewModel, ProfileManagementDialog, App.xaml.cs] tech_stack: added: [] patterns: [IAsyncRelayCommand, ObservableProperty, BooleanToVisibilityConverter, TranslationSource] key_files: created: - SharepointToolbox.Tests/ViewModels/ProfileManagementViewModelRegistrationTests.cs modified: - SharepointToolbox/ViewModels/ProfileManagementViewModel.cs - SharepointToolbox/Views/Dialogs/ProfileManagementDialog.xaml - SharepointToolbox/Localization/Strings.resx - SharepointToolbox/Localization/Strings.fr.resx - SharepointToolbox/App.xaml.cs - SharepointToolbox.Tests/ViewModels/ProfileManagementViewModelLogoTests.cs decisions: - ProfileManagementViewModel constructor gains IAppRegistrationService as last param — existing logo tests updated to 5-param - RegisterAppAsync/RemoveAppAsync use CancellationToken from IAsyncRelayCommand overload - TranslationSource.Instance used directly in ViewModel for status strings (consistent with runtime locale switching) - BooleanToVisibilityConverter declared in Window.Resources (WPF built-in, no custom converter needed) metrics: duration: ~5 minutes completed_date: "2026-04-09" tasks_completed: 2 files_modified: 7 --- # Phase 19 Plan 02: Register/Remove App UI Summary Register and Remove app commands wired into ProfileManagementViewModel with fallback instructions panel, DI registration, EN/FR localization, and 7 passing unit tests. ## Tasks Completed | # | Task | Commit | Status | |---|------|--------|--------| | 1 | ViewModel commands + DI + Localization | 42b5eda | Done | | 2 | Profile dialog XAML + ViewModel tests | 809ac86 | Done | ## What Was Built **ProfileManagementViewModel** gained: - `IAppRegistrationService` constructor injection - `RegisterAppCommand` / `RemoveAppCommand` (IAsyncRelayCommand) - `IsRegistering`, `ShowFallbackInstructions`, `RegistrationStatus` observable properties - `HasRegisteredApp` computed property - `CanRegisterApp` / `CanRemoveApp` guards (profile selected, AppId null/non-null, not busy) - `RegisterAppAsync`: admin check → fallback panel or full registration → AppId persistence - `RemoveAppAsync`: app removal + MSAL clear + AppId null + persistence - `OnIsRegisteringChanged` partial: notifies both commands on busy state change **ProfileManagementDialog.xaml**: - Height 750 (was 620) - New Row 4: Register/Remove buttons, RegistrationStatus TextBlock, fallback instructions Border (6 steps) - `BooleanToVisibilityConverter` added to `Window.Resources` - Buttons row shifted from Row 4 to Row 5 **Localization**: 16 new keys in both Strings.resx and Strings.fr.resx (register/remove/fallback flow, all accented FR characters). **App.xaml.cs**: `IAppRegistrationService` registered as singleton. **Tests**: 7 unit tests, all passing — CanExecute guards, fallback on non-admin, AppId set on success, AppId cleared on remove. ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 2 - Missing critical update] Updated ProfileManagementViewModelLogoTests to 5-param constructor** - **Found during:** Task 2 - **Issue:** Existing logo tests used the 4-param constructor which no longer exists after adding IAppRegistrationService - **Fix:** Added `Mock` field and passed `_mockAppReg.Object` as 5th param in all 3 constructor calls - **Files modified:** SharepointToolbox.Tests/ViewModels/ProfileManagementViewModelLogoTests.cs - **Commit:** 809ac86 ## Self-Check: PASSED All key files found. Both task commits verified (42b5eda, 809ac86). Full solution builds clean. 7/7 tests pass.