diff --git a/SharepointToolbox/Services/Export/StorageCsvExportService.cs b/SharepointToolbox/Services/Export/StorageCsvExportService.cs
index a42b619..9fdc149 100644
--- a/SharepointToolbox/Services/Export/StorageCsvExportService.cs
+++ b/SharepointToolbox/Services/Export/StorageCsvExportService.cs
@@ -1,14 +1,56 @@
using SharepointToolbox.Core.Models;
+using System.IO;
+using System.Text;
namespace SharepointToolbox.Services.Export;
+///
+/// Exports a flat list of StorageNode objects to a UTF-8 BOM CSV.
+/// Compatible with Microsoft Excel (BOM signals UTF-8 encoding).
+///
public class StorageCsvExportService
{
- public string BuildCsv(IReadOnlyList nodes) => string.Empty; // implemented in Plan 03-03
+ public string BuildCsv(IReadOnlyList nodes)
+ {
+ var sb = new StringBuilder();
+
+ // Header
+ sb.AppendLine("Library,Site,Files,Total Size (MB),Version Size (MB),Last Modified");
+
+ foreach (var node in nodes)
+ {
+ sb.AppendLine(string.Join(",",
+ Csv(node.Name),
+ Csv(node.SiteTitle),
+ node.TotalFileCount.ToString(),
+ FormatMb(node.TotalSizeBytes),
+ FormatMb(node.VersionSizeBytes),
+ node.LastModified.HasValue
+ ? Csv(node.LastModified.Value.ToString("yyyy-MM-dd"))
+ : string.Empty));
+ }
+
+ return sb.ToString();
+ }
public async Task WriteAsync(IReadOnlyList nodes, string filePath, CancellationToken ct)
{
var csv = BuildCsv(nodes);
- await System.IO.File.WriteAllTextAsync(filePath, csv, new System.Text.UTF8Encoding(true), ct);
+ // UTF-8 with BOM for Excel compatibility
+ await File.WriteAllTextAsync(filePath, csv, new UTF8Encoding(encoderShouldEmitUTF8Identifier: true), ct);
+ }
+
+ // ── Helpers ───────────────────────────────────────────────────────────────
+
+ private static string FormatMb(long bytes)
+ => (bytes / (1024.0 * 1024.0)).ToString("F2");
+
+ /// RFC 4180 CSV field quoting.
+ private static string Csv(string value)
+ {
+ if (string.IsNullOrEmpty(value)) return string.Empty;
+ if (value.Contains(',') || value.Contains('"') || value.Contains('\n'))
+ return $"\"{value.Replace("\"", "\"\"")}\"";
+ return value;
}
}