From ff2ed4f7cd5f7949a396b7fd9093aa7305a5965d Mon Sep 17 00:00:00 2001 From: REDCODE Date: Thu, 16 Jul 2026 19:28:45 +0200 Subject: [PATCH] feat: add MergeModal shared component for merge destination selection --- .../Components/Shared/MergeModal.razor | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 MilkStream.Client/Components/Shared/MergeModal.razor diff --git a/MilkStream.Client/Components/Shared/MergeModal.razor b/MilkStream.Client/Components/Shared/MergeModal.razor new file mode 100644 index 0000000..727ad2c --- /dev/null +++ b/MilkStream.Client/Components/Shared/MergeModal.razor @@ -0,0 +1,99 @@ + + +
+
+ + This action is irreversible. Merged @LabelLower data will be reassigned to the destination. +
+

Select the destination @LabelLower to keep:

+ @foreach (var item in Items) { +
+ + +
+ } +
+ +
+ + +
+
+ +@code { + /// + /// Represents an item in the merge list. + /// + public sealed class MergeItem { + /// + /// Gets or sets the item ID. + /// + public required Guid Id { get; set; } + /// + /// Gets or sets the item display name. + /// + public required string Name { get; set; } + /// + /// Gets or sets the item subtitle (e.g. asset count). + /// + public string Subtitle { get; set; } = string.Empty; + } + + /// + /// Gets or sets whether the modal is visible. + /// + [Parameter] public bool Show { get; set; } + + /// + /// Gets or sets the label for the item type (e.g. "Album", "Cosplayer"). + /// + [Parameter] public string ItemLabel { get; set; } = "Item"; + + /// + /// Gets or sets the list of items available for merge. + /// + [Parameter] public required List Items { get; set; } + + /// + /// Gets or sets the callback invoked when the user confirms the merge. + /// Passes the selected destination ID and the full list of source IDs. + /// + [Parameter] public EventCallback<(Guid DestinationId, List SourceIds)> OnMergeConfirmed { get; set; } + + /// + /// Gets or sets the callback invoked when the modal is closed. + /// + [Parameter] public EventCallback OnClose { get; set; } + + Guid? DestinationId; + + string GetTitle() => $"Merge {ItemLabel}(s)"; + + string LabelLower => ItemLabel.ToLowerInvariant(); + + protected override void OnParametersSet() { + if (Show && Items.Count > 0 && DestinationId is null) { + DestinationId = Items[0].Id; + } + } + + void SelectDestination(Guid id) { + DestinationId = id; + } + + async Task OnMerge() { + if (!DestinationId.HasValue) return; + var sourceIds = Items.Where(i => i.Id != DestinationId.Value).Select(i => i.Id).ToList(); + await OnMergeConfirmed.InvokeAsync((DestinationId.Value, sourceIds)); + } +}