chore: release v2.4

- 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>
This commit is contained in:
Dev
2026-04-20 11:23:11 +02:00
parent 8f30a60d2a
commit f4cc81bb71
64 changed files with 3315 additions and 405 deletions
@@ -1,4 +1,5 @@
using SharepointToolbox.Core.Models;
using SharepointToolbox.Localization;
using System.IO;
using System.Text;
@@ -12,10 +13,11 @@ public class StorageCsvExportService
{
public string BuildCsv(IReadOnlyList<StorageNode> nodes)
{
var T = TranslationSource.Instance;
var sb = new StringBuilder();
// Header
sb.AppendLine("Library,Site,Files,Total Size (MB),Version Size (MB),Last Modified");
sb.AppendLine($"{T["report.col.library"]},{T["report.col.site"]},{T["report.stat.files"]},{T["report.col.total_size_mb"]},{T["report.col.version_size_mb"]},{T["report.col.last_modified"]}");
foreach (var node in nodes)
{
@@ -44,10 +46,11 @@ public class StorageCsvExportService
/// </summary>
public string BuildCsv(IReadOnlyList<StorageNode> nodes, IReadOnlyList<FileTypeMetric> fileTypeMetrics)
{
var T = TranslationSource.Instance;
var sb = new StringBuilder();
// Library details
sb.AppendLine("Library,Site,Files,Total Size (MB),Version Size (MB),Last Modified");
sb.AppendLine($"{T["report.col.library"]},{T["report.col.site"]},{T["report.stat.files"]},{T["report.col.total_size_mb"]},{T["report.col.version_size_mb"]},{T["report.col.last_modified"]}");
foreach (var node in nodes)
{
sb.AppendLine(string.Join(",",
@@ -65,10 +68,10 @@ public class StorageCsvExportService
if (fileTypeMetrics.Count > 0)
{
sb.AppendLine();
sb.AppendLine("File Type,Size (MB),File Count");
sb.AppendLine($"{T["report.col.file_type"]},{T["report.col.size_mb"]},{T["report.col.file_count"]}");
foreach (var m in fileTypeMetrics)
{
string label = string.IsNullOrEmpty(m.Extension) ? "(no extension)" : m.Extension;
string label = string.IsNullOrEmpty(m.Extension) ? T["report.text.no_extension"] : m.Extension;
sb.AppendLine(string.Join(",", Csv(label), FormatMb(m.TotalSizeBytes), m.FileCount.ToString()));
}
}