@cobaltcore-dev/aurora 0.1.0 → 0.2.1

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 (172) hide show
  1. package/dist/client/{ContentHeader-H8KGY3Wd.mjs → ContentHeader-DtBiIwRY.mjs} +15 -15
  2. package/dist/client/{ContentHeader-H8KGY3Wd.mjs.map → ContentHeader-DtBiIwRY.mjs.map} +1 -1
  3. package/dist/client/{DeleteFlavorModal-B98oiHWx.mjs → DeleteFlavorModal-rmuYIafD.mjs} +142 -142
  4. package/dist/client/{DeleteFlavorModal-B98oiHWx.mjs.map → DeleteFlavorModal-rmuYIafD.mjs.map} +1 -1
  5. package/dist/client/{EditSecurityGroupModal-wQVNIVg1.mjs → EditSecurityGroupModal-B7Sz9puM.mjs} +15 -15
  6. package/dist/client/{EditSecurityGroupModal-wQVNIVg1.mjs.map → EditSecurityGroupModal-B7Sz9puM.mjs.map} +1 -1
  7. package/dist/client/{FloatingIpActionModals-qu1NMI5a.mjs → FloatingIpActionModals-CfRJiZqD.mjs} +59 -59
  8. package/dist/client/{FloatingIpActionModals-qu1NMI5a.mjs.map → FloatingIpActionModals-CfRJiZqD.mjs.map} +1 -1
  9. package/dist/client/{ImageToastNotifications-wsQDNEh7.mjs → ImageToastNotifications-Cw30RXsw.mjs} +374 -374
  10. package/dist/client/{ImageToastNotifications-wsQDNEh7.mjs.map → ImageToastNotifications-Cw30RXsw.mjs.map} +1 -1
  11. package/dist/client/{ListToolbar-CHlkZrpl.mjs → ListToolbar-DuazvsAu.mjs} +61 -61
  12. package/dist/client/{ListToolbar-CHlkZrpl.mjs.map → ListToolbar-DuazvsAu.mjs.map} +1 -1
  13. package/dist/client/{RouteError-BwgDIwJE.mjs → RouteError-Cyto623-.mjs} +2 -2
  14. package/dist/client/{RouteError-BwgDIwJE.mjs.map → RouteError-Cyto623-.mjs.map} +1 -1
  15. package/dist/client/{_auth-CsliQdkJ.mjs → _auth-CJj1Cnbm.mjs} +1 -1
  16. package/dist/client/{_auth-CsliQdkJ.mjs.map → _auth-CJj1Cnbm.mjs.map} +1 -1
  17. package/dist/client/_flavorId-B-1fYadl.mjs +188 -0
  18. package/dist/client/_flavorId-B-1fYadl.mjs.map +1 -0
  19. package/dist/client/{_flavorId-D_A53VYa.mjs → _flavorId-BYfIHIV_.mjs} +20 -10
  20. package/dist/client/_flavorId-BYfIHIV_.mjs.map +1 -0
  21. package/dist/client/_floatingIpId-FQ5P2qMV.mjs +228 -0
  22. package/dist/client/_floatingIpId-FQ5P2qMV.mjs.map +1 -0
  23. package/dist/client/{_floatingIpId-BGgftRBQ.mjs → _floatingIpId-IrnN-ozB.mjs} +13 -3
  24. package/dist/client/_floatingIpId-IrnN-ozB.mjs.map +1 -0
  25. package/dist/client/_imageId-Tx_9bqEc.mjs +527 -0
  26. package/dist/client/_imageId-Tx_9bqEc.mjs.map +1 -0
  27. package/dist/client/{_pcaId-DBgz5V_9.mjs → _pcaId-DWHfvMhT.mjs} +16 -3
  28. package/dist/client/_pcaId-DWHfvMhT.mjs.map +1 -0
  29. package/dist/client/{_pcaId-C7Lrv1H_.mjs → _pcaId-r2BTjN9y.mjs} +81 -81
  30. package/dist/client/_pcaId-r2BTjN9y.mjs.map +1 -0
  31. package/dist/client/{_projectId-INhedXor.mjs → _projectId-B1VjDd0Z.mjs} +3 -3
  32. package/dist/client/{_projectId-INhedXor.mjs.map → _projectId-B1VjDd0Z.mjs.map} +1 -1
  33. package/dist/client/_projectId-Bs4W9hos.mjs +283 -0
  34. package/dist/client/_projectId-Bs4W9hos.mjs.map +1 -0
  35. package/dist/client/{_projectId-C-E4NNgo.mjs → _projectId-PSpuCKO7.mjs} +9 -9
  36. package/dist/client/{_projectId-C-E4NNgo.mjs.map → _projectId-PSpuCKO7.mjs.map} +1 -1
  37. package/dist/client/{_projectId-B9fln31N.mjs → _projectId-Pxp-RXK4.mjs} +2 -2
  38. package/dist/client/{_projectId-B9fln31N.mjs.map → _projectId-Pxp-RXK4.mjs.map} +1 -1
  39. package/dist/client/{_securityGroupId-DQoRQ-yA.mjs → _securityGroupId-Dqi6ddw4.mjs} +499 -499
  40. package/dist/client/_securityGroupId-Dqi6ddw4.mjs.map +1 -0
  41. package/dist/client/{_securityGroupId-ihjy8Lcd.mjs → _securityGroupId-VV2lUcGQ.mjs} +16 -3
  42. package/dist/client/_securityGroupId-VV2lUcGQ.mjs.map +1 -0
  43. package/dist/client/{about-oT6ccz8T.mjs → about-B2AzqxFI.mjs} +8 -8
  44. package/dist/client/{about-oT6ccz8T.mjs.map → about-B2AzqxFI.mjs.map} +1 -1
  45. package/dist/client/{aurora-D_NPTbo-.mjs → aurora-CRcxVUCo.mjs} +1 -1
  46. package/dist/client/{aurora-D_NPTbo-.mjs.map → aurora-CRcxVUCo.mjs.map} +1 -1
  47. package/dist/client/{build-eu9eg0zF.mjs → build-Cf7iWbpH.mjs} +2727 -2646
  48. package/dist/client/build-Cf7iWbpH.mjs.map +1 -0
  49. package/dist/client/{buildFilterParams-BDOIRDeD.mjs → buildFilterParams-ngVK3ybs.mjs} +1 -1
  50. package/dist/client/{buildFilterParams-BDOIRDeD.mjs.map → buildFilterParams-ngVK3ybs.mjs.map} +1 -1
  51. package/dist/client/{constants-ByHCdNsI.mjs → constants-CCgR6fKI.mjs} +15 -15
  52. package/dist/client/{constants-ByHCdNsI.mjs.map → constants-CCgR6fKI.mjs.map} +1 -1
  53. package/dist/client/{containers-rn_ntCJu.mjs → containers-BWERuY0O.mjs} +978 -976
  54. package/dist/client/containers-BWERuY0O.mjs.map +1 -0
  55. package/dist/client/{containers-Dx7TYruP.mjs → containers-Cs5vOeR2.mjs} +1 -1
  56. package/dist/client/{containers-Dx7TYruP.mjs.map → containers-Cs5vOeR2.mjs.map} +1 -1
  57. package/dist/client/{containers-B_ozmVlx.mjs → containers-DovytjVP.mjs} +6 -3
  58. package/dist/client/containers-DovytjVP.mjs.map +1 -0
  59. package/dist/client/{flavors-DRZb9LJP.mjs → flavors-BXPYAFyQ.mjs} +1 -1
  60. package/dist/client/flavors-BXPYAFyQ.mjs.map +1 -0
  61. package/dist/client/{flavors-CT4auvLO.mjs → flavors-Bovz-I2U.mjs} +137 -137
  62. package/dist/client/flavors-Bovz-I2U.mjs.map +1 -0
  63. package/dist/client/flavors-CUiALHcB.mjs +16 -0
  64. package/dist/client/flavors-CUiALHcB.mjs.map +1 -0
  65. package/dist/client/floatingips-BrjDiY2t.mjs +435 -0
  66. package/dist/client/floatingips-BrjDiY2t.mjs.map +1 -0
  67. package/dist/client/{formatBytes-GYujK0dP.mjs → formatBytes-D6oa0wU9.mjs} +1 -1
  68. package/dist/client/{formatBytes-GYujK0dP.mjs.map → formatBytes-D6oa0wU9.mjs.map} +1 -1
  69. package/dist/client/{hooks-s-I8vWww.mjs → hooks-D0krAKvo.mjs} +1 -1
  70. package/dist/client/images-BZP3pBqj.mjs +16 -0
  71. package/dist/client/images-BZP3pBqj.mjs.map +1 -0
  72. package/dist/client/{images-DRTfx8k2.mjs → images-DM9I8G0p.mjs} +1 -1
  73. package/dist/client/images-DM9I8G0p.mjs.map +1 -0
  74. package/dist/client/{images-BTqRflJv2.mjs → images-DaaCUXMI.mjs} +413 -410
  75. package/dist/client/images-DaaCUXMI.mjs.map +1 -0
  76. package/dist/client/index.js +494 -414
  77. package/dist/client/index.js.map +1 -1
  78. package/dist/client/{md-CI9FmfYv.mjs → md-BivyCkGC.mjs} +1 -1
  79. package/dist/client/{md-CI9FmfYv.mjs.map → md-BivyCkGC.mjs.map} +1 -1
  80. package/dist/client/{network-DFVVVNS5.mjs → network-SCVadZsv.mjs} +1 -1
  81. package/dist/client/{network-DFVVVNS5.mjs.map → network-SCVadZsv.mjs.map} +1 -1
  82. package/dist/client/{objects-DkDKVSmQ.mjs → objects-B4yrYf_a.mjs} +1 -1
  83. package/dist/client/objects-B4yrYf_a.mjs.map +1 -0
  84. package/dist/client/objects-Cw4Vu01M.mjs +4760 -0
  85. package/dist/client/objects-Cw4Vu01M.mjs.map +1 -0
  86. package/dist/client/{objects-r_Vl31oj.mjs → objects-D4zBka5e.mjs} +9 -3
  87. package/dist/client/objects-D4zBka5e.mjs.map +1 -0
  88. package/dist/client/overview-2J54-loz.mjs +15 -0
  89. package/dist/client/overview-2J54-loz.mjs.map +1 -0
  90. package/dist/client/{overview-B7pXx6bt.mjs → overview-BnmukbFh.mjs} +5 -5
  91. package/dist/client/overview-BnmukbFh.mjs.map +1 -0
  92. package/dist/client/overview-BtIXpYBo.mjs +15 -0
  93. package/dist/client/overview-BtIXpYBo.mjs.map +1 -0
  94. package/dist/client/overview-D0AAvsmL.mjs +15 -0
  95. package/dist/client/overview-D0AAvsmL.mjs.map +1 -0
  96. package/dist/client/pca-BqZycwCu.mjs +16 -0
  97. package/dist/client/pca-BqZycwCu.mjs.map +1 -0
  98. package/dist/client/pca-V2aaOxZA.mjs +167 -0
  99. package/dist/client/pca-V2aaOxZA.mjs.map +1 -0
  100. package/dist/client/{projects-_Dfn6eQT.mjs → projects-0feOw_b6.mjs} +2 -2
  101. package/dist/client/{projects-_Dfn6eQT.mjs.map → projects-0feOw_b6.mjs.map} +1 -1
  102. package/dist/client/{projects-MbS1USl2.mjs → projects-BsN4bvU2.mjs} +1 -1
  103. package/dist/client/{projects-MbS1USl2.mjs.map → projects-BsN4bvU2.mjs.map} +1 -1
  104. package/dist/client/{projects-BuN69cxO.mjs → projects-C1IYOvFQ.mjs} +29 -29
  105. package/dist/client/{projects-BuN69cxO.mjs.map → projects-C1IYOvFQ.mjs.map} +1 -1
  106. package/dist/client/{projects-D1pP0XdA.mjs → projects-jyIHL6DE.mjs} +2 -2
  107. package/dist/client/{projects-D1pP0XdA.mjs.map → projects-jyIHL6DE.mjs.map} +1 -1
  108. package/dist/client/securitygroups-B4MkSBtI.mjs +441 -0
  109. package/dist/client/securitygroups-B4MkSBtI.mjs.map +1 -0
  110. package/dist/client/{useListWithFiltering-mMX_EfyI.mjs → useListWithFiltering-CEDh1LO-.mjs} +1 -1
  111. package/dist/client/{useListWithFiltering-mMX_EfyI.mjs.map → useListWithFiltering-CEDh1LO-.mjs.map} +1 -1
  112. package/dist/client/{useModal-Dg4CBeqL.mjs → useModal-DxxlilRm.mjs} +1 -1
  113. package/dist/client/{useModal-Dg4CBeqL.mjs.map → useModal-DxxlilRm.mjs.map} +1 -1
  114. package/dist/client/{useProjectId-BWaeJZOy.mjs → useProjectId-CgOTejka.mjs} +1 -1
  115. package/dist/client/{useProjectId-BWaeJZOy.mjs.map → useProjectId-CgOTejka.mjs.map} +1 -1
  116. package/package.json +8 -7
  117. package/permission_policies/compute.yaml +975 -0
  118. package/permission_policies/image.yaml +71 -0
  119. package/dist/client/_flavorId-D_A53VYa.mjs.map +0 -1
  120. package/dist/client/_flavorId-DbhYLFxY.mjs +0 -190
  121. package/dist/client/_flavorId-DbhYLFxY.mjs.map +0 -1
  122. package/dist/client/_floatingIpId-BGgftRBQ.mjs.map +0 -1
  123. package/dist/client/_floatingIpId-D5myuLFz.mjs +0 -228
  124. package/dist/client/_floatingIpId-D5myuLFz.mjs.map +0 -1
  125. package/dist/client/_imageId-BoHX155h.mjs +0 -27
  126. package/dist/client/_imageId-BoHX155h.mjs.map +0 -1
  127. package/dist/client/_imageId-CTa0c3Av.mjs +0 -530
  128. package/dist/client/_imageId-CTa0c3Av.mjs.map +0 -1
  129. package/dist/client/_pcaId-C7Lrv1H_.mjs.map +0 -1
  130. package/dist/client/_pcaId-DBgz5V_9.mjs.map +0 -1
  131. package/dist/client/_projectId-Be1Erj68.mjs +0 -300
  132. package/dist/client/_projectId-Be1Erj68.mjs.map +0 -1
  133. package/dist/client/_securityGroupId-DQoRQ-yA.mjs.map +0 -1
  134. package/dist/client/_securityGroupId-ihjy8Lcd.mjs.map +0 -1
  135. package/dist/client/build-eu9eg0zF.mjs.map +0 -1
  136. package/dist/client/containers-B_ozmVlx.mjs.map +0 -1
  137. package/dist/client/containers-rn_ntCJu.mjs.map +0 -1
  138. package/dist/client/flavors-CT4auvLO.mjs.map +0 -1
  139. package/dist/client/flavors-DRZb9LJP.mjs.map +0 -1
  140. package/dist/client/flavors-DtgMd0Ii.mjs +0 -12
  141. package/dist/client/flavors-DtgMd0Ii.mjs.map +0 -1
  142. package/dist/client/floatingips-DG5cFJSZ.mjs +0 -12
  143. package/dist/client/floatingips-DG5cFJSZ.mjs.map +0 -1
  144. package/dist/client/floatingips-iCMR0ZiL.mjs +0 -436
  145. package/dist/client/floatingips-iCMR0ZiL.mjs.map +0 -1
  146. package/dist/client/images-BTqRflJv2.mjs.map +0 -1
  147. package/dist/client/images-DRTfx8k2.mjs.map +0 -1
  148. package/dist/client/images-xBfsjxkX.mjs +0 -12
  149. package/dist/client/images-xBfsjxkX.mjs.map +0 -1
  150. package/dist/client/objects-CKk6cST_.mjs +0 -4762
  151. package/dist/client/objects-CKk6cST_.mjs.map +0 -1
  152. package/dist/client/objects-DkDKVSmQ.mjs.map +0 -1
  153. package/dist/client/objects-r_Vl31oj.mjs.map +0 -1
  154. package/dist/client/overview-B7pXx6bt.mjs.map +0 -1
  155. package/dist/client/overview-CKGLIu6W.mjs +0 -12
  156. package/dist/client/overview-CKGLIu6W.mjs.map +0 -1
  157. package/dist/client/overview-Ca8r3SAz.mjs +0 -16
  158. package/dist/client/overview-Ca8r3SAz.mjs.map +0 -1
  159. package/dist/client/overview-DkPM0Od5.mjs +0 -12
  160. package/dist/client/overview-DkPM0Od5.mjs.map +0 -1
  161. package/dist/client/overview-Dxm7Ef3X.mjs +0 -12
  162. package/dist/client/overview-Dxm7Ef3X.mjs.map +0 -1
  163. package/dist/client/overview-ag4Envez.mjs +0 -16
  164. package/dist/client/overview-ag4Envez.mjs.map +0 -1
  165. package/dist/client/pca-BGv7Mprl.mjs +0 -12
  166. package/dist/client/pca-BGv7Mprl.mjs.map +0 -1
  167. package/dist/client/pca-DpULpMu5.mjs +0 -167
  168. package/dist/client/pca-DpULpMu5.mjs.map +0 -1
  169. package/dist/client/securitygroups-DURjFfYK.mjs +0 -12
  170. package/dist/client/securitygroups-DURjFfYK.mjs.map +0 -1
  171. package/dist/client/securitygroups-KC2qvmH8.mjs +0 -442
  172. package/dist/client/securitygroups-KC2qvmH8.mjs.map +0 -1
@@ -0,0 +1,441 @@
1
+ import { $ as e, A as t, C as n, K as r, L as i, N as a, O as o, T as s, U as c, V as l, W as u, Y as d, c as f, h as p, it as m, l as h, nt as g, r as _, rt as v, w as y, z as b } from "./build-Cf7iWbpH.mjs";
2
+ import { r as x } from "./trpcClient-BxguzNYF.mjs";
3
+ import { t as S } from "./ListToolbar-DuazvsAu.mjs";
4
+ import { t as C } from "./useProjectId-CgOTejka.mjs";
5
+ import "./hooks-D0krAKvo.mjs";
6
+ import { t as w } from "./buildFilterParams-ngVK3ybs.mjs";
7
+ import { t as T } from "./useListWithFiltering-CEDh1LO-.mjs";
8
+ import { t as E } from "./EditSecurityGroupModal-B7Sz9puM.mjs";
9
+ import { Fragment as D, jsx as O, jsxs as k } from "react/jsx-runtime";
10
+ import { useEffect as A, useRef as j, useState as M } from "react";
11
+ import { useNavigate as N } from "@tanstack/react-router";
12
+ import { Trans as P, useLingui as F } from "@lingui/react";
13
+ //#region src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/-modals/DeleteSecurityGroupDialog.tsx
14
+ var I = ({ isOpen: e, onClose: t, securityGroup: n, onDelete: r, isDeleting: a = !1, error: o = null }) => {
15
+ let { i18n: s, _: l } = F(), [u, d] = M(""), f = s._({ id: "HNlEFZ" }), g = u.toLowerCase() === f.toLowerCase(), _ = n.name || n.id, v = (e) => {
16
+ e.preventDefault(), g && !a && r(n.id);
17
+ }, b = () => {
18
+ d(""), t();
19
+ };
20
+ return /* @__PURE__ */ O(m, {
21
+ open: e,
22
+ onCancel: b,
23
+ size: "small",
24
+ title: s._({
25
+ id: "Y+2SDm",
26
+ values: { securityGroupName: _ }
27
+ }),
28
+ modalFooter: /* @__PURE__ */ O(i, {
29
+ className: "flex justify-end",
30
+ children: /* @__PURE__ */ k(p, { children: [/* @__PURE__ */ O(h, {
31
+ variant: "default",
32
+ onClick: b,
33
+ disabled: a,
34
+ children: /* @__PURE__ */ O(P, { id: "dEgA5A" })
35
+ }), /* @__PURE__ */ O(h, {
36
+ variant: "primary-danger",
37
+ onClick: v,
38
+ disabled: !g || a,
39
+ "data-testid": "confirm-delete-button",
40
+ children: a ? /* @__PURE__ */ O(P, { id: "EF2EU9" }) : /* @__PURE__ */ O(P, { id: "cnGeoo" })
41
+ })] })
42
+ }),
43
+ children: /* @__PURE__ */ k("div", { children: [
44
+ o && /* @__PURE__ */ O(c, {
45
+ dismissible: !1,
46
+ variant: "error",
47
+ className: "mt-4",
48
+ children: o
49
+ }),
50
+ /* @__PURE__ */ O(P, { id: "Qb+14I" }),
51
+ /* @__PURE__ */ k("div", {
52
+ className: "mt-4",
53
+ children: [/* @__PURE__ */ O("p", {
54
+ className: "mb-2 text-sm",
55
+ children: /* @__PURE__ */ O(P, {
56
+ id: "hLp49h",
57
+ values: { deleteWord: f },
58
+ components: { 0: /* @__PURE__ */ O("strong", {}) }
59
+ })
60
+ }), /* @__PURE__ */ O(y, {
61
+ id: "confirmation",
62
+ name: "confirmation",
63
+ value: u,
64
+ onChange: (e) => d(e.target.value),
65
+ placeholder: f,
66
+ disabled: a,
67
+ autoComplete: "off",
68
+ "data-testid": "delete-confirmation-input"
69
+ })]
70
+ })
71
+ ] })
72
+ });
73
+ };
74
+ //#endregion
75
+ //#region src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/SecurityGroupTableRow.tsx
76
+ function L({ securityGroup: e, permissions: n, onEdit: r, onDelete: i, onViewDetails: o, isReadOnly: c = !1 }) {
77
+ let { i18n: l, _: f } = F(), p = ({ value: e }) => /* @__PURE__ */ O("span", { children: e ? l._({ id: "l75CjT" }) : l._({ id: "1UzENP" }) }), m = () => {
78
+ o && o(e);
79
+ };
80
+ return /* @__PURE__ */ k(a, {
81
+ "data-testid": `security-group-row-${e.id}`,
82
+ onClick: m,
83
+ className: "hover:bg-theme-background-lvl-2 cursor-pointer",
84
+ children: [
85
+ /* @__PURE__ */ O(t, { children: /* @__PURE__ */ k("div", { children: [/* @__PURE__ */ O("p", {
86
+ className: "text-md",
87
+ children: e.name
88
+ }), /* @__PURE__ */ O("p", {
89
+ className: "text-theme-light text-xs",
90
+ children: e.id
91
+ })] }) }),
92
+ /* @__PURE__ */ O(t, { children: e.description || l._({ id: "h47p9L" }) }),
93
+ /* @__PURE__ */ k(t, { children: [/* @__PURE__ */ O(p, { value: e.shared }), e.shared && /* @__PURE__ */ k("p", { children: [
94
+ /* @__PURE__ */ O(P, { id: "LtI9AS" }),
95
+ ": ",
96
+ /* @__PURE__ */ O("span", {
97
+ className: "text-theme-light text-xs",
98
+ children: e.project_id
99
+ })
100
+ ] })] }),
101
+ /* @__PURE__ */ O(t, { children: /* @__PURE__ */ O(p, { value: e.stateful }) }),
102
+ /* @__PURE__ */ O(t, {
103
+ onClick: (e) => e.stopPropagation(),
104
+ className: "items-end justify-end pr-0",
105
+ children: /* @__PURE__ */ O(d, { children: /* @__PURE__ */ k(s, { children: [
106
+ /* @__PURE__ */ O(u, {
107
+ label: l._({ id: "v0hPHE" }),
108
+ onClick: () => m()
109
+ }),
110
+ n.canUpdate && !c && /* @__PURE__ */ O(u, {
111
+ label: l._({ id: "ePK91l" }),
112
+ onClick: () => r(e)
113
+ }),
114
+ n.canDelete && !c && /* @__PURE__ */ O(u, {
115
+ label: l._({ id: "cnGeoo" }),
116
+ onClick: () => i(e)
117
+ })
118
+ ] }) })
119
+ })
120
+ ]
121
+ }, e.id);
122
+ }
123
+ //#endregion
124
+ //#region src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/SecurityGroupListContainer.tsx
125
+ var R = ({ securityGroups: t, isLoading: n, isError: i, error: o, permissions: s, onDeleteSecurityGroup: c, isDeletingSecurityGroup: u = !1, deleteError: d = null, onUpdateSecurityGroup: f, isUpdatingSecurityGroup: p = !1, updateError: m = null, currentProjectId: h }) => {
126
+ let { i18n: g, _ } = F(), v = N(), y = C(), [x, S] = M(null), [w, T] = M(!1), [R, z] = M(!1), B = j(!1), V = j(!1), H = (e) => {
127
+ S(e), T(!0);
128
+ }, U = (e) => {
129
+ S(e), z(!0);
130
+ }, W = (e) => {
131
+ v({
132
+ to: "/projects/$projectId/network/securitygroups/$securityGroupId",
133
+ params: {
134
+ projectId: y,
135
+ securityGroupId: e.id
136
+ }
137
+ });
138
+ }, G = () => {
139
+ S(null), T(!1);
140
+ }, K = () => {
141
+ S(null), z(!1);
142
+ };
143
+ return A(() => {
144
+ B.current && !u && R && !d && K(), B.current = u;
145
+ }, [
146
+ u,
147
+ d,
148
+ R
149
+ ]), A(() => {
150
+ V.current && !p && w && !m && G(), V.current = p;
151
+ }, [
152
+ p,
153
+ m,
154
+ w
155
+ ]), n ? /* @__PURE__ */ k(e, {
156
+ className: "py-8",
157
+ distribution: "center",
158
+ alignment: "center",
159
+ direction: "vertical",
160
+ children: [/* @__PURE__ */ O(r, {
161
+ variant: "primary",
162
+ size: "large",
163
+ className: "mb-2"
164
+ }), /* @__PURE__ */ O(P, { id: "Z3FXyt" })]
165
+ }) : i ? /* @__PURE__ */ O(e, {
166
+ className: "py-8",
167
+ distribution: "center",
168
+ alignment: "center",
169
+ direction: "vertical",
170
+ children: o?.message ?? g._({ id: "Wca9WC" })
171
+ }) : t.length === 0 ? /* @__PURE__ */ O(P, { id: "SfW/3r" }) : /* @__PURE__ */ k(D, { children: [/* @__PURE__ */ k(b, {
172
+ columns: 5,
173
+ children: [/* @__PURE__ */ O(a, { children: [
174
+ g._({ id: "6YtxFj" }),
175
+ g._({ id: "Nu4oKW" }),
176
+ g._({ id: "0Gd0NU" }),
177
+ g._({ id: "Jim5X9" }),
178
+ ""
179
+ ].map((e) => /* @__PURE__ */ O(l, { children: e }, e)) }), t.map((e) => /* @__PURE__ */ O(L, {
180
+ securityGroup: e,
181
+ permissions: s,
182
+ onEdit: H,
183
+ onDelete: U,
184
+ onViewDetails: W,
185
+ isReadOnly: !!(h && e.project_id && e.project_id !== h)
186
+ }, e.id))]
187
+ }), x && /* @__PURE__ */ k(D, { children: [/* @__PURE__ */ O(E, {
188
+ securityGroup: x,
189
+ open: w,
190
+ onClose: G,
191
+ onUpdate: async (e, t) => {
192
+ f && await f(e, t);
193
+ },
194
+ isLoading: p,
195
+ error: m
196
+ }), /* @__PURE__ */ O(I, {
197
+ securityGroup: x,
198
+ isOpen: R,
199
+ onClose: K,
200
+ onDelete: (e) => {
201
+ c && c(e);
202
+ },
203
+ isDeleting: u,
204
+ error: d
205
+ })] })] });
206
+ }, z = {
207
+ name: "",
208
+ description: "",
209
+ stateful: !0
210
+ }, B = ({ isOpen: e, onClose: t, onCreate: a, isLoading: o = !1, error: s = null }) => {
211
+ let { i18n: l, _: u } = F(), [d, b] = M({ ...z }), [x, S] = M({}), C = (e) => {
212
+ let { name: t, value: n, type: r } = e.target, i = e.target.checked;
213
+ b((e) => ({
214
+ ...e,
215
+ [t]: r === "checkbox" ? i : n
216
+ })), x[t] && S((e) => {
217
+ let n = { ...e };
218
+ return delete n[t], n;
219
+ });
220
+ }, w = () => {
221
+ let e = {};
222
+ return (!d.name || d.name.trim() === "") && (e.name = l._({ id: "lN/Z9n" })), S(e), Object.keys(e).length === 0;
223
+ }, T = async (e) => {
224
+ e.preventDefault(), w() && (await a({
225
+ name: d.name.trim(),
226
+ description: d.description.trim() || void 0,
227
+ stateful: d.stateful
228
+ }), E());
229
+ }, E = () => {
230
+ b({ ...z }), S({}), t();
231
+ };
232
+ return /* @__PURE__ */ k(m, {
233
+ open: e,
234
+ onCancel: E,
235
+ size: "large",
236
+ title: l._({ id: "YjAOtb" }),
237
+ modalFooter: /* @__PURE__ */ O(i, {
238
+ className: "flex justify-end",
239
+ children: /* @__PURE__ */ k(p, { children: [/* @__PURE__ */ O(h, {
240
+ variant: "default",
241
+ onClick: E,
242
+ disabled: o,
243
+ children: /* @__PURE__ */ O(P, { id: "dEgA5A" })
244
+ }), /* @__PURE__ */ O(h, {
245
+ variant: "primary",
246
+ onClick: (e) => {
247
+ T(e);
248
+ },
249
+ disabled: o,
250
+ "data-testid": "create-security-group-button",
251
+ children: o ? /* @__PURE__ */ O(r, { size: "small" }) : /* @__PURE__ */ O(P, { id: "YjAOtb" })
252
+ })] })
253
+ }),
254
+ children: [
255
+ s && /* @__PURE__ */ O(c, {
256
+ dismissible: !1,
257
+ variant: "error",
258
+ className: "mb-4",
259
+ children: s
260
+ }),
261
+ o && /* @__PURE__ */ k("div", {
262
+ className: "mb-4 flex items-center justify-center gap-2",
263
+ children: [/* @__PURE__ */ O(r, { variant: "primary" }), /* @__PURE__ */ O("span", {
264
+ className: "text-theme-high text-sm",
265
+ children: /* @__PURE__ */ O(P, { id: "Km4AGG" })
266
+ })]
267
+ }),
268
+ !o && /* @__PURE__ */ O(v, {
269
+ className: "mb-6",
270
+ children: /* @__PURE__ */ k(_, {
271
+ className: "mb-6",
272
+ children: [
273
+ /* @__PURE__ */ O(g, {
274
+ className: "mb-6",
275
+ children: /* @__PURE__ */ O(y, {
276
+ id: "name",
277
+ name: "name",
278
+ label: l._({ id: "6YtxFj" }),
279
+ value: d.name,
280
+ onChange: C,
281
+ required: !0,
282
+ errortext: x.name,
283
+ placeholder: l._({ id: "Ac6dy9" }),
284
+ disabled: o
285
+ })
286
+ }),
287
+ /* @__PURE__ */ O(g, {
288
+ className: "mb-6",
289
+ children: /* @__PURE__ */ O(f, {
290
+ id: "description",
291
+ name: "description",
292
+ label: l._({ id: "Nu4oKW" }),
293
+ value: d.description,
294
+ onChange: C,
295
+ placeholder: l._({ id: "Nu4oKW" }),
296
+ disabled: o,
297
+ rows: 3
298
+ })
299
+ }),
300
+ /* @__PURE__ */ O(g, {
301
+ className: "mb-0",
302
+ children: /* @__PURE__ */ O(n, {
303
+ id: "stateful",
304
+ name: "stateful",
305
+ label: l._({ id: "Jim5X9" }),
306
+ checked: d.stateful,
307
+ onChange: C,
308
+ disabled: o
309
+ })
310
+ })
311
+ ]
312
+ })
313
+ })
314
+ ]
315
+ });
316
+ }, V = {
317
+ TRUE: "true",
318
+ FALSE: "false"
319
+ }, H = () => {
320
+ let { i18n: e, _: t } = F(), n = N(), r = C(), [i, a] = M(!1), [o, s] = M(null), [c, l] = M(null), [u, d] = M(null), { searchTerm: f, sortSettings: p, filterSettings: m, handleSearchChange: g, handleSortChange: _, handleFilterChange: v } = T({
321
+ defaultSortKey: "name",
322
+ defaultSortDir: "asc",
323
+ sortOptions: [{
324
+ label: e._({ id: "6YtxFj" }),
325
+ value: "name"
326
+ }, {
327
+ label: e._({ id: "podzPY" }),
328
+ value: "project_id"
329
+ }],
330
+ filterSettings: { filters: [{
331
+ displayName: e._({ id: "0Gd0NU" }),
332
+ filterName: "shared",
333
+ values: Object.values(V),
334
+ supportsMultiValue: !1
335
+ }] }
336
+ }), y = x.useUtils(), b = {
337
+ canCreate: !0,
338
+ canUpdate: !0,
339
+ canDelete: !0,
340
+ canManageAccess: !0
341
+ }, { data: E = [], isLoading: D, isError: A, error: j } = x.network.securityGroup.list.useQuery({
342
+ project_id: r || "",
343
+ sort_key: p.sortBy,
344
+ sort_dir: p.sortDirection,
345
+ ...w(m),
346
+ ...f ? { searchTerm: f } : {}
347
+ }, { enabled: !!r }), I = x.network.securityGroup.create.useMutation({
348
+ onSuccess: (e) => {
349
+ y.network.securityGroup.list.invalidate(), l(null), n({
350
+ to: "/projects/$projectId/network/securitygroups/$securityGroupId",
351
+ params: {
352
+ projectId: r,
353
+ securityGroupId: e.id
354
+ }
355
+ });
356
+ },
357
+ onError: (t) => {
358
+ l(t.message || e._({ id: "o6M6l0" }));
359
+ }
360
+ }), L = x.network.securityGroup.deleteById.useMutation({
361
+ onSuccess: () => {
362
+ y.network.securityGroup.list.invalidate(), s(null);
363
+ },
364
+ onError: (t) => {
365
+ s(t.message || e._({ id: "d0pLfy" }));
366
+ }
367
+ }), z = x.network.securityGroup.update.useMutation({
368
+ onSuccess: () => {
369
+ y.network.securityGroup.list.invalidate(), d(null);
370
+ },
371
+ onError: (t) => {
372
+ d(t.message || e._({ id: "jPxavx" }));
373
+ }
374
+ });
375
+ return /* @__PURE__ */ k("div", {
376
+ className: "relative",
377
+ children: [
378
+ /* @__PURE__ */ O(S, {
379
+ sortSettings: p,
380
+ filterSettings: m,
381
+ searchTerm: f,
382
+ onSort: _,
383
+ onFilter: v,
384
+ onSearch: g,
385
+ actions: b.canCreate && /* @__PURE__ */ O(h, {
386
+ onClick: () => a(!0),
387
+ variant: "primary",
388
+ children: /* @__PURE__ */ O(P, { id: "YjAOtb" })
389
+ })
390
+ }),
391
+ /* @__PURE__ */ O(R, {
392
+ securityGroups: E,
393
+ isLoading: D,
394
+ isError: A,
395
+ error: j,
396
+ permissions: b,
397
+ onCreateClick: () => a(!0),
398
+ onDeleteSecurityGroup: (e) => {
399
+ s(null), L.mutate({
400
+ project_id: r,
401
+ securityGroupId: e
402
+ });
403
+ },
404
+ isDeletingSecurityGroup: L.isPending,
405
+ deleteError: o,
406
+ onUpdateSecurityGroup: async (e, t) => {
407
+ d(null), await z.mutateAsync({
408
+ project_id: r,
409
+ securityGroupId: e,
410
+ ...t
411
+ });
412
+ },
413
+ isUpdatingSecurityGroup: z.isPending,
414
+ updateError: u,
415
+ currentProjectId: r
416
+ }),
417
+ /* @__PURE__ */ O(B, {
418
+ isOpen: i,
419
+ onClose: () => a(!1),
420
+ onCreate: async (e) => {
421
+ l(null), await I.mutateAsync({
422
+ project_id: r,
423
+ ...e
424
+ });
425
+ },
426
+ isLoading: I.isPending,
427
+ error: c
428
+ })
429
+ ]
430
+ });
431
+ };
432
+ //#endregion
433
+ //#region src/client/routes/_auth/projects/$projectId/network/securitygroups/index.tsx?tsr-split=component
434
+ function U() {
435
+ let { i18n: e, _: t } = F();
436
+ return /* @__PURE__ */ k(D, { children: [/* @__PURE__ */ O(o, { children: e._({ id: "4opp4r" }) }), /* @__PURE__ */ O(H, {})] });
437
+ }
438
+ //#endregion
439
+ export { U as component };
440
+
441
+ //# sourceMappingURL=securitygroups-B4MkSBtI.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"securitygroups-B4MkSBtI.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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"}
@@ -29,4 +29,4 @@ function n({ defaultSortKey: n, defaultSortDir: r, sortOptions: i, filterSetting
29
29
  //#endregion
30
30
  export { n as t };
31
31
 
32
- //# sourceMappingURL=useListWithFiltering-mMX_EfyI.mjs.map
32
+ //# sourceMappingURL=useListWithFiltering-CEDh1LO-.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useListWithFiltering-mMX_EfyI.mjs","names":["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/utils/useListWithFiltering.ts"],"sourcesContent":["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":";;AAyFA,SAAgBE,EAAuC,EACrDC,mBACAC,mBACAC,gBACAC,gBAAgBC,KACe;CAE/B,IAAM,CAACC,GAAYC,KAAiBR,EAAS,GAAA,EACvCS,KAAsBC,MAAAA;EAC1B,IAAMC,IAAc,OAAOD,KAAS,WAAWA,IAAO;AACtDX,UAAsBS,EAAcG,EAAAA,CAAAA;IAIhC,CAACC,GAAcC,KAAmBb,EAA4B;EAClEc,SAASV;EACTW,QAAQb;EACRc,eAAeb;EACjB,CAAA,EACMc,KAAoBC,MAAAA;AAMxBL,IALoC;GAClCC,SAASI,EAAgBJ,WAAWF,EAAaE;GACjDC,QAAQ,EAAiBA,QAAQK,UAAAA,IAAoBlB;GACrDc,eAAe,EAAiBA,iBAAmCb;GACrE,CACgBgB;IAIZ,CAACd,GAAgBgB,KAAqBrB,EAAyBM,EAAAA;AAKrE,QAAO;EACLC;EACAK;EACAP;EACAI;EACAQ;EACAK,qBAV0BC,MAAAA;AAC1BxB,WAAsBsB,EAAkBE,EAAAA,CAAAA;;EAU1C"}
1
+ {"version":3,"file":"useListWithFiltering-CEDh1LO-.mjs","names":["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/utils/useListWithFiltering.ts"],"sourcesContent":["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":";;AAyFA,SAAgBE,EAAuC,EACrDC,mBACAC,mBACAC,gBACAC,gBAAgBC,KACe;CAE/B,IAAM,CAACC,GAAYC,KAAiBR,EAAS,GAAA,EACvCS,KAAsBC,MAAAA;EAC1B,IAAMC,IAAc,OAAOD,KAAS,WAAWA,IAAO;AACtDX,UAAsBS,EAAcG,EAAAA,CAAAA;IAIhC,CAACC,GAAcC,KAAmBb,EAA4B;EAClEc,SAASV;EACTW,QAAQb;EACRc,eAAeb;EACjB,CAAA,EACMc,KAAoBC,MAAAA;AAMxBL,IALoC;GAClCC,SAASI,EAAgBJ,WAAWF,EAAaE;GACjDC,QAAQ,EAAiBA,QAAQK,UAAAA,IAAoBlB;GACrDc,eAAe,EAAiBA,iBAAmCb;GACrE,CACgBgB;IAIZ,CAACd,GAAgBgB,KAAqBrB,EAAyBM,EAAAA;AAKrE,QAAO;EACLC;EACAK;EACAP;EACAI;EACAQ;EACAK,qBAV0BC,MAAAA;AAC1BxB,WAAsBsB,EAAkBE,EAAAA,CAAAA;;EAU1C"}
@@ -9,4 +9,4 @@ var n = (n = !1) => {
9
9
  //#endregion
10
10
  export { n as t };
11
11
 
12
- //# sourceMappingURL=useModal-Dg4CBeqL.mjs.map
12
+ //# sourceMappingURL=useModal-DxxlilRm.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useModal-Dg4CBeqL.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-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"}
@@ -8,4 +8,4 @@ function t() {
8
8
  //#endregion
9
9
  export { t };
10
10
 
11
- //# sourceMappingURL=useProjectId-BWaeJZOy.mjs.map
11
+ //# sourceMappingURL=useProjectId-CgOTejka.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useProjectId-BWaeJZOy.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-CgOTejka.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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cobaltcore-dev/aurora",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "private": false,
5
5
  "description": "Aurora OpenStack dashboard — server and client library",
6
6
  "license": "Apache-2.0",
@@ -13,7 +13,8 @@
13
13
  "node": ">=18.0.0"
14
14
  },
15
15
  "files": [
16
- "dist"
16
+ "dist",
17
+ "permission_policies"
17
18
  ],
18
19
  "publishConfig": {
19
20
  "access": "public"
@@ -67,12 +68,10 @@
67
68
  "react-error-boundary": "^6.1.0",
68
69
  "react-icons": "^5.6.0",
69
70
  "tailwind-merge": "^3.2.0",
70
- "zod": "^4.0.0",
71
- "@cobaltcore-dev/signal-openstack": "1.0.0",
72
- "@cobaltcore-dev/policy-engine": "2.0.0"
71
+ "zod": "^4.0.0"
73
72
  },
74
73
  "devDependencies": {
75
- "@cloudoperators/juno-ui-components": "6.3.1",
74
+ "@cloudoperators/juno-ui-components": "6.5.0",
76
75
  "@fastify/vite": "^8.4.1",
77
76
  "@lingui/cli": "^5.9.5",
78
77
  "@lingui/format-po": "^5.9.5",
@@ -110,7 +109,9 @@
110
109
  "vite-plugin-svgr": "^5.0.0",
111
110
  "vite-tsconfig-paths": "^6.1.1",
112
111
  "vitest": "^4.1.2",
113
- "@cobaltcore-dev/aurora-config": "0.0.1"
112
+ "@cobaltcore-dev/policy-engine": "2.0.0",
113
+ "@cobaltcore-dev/aurora-config": "0.0.1",
114
+ "@cobaltcore-dev/signal-openstack": "1.0.0"
114
115
  },
115
116
  "scripts": {
116
117
  "build": "pnpm build:server && pnpm build:client",