@cobaltcore-dev/aurora 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/README.md +73 -77
  2. package/dist/client/AuroraApp.d.ts +15 -0
  3. package/dist/client/{AuthProvider-D-5Jpa6F.mjs → AuthProvider-Co4d0WzB.mjs} +3 -3
  4. package/dist/client/{AuthProvider-D-5Jpa6F.mjs.map → AuthProvider-Co4d0WzB.mjs.map} +1 -1
  5. package/dist/client/{ContentHeader-BXZoN3B9.mjs → ContentHeader-kx1Th5Sq.mjs} +29 -29
  6. package/dist/client/{ContentHeader-BXZoN3B9.mjs.map → ContentHeader-kx1Th5Sq.mjs.map} +1 -1
  7. package/dist/client/DeleteFlavorModal-C3cb7YiJ.mjs +638 -0
  8. package/dist/client/DeleteFlavorModal-C3cb7YiJ.mjs.map +1 -0
  9. package/dist/client/{EditSecurityGroupModal-DK3WYikA.mjs → EditSecurityGroupModal-CpP54WIK.mjs} +22 -22
  10. package/dist/client/{EditSecurityGroupModal-DK3WYikA.mjs.map → EditSecurityGroupModal-CpP54WIK.mjs.map} +1 -1
  11. package/dist/client/{FiltersInput-OVeIJzIo.mjs → FiltersInput-DxcyR6Bp.mjs} +18 -18
  12. package/dist/client/{FiltersInput-OVeIJzIo.mjs.map → FiltersInput-DxcyR6Bp.mjs.map} +1 -1
  13. package/dist/client/{FloatingIpActionModals-Dok7fJss.mjs → FloatingIpActionModals-BP8RWHbu.mjs} +77 -77
  14. package/dist/client/{FloatingIpActionModals-Dok7fJss.mjs.map → FloatingIpActionModals-BP8RWHbu.mjs.map} +1 -1
  15. package/dist/client/ImageToastNotifications-TZ3EfQg-.mjs +1219 -0
  16. package/dist/client/ImageToastNotifications-TZ3EfQg-.mjs.map +1 -0
  17. package/dist/client/{RouteError-pDEWC_k7.mjs → RouteError-QSV7qOoJ.mjs} +6 -6
  18. package/dist/client/{RouteError-pDEWC_k7.mjs.map → RouteError-QSV7qOoJ.mjs.map} +1 -1
  19. package/dist/client/{SortInput-DXWSqSny.mjs → SortInput-CYv2_Pur.mjs} +6 -6
  20. package/dist/client/{SortInput-DXWSqSny.mjs.map → SortInput-CYv2_Pur.mjs.map} +1 -1
  21. package/dist/client/{_auth-CJj1Cnbm.mjs → _auth-DXJkv9QO.mjs} +3 -3
  22. package/dist/client/{_auth-CJj1Cnbm.mjs.map → _auth-DXJkv9QO.mjs.map} +1 -1
  23. package/dist/client/{_flavorId-BoNcxYmF.mjs → _flavorId-C2x43-6S.mjs} +15 -15
  24. package/dist/client/_flavorId-C2x43-6S.mjs.map +1 -0
  25. package/dist/client/_flavorId-CR8ZUI-P.mjs +194 -0
  26. package/dist/client/_flavorId-CR8ZUI-P.mjs.map +1 -0
  27. package/dist/client/{_floatingIpId-D33bOEmH.mjs → _floatingIpId-BCk41_Lb.mjs} +3 -3
  28. package/dist/client/{_floatingIpId-D33bOEmH.mjs.map → _floatingIpId-BCk41_Lb.mjs.map} +1 -1
  29. package/dist/client/{_floatingIpId-DF_BSJN6.mjs → _floatingIpId-BGrOAmPT.mjs} +48 -48
  30. package/dist/client/{_floatingIpId-DF_BSJN6.mjs.map → _floatingIpId-BGrOAmPT.mjs.map} +1 -1
  31. package/dist/client/_imageId-CvfD832b.mjs +534 -0
  32. package/dist/client/_imageId-CvfD832b.mjs.map +1 -0
  33. package/dist/client/_pcaId-BxBt5DXi.mjs +459 -0
  34. package/dist/client/_pcaId-BxBt5DXi.mjs.map +1 -0
  35. package/dist/client/{_pcaId-BYCoeK6_.mjs → _pcaId-DOHycvCf.mjs} +3 -3
  36. package/dist/client/{_pcaId-BYCoeK6_.mjs.map → _pcaId-DOHycvCf.mjs.map} +1 -1
  37. package/dist/client/_projectId-BDSWnMGj.mjs +46 -0
  38. package/dist/client/_projectId-BDSWnMGj.mjs.map +1 -0
  39. package/dist/client/{_projectId-OW2xkK43.mjs → _projectId-DOgwFiqD.mjs} +3 -3
  40. package/dist/client/{_projectId-OW2xkK43.mjs.map → _projectId-DOgwFiqD.mjs.map} +1 -1
  41. package/dist/client/_projectId-DS4nR59B.mjs +299 -0
  42. package/dist/client/_projectId-DS4nR59B.mjs.map +1 -0
  43. package/dist/client/{_projectId-BwLMEMGC.mjs → _projectId-MxcHrXW4.mjs} +21 -21
  44. package/dist/client/_projectId-MxcHrXW4.mjs.map +1 -0
  45. package/dist/client/{_securityGroupId-B-Z-CzLp.mjs → _securityGroupId-CJJanWiY.mjs} +3 -3
  46. package/dist/client/{_securityGroupId-B-Z-CzLp.mjs.map → _securityGroupId-CJJanWiY.mjs.map} +1 -1
  47. package/dist/client/{_securityGroupId-B1bOYRbX.mjs → _securityGroupId-KKw4RPdH.mjs} +445 -446
  48. package/dist/client/{_securityGroupId-B1bOYRbX.mjs.map → _securityGroupId-KKw4RPdH.mjs.map} +1 -1
  49. package/dist/client/{containers-BjWqjNOx.mjs → _storageType-4wSxI__0.mjs} +19 -16
  50. package/dist/client/_storageType-4wSxI__0.mjs.map +1 -0
  51. package/dist/client/_storageType-DYjo-6ej.mjs +3243 -0
  52. package/dist/client/_storageType-DYjo-6ej.mjs.map +1 -0
  53. package/dist/client/_storageType-zeSZe--V.mjs +7 -0
  54. package/dist/client/_storageType-zeSZe--V.mjs.map +1 -0
  55. package/dist/client/about-Bo9vxGHy.mjs +92 -0
  56. package/dist/client/{about-DLn1ShhF.mjs.map → about-Bo9vxGHy.mjs.map} +1 -1
  57. package/dist/client/aurora-DDzsst74.mjs +19 -0
  58. package/dist/client/{aurora-CRcxVUCo.mjs.map → aurora-DDzsst74.mjs.map} +1 -1
  59. package/dist/client/{build-BJDfnAyi.mjs → build-DeJcDjPi.mjs} +3802 -3376
  60. package/dist/client/build-DeJcDjPi.mjs.map +1 -0
  61. package/dist/client/{buildFilterParams-TeyosGyK.mjs → buildFilterParams-DoZzMKX9.mjs} +1 -1
  62. package/dist/client/{buildFilterParams-TeyosGyK.mjs.map → buildFilterParams-DoZzMKX9.mjs.map} +1 -1
  63. package/dist/client/{cn-C3laVXMm.mjs → cn-DM4Cy3jv.mjs} +1 -1
  64. package/dist/client/{cn-C3laVXMm.mjs.map → cn-DM4Cy3jv.mjs.map} +1 -1
  65. package/dist/client/constants-BmcGYeR-.mjs +153 -0
  66. package/dist/client/constants-BmcGYeR-.mjs.map +1 -0
  67. package/dist/client/{flavors-D8oElC2K.mjs → flavors-BxFVqgnb.mjs} +2 -2
  68. package/dist/client/{flavors-D8oElC2K.mjs.map → flavors-BxFVqgnb.mjs.map} +1 -1
  69. package/dist/client/flavors-CfdgjsZY.mjs +632 -0
  70. package/dist/client/flavors-CfdgjsZY.mjs.map +1 -0
  71. package/dist/client/{flavors-BXPYAFyQ.mjs → flavors-DWMZ6TuJ.mjs} +2 -2
  72. package/dist/client/{flavors-BXPYAFyQ.mjs.map → flavors-DWMZ6TuJ.mjs.map} +1 -1
  73. package/dist/client/{floatingips-Fa6ocNUu.mjs → floatingips-ByRb82wS.mjs} +138 -139
  74. package/dist/client/{floatingips-Fa6ocNUu.mjs.map → floatingips-ByRb82wS.mjs.map} +1 -1
  75. package/dist/client/{formatBytes-tQBEnPoL.mjs → formatBytes-CZv_XyCY.mjs} +1 -1
  76. package/dist/client/{formatBytes-tQBEnPoL.mjs.map → formatBytes-CZv_XyCY.mjs.map} +1 -1
  77. package/dist/client/{helpers--JWXi40U.mjs → helpers-1PpYf-fC.mjs} +1 -1
  78. package/dist/client/{helpers--JWXi40U.mjs.map → helpers-1PpYf-fC.mjs.map} +1 -1
  79. package/dist/client/hooks-dSArr2Ca.mjs +2 -0
  80. package/dist/client/images-CKqIXUq52.mjs +1873 -0
  81. package/dist/client/images-CKqIXUq52.mjs.map +1 -0
  82. package/dist/client/{images-tYfyOkX8.mjs → images-C_dX7nY6.mjs} +3 -3
  83. package/dist/client/{images-tYfyOkX8.mjs.map → images-C_dX7nY6.mjs.map} +1 -1
  84. package/dist/client/{images-CTLCY-yY.mjs → images-CenluYV8.mjs} +2 -2
  85. package/dist/client/{images-CTLCY-yY.mjs.map → images-CenluYV8.mjs.map} +1 -1
  86. package/dist/client/{images-DM9I8G0p.mjs → images-NBf2bV43.mjs} +2 -2
  87. package/dist/client/{images-DM9I8G0p.mjs.map → images-NBf2bV43.mjs.map} +1 -1
  88. package/dist/client/index.js +428 -477
  89. package/dist/client/index.js.map +1 -1
  90. package/dist/client/{md-BivyCkGC.mjs → md-CYTrL5dq.mjs} +58 -22
  91. package/dist/client/{md-BivyCkGC.mjs.map → md-CYTrL5dq.mjs.map} +1 -1
  92. package/dist/client/{network-rYLHyf15.mjs → network-DuZm76BZ.mjs} +2 -2
  93. package/dist/client/{network-rYLHyf15.mjs.map → network-DuZm76BZ.mjs.map} +1 -1
  94. package/dist/client/objects-BJM6YeuF.mjs +5708 -0
  95. package/dist/client/objects-BJM6YeuF.mjs.map +1 -0
  96. package/dist/client/objects-gxSjvbvF.mjs +101 -0
  97. package/dist/client/objects-gxSjvbvF.mjs.map +1 -0
  98. package/dist/client/objects-o2Cj_ndZ.mjs +8 -0
  99. package/dist/client/objects-o2Cj_ndZ.mjs.map +1 -0
  100. package/dist/client/{pca-COmKvp3J.mjs → pca-Bl8NmoVZ.mjs} +2 -2
  101. package/dist/client/{pca-COmKvp3J.mjs.map → pca-Bl8NmoVZ.mjs.map} +1 -1
  102. package/dist/client/pca-RSiWpJs9.mjs +182 -0
  103. package/dist/client/pca-RSiWpJs9.mjs.map +1 -0
  104. package/dist/client/projects-CgclWI16.mjs +105 -0
  105. package/dist/client/projects-CgclWI16.mjs.map +1 -0
  106. package/dist/client/{projects-DI_L4oDw.mjs → projects-D2iewAzu.mjs} +2 -2
  107. package/dist/client/{projects-DI_L4oDw.mjs.map → projects-D2iewAzu.mjs.map} +1 -1
  108. package/dist/client/{projects-Dl5XkXUP.mjs → projects-pe2_dCnV.mjs} +3 -3
  109. package/dist/client/{projects-Dl5XkXUP.mjs.map → projects-pe2_dCnV.mjs.map} +1 -1
  110. package/dist/client/{projects-HoQ0gE5Y.mjs → projects-yiK0HGSA.mjs} +2 -2
  111. package/dist/client/{projects-HoQ0gE5Y.mjs.map → projects-yiK0HGSA.mjs.map} +1 -1
  112. package/dist/client/{securitygroups-BjkmHk2J.mjs → securitygroups-DahZkVYQ.mjs} +139 -140
  113. package/dist/client/{securitygroups-BjkmHk2J.mjs.map → securitygroups-DahZkVYQ.mjs.map} +1 -1
  114. package/dist/client/{trpcClient-BxguzNYF.mjs → trpcClient-BzPUgiM2.mjs} +1 -1
  115. package/dist/client/{trpcClient-BxguzNYF.mjs.map → trpcClient-BzPUgiM2.mjs.map} +1 -1
  116. package/dist/client/{useErrorTranslation-TZVwIAzq.mjs → useErrorTranslation-Dc0eE8Zt.mjs} +1 -1
  117. package/dist/client/{useErrorTranslation-TZVwIAzq.mjs.map → useErrorTranslation-Dc0eE8Zt.mjs.map} +1 -1
  118. package/dist/client/useListWithFiltering-DaYcu5AB.mjs +157 -0
  119. package/dist/client/useListWithFiltering-DaYcu5AB.mjs.map +1 -0
  120. package/dist/client/{useModal-DxxlilRm.mjs → useModal-DCs1OJh7.mjs} +1 -1
  121. package/dist/client/{useModal-DxxlilRm.mjs.map → useModal-DCs1OJh7.mjs.map} +1 -1
  122. package/dist/client/{useProjectId-OQv2KBbG.mjs → useProjectId-DBc5lpoU.mjs} +1 -1
  123. package/dist/client/{useProjectId-OQv2KBbG.mjs.map → useProjectId-DBc5lpoU.mjs.map} +1 -1
  124. package/dist/server/index.js +1285 -435
  125. package/package.json +5 -4
  126. package/dist/client/DeleteFlavorModal-BusYn32r.mjs +0 -629
  127. package/dist/client/DeleteFlavorModal-BusYn32r.mjs.map +0 -1
  128. package/dist/client/ImageToastNotifications-BG9LPnXf.mjs +0 -1267
  129. package/dist/client/ImageToastNotifications-BG9LPnXf.mjs.map +0 -1
  130. package/dist/client/ListToolbar-BojRTNbo.mjs +0 -129
  131. package/dist/client/ListToolbar-BojRTNbo.mjs.map +0 -1
  132. package/dist/client/_flavorId-BRonXvCo.mjs +0 -188
  133. package/dist/client/_flavorId-BRonXvCo.mjs.map +0 -1
  134. package/dist/client/_flavorId-BoNcxYmF.mjs.map +0 -1
  135. package/dist/client/_imageId-BL0I5_pv.mjs +0 -527
  136. package/dist/client/_imageId-BL0I5_pv.mjs.map +0 -1
  137. package/dist/client/_pcaId-CbBhBrX1.mjs +0 -466
  138. package/dist/client/_pcaId-CbBhBrX1.mjs.map +0 -1
  139. package/dist/client/_projectId-5NiasyXm.mjs +0 -26
  140. package/dist/client/_projectId-5NiasyXm.mjs.map +0 -1
  141. package/dist/client/_projectId-BwLMEMGC.mjs.map +0 -1
  142. package/dist/client/_projectId-D35MN1kY.mjs +0 -316
  143. package/dist/client/_projectId-D35MN1kY.mjs.map +0 -1
  144. package/dist/client/about-DLn1ShhF.mjs +0 -92
  145. package/dist/client/aurora-CRcxVUCo.mjs +0 -19
  146. package/dist/client/build-BJDfnAyi.mjs.map +0 -1
  147. package/dist/client/constants-B-P2r5F1.mjs +0 -128
  148. package/dist/client/constants-B-P2r5F1.mjs.map +0 -1
  149. package/dist/client/containers-BjWqjNOx.mjs.map +0 -1
  150. package/dist/client/containers-DsRWc1L5.mjs +0 -7
  151. package/dist/client/containers-DsRWc1L5.mjs.map +0 -1
  152. package/dist/client/containers-J7WFA18U.mjs +0 -3120
  153. package/dist/client/containers-J7WFA18U.mjs.map +0 -1
  154. package/dist/client/flavors-qvgPSI7J.mjs +0 -613
  155. package/dist/client/flavors-qvgPSI7J.mjs.map +0 -1
  156. package/dist/client/hooks-DEjb9d1F.mjs +0 -2
  157. package/dist/client/images-CSFfefAu.mjs +0 -1901
  158. package/dist/client/images-CSFfefAu.mjs.map +0 -1
  159. package/dist/client/objects-BciXwZ00.mjs +0 -86
  160. package/dist/client/objects-BciXwZ00.mjs.map +0 -1
  161. package/dist/client/objects-Cdew99tK.mjs +0 -8
  162. package/dist/client/objects-Cdew99tK.mjs.map +0 -1
  163. package/dist/client/objects-DaElrban.mjs +0 -5340
  164. package/dist/client/objects-DaElrban.mjs.map +0 -1
  165. package/dist/client/overview-BMhjFMIV.mjs +0 -15
  166. package/dist/client/overview-BMhjFMIV.mjs.map +0 -1
  167. package/dist/client/overview-BYIRj7_X.mjs +0 -15
  168. package/dist/client/overview-BYIRj7_X.mjs.map +0 -1
  169. package/dist/client/overview-DRCKNBH2.mjs +0 -15
  170. package/dist/client/overview-DRCKNBH2.mjs.map +0 -1
  171. package/dist/client/overview-urYLOVQE.mjs +0 -173
  172. package/dist/client/overview-urYLOVQE.mjs.map +0 -1
  173. package/dist/client/pca-oc7J0_Xd.mjs +0 -167
  174. package/dist/client/pca-oc7J0_Xd.mjs.map +0 -1
  175. package/dist/client/projects-BUabCzvw.mjs +0 -95
  176. package/dist/client/projects-BUabCzvw.mjs.map +0 -1
  177. package/dist/client/useListWithFiltering-CbhHJO4V.mjs +0 -32
  178. package/dist/client/useListWithFiltering-CbhHJO4V.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"securitygroups-BjkmHk2J.mjs","names":["React","useState","Modal","Button","ModalFooter","ButtonRow","Message","TextInput","DeleteSecurityGroupDialog","isOpen","onClose","securityGroup","onDelete","isDeleting","error","useLingui","confirmationText","setConfirmationText","deleteWord","t","isDeleteEnabled","toLowerCase","securityGroupName","name","id","handleDelete","e","preventDefault","handleClose","open","onCancel","size","title","modalFooter","className","variant","onClick","disabled","data-testid","div","dismissible","p","strong","value","onChange","target","placeholder","autoComplete","DataGridCell","DataGridRow","PopupMenu","PopupMenuItem","PopupMenuOptions","SecurityGroupTableRow","securityGroup","sg","permissions","onEdit","onDelete","onViewDetails","isReadOnly","useLingui","BooleanValue","value","span","t","handleShowDetails","data-testid","id","onClick","className","div","p","name","description","shared","project_id","stateful","e","stopPropagation","label","canUpdate","canDelete","useState","useEffect","useRef","DataGrid","DataGridHeadCell","DataGridRow","Stack","Spinner","useNavigate","useProjectId","EditSecurityGroupModal","DeleteSecurityGroupDialog","SecurityGroupTableRow","SecurityGroupListContainer","securityGroups","isLoading","isError","error","permissions","onDeleteSecurityGroup","isDeletingSecurityGroup","deleteError","onUpdateSecurityGroup","isUpdatingSecurityGroup","updateError","currentProjectId","useLingui","navigate","projectId","selectedSecurityGroup","setSelectedSecurityGroup","editModalOpen","setEditModalOpen","deleteDialogOpen","setDeleteDialogOpen","prevIsDeletingRef","prevIsUpdatingRef","handleEdit","sg","handleDelete","handleViewDetails","to","params","securityGroupId","id","closeEditModal","closeDeleteDialog","deletionJustFinished","current","updateJustFinished","className","distribution","alignment","direction","variant","size","message","t","length","columns","map","label","isReadOnly","Boolean","project_id","securityGroup","onEdit","onDelete","onViewDetails","open","onClose","onUpdate","data","isOpen","isDeleting","React","useState","Modal","Form","FormRow","FormSection","TextInput","Checkbox","Button","ButtonRow","Spinner","ModalFooter","Textarea","Message","defaultSecurityGroupValues","name","description","stateful","CreateSecurityGroupModal","isOpen","onClose","onCreate","isLoading","error","useLingui","properties","setProperties","errors","setErrors","handleInputChange","e","value","type","target","checked","prev","newErrors","validateForm","trim","t","Object","keys","length","handleSubmit","preventDefault","securityGroupData","undefined","handleClose","open","onCancel","size","title","modalFooter","className","variant","onClick","disabled","data-testid","dismissible","div","span","id","label","onChange","required","errortext","placeholder","rows","useState","useNavigate","Button","trpcReact","ListToolbar","buildFilterParams","useListWithFiltering","useProjectId","SecurityGroupListContainer","CreateSecurityGroupModal","SECURITY_GROUP_SHARED","TRUE","FALSE","SecurityGroups","useLingui","navigate","projectId","createModalOpen","setCreateModalOpen","deleteError","setDeleteError","createError","setCreateError","updateError","setUpdateError","searchTerm","sortSettings","filterSettings","handleSearchChange","handleSortChange","handleFilterChange","defaultSortKey","defaultSortDir","sortOptions","label","t","value","filters","displayName","filterName","values","Object","supportsMultiValue","utils","useUtils","permissions","canCreate","canUpdate","canDelete","canManageAccess","data","securityGroups","isLoading","isError","error","network","securityGroup","list","useQuery","project_id","sort_key","sortBy","sort_dir","sortDirection","enabled","createSecurityGroupMutation","create","useMutation","onSuccess","createdSecurityGroup","invalidate","to","params","securityGroupId","id","onError","message","deleteSecurityGroupMutation","deleteById","updateSecurityGroupMutation","update","handleCreateSecurityGroup","securityGroupData","mutateAsync","handleDeleteSecurityGroup","mutate","handleUpdateSecurityGroup","div","className","onSort","onFilter","onSearch","actions","onClick","variant","onCreateClick","onDeleteSecurityGroup","isDeletingSecurityGroup","isPending","onUpdateSecurityGroup","isUpdatingSecurityGroup","currentProjectId","isOpen","onClose","onCreate","useLingui","SecurityGroups","ContentHeading","RouteComponent","t","component"],"sources":["../../src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/-modals/DeleteSecurityGroupDialog.tsx","../../src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/SecurityGroupTableRow.tsx","../../src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/SecurityGroupListContainer.tsx","../../src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/-modals/CreateSecurityGroupModal.tsx","../../src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/SecurityGroupsList.tsx","../../src/client/routes/_auth/projects/$projectId/network/securitygroups/index.tsx?tsr-split=component"],"sourcesContent":["import React, { useState } from \"react\"\nimport { Modal, Button, ModalFooter, ButtonRow, Message, TextInput } from \"@cloudoperators/juno-ui-components\"\nimport { Trans, useLingui } from \"@lingui/react/macro\"\nimport type { SecurityGroup } from \"@/server/Network/types/securityGroup\"\n\ninterface DeleteSecurityGroupDialogProps {\n isOpen: boolean\n securityGroup: SecurityGroup\n onClose: () => void\n onDelete: (securityGroupId: string) => void\n isDeleting?: boolean\n error?: string | null\n}\n\nexport const DeleteSecurityGroupDialog: React.FC<DeleteSecurityGroupDialogProps> = ({\n isOpen,\n onClose,\n securityGroup,\n onDelete,\n isDeleting = false,\n error = null,\n}) => {\n const { t } = useLingui()\n const [confirmationText, setConfirmationText] = useState(\"\")\n\n const deleteWord = t`delete`\n const isDeleteEnabled = confirmationText.toLowerCase() === deleteWord.toLowerCase()\n const securityGroupName = securityGroup.name || securityGroup.id\n\n const handleDelete = (e: React.MouseEvent<HTMLElement>) => {\n e.preventDefault()\n if (isDeleteEnabled && !isDeleting) {\n onDelete(securityGroup.id)\n }\n }\n\n const handleClose = () => {\n setConfirmationText(\"\")\n onClose()\n }\n\n return (\n <Modal\n open={isOpen}\n onCancel={handleClose}\n size=\"small\"\n title={t`Delete Security Group \"${securityGroupName}\"`}\n modalFooter={\n <ModalFooter className=\"flex justify-end\">\n <ButtonRow>\n <Button variant=\"default\" onClick={handleClose} disabled={isDeleting}>\n <Trans>Cancel</Trans>\n </Button>\n <Button\n variant=\"primary-danger\"\n onClick={handleDelete}\n disabled={!isDeleteEnabled || isDeleting}\n data-testid=\"confirm-delete-button\"\n >\n {isDeleting ? <Trans>Deleting...</Trans> : <Trans>Delete</Trans>}\n </Button>\n </ButtonRow>\n </ModalFooter>\n }\n >\n <div>\n {/* Error Message */}\n {error && (\n <Message dismissible={false} variant=\"error\" className=\"mt-4\">\n {error}\n </Message>\n )}\n\n {/* Warning */}\n <Trans>This action cannot be undone. The security group will be permanently deleted.</Trans>\n\n {/* Confirmation Input */}\n <div className=\"mt-4\">\n <p className=\"mb-2 text-sm\">\n <Trans>\n Type <strong>{deleteWord}</strong> to confirm:\n </Trans>\n </p>\n <TextInput\n id=\"confirmation\"\n name=\"confirmation\"\n value={confirmationText}\n onChange={(e) => setConfirmationText(e.target.value)}\n placeholder={deleteWord}\n disabled={isDeleting}\n autoComplete=\"off\"\n data-testid=\"delete-confirmation-input\"\n />\n </div>\n </div>\n </Modal>\n )\n}\n","import {\n DataGridCell,\n DataGridRow,\n PopupMenu,\n PopupMenuItem,\n PopupMenuOptions,\n} from \"@cloudoperators/juno-ui-components\"\nimport { useLingui, Trans } from \"@lingui/react/macro\"\nimport type { SecurityGroup } from \"@/server/Network/types/securityGroup\"\n\nexport interface SecurityGroupPermissions {\n canCreate: boolean\n canUpdate: boolean\n canDelete: boolean\n canManageAccess: boolean\n}\n\ninterface SecurityGroupTableRowProps {\n securityGroup: SecurityGroup\n permissions: SecurityGroupPermissions\n onEdit: (sg: SecurityGroup) => void\n onDelete: (sg: SecurityGroup) => void\n onViewDetails?: (sg: SecurityGroup) => void\n isReadOnly?: boolean\n}\n\nexport function SecurityGroupTableRow({\n securityGroup: sg,\n permissions,\n onEdit,\n onDelete,\n onViewDetails,\n isReadOnly = false,\n}: SecurityGroupTableRowProps) {\n const { t } = useLingui()\n\n const BooleanValue = ({ value }: { value: boolean | undefined }) => <span>{value ? t`Yes` : t`No`}</span>\n\n const handleShowDetails = () => {\n if (onViewDetails) {\n onViewDetails(sg)\n }\n }\n\n return (\n <DataGridRow\n key={sg.id}\n data-testid={`security-group-row-${sg.id}`}\n onClick={handleShowDetails}\n className=\"hover:bg-theme-background-lvl-2 cursor-pointer\"\n >\n <DataGridCell>\n <div>\n <p className=\"text-md\">{sg.name}</p>\n <p className=\"text-theme-light text-xs\">{sg.id}</p>\n </div>\n </DataGridCell>\n <DataGridCell>{sg.description || t`—`}</DataGridCell>\n <DataGridCell>\n <BooleanValue value={sg.shared} />\n {sg.shared && (\n <p>\n <Trans>Owner</Trans>: <span className=\"text-theme-light text-xs\">{sg.project_id}</span>\n </p>\n )}\n </DataGridCell>\n <DataGridCell>\n <BooleanValue value={sg.stateful} />\n </DataGridCell>\n <DataGridCell onClick={(e) => e.stopPropagation()} className=\"items-end justify-end pr-0\">\n <PopupMenu>\n <PopupMenuOptions>\n <PopupMenuItem label={t`Show Details`} onClick={() => handleShowDetails()} />\n {permissions.canUpdate && !isReadOnly && <PopupMenuItem label={t`Edit`} onClick={() => onEdit(sg)} />}\n {permissions.canDelete && !isReadOnly && <PopupMenuItem label={t`Delete`} onClick={() => onDelete(sg)} />}\n </PopupMenuOptions>\n </PopupMenu>\n </DataGridCell>\n </DataGridRow>\n )\n}\n","import { useState, useEffect, useRef } from \"react\"\nimport { DataGrid, DataGridHeadCell, DataGridRow, Stack, Spinner } from \"@cloudoperators/juno-ui-components\"\nimport { Trans, useLingui } from \"@lingui/react/macro\"\nimport { useNavigate } from \"@tanstack/react-router\"\nimport { useProjectId } from \"@/client/hooks\"\nimport type { SecurityGroup } from \"@/server/Network/types/securityGroup\"\nimport type { UpdateSecurityGroupInput } from \"@/server/Network/types/securityGroup\"\nimport { EditSecurityGroupModal } from \"./-modals/EditSecurityGroupModal\"\nimport { DeleteSecurityGroupDialog } from \"./-modals/DeleteSecurityGroupDialog\"\nimport { SecurityGroupTableRow, type SecurityGroupPermissions } from \"./SecurityGroupTableRow\"\n\ninterface SecurityGroupListContainerProps {\n securityGroups: SecurityGroup[]\n isLoading: boolean\n isError: boolean\n error: { message?: string } | null\n permissions: SecurityGroupPermissions\n onCreateClick?: () => void\n onDeleteSecurityGroup?: (securityGroupId: string) => void\n isDeletingSecurityGroup?: boolean\n deleteError?: string | null\n onUpdateSecurityGroup?: (\n securityGroupId: string,\n data: Omit<UpdateSecurityGroupInput, \"securityGroupId\" | \"project_id\">\n ) => void\n isUpdatingSecurityGroup?: boolean\n updateError?: string | null\n currentProjectId?: string\n}\n\nexport const SecurityGroupListContainer = ({\n securityGroups,\n isLoading,\n isError,\n error,\n permissions,\n onDeleteSecurityGroup,\n isDeletingSecurityGroup = false,\n deleteError = null,\n onUpdateSecurityGroup,\n isUpdatingSecurityGroup = false,\n updateError = null,\n currentProjectId,\n}: SecurityGroupListContainerProps) => {\n const { t } = useLingui()\n const navigate = useNavigate()\n const projectId = useProjectId()\n const [selectedSecurityGroup, setSelectedSecurityGroup] = useState<SecurityGroup | null>(null)\n const [editModalOpen, setEditModalOpen] = useState(false)\n const [deleteDialogOpen, setDeleteDialogOpen] = useState(false)\n const prevIsDeletingRef = useRef<boolean>(false)\n const prevIsUpdatingRef = useRef<boolean>(false)\n\n const handleEdit = (sg: SecurityGroup) => {\n setSelectedSecurityGroup(sg)\n setEditModalOpen(true)\n }\n\n const handleDelete = (sg: SecurityGroup) => {\n setSelectedSecurityGroup(sg)\n setDeleteDialogOpen(true)\n }\n\n const handleViewDetails = (sg: SecurityGroup) => {\n navigate({\n to: \"/projects/$projectId/network/securitygroups/$securityGroupId\",\n params: { projectId, securityGroupId: sg.id },\n })\n }\n\n const closeEditModal = () => {\n setSelectedSecurityGroup(null)\n setEditModalOpen(false)\n }\n\n const closeDeleteDialog = () => {\n setSelectedSecurityGroup(null)\n setDeleteDialogOpen(false)\n }\n\n // Close delete dialog when deletion completes successfully\n useEffect(() => {\n // Check if deletion just finished (was deleting before, now not deleting)\n const deletionJustFinished = prevIsDeletingRef.current && !isDeletingSecurityGroup\n\n // Close dialog if deletion just finished and there's no error\n if (deletionJustFinished && deleteDialogOpen && !deleteError) {\n closeDeleteDialog()\n }\n\n // Update the ref for next render\n prevIsDeletingRef.current = isDeletingSecurityGroup\n }, [isDeletingSecurityGroup, deleteError, deleteDialogOpen])\n\n // Close edit modal when update completes successfully\n useEffect(() => {\n // Check if update just finished (was updating before, now not updating)\n const updateJustFinished = prevIsUpdatingRef.current && !isUpdatingSecurityGroup\n\n // Close modal if update just finished and there's no error\n if (updateJustFinished && editModalOpen && !updateError) {\n closeEditModal()\n }\n\n // Update the ref for next render\n prevIsUpdatingRef.current = isUpdatingSecurityGroup\n }, [isUpdatingSecurityGroup, updateError, editModalOpen])\n\n // Loading state\n if (isLoading) {\n return (\n <Stack className=\"py-8\" distribution=\"center\" alignment=\"center\" direction=\"vertical\">\n <Spinner variant=\"primary\" size=\"large\" className=\"mb-2\" />\n <Trans>Loading...</Trans>\n </Stack>\n )\n }\n\n // Error state\n if (isError) {\n return (\n <Stack className=\"py-8\" distribution=\"center\" alignment=\"center\" direction=\"vertical\">\n {error?.message ?? t`Failed to load security groups`}\n </Stack>\n )\n }\n\n // Empty state\n if (securityGroups.length === 0) {\n return <Trans>There are no groups</Trans>\n }\n\n return (\n <>\n <DataGrid columns={5}>\n <DataGridRow>\n {[t`Name`, t`Description`, t`Shared`, t`Stateful`, \"\"].map((label) => (\n <DataGridHeadCell key={label}>{label}</DataGridHeadCell>\n ))}\n </DataGridRow>\n {securityGroups.map((sg) => {\n // Compute isReadOnly only when the security group has an explicit project owner\n const isReadOnly = Boolean(currentProjectId && sg.project_id && sg.project_id !== currentProjectId)\n\n return (\n <SecurityGroupTableRow\n key={sg.id}\n securityGroup={sg}\n permissions={permissions}\n onEdit={handleEdit}\n onDelete={handleDelete}\n onViewDetails={handleViewDetails}\n isReadOnly={isReadOnly}\n />\n )\n })}\n </DataGrid>\n\n {selectedSecurityGroup && (\n <>\n <EditSecurityGroupModal\n securityGroup={selectedSecurityGroup}\n open={editModalOpen}\n onClose={closeEditModal}\n onUpdate={async (id, data) => {\n if (onUpdateSecurityGroup) {\n await onUpdateSecurityGroup(id, data)\n }\n }}\n isLoading={isUpdatingSecurityGroup}\n error={updateError}\n />\n <DeleteSecurityGroupDialog\n securityGroup={selectedSecurityGroup}\n isOpen={deleteDialogOpen}\n onClose={closeDeleteDialog}\n onDelete={(id) => {\n if (onDeleteSecurityGroup) {\n onDeleteSecurityGroup(id)\n }\n }}\n isDeleting={isDeletingSecurityGroup}\n error={deleteError}\n />\n </>\n )}\n </>\n )\n}\n","import React, { useState } from \"react\"\nimport { Trans, useLingui } from \"@lingui/react/macro\"\nimport {\n Modal,\n Form,\n FormRow,\n FormSection,\n TextInput,\n Checkbox,\n Button,\n ButtonRow,\n Spinner,\n ModalFooter,\n Textarea,\n Message,\n} from \"@cloudoperators/juno-ui-components\"\nimport { CreateSecurityGroupInput } from \"@/server/Network/types/securityGroup\"\n\ninterface CreateSecurityGroupModalProps {\n isOpen: boolean\n onClose: () => void\n onCreate: (securityGroupData: Omit<CreateSecurityGroupInput, \"project_id\">) => Promise<void>\n isLoading?: boolean\n error?: string | null\n}\n\ninterface SecurityGroupProperties {\n name: string\n description: string\n stateful: boolean\n}\n\nconst defaultSecurityGroupValues: SecurityGroupProperties = {\n name: \"\",\n description: \"\",\n stateful: true,\n}\n\nexport const CreateSecurityGroupModal: React.FC<CreateSecurityGroupModalProps> = ({\n isOpen,\n onClose,\n onCreate,\n isLoading = false,\n error = null,\n}) => {\n const { t } = useLingui()\n\n const [properties, setProperties] = useState<SecurityGroupProperties>({ ...defaultSecurityGroupValues })\n const [errors, setErrors] = useState<{ [key: string]: string }>({})\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {\n const { name, value, type } = e.target\n const checked = (e.target as HTMLInputElement).checked\n\n setProperties((prev) => ({\n ...prev,\n [name]: type === \"checkbox\" ? checked : value,\n }))\n\n if (errors[name]) {\n setErrors((prev) => {\n const newErrors = { ...prev }\n delete newErrors[name]\n return newErrors\n })\n }\n }\n\n const validateForm = (): boolean => {\n const newErrors: { [key: string]: string } = {}\n\n if (!properties.name || properties.name.trim() === \"\") {\n newErrors.name = t`Security group name is required`\n }\n\n setErrors(newErrors)\n return Object.keys(newErrors).length === 0\n }\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault()\n\n if (!validateForm()) {\n return\n }\n\n const securityGroupData: Omit<CreateSecurityGroupInput, \"project_id\"> = {\n name: properties.name.trim(),\n description: properties.description.trim() || undefined,\n stateful: properties.stateful,\n }\n\n await onCreate(securityGroupData)\n handleClose()\n }\n\n const handleClose = () => {\n setProperties({ ...defaultSecurityGroupValues })\n setErrors({})\n onClose()\n }\n\n return (\n <Modal\n open={isOpen}\n onCancel={handleClose}\n size=\"large\"\n title={t`Create Security Group`}\n modalFooter={\n <ModalFooter className=\"flex justify-end\">\n <ButtonRow>\n <Button variant=\"default\" onClick={handleClose} disabled={isLoading}>\n <Trans>Cancel</Trans>\n </Button>\n <Button\n variant=\"primary\"\n onClick={(e) => {\n handleSubmit(e)\n }}\n disabled={isLoading}\n data-testid=\"create-security-group-button\"\n >\n {isLoading ? <Spinner size=\"small\" /> : <Trans>Create Security Group</Trans>}\n </Button>\n </ButtonRow>\n </ModalFooter>\n }\n >\n {/* Error Message */}\n {error && (\n <Message dismissible={false} variant=\"error\" className=\"mb-4\">\n {error}\n </Message>\n )}\n\n {isLoading && (\n <div className=\"mb-4 flex items-center justify-center gap-2\">\n <Spinner variant=\"primary\" />\n <span className=\"text-theme-high text-sm\">\n <Trans>Creating security group...</Trans>\n </span>\n </div>\n )}\n\n {!isLoading && (\n <Form className=\"mb-6\">\n <FormSection className=\"mb-6\">\n <FormRow className=\"mb-6\">\n <TextInput\n id=\"name\"\n name=\"name\"\n label={t`Name`}\n value={properties.name}\n onChange={handleInputChange}\n required\n errortext={errors.name}\n placeholder={t`Type name`}\n disabled={isLoading}\n />\n </FormRow>\n\n <FormRow className=\"mb-6\">\n <Textarea\n id=\"description\"\n name=\"description\"\n label={t`Description`}\n value={properties.description}\n onChange={handleInputChange}\n placeholder={t`Description`}\n disabled={isLoading}\n rows={3}\n />\n </FormRow>\n\n <FormRow className=\"mb-0\">\n <Checkbox\n id=\"stateful\"\n name=\"stateful\"\n label={t`Stateful`}\n checked={properties.stateful}\n onChange={handleInputChange}\n disabled={isLoading}\n />\n </FormRow>\n </FormSection>\n </Form>\n )}\n </Modal>\n )\n}\n","import { useState } from \"react\"\nimport { Trans, useLingui } from \"@lingui/react/macro\"\nimport { useNavigate } from \"@tanstack/react-router\"\nimport { Button } from \"@cloudoperators/juno-ui-components\"\nimport { trpcReact } from \"@/client/trpcClient\"\nimport { ListToolbar } from \"@/client/components/ListToolbar\"\nimport { buildFilterParams } from \"@/client/utils/buildFilterParams\"\nimport { useListWithFiltering } from \"@/client/utils/useListWithFiltering\"\nimport { useProjectId } from \"@/client/hooks\"\nimport { SecurityGroupListContainer } from \"./SecurityGroupListContainer\"\nimport { CreateSecurityGroupModal } from \"./-modals/CreateSecurityGroupModal\"\nimport { CreateSecurityGroupInput, UpdateSecurityGroupInput } from \"@/server/Network/types/securityGroup\"\n\n// Security group shared filter constants\nconst SECURITY_GROUP_SHARED = {\n TRUE: \"true\",\n FALSE: \"false\",\n} as const\n\ntype SecurityGroupSortKey = \"name\" | \"project_id\"\n\nexport const SecurityGroups = () => {\n const { t } = useLingui()\n const navigate = useNavigate()\n const projectId = useProjectId()\n\n const [createModalOpen, setCreateModalOpen] = useState(false)\n const [deleteError, setDeleteError] = useState<string | null>(null)\n const [createError, setCreateError] = useState<string | null>(null)\n const [updateError, setUpdateError] = useState<string | null>(null)\n\n const { searchTerm, sortSettings, filterSettings, handleSearchChange, handleSortChange, handleFilterChange } =\n useListWithFiltering<SecurityGroupSortKey>({\n defaultSortKey: \"name\",\n defaultSortDir: \"asc\",\n sortOptions: [\n { label: t`Name`, value: \"name\" },\n { label: t`Project id`, value: \"project_id\" },\n ],\n filterSettings: {\n filters: [\n {\n displayName: t`Shared`,\n filterName: \"shared\",\n values: Object.values(SECURITY_GROUP_SHARED),\n supportsMultiValue: false,\n },\n ],\n },\n })\n\n const utils = trpcReact.useUtils()\n\n // TODO: replace with trpc.network.canUser when security group permissions are available\n const permissions = {\n canCreate: true,\n canUpdate: true,\n canDelete: true,\n canManageAccess: true,\n }\n\n const {\n data: securityGroups = [],\n isLoading,\n isError,\n error,\n } = trpcReact.network.securityGroup.list.useQuery(\n {\n project_id: projectId || \"\",\n sort_key: sortSettings.sortBy,\n sort_dir: sortSettings.sortDirection,\n ...buildFilterParams(filterSettings),\n ...(searchTerm ? { searchTerm } : {}),\n },\n {\n enabled: !!projectId,\n }\n )\n\n const createSecurityGroupMutation = trpcReact.network.securityGroup.create.useMutation({\n onSuccess: (createdSecurityGroup) => {\n // Invalidate and refetch the security groups list\n utils.network.securityGroup.list.invalidate()\n setCreateError(null)\n\n // Navigate to the details page of the newly created security group\n navigate({\n to: \"/projects/$projectId/network/securitygroups/$securityGroupId\",\n params: {\n projectId,\n securityGroupId: createdSecurityGroup.id,\n },\n })\n },\n onError: (error) => {\n // Backend handles error parsing, just display the message\n setCreateError(error.message || t`Failed to create security group`)\n },\n })\n\n const deleteSecurityGroupMutation = trpcReact.network.securityGroup.deleteById.useMutation({\n onSuccess: () => {\n // Invalidate and refetch the security groups list\n utils.network.securityGroup.list.invalidate()\n setDeleteError(null)\n },\n onError: (error) => {\n // Backend handles error parsing, just display the message\n setDeleteError(error.message || t`Failed to delete security group`)\n },\n })\n\n const updateSecurityGroupMutation = trpcReact.network.securityGroup.update.useMutation({\n onSuccess: () => {\n // Invalidate and refetch the security groups list\n utils.network.securityGroup.list.invalidate()\n setUpdateError(null)\n },\n onError: (error) => {\n // Backend handles error parsing, just display the message\n setUpdateError(error.message || t`Failed to update security group`)\n },\n })\n\n const handleCreateSecurityGroup = async (securityGroupData: Omit<CreateSecurityGroupInput, \"project_id\">) => {\n setCreateError(null)\n await createSecurityGroupMutation.mutateAsync({ project_id: projectId, ...securityGroupData })\n }\n\n const handleDeleteSecurityGroup = (securityGroupId: string) => {\n setDeleteError(null)\n deleteSecurityGroupMutation.mutate({ project_id: projectId, securityGroupId })\n }\n\n const handleUpdateSecurityGroup = async (\n securityGroupId: string,\n data: Omit<UpdateSecurityGroupInput, \"securityGroupId\" | \"project_id\">\n ) => {\n setUpdateError(null)\n await updateSecurityGroupMutation.mutateAsync({ project_id: projectId, securityGroupId, ...data })\n }\n\n return (\n <div className=\"relative\">\n <ListToolbar\n sortSettings={sortSettings}\n filterSettings={filterSettings}\n searchTerm={searchTerm}\n onSort={handleSortChange}\n onFilter={handleFilterChange}\n onSearch={handleSearchChange}\n actions={\n permissions.canCreate && (\n <Button onClick={() => setCreateModalOpen(true)} variant=\"primary\">\n <Trans>Create Security Group</Trans>\n </Button>\n )\n }\n />\n\n <SecurityGroupListContainer\n securityGroups={securityGroups}\n isLoading={isLoading}\n isError={isError}\n error={error}\n permissions={permissions}\n onCreateClick={() => setCreateModalOpen(true)}\n onDeleteSecurityGroup={handleDeleteSecurityGroup}\n isDeletingSecurityGroup={deleteSecurityGroupMutation.isPending}\n deleteError={deleteError}\n onUpdateSecurityGroup={handleUpdateSecurityGroup}\n isUpdatingSecurityGroup={updateSecurityGroupMutation.isPending}\n updateError={updateError}\n currentProjectId={projectId}\n />\n\n <CreateSecurityGroupModal\n isOpen={createModalOpen}\n onClose={() => setCreateModalOpen(false)}\n onCreate={handleCreateSecurityGroup}\n isLoading={createSecurityGroupMutation.isPending}\n error={createError}\n />\n </div>\n )\n}\n","import { createFileRoute } from \"@tanstack/react-router\"\nimport { t } from \"@lingui/core/macro\"\nimport { useLingui } from \"@lingui/react/macro\"\nimport { SecurityGroups } from \"./-components/SecurityGroupsList\"\nimport type { RouteInfo } from \"@/client/routes/routeInfo\"\nimport { ContentHeading } from \"@cloudoperators/juno-ui-components\"\n\nexport const Route = createFileRoute(\"/_auth/projects/$projectId/network/securitygroups/\")({\n staticData: {\n section: \"network\",\n service: \"securitygroups\",\n sectionCrumb: { labelKey: \"Network\" },\n crumb: { labelKey: \"Security Groups\" },\n } satisfies RouteInfo,\n head: () => ({ meta: [{ title: t`Security Groups` }] }),\n component: RouteComponent,\n})\n\nfunction RouteComponent() {\n const { t } = useLingui()\n return (\n <>\n <ContentHeading>{t`Security Groups`}</ContentHeading>\n <SecurityGroups />\n </>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"securitygroups-DahZkVYQ.mjs","names":["React","useState","Modal","Button","ModalFooter","ButtonRow","Message","TextInput","DeleteSecurityGroupDialog","isOpen","onClose","securityGroup","onDelete","isDeleting","error","useLingui","confirmationText","setConfirmationText","deleteWord","t","isDeleteEnabled","toLowerCase","securityGroupName","name","id","handleDelete","e","preventDefault","handleClose","open","onCancel","size","title","modalFooter","className","variant","onClick","disabled","data-testid","div","dismissible","p","strong","value","onChange","target","placeholder","autoComplete","DataGridCell","DataGridRow","PopupMenu","PopupMenuItem","PopupMenuOptions","SecurityGroupTableRow","securityGroup","sg","permissions","onEdit","onDelete","onViewDetails","isReadOnly","useLingui","BooleanValue","value","span","t","handleShowDetails","data-testid","id","onClick","className","div","p","name","description","shared","project_id","stateful","e","stopPropagation","label","canUpdate","canDelete","useState","useEffect","useRef","DataGrid","DataGridHeadCell","DataGridRow","Stack","Spinner","useNavigate","useProjectId","EditSecurityGroupModal","DeleteSecurityGroupDialog","SecurityGroupTableRow","SecurityGroupListContainer","securityGroups","isLoading","isError","error","permissions","onDeleteSecurityGroup","isDeletingSecurityGroup","deleteError","onUpdateSecurityGroup","isUpdatingSecurityGroup","updateError","currentProjectId","useLingui","navigate","projectId","selectedSecurityGroup","setSelectedSecurityGroup","editModalOpen","setEditModalOpen","deleteDialogOpen","setDeleteDialogOpen","prevIsDeletingRef","prevIsUpdatingRef","handleEdit","sg","handleDelete","handleViewDetails","to","params","securityGroupId","id","closeEditModal","closeDeleteDialog","deletionJustFinished","current","updateJustFinished","className","distribution","alignment","direction","variant","size","message","t","length","columns","map","label","isReadOnly","Boolean","project_id","securityGroup","onEdit","onDelete","onViewDetails","open","onClose","onUpdate","data","isOpen","isDeleting","React","useState","Modal","Form","FormRow","FormSection","TextInput","Checkbox","Button","ButtonRow","Spinner","ModalFooter","Textarea","Message","defaultSecurityGroupValues","name","description","stateful","CreateSecurityGroupModal","isOpen","onClose","onCreate","isLoading","error","useLingui","properties","setProperties","errors","setErrors","handleInputChange","e","value","type","target","checked","prev","newErrors","validateForm","trim","t","Object","keys","length","handleSubmit","preventDefault","securityGroupData","undefined","handleClose","open","onCancel","size","title","modalFooter","className","variant","onClick","disabled","data-testid","dismissible","div","span","id","label","onChange","required","errortext","placeholder","rows","useState","useNavigate","Button","trpcReact","ListToolbar","buildFilterParams","useListWithFiltering","useProjectId","SecurityGroupListContainer","CreateSecurityGroupModal","SECURITY_GROUP_SHARED","TRUE","FALSE","SecurityGroups","useLingui","navigate","projectId","createModalOpen","setCreateModalOpen","deleteError","setDeleteError","createError","setCreateError","updateError","setUpdateError","searchTerm","sortSettings","filterSettings","handleSearchChange","handleSortChange","handleFilterChange","defaultSortKey","defaultSortDir","sortOptions","label","t","value","filters","displayName","filterName","values","Object","supportsMultiValue","utils","useUtils","permissions","canCreate","canUpdate","canDelete","canManageAccess","data","securityGroups","isLoading","isError","error","network","securityGroup","list","useQuery","project_id","sort_key","sortBy","sort_dir","sortDirection","enabled","createSecurityGroupMutation","create","useMutation","onSuccess","createdSecurityGroup","invalidate","to","params","securityGroupId","id","onError","message","deleteSecurityGroupMutation","deleteById","updateSecurityGroupMutation","update","handleCreateSecurityGroup","securityGroupData","mutateAsync","handleDeleteSecurityGroup","mutate","handleUpdateSecurityGroup","div","className","onSort","onFilter","onSearch","actions","onClick","variant","onCreateClick","onDeleteSecurityGroup","isDeletingSecurityGroup","isPending","onUpdateSecurityGroup","isUpdatingSecurityGroup","currentProjectId","isOpen","onClose","onCreate","useLingui","SecurityGroups","ContentHeading","RouteComponent","t","component"],"sources":["../../src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/-modals/DeleteSecurityGroupDialog.tsx","../../src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/SecurityGroupTableRow.tsx","../../src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/SecurityGroupListContainer.tsx","../../src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/-modals/CreateSecurityGroupModal.tsx","../../src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/SecurityGroupsList.tsx","../../src/client/routes/_auth/projects/$projectId/network/securitygroups/index.tsx?tsr-split=component"],"sourcesContent":["import React, { useState } from \"react\"\nimport { Modal, Button, ModalFooter, ButtonRow, Message, TextInput } from \"@cloudoperators/juno-ui-components\"\nimport { Trans, useLingui } from \"@lingui/react/macro\"\nimport type { SecurityGroup } from \"@/server/Network/types/securityGroup\"\n\ninterface DeleteSecurityGroupDialogProps {\n isOpen: boolean\n securityGroup: SecurityGroup\n onClose: () => void\n onDelete: (securityGroupId: string) => void\n isDeleting?: boolean\n error?: string | null\n}\n\nexport const DeleteSecurityGroupDialog: React.FC<DeleteSecurityGroupDialogProps> = ({\n isOpen,\n onClose,\n securityGroup,\n onDelete,\n isDeleting = false,\n error = null,\n}) => {\n const { t } = useLingui()\n const [confirmationText, setConfirmationText] = useState(\"\")\n\n const deleteWord = t`delete`\n const isDeleteEnabled = confirmationText.toLowerCase() === deleteWord.toLowerCase()\n const securityGroupName = securityGroup.name || securityGroup.id\n\n const handleDelete = (e: React.MouseEvent<HTMLElement>) => {\n e.preventDefault()\n if (isDeleteEnabled && !isDeleting) {\n onDelete(securityGroup.id)\n }\n }\n\n const handleClose = () => {\n setConfirmationText(\"\")\n onClose()\n }\n\n return (\n <Modal\n open={isOpen}\n onCancel={handleClose}\n size=\"small\"\n title={t`Delete Security Group \"${securityGroupName}\"`}\n modalFooter={\n <ModalFooter className=\"flex justify-end\">\n <ButtonRow>\n <Button variant=\"default\" onClick={handleClose} disabled={isDeleting}>\n <Trans>Cancel</Trans>\n </Button>\n <Button\n variant=\"primary-danger\"\n onClick={handleDelete}\n disabled={!isDeleteEnabled || isDeleting}\n data-testid=\"confirm-delete-button\"\n >\n {isDeleting ? <Trans>Deleting...</Trans> : <Trans>Delete</Trans>}\n </Button>\n </ButtonRow>\n </ModalFooter>\n }\n >\n <div>\n {/* Error Message */}\n {error && (\n <Message dismissible={false} variant=\"error\" className=\"mt-4\">\n {error}\n </Message>\n )}\n\n {/* Warning */}\n <Trans>This action cannot be undone. The security group will be permanently deleted.</Trans>\n\n {/* Confirmation Input */}\n <div className=\"mt-4\">\n <p className=\"mb-2 text-sm\">\n <Trans>\n Type <strong>{deleteWord}</strong> to confirm:\n </Trans>\n </p>\n <TextInput\n id=\"confirmation\"\n name=\"confirmation\"\n value={confirmationText}\n onChange={(e) => setConfirmationText(e.target.value)}\n placeholder={deleteWord}\n disabled={isDeleting}\n autoComplete=\"off\"\n data-testid=\"delete-confirmation-input\"\n />\n </div>\n </div>\n </Modal>\n )\n}\n","import {\n DataGridCell,\n DataGridRow,\n PopupMenu,\n PopupMenuItem,\n PopupMenuOptions,\n} from \"@cloudoperators/juno-ui-components\"\nimport { useLingui, Trans } from \"@lingui/react/macro\"\nimport type { SecurityGroup } from \"@/server/Network/types/securityGroup\"\n\nexport interface SecurityGroupPermissions {\n canCreate: boolean\n canUpdate: boolean\n canDelete: boolean\n canManageAccess: boolean\n}\n\ninterface SecurityGroupTableRowProps {\n securityGroup: SecurityGroup\n permissions: SecurityGroupPermissions\n onEdit: (sg: SecurityGroup) => void\n onDelete: (sg: SecurityGroup) => void\n onViewDetails?: (sg: SecurityGroup) => void\n isReadOnly?: boolean\n}\n\nexport function SecurityGroupTableRow({\n securityGroup: sg,\n permissions,\n onEdit,\n onDelete,\n onViewDetails,\n isReadOnly = false,\n}: SecurityGroupTableRowProps) {\n const { t } = useLingui()\n\n const BooleanValue = ({ value }: { value: boolean | undefined }) => <span>{value ? t`Yes` : t`No`}</span>\n\n const handleShowDetails = () => {\n if (onViewDetails) {\n onViewDetails(sg)\n }\n }\n\n return (\n <DataGridRow\n key={sg.id}\n data-testid={`security-group-row-${sg.id}`}\n onClick={handleShowDetails}\n className=\"hover:bg-theme-background-lvl-2 cursor-pointer\"\n >\n <DataGridCell>\n <div>\n <p className=\"text-md\">{sg.name}</p>\n <p className=\"text-theme-light text-xs\">{sg.id}</p>\n </div>\n </DataGridCell>\n <DataGridCell>{sg.description || t`—`}</DataGridCell>\n <DataGridCell>\n <BooleanValue value={sg.shared} />\n {sg.shared && (\n <p>\n <Trans>Owner</Trans>: <span className=\"text-theme-light text-xs\">{sg.project_id}</span>\n </p>\n )}\n </DataGridCell>\n <DataGridCell>\n <BooleanValue value={sg.stateful} />\n </DataGridCell>\n <DataGridCell onClick={(e) => e.stopPropagation()} className=\"items-end justify-end pr-0\">\n <PopupMenu>\n <PopupMenuOptions>\n <PopupMenuItem label={t`Show Details`} onClick={() => handleShowDetails()} />\n {permissions.canUpdate && !isReadOnly && <PopupMenuItem label={t`Edit`} onClick={() => onEdit(sg)} />}\n {permissions.canDelete && !isReadOnly && <PopupMenuItem label={t`Delete`} onClick={() => onDelete(sg)} />}\n </PopupMenuOptions>\n </PopupMenu>\n </DataGridCell>\n </DataGridRow>\n )\n}\n","import { useState, useEffect, useRef } from \"react\"\nimport { DataGrid, DataGridHeadCell, DataGridRow, Stack, Spinner } from \"@cloudoperators/juno-ui-components\"\nimport { Trans, useLingui } from \"@lingui/react/macro\"\nimport { useNavigate } from \"@tanstack/react-router\"\nimport { useProjectId } from \"@/client/hooks\"\nimport type { SecurityGroup } from \"@/server/Network/types/securityGroup\"\nimport type { UpdateSecurityGroupInput } from \"@/server/Network/types/securityGroup\"\nimport { EditSecurityGroupModal } from \"./-modals/EditSecurityGroupModal\"\nimport { DeleteSecurityGroupDialog } from \"./-modals/DeleteSecurityGroupDialog\"\nimport { SecurityGroupTableRow, type SecurityGroupPermissions } from \"./SecurityGroupTableRow\"\n\ninterface SecurityGroupListContainerProps {\n securityGroups: SecurityGroup[]\n isLoading: boolean\n isError: boolean\n error: { message?: string } | null\n permissions: SecurityGroupPermissions\n onCreateClick?: () => void\n onDeleteSecurityGroup?: (securityGroupId: string) => void\n isDeletingSecurityGroup?: boolean\n deleteError?: string | null\n onUpdateSecurityGroup?: (\n securityGroupId: string,\n data: Omit<UpdateSecurityGroupInput, \"securityGroupId\" | \"project_id\">\n ) => void\n isUpdatingSecurityGroup?: boolean\n updateError?: string | null\n currentProjectId?: string\n}\n\nexport const SecurityGroupListContainer = ({\n securityGroups,\n isLoading,\n isError,\n error,\n permissions,\n onDeleteSecurityGroup,\n isDeletingSecurityGroup = false,\n deleteError = null,\n onUpdateSecurityGroup,\n isUpdatingSecurityGroup = false,\n updateError = null,\n currentProjectId,\n}: SecurityGroupListContainerProps) => {\n const { t } = useLingui()\n const navigate = useNavigate()\n const projectId = useProjectId()\n const [selectedSecurityGroup, setSelectedSecurityGroup] = useState<SecurityGroup | null>(null)\n const [editModalOpen, setEditModalOpen] = useState(false)\n const [deleteDialogOpen, setDeleteDialogOpen] = useState(false)\n const prevIsDeletingRef = useRef<boolean>(false)\n const prevIsUpdatingRef = useRef<boolean>(false)\n\n const handleEdit = (sg: SecurityGroup) => {\n setSelectedSecurityGroup(sg)\n setEditModalOpen(true)\n }\n\n const handleDelete = (sg: SecurityGroup) => {\n setSelectedSecurityGroup(sg)\n setDeleteDialogOpen(true)\n }\n\n const handleViewDetails = (sg: SecurityGroup) => {\n navigate({\n to: \"/projects/$projectId/network/securitygroups/$securityGroupId\",\n params: { projectId, securityGroupId: sg.id },\n })\n }\n\n const closeEditModal = () => {\n setSelectedSecurityGroup(null)\n setEditModalOpen(false)\n }\n\n const closeDeleteDialog = () => {\n setSelectedSecurityGroup(null)\n setDeleteDialogOpen(false)\n }\n\n // Close delete dialog when deletion completes successfully\n useEffect(() => {\n // Check if deletion just finished (was deleting before, now not deleting)\n const deletionJustFinished = prevIsDeletingRef.current && !isDeletingSecurityGroup\n\n // Close dialog if deletion just finished and there's no error\n if (deletionJustFinished && deleteDialogOpen && !deleteError) {\n closeDeleteDialog()\n }\n\n // Update the ref for next render\n prevIsDeletingRef.current = isDeletingSecurityGroup\n }, [isDeletingSecurityGroup, deleteError, deleteDialogOpen])\n\n // Close edit modal when update completes successfully\n useEffect(() => {\n // Check if update just finished (was updating before, now not updating)\n const updateJustFinished = prevIsUpdatingRef.current && !isUpdatingSecurityGroup\n\n // Close modal if update just finished and there's no error\n if (updateJustFinished && editModalOpen && !updateError) {\n closeEditModal()\n }\n\n // Update the ref for next render\n prevIsUpdatingRef.current = isUpdatingSecurityGroup\n }, [isUpdatingSecurityGroup, updateError, editModalOpen])\n\n // Loading state\n if (isLoading) {\n return (\n <Stack className=\"py-8\" distribution=\"center\" alignment=\"center\" direction=\"vertical\">\n <Spinner variant=\"primary\" size=\"large\" className=\"mb-2\" />\n <Trans>Loading...</Trans>\n </Stack>\n )\n }\n\n // Error state\n if (isError) {\n return (\n <Stack className=\"py-8\" distribution=\"center\" alignment=\"center\" direction=\"vertical\">\n {error?.message ?? t`Failed to load security groups`}\n </Stack>\n )\n }\n\n // Empty state\n if (securityGroups.length === 0) {\n return <Trans>There are no groups</Trans>\n }\n\n return (\n <>\n <DataGrid columns={5}>\n <DataGridRow>\n {[t`Name`, t`Description`, t`Shared`, t`Stateful`, \"\"].map((label) => (\n <DataGridHeadCell key={label}>{label}</DataGridHeadCell>\n ))}\n </DataGridRow>\n {securityGroups.map((sg) => {\n // Compute isReadOnly only when the security group has an explicit project owner\n const isReadOnly = Boolean(currentProjectId && sg.project_id && sg.project_id !== currentProjectId)\n\n return (\n <SecurityGroupTableRow\n key={sg.id}\n securityGroup={sg}\n permissions={permissions}\n onEdit={handleEdit}\n onDelete={handleDelete}\n onViewDetails={handleViewDetails}\n isReadOnly={isReadOnly}\n />\n )\n })}\n </DataGrid>\n\n {selectedSecurityGroup && (\n <>\n <EditSecurityGroupModal\n securityGroup={selectedSecurityGroup}\n open={editModalOpen}\n onClose={closeEditModal}\n onUpdate={async (id, data) => {\n if (onUpdateSecurityGroup) {\n await onUpdateSecurityGroup(id, data)\n }\n }}\n isLoading={isUpdatingSecurityGroup}\n error={updateError}\n />\n <DeleteSecurityGroupDialog\n securityGroup={selectedSecurityGroup}\n isOpen={deleteDialogOpen}\n onClose={closeDeleteDialog}\n onDelete={(id) => {\n if (onDeleteSecurityGroup) {\n onDeleteSecurityGroup(id)\n }\n }}\n isDeleting={isDeletingSecurityGroup}\n error={deleteError}\n />\n </>\n )}\n </>\n )\n}\n","import React, { useState } from \"react\"\nimport { Trans, useLingui } from \"@lingui/react/macro\"\nimport {\n Modal,\n Form,\n FormRow,\n FormSection,\n TextInput,\n Checkbox,\n Button,\n ButtonRow,\n Spinner,\n ModalFooter,\n Textarea,\n Message,\n} from \"@cloudoperators/juno-ui-components\"\nimport { CreateSecurityGroupInput } from \"@/server/Network/types/securityGroup\"\n\ninterface CreateSecurityGroupModalProps {\n isOpen: boolean\n onClose: () => void\n onCreate: (securityGroupData: Omit<CreateSecurityGroupInput, \"project_id\">) => Promise<void>\n isLoading?: boolean\n error?: string | null\n}\n\ninterface SecurityGroupProperties {\n name: string\n description: string\n stateful: boolean\n}\n\nconst defaultSecurityGroupValues: SecurityGroupProperties = {\n name: \"\",\n description: \"\",\n stateful: true,\n}\n\nexport const CreateSecurityGroupModal: React.FC<CreateSecurityGroupModalProps> = ({\n isOpen,\n onClose,\n onCreate,\n isLoading = false,\n error = null,\n}) => {\n const { t } = useLingui()\n\n const [properties, setProperties] = useState<SecurityGroupProperties>({ ...defaultSecurityGroupValues })\n const [errors, setErrors] = useState<{ [key: string]: string }>({})\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {\n const { name, value, type } = e.target\n const checked = (e.target as HTMLInputElement).checked\n\n setProperties((prev) => ({\n ...prev,\n [name]: type === \"checkbox\" ? checked : value,\n }))\n\n if (errors[name]) {\n setErrors((prev) => {\n const newErrors = { ...prev }\n delete newErrors[name]\n return newErrors\n })\n }\n }\n\n const validateForm = (): boolean => {\n const newErrors: { [key: string]: string } = {}\n\n if (!properties.name || properties.name.trim() === \"\") {\n newErrors.name = t`Security group name is required`\n }\n\n setErrors(newErrors)\n return Object.keys(newErrors).length === 0\n }\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault()\n\n if (!validateForm()) {\n return\n }\n\n const securityGroupData: Omit<CreateSecurityGroupInput, \"project_id\"> = {\n name: properties.name.trim(),\n description: properties.description.trim() || undefined,\n stateful: properties.stateful,\n }\n\n await onCreate(securityGroupData)\n handleClose()\n }\n\n const handleClose = () => {\n setProperties({ ...defaultSecurityGroupValues })\n setErrors({})\n onClose()\n }\n\n return (\n <Modal\n open={isOpen}\n onCancel={handleClose}\n size=\"large\"\n title={t`Create Security Group`}\n modalFooter={\n <ModalFooter className=\"flex justify-end\">\n <ButtonRow>\n <Button variant=\"default\" onClick={handleClose} disabled={isLoading}>\n <Trans>Cancel</Trans>\n </Button>\n <Button\n variant=\"primary\"\n onClick={(e) => {\n handleSubmit(e)\n }}\n disabled={isLoading}\n data-testid=\"create-security-group-button\"\n >\n {isLoading ? <Spinner size=\"small\" /> : <Trans>Create Security Group</Trans>}\n </Button>\n </ButtonRow>\n </ModalFooter>\n }\n >\n {/* Error Message */}\n {error && (\n <Message dismissible={false} variant=\"error\" className=\"mb-4\">\n {error}\n </Message>\n )}\n\n {isLoading && (\n <div className=\"mb-4 flex items-center justify-center gap-2\">\n <Spinner variant=\"primary\" />\n <span className=\"text-theme-high text-sm\">\n <Trans>Creating security group...</Trans>\n </span>\n </div>\n )}\n\n {!isLoading && (\n <Form className=\"mb-6\">\n <FormSection className=\"mb-6\">\n <FormRow className=\"mb-6\">\n <TextInput\n id=\"name\"\n name=\"name\"\n label={t`Name`}\n value={properties.name}\n onChange={handleInputChange}\n required\n errortext={errors.name}\n placeholder={t`Type name`}\n disabled={isLoading}\n />\n </FormRow>\n\n <FormRow className=\"mb-6\">\n <Textarea\n id=\"description\"\n name=\"description\"\n label={t`Description`}\n value={properties.description}\n onChange={handleInputChange}\n placeholder={t`Description`}\n disabled={isLoading}\n rows={3}\n />\n </FormRow>\n\n <FormRow className=\"mb-0\">\n <Checkbox\n id=\"stateful\"\n name=\"stateful\"\n label={t`Stateful`}\n checked={properties.stateful}\n onChange={handleInputChange}\n disabled={isLoading}\n />\n </FormRow>\n </FormSection>\n </Form>\n )}\n </Modal>\n )\n}\n","import { useState } from \"react\"\nimport { Trans, useLingui } from \"@lingui/react/macro\"\nimport { useNavigate } from \"@tanstack/react-router\"\nimport { Button } from \"@cloudoperators/juno-ui-components\"\nimport { trpcReact } from \"@/client/trpcClient\"\nimport { ListToolbar } from \"@/client/components/ListToolbar\"\nimport { buildFilterParams } from \"@/client/utils/buildFilterParams\"\nimport { useListWithFiltering } from \"@/client/utils/useListWithFiltering\"\nimport { useProjectId } from \"@/client/hooks\"\nimport { SecurityGroupListContainer } from \"./SecurityGroupListContainer\"\nimport { CreateSecurityGroupModal } from \"./-modals/CreateSecurityGroupModal\"\nimport { CreateSecurityGroupInput, UpdateSecurityGroupInput } from \"@/server/Network/types/securityGroup\"\n\n// Security group shared filter constants\nconst SECURITY_GROUP_SHARED = {\n TRUE: \"true\",\n FALSE: \"false\",\n} as const\n\ntype SecurityGroupSortKey = \"name\" | \"project_id\"\n\nexport const SecurityGroups = () => {\n const { t } = useLingui()\n const navigate = useNavigate()\n const projectId = useProjectId()\n\n const [createModalOpen, setCreateModalOpen] = useState(false)\n const [deleteError, setDeleteError] = useState<string | null>(null)\n const [createError, setCreateError] = useState<string | null>(null)\n const [updateError, setUpdateError] = useState<string | null>(null)\n\n const { searchTerm, sortSettings, filterSettings, handleSearchChange, handleSortChange, handleFilterChange } =\n useListWithFiltering<SecurityGroupSortKey>({\n defaultSortKey: \"name\",\n defaultSortDir: \"asc\",\n sortOptions: [\n { label: t`Name`, value: \"name\" },\n { label: t`Project id`, value: \"project_id\" },\n ],\n filterSettings: {\n filters: [\n {\n displayName: t`Shared`,\n filterName: \"shared\",\n values: Object.values(SECURITY_GROUP_SHARED),\n supportsMultiValue: false,\n },\n ],\n },\n })\n\n const utils = trpcReact.useUtils()\n\n // TODO: replace with trpc.network.canUser when security group permissions are available\n const permissions = {\n canCreate: true,\n canUpdate: true,\n canDelete: true,\n canManageAccess: true,\n }\n\n const {\n data: securityGroups = [],\n isLoading,\n isError,\n error,\n } = trpcReact.network.securityGroup.list.useQuery(\n {\n project_id: projectId || \"\",\n sort_key: sortSettings.sortBy,\n sort_dir: sortSettings.sortDirection,\n ...buildFilterParams(filterSettings),\n ...(searchTerm ? { searchTerm } : {}),\n },\n {\n enabled: !!projectId,\n }\n )\n\n const createSecurityGroupMutation = trpcReact.network.securityGroup.create.useMutation({\n onSuccess: (createdSecurityGroup) => {\n // Invalidate and refetch the security groups list\n utils.network.securityGroup.list.invalidate()\n setCreateError(null)\n\n // Navigate to the details page of the newly created security group\n navigate({\n to: \"/projects/$projectId/network/securitygroups/$securityGroupId\",\n params: {\n projectId,\n securityGroupId: createdSecurityGroup.id,\n },\n })\n },\n onError: (error) => {\n // Backend handles error parsing, just display the message\n setCreateError(error.message || t`Failed to create security group`)\n },\n })\n\n const deleteSecurityGroupMutation = trpcReact.network.securityGroup.deleteById.useMutation({\n onSuccess: () => {\n // Invalidate and refetch the security groups list\n utils.network.securityGroup.list.invalidate()\n setDeleteError(null)\n },\n onError: (error) => {\n // Backend handles error parsing, just display the message\n setDeleteError(error.message || t`Failed to delete security group`)\n },\n })\n\n const updateSecurityGroupMutation = trpcReact.network.securityGroup.update.useMutation({\n onSuccess: () => {\n // Invalidate and refetch the security groups list\n utils.network.securityGroup.list.invalidate()\n setUpdateError(null)\n },\n onError: (error) => {\n // Backend handles error parsing, just display the message\n setUpdateError(error.message || t`Failed to update security group`)\n },\n })\n\n const handleCreateSecurityGroup = async (securityGroupData: Omit<CreateSecurityGroupInput, \"project_id\">) => {\n setCreateError(null)\n await createSecurityGroupMutation.mutateAsync({ project_id: projectId, ...securityGroupData })\n }\n\n const handleDeleteSecurityGroup = (securityGroupId: string) => {\n setDeleteError(null)\n deleteSecurityGroupMutation.mutate({ project_id: projectId, securityGroupId })\n }\n\n const handleUpdateSecurityGroup = async (\n securityGroupId: string,\n data: Omit<UpdateSecurityGroupInput, \"securityGroupId\" | \"project_id\">\n ) => {\n setUpdateError(null)\n await updateSecurityGroupMutation.mutateAsync({ project_id: projectId, securityGroupId, ...data })\n }\n\n return (\n <div className=\"relative\">\n <ListToolbar\n sortSettings={sortSettings}\n filterSettings={filterSettings}\n searchTerm={searchTerm}\n onSort={handleSortChange}\n onFilter={handleFilterChange}\n onSearch={handleSearchChange}\n actions={\n permissions.canCreate && (\n <Button onClick={() => setCreateModalOpen(true)} variant=\"primary\">\n <Trans>Create Security Group</Trans>\n </Button>\n )\n }\n />\n\n <SecurityGroupListContainer\n securityGroups={securityGroups}\n isLoading={isLoading}\n isError={isError}\n error={error}\n permissions={permissions}\n onCreateClick={() => setCreateModalOpen(true)}\n onDeleteSecurityGroup={handleDeleteSecurityGroup}\n isDeletingSecurityGroup={deleteSecurityGroupMutation.isPending}\n deleteError={deleteError}\n onUpdateSecurityGroup={handleUpdateSecurityGroup}\n isUpdatingSecurityGroup={updateSecurityGroupMutation.isPending}\n updateError={updateError}\n currentProjectId={projectId}\n />\n\n <CreateSecurityGroupModal\n isOpen={createModalOpen}\n onClose={() => setCreateModalOpen(false)}\n onCreate={handleCreateSecurityGroup}\n isLoading={createSecurityGroupMutation.isPending}\n error={createError}\n />\n </div>\n )\n}\n","import { createFileRoute } from \"@tanstack/react-router\"\nimport { t } from \"@lingui/core/macro\"\nimport { useLingui } from \"@lingui/react/macro\"\nimport { SecurityGroups } from \"./-components/SecurityGroupsList\"\nimport type { RouteInfo } from \"@/client/routes/routeInfo\"\nimport { ContentHeading } from \"@cloudoperators/juno-ui-components\"\n\nexport const Route = createFileRoute(\"/_auth/projects/$projectId/network/securitygroups/\")({\n staticData: {\n section: \"network\",\n service: \"securitygroups\",\n sectionCrumb: { labelKey: \"Network\" },\n crumb: { labelKey: \"Security Groups\" },\n } satisfies RouteInfo,\n head: () => ({ meta: [{ title: t`Security Groups` }] }),\n component: RouteComponent,\n})\n\nfunction RouteComponent() {\n const { t } = useLingui()\n return (\n <>\n <ContentHeading>{t`Security Groups`}</ContentHeading>\n <SecurityGroups />\n </>\n )\n}\n"],"mappings":"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"}
@@ -54,4 +54,4 @@ var f = () => [s({
54
54
  //#endregion
55
55
  export { g as i, _ as n, p as r, d as t };
56
56
 
57
- //# sourceMappingURL=trpcClient-BxguzNYF.mjs.map
57
+ //# sourceMappingURL=trpcClient-BzPUgiM2.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"trpcClient-BxguzNYF.mjs","names":["createTRPCReact","createTRPCClient","httpBatchLink","httpBatchStreamLink","httpLink","splitLink","isNonJsonSerializable","httpSubscriptionLink","getCsrfHeaders","csrfToken","fetch","then","res","json","error","console","STREAMING_PROCEDURES","Set","_bffEndpoint","setBffEndpoint","endpoint","getLinks","condition","op","type","true","url","eventSourceOptions","headers","false","input","csrf","extra","context","has","path","trpcReact","_trpcReactClient","_trpcClient","trpcReactClient","Proxy","get","_target","prop","createClient","links","trpcClient"],"sources":["../../src/client/trpcClient.ts"],"sourcesContent":["import { createTRPCReact } from \"@trpc/react-query\"\nimport {\n createTRPCClient,\n httpBatchLink,\n httpBatchStreamLink,\n httpLink,\n splitLink,\n isNonJsonSerializable,\n httpSubscriptionLink,\n} from \"@trpc/client\"\nimport type { AuroraRouter } from \"../server/routers\"\n\n// CSRF headers factory\nconst getCsrfHeaders = async () => {\n try {\n const { csrfToken } = await fetch(\"/csrf-token\").then((res) => res.json())\n return {\n \"x-csrf-token\": csrfToken,\n }\n } catch (error) {\n console.error(\"Failed to fetch CSRF token:\", error)\n return {}\n }\n}\n\n// Procedures that return async iterables (chunked streaming responses).\n// These are routed through httpBatchStreamLink instead of httpBatchLink.\n// Add any new streaming procedure paths here.\nconst STREAMING_PROCEDURES = new Set<string>([\"storage.swift.downloadObject\"])\n\n// Mutable endpoint — set once by App before any tRPC calls are made.\nlet _bffEndpoint = \"/polaris-bff\"\n\nexport function setBffEndpoint(endpoint: string) {\n _bffEndpoint = endpoint\n}\n\nconst getLinks = () => [\n splitLink({\n // First check: is it a subscription?\n condition: (op) => op.type === \"subscription\",\n // Use HTTP subscription link for subscriptions (long-polling)\n true: httpSubscriptionLink({\n url: _bffEndpoint,\n // Callback to populate headers\n eventSourceOptions: async () => {\n return { headers: await getCsrfHeaders() }\n },\n }),\n // For non-subscriptions, check for non-JSON data or streaming procedures\n false: splitLink({\n // Non-JSON serializable input (FormData, Blob, ArrayBuffer, etc.) → plain httpLink.\n condition: (op) => isNonJsonSerializable(op.input),\n true: httpLink({\n url: _bffEndpoint,\n async headers({ op }) {\n const csrf = await getCsrfHeaders()\n // Per-request headers can be injected via tRPC operation context.\n // op.context is set by the caller and forwarded through the link chain.\n const extra = (op.context as { headers?: Record<string, string> } | undefined)?.headers ?? {}\n return { ...csrf, ...extra }\n },\n }),\n // For JSON procedures, decide between streaming and regular batching\n false: splitLink({\n // Procedures returning async iterables must use httpBatchStreamLink.\n // httpBatchLink buffers the full response before resolving, which\n // breaks streaming. httpBatchStreamLink preserves batching while\n // supporting chunked/streamed responses.\n // Note: httpBatchStreamLink is intentionally NOT used globally because\n // it is incompatible with the @fastify/csrf-protection cookie rotation\n // used in this app — CSRF tokens can expire mid-stream on long-lived\n // connections. Scope it only to procedures that actually need streaming.\n condition: (op) => STREAMING_PROCEDURES.has(op.path),\n true: httpBatchStreamLink({\n url: _bffEndpoint,\n async headers() {\n return getCsrfHeaders()\n },\n }),\n // Regular JSON mutations/queries — standard batching\n false: httpBatchLink({\n url: _bffEndpoint,\n async headers() {\n return getCsrfHeaders()\n },\n }),\n }),\n }),\n }),\n]\n\n// React Query client (for hooks like useQuery/useMutation/useSubscription)\nexport const trpcReact: ReturnType<typeof createTRPCReact<AuroraRouter>> = createTRPCReact<AuroraRouter>()\n\nlet _trpcReactClient: ReturnType<typeof trpcReact.createClient> | null = null\nlet _trpcClient: ReturnType<typeof createTRPCClient<AuroraRouter>> | null = null\n\n// Lazily initialised — created on first access after setBffEndpoint() has been called by App.\nexport const trpcReactClient = new Proxy({} as ReturnType<typeof trpcReact.createClient>, {\n get(_target, prop) {\n if (!_trpcReactClient) {\n _trpcReactClient = trpcReact.createClient({ links: getLinks() })\n }\n return (_trpcReactClient as Record<string | symbol, unknown>)[prop]\n },\n})\n\nexport const trpcClient = new Proxy({} as ReturnType<typeof createTRPCClient<AuroraRouter>>, {\n get(_target, prop) {\n if (!_trpcClient) {\n _trpcClient = createTRPCClient<AuroraRouter>({ links: getLinks() })\n }\n return (_trpcClient as Record<string | symbol, unknown>)[prop]\n },\n})\n\nexport type TrpcReact = typeof trpcReact\nexport type TrpcClient = typeof trpcClient\n"],"mappings":";;;AAaA,IAAMQ,IAAiB,YAAA;AACrB,KAAI;EACF,IAAM,EAAEC,iBAAc,MAAMC,MAAM,cAAA,CAAeC,MAAMC,MAAQA,EAAIC,MAAI,CAAA;AACvE,SAAO,EACL,gBAAgBJ,GAClB;UACOK,GAAO;AAEd,SADAC,QAAQD,MAAM,+BAA+BA,EAAAA,EACtC,EAAC;;GAONE,IAAuB,IAAIC,IAAY,CAAC,+BAA+B,CAAA,EAGzEC,IAAe;AAEnB,SAAgBC,EAAeC,GAAgB;AAC7CF,KAAeE;;AAGjB,IAAMC,UAAiB,CACrBhB,EAAU;CAERiB,YAAYC,MAAOA,EAAGC,SAAS;CAE/BC,MAAMlB,EAAqB;EACzBmB,KAAKR;EAELS,oBAAoB,aACX,EAAEC,SAAS,MAAMpB,GAAAA,EAAiB;EAE7C,CAAA;CAEAqB,OAAOxB,EAAU;EAEfiB,YAAYC,MAAOjB,EAAsBiB,EAAGO,MAAK;EACjDL,MAAMrB,EAAS;GACbsB,KAAKR;GACL,MAAMU,QAAQ,EAAEL,SAAI;IAClB,IAAMQ,IAAO,MAAMvB,GAAAA,EAGbwB,IAAQ,EAAIC,SAA8DL,WAAW,EAAC;AAC5F,WAAO;KAAE,GAAGG;KAAM,GAAGC;KAAM;;GAE/B,CAAA;EAEAH,OAAOxB,EAAU;GASfiB,YAAYC,MAAOP,EAAqBkB,IAAIX,EAAGY,KAAI;GACnDV,MAAMtB,EAAoB;IACxBuB,KAAKR;IACL,MAAMU,UAAAA;AACJ,YAAOpB,GAAAA;;IAEX,CAAA;GAEAqB,OAAO3B,EAAc;IACnBwB,KAAKR;IACL,MAAMU,UAAAA;AACJ,YAAOpB,GAAAA;;IAEX,CAAA;GACF,CAAA;EACF,CAAA;CACF,CAAA,CACD,EAGY4B,IAA8DpC,GAAAA,EAEvEqC,IAAqE,MACrEC,IAAwE,MAG/DC,IAAkB,IAAIC,MAAM,EAAC,EAAgD,EACxFC,IAAIC,GAASC,GAAI;AAIf,QAHA,AACEN,MAAmBD,EAAUQ,aAAa,EAAEC,OAAOxB,GAAAA,EAAW,CAAA,EAEzD,EAAuDsB;GAElE,CAAA,EAEaG,IAAa,IAAIN,MAAM,EAAC,EAAwD,EAC3FC,IAAIC,GAASC,GAAI;AAIf,QAHA,AACEL,MAAcrC,EAA+B,EAAE4C,OAAOxB,GAAAA,EAAW,CAAA,EAE5D,EAAkDsB;GAE7D,CAAA"}
1
+ {"version":3,"file":"trpcClient-BzPUgiM2.mjs","names":["createTRPCReact","createTRPCClient","httpBatchLink","httpBatchStreamLink","httpLink","splitLink","isNonJsonSerializable","httpSubscriptionLink","getCsrfHeaders","csrfToken","fetch","then","res","json","error","console","STREAMING_PROCEDURES","Set","_bffEndpoint","setBffEndpoint","endpoint","getLinks","condition","op","type","true","url","eventSourceOptions","headers","false","input","csrf","extra","context","has","path","trpcReact","_trpcReactClient","_trpcClient","trpcReactClient","Proxy","get","_target","prop","createClient","links","trpcClient"],"sources":["../../src/client/trpcClient.ts"],"sourcesContent":["import { createTRPCReact } from \"@trpc/react-query\"\nimport {\n createTRPCClient,\n httpBatchLink,\n httpBatchStreamLink,\n httpLink,\n splitLink,\n isNonJsonSerializable,\n httpSubscriptionLink,\n} from \"@trpc/client\"\nimport type { AuroraRouter } from \"../server/routers\"\n\n// CSRF headers factory\nconst getCsrfHeaders = async () => {\n try {\n const { csrfToken } = await fetch(\"/csrf-token\").then((res) => res.json())\n return {\n \"x-csrf-token\": csrfToken,\n }\n } catch (error) {\n console.error(\"Failed to fetch CSRF token:\", error)\n return {}\n }\n}\n\n// Procedures that return async iterables (chunked streaming responses).\n// These are routed through httpBatchStreamLink instead of httpBatchLink.\n// Add any new streaming procedure paths here.\nconst STREAMING_PROCEDURES = new Set<string>([\"storage.swift.downloadObject\"])\n\n// Mutable endpoint — set once by App before any tRPC calls are made.\nlet _bffEndpoint = \"/polaris-bff\"\n\nexport function setBffEndpoint(endpoint: string) {\n _bffEndpoint = endpoint\n}\n\nconst getLinks = () => [\n splitLink({\n // First check: is it a subscription?\n condition: (op) => op.type === \"subscription\",\n // Use HTTP subscription link for subscriptions (long-polling)\n true: httpSubscriptionLink({\n url: _bffEndpoint,\n // Callback to populate headers\n eventSourceOptions: async () => {\n return { headers: await getCsrfHeaders() }\n },\n }),\n // For non-subscriptions, check for non-JSON data or streaming procedures\n false: splitLink({\n // Non-JSON serializable input (FormData, Blob, ArrayBuffer, etc.) → plain httpLink.\n condition: (op) => isNonJsonSerializable(op.input),\n true: httpLink({\n url: _bffEndpoint,\n async headers({ op }) {\n const csrf = await getCsrfHeaders()\n // Per-request headers can be injected via tRPC operation context.\n // op.context is set by the caller and forwarded through the link chain.\n const extra = (op.context as { headers?: Record<string, string> } | undefined)?.headers ?? {}\n return { ...csrf, ...extra }\n },\n }),\n // For JSON procedures, decide between streaming and regular batching\n false: splitLink({\n // Procedures returning async iterables must use httpBatchStreamLink.\n // httpBatchLink buffers the full response before resolving, which\n // breaks streaming. httpBatchStreamLink preserves batching while\n // supporting chunked/streamed responses.\n // Note: httpBatchStreamLink is intentionally NOT used globally because\n // it is incompatible with the @fastify/csrf-protection cookie rotation\n // used in this app — CSRF tokens can expire mid-stream on long-lived\n // connections. Scope it only to procedures that actually need streaming.\n condition: (op) => STREAMING_PROCEDURES.has(op.path),\n true: httpBatchStreamLink({\n url: _bffEndpoint,\n async headers() {\n return getCsrfHeaders()\n },\n }),\n // Regular JSON mutations/queries — standard batching\n false: httpBatchLink({\n url: _bffEndpoint,\n async headers() {\n return getCsrfHeaders()\n },\n }),\n }),\n }),\n }),\n]\n\n// React Query client (for hooks like useQuery/useMutation/useSubscription)\nexport const trpcReact: ReturnType<typeof createTRPCReact<AuroraRouter>> = createTRPCReact<AuroraRouter>()\n\nlet _trpcReactClient: ReturnType<typeof trpcReact.createClient> | null = null\nlet _trpcClient: ReturnType<typeof createTRPCClient<AuroraRouter>> | null = null\n\n// Lazily initialised — created on first access after setBffEndpoint() has been called by App.\nexport const trpcReactClient = new Proxy({} as ReturnType<typeof trpcReact.createClient>, {\n get(_target, prop) {\n if (!_trpcReactClient) {\n _trpcReactClient = trpcReact.createClient({ links: getLinks() })\n }\n return (_trpcReactClient as Record<string | symbol, unknown>)[prop]\n },\n})\n\nexport const trpcClient = new Proxy({} as ReturnType<typeof createTRPCClient<AuroraRouter>>, {\n get(_target, prop) {\n if (!_trpcClient) {\n _trpcClient = createTRPCClient<AuroraRouter>({ links: getLinks() })\n }\n return (_trpcClient as Record<string | symbol, unknown>)[prop]\n },\n})\n\nexport type TrpcReact = typeof trpcReact\nexport type TrpcClient = typeof trpcClient\n"],"mappings":";;;AAaA,IAAMQ,IAAiB,YAAA;CACrB,IAAI;EACF,IAAM,EAAEC,iBAAc,MAAMC,MAAM,aAAA,EAAeC,MAAMC,MAAQA,EAAIC,KAAI,CAAA;EACvE,OAAO,EACL,gBAAgBJ,EAClB;CACF,SAASK,GAAO;EAEd,OADAC,QAAQD,MAAM,+BAA+BA,CAAAA,GACtC,CAAC;CACV;AACF,GAKME,IAAuB,IAAIC,IAAY,CAAC,8BAAA,CAA+B,GAGzEC,IAAe;AAEnB,SAAgBC,EAAeC,GAAgB;CAC7CF,IAAeE;AACjB;AAEA,IAAMC,UAAiB,CACrBhB,EAAU;CAERiB,YAAYC,MAAOA,EAAGC,SAAS;CAE/BC,MAAMlB,EAAqB;EACzBmB,KAAKR;EAELS,oBAAoB,aACX,EAAEC,SAAS,MAAMpB,EAAAA,EAAiB;CAE7C,CAAA;CAEAqB,OAAOxB,EAAU;EAEfiB,YAAYC,MAAOjB,EAAsBiB,EAAGO,KAAK;EACjDL,MAAMrB,EAAS;GACbsB,KAAKR;GACL,MAAMU,QAAQ,EAAEL,SAAI;IAClB,IAAMQ,IAAO,MAAMvB,EAAAA,GAGbwB,IAAQ,EAAIC,SAA8DL,WAAW,CAAC;IAC5F,OAAO;KAAE,GAAGG;KAAM,GAAGC;IAAM;GAC7B;EACF,CAAA;EAEAH,OAAOxB,EAAU;GASfiB,YAAYC,MAAOP,EAAqBkB,IAAIX,EAAGY,IAAI;GACnDV,MAAMtB,EAAoB;IACxBuB,KAAKR;IACL,MAAMU,UAAAA;KACJ,OAAOpB,EAAAA;IACT;GACF,CAAA;GAEAqB,OAAO3B,EAAc;IACnBwB,KAAKR;IACL,MAAMU,UAAAA;KACJ,OAAOpB,EAAAA;IACT;GACF,CAAA;EACF,CAAA;CACF,CAAA;AACF,CAAA,CAAA,GAIW4B,IAA8DpC,EAAAA,GAEvEqC,IAAqE,MACrEC,IAAwE,MAG/DC,IAAkB,IAAIC,MAAM,CAAC,GAAgD,EACxFC,IAAIC,GAASC,GAAI;CAIf,OAHA,AACEN,MAAmBD,EAAUQ,aAAa,EAAEC,OAAOxB,EAAAA,EAAW,CAAA,GAEzD,EAAuDsB;AAChE,EACF,CAAA,GAEaG,IAAa,IAAIN,MAAM,CAAC,GAAwD,EAC3FC,IAAIC,GAASC,GAAI;CAIf,OAHA,AACEL,MAAcrC,EAA+B,EAAE4C,OAAOxB,EAAAA,EAAW,CAAA,GAE5D,EAAkDsB;AAC3D,EACF,CAAA"}
@@ -80,4 +80,4 @@ var t = () => {
80
80
  //#endregion
81
81
  export { t };
82
82
 
83
- //# sourceMappingURL=useErrorTranslation-TZVwIAzq.mjs.map
83
+ //# sourceMappingURL=useErrorTranslation-Dc0eE8Zt.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useErrorTranslation-TZVwIAzq.mjs","names":["useErrorTranslation","useLingui","translateError","errorCode","t","isRetryableError","includes"],"sources":["../../src/client/utils/useErrorTranslation.ts"],"sourcesContent":["import { useLingui } from \"@lingui/react/macro\"\n\nexport const useErrorTranslation = () => {\n const { t } = useLingui()\n\n const translateError = (errorCode: string): string => {\n switch (errorCode) {\n // List Flavor\n case \"FLAVORS_UNAUTHORIZED\":\n return t`Your session has expired. Please log in again.`\n case \"FLAVORS_FORBIDDEN\":\n return t`You don't have permission to access flavors for this project.`\n case \"FLAVORS_NOT_FOUND\":\n return t`Flavor service is not available for this project.`\n case \"FLAVORS_SERVER_ERROR\":\n return t`Server is experiencing issues. Please try again later.`\n case \"FLAVORS_FETCH_FAILED\":\n return t`Failed to fetch flavors from server.`\n case \"FLAVORS_PARSE_ERROR\":\n return t`Server returned unexpected data format.`\n\n // Get flavor\n case \"GET_FLAVOR_DETAILS_UNAUTHORIZED\":\n return t`You are not authorized to view flavor details. Please log in again.`\n case \"GET_FLAVOR_DETAILS_FORBIDDEN\":\n return t`You don't have permission to view this flavor's details.`\n case \"GET_FLAVOR_DETAILS_NOT_FOUND\":\n return t`The flavor could not be found. It may have been deleted.`\n case \"GET_FLAVOR_DETAILS_SERVER_ERROR\":\n return t`Server error occurred while fetching flavor details. Please try again later.`\n case \"GET_FLAVOR_DETAILS_INVALID_ID\":\n return t`Flavor ID is required and cannot be empty.`\n case \"GET_FLAVOR_DETAILS_PARSE_ERROR\":\n return t`Server returned unexpected data format for flavor details.`\n case \"GET_FLAVOR_DETAILS_FAILED\":\n return t`Failed to load flavor details. Please try again.`\n\n // Create Flavor\n case \"CREATE_FLAVOR_INVALID_DATA\":\n return t`The flavor data provided is invalid. Please check your input.`\n case \"CREATE_FLAVOR_UNAUTHORIZED\":\n return t`You are not authorized to create flavors. Please log in again.`\n case \"CREATE_FLAVOR_FORBIDDEN\":\n return t`You don't have permission to create flavors in this project.`\n case \"CREATE_FLAVOR_CONFLICT\":\n return t`A flavor with this ID or name already exists. Please use different values.`\n case \"CREATE_FLAVOR_SERVER_ERROR\":\n return t`Server error occurred while creating the flavor. Please try again later.`\n case \"CREATE_FLAVOR_FAILED\":\n return t`Failed to create the flavor. Please try again.`\n\n // Delete Flavor\n case \"DELETE_FLAVOR_UNAUTHORIZED\":\n return t`You are not authorized to delete flavors. Please log in again.`\n case \"DELETE_FLAVOR_FORBIDDEN\":\n return t`You don't have permission to delete flavors in this project.`\n case \"DELETE_FLAVOR_NOT_FOUND\":\n return t`The flavor could not be found. It may have already been deleted.`\n case \"DELETE_FLAVOR_SERVER_ERROR\":\n return t`Server error occurred while deleting the flavor. Please try again later.`\n case \"DELETE_FLAVOR_FAILED\":\n return t`Failed to delete the flavor. Please try again.`\n case \"DELETE_FLAVOR_INVALID_ID\":\n return t`Flavor ID is required and cannot be empty.`\n\n // Create Metadata\n case \"CREATE_EXTRA_SPECS_UNAUTHORIZED\":\n return t`You are not authorized to create extra specs. Please log in again.`\n case \"CREATE_EXTRA_SPECS_FORBIDDEN\":\n return t`You don't have permission to create extra specs for this flavor.`\n case \"CREATE_EXTRA_SPECS_NOT_FOUND\":\n return t`The flavor could not be found. It may have been deleted.`\n case \"CREATE_EXTRA_SPECS_CONFLICT\":\n return t`This extra spec keys already exist. Please use different keys.`\n case \"CREATE_EXTRA_SPECS_INVALID_DATA\":\n return t`The extra spec data provided is invalid. Please check your input.`\n case \"CREATE_EXTRA_SPECS_SERVER_ERROR\":\n return t`Server error occurred while creating extra specs. Please try again later.`\n case \"CREATE_EXTRA_SPECS_PARSE_ERROR\":\n return t`Server returned unexpected data format for extra specs.`\n case \"CREATE_EXTRA_SPECS_FAILED\":\n return t`Failed to create extra specs. Please try again.`\n\n // Get Metadata\n case \"GET_EXTRA_SPECS_UNAUTHORIZED\":\n return t`You are not authorized to view extra specs. Please log in again.`\n case \"GET_EXTRA_SPECS_FORBIDDEN\":\n return t`You don't have permission to view extra specs for this flavor.`\n case \"GET_EXTRA_SPECS_NOT_FOUND\":\n return t`The flavor could not be found or has no extra specs.`\n case \"GET_EXTRA_SPECS_SERVER_ERROR\":\n return t`Server error occurred while fetching extra specs. Please try again later.`\n case \"GET_EXTRA_SPECS_PARSE_ERROR\":\n return t`Server returned unexpected data format for extra specs.`\n case \"GET_EXTRA_SPECS_FAILED\":\n return t`Failed to load extra specs. Please try again.`\n\n // Delete Metadata\n case \"DELETE_EXTRA_SPEC_UNAUTHORIZED\":\n return t`You are not authorized to delete extra specs. Please log in again.`\n case \"DELETE_EXTRA_SPEC_FORBIDDEN\":\n return t`You don't have permission to delete extra specs for this flavor.`\n case \"DELETE_EXTRA_SPEC_NOT_FOUND\":\n return t`The extra spec could not be found. It may have already been deleted.`\n case \"DELETE_EXTRA_SPEC_SERVER_ERROR\":\n return t`Server error occurred while deleting the extra spec. Please try again later.`\n case \"DELETE_EXTRA_SPEC_INVALID_KEY\":\n return t`Extra spec key is required and cannot be empty.`\n case \"DELETE_EXTRA_SPEC_FAILED\":\n return t`Failed to delete the extra spec. Please try again.`\n\n // Get Flavor Access\n case \"GET_FLAVOR_ACCESS_UNAUTHORIZED\":\n return t`You are not authorized to access flavor access information. Please log in again.`\n case \"GET_FLAVOR_ACCESS_FORBIDDEN\":\n return t`You don't have permission to access flavor access information for this flavor.`\n case \"GET_FLAVOR_ACCESS_NOT_FOUND\":\n return t`The flavor could not be found. It may have been deleted.`\n case \"GET_FLAVOR_ACCESS_SERVER_ERROR\":\n return t`Server error occurred while fetching flavor access information. Please try again later.`\n case \"GET_FLAVOR_ACCESS_FAILED\":\n return t`Failed to fetch flavor access information. Please try again.`\n\n // Add Tenant Access\n case \"ADD_TENANT_ACCESS_FAILED\":\n return t`Failed to add tenant access to flavor. Please try again.`\n case \"ADD_TENANT_ACCESS_INVALID_DATA\":\n return t`The tenant ID provided is invalid. Please check your input.`\n case \"ADD_TENANT_ACCESS_UNAUTHORIZED\":\n return t`You are not authorized to add tenant access. Please log in again.`\n case \"ADD_TENANT_ACCESS_FORBIDDEN\":\n return t`You don't have permission to add tenant access to this flavor.`\n case \"ADD_TENANT_ACCESS_NOT_FOUND\":\n return t`The flavor or tenant could not be found. Please verify they exist.`\n case \"ADD_TENANT_ACCESS_CONFLICT\":\n return t`This tenant already has access to the flavor.`\n case \"ADD_TENANT_ACCESS_SERVER_ERROR\":\n return t`Server error occurred while adding tenant access. Please try again later.`\n\n // Remove Tenant Access\n case \"REMOVE_TENANT_ACCESS_FAILED\":\n return t`Failed to remove tenant access from flavor. Please try again.`\n case \"REMOVE_TENANT_ACCESS_INVALID_DATA\":\n return t`The tenant ID provided is invalid. Please check your input.`\n case \"REMOVE_TENANT_ACCESS_UNAUTHORIZED\":\n return t`You are not authorized to remove tenant access. Please log in again.`\n case \"REMOVE_TENANT_ACCESS_FORBIDDEN\":\n return t`You don't have permission to remove tenant access from this flavor.`\n case \"REMOVE_TENANT_ACCESS_NOT_FOUND\":\n return t`The flavor or tenant could not be found. It may have already been removed.`\n case \"REMOVE_TENANT_ACCESS_CONFLICT\":\n return t`This tenant does not have access to the flavor.`\n case \"REMOVE_TENANT_ACCESS_SERVER_ERROR\":\n return t`Server error occurred while removing tenant access. Please try again later.`\n\n // Service Connection Errors\n case \"COMPUTE_SERVICE_UNAVAILABLE\":\n return t`The compute service is currently unavailable for this project. Please try again later.`\n case \"COMPUTE_SERVICE_CONNECTION_FAILED\":\n return t`Unable to connect to the compute service. Please check your connection and try again.`\n\n default:\n return t`An unexpected error occurred. Please try again.`\n }\n }\n\n const isRetryableError = (errorCode: string): boolean => {\n return [\"FLAVORS_SERVER_ERROR\", \"FLAVORS_FETCH_FAILED\"].includes(errorCode)\n }\n\n return { translateError, isRetryableError }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useErrorTranslation-Dc0eE8Zt.mjs","names":["useErrorTranslation","useLingui","translateError","errorCode","t","isRetryableError","includes"],"sources":["../../src/client/utils/useErrorTranslation.ts"],"sourcesContent":["import { useLingui } from \"@lingui/react/macro\"\n\nexport const useErrorTranslation = () => {\n const { t } = useLingui()\n\n const translateError = (errorCode: string): string => {\n switch (errorCode) {\n // List Flavor\n case \"FLAVORS_UNAUTHORIZED\":\n return t`Your session has expired. Please log in again.`\n case \"FLAVORS_FORBIDDEN\":\n return t`You don't have permission to access flavors for this project.`\n case \"FLAVORS_NOT_FOUND\":\n return t`Flavor service is not available for this project.`\n case \"FLAVORS_SERVER_ERROR\":\n return t`Server is experiencing issues. Please try again later.`\n case \"FLAVORS_FETCH_FAILED\":\n return t`Failed to fetch flavors from server.`\n case \"FLAVORS_PARSE_ERROR\":\n return t`Server returned unexpected data format.`\n\n // Get flavor\n case \"GET_FLAVOR_DETAILS_UNAUTHORIZED\":\n return t`You are not authorized to view flavor details. Please log in again.`\n case \"GET_FLAVOR_DETAILS_FORBIDDEN\":\n return t`You don't have permission to view this flavor's details.`\n case \"GET_FLAVOR_DETAILS_NOT_FOUND\":\n return t`The flavor could not be found. It may have been deleted.`\n case \"GET_FLAVOR_DETAILS_SERVER_ERROR\":\n return t`Server error occurred while fetching flavor details. Please try again later.`\n case \"GET_FLAVOR_DETAILS_INVALID_ID\":\n return t`Flavor ID is required and cannot be empty.`\n case \"GET_FLAVOR_DETAILS_PARSE_ERROR\":\n return t`Server returned unexpected data format for flavor details.`\n case \"GET_FLAVOR_DETAILS_FAILED\":\n return t`Failed to load flavor details. Please try again.`\n\n // Create Flavor\n case \"CREATE_FLAVOR_INVALID_DATA\":\n return t`The flavor data provided is invalid. Please check your input.`\n case \"CREATE_FLAVOR_UNAUTHORIZED\":\n return t`You are not authorized to create flavors. Please log in again.`\n case \"CREATE_FLAVOR_FORBIDDEN\":\n return t`You don't have permission to create flavors in this project.`\n case \"CREATE_FLAVOR_CONFLICT\":\n return t`A flavor with this ID or name already exists. Please use different values.`\n case \"CREATE_FLAVOR_SERVER_ERROR\":\n return t`Server error occurred while creating the flavor. Please try again later.`\n case \"CREATE_FLAVOR_FAILED\":\n return t`Failed to create the flavor. Please try again.`\n\n // Delete Flavor\n case \"DELETE_FLAVOR_UNAUTHORIZED\":\n return t`You are not authorized to delete flavors. Please log in again.`\n case \"DELETE_FLAVOR_FORBIDDEN\":\n return t`You don't have permission to delete flavors in this project.`\n case \"DELETE_FLAVOR_NOT_FOUND\":\n return t`The flavor could not be found. It may have already been deleted.`\n case \"DELETE_FLAVOR_SERVER_ERROR\":\n return t`Server error occurred while deleting the flavor. Please try again later.`\n case \"DELETE_FLAVOR_FAILED\":\n return t`Failed to delete the flavor. Please try again.`\n case \"DELETE_FLAVOR_INVALID_ID\":\n return t`Flavor ID is required and cannot be empty.`\n\n // Create Metadata\n case \"CREATE_EXTRA_SPECS_UNAUTHORIZED\":\n return t`You are not authorized to create extra specs. Please log in again.`\n case \"CREATE_EXTRA_SPECS_FORBIDDEN\":\n return t`You don't have permission to create extra specs for this flavor.`\n case \"CREATE_EXTRA_SPECS_NOT_FOUND\":\n return t`The flavor could not be found. It may have been deleted.`\n case \"CREATE_EXTRA_SPECS_CONFLICT\":\n return t`This extra spec keys already exist. Please use different keys.`\n case \"CREATE_EXTRA_SPECS_INVALID_DATA\":\n return t`The extra spec data provided is invalid. Please check your input.`\n case \"CREATE_EXTRA_SPECS_SERVER_ERROR\":\n return t`Server error occurred while creating extra specs. Please try again later.`\n case \"CREATE_EXTRA_SPECS_PARSE_ERROR\":\n return t`Server returned unexpected data format for extra specs.`\n case \"CREATE_EXTRA_SPECS_FAILED\":\n return t`Failed to create extra specs. Please try again.`\n\n // Get Metadata\n case \"GET_EXTRA_SPECS_UNAUTHORIZED\":\n return t`You are not authorized to view extra specs. Please log in again.`\n case \"GET_EXTRA_SPECS_FORBIDDEN\":\n return t`You don't have permission to view extra specs for this flavor.`\n case \"GET_EXTRA_SPECS_NOT_FOUND\":\n return t`The flavor could not be found or has no extra specs.`\n case \"GET_EXTRA_SPECS_SERVER_ERROR\":\n return t`Server error occurred while fetching extra specs. Please try again later.`\n case \"GET_EXTRA_SPECS_PARSE_ERROR\":\n return t`Server returned unexpected data format for extra specs.`\n case \"GET_EXTRA_SPECS_FAILED\":\n return t`Failed to load extra specs. Please try again.`\n\n // Delete Metadata\n case \"DELETE_EXTRA_SPEC_UNAUTHORIZED\":\n return t`You are not authorized to delete extra specs. Please log in again.`\n case \"DELETE_EXTRA_SPEC_FORBIDDEN\":\n return t`You don't have permission to delete extra specs for this flavor.`\n case \"DELETE_EXTRA_SPEC_NOT_FOUND\":\n return t`The extra spec could not be found. It may have already been deleted.`\n case \"DELETE_EXTRA_SPEC_SERVER_ERROR\":\n return t`Server error occurred while deleting the extra spec. Please try again later.`\n case \"DELETE_EXTRA_SPEC_INVALID_KEY\":\n return t`Extra spec key is required and cannot be empty.`\n case \"DELETE_EXTRA_SPEC_FAILED\":\n return t`Failed to delete the extra spec. Please try again.`\n\n // Get Flavor Access\n case \"GET_FLAVOR_ACCESS_UNAUTHORIZED\":\n return t`You are not authorized to access flavor access information. Please log in again.`\n case \"GET_FLAVOR_ACCESS_FORBIDDEN\":\n return t`You don't have permission to access flavor access information for this flavor.`\n case \"GET_FLAVOR_ACCESS_NOT_FOUND\":\n return t`The flavor could not be found. It may have been deleted.`\n case \"GET_FLAVOR_ACCESS_SERVER_ERROR\":\n return t`Server error occurred while fetching flavor access information. Please try again later.`\n case \"GET_FLAVOR_ACCESS_FAILED\":\n return t`Failed to fetch flavor access information. Please try again.`\n\n // Add Tenant Access\n case \"ADD_TENANT_ACCESS_FAILED\":\n return t`Failed to add tenant access to flavor. Please try again.`\n case \"ADD_TENANT_ACCESS_INVALID_DATA\":\n return t`The tenant ID provided is invalid. Please check your input.`\n case \"ADD_TENANT_ACCESS_UNAUTHORIZED\":\n return t`You are not authorized to add tenant access. Please log in again.`\n case \"ADD_TENANT_ACCESS_FORBIDDEN\":\n return t`You don't have permission to add tenant access to this flavor.`\n case \"ADD_TENANT_ACCESS_NOT_FOUND\":\n return t`The flavor or tenant could not be found. Please verify they exist.`\n case \"ADD_TENANT_ACCESS_CONFLICT\":\n return t`This tenant already has access to the flavor.`\n case \"ADD_TENANT_ACCESS_SERVER_ERROR\":\n return t`Server error occurred while adding tenant access. Please try again later.`\n\n // Remove Tenant Access\n case \"REMOVE_TENANT_ACCESS_FAILED\":\n return t`Failed to remove tenant access from flavor. Please try again.`\n case \"REMOVE_TENANT_ACCESS_INVALID_DATA\":\n return t`The tenant ID provided is invalid. Please check your input.`\n case \"REMOVE_TENANT_ACCESS_UNAUTHORIZED\":\n return t`You are not authorized to remove tenant access. Please log in again.`\n case \"REMOVE_TENANT_ACCESS_FORBIDDEN\":\n return t`You don't have permission to remove tenant access from this flavor.`\n case \"REMOVE_TENANT_ACCESS_NOT_FOUND\":\n return t`The flavor or tenant could not be found. It may have already been removed.`\n case \"REMOVE_TENANT_ACCESS_CONFLICT\":\n return t`This tenant does not have access to the flavor.`\n case \"REMOVE_TENANT_ACCESS_SERVER_ERROR\":\n return t`Server error occurred while removing tenant access. Please try again later.`\n\n // Service Connection Errors\n case \"COMPUTE_SERVICE_UNAVAILABLE\":\n return t`The compute service is currently unavailable for this project. Please try again later.`\n case \"COMPUTE_SERVICE_CONNECTION_FAILED\":\n return t`Unable to connect to the compute service. Please check your connection and try again.`\n\n default:\n return t`An unexpected error occurred. Please try again.`\n }\n }\n\n const isRetryableError = (errorCode: string): boolean => {\n return [\"FLAVORS_SERVER_ERROR\", \"FLAVORS_FETCH_FAILED\"].includes(errorCode)\n }\n\n return { translateError, isRetryableError }\n}\n"],"mappings":"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"}
@@ -0,0 +1,157 @@
1
+ import { N as e, Q as t, X as n, n as r } from "./build-DeJcDjPi.mjs";
2
+ import { t as i } from "./SortInput-CYv2_Pur.mjs";
3
+ import { n as a, t as o } from "./FiltersInput-DxcyR6Bp.mjs";
4
+ import { Fragment as s, jsx as c, jsxs as l } from "react/jsx-runtime";
5
+ import { startTransition as u, useCallback as d, useEffect as f, useRef as p, useState as m } from "react";
6
+ import { Trans as h, useLingui as g } from "@lingui/react";
7
+ //#region src/client/components/ListToolbar/index.tsx
8
+ var _ = ({ filterSettings: u, onFilter: m, sortSettings: _, onSort: v, searchTerm: y, onSearch: b, searchInputProps: x = {}, actions: S, tabs: C, totalCount: w, filteredCount: T, itemName: E = "items", lastUpdated: D }) => {
9
+ let { i18n: O, _: k } = g(), A = p(void 0);
10
+ f(() => () => {
11
+ A.current && clearTimeout(A.current);
12
+ }, []);
13
+ let j = (e) => e ? (typeof e == "string" ? new Date(e) : e).toLocaleString() : "", M = w !== void 0 && T !== void 0, N = d((e) => {
14
+ !m || !u || m({
15
+ ...u,
16
+ selectedFilters: u.selectedFilters?.filter((t) => !(t.name === e.name && t.value === e.value))
17
+ });
18
+ }, [u, m]), P = (e) => {
19
+ if (!m || !u || u.selectedFilters?.some((t) => t.name === e.name && t.value === e.value)) return;
20
+ let t = u.filters.find((t) => e.name === t.filterName)?.supportsMultiValue ? [...u.selectedFilters || [], e] : [...(u.selectedFilters || []).filter((t) => t.name !== e.name), e];
21
+ m({
22
+ ...u,
23
+ selectedFilters: t
24
+ });
25
+ }, F = d((e) => {
26
+ let t = typeof e == "string" ? e : "";
27
+ A.current && clearTimeout(A.current), b?.(t);
28
+ }, [b]), I = d((e) => {
29
+ let t = e.currentTarget.value;
30
+ A.current && clearTimeout(A.current), A.current = window.setTimeout(() => b?.(t), 500);
31
+ }, [b]), L = d(() => {
32
+ A.current && clearTimeout(A.current), b?.("");
33
+ }, [b]), R = u && m ? {
34
+ filters: u.filters,
35
+ onChange: P
36
+ } : null, z = _ && v ? {
37
+ options: _.options,
38
+ sortBy: _.sortBy,
39
+ sortDirection: _.sortDirection || "asc",
40
+ onSortByChange: (e) => v({
41
+ ..._,
42
+ sortBy: e,
43
+ sortDirection: _.sortDirection || "asc"
44
+ }),
45
+ onSortDirectionChange: (e) => v({
46
+ ..._,
47
+ sortDirection: e
48
+ })
49
+ } : null, B = b ? {
50
+ placeholder: O._({ id: "YIix5Y" }),
51
+ "data-testid": "searchbar",
52
+ value: y,
53
+ onInput: I,
54
+ onClear: L,
55
+ onSearch: F,
56
+ ...x
57
+ } : null;
58
+ return /*#__PURE__*/ l(s, { children: [C && /*#__PURE__*/ c("div", {
59
+ className: "w-full",
60
+ children: /*#__PURE__*/ c(n, {
61
+ activeItem: C.activeItem,
62
+ onActiveItemChange: C.onActiveItemChange,
63
+ children: C.items.map((t) => /*#__PURE__*/ c(e, {
64
+ label: t.label,
65
+ value: t.value
66
+ }, t.value))
67
+ })
68
+ }), /*#__PURE__*/ l(t, {
69
+ alignment: "center",
70
+ gap: "6",
71
+ className: "bg-theme-background-lvl-1 flex w-full flex-col p-4",
72
+ children: [
73
+ S && /*#__PURE__*/ c(t, {
74
+ direction: "horizontal",
75
+ className: "w-full justify-end",
76
+ children: S
77
+ }),
78
+ /*#__PURE__*/ l("div", {
79
+ className: "flex w-full flex-col items-stretch gap-4 md:flex-row md:items-center",
80
+ children: [
81
+ R && /*#__PURE__*/ c("div", {
82
+ className: "w-full md:w-auto md:min-w-37.5",
83
+ children: /*#__PURE__*/ c(o, { ...R })
84
+ }),
85
+ z && /*#__PURE__*/ c("div", {
86
+ className: "w-full md:w-auto md:min-w-45",
87
+ children: /*#__PURE__*/ c(i, { ...z })
88
+ }),
89
+ B && /*#__PURE__*/ c("div", {
90
+ className: "w-full md:ml-auto md:w-auto md:min-w-25",
91
+ children: /*#__PURE__*/ c(r, { ...B })
92
+ })
93
+ ]
94
+ }),
95
+ u?.selectedFilters && u.selectedFilters.length > 0 && m && /*#__PURE__*/ c("div", {
96
+ className: "w-full",
97
+ children: /*#__PURE__*/ c(a, {
98
+ selectedFilters: u.selectedFilters,
99
+ onDelete: N,
100
+ onClear: () => m({
101
+ ...u,
102
+ selectedFilters: []
103
+ })
104
+ })
105
+ }),
106
+ (M || D) && /*#__PURE__*/ c("div", {
107
+ className: "text-theme-secondary flex w-full items-center justify-between text-sm",
108
+ children: /*#__PURE__*/ l("div", {
109
+ className: "flex items-center gap-2",
110
+ children: [M && /*#__PURE__*/ c("span", { children: /*#__PURE__*/ c(h, {
111
+ id: "Vg0k6h",
112
+ values: {
113
+ filteredCount: T,
114
+ totalCount: w,
115
+ itemName: E
116
+ }
117
+ }) }), D && /*#__PURE__*/ c("span", { children: /*#__PURE__*/ c(h, {
118
+ id: "wlUDbB",
119
+ values: { formattedDate: j(D) }
120
+ }) })]
121
+ })
122
+ })
123
+ ]
124
+ })] });
125
+ };
126
+ //#endregion
127
+ //#region src/client/utils/useListWithFiltering.ts
128
+ function v({ defaultSortKey: e, defaultSortDir: t, sortOptions: n, filterSettings: r }) {
129
+ let [i, a] = m(""), o = (e) => {
130
+ let t = typeof e == "string" ? e : "";
131
+ u(() => a(t));
132
+ }, [s, c] = m({
133
+ options: n,
134
+ sortBy: e,
135
+ sortDirection: t
136
+ }), l = (n) => {
137
+ c({
138
+ options: n.options ?? s.options,
139
+ sortBy: n.sortBy?.toString() || e,
140
+ sortDirection: n.sortDirection || t
141
+ });
142
+ }, [d, f] = m(r);
143
+ return {
144
+ searchTerm: i,
145
+ sortSettings: s,
146
+ filterSettings: d,
147
+ handleSearchChange: o,
148
+ handleSortChange: l,
149
+ handleFilterChange: (e) => {
150
+ u(() => f(e));
151
+ }
152
+ };
153
+ }
154
+ //#endregion
155
+ export { _ as n, v as t };
156
+
157
+ //# sourceMappingURL=useListWithFiltering-DaYcu5AB.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useListWithFiltering-DaYcu5AB.mjs","names":["useCallback","useRef","useEffect","SearchInput","Stack","TabNavigation","TabNavigationItem","SelectedFilters","FiltersInput","SortInput","ListToolbar","filterSettings","onFilter","sortSettings","onSort","searchTerm","onSearch","searchInputProps","actions","tabs","totalCount","filteredCount","itemName","lastUpdated","useLingui","debounceTimerRef","undefined","current","clearTimeout","formatLastUpdated","date","dateObj","Date","toLocaleString","showCountInfo","handleFilterDelete","filterToRemove","selectedFilters","filter","name","value","handleSelect","selectedFilter","filterExists","some","supportsMultiValue","filters","find","filterName","newSelected","handleSearch","searchValue","handleSearchInput","event","currentTarget","window","setTimeout","handleSearchClear","filtersProps","onChange","sortProps","options","sortBy","sortDirection","onSortByChange","param","onSortDirectionChange","direction","searchProps","placeholder","t","onInput","onClear","div","className","activeItem","onActiveItemChange","items","map","item","label","alignment","gap","length","onDelete","span","formattedDate","startTransition","useState","useListWithFiltering","defaultSortKey","defaultSortDir","sortOptions","filterSettings","initialFilterSettings","searchTerm","setSearchTerm","handleSearchChange","term","searchValue","sortSettings","setSortSettings","options","sortBy","sortDirection","handleSortChange","newSortSettings","settings","toString","setFilterSettings","handleFilterChange","newFilterSettings"],"sources":["../../src/client/components/ListToolbar/index.tsx","../../src/client/utils/useListWithFiltering.ts"],"sourcesContent":["import { ReactNode, useCallback, useRef, useEffect } from \"react\"\nimport { Trans, useLingui } from \"@lingui/react/macro\"\nimport {\n SearchInput,\n SearchInputProps,\n Stack,\n TabNavigation,\n TabNavigationItem,\n} from \"@cloudoperators/juno-ui-components\"\nimport { SelectedFilters } from \"./SelectedFilters\"\nimport { FiltersInput } from \"./FiltersInput\"\nimport { SortInput } from \"./SortInput\"\nimport { FilterSettings, SelectedFilter, SortSettings } from \"./types\"\n\nexport type ListToolbarProps = {\n filterSettings?: FilterSettings\n onFilter?: (filterSettings: FilterSettings) => void\n sortSettings?: SortSettings\n onSort?: (sortSettings: SortSettings) => void\n searchTerm?: string\n onSearch?: (searchTerm: string) => void\n searchInputProps?: Omit<SearchInputProps, \"value\" | \"onSearch\" | \"onClear\" | \"onInput\">\n actions?: ReactNode\n tabs?: {\n items: Array<{ label: string; value: string }>\n activeItem: string\n onActiveItemChange: (value: ReactNode) => void\n }\n // Count information\n totalCount?: number\n itemName?: string // e.g. \"items\", \"users\", \"projects\" - used in count display\n filteredCount?: number\n // Last updated timestamp\n lastUpdated?: Date | string\n}\n\nexport const ListToolbar = ({\n filterSettings,\n onFilter,\n sortSettings,\n onSort,\n searchTerm,\n onSearch,\n searchInputProps = {},\n actions,\n tabs,\n totalCount,\n filteredCount,\n itemName = \"items\",\n lastUpdated,\n}: ListToolbarProps) => {\n const { t } = useLingui()\n\n const debounceTimerRef = useRef<number | undefined>(undefined)\n\n useEffect(() => {\n return () => {\n if (debounceTimerRef.current) {\n clearTimeout(debounceTimerRef.current)\n }\n }\n }, [])\n\n // Format last updated time\n const formatLastUpdated = (date: Date | string | undefined): string => {\n if (!date) return \"\"\n const dateObj = typeof date === \"string\" ? new Date(date) : date\n return dateObj.toLocaleString()\n }\n\n const showCountInfo = totalCount !== undefined && filteredCount !== undefined\n\n const handleFilterDelete = useCallback(\n (filterToRemove: SelectedFilter) => {\n if (!onFilter || !filterSettings) return\n onFilter({\n ...filterSettings,\n selectedFilters: filterSettings.selectedFilters?.filter(\n (filter) => !(filter.name === filterToRemove.name && filter.value === filterToRemove.value)\n ),\n })\n },\n [filterSettings, onFilter]\n )\n\n const handleSelect = (selectedFilter: SelectedFilter) => {\n if (!onFilter || !filterSettings) return\n const filterExists = filterSettings.selectedFilters?.some(\n (filter) => filter.name === selectedFilter.name && filter.value === selectedFilter.value\n )\n if (filterExists) return\n\n const supportsMultiValue = filterSettings.filters.find(\n (filter) => selectedFilter.name === filter.filterName\n )?.supportsMultiValue\n\n const newSelected = supportsMultiValue\n ? [...(filterSettings.selectedFilters || []), selectedFilter]\n : [\n ...(filterSettings.selectedFilters || []).filter((filter) => filter.name !== selectedFilter.name),\n selectedFilter,\n ]\n\n onFilter({ ...filterSettings, selectedFilters: newSelected })\n }\n\n const handleSearch = useCallback(\n (value: string | number | string[] | undefined) => {\n const searchValue = typeof value === \"string\" ? value : \"\"\n\n if (debounceTimerRef.current) {\n clearTimeout(debounceTimerRef.current)\n }\n\n onSearch?.(searchValue)\n },\n [onSearch]\n )\n\n const handleSearchInput = useCallback(\n (event: React.FormEvent<HTMLInputElement>) => {\n const searchValue = event.currentTarget.value\n if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current)\n debounceTimerRef.current = window.setTimeout(() => onSearch?.(searchValue), 500)\n },\n [onSearch]\n )\n\n const handleSearchClear = useCallback(() => {\n if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current)\n onSearch?.(\"\")\n }, [onSearch])\n\n const filtersProps = filterSettings && onFilter ? { filters: filterSettings.filters, onChange: handleSelect } : null\n const sortProps =\n sortSettings && onSort\n ? {\n options: sortSettings.options,\n sortBy: sortSettings.sortBy,\n sortDirection: sortSettings.sortDirection || \"asc\",\n onSortByChange: (param: string | number | string[] | undefined) =>\n onSort({ ...sortSettings, sortBy: param, sortDirection: sortSettings.sortDirection || \"asc\" }),\n onSortDirectionChange: (direction: \"asc\" | \"desc\") => onSort({ ...sortSettings, sortDirection: direction }),\n }\n : null\n\n const searchProps: (SearchInputProps & { \"data-testid\"?: string }) | null = onSearch\n ? {\n placeholder: t`Search...`,\n \"data-testid\": \"searchbar\",\n value: searchTerm,\n onInput: handleSearchInput,\n onClear: handleSearchClear,\n onSearch: handleSearch,\n ...searchInputProps,\n }\n : null\n\n return (\n <>\n {tabs && (\n <div className=\"w-full\">\n <TabNavigation activeItem={tabs.activeItem} onActiveItemChange={tabs.onActiveItemChange}>\n {tabs.items.map((item) => (\n <TabNavigationItem key={item.value} label={item.label} value={item.value} />\n ))}\n </TabNavigation>\n </div>\n )}\n <Stack alignment=\"center\" gap=\"6\" className=\"bg-theme-background-lvl-1 flex w-full flex-col p-4\">\n {actions && (\n <Stack direction=\"horizontal\" className=\"w-full justify-end\">\n {actions}\n </Stack>\n )}\n\n <div className=\"flex w-full flex-col items-stretch gap-4 md:flex-row md:items-center\">\n {filtersProps && (\n <div className=\"w-full md:w-auto md:min-w-37.5\">\n <FiltersInput {...filtersProps} />\n </div>\n )}\n {sortProps && (\n <div className=\"w-full md:w-auto md:min-w-45\">\n <SortInput {...sortProps} />\n </div>\n )}\n {searchProps && (\n <div className=\"w-full md:ml-auto md:w-auto md:min-w-25\">\n <SearchInput {...searchProps} />\n </div>\n )}\n </div>\n\n {filterSettings?.selectedFilters && filterSettings.selectedFilters.length > 0 && onFilter && (\n <div className=\"w-full\">\n <SelectedFilters\n selectedFilters={filterSettings.selectedFilters}\n onDelete={handleFilterDelete}\n onClear={() => onFilter({ ...filterSettings, selectedFilters: [] })}\n />\n </div>\n )}\n {/* Count and Last Updated Info */}\n {(showCountInfo || lastUpdated) && (\n <div className=\"text-theme-secondary flex w-full items-center justify-between text-sm\">\n <div className=\"flex items-center gap-2\">\n {showCountInfo && (\n <span>\n <Trans>\n Showing {filteredCount} of {totalCount} {itemName}\n </Trans>\n </span>\n )}\n {lastUpdated &&\n (() => {\n const formattedDate = formatLastUpdated(lastUpdated)\n return (\n <span>\n <Trans>Last updated: {formattedDate}</Trans>\n </span>\n )\n })()}\n </div>\n </div>\n )}\n </Stack>\n </>\n )\n}\n","import { startTransition, useState } from \"react\"\nimport { FilterSettings, SortOption, SortSettings } from \"@/client/components/ListToolbar/types\"\n\n/**\n * Sort direction enumeration\n * Used across all sort-enabled list views\n */\nexport type SortDirection = \"asc\" | \"desc\"\n\n/**\n * Generic required sort settings with guaranteed non-optional properties\n * Used by components to maintain a fully-defined sort state\n *\n * Type parameter K is the sort key type (string literal union of allowed sort fields)\n *\n * @example\n * // For floating IPs\n * type FloatingIpListSortSettings = ListSortConfig<FloatingIpsSortKey>\n *\n * // For security groups\n * type SecurityGroupListSortSettings = ListSortConfig<\"name\" | \"project_id\">\n */\nexport type ListSortConfig<T extends string = string> = {\n /**\n * Array of available sort options to display in the UI\n */\n options: SortOption[]\n /**\n * The currently selected sort field (guaranteed to be one of the available options)\n */\n sortBy: T\n /**\n * The currently active sort direction (guaranteed to be either \"asc\" or \"desc\")\n */\n sortDirection: SortDirection\n}\n\n/**\n * Configuration options for the useListWithFiltering hook\n */\nexport interface UseListWithFilteringOptions<T extends string> {\n /** Default sort key to use on initial render */\n defaultSortKey: T\n /** Default sort direction to use on initial render */\n defaultSortDir: SortDirection\n /** Available sort options to display in the sort dropdown */\n sortOptions: Array<{ label: string; value: string }>\n /** Initial filter settings configuration */\n filterSettings: FilterSettings\n}\n\n/**\n * Return value from the useListWithFiltering hook\n */\nexport interface UseListWithFilteringReturn<T extends string> {\n // State\n searchTerm: string\n sortSettings: ListSortConfig<T>\n filterSettings: FilterSettings\n\n // Handlers\n handleSearchChange: (term: string | number | string[] | undefined) => void\n handleSortChange: (newSortSettings: SortSettings) => void\n handleFilterChange: (newFilterSettings: FilterSettings) => void\n}\n\n/**\n * Custom hook to manage common list view state: search, sort, and filter\n *\n * Extracts shared logic from list components to ensure consistent behavior\n * across all list pages and reduce code duplication.\n *\n * @example\n * ```tsx\n * const listState = useListWithFiltering({\n * defaultSortKey: \"name\",\n * defaultSortDir: \"asc\",\n * sortOptions: [\n * { label: t`Name`, value: \"name\" },\n * { label: t`Created`, value: \"created_at\" },\n * ],\n * filterSettings: {\n * filters: [\n * { displayName: t`Status`, filterName: \"status\", values: [\"active\", \"inactive\"] }\n * ]\n * }\n * })\n * ```\n */\nexport function useListWithFiltering<T extends string>({\n defaultSortKey,\n defaultSortDir,\n sortOptions,\n filterSettings: initialFilterSettings,\n}: UseListWithFilteringOptions<T>): UseListWithFilteringReturn<T> {\n // Search\n const [searchTerm, setSearchTerm] = useState(\"\")\n const handleSearchChange = (term: string | number | string[] | undefined) => {\n const searchValue = typeof term === \"string\" ? term : \"\"\n startTransition(() => setSearchTerm(searchValue))\n }\n\n // Sort\n const [sortSettings, setSortSettings] = useState<ListSortConfig<T>>({\n options: sortOptions,\n sortBy: defaultSortKey,\n sortDirection: defaultSortDir,\n })\n const handleSortChange = (newSortSettings: SortSettings) => {\n const settings: ListSortConfig<T> = {\n options: newSortSettings.options ?? sortSettings.options,\n sortBy: (newSortSettings.sortBy?.toString() as T) || defaultSortKey,\n sortDirection: (newSortSettings.sortDirection as SortDirection) || defaultSortDir,\n }\n setSortSettings(settings)\n }\n\n // Filter\n const [filterSettings, setFilterSettings] = useState<FilterSettings>(initialFilterSettings)\n const handleFilterChange = (newFilterSettings: FilterSettings) => {\n startTransition(() => setFilterSettings(newFilterSettings))\n }\n\n return {\n searchTerm,\n sortSettings,\n filterSettings,\n handleSearchChange,\n handleSortChange,\n handleFilterChange,\n }\n}\n"],"mappings":"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"}
@@ -9,4 +9,4 @@ var n = (n = !1) => {
9
9
  //#endregion
10
10
  export { n as t };
11
11
 
12
- //# sourceMappingURL=useModal-DxxlilRm.mjs.map
12
+ //# sourceMappingURL=useModal-DCs1OJh7.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useModal-DxxlilRm.mjs","names":["useCallback","useState","useModal","initialState","open","setOpen","toggleOpen"],"sources":["../../src/client/utils/useModal.ts"],"sourcesContent":["import { useCallback, useState } from \"react\"\n\ntype UseModalReturn = [boolean, () => void]\n\nexport const useModal = (initialState: boolean = false): UseModalReturn => {\n const [open, setOpen] = useState(initialState)\n\n const toggleOpen = useCallback(() => {\n setOpen((open) => !open)\n }, [])\n\n return [open, toggleOpen]\n}\n"],"mappings":";;AAIA,IAAaE,KAAYC,IAAwB,OAAK;CACpD,IAAM,CAACC,GAAMC,KAAWJ,EAASE,EAAAA;AAMjC,QAAO,CAACC,GAJWJ,QAAY;AAC7BK,KAASD,MAAS,CAACA,EAAAA;IAClB,EAAE,CAAA,CAEoB"}
1
+ {"version":3,"file":"useModal-DCs1OJh7.mjs","names":["useCallback","useState","useModal","initialState","open","setOpen","toggleOpen"],"sources":["../../src/client/utils/useModal.ts"],"sourcesContent":["import { useCallback, useState } from \"react\"\n\ntype UseModalReturn = [boolean, () => void]\n\nexport const useModal = (initialState: boolean = false): UseModalReturn => {\n const [open, setOpen] = useState(initialState)\n\n const toggleOpen = useCallback(() => {\n setOpen((open) => !open)\n }, [])\n\n return [open, toggleOpen]\n}\n"],"mappings":";;AAIA,IAAaE,KAAYC,IAAwB,OAAK;CACpD,IAAM,CAACC,GAAMC,KAAWJ,EAASE,CAAAA;CAMjC,OAAO,CAACC,GAJWJ,QAAY;EAC7BK,GAASD,MAAS,CAACA,CAAAA;CACrB,GAAG,CAAA,CAEWE,CAAAA;AAChB"}
@@ -8,4 +8,4 @@ function t() {
8
8
  //#endregion
9
9
  export { t };
10
10
 
11
- //# sourceMappingURL=useProjectId-OQv2KBbG.mjs.map
11
+ //# sourceMappingURL=useProjectId-DBc5lpoU.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useProjectId-OQv2KBbG.mjs","names":["useParams","useProjectId","projectId","strict","Error"],"sources":["../../src/client/hooks/useProjectId.ts"],"sourcesContent":["import { useParams } from \"@tanstack/react-router\"\n\n/**\n * Extract projectId from the current URL.\n *\n * This hook works with both old and new route structures:\n * - Old: /accounts/:accountId/projects/:projectId/...\n * - New: /projects/:projectId/...\n *\n * @throws {Error} If used outside of a project route context\n * @returns {string} The current project ID from URL params\n *\n * @example\n * ```tsx\n * function SecurityGroupsList() {\n * const projectId = useProjectId()\n *\n * const { data } = trpc.network.securityGroup.list.useQuery({\n * project_id: projectId\n * })\n * }\n * ```\n */\nexport function useProjectId(): string {\n // Use strict: false to work with any route that has projectId param\n // Works with both:\n // - /accounts/:accountId/projects/:projectId/... (old)\n // - /projects/:projectId/... (new)\n const { projectId } = useParams({\n strict: false,\n })\n\n // Runtime validation - provides clear error message if projectId is not in URL\n if (!projectId) {\n throw new Error(\n \"useProjectId() must be used within a project-scoped route. \" +\n \"This is likely a routing configuration error. \" +\n \"Expected route pattern with :projectId parameter\"\n )\n }\n\n return projectId\n}\n"],"mappings":";;AAuBA,SAAgBC,IAAAA;CAKd,IAAM,EAAEC,iBAAcF,EAAU,EAC9BG,QAAQ,IACV,CAAA;AAGA,KAAI,CAACD,EACH,OAAUE,MACR,4JAEE;AAIN,QAAOF"}
1
+ {"version":3,"file":"useProjectId-DBc5lpoU.mjs","names":["useParams","useProjectId","projectId","strict","Error"],"sources":["../../src/client/hooks/useProjectId.ts"],"sourcesContent":["import { useParams } from \"@tanstack/react-router\"\n\n/**\n * Extract projectId from the current URL.\n *\n * This hook works with both old and new route structures:\n * - Old: /accounts/:accountId/projects/:projectId/...\n * - New: /projects/:projectId/...\n *\n * @throws {Error} If used outside of a project route context\n * @returns {string} The current project ID from URL params\n *\n * @example\n * ```tsx\n * function SecurityGroupsList() {\n * const projectId = useProjectId()\n *\n * const { data } = trpc.network.securityGroup.list.useQuery({\n * project_id: projectId\n * })\n * }\n * ```\n */\nexport function useProjectId(): string {\n // Use strict: false to work with any route that has projectId param\n // Works with both:\n // - /accounts/:accountId/projects/:projectId/... (old)\n // - /projects/:projectId/... (new)\n const { projectId } = useParams({\n strict: false,\n })\n\n // Runtime validation - provides clear error message if projectId is not in URL\n if (!projectId) {\n throw new Error(\n \"useProjectId() must be used within a project-scoped route. \" +\n \"This is likely a routing configuration error. \" +\n \"Expected route pattern with :projectId parameter\"\n )\n }\n\n return projectId\n}\n"],"mappings":";;AAuBA,SAAgBC,IAAAA;CAKd,IAAM,EAAEC,iBAAcF,EAAU,EAC9BG,QAAQ,GACV,CAAA;CAGA,IAAI,CAACD,GACH,MAAUE,MACR,2JAEE;CAIN,OAAOF;AACT"}