using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using SharepointToolbox.Core.Models;
using SharepointToolbox.Localization;
using SharepointToolbox.Services;
using SharepointToolbox.ViewModels.Dialogs;
namespace SharepointToolbox.Views.Dialogs;
///
/// Dialog for selecting multiple SharePoint sites.
/// Delegates loading and filter/sort logic to
/// so the code-behind only handles WPF plumbing.
///
public partial class SitePickerDialog : Window
{
private readonly SitePickerDialogLogic _logic;
private List _allItems = new();
private string _sortColumn = "Url";
private ListSortDirection _sortDirection = ListSortDirection.Ascending;
///
/// Returns the list of sites the user checked before clicking OK.
///
public IReadOnlyList SelectedUrls =>
_allItems.Where(i => i.IsSelected).Select(i => new SiteInfo(i.Url, i.Title)
{
StorageUsedMb = i.StorageUsedMb,
StorageQuotaMb = i.StorageQuotaMb,
Template = i.Template
}).ToList();
public SitePickerDialog(ISiteListService siteListService, TenantProfile profile)
{
InitializeComponent();
_logic = new SitePickerDialogLogic(siteListService, profile);
}
private async void Window_Loaded(object sender, RoutedEventArgs e) => await LoadSitesAsync();
private async Task LoadSitesAsync()
{
StatusText.Text = TranslationSource.Instance["sitepicker.status.loading"];
LoadButton.IsEnabled = false;
try
{
var items = await _logic.LoadAsync(
new Progress(),
System.Threading.CancellationToken.None);
_allItems = items.ToList();
ApplyFilter();
StatusText.Text = string.Format(
CultureInfo.CurrentUICulture,
TranslationSource.Instance["sitepicker.status.loaded"],
_allItems.Count);
}
catch (InvalidOperationException ex)
{
StatusText.Text = ex.Message;
}
catch (Exception ex)
{
StatusText.Text = string.Format(
CultureInfo.CurrentUICulture,
TranslationSource.Instance["sitepicker.status.error"],
ex.Message);
}
finally
{
LoadButton.IsEnabled = true;
}
}
private void ApplyFilter()
{
var text = FilterBox.Text.Trim();
var minMb = SitePickerDialogLogic.ParseLongOrDefault(MinSizeBox.Text, 0);
var maxMb = SitePickerDialogLogic.ParseLongOrDefault(MaxSizeBox.Text, long.MaxValue);
var kindFilter = (TypeFilter.SelectedItem as ComboBoxItem)?.Tag as string ?? "All";
var filtered = SitePickerDialogLogic.ApplyFilter(_allItems, text, minMb, maxMb, kindFilter);
var sorted = SitePickerDialogLogic.ApplySort(filtered, _sortColumn, _sortDirection);
var list = sorted.ToList();
SiteList.ItemsSource = list;
if (_allItems.Count > 0)
StatusText.Text = string.Format(
CultureInfo.CurrentUICulture,
TranslationSource.Instance["sitepicker.status.shown"],
list.Count, _allItems.Count);
}
private void SiteList_ColumnHeaderClick(object sender, RoutedEventArgs e)
{
if (e.OriginalSource is not GridViewColumnHeader header) return;
if (header.Role == GridViewColumnHeaderRole.Padding) return;
if (header.Tag is not string column || string.IsNullOrEmpty(column)) return;
if (_sortColumn == column)
{
_sortDirection = _sortDirection == ListSortDirection.Ascending
? ListSortDirection.Descending
: ListSortDirection.Ascending;
}
else
{
_sortColumn = column;
_sortDirection = ListSortDirection.Ascending;
}
ApplyFilter();
}
private void FilterBox_TextChanged(object sender, TextChangedEventArgs e) => ApplyFilter();
private void SizeBox_TextChanged(object sender, TextChangedEventArgs e) => ApplyFilter();
private void TypeFilter_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (!IsLoaded) return;
ApplyFilter();
}
private void SelectAll_Click(object sender, RoutedEventArgs e)
{
if (SiteList.ItemsSource is IEnumerable visible)
{
foreach (var item in visible) item.IsSelected = true;
}
ApplyFilter();
}
private void DeselectAll_Click(object sender, RoutedEventArgs e)
{
if (SiteList.ItemsSource is IEnumerable visible)
{
foreach (var item in visible) item.IsSelected = false;
}
ApplyFilter();
}
private async void LoadButton_Click(object sender, RoutedEventArgs e) => await LoadSitesAsync();
private void OK_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
Close();
}
}
///
/// Mutable wrapper for a site entry shown in the SitePickerDialog list.
/// Supports two-way CheckBox binding via INotifyPropertyChanged.
///
public class SitePickerItem : INotifyPropertyChanged
{
private bool _isSelected;
public string Url { get; init; } = string.Empty;
public string Title { get; init; } = string.Empty;
public long StorageUsedMb { get; init; }
public long StorageQuotaMb { get; init; }
public string Template { get; init; } = string.Empty;
public SiteKind Kind => SiteKindHelper.FromTemplate(Template);
public string KindDisplay => SiteKindHelper.DisplayName(Kind);
public string SizeDisplay => FormatSize(StorageUsedMb);
public bool IsSelected
{
get => _isSelected;
set
{
if (_isSelected == value) return;
_isSelected = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsSelected)));
}
}
public event PropertyChangedEventHandler? PropertyChanged;
public SitePickerItem(string url, string title, long storageUsedMb = 0, long storageQuotaMb = 0, string template = "")
{
Url = url;
Title = title;
StorageUsedMb = storageUsedMb;
StorageQuotaMb = storageQuotaMb;
Template = template;
}
private static string FormatSize(long mb)
{
if (mb <= 0) return "—";
if (mb >= 1024) return $"{mb / 1024.0:F1} GB";
return $"{mb} MB";
}
}