12dd1de9f2
- Add theme system (Dark/Light palettes, ModernTheme, ThemeManager) - Add InputDialog, Spinner common view - Add DuplicatesCsvExportService - Refresh views, dialogs, and view models across tabs - Update localization strings (en/fr) - Tweak services (transfer, permissions, search, user access, ownership elevation, bulk operations) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
105 lines
3.7 KiB
C#
105 lines
3.7 KiB
C#
using System.ComponentModel;
|
|
using System.Globalization;
|
|
using SharepointToolbox.Core.Models;
|
|
using SharepointToolbox.Services;
|
|
using SharepointToolbox.Views.Dialogs;
|
|
|
|
namespace SharepointToolbox.ViewModels.Dialogs;
|
|
|
|
/// <summary>
|
|
/// Headless logic for <see cref="SitePickerDialog"/>. Loads sites via
|
|
/// <see cref="ISiteListService"/> and applies filter/sort in memory so the
|
|
/// dialog's code-behind stays a thin shim and the logic is unit-testable
|
|
/// without a WPF host.
|
|
/// </summary>
|
|
public class SitePickerDialogLogic
|
|
{
|
|
private readonly ISiteListService _siteListService;
|
|
private readonly TenantProfile _profile;
|
|
|
|
public SitePickerDialogLogic(ISiteListService siteListService, TenantProfile profile)
|
|
{
|
|
_siteListService = siteListService;
|
|
_profile = profile;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loads all accessible sites for the tenant profile and wraps them in
|
|
/// <see cref="SitePickerItem"/> so the dialog can bind checkboxes.
|
|
/// </summary>
|
|
public async Task<IReadOnlyList<SitePickerItem>> LoadAsync(
|
|
IProgress<OperationProgress> progress,
|
|
CancellationToken ct)
|
|
{
|
|
var sites = await _siteListService.GetSitesAsync(_profile, progress, ct);
|
|
return sites
|
|
.Select(s => new SitePickerItem(s.Url, s.Title, s.StorageUsedMb, s.StorageQuotaMb, s.Template))
|
|
.ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Filters items by free-text (title/url substring), storage-size range,
|
|
/// and site kind. Empty or zero-range parameters become no-ops.
|
|
/// </summary>
|
|
public static IEnumerable<SitePickerItem> ApplyFilter(
|
|
IEnumerable<SitePickerItem> items,
|
|
string text,
|
|
long minMb,
|
|
long maxMb,
|
|
string kindFilter)
|
|
{
|
|
var result = items;
|
|
|
|
if (!string.IsNullOrEmpty(text))
|
|
{
|
|
result = result.Where(i =>
|
|
i.Url.Contains(text, StringComparison.OrdinalIgnoreCase) ||
|
|
i.Title.Contains(text, StringComparison.OrdinalIgnoreCase));
|
|
}
|
|
|
|
result = result.Where(i => i.StorageUsedMb >= minMb && i.StorageUsedMb <= maxMb);
|
|
|
|
if (!string.IsNullOrEmpty(kindFilter) && kindFilter != "All")
|
|
result = result.Where(i => i.Kind.ToString() == kindFilter);
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stable sort by a named column and direction. Unknown column names
|
|
/// return the input sequence unchanged.
|
|
/// </summary>
|
|
public static IEnumerable<SitePickerItem> ApplySort(
|
|
IEnumerable<SitePickerItem> items,
|
|
string column,
|
|
ListSortDirection direction)
|
|
{
|
|
var asc = direction == ListSortDirection.Ascending;
|
|
return column switch
|
|
{
|
|
"Title" => asc ? items.OrderBy(i => i.Title) : items.OrderByDescending(i => i.Title),
|
|
"Url" => asc ? items.OrderBy(i => i.Url) : items.OrderByDescending(i => i.Url),
|
|
"Kind" => asc ? items.OrderBy(i => i.KindDisplay) : items.OrderByDescending(i => i.KindDisplay),
|
|
"StorageUsedMb" => asc
|
|
? items.OrderBy(i => i.StorageUsedMb)
|
|
: items.OrderByDescending(i => i.StorageUsedMb),
|
|
"IsSelected" => asc
|
|
? items.OrderBy(i => i.IsSelected)
|
|
: items.OrderByDescending(i => i.IsSelected),
|
|
_ => items
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lenient long parse: whitespace-only or unparseable input yields
|
|
/// <paramref name="fallback"/> instead of throwing.
|
|
/// </summary>
|
|
public static long ParseLongOrDefault(string text, long fallback)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(text)) return fallback;
|
|
return long.TryParse(text.Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var v)
|
|
? v
|
|
: fallback;
|
|
}
|
|
}
|