feat(ui/templates): per-kind context menus + folder rename/delete
This commit is contained in:
@@ -19,6 +19,28 @@
|
|||||||
<ToastNotification @ref="_toast" />
|
<ToastNotification @ref="_toast" />
|
||||||
<ConfirmDialog @ref="_confirmDialog" />
|
<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)
|
@if (_loading)
|
||||||
{
|
{
|
||||||
<LoadingSpinner IsLoading="true" />
|
<LoadingSpinner IsLoading="true" />
|
||||||
@@ -469,10 +491,85 @@
|
|||||||
// Folder selection: no-op (Section 4 design — folder click does not load detail).
|
// 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 OpenNewFolderDialog(int? parentFolderId) { /* Task 17 */ }
|
||||||
private void OpenNewTemplateDialog(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)
|
private async Task DeleteTemplate(Template template)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user