- 01-04: wave 3 → 4 (01-03 is also wave 3; parallel executor would race) - 01-06: wave 4 → 5 (cascades from 01-04 fix); add FeatureTabBase UserControl for per-tab progress/cancel strip; bind StatusBar middle item to ProgressStatus instead of ConnectionStatus per locked CONTEXT.md decision - 01-07: wave 5 → 6 (cascades) - 01-08: wave 6 → 7 (cascades) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
272 lines
13 KiB
Markdown
272 lines
13 KiB
Markdown
---
|
|
phase: 01-foundation
|
|
plan: 07
|
|
type: execute
|
|
wave: 6
|
|
depends_on:
|
|
- 01-06
|
|
files_modified:
|
|
- SharepointToolbox/Views/Dialogs/ProfileManagementDialog.xaml
|
|
- SharepointToolbox/Views/Dialogs/ProfileManagementDialog.xaml.cs
|
|
- SharepointToolbox/Views/Tabs/SettingsView.xaml
|
|
- SharepointToolbox/Views/Tabs/SettingsView.xaml.cs
|
|
- SharepointToolbox/Views/MainWindow.xaml
|
|
autonomous: true
|
|
requirements:
|
|
- FOUND-02
|
|
- FOUND-09
|
|
- FOUND-12
|
|
must_haves:
|
|
truths:
|
|
- "ProfileManagementDialog opens as a modal window from the Manage Profiles button"
|
|
- "User can add a new profile (Name + Tenant URL + Client ID fields) and it appears in the toolbar ComboBox"
|
|
- "User can rename and delete existing profiles in the dialog"
|
|
- "SettingsView has a language ComboBox (English / French) and a data folder TextBox with Browse button"
|
|
- "Changing language in SettingsView switches the UI language immediately without restart"
|
|
- "Data folder setting persists to Sharepoint_Settings.json"
|
|
artifacts:
|
|
- path: "SharepointToolbox/Views/Dialogs/ProfileManagementDialog.xaml"
|
|
provides: "Modal dialog for profile CRUD"
|
|
contains: "ProfileManagementViewModel"
|
|
- path: "SharepointToolbox/Views/Tabs/SettingsView.xaml"
|
|
provides: "Settings tab content with language and folder controls"
|
|
contains: "TranslationSource"
|
|
key_links:
|
|
- from: "SharepointToolbox/Views/Dialogs/ProfileManagementDialog.xaml"
|
|
to: "SharepointToolbox/ViewModels/ProfileManagementViewModel.cs"
|
|
via: "DataContext = viewModel (constructor injected)"
|
|
pattern: "DataContext"
|
|
- from: "SharepointToolbox/Views/Tabs/SettingsView.xaml"
|
|
to: "SharepointToolbox/Localization/TranslationSource.cs"
|
|
via: "Language ComboBox selection sets TranslationSource.Instance.CurrentCulture"
|
|
pattern: "TranslationSource"
|
|
- from: "SharepointToolbox/Views/MainWindow.xaml"
|
|
to: "SharepointToolbox/Views/Tabs/SettingsView.xaml"
|
|
via: "Settings TabItem ContentTemplate or direct UserControl reference"
|
|
pattern: "SettingsView"
|
|
---
|
|
|
|
<objective>
|
|
Build the two user-facing views completing Phase 1 UX: ProfileManagementDialog (profile CRUD modal) and SettingsView (language + data folder). Wire SettingsView into the MainWindow Settings tab.
|
|
|
|
Purpose: These are the last two user-visible pieces before the visual checkpoint. After this plan the application is functional enough for a human to create a tenant profile, connect, and switch language.
|
|
Output: ProfileManagementDialog + SettingsView wired into the shell.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@C:/Users/SebastienQUEROL/.claude/get-shit-done/workflows/execute-plan.md
|
|
@C:/Users/SebastienQUEROL/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/PROJECT.md
|
|
@.planning/phases/01-foundation/01-CONTEXT.md
|
|
@.planning/phases/01-foundation/01-06-SUMMARY.md
|
|
|
|
<interfaces>
|
|
<!-- From ProfileManagementViewModel (plan 01-06) -->
|
|
```csharp
|
|
public class ProfileManagementViewModel : ObservableObject
|
|
{
|
|
public ObservableCollection<TenantProfile> Profiles { get; }
|
|
public TenantProfile? SelectedProfile { get; set; }
|
|
public string NewName { get; set; }
|
|
public string NewTenantUrl { get; set; }
|
|
public string NewClientId { get; set; }
|
|
public IAsyncRelayCommand AddCommand { get; }
|
|
public IAsyncRelayCommand RenameCommand { get; }
|
|
public IAsyncRelayCommand DeleteCommand { get; }
|
|
}
|
|
```
|
|
|
|
<!-- From SettingsViewModel (plan 01-06) -->
|
|
```csharp
|
|
public class SettingsViewModel : FeatureViewModelBase
|
|
{
|
|
public string SelectedLanguage { get; set; } // "en" or "fr"
|
|
public string DataFolder { get; set; }
|
|
public RelayCommand BrowseFolderCommand { get; }
|
|
}
|
|
```
|
|
|
|
<!-- Locked UI spec from CONTEXT.md -->
|
|
// ProfileManagementDialog: modal Window, fields: Name + Tenant URL + Client ID
|
|
// Profile fields: { name, tenantUrl, clientId } — JSON schema
|
|
// SettingsView: language ComboBox (English/French) + DataFolder TextBox + Browse button
|
|
// Language switch: immediate, no restart, via TranslationSource.Instance.CurrentCulture
|
|
</interfaces>
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: ProfileManagementDialog XAML and code-behind</name>
|
|
<files>
|
|
SharepointToolbox/Views/Dialogs/ProfileManagementDialog.xaml,
|
|
SharepointToolbox/Views/Dialogs/ProfileManagementDialog.xaml.cs
|
|
</files>
|
|
<action>
|
|
Create `Views/Dialogs/` directory.
|
|
|
|
**ProfileManagementDialog.xaml** — modal Window (not UserControl):
|
|
```xml
|
|
<Window x:Class="SharepointToolbox.Views.Dialogs.ProfileManagementDialog"
|
|
Title="Manage Profiles" Width="500" Height="480"
|
|
WindowStartupLocation="CenterOwner" ShowInTaskbar="False"
|
|
ResizeMode="NoResize">
|
|
<Grid Margin="12">
|
|
<Grid.RowDefinitions>
|
|
<RowDefinition Height="Auto" /> <!-- Existing profiles list -->
|
|
<RowDefinition Height="*" />
|
|
<RowDefinition Height="Auto" /> <!-- Add/Edit fields -->
|
|
<RowDefinition Height="Auto" /> <!-- Action buttons -->
|
|
</Grid.RowDefinitions>
|
|
|
|
<!-- Profile list -->
|
|
<Label Content="Profiles" Grid.Row="0" />
|
|
<ListBox Grid.Row="1" Margin="0,0,0,8"
|
|
ItemsSource="{Binding Profiles}"
|
|
SelectedItem="{Binding SelectedProfile}"
|
|
DisplayMemberPath="Name" />
|
|
|
|
<!-- Input fields -->
|
|
<Grid Grid.Row="2" Margin="0,0,0,8">
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="100" />
|
|
<ColumnDefinition Width="*" />
|
|
</Grid.ColumnDefinitions>
|
|
<Grid.RowDefinitions>
|
|
<RowDefinition Height="Auto" />
|
|
<RowDefinition Height="Auto" />
|
|
<RowDefinition Height="Auto" />
|
|
</Grid.RowDefinitions>
|
|
<Label Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[profile.name]}"
|
|
Grid.Row="0" Grid.Column="0" />
|
|
<TextBox Text="{Binding NewName, UpdateSourceTrigger=PropertyChanged}"
|
|
Grid.Row="0" Grid.Column="1" Margin="0,2" />
|
|
<Label Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[profile.url]}"
|
|
Grid.Row="1" Grid.Column="0" />
|
|
<TextBox Text="{Binding NewTenantUrl, UpdateSourceTrigger=PropertyChanged}"
|
|
Grid.Row="1" Grid.Column="1" Margin="0,2" />
|
|
<Label Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[profile.clientid]}"
|
|
Grid.Row="2" Grid.Column="0" />
|
|
<TextBox Text="{Binding NewClientId, UpdateSourceTrigger=PropertyChanged}"
|
|
Grid.Row="2" Grid.Column="1" Margin="0,2" />
|
|
</Grid>
|
|
|
|
<!-- Buttons -->
|
|
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right">
|
|
<Button Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[profile.add]}"
|
|
Command="{Binding AddCommand}" Width="60" Margin="4,0" />
|
|
<Button Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[profile.rename]}"
|
|
Command="{Binding RenameCommand}" Width="60" Margin="4,0" />
|
|
<Button Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[profile.delete]}"
|
|
Command="{Binding DeleteCommand}" Width="60" Margin="4,0" />
|
|
<Button Content="Close" Width="60" Margin="4,0"
|
|
Click="CloseButton_Click" IsCancel="True" />
|
|
</StackPanel>
|
|
</Grid>
|
|
</Window>
|
|
```
|
|
|
|
**ProfileManagementDialog.xaml.cs**:
|
|
- Constructor receives `ProfileManagementViewModel` via DI (register as `Transient` in App.xaml.cs — already done in plan 01-06)
|
|
- Sets `DataContext = viewModel`
|
|
- `CloseButton_Click`: calls `this.Close()`
|
|
- `Owner` set by caller (`MainWindowViewModel.ManageProfilesCommand` opens as `new ProfileManagementDialog { Owner = Application.Current.MainWindow }.ShowDialog()`)
|
|
|
|
After adding: the Add command in `ProfileManagementViewModel` must also trigger `MainWindowViewModel.TenantProfiles` refresh. Implement by having `ProfileManagementViewModel` accept a callback or raise an event. The simplest approach: `MainWindowViewModel.ManageProfilesCommand` reloads profiles after the dialog closes (dialog is modal — `ShowDialog()` blocks until closed).
|
|
</action>
|
|
<verify>
|
|
<automated>cd "C:\Users\dev\Documents\projets\Sharepoint" && dotnet build SharepointToolbox/SharepointToolbox.csproj 2>&1 | tail -5</automated>
|
|
</verify>
|
|
<done>Build succeeds. ProfileManagementDialog.xaml contains all three input fields (Name, Tenant URL, Client ID). All labels use TranslationSource bindings.</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: SettingsView XAML and MainWindow Settings tab wiring</name>
|
|
<files>
|
|
SharepointToolbox/Views/Tabs/SettingsView.xaml,
|
|
SharepointToolbox/Views/Tabs/SettingsView.xaml.cs,
|
|
SharepointToolbox/Views/MainWindow.xaml
|
|
</files>
|
|
<action>
|
|
Create `Views/Tabs/` directory.
|
|
|
|
**SettingsView.xaml** — UserControl (embedded in TabItem):
|
|
```xml
|
|
<UserControl x:Class="SharepointToolbox.Views.Tabs.SettingsView">
|
|
<StackPanel Margin="16">
|
|
<!-- Language -->
|
|
<Label Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[settings.language]}" />
|
|
<ComboBox Width="200" HorizontalAlignment="Left"
|
|
SelectedValue="{Binding SelectedLanguage}"
|
|
SelectedValuePath="Tag">
|
|
<ComboBoxItem Tag="en"
|
|
Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[settings.lang.en]}" />
|
|
<ComboBoxItem Tag="fr"
|
|
Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[settings.lang.fr]}" />
|
|
</ComboBox>
|
|
|
|
<Separator Margin="0,12" />
|
|
|
|
<!-- Data folder -->
|
|
<Label Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[settings.folder]}" />
|
|
<DockPanel>
|
|
<Button DockPanel.Dock="Right"
|
|
Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[settings.browse]}"
|
|
Command="{Binding BrowseFolderCommand}" Width="80" Margin="8,0,0,0" />
|
|
<TextBox Text="{Binding DataFolder, UpdateSourceTrigger=PropertyChanged}" />
|
|
</DockPanel>
|
|
</StackPanel>
|
|
</UserControl>
|
|
```
|
|
|
|
**SettingsView.xaml.cs**: Constructor receives `SettingsViewModel` via DI. Sets `DataContext = viewModel`. Calls `viewModel.LoadAsync()` in `Loaded` event to populate current settings.
|
|
|
|
Add `LoadAsync()` to SettingsViewModel if not present — loads current settings from SettingsService and sets `SelectedLanguage` and `DataFolder` properties.
|
|
|
|
**MainWindow.xaml** — Update Settings TabItem to use SettingsView (replace placeholder TextBlock):
|
|
```xml
|
|
<TabItem Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[tab.settings]}">
|
|
<views:SettingsView />
|
|
</TabItem>
|
|
```
|
|
Add namespace: `xmlns:views="clr-namespace:SharepointToolbox.Views.Tabs"`
|
|
|
|
Also register `SettingsView` in DI in App.xaml.cs (if not already):
|
|
```csharp
|
|
services.AddTransient<SettingsView>();
|
|
```
|
|
And resolve it in MainWindow constructor to inject into the Settings TabItem Content, OR use a DataTemplate approach. The simpler approach for Phase 1: resolve `SettingsView` from DI in `MainWindow.xaml.cs` constructor and set it as the TabItem Content directly:
|
|
```csharp
|
|
SettingsTabItem.Content = serviceProvider.GetRequiredService<SettingsView>();
|
|
```
|
|
The Settings TabItem already has `x:Name="SettingsTabItem"` from plan 01-06.
|
|
|
|
Run `dotnet build` and fix any errors.
|
|
</action>
|
|
<verify>
|
|
<automated>cd "C:\Users\dev\Documents\projets\Sharepoint" && dotnet build SharepointToolbox/SharepointToolbox.csproj 2>&1 | tail -5</automated>
|
|
</verify>
|
|
<done>Build succeeds. SettingsView.xaml contains language ComboBox with "en"/"fr" options and data folder TextBox with Browse button. MainWindow.xaml Settings tab shows SettingsView (not placeholder TextBlock).</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
- `dotnet build SharepointToolbox.sln` passes with 0 errors
|
|
- `dotnet test --filter "Category=Unit"` still passes (no regressions)
|
|
- ProfileManagementDialog has all three input fields using TranslationSource keys
|
|
- SettingsView language ComboBox has Tag="en" and Tag="fr" items
|
|
- MainWindow Settings TabItem Content is SettingsView (not placeholder)
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
All Phase 1 UI is built. Application runs and shows: shell with 8 tabs, log panel, status bar, language switching, profile management dialog, and settings. Ready for the visual checkpoint in plan 01-08.
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/01-foundation/01-07-SUMMARY.md`
|
|
</output>
|