feat(ui/templates): per-kind context menus + folder rename/delete
This commit is contained in:
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user