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"; } }