chore: archive v1.1 Enhanced Reports milestone
Some checks failed
Release SharePoint Toolbox v2 / release (push) Failing after 14s
Some checks failed
Release SharePoint Toolbox v2 / release (push) Failing after 14s
v1.1 shipped with 4 phases (25 plans), 10/10 requirements complete: - Global site selection (toolbar picker, all tabs consume) - User access audit (Graph people-picker, direct/group/inherited) - Simplified permissions (plain-language labels, risk levels, detail toggle) - Storage visualization (LiveCharts2 pie/donut + bar charts) Post-phase polish: centralized site selection (removed per-tab pickers), claims prefix stripping, StorageMetrics backfill, chart tooltip fix, summary stats in app + HTML exports. 205 tests passing, 10,484 LOC. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,11 +6,6 @@
|
||||
<!-- Options panel -->
|
||||
<ScrollViewer DockPanel.Dock="Left" Width="240" VerticalScrollBarVisibility="Auto" Margin="8,8,4,8">
|
||||
<StackPanel>
|
||||
<Label Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[lbl.site.url]}" />
|
||||
<TextBox Text="{Binding SiteUrl, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding IsRunning, Converter={StaticResource InverseBoolConverter}}"
|
||||
Height="26" Margin="0,0,0,8" />
|
||||
|
||||
<GroupBox Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[grp.dup.type]}" Margin="0,0,0,8">
|
||||
<StackPanel Margin="4">
|
||||
<RadioButton Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[rad.dup.files]}"
|
||||
|
||||
@@ -4,11 +4,7 @@
|
||||
xmlns:loc="clr-namespace:SharepointToolbox.Localization">
|
||||
<DockPanel Margin="10">
|
||||
<StackPanel DockPanel.Dock="Left" Width="280" Margin="0,0,10,0">
|
||||
<!-- Site URL and Library inputs -->
|
||||
<TextBlock Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[folderstruct.siteurl]}"
|
||||
Margin="0,0,0,3" />
|
||||
<TextBox Text="{Binding SiteUrl, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,10" />
|
||||
|
||||
<!-- Library input -->
|
||||
<TextBlock Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[folderstruct.library]}"
|
||||
Margin="0,0,0,3" />
|
||||
<TextBox Text="{Binding LibraryTitle, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,10" />
|
||||
|
||||
@@ -28,21 +28,6 @@
|
||||
DockPanel.Dock="Top" Margin="0,0,0,8" Padding="8">
|
||||
<StackPanel>
|
||||
|
||||
<!-- Site URL -->
|
||||
<TextBlock Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[perm.site.url]}"
|
||||
Margin="0,0,0,2" />
|
||||
<TextBox Text="{Binding SiteUrl, UpdateSourceTrigger=PropertyChanged}"
|
||||
Margin="0,0,0,6" />
|
||||
|
||||
<!-- View Sites + selected label -->
|
||||
<TextBlock Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[perm.or.select]}"
|
||||
Margin="0,0,0,2" />
|
||||
<Button Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[btn.view.sites]}"
|
||||
Command="{Binding OpenSitePickerCommand}"
|
||||
HorizontalAlignment="Left" Padding="8,2" Margin="0,0,0,4" />
|
||||
<TextBlock Text="{Binding SitesSelectedLabel}"
|
||||
FontStyle="Italic" Foreground="Gray" Margin="0,0,0,8" />
|
||||
|
||||
<!-- Checkboxes -->
|
||||
<CheckBox Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[chk.scan.folders]}"
|
||||
IsChecked="{Binding ScanFolders}" Margin="0,0,0,4" />
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
using System.Windows.Controls;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SharepointToolbox.Core.Models;
|
||||
using SharepointToolbox.ViewModels.Tabs;
|
||||
using SharepointToolbox.Views.Dialogs;
|
||||
|
||||
namespace SharepointToolbox.Views.Tabs;
|
||||
|
||||
@@ -11,12 +9,6 @@ public partial class PermissionsView : UserControl
|
||||
public PermissionsView(IServiceProvider serviceProvider)
|
||||
{
|
||||
InitializeComponent();
|
||||
var vm = serviceProvider.GetRequiredService<PermissionsViewModel>();
|
||||
DataContext = vm;
|
||||
vm.OpenSitePickerDialog = () =>
|
||||
{
|
||||
var factory = serviceProvider.GetRequiredService<Func<TenantProfile, SitePickerDialog>>();
|
||||
return factory(vm.CurrentProfile ?? new TenantProfile());
|
||||
};
|
||||
DataContext = serviceProvider.GetRequiredService<PermissionsViewModel>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,6 @@
|
||||
<!-- Filters panel -->
|
||||
<ScrollViewer DockPanel.Dock="Left" Width="260" VerticalScrollBarVisibility="Auto" Margin="8,8,4,8">
|
||||
<StackPanel>
|
||||
<Label Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[lbl.site.url]}" />
|
||||
<TextBox Text="{Binding SiteUrl, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding IsRunning, Converter={StaticResource InverseBoolConverter}}"
|
||||
Height="26" Margin="0,0,0,8" />
|
||||
|
||||
<GroupBox Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[grp.search.filters]}"
|
||||
Margin="0,0,0,8">
|
||||
<StackPanel Margin="4">
|
||||
|
||||
@@ -9,12 +9,6 @@
|
||||
<ScrollViewer DockPanel.Dock="Left" Width="240" VerticalScrollBarVisibility="Auto"
|
||||
Margin="8,8,4,8">
|
||||
<StackPanel>
|
||||
<!-- Site URL -->
|
||||
<Label Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[lbl.site.url]}" />
|
||||
<TextBox Text="{Binding SiteUrl, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding IsRunning, Converter={StaticResource InverseBoolConverter}}"
|
||||
Height="26" Margin="0,0,0,8"
|
||||
ToolTip="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[ph.site.url]}" />
|
||||
|
||||
<!-- Scan options group -->
|
||||
<GroupBox Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[grp.scan.opts]}"
|
||||
@@ -78,17 +72,46 @@
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- Right content area: DataGrid on top, Chart on bottom -->
|
||||
<!-- Right content area: Summary + DataGrid on top, Chart on bottom -->
|
||||
<Grid Margin="4,8,8,8">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" MinHeight="150" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="300" MinHeight="200" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Summary bar -->
|
||||
<Border Grid.Row="0" Background="#F0F7FF" CornerRadius="4" Padding="12,8" Margin="0,0,0,6">
|
||||
<Border.Style>
|
||||
<Style TargetType="Border">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding HasResults}" Value="True">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Border.Style>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Margin="0,0,24,0">
|
||||
<Run Text="Total Size: " FontWeight="SemiBold" />
|
||||
<Run Text="{Binding SummaryTotalSize, Converter={StaticResource BytesConverter}, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
<TextBlock Margin="0,0,24,0">
|
||||
<Run Text="Version Size: " FontWeight="SemiBold" />
|
||||
<Run Text="{Binding SummaryVersionSize, Converter={StaticResource BytesConverter}, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
<TextBlock>
|
||||
<Run Text="Files: " FontWeight="SemiBold" />
|
||||
<Run Text="{Binding SummaryFileCount, StringFormat=N0, Mode=OneWay}" />
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Results DataGrid -->
|
||||
<DataGrid x:Name="ResultsGrid"
|
||||
Grid.Row="0"
|
||||
Grid.Row="1"
|
||||
ItemsSource="{Binding Results}"
|
||||
IsReadOnly="True"
|
||||
AutoGenerateColumns="False"
|
||||
@@ -123,11 +146,11 @@
|
||||
</DataGrid>
|
||||
|
||||
<!-- Splitter between DataGrid and Chart -->
|
||||
<GridSplitter Grid.Row="1" Height="5" HorizontalAlignment="Stretch"
|
||||
<GridSplitter Grid.Row="2" Height="5" HorizontalAlignment="Stretch"
|
||||
Background="#DDD" ResizeDirection="Rows" />
|
||||
|
||||
<!-- Chart panel -->
|
||||
<Border Grid.Row="2" BorderBrush="#DDD" BorderThickness="1" CornerRadius="4"
|
||||
<Border Grid.Row="3" BorderBrush="#DDD" BorderThickness="1" CornerRadius="4"
|
||||
Padding="8" Background="White">
|
||||
<Grid>
|
||||
<!-- Chart title -->
|
||||
@@ -167,7 +190,8 @@
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Grid.Style>
|
||||
<lvc:PieChart Series="{Binding PieChartSeries}"
|
||||
<lvc:PieChart x:Name="StoragePieChart"
|
||||
Series="{Binding PieChartSeries}"
|
||||
LegendPosition="Right" />
|
||||
</Grid>
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using System.Windows.Controls;
|
||||
using LiveChartsCore;
|
||||
using LiveChartsCore.Kernel;
|
||||
using LiveChartsCore.Kernel.Sketches;
|
||||
|
||||
namespace SharepointToolbox.Views.Tabs;
|
||||
|
||||
@@ -8,5 +11,43 @@ public partial class StorageView : UserControl
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = viewModel;
|
||||
|
||||
StoragePieChart.Tooltip = new SingleSliceTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Custom tooltip that only shows the single closest/hovered pie slice
|
||||
/// instead of LiveCharts2's default which shows multiple nearby slices.
|
||||
/// </summary>
|
||||
internal sealed class SingleSliceTooltip : IChartTooltip
|
||||
{
|
||||
private readonly System.Windows.Controls.ToolTip _tip = new()
|
||||
{
|
||||
Padding = new System.Windows.Thickness(8, 4, 8, 4),
|
||||
FontSize = 13,
|
||||
Background = new System.Windows.Media.SolidColorBrush(
|
||||
System.Windows.Media.Color.FromRgb(255, 255, 255)),
|
||||
BorderBrush = new System.Windows.Media.SolidColorBrush(
|
||||
System.Windows.Media.Color.FromRgb(200, 200, 200)),
|
||||
BorderThickness = new System.Windows.Thickness(1),
|
||||
};
|
||||
|
||||
public void Show(IEnumerable<ChartPoint> foundPoints, Chart chart)
|
||||
{
|
||||
// Only show the first (closest) point
|
||||
var point = foundPoints.FirstOrDefault();
|
||||
if (point == null) { Hide(chart); return; }
|
||||
|
||||
var label = point.Context.Series.GetPrimaryToolTipText(point);
|
||||
if (string.IsNullOrEmpty(label)) label = point.Context.Series.Name ?? "";
|
||||
|
||||
_tip.Content = label;
|
||||
_tip.IsOpen = true;
|
||||
}
|
||||
|
||||
public void Hide(Chart chart)
|
||||
{
|
||||
_tip.IsOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,6 @@
|
||||
<GroupBox Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[templates.capture]}"
|
||||
Margin="0,0,0,10">
|
||||
<StackPanel Margin="5">
|
||||
<TextBlock Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[templates.siteurl]}"
|
||||
Margin="0,0,0,3" />
|
||||
<TextBox Text="{Binding CaptureSiteUrl, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,5" />
|
||||
|
||||
<TextBlock Text="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[templates.name]}"
|
||||
Margin="0,0,0,3" />
|
||||
<TextBox Text="{Binding TemplateName, UpdateSourceTrigger=PropertyChanged}" Margin="0,0,0,10" />
|
||||
|
||||
@@ -77,16 +77,6 @@
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[audit.grp.sites]}"
|
||||
DockPanel.Dock="Top" Margin="0,0,0,8" Padding="8">
|
||||
<StackPanel>
|
||||
<Button Content="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[btn.view.sites]}"
|
||||
Command="{Binding OpenSitePickerCommand}"
|
||||
HorizontalAlignment="Left" Padding="8,2" Margin="0,0,0,4" />
|
||||
<TextBlock Text="{Binding SitesSelectedLabel}" FontStyle="Italic" Foreground="Gray" />
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="{Binding Source={x:Static loc:TranslationSource.Instance}, Path=[audit.grp.options]}"
|
||||
DockPanel.Dock="Top" Margin="0,0,0,8" Padding="8">
|
||||
<StackPanel>
|
||||
@@ -244,7 +234,7 @@
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="⚠" Foreground="#E74C3C" Margin="0,0,4,0"
|
||||
<TextBlock Text="⚠" Foreground="#E74C3C" Margin="0,0,4,0"
|
||||
FontSize="12" VerticalAlignment="Center">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
@@ -301,4 +291,4 @@
|
||||
</StatusBar>
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
||||
</UserControl>
|
||||
|
||||
Reference in New Issue
Block a user