ResourceLabelEditor.razor 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. @using RackPeek.Domain.UseCases.Labels
  2. @typeparam TResource where TResource : RackPeek.Domain.Resources.Resource
  3. @inject IAddLabelUseCase<TResource> AddLabelUseCase
  4. @inject IRemoveLabelUseCase<TResource> RemoveLabelUseCase
  5. <div class="md:col-span-2"
  6. data-testid="@BaseTestId">
  7. <div class="flex items-center justify-between mb-1 group"
  8. data-testid="@($"{BaseTestId}-header")">
  9. <div class="text-zinc-400">
  10. Labels
  11. <button class="hover:text-emerald-400 ml-1"
  12. title="Add Label"
  13. data-testid="@($"{BaseTestId}-add")"
  14. @onclick="OpenAddLabel">
  15. +
  16. </button>
  17. </div>
  18. </div>
  19. @if (Resource.Labels.Any())
  20. {
  21. <div class="flex flex-wrap gap-2"
  22. data-testid="@($"{BaseTestId}-list")">
  23. @foreach (var kvp in Resource.Labels.OrderBy(k => k.Key))
  24. {
  25. <div class="flex text-xs rounded overflow-hidden border border-zinc-700"
  26. data-testid="@($"{BaseTestId}-label-{kvp.Key}")">
  27. <button type="button"
  28. class="px-2 py-0.5 bg-zinc-800 text-zinc-300 hover:bg-emerald-800 hover:text-emerald-200 transition text-left"
  29. title="Edit label"
  30. data-testid="@($"{BaseTestId}-label-{kvp.Key}-view")"
  31. @onclick="() => OpenEditLabel(kvp.Key, kvp.Value)">
  32. @kvp.Key: @kvp.Value
  33. </button>
  34. <button type="button"
  35. class="px-1 py-0.5 bg-zinc-800 text-zinc-400 hover:bg-red-800 hover:text-red-200 transition border-l border-zinc-700"
  36. title="Remove label"
  37. data-testid="@($"{BaseTestId}-label-{kvp.Key}-remove")"
  38. @onclick="() => RemoveLabel(kvp.Key)">
  39. </button>
  40. </div>
  41. }
  42. </div>
  43. }
  44. </div>
  45. <KeyValueModal
  46. IsOpen="_labelModalOpen"
  47. IsOpenChanged="v => { _labelModalOpen = v; if (!v) { _editingKey = null; _editingValue = null; } }"
  48. Title="@(_editingKey is null ? "Add Label" : "Edit Label")"
  49. Description="@(_editingKey is null ? "Enter a key and value for the label" : "Update the key and value for the label")"
  50. KeyLabel="Key"
  51. ValueLabel="Value"
  52. Key="@_editingKey"
  53. Value="@_editingValue"
  54. TestIdPrefix="@BaseTestId"
  55. OnSubmit="HandleLabelSubmit" />
  56. @code {
  57. [Parameter][EditorRequired] public TResource Resource { get; set; } = default!;
  58. [Parameter] public EventCallback OnLabelsChanged { get; set; }
  59. [Parameter] public string? TestIdPrefix { get; set; }
  60. private bool _labelModalOpen;
  61. private string? _editingKey;
  62. private string? _editingValue;
  63. private string BaseTestId =>
  64. string.IsNullOrWhiteSpace(TestIdPrefix)
  65. ? "resource-label-editor"
  66. : $"{TestIdPrefix}-resource-label-editor";
  67. void OpenAddLabel()
  68. {
  69. _editingKey = null;
  70. _editingValue = null;
  71. _labelModalOpen = true;
  72. }
  73. void OpenEditLabel(string key, string value)
  74. {
  75. _editingKey = key;
  76. _editingValue = value;
  77. _labelModalOpen = true;
  78. }
  79. public async Task HandleLabelSubmit((string Key, string Value) label)
  80. {
  81. var originalKey = _editingKey;
  82. _editingKey = null;
  83. _editingValue = null;
  84. if (originalKey is not null && originalKey != label.Key)
  85. await RemoveLabelUseCase.ExecuteAsync(Resource.Name, originalKey);
  86. await AddLabelUseCase.ExecuteAsync(Resource.Name, label.Key, label.Value);
  87. if (OnLabelsChanged.HasDelegate)
  88. await OnLabelsChanged.InvokeAsync();
  89. }
  90. async Task RemoveLabel(string key)
  91. {
  92. await RemoveLabelUseCase.ExecuteAsync(Resource.Name, key);
  93. if (OnLabelsChanged.HasDelegate)
  94. await OnLabelsChanged.InvokeAsync();
  95. }
  96. }