feat(ui/templates): replace flat tree model with TmplNode discriminated by kind
This commit is contained in:
@@ -293,22 +293,98 @@
|
|||||||
_loading = false;
|
_loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private record TmplTreeNode(Template Template, List<TmplTreeNode> Children);
|
private enum TmplNodeKind { Folder, Template, Composition }
|
||||||
|
|
||||||
private List<TmplTreeNode> _templateTreeRoots = new();
|
private record TmplNode(
|
||||||
|
string Key,
|
||||||
|
TmplNodeKind Kind,
|
||||||
|
int EntityId,
|
||||||
|
string Label,
|
||||||
|
int? ParentFolderId,
|
||||||
|
int? OwnerTemplateId,
|
||||||
|
Template? Template,
|
||||||
|
TemplateComposition? Composition,
|
||||||
|
List<TmplNode> Children);
|
||||||
|
|
||||||
|
private List<TmplNode> _treeRoots = new();
|
||||||
|
|
||||||
private void BuildTemplateTree()
|
private void BuildTemplateTree()
|
||||||
{
|
{
|
||||||
_templateTreeRoots = BuildTmplChildren(null);
|
// 1. Folder nodes keyed by id
|
||||||
|
var folderNodes = _folders.ToDictionary(
|
||||||
|
f => f.Id,
|
||||||
|
f => new TmplNode(
|
||||||
|
Key: $"f:{f.Id}",
|
||||||
|
Kind: TmplNodeKind.Folder,
|
||||||
|
EntityId: f.Id,
|
||||||
|
Label: f.Name,
|
||||||
|
ParentFolderId: f.ParentFolderId,
|
||||||
|
OwnerTemplateId: null,
|
||||||
|
Template: null,
|
||||||
|
Composition: null,
|
||||||
|
Children: new List<TmplNode>()));
|
||||||
|
|
||||||
|
// 2. Attach folder nodes by ParentFolderId
|
||||||
|
var roots = new List<TmplNode>();
|
||||||
|
foreach (var f in _folders.OrderBy(f => f.Name, StringComparer.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var node = folderNodes[f.Id];
|
||||||
|
if (f.ParentFolderId is int pid && folderNodes.TryGetValue(pid, out var parent))
|
||||||
|
parent.Children.Add(node);
|
||||||
|
else
|
||||||
|
roots.Add(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<TmplTreeNode> BuildTmplChildren(int? parentId)
|
// 3. Template nodes with composition leaves
|
||||||
|
foreach (var t in _templates.OrderBy(t => t.Name, StringComparer.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return _templates
|
var compChildren = t.Compositions
|
||||||
.Where(t => t.ParentTemplateId == parentId)
|
.OrderBy(c => c.InstanceName, StringComparer.OrdinalIgnoreCase)
|
||||||
.OrderBy(t => t.Name)
|
.Select(c => new TmplNode(
|
||||||
.Select(t => new TmplTreeNode(t, BuildTmplChildren(t.Id)))
|
Key: $"c:{c.Id}",
|
||||||
|
Kind: TmplNodeKind.Composition,
|
||||||
|
EntityId: c.Id,
|
||||||
|
Label: c.InstanceName,
|
||||||
|
ParentFolderId: null,
|
||||||
|
OwnerTemplateId: t.Id,
|
||||||
|
Template: null,
|
||||||
|
Composition: c,
|
||||||
|
Children: new List<TmplNode>()))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
var tNode = new TmplNode(
|
||||||
|
Key: $"t:{t.Id}",
|
||||||
|
Kind: TmplNodeKind.Template,
|
||||||
|
EntityId: t.Id,
|
||||||
|
Label: t.Name,
|
||||||
|
ParentFolderId: t.FolderId,
|
||||||
|
OwnerTemplateId: null,
|
||||||
|
Template: t,
|
||||||
|
Composition: null,
|
||||||
|
Children: compChildren);
|
||||||
|
|
||||||
|
if (t.FolderId is int fid && folderNodes.TryGetValue(fid, out var parentFolder))
|
||||||
|
parentFolder.Children.Add(tNode);
|
||||||
|
else
|
||||||
|
roots.Add(tNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Sort each level: folders before templates, alphabetical
|
||||||
|
SortChildren(roots);
|
||||||
|
foreach (var node in folderNodes.Values)
|
||||||
|
SortChildren(node.Children);
|
||||||
|
|
||||||
|
_treeRoots = roots;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SortChildren(List<TmplNode> children)
|
||||||
|
{
|
||||||
|
children.Sort((a, b) =>
|
||||||
|
{
|
||||||
|
var kindOrder = (int)a.Kind - (int)b.Kind;
|
||||||
|
if (kindOrder != 0) return kindOrder;
|
||||||
|
return string.Compare(a.Label, b.Label, StringComparison.OrdinalIgnoreCase);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SelectTemplate(int templateId)
|
private async Task SelectTemplate(int templateId)
|
||||||
|
|||||||
Reference in New Issue
Block a user