---
phase: 14-user-directory-view
plan: 02
type: execute
wave: 2
depends_on: [14-01]
files_modified:
  - SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml
autonomous: true
requirements:
  - UDIR-05
  - UDIR-01

must_haves:
  truths:
    - "The left panel shows a mode toggle (two RadioButtons: Search / Browse Directory) at the top"
    - "When Search mode is selected (IsBrowseMode=false), the existing people-picker GroupBox is visible and the directory panel is collapsed"
    - "When Browse mode is selected (IsBrowseMode=true), the directory panel is visible and the people-picker GroupBox is collapsed"
    - "The Scan Options GroupBox and Run/Export buttons remain visible in both modes"
    - "The directory panel contains: Load Directory button, Cancel button, Include guests checkbox, filter TextBox, status text, user count, and a DataGrid"
    - "The DataGrid is bound to DirectoryUsersView with columns: Name, Email, Department, Job Title, Type"
    - "The DataGrid has MouseDoubleClick wired to DirectoryDataGrid_MouseDoubleClick code-behind handler"
    - "While loading, the status text shows DirectoryLoadStatus and Load button is disabled"
    - "A hint text tells users to double-click to add a user to the audit"
    - "The SelectedUsers ItemsControl remains visible in both modes (users added from directory appear here)"
  artifacts:
    - path: "SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml"
      provides: "Complete directory browse UI with mode toggle, directory DataGrid, and loading UX"
      contains: "DirectoryUsersView"
  key_links:
    - from: "SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml"
      to: "SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs"
      via: "data binding"
      pattern: "IsBrowseMode|DirectoryUsersView|LoadDirectoryCommand|DirectoryFilterText|IncludeGuests"
---

<objective>
Add the complete directory browse UI to UserAccessAuditView.xaml with mode toggle, directory DataGrid, loading indicators, and seamless integration with the existing audit workflow.

Purpose: SC1-SC4 require visible UI for mode switching, directory display, loading progress, and cancellation. This plan wires all Phase 13 ViewModel properties to the View layer.

Output: Updated UserAccessAuditView.xaml with full directory browse mode.
</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>
<!-- ViewModel bindings available (Phase 13 + 14-01) -->
From SharepointToolbox/ViewModels/Tabs/UserAccessAuditViewModel.cs:
```csharp
// Mode toggle
public bool IsBrowseMode { get; set; }

// Directory data
public ObservableCollection<GraphDirectoryUser> DirectoryUsers { get; }
public ICollectionView DirectoryUsersView { get; }  // filtered + sorted
public int DirectoryUserCount { get; }               // computed filtered count

// Directory commands
public IAsyncRelayCommand LoadDirectoryCommand { get; }
public RelayCommand CancelDirectoryLoadCommand { get; }
public RelayCommand<GraphDirectoryUser> SelectDirectoryUserCommand { get; }

// Directory state
public bool IsLoadingDirectory { get; }
public string DirectoryLoadStatus { get; }
public bool IncludeGuests { get; set; }
public string DirectoryFilterText { get; set; }

// Existing (still visible in both modes)
public ObservableCollection<GraphUserResult> SelectedUsers { get; }
public string SelectedUsersLabel { get; }
public IAsyncRelayCommand RunCommand { get; }
public RelayCommand CancelCommand { get; }
public IAsyncRelayCommand ExportCsvCommand { get; }
public IAsyncRelayCommand ExportHtmlCommand { get; }
```

<!-- Available converters from App.xaml -->
- `{StaticResource BoolToVisibilityConverter}` — true→Visible, false→Collapsed
- `{StaticResource InverseBoolConverter}` — inverts bool
- `{StaticResource StringToVisibilityConverter}` — non-empty→Visible

<!-- Current left panel structure -->
```
DockPanel (290px, Margin 8)
├── GroupBox "Select Users" (DockPanel.Dock="Top") — SEARCH MODE (hide when IsBrowseMode)
│   └── SearchQuery, SearchResults, SelectedUsers, SelectedUsersLabel
├── GroupBox "Scan Options" (DockPanel.Dock="Top") — ALWAYS VISIBLE
│   └── CheckBoxes
└── StackPanel (DockPanel.Dock="Top") — ALWAYS VISIBLE
    └── Run/Cancel/Export buttons
```

<!-- Code-behind handler (from 14-01) -->
```csharp
private void DirectoryDataGrid_MouseDoubleClick(object sender, 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);
    }
}
```
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Restructure left panel with mode toggle and conditional panels</name>
  <files>
    SharepointToolbox/Views/Tabs/UserAccessAuditView.xaml
  </files>
  <behavior>
    - At the top of the left panel DockPanel, a mode toggle section appears with two RadioButtons
    - RadioButton "Search" is checked when IsBrowseMode=false (uses InverseBoolConverter)
    - RadioButton "Browse Directory" is checked when IsBrowseMode=true
    - Below the toggle: existing Search GroupBox (visible when IsBrowseMode=false) OR new Directory GroupBox (visible when IsBrowseMode=true)
    - SelectedUsers ItemsControl + label extracted from Search GroupBox and placed in a shared section visible in BOTH modes
    - Scan Options GroupBox and buttons remain always visible
    - Directory GroupBox contains:
      a) Two-button grid: Load Directory + Cancel (like Run/Cancel pattern)
      b) CheckBox for IncludeGuests
      c) Filter TextBox bound to DirectoryFilterText
      d) Status/count row: DirectoryLoadStatus + DirectoryUserCount
      e) DataGrid bound to DirectoryUsersView with 5 columns (Name, Email, Department, Job Title, Type)
      f) Hint text: "Double-click a user to add to audit"
    - DataGrid has MouseDoubleClick="DirectoryDataGrid_MouseDoubleClick"
    - DataGrid uses AutoGenerateColumns="False", IsReadOnly="True", virtualization enabled
    - DataGrid columns are DataGridTextColumn (simple text, sortable by default)
    - Guest users highlighted with a subtle "Guest" badge in the Type column (orange, like the existing UserAccessAuditView pattern)
  </behavior>
  <action>
    1. Read the current `UserAccessAuditView.xaml` to get the exact current content.

    2. Replace the left panel DockPanel content with the new structure:

       ```xml
       <!-- Mode toggle -->
       <StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="0,0,0,8">
         <RadioButton Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[audit.mode.search]}"
                      IsChecked="{Binding IsBrowseMode, Converter={StaticResource InverseBoolConverter}}"
                      Margin="0,0,12,0" />
         <RadioButton Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[audit.mode.browse]}"
                      IsChecked="{Binding IsBrowseMode}" />
       </StackPanel>

       <!-- SEARCH MODE PANEL (visible when IsBrowseMode=false) -->
       <GroupBox Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[audit.grp.users]}"
                 DockPanel.Dock="Top" Margin="0,0,0,8" Padding="8"
                 Visibility="{Binding IsBrowseMode, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter=Inverse}">
         <!-- Keep existing SearchQuery, SearchResults, but move SelectedUsers OUT -->
         <StackPanel>
           <TextBox Text="{Binding SearchQuery, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,2" />
           <!-- Searching indicator -->
           <TextBlock Text="Searching..." FontStyle="Italic" FontSize="10" Foreground="Gray" Margin="0,0,0,2">
             [existing DataTrigger style]
           </TextBlock>
           <!-- Search results dropdown -->
           <ListBox x:Name="SearchResultsListBox" [existing bindings] />
         </StackPanel>
       </GroupBox>

       <!-- BROWSE MODE PANEL (visible when IsBrowseMode=true) -->
       <GroupBox Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[directory.grp.browse]}"
                 DockPanel.Dock="Top" Margin="0,0,0,8" Padding="8"
                 Visibility="{Binding IsBrowseMode, Converter={StaticResource BoolToVisibilityConverter}}">
         <DockPanel>
           <!-- Load/Cancel buttons -->
           <Grid DockPanel.Dock="Top" Margin="0,0,0,4">
             <Grid.ColumnDefinitions>
               <ColumnDefinition Width="*" />
               <ColumnDefinition Width="*" />
             </Grid.ColumnDefinitions>
             <Button Grid.Column="0"
                     Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[directory.btn.load]}"
                     Command="{Binding LoadDirectoryCommand}" Margin="0,0,4,0" Padding="6,3" />
             <Button Grid.Column="1"
                     Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[directory.btn.cancel]}"
                     Command="{Binding CancelDirectoryLoadCommand}" Padding="6,3" />
           </Grid>

           <!-- Include guests checkbox -->
           <CheckBox DockPanel.Dock="Top"
                     Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[directory.chk.guests]}"
                     IsChecked="{Binding IncludeGuests}" Margin="0,0,0,4" />

           <!-- Filter text -->
           <TextBox DockPanel.Dock="Top"
                    Text="{Binding DirectoryFilterText, UpdateSourceTrigger=PropertyChanged}"
                    Margin="0,0,0,4" />

           <!-- Status row: load status + user count -->
           <StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="0,0,0,4">
             <TextBlock Text="{Binding DirectoryLoadStatus}" FontStyle="Italic" Foreground="Gray" FontSize="10"
                        Margin="0,0,8,0" />
             <TextBlock FontSize="10" Foreground="Gray">
               <TextBlock.Text>
                 <MultiBinding StringFormat="{}{0} {1}">
                   <Binding Path="DirectoryUserCount" />
                   <Binding Source="{x:Static loc:TranslationSource.Instance}" Path="[directory.status.count]" />
                 </MultiBinding>
               </TextBlock.Text>
             </TextBlock>
           </StackPanel>

           <!-- Hint text -->
           <TextBlock DockPanel.Dock="Bottom"
                      Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[directory.hint.doubleclick]}"
                      FontStyle="Italic" Foreground="Gray" FontSize="10" Margin="0,4,0,0"
                      TextWrapping="Wrap" />

           <!-- Directory DataGrid -->
           <DataGrid x:Name="DirectoryDataGrid"
                     ItemsSource="{Binding DirectoryUsersView}"
                     AutoGenerateColumns="False" IsReadOnly="True"
                     VirtualizingPanel.IsVirtualizing="True" EnableRowVirtualization="True"
                     MouseDoubleClick="DirectoryDataGrid_MouseDoubleClick"
                     CanUserSortColumns="True"
                     SelectionMode="Single" SelectionUnit="FullRow"
                     HeadersVisibility="Column" GridLinesVisibility="Horizontal"
                     BorderThickness="1" BorderBrush="#DDDDDD">
             <DataGrid.Columns>
               <DataGridTextColumn Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[directory.col.name]}"
                                   Binding="{Binding DisplayName}" Width="120" />
               <DataGridTextColumn Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[directory.col.upn]}"
                                   Binding="{Binding UserPrincipalName}" Width="140" />
               <DataGridTextColumn Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[directory.col.department]}"
                                   Binding="{Binding Department}" Width="90" />
               <DataGridTextColumn Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[directory.col.jobtitle]}"
                                   Binding="{Binding JobTitle}" Width="90" />
               <DataGridTemplateColumn Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[directory.col.type]}" Width="60">
                 <DataGridTemplateColumn.CellTemplate>
                   <DataTemplate>
                     <TextBlock Text="{Binding UserType}" VerticalAlignment="Center">
                       <TextBlock.Style>
                         <Style TargetType="TextBlock">
                           <Style.Triggers>
                             <DataTrigger Binding="{Binding UserType}" Value="Guest">
                               <Setter Property="Foreground" Value="#F39C12" />
                               <Setter Property="FontWeight" Value="SemiBold" />
                             </DataTrigger>
                           </Style.Triggers>
                         </Style>
                       </TextBlock.Style>
                     </TextBlock>
                   </DataTemplate>
                 </DataGridTemplateColumn.CellTemplate>
               </DataGridTemplateColumn>
             </DataGrid.Columns>
           </DataGrid>
         </DockPanel>
       </GroupBox>

       <!-- SHARED: Selected users (visible in both modes) -->
       <StackPanel DockPanel.Dock="Top" Margin="0,0,0,8">
         <ItemsControl ItemsSource="{Binding SelectedUsers}" Margin="0,0,0,4">
           [existing ItemTemplate with blue border badges + x remove button]
         </ItemsControl>
         <TextBlock Text="{Binding SelectedUsersLabel}" FontStyle="Italic" Foreground="Gray" FontSize="10" />
       </StackPanel>

       <!-- Scan Options GroupBox (unchanged, always visible) -->
       <GroupBox Header="..." DockPanel.Dock="Top" Margin="0,0,0,8" Padding="8">
         [existing checkboxes]
       </GroupBox>

       <!-- Run/Export buttons (unchanged, always visible) -->
       <StackPanel DockPanel.Dock="Top">
         [existing button grids]
       </StackPanel>
       ```

    IMPORTANT NOTES:
    - The `BoolToVisibilityConverter` natively shows when true. For the Search panel (show when IsBrowseMode=false), we need inverse behavior. Two approaches:
      a) Use a DataTrigger-based Style on Visibility (reliable)
      b) Check if BoolToVisibilityConverter supports a ConverterParameter for inversion
      Since we're not sure the converter supports inversion, use DataTrigger approach for the Search panel:
      ```xml
      <GroupBox.Style>
        <Style TargetType="GroupBox">
          <Setter Property="Visibility" Value="Visible" />
          <Style.Triggers>
            <DataTrigger Binding="{Binding IsBrowseMode}" Value="True">
              <Setter Property="Visibility" Value="Collapsed" />
            </DataTrigger>
          </Style.Triggers>
        </Style>
      </GroupBox.Style>
      ```
      And for the Browse panel, use `BoolToVisibilityConverter` directly (shows when IsBrowseMode=true).

    - The SelectedUsers ItemsControl must be EXTRACTED from the Search GroupBox and placed in a standalone section — it needs to remain visible when in Browse mode so users can see who they've selected from the directory.

    - DataGrid column headers use localized bindings. Note: DataGridTextColumn.Header does NOT support binding in standard WPF — it's not a FrameworkElement. Instead, use DataGridTemplateColumn with HeaderTemplate for localized headers, OR set the Header as a plain string and skip localization for column headers (simpler approach). DECISION: Use plain English headers for DataGrid columns (they are technical column names that don't benefit from localization as much). This avoids the complex HeaderTemplate pattern. Use the localization keys in other UI elements.
    
    Alternative if Header binding works (some WPF versions support it via x:Static): Test with `Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[directory.col.name]}"` — if it compiles and works, great. If not, fall back to plain strings.
  </action>
  <verify>
    <automated>dotnet build --no-restore -warnaserror</automated>
  </verify>
  <done>UserAccessAuditView has full directory browse UI with mode toggle, conditional panels, directory DataGrid, loading status, and double-click selection. Build passes.</done>
</task>

</tasks>

<verification>
```bash
dotnet build --no-restore -warnaserror
```
Build must pass. Visual verification requires manual testing.
</verification>

<success_criteria>
- SC1: Mode toggle (RadioButtons) visibly switches left panel between search and browse
- SC2: DataGrid double-click adds user to SelectedUsers; Run Audit button works as usual
- SC3: Loading status shows DirectoryLoadStatus, Load button disabled while loading, Cancel button active
- SC4: Cancel clears loading state; status returns to ready; no broken UI
- SelectedUsers visible in both modes
- DataGrid columns: Name, Email, Department, Job Title, Type (Guest highlighted in orange)
- Filter TextBox and IncludeGuests checkbox functional
- Build passes with zero warnings
</success_criteria>

<output>
After completion, create `.planning/phases/14-user-directory-view/14-02-SUMMARY.md`
</output>
