docs(07): create phase plan - 8 plans across 5 waves
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
268
.planning/phases/07-user-access-audit/07-05-PLAN.md
Normal file
268
.planning/phases/07-user-access-audit/07-05-PLAN.md
Normal file
@@ -0,0 +1,268 @@
|
||||
---
|
||||
phase: 07-user-access-audit
|
||||
plan: 05
|
||||
type: execute
|
||||
wave: 4
|
||||
depends_on: ["07-04"]
|
||||
files_modified:
|
||||
- SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml
|
||||
- SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml.cs
|
||||
autonomous: true
|
||||
requirements:
|
||||
- UACC-01
|
||||
- UACC-02
|
||||
must_haves:
|
||||
truths:
|
||||
- "View has left panel with people picker (TextBox + autocomplete Popup), site picker button, scan options, run/cancel/export buttons"
|
||||
- "View has right panel with summary banner (total accesses, sites, high-privilege) and DataGrid"
|
||||
- "DataGrid columns: User, Site, Object, Permission Level, Access Type, Granted Through"
|
||||
- "Access type rows are color-coded: Direct (blue tint), Group (green tint), Inherited (gray tint)"
|
||||
- "High-privilege entries show warning icon, external users show guest badge"
|
||||
- "Group-by toggle switches DataGrid GroupStyle between user and site"
|
||||
- "Filter TextBox filters results in real-time"
|
||||
- "People picker shows autocomplete Popup with search results below the search TextBox"
|
||||
artifacts:
|
||||
- path: "SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml"
|
||||
provides: "XAML layout for User Access Audit tab"
|
||||
contains: "UserAccessAuditView"
|
||||
- path: "SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml.cs"
|
||||
provides: "Code-behind for dialog factory wiring"
|
||||
contains: "UserAccessAuditView"
|
||||
key_links:
|
||||
- from: "SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml"
|
||||
to: "SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs"
|
||||
via: "DataContext binding to ViewModel properties"
|
||||
pattern: "Binding"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Create the XAML view for the User Access Audit tab with people picker autocomplete, site picker, scan options, summary banner, color-coded DataGrid with grouping, filter, and export buttons.
|
||||
|
||||
Purpose: The visual interface for the audit feature. Follows the established PermissionsView two-panel layout pattern.
|
||||
Output: UserAccessAuditView.xaml + UserAccessAuditView.xaml.cs
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@C:/Users/dev/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@C:/Users/dev/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/phases/07-user-access-audit/07-CONTEXT.md
|
||||
@.planning/phases/07-user-access-audit/07-04-SUMMARY.md
|
||||
|
||||
<interfaces>
|
||||
<!-- ViewModel properties the View binds to -->
|
||||
From SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs (expected):
|
||||
```csharp
|
||||
// People picker
|
||||
[ObservableProperty] string SearchQuery;
|
||||
[ObservableProperty] ObservableCollection<GraphUserResult> SearchResults;
|
||||
[ObservableProperty] ObservableCollection<GraphUserResult> SelectedUsers;
|
||||
[ObservableProperty] bool IsSearching;
|
||||
RelayCommand<GraphUserResult> AddUserCommand;
|
||||
RelayCommand<GraphUserResult> RemoveUserCommand;
|
||||
string SelectedUsersLabel { get; }
|
||||
|
||||
// Site selection
|
||||
ObservableCollection<SiteInfo> SelectedSites;
|
||||
RelayCommand OpenSitePickerCommand;
|
||||
string SitesSelectedLabel { get; }
|
||||
|
||||
// Scan options
|
||||
[ObservableProperty] bool IncludeInherited;
|
||||
[ObservableProperty] bool ScanFolders;
|
||||
[ObservableProperty] bool IncludeSubsites;
|
||||
|
||||
// Results
|
||||
[ObservableProperty] ObservableCollection<UserAccessEntry> Results;
|
||||
ICollectionView ResultsView { get; }
|
||||
[ObservableProperty] string FilterText;
|
||||
[ObservableProperty] bool IsGroupByUser;
|
||||
|
||||
// Summary
|
||||
int TotalAccessCount { get; }
|
||||
int SitesCount { get; }
|
||||
int HighPrivilegeCount { get; }
|
||||
|
||||
// Commands (from base + this VM)
|
||||
IAsyncRelayCommand RunCommand; // from base
|
||||
RelayCommand CancelCommand; // from base
|
||||
IAsyncRelayCommand ExportCsvCommand;
|
||||
IAsyncRelayCommand ExportHtmlCommand;
|
||||
|
||||
// State from base
|
||||
bool IsRunning;
|
||||
string StatusMessage;
|
||||
int ProgressValue;
|
||||
```
|
||||
|
||||
<!-- Existing View pattern to follow -->
|
||||
PermissionsView.xaml: Left panel (290px) + Right panel (*) + Bottom StatusBar
|
||||
Localization: {Binding Source={x:Static loc:TranslationSource.Instance}, Path=[key]}
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Create UserAccessAuditView XAML layout</name>
|
||||
<files>SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml</files>
|
||||
<action>
|
||||
Create `SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml` following the PermissionsView.xaml pattern (left panel config + right panel DataGrid + bottom status bar).
|
||||
|
||||
Layout structure:
|
||||
1. **Left panel (290px)** in DockPanel:
|
||||
a. **People Picker GroupBox** ("Select Users"):
|
||||
- TextBox bound to SearchQuery (UpdateSourceTrigger=PropertyChanged)
|
||||
- Below TextBox: a Popup (IsOpen bound to SearchResults.Count > 0 and IsSearching or has results) containing a ListBox of SearchResults. Each item shows DisplayName + Mail. Clicking an item fires AddUserCommand.
|
||||
- Below Popup: ItemsControl showing SelectedUsers as removable chips/pills. Each pill has user name + X button (RemoveUserCommand).
|
||||
- TextBlock showing SelectedUsersLabel
|
||||
b. **Site Selection GroupBox** ("Target Sites"):
|
||||
- Button "Select Sites" bound to OpenSitePickerCommand
|
||||
- TextBlock showing SitesSelectedLabel
|
||||
c. **Scan Options GroupBox**:
|
||||
- CheckBox "Include inherited" bound to IncludeInherited
|
||||
- CheckBox "Scan folders" bound to ScanFolders
|
||||
- CheckBox "Include subsites" bound to IncludeSubsites
|
||||
d. **Action buttons**:
|
||||
- Run Audit / Cancel row
|
||||
- Export CSV / Export HTML row
|
||||
|
||||
2. **Right panel** in Grid:
|
||||
a. **Summary banner** (StackPanel, horizontal, at top):
|
||||
- Three stat cards (Border with background): Total Accesses, Sites, High Privilege
|
||||
- Each shows the count value and label
|
||||
b. **Toolbar row**:
|
||||
- Filter TextBox bound to FilterText
|
||||
- ToggleButton "Group by User" / "Group by Site" bound to IsGroupByUser
|
||||
c. **DataGrid** bound to ResultsView (ICollectionView):
|
||||
- Columns: User (DisplayName), Site (SiteTitle), Object (ObjectTitle), Permission Level, Access Type, Granted Through
|
||||
- Row style with DataTriggers for color coding:
|
||||
- AccessType.Direct: light blue background (#EBF5FB)
|
||||
- AccessType.Group: light green background (#EAFAF1)
|
||||
- AccessType.Inherited: light gray background (#F4F6F6)
|
||||
- DataTemplate for Access Type column: TextBlock with icon (Unicode chars: Direct = key icon, Group = people icon, Inherited = arrow-down icon)
|
||||
- DataTrigger for IsHighPrivilege=true: bold text + warning icon (Unicode shield)
|
||||
- DataTrigger for IsExternalUser=true: guest badge styling
|
||||
- GroupStyle with expander header showing group name + count
|
||||
d. **DataGrid GroupStyle**: Expander with header template showing group key (user name or site title) and item count
|
||||
|
||||
3. **Bottom StatusBar** spanning both columns: ProgressBar + StatusMessage (same as PermissionsView)
|
||||
|
||||
Color-coding approach:
|
||||
- Use Style with DataTriggers on the DataGrid Row, binding to AccessType property
|
||||
- Access type icons: use Unicode characters that render in Segoe UI Symbol:
|
||||
- Direct: "\uE192" (key) or plain text "Direct" with blue foreground
|
||||
- Group: "\uE125" (people) or plain text "Group" with green foreground
|
||||
- Inherited: "\uE19C" (hierarchy) or plain text "Inherited" with gray foreground
|
||||
- High privilege warning: "\u26A0" (warning triangle) prepended to permission level
|
||||
- External user badge: orange-tinted pill in user column
|
||||
|
||||
The people picker Popup approach:
|
||||
- Use a Popup element positioned below the SearchQuery TextBox
|
||||
- Popup.IsOpen bound to a computed property (HasSearchResults) or use a MultiBinding
|
||||
- Popup contains a ListBox with ItemTemplate showing DisplayName and Mail
|
||||
- Clicking a ListBox item invokes AddUserCommand via EventTrigger or by binding SelectedItem
|
||||
- Simpler alternative: Use a ListBox directly below the TextBox (not a Popup) that is visible when SearchResults.Count > 0. This avoids Popup complexity.
|
||||
|
||||
For the autocomplete, the simplest WPF approach is:
|
||||
- ListBox below TextBox, Visibility collapsed when SearchResults is empty
|
||||
- ListBox.ItemTemplate shows "{DisplayName} ({Mail})"
|
||||
- On SelectionChanged or mouse click, add user to SelectedUsers via AddUserCommand
|
||||
|
||||
Localization keys to use (will be added in 07-07):
|
||||
- audit.grp.users, audit.grp.sites, audit.grp.options
|
||||
- audit.search.placeholder, audit.btn.run, audit.btn.exportCsv, audit.btn.exportHtml
|
||||
- audit.summary.total, audit.summary.sites, audit.summary.highPriv
|
||||
- audit.toggle.byUser, audit.toggle.bySite
|
||||
- audit.filter.placeholder
|
||||
- btn.cancel (existing key)
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd "C:\Users\dev\Documents\projets\Sharepoint" && dotnet build SharepointToolbox/SharepointToolbox.csproj --no-incremental 2>&1 | tail -5</automated>
|
||||
</verify>
|
||||
<done>UserAccessAuditView.xaml compiles. Layout has: people picker with autocomplete list + removable user pills, site picker button, scan option checkboxes, run/cancel/export buttons, summary banner with 3 stats, filter TextBox, group-by toggle, color-coded DataGrid with access type icons and group headers, status bar.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Create UserAccessAuditView code-behind</name>
|
||||
<files>SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml.cs</files>
|
||||
<action>
|
||||
Create `SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml.cs`:
|
||||
|
||||
```csharp
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace SharepointToolbox.Views.Tabs;
|
||||
|
||||
public partial class UserAccessAuditView : UserControl
|
||||
{
|
||||
public UserAccessAuditView(ViewModels.Tabs.UserAccessAuditViewModel viewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = viewModel;
|
||||
|
||||
// Wire site picker dialog factory (same pattern as PermissionsView)
|
||||
viewModel.OpenSitePickerDialog = () =>
|
||||
{
|
||||
if (viewModel.CurrentProfile is null) return null!;
|
||||
var factory = new Views.Dialogs.SitePickerDialog(
|
||||
App.Current.MainWindow is MainWindow mw
|
||||
? ((IServiceProvider)mw.GetType().GetField("_serviceProvider",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
|
||||
?.GetValue(mw)!).GetService(typeof(Services.ISiteListService)) as Services.ISiteListService
|
||||
: null!,
|
||||
viewModel.CurrentProfile);
|
||||
return factory;
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
IMPORTANT: The actual dialog factory wiring will be cleaner — it will be done from MainWindow.xaml.cs in plan 07-07 (same pattern as PermissionsView where the View's constructor receives the ViewModel from DI, and MainWindow sets the dialog factory after creating the View). So keep the code-behind minimal:
|
||||
|
||||
```csharp
|
||||
using System.Windows.Controls;
|
||||
using SharepointToolbox.ViewModels.Tabs;
|
||||
|
||||
namespace SharepointToolbox.Views.Tabs;
|
||||
|
||||
public partial class UserAccessAuditView : UserControl
|
||||
{
|
||||
public UserAccessAuditView(UserAccessAuditViewModel viewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = viewModel;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The dialog factory wiring for the site picker will be handled in 07-07 from MainWindow.xaml.cs, following the same pattern where MainWindow wires dialog factories after resolving Views from DI.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd "C:\Users\dev\Documents\projets\Sharepoint" && dotnet build SharepointToolbox/SharepointToolbox.csproj --no-incremental 2>&1 | tail -5</automated>
|
||||
</verify>
|
||||
<done>UserAccessAuditView.xaml.cs compiles, receives UserAccessAuditViewModel via constructor injection, sets DataContext.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
- `dotnet build SharepointToolbox/SharepointToolbox.csproj` succeeds with 0 errors
|
||||
- UserAccessAuditView.xaml + .cs compile as a UserControl
|
||||
- XAML has two-panel layout with all required UI elements
|
||||
- DataGrid has color-coded rows via DataTriggers on AccessType
|
||||
- Summary banner shows three computed stats
|
||||
- People picker has search TextBox + results list + selected user pills
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
The complete audit tab UI is rendered: administrators see a people picker, site selector, scan options, and a rich DataGrid with color-coded access types, grouping toggle, filter, summary banner, and export buttons. All bound to ViewModel properties from 07-04.
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/07-user-access-audit/07-05-SUMMARY.md`
|
||||
</output>
|
||||
Reference in New Issue
Block a user