ResourceBreadCrumbComponent.razor 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. @using RackPeek.Domain.Persistence
  2. @using RackPeek.Domain.Resources
  3. @inject IResourceCollection Repo
  4. <div class="text-sm text-zinc-300 flex items-stretch gap-3">
  5. @for (var i = 0; i < Levels.Count; i++)
  6. {
  7. if (i > 0)
  8. {
  9. <div class="flex items-center text-zinc-500">/</div>
  10. }
  11. <div class="flex">
  12. <CrumbLevel Items="Levels[i]" />
  13. </div>
  14. }
  15. </div>
  16. @code {
  17. [Parameter, EditorRequired]
  18. public ResourceType ResourceType { get; set; }
  19. [Parameter, EditorRequired]
  20. public string ResourceName { get; set; } = default!;
  21. private List<List<Breadcrumb>> Levels { get; } = new();
  22. protected override async Task OnParametersSetAsync()
  23. {
  24. Levels.Clear();
  25. switch (ResourceType)
  26. {
  27. case ResourceType.Hardware:
  28. AddLevel(new Breadcrumb(
  29. ResourceName,
  30. Resource.GetResourceUrl("hardware", ResourceName)));
  31. break;
  32. case ResourceType.System:
  33. await BuildFromNode(ResourceName, "system");
  34. break;
  35. case ResourceType.Service:
  36. await BuildFromNode(ResourceName, "service");
  37. break;
  38. }
  39. }
  40. private async Task BuildFromNode(string name, string kind)
  41. {
  42. var resource = await Repo.GetByNameAsync(name);
  43. var parents = resource?.RunsOn ?? Enumerable.Empty<string>();
  44. var byDistance = await BuildAncestorGraph(parents);
  45. RenderLevels(byDistance);
  46. AddLevel(new Breadcrumb(
  47. name,
  48. Resource.GetResourceUrl(kind, name)));
  49. }
  50. private void RenderLevels(Dictionary<int, List<(string Name, string Kind)>> byDistance)
  51. {
  52. foreach (var dist in byDistance.Keys.OrderByDescending(x => x))
  53. {
  54. var items = byDistance[dist];
  55. var hardware = items
  56. .Where(x => Resource.IsHardware(x.Kind))
  57. .OrderBy(x => x.Name, StringComparer.OrdinalIgnoreCase)
  58. .Select(x => new Breadcrumb(
  59. x.Name,
  60. Resource.GetResourceUrl(x.Kind, x.Name)));
  61. var systems = items
  62. .Where(x => x.Kind == "system")
  63. .OrderBy(x => x.Name, StringComparer.OrdinalIgnoreCase)
  64. .Select(x => new Breadcrumb(
  65. x.Name,
  66. Resource.GetResourceUrl(x.Kind, x.Name)));
  67. AddLevel(hardware);
  68. AddLevel(systems);
  69. }
  70. }
  71. private async Task<Dictionary<int, List<(string Name, string Kind)>>>
  72. BuildAncestorGraph(IEnumerable<string> startingNodes)
  73. {
  74. var byDistance = new Dictionary<int, List<(string, string)>>();
  75. var visited = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
  76. var queue = new Queue<(string Name, int Dist)>();
  77. foreach (var node in startingNodes
  78. .Where(x => !string.IsNullOrWhiteSpace(x))
  79. .Distinct(StringComparer.OrdinalIgnoreCase))
  80. {
  81. queue.Enqueue((node, 1));
  82. }
  83. while (queue.Count > 0)
  84. {
  85. var (name, dist) = queue.Dequeue();
  86. if (!visited.Add(name))
  87. continue;
  88. var kind = await Repo.GetKind(name);
  89. if (kind is null)
  90. continue;
  91. kind = kind.Trim().ToLowerInvariant();
  92. if (!byDistance.TryGetValue(dist, out var list))
  93. {
  94. list = new List<(string, string)>();
  95. byDistance[dist] = list;
  96. }
  97. list.Add((name, kind));
  98. if (kind == "system")
  99. {
  100. var res = await Repo.GetByNameAsync(name);
  101. foreach (var parent in (res?.RunsOn ?? Enumerable.Empty<string>())
  102. .Where(x => !string.IsNullOrWhiteSpace(x))
  103. .Distinct(StringComparer.OrdinalIgnoreCase))
  104. {
  105. queue.Enqueue((parent, dist + 1));
  106. }
  107. }
  108. }
  109. return byDistance;
  110. }
  111. private void AddLevel(params Breadcrumb[] items)
  112. => AddLevel((IEnumerable<Breadcrumb>)items);
  113. private void AddLevel(IEnumerable<Breadcrumb> items)
  114. {
  115. var list = items.ToList();
  116. if (list.Count > 0)
  117. Levels.Add(list);
  118. }
  119. public record Breadcrumb(string Label, string Href);
  120. }