- Add 07-04-SUMMARY.md with task commits and decisions - Update STATE.md: progress 77%, session record, decisions - Update ROADMAP.md: phase 7 plan count updated to 5/8 summaries
104 lines
5.1 KiB
Markdown
104 lines
5.1 KiB
Markdown
---
|
|
phase: 07-user-access-audit
|
|
plan: 04
|
|
subsystem: viewmodel
|
|
tags: [viewmodel, wpf, people-picker, debounce, collectionview, grouping, filtering, export, mvvm]
|
|
|
|
requires:
|
|
- phase: 07-01
|
|
provides: [UserAccessEntry, AccessType, IUserAccessAuditService, IGraphUserSearchService, GraphUserResult]
|
|
- phase: 07-02
|
|
provides: [UserAccessAuditService]
|
|
- phase: 07-03
|
|
provides: [GraphUserSearchService]
|
|
- phase: 07-06
|
|
provides: [UserAccessCsvExportService, UserAccessHtmlExportService]
|
|
provides:
|
|
- UserAccessAuditViewModel with full orchestration of people picker, site selection, audit execution, grouping, filtering, summary banner, export
|
|
affects: [07-05, 07-07, 07-08]
|
|
|
|
tech-stack:
|
|
added: []
|
|
patterns: [CollectionViewSource grouping toggle, debounced CancellationTokenSource search, FeatureViewModelBase extension, dual-constructor pattern, _hasLocalSiteOverride site override]
|
|
|
|
key-files:
|
|
created:
|
|
- SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs
|
|
modified: []
|
|
|
|
key-decisions:
|
|
- "CollectionViewSource is created over Results in constructor; ApplyGrouping() clears and re-adds PropertyGroupDescription on IsGroupByUser toggle (UserLogin or SiteUrl)"
|
|
- "Debounced search uses _searchCts CancellationTokenSource cancelled on each SearchQuery change; Task.Delay(300, ct) pattern with OperationCanceledException swallowed"
|
|
- "OnResultsChanged partial rebuilds grouping/filter when Results collection reference is replaced after RunOperationAsync"
|
|
- "ExportCsvAsync calls WriteSingleFileAsync (combined single-file export) rather than WriteAsync (per-user directory) to match SaveFileDialog single-path UX"
|
|
|
|
patterns-established:
|
|
- "UserAccessAuditViewModel: same _hasLocalSiteOverride + OnGlobalSitesChanged guard as PermissionsViewModel"
|
|
- "Dual constructor: full DI constructor + internal test constructor omitting export services — both initialize all commands and wire collection events"
|
|
- "Summary properties (TotalAccessCount, SitesCount, HighPrivilegeCount) are computed getters calling Results LINQ — NotifySummaryProperties() triggers all three"
|
|
|
|
requirements-completed: [UACC-01, UACC-02]
|
|
|
|
duration: 2min
|
|
completed: 2026-04-07
|
|
---
|
|
|
|
# Phase 7 Plan 04: UserAccessAuditViewModel Summary
|
|
|
|
**UserAccessAuditViewModel wires people-picker (300ms debounced Graph search), multi-site selection with override guard, IUserAccessAuditService.AuditUsersAsync execution, CollectionViewSource group-by-user/site toggle with real-time filter, computed summary banner (TotalAccessCount, SitesCount, HighPrivilegeCount), and CSV/HTML export commands — zero-error build.**
|
|
|
|
## Performance
|
|
|
|
- **Duration:** ~2 min
|
|
- **Started:** 2026-04-07T10:42:51Z
|
|
- **Completed:** 2026-04-07T10:44:56Z
|
|
- **Tasks:** 1
|
|
- **Files modified:** 1
|
|
|
|
## Accomplishments
|
|
|
|
- UserAccessAuditViewModel.cs (~300 lines) extends FeatureViewModelBase and implements all 10 observable properties, 5 commands, CollectionViewSource grouping/filtering, and dual constructors
|
|
- Debounced people-picker: _searchCts cancelled/recreated on SearchQuery change, 300ms Task.Delay, IsSearching spinner, 2-char minimum guard consistent with GraphUserSearchService
|
|
- CollectionViewSource grouping: ApplyGrouping() swaps PropertyGroupDescription between UserLogin and SiteUrl; FilterPredicate applies to 6 fields case-insensitively
|
|
- Summary banner computed properties (TotalAccessCount, SitesCount, HighPrivilegeCount) notified via NotifySummaryProperties() after each RunOperationAsync and tenant switch
|
|
|
|
## Task Commits
|
|
|
|
1. **Task 1: Implement UserAccessAuditViewModel** - `3de737a` (feat)
|
|
|
|
**Plan metadata:** (docs commit pending)
|
|
|
|
## Files Created/Modified
|
|
|
|
- `SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs` — Full orchestration ViewModel for User Access Audit tab
|
|
|
|
## Decisions Made
|
|
|
|
1. **CollectionViewSource bound at construction** — ResultsView is created from a `new CollectionViewSource { Source = Results }` in the constructor. When Results is replaced by a new collection in RunOperationAsync, OnResultsChanged re-applies grouping and filter. This avoids ICollectionView rebinding complexity in XAML.
|
|
|
|
2. **WriteSingleFileAsync for CSV export** — UserAccessCsvExportService has two modes: WriteAsync (per-user files to directory) and WriteSingleFileAsync (combined). The ViewModel uses WriteSingleFileAsync since the SaveFileDialog returns a single file path — the per-directory mode is for batch export scenarios.
|
|
|
|
3. **SelectedUsers UPNs as login keys** — AuditUsersAsync receives `SelectedUsers.Select(u => u.UserPrincipalName)` as the targetUserLogins parameter, matching the UPN-based bidirectional matching in UserAccessAuditService.
|
|
|
|
## Deviations from Plan
|
|
|
|
None — plan executed exactly as written.
|
|
|
|
## Issues Encountered
|
|
|
|
None.
|
|
|
|
## User Setup Required
|
|
|
|
None - no external service configuration required.
|
|
|
|
## Next Phase Readiness
|
|
|
|
- UserAccessAuditViewModel ready for XAML binding in 07-05 (View)
|
|
- All observable properties, commands, and ResultsView ICollectionView available for DataGrid/ComboBox/AutoComplete binding
|
|
- Export commands wired to UserAccessCsvExportService.WriteSingleFileAsync and UserAccessHtmlExportService.WriteAsync
|
|
|
|
---
|
|
*Phase: 07-user-access-audit*
|
|
*Completed: 2026-04-07*
|