Files
Sharepoint-Toolbox/.planning/phases/13-user-directory-viewmodel/13-RESEARCH.md
Dev df6f4949a8 docs(13-02): complete User Directory ViewModel plan
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:44:56 +02:00

74 lines
3.4 KiB
Markdown

# Phase 13 Research: User Directory ViewModel
## What Exists
### GraphUserDirectoryService (Phase 10)
- `GetUsersAsync(clientId, progress?, ct)``IReadOnlyList<GraphDirectoryUser>`
- Filter: `accountEnabled eq true and userType eq 'Member'` (members only)
- Select: displayName, userPrincipalName, mail, department, jobTitle
- Uses `PageIterator<User, UserCollectionResponse>` for transparent pagination
- Reports progress via `IProgress<int>` (running count)
- Honors cancellation in page callback
### GraphDirectoryUser Model
```csharp
public record GraphDirectoryUser(
string DisplayName, string UserPrincipalName,
string? Mail, string? Department, string? JobTitle);
```
**GAP**: No `UserType` property — needed for SC3 member/guest in-memory filtering.
### UserAccessAuditViewModel (Phase 7)
- Inherits `FeatureViewModelBase` (IsRunning, StatusMessage, ProgressValue, RunCommand, CancelCommand)
- People-picker search: `SearchQuery` → debounce → `IGraphUserSearchService.SearchUsersAsync``SearchResults`
- User selection: `SelectedUsers` (ObservableCollection<GraphUserResult>) → `RunOperationAsync` → audit
- Results: `Results` (ObservableCollection<UserAccessEntry>) + `ResultsView` (ICollectionView with grouping/filtering)
- Two constructors: full (DI) and test (omits export services)
- `_currentProfile` tracks active tenant (via TenantSwitchedMessage)
- `OnTenantSwitched` clears all state
### ICollectionView Pattern (existing in same ViewModel)
```csharp
var cvs = new CollectionViewSource { Source = Results };
ResultsView = cvs.View;
ResultsView.GroupDescriptions.Add(new PropertyGroupDescription(...));
ResultsView.Filter = FilterPredicate;
// On filter change: ResultsView.Refresh();
```
### DI Registration
- `IGraphUserDirectoryService` registered as Transient
- `UserAccessAuditViewModel` registered as Transient
- Currently NOT injected into UserAccessAuditViewModel
## Gaps to Fill
1. **GraphDirectoryUser needs UserType** — add `string? UserType` to record + update MapUser + select
2. **Service needs guest inclusion** — add `bool includeGuests` parameter; when true, drop userType filter
3. **ViewModel needs IGraphUserDirectoryService** — add to both constructors
4. **ViewModel needs browse mode** — mode toggle, directory collection, load command, cancel, filter, sort
5. **DI registration** — add IGraphUserDirectoryService to UserAccessAuditViewModel constructor resolution
## Plan Breakdown
1. **13-01** (Wave 1): Extend GraphDirectoryUser + GraphUserDirectoryService
- Add UserType to model
- Add userType to select fields
- Add `includeGuests` parameter (default false for backward compat)
- Update MapUser
- Update tests
2. **13-02** (Wave 2): UserAccessAuditViewModel directory browse mode
- Inject IGraphUserDirectoryService
- Add AuditMode enum (Search/Browse) + IsBrowseMode toggle
- Add DirectoryUsers collection + DirectoryUsersView (ICollectionView)
- Add LoadDirectoryCommand with own CTS, progress reporting
- Add CancelDirectoryLoadCommand
- Add IncludeGuests toggle + in-memory filter by UserType
- Add DirectoryFilterText + filter predicate (DisplayName, UPN, Department, JobTitle)
- Add SortDescription defaults (DisplayName ascending)
- Add DirectoryLoadStatus string for "Loading... X users" display
- Update OnTenantSwitched to clear directory state
- Update DI in App.xaml.cs
- Comprehensive tests