ResourceLabelEditor.razor 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  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. ✕
  40. </button>
  41. </div>
  42. }
  43. </div>
  44. }
  45. </div>
  46. <KeyValueModal
  47. IsOpen="_labelModalOpen"
  48. IsOpenChanged="v => { _labelModalOpen = v; if (!v) { _editingKey = null; _editingValue = null; } }"
  49. Title="@(_editingKey is null ? "Add Label" : "Edit Label")"
  50. Description="@(_editingKey is null ? "Enter a key and value for the label" : "Update the key and value for the label")"
  51. KeyLabel="Key"
  52. ValueLabel="Value"
  53. Key="@_editingKey"
  54. Value="@_editingValue"
  55. TestIdPrefix="@BaseTestId"
  56. OnSubmit="HandleLabelSubmit"/>
  57. @code {
  58. [Parameter] [EditorRequired] public TResource Resource { get; set; } = default!;
  59. [Parameter] public EventCallback OnLabelsChanged { get; set; }
  60. [Parameter] public string? TestIdPrefix { get; set; }
  61. private bool _labelModalOpen;
  62. private string? _editingKey;
  63. private string? _editingValue;
  64. private string BaseTestId =>
  65. string.IsNullOrWhiteSpace(TestIdPrefix)
  66. ? "resource-label-editor"
  67. : $"{TestIdPrefix}-resource-label-editor";
  68. void OpenAddLabel()
  69. {
  70. _editingKey = null;
  71. _editingValue = null;
  72. _labelModalOpen = true;
  73. }
  74. void OpenEditLabel(string key, string value)
  75. {
  76. _editingKey = key;
  77. _editingValue = value;
  78. _labelModalOpen = true;
  79. }
  80. public async Task HandleLabelSubmit((string Key, string Value) label)
  81. {
  82. var originalKey = _editingKey;
  83. _editingKey = null;
  84. _editingValue = null;
  85. if (originalKey is not null && originalKey != label.Key)
  86. await RemoveLabelUseCase.ExecuteAsync(Resource.Name, originalKey);
  87. await AddLabelUseCase.ExecuteAsync(Resource.Name, label.Key, label.Value);
  88. if (OnLabelsChanged.HasDelegate)
  89. await OnLabelsChanged.InvokeAsync();
  90. }
  91. async Task RemoveLabel(string key)
  92. {
  93. await RemoveLabelUseCase.ExecuteAsync(Resource.Name, key);
  94. if (OnLabelsChanged.HasDelegate)
  95. await OnLabelsChanged.InvokeAsync();
  96. }
  97. }