# 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
