feat(ui/templates): per-kind context menus + folder rename/delete

This commit is contained in:
Joseph Doherty
2026-05-11 11:15:25 -04:00
parent 4977f99a74
commit 39e6e0a525

View File

@@ -19,6 +19,28 @@
<ToastNotification @ref="_toast" />
<ConfirmDialog @ref="_confirmDialog" />
@if (_showRenameFolderDialog)
{
<div class="modal show d-block" tabindex="-1" style="background: rgba(0,0,0,0.4);">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<h6 class="modal-title">Rename Folder</h6>
<button type="button" class="btn-close" @onclick="() => _showRenameFolderDialog = false"></button>
</div>
<div class="modal-body">
<input class="form-control form-control-sm" @bind="_renameFolderName" />
@if (_renameFolderError != null) { <div class="text-danger small mt-1">@_renameFolderError</div> }
</div>
<div class="modal-footer">
<button class="btn btn-outline-secondary btn-sm" @onclick="() => _showRenameFolderDialog = false">Cancel</button>
<button class="btn btn-primary btn-sm" @onclick="SubmitRenameFolder">Save</button>
</div>
</div>
</div>
</div>
}
@if (_loading)
{
<LoadingSpinner IsLoading="true" />
@@ -469,10 +491,85 @@
// Folder selection: no-op (Section 4 design — folder click does not load detail).
}
private RenderFragment RenderNodeContextMenu(TmplNode node) => __builder => { };
private RenderFragment RenderNodeContextMenu(TmplNode node) => __builder =>
{
switch (node.Kind)
{
case TmplNodeKind.Folder:
<button class="dropdown-item" @onclick="() => OpenNewFolderDialog(node.EntityId)">New Folder</button>
<button class="dropdown-item" @onclick="() => OpenNewTemplateDialog(node.EntityId)">New Template</button>
<button class="dropdown-item" @onclick="() => OpenRenameFolderDialog(node.EntityId, node.Label)">Rename</button>
<div class="dropdown-divider"></div>
<button class="dropdown-item text-danger" @onclick="() => DeleteFolder(node.EntityId, node.Label)">Delete</button>
break;
case TmplNodeKind.Template:
<button class="dropdown-item" @onclick="() => SelectTemplate(node.EntityId)">Edit</button>
<button class="dropdown-item" @onclick="() => OpenMoveTemplateDialog(node.EntityId, node.Label)">Move to Folder…</button>
<div class="dropdown-divider"></div>
<button class="dropdown-item text-danger" @onclick="() => DeleteTemplate(node.Template!)">Delete</button>
break;
case TmplNodeKind.Composition:
var composedKey = $"t:{node.Composition!.ComposedTemplateId}";
<button class="dropdown-item" @onclick="() => OnTreeNodeSelected(composedKey)">Open composed template</button>
<button class="dropdown-item text-danger" @onclick="() => DeleteComposition(node.Composition)">Remove composition</button>
break;
}
};
private void OpenNewFolderDialog(int? parentFolderId) { /* Task 17 */ }
private void OpenNewTemplateDialog(int? parentFolderId) { /* Task 17 */ }
private void OpenMoveTemplateDialog(int templateId, string label) { /* Task 17 */ }
// Rename folder dialog state
private bool _showRenameFolderDialog;
private int _renameFolderId;
private string _renameFolderName = string.Empty;
private string? _renameFolderError;
private void OpenRenameFolderDialog(int folderId, string currentName)
{
_renameFolderId = folderId;
_renameFolderName = currentName;
_renameFolderError = null;
_showRenameFolderDialog = true;
}
private async Task SubmitRenameFolder()
{
_renameFolderError = null;
var user = await GetCurrentUserAsync();
var result = await TemplateFolderService.RenameFolderAsync(_renameFolderId, _renameFolderName.Trim(), user);
if (result.IsSuccess)
{
_showRenameFolderDialog = false;
_toast.ShowSuccess("Folder renamed.");
await LoadTemplatesAsync();
}
else
{
_renameFolderError = result.Error;
}
}
private async Task DeleteFolder(int folderId, string label)
{
var confirmed = await _confirmDialog.ShowAsync($"Delete folder '{label}'?", "Delete Folder");
if (!confirmed) return;
var user = await GetCurrentUserAsync();
var result = await TemplateFolderService.DeleteFolderAsync(folderId, user);
if (result.IsSuccess)
{
_toast.ShowSuccess($"Folder '{label}' deleted.");
await LoadTemplatesAsync();
}
else
{
_toast.ShowError(result.Error);
}
}
private async Task DeleteTemplate(Template template)
{