276 lines
11 KiB
Markdown
276 lines
11 KiB
Markdown
---
|
|
phase: 14-user-directory-view
|
|
plan: 01
|
|
type: execute
|
|
wave: 1
|
|
depends_on: []
|
|
files_modified:
|
|
- SharepointToolbox/Localization/Strings.resx
|
|
- SharepointToolbox/Localization/Strings.fr.resx
|
|
- SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs
|
|
- SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml.cs
|
|
- SharepointToolbox.Tests/ViewModels/UserAccessAuditViewModelDirectoryTests.cs
|
|
autonomous: true
|
|
requirements:
|
|
- UDIR-05
|
|
- UDIR-01
|
|
|
|
must_haves:
|
|
truths:
|
|
- "SelectDirectoryUserCommand takes a GraphDirectoryUser, converts it to GraphUserResult, adds it to SelectedUsers via existing logic"
|
|
- "After SelectDirectoryUserCommand, the user appears in SelectedUsers and can be audited with RunCommand"
|
|
- "SelectDirectoryUserCommand does not add duplicates (same UPN check as existing AddUserCommand)"
|
|
- "Localization keys for directory UI exist in both EN and FR resource files"
|
|
- "Code-behind has a DirectoryDataGrid_MouseDoubleClick handler that invokes SelectDirectoryUserCommand"
|
|
artifacts:
|
|
- path: "SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs"
|
|
provides: "SelectDirectoryUserCommand bridging directory selection to audit pipeline"
|
|
contains: "SelectDirectoryUserCommand"
|
|
- path: "SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml.cs"
|
|
provides: "Event handler for directory DataGrid double-click"
|
|
contains: "DirectoryDataGrid_MouseDoubleClick"
|
|
- path: "SharepointToolbox.Tests/ViewModels/UserAccessAuditViewModelDirectoryTests.cs"
|
|
provides: "Tests for SelectDirectoryUserCommand"
|
|
contains: "SelectDirectoryUser"
|
|
key_links:
|
|
- from: "SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs"
|
|
to: "SharepointToolbox/Core/Models/GraphDirectoryUser.cs"
|
|
via: "command parameter type"
|
|
pattern: "GraphDirectoryUser"
|
|
- from: "SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml.cs"
|
|
to: "SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs"
|
|
via: "command invocation"
|
|
pattern: "SelectDirectoryUserCommand"
|
|
---
|
|
|
|
<objective>
|
|
Add localization keys for directory UI, the SelectDirectoryUserCommand that bridges directory selection to the audit pipeline, and a code-behind event handler for DataGrid double-click.
|
|
|
|
Purpose: Provides the infrastructure (localization, command, event handler) that Plan 14-02 needs to build the XAML view. SC2 requires selecting a directory user to trigger an audit — this command makes that possible.
|
|
|
|
Output: Localization keys (EN+FR), SelectDirectoryUserCommand with tests, code-behind event handler.
|
|
</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/14-user-directory-view/14-RESEARCH.md
|
|
|
|
<interfaces>
|
|
<!-- Current ViewModel command pattern (AddUserCommand) -->
|
|
From SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs:
|
|
```csharp
|
|
public RelayCommand<GraphUserResult> AddUserCommand { get; }
|
|
|
|
private void ExecuteAddUser(GraphUserResult? user)
|
|
{
|
|
if (user == null) return;
|
|
if (!SelectedUsers.Any(u => u.UserPrincipalName == user.UserPrincipalName))
|
|
{
|
|
SelectedUsers.Add(user);
|
|
}
|
|
SearchQuery = string.Empty;
|
|
SearchResults.Clear();
|
|
}
|
|
```
|
|
|
|
<!-- GraphDirectoryUser record -->
|
|
From SharepointToolbox/Core/Models/GraphDirectoryUser.cs:
|
|
```csharp
|
|
public record GraphDirectoryUser(
|
|
string DisplayName, string UserPrincipalName,
|
|
string? Mail, string? Department, string? JobTitle, string? UserType);
|
|
```
|
|
|
|
<!-- GraphUserResult record -->
|
|
From SharepointToolbox/Services/IGraphUserSearchService.cs:
|
|
```csharp
|
|
public record GraphUserResult(string DisplayName, string UserPrincipalName, string? Mail);
|
|
```
|
|
|
|
<!-- Existing code-behind pattern -->
|
|
From SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml.cs:
|
|
```csharp
|
|
private void SearchResultsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
|
{
|
|
if (sender is ListBox listBox && listBox.SelectedItem is GraphUserResult user)
|
|
{
|
|
var vm = (UserAccessAuditViewModel)DataContext;
|
|
if (vm.AddUserCommand.CanExecute(user))
|
|
vm.AddUserCommand.Execute(user);
|
|
listBox.SelectedItem = null;
|
|
}
|
|
}
|
|
```
|
|
|
|
<!-- Existing localization key pattern -->
|
|
From Strings.resx:
|
|
```xml
|
|
<data name="audit.grp.users" xml:space="preserve"><value>Select Users</value></data>
|
|
<data name="audit.btn.run" xml:space="preserve"><value>Run Audit</value></data>
|
|
```
|
|
</interfaces>
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Add localization keys for directory UI (EN + FR)</name>
|
|
<files>
|
|
SharepointToolbox/Localization/Strings.resx,
|
|
SharepointToolbox/Localization/Strings.fr.resx
|
|
</files>
|
|
<behavior>
|
|
- Both resx files contain matching keys for directory browse UI
|
|
</behavior>
|
|
<action>
|
|
1. Add to `Strings.resx` (EN):
|
|
- `audit.mode.search` = "Search"
|
|
- `audit.mode.browse` = "Browse Directory"
|
|
- `directory.grp.browse` = "User Directory"
|
|
- `directory.btn.load` = "Load Directory"
|
|
- `directory.btn.cancel` = "Cancel"
|
|
- `directory.filter.placeholder` = "Filter users..."
|
|
- `directory.chk.guests` = "Include guests"
|
|
- `directory.status.count` = "users"
|
|
- `directory.hint.doubleclick` = "Double-click a user to add to audit"
|
|
- `directory.col.name` = "Name"
|
|
- `directory.col.upn` = "Email"
|
|
- `directory.col.department` = "Department"
|
|
- `directory.col.jobtitle` = "Job Title"
|
|
- `directory.col.type` = "Type"
|
|
|
|
2. Add to `Strings.fr.resx` (FR):
|
|
- `audit.mode.search` = "Recherche"
|
|
- `audit.mode.browse` = "Parcourir l'annuaire"
|
|
- `directory.grp.browse` = "Annuaire utilisateurs"
|
|
- `directory.btn.load` = "Charger l'annuaire"
|
|
- `directory.btn.cancel` = "Annuler"
|
|
- `directory.filter.placeholder` = "Filtrer les utilisateurs..."
|
|
- `directory.chk.guests` = "Inclure les invités"
|
|
- `directory.status.count` = "utilisateurs"
|
|
- `directory.hint.doubleclick` = "Double-cliquez sur un utilisateur pour l'ajouter à l'audit"
|
|
- `directory.col.name` = "Nom"
|
|
- `directory.col.upn` = "Courriel"
|
|
- `directory.col.department` = "Département"
|
|
- `directory.col.jobtitle` = "Poste"
|
|
- `directory.col.type` = "Type"
|
|
</action>
|
|
<verify>
|
|
<automated>dotnet build --no-restore -warnaserror</automated>
|
|
</verify>
|
|
<done>14 localization keys present in both EN and FR resource files.</done>
|
|
</task>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 2: Add SelectDirectoryUserCommand to ViewModel</name>
|
|
<files>
|
|
SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs,
|
|
SharepointToolbox.Tests/ViewModels/UserAccessAuditViewModelDirectoryTests.cs
|
|
</files>
|
|
<behavior>
|
|
- SelectDirectoryUserCommand is a RelayCommand<GraphDirectoryUser>
|
|
- It converts GraphDirectoryUser to GraphUserResult and adds to SelectedUsers
|
|
- Duplicate UPN check (same as AddUserCommand)
|
|
- Does NOT clear SearchQuery/SearchResults (not in search mode context)
|
|
- After execution, IsBrowseMode stays true — user can continue selecting from directory
|
|
</behavior>
|
|
<action>
|
|
1. Add command declaration in ViewModel:
|
|
```csharp
|
|
public RelayCommand<GraphDirectoryUser> SelectDirectoryUserCommand { get; }
|
|
```
|
|
|
|
2. Initialize in BOTH constructors:
|
|
```csharp
|
|
SelectDirectoryUserCommand = new RelayCommand<GraphDirectoryUser>(ExecuteSelectDirectoryUser);
|
|
```
|
|
|
|
3. Implement the command method:
|
|
```csharp
|
|
private void ExecuteSelectDirectoryUser(GraphDirectoryUser? dirUser)
|
|
{
|
|
if (dirUser == null) return;
|
|
var userResult = new GraphUserResult(dirUser.DisplayName, dirUser.UserPrincipalName, dirUser.Mail);
|
|
if (!SelectedUsers.Any(u => u.UserPrincipalName == userResult.UserPrincipalName))
|
|
{
|
|
SelectedUsers.Add(userResult);
|
|
}
|
|
}
|
|
```
|
|
|
|
4. Add tests to `UserAccessAuditViewModelDirectoryTests.cs`:
|
|
- Test: SelectDirectoryUserCommand adds user to SelectedUsers
|
|
- Test: SelectDirectoryUserCommand skips duplicates
|
|
- Test: SelectDirectoryUserCommand with null does nothing
|
|
- Test: After SelectDirectoryUser, user can be audited with RunCommand (integration: add user + check SelectedUsers.Count > 0)
|
|
</action>
|
|
<verify>
|
|
<automated>dotnet build --no-restore -warnaserror && dotnet test SharepointToolbox.Tests --filter "FullyQualifiedName~UserAccessAuditViewModelDirectory" --no-build -q</automated>
|
|
</verify>
|
|
<done>SelectDirectoryUserCommand bridges directory selection to audit pipeline. Tests pass.</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 3: Add code-behind event handler for directory DataGrid</name>
|
|
<files>
|
|
SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml.cs
|
|
</files>
|
|
<behavior>
|
|
- DirectoryDataGrid_MouseDoubleClick handler extracts the clicked GraphDirectoryUser
|
|
- Invokes SelectDirectoryUserCommand with the selected user
|
|
- Uses the same pattern as SearchResultsListBox_SelectionChanged
|
|
</behavior>
|
|
<action>
|
|
1. Add to `UserAccessAuditView.xaml.cs`:
|
|
```csharp
|
|
private void DirectoryDataGrid_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
|
{
|
|
if (sender is DataGrid grid && grid.SelectedItem is GraphDirectoryUser user)
|
|
{
|
|
var vm = (UserAccessAuditViewModel)DataContext;
|
|
if (vm.SelectDirectoryUserCommand.CanExecute(user))
|
|
vm.SelectDirectoryUserCommand.Execute(user);
|
|
}
|
|
}
|
|
```
|
|
|
|
2. Add the required using statement if not present:
|
|
```csharp
|
|
using System.Windows.Controls; // Already present
|
|
using SharepointToolbox.Core.Models; // For GraphDirectoryUser
|
|
```
|
|
</action>
|
|
<verify>
|
|
<automated>dotnet build --no-restore -warnaserror</automated>
|
|
</verify>
|
|
<done>Code-behind event handler exists, ready to be wired in XAML (Plan 14-02).</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
```bash
|
|
dotnet build --no-restore -warnaserror
|
|
dotnet test SharepointToolbox.Tests --filter "FullyQualifiedName~UserAccessAuditViewModelDirectory" --no-build -q
|
|
```
|
|
Both must pass with zero failures.
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- 14 localization keys in both EN and FR resx files
|
|
- SelectDirectoryUserCommand converts GraphDirectoryUser → GraphUserResult → SelectedUsers
|
|
- Duplicate UPN check prevents adding same user twice
|
|
- Code-behind event handler for DataGrid double-click
|
|
- All tests pass, build clean
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/14-user-directory-view/14-01-SUMMARY.md`
|
|
</output>
|