RouterCardComponent.razor 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. @inject UpdateRouterUseCase UpdateRouterUseCase
  2. @inject GetRouterUseCase GetRouterUseCase
  3. @inject AddRouterPortUseCase AddRouterPortUseCase
  4. @inject UpdateRouterPortUseCase UpdateRouterPortUseCase
  5. @inject RemoveRouterPortUseCase RemoveRouterPortUseCase
  6. @inject DeleteRouterUseCase DeleteUseCase
  7. @using RackPeek.Domain.Resources.Hardware.Routers
  8. @using RackPeek.Domain.Resources.Hardware.Routers.Ports
  9. @using RackPeek.Domain.Resources.Models
  10. @using Router = RackPeek.Domain.Resources.Models.Router
  11. @inject RenameRouterUseCase RenameUseCase
  12. @inject CloneRouterUseCase CloneUseCase
  13. @inject NavigationManager Nav
  14. <div class="border border-zinc-800 rounded p-4 bg-zinc-900">
  15. <div class="flex justify-between items-center mb-3">
  16. <div class="text-zinc-100 hover:text-emerald-300">
  17. <NavLink href="@($"resources/hardware/{Router.Name}")" class="block">
  18. @Router.Name
  19. </NavLink>
  20. </div>
  21. <div class="flex gap-3 text-xs">
  22. @if (!_isEditing)
  23. {
  24. <button class="text-zinc-400 hover:text-zinc-200"
  25. @onclick="BeginEdit">
  26. Edit
  27. </button>
  28. <button class="text-xs text-blue-400 hover:text-blue-300 transition"
  29. title="Rename service"
  30. @onclick="OpenRename">
  31. Rename
  32. </button>
  33. <button
  34. class="text-xs text-emerald-400 hover:text-emerald-300 transition"
  35. title="Clone service"
  36. @onclick="OpenClone">
  37. Clone
  38. </button>
  39. <button
  40. class="text-xs text-red-400 hover:text-red-300 transition"
  41. title="Delete server"
  42. @onclick="ConfirmDelete">
  43. Delete
  44. </button>
  45. }
  46. else
  47. {
  48. <button class="text-emerald-400 hover:text-emerald-300"
  49. @onclick="Save">
  50. Save
  51. </button>
  52. <button class="text-zinc-500 hover:text-zinc-300"
  53. @onclick="Cancel">
  54. Cancel
  55. </button>
  56. }
  57. </div>
  58. </div>
  59. <div class="grid grid-cols-1 md:grid-cols-2 gap-3 text-sm">
  60. <!-- Model -->
  61. <div>
  62. <div class="text-zinc-400 mb-1">Model</div>
  63. @if (_isEditing)
  64. {
  65. <input class="input"
  66. @bind="_edit.Model"/>
  67. }
  68. else if (!string.IsNullOrWhiteSpace(Router.Model))
  69. {
  70. <div class="text-zinc-300">@Router.Model</div>
  71. }
  72. </div>
  73. <!-- Features -->
  74. <div>
  75. <div class="text-zinc-400 mb-1">Features</div>
  76. @if (_isEditing)
  77. {
  78. <div class="flex gap-4">
  79. <label class="flex items-center gap-2 text-zinc-300">
  80. <input type="checkbox" @bind="_edit.Managed"/>
  81. Managed
  82. </label>
  83. <label class="flex items-center gap-2 text-zinc-300">
  84. <input type="checkbox" @bind="_edit.Poe"/>
  85. PoE
  86. </label>
  87. </div>
  88. }
  89. else
  90. {
  91. <div class="flex gap-2 flex-wrap">
  92. @if (Router.Managed == true)
  93. {
  94. <span class="text-xs px-2 py-0.5 rounded bg-zinc-800 text-zinc-300">
  95. Managed
  96. </span>
  97. }
  98. @if (Router.Poe == true)
  99. {
  100. <span class="text-xs px-2 py-0.5 rounded bg-zinc-800 text-zinc-300">
  101. PoE
  102. </span>
  103. }
  104. </div>
  105. }
  106. </div>
  107. <!-- Ports -->
  108. <div class="md:col-span-2">
  109. <div class="flex items-center justify-between mb-1 group">
  110. <div class="text-zinc-400">
  111. Ports
  112. <button class="hover:text-emerald-400 ml-1"
  113. title="Add Port"
  114. @onclick="OpenAddPort">
  115. +
  116. </button>
  117. </div>
  118. </div>
  119. @if (Router.Ports?.Any() == true)
  120. {
  121. @foreach (var port in Router.Ports)
  122. {
  123. <div
  124. class="flex items-center justify-between text-zinc-300 group hover:bg-zinc-800/40 rounded px-1 py-0.5">
  125. <button class="hover:text-emerald-400"
  126. title="Edit Port"
  127. @onclick="() => OpenEditPort(port)">
  128. @port.Count× @port.Type — @port.Speed Gbps
  129. </button>
  130. </div>
  131. }
  132. }
  133. </div>
  134. </div>
  135. </div>
  136. <PortModal
  137. IsOpen="@_portModalOpen"
  138. IsOpenChanged="v => _portModalOpen = v"
  139. Value="@_editingPort"
  140. OnSubmit="HandlePortSubmit"
  141. OnDelete="HandlePortDelete"/>
  142. <ConfirmModal
  143. IsOpen="_confirmDeleteOpen"
  144. IsOpenChanged="v => _confirmDeleteOpen = v"
  145. Title="Delete server"
  146. ConfirmText="Delete"
  147. ConfirmClass="bg-red-600 hover:bg-red-500"
  148. OnConfirm="DeleteServer">
  149. Are you sure you want to delete <strong>@Router.Name</strong>?
  150. </ConfirmModal>
  151. <StringValueModal
  152. IsOpen="_renameOpen"
  153. IsOpenChanged="v => _renameOpen = v"
  154. Title="Rename router"
  155. Description="Enter a new name for this router"
  156. Label="New router name"
  157. Value="@Router.Name"
  158. OnSubmit="HandleRenameSubmit" />
  159. <StringValueModal
  160. IsOpen="_cloneOpen"
  161. IsOpenChanged="v => _cloneOpen = v"
  162. Title="Clone resource"
  163. Description="Enter a name for the cloned resource"
  164. Label="New resource name"
  165. Value="@($"{Router.Name}-copy")"
  166. OnSubmit="HandleCloneSubmit" />
  167. @code {
  168. [Parameter][EditorRequired] public Router Router { get; set; } = default!;
  169. bool _isEditing;
  170. RouterEditModel _edit = new();
  171. void BeginEdit()
  172. {
  173. _edit = RouterEditModel.From(Router);
  174. _isEditing = true;
  175. }
  176. async Task Save()
  177. {
  178. _isEditing = false;
  179. await UpdateRouterUseCase.ExecuteAsync(
  180. Router.Name,
  181. _edit.Model,
  182. _edit.Managed,
  183. _edit.Poe);
  184. Router = await GetRouterUseCase.ExecuteAsync(Router.Name);
  185. }
  186. void Cancel()
  187. {
  188. _isEditing = false;
  189. }
  190. #region Ports
  191. bool _portModalOpen;
  192. int _editingPortIndex;
  193. Port? _editingPort;
  194. void OpenAddPort()
  195. {
  196. _editingPortIndex = -1;
  197. _editingPort = null;
  198. _portModalOpen = true;
  199. }
  200. void OpenEditPort(Port port)
  201. {
  202. Router.Ports ??= new List<Port>();
  203. _editingPortIndex = Router.Ports.IndexOf(port);
  204. _editingPort = port;
  205. _portModalOpen = true;
  206. }
  207. async Task HandlePortSubmit(Port port)
  208. {
  209. if (_editingPortIndex < 0)
  210. {
  211. await AddRouterPortUseCase.ExecuteAsync(
  212. Router.Name,
  213. port.Type,
  214. port.Speed,
  215. port.Count);
  216. }
  217. else
  218. {
  219. await UpdateRouterPortUseCase.ExecuteAsync(
  220. Router.Name,
  221. _editingPortIndex,
  222. port.Type,
  223. port.Speed,
  224. port.Count);
  225. }
  226. Router = await GetRouterUseCase.ExecuteAsync(Router.Name);
  227. StateHasChanged();
  228. }
  229. async Task HandlePortDelete(Port _)
  230. {
  231. await RemoveRouterPortUseCase.ExecuteAsync(
  232. Router.Name,
  233. _editingPortIndex);
  234. Router = await GetRouterUseCase.ExecuteAsync(Router.Name);
  235. StateHasChanged();
  236. }
  237. #endregion
  238. public class RouterEditModel
  239. {
  240. public string? Model { get; set; }
  241. public bool? Managed { get; set; }
  242. public bool? Poe { get; set; }
  243. public static RouterEditModel From(Router Router)
  244. {
  245. return new RouterEditModel
  246. {
  247. Model = Router.Model,
  248. Managed = Router.Managed,
  249. Poe = Router.Poe
  250. };
  251. }
  252. }
  253. }
  254. @code {
  255. private bool _confirmDeleteOpen;
  256. [Parameter] public EventCallback<string> OnDeleted { get; set; }
  257. void ConfirmDelete()
  258. {
  259. _confirmDeleteOpen = true;
  260. }
  261. async Task DeleteServer()
  262. {
  263. _confirmDeleteOpen = false;
  264. await DeleteUseCase.ExecuteAsync(Router.Name);
  265. if (OnDeleted.HasDelegate)
  266. await OnDeleted.InvokeAsync(Router.Name);
  267. }
  268. }
  269. @code
  270. {
  271. bool _renameOpen;
  272. void OpenRename()
  273. {
  274. _renameOpen = true;
  275. }
  276. async Task HandleRenameSubmit(string newName)
  277. {
  278. await RenameUseCase.ExecuteAsync(Router.Name, newName);
  279. Nav.NavigateTo($"resources/hardware/{newName}");
  280. }
  281. }
  282. @code
  283. {
  284. bool _cloneOpen;
  285. void OpenClone()
  286. {
  287. _cloneOpen = true;
  288. }
  289. async Task HandleCloneSubmit(string newName)
  290. {
  291. await CloneUseCase.ExecuteAsync(Router.Name, newName);
  292. Nav.NavigateTo($"resources/hardware/{newName}");
  293. }
  294. }