@cobaltcore-dev/aurora 0.2.2 → 0.3.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 (105) hide show
  1. package/dist/client/AuroraApp.d.ts +10 -0
  2. package/dist/client/{ContentHeader-DtBiIwRY.mjs → ContentHeader-B_PWrxbw.mjs} +17 -17
  3. package/dist/client/{ContentHeader-DtBiIwRY.mjs.map → ContentHeader-B_PWrxbw.mjs.map} +1 -1
  4. package/dist/client/{DeleteFlavorModal-rmuYIafD.mjs → DeleteFlavorModal-BkPrQKyE.mjs} +146 -146
  5. package/dist/client/{DeleteFlavorModal-rmuYIafD.mjs.map → DeleteFlavorModal-BkPrQKyE.mjs.map} +1 -1
  6. package/dist/client/{EditSecurityGroupModal-B7Sz9puM.mjs → EditSecurityGroupModal-ad4uzlt0.mjs} +14 -14
  7. package/dist/client/{EditSecurityGroupModal-B7Sz9puM.mjs.map → EditSecurityGroupModal-ad4uzlt0.mjs.map} +1 -1
  8. package/dist/client/{FloatingIpActionModals-CfRJiZqD.mjs → FloatingIpActionModals-lKMwLuL8.mjs} +51 -51
  9. package/dist/client/{FloatingIpActionModals-CfRJiZqD.mjs.map → FloatingIpActionModals-lKMwLuL8.mjs.map} +1 -1
  10. package/dist/client/{ImageToastNotifications-Cw30RXsw.mjs → ImageToastNotifications--U13YiQ_.mjs} +328 -328
  11. package/dist/client/{ImageToastNotifications-Cw30RXsw.mjs.map → ImageToastNotifications--U13YiQ_.mjs.map} +1 -1
  12. package/dist/client/{ListToolbar-DuazvsAu.mjs → ListToolbar-C5lzTrit.mjs} +56 -56
  13. package/dist/client/{ListToolbar-DuazvsAu.mjs.map → ListToolbar-C5lzTrit.mjs.map} +1 -1
  14. package/dist/client/{RouteError-Cyto623-.mjs → RouteError-iP1Vd6bY.mjs} +2 -2
  15. package/dist/client/{RouteError-Cyto623-.mjs.map → RouteError-iP1Vd6bY.mjs.map} +1 -1
  16. package/dist/client/{_flavorId-B-1fYadl.mjs → _flavorId-BaNXUJhA.mjs} +49 -49
  17. package/dist/client/_flavorId-BaNXUJhA.mjs.map +1 -0
  18. package/dist/client/{_flavorId-BYfIHIV_.mjs → _flavorId-CnO76tuy.mjs} +10 -10
  19. package/dist/client/_flavorId-CnO76tuy.mjs.map +1 -0
  20. package/dist/client/{_floatingIpId-IrnN-ozB.mjs → _floatingIpId-C8G20nNt.mjs} +2 -2
  21. package/dist/client/{_floatingIpId-IrnN-ozB.mjs.map → _floatingIpId-C8G20nNt.mjs.map} +1 -1
  22. package/dist/client/{_floatingIpId-FQ5P2qMV.mjs → _floatingIpId-DdKnjdIV.mjs} +36 -36
  23. package/dist/client/{_floatingIpId-FQ5P2qMV.mjs.map → _floatingIpId-DdKnjdIV.mjs.map} +1 -1
  24. package/dist/client/{_imageId-Tx_9bqEc.mjs → _imageId-DdSbxFqG.mjs} +135 -135
  25. package/dist/client/_imageId-DdSbxFqG.mjs.map +1 -0
  26. package/dist/client/{_pcaId-CFuKY82d.mjs → _pcaId-DFkYJEb5.mjs} +136 -136
  27. package/dist/client/{_pcaId-CFuKY82d.mjs.map → _pcaId-DFkYJEb5.mjs.map} +1 -1
  28. package/dist/client/{_pcaId-Bck7S7gJ.mjs → _pcaId-DX_S-3hE.mjs} +2 -2
  29. package/dist/client/{_pcaId-Bck7S7gJ.mjs.map → _pcaId-DX_S-3hE.mjs.map} +1 -1
  30. package/dist/client/{_projectId-PSpuCKO7.mjs → _projectId-DYrcZ3E3.mjs} +9 -9
  31. package/dist/client/{_projectId-PSpuCKO7.mjs.map → _projectId-DYrcZ3E3.mjs.map} +1 -1
  32. package/dist/client/{_projectId-B1VjDd0Z.mjs → _projectId-Dha4XqX4.mjs} +3 -3
  33. package/dist/client/{_projectId-B1VjDd0Z.mjs.map → _projectId-Dha4XqX4.mjs.map} +1 -1
  34. package/dist/client/_projectId-DsSVV2EA.mjs +316 -0
  35. package/dist/client/_projectId-DsSVV2EA.mjs.map +1 -0
  36. package/dist/client/{_projectId-Pxp-RXK4.mjs → _projectId-KH5si25Q.mjs} +2 -2
  37. package/dist/client/{_projectId-Pxp-RXK4.mjs.map → _projectId-KH5si25Q.mjs.map} +1 -1
  38. package/dist/client/{_securityGroupId-VV2lUcGQ.mjs → _securityGroupId-DgaSqYex.mjs} +2 -2
  39. package/dist/client/{_securityGroupId-VV2lUcGQ.mjs.map → _securityGroupId-DgaSqYex.mjs.map} +1 -1
  40. package/dist/client/{_securityGroupId-Dqi6ddw4.mjs → _securityGroupId-O7FXfx0M.mjs} +306 -306
  41. package/dist/client/{_securityGroupId-Dqi6ddw4.mjs.map → _securityGroupId-O7FXfx0M.mjs.map} +1 -1
  42. package/dist/client/{about-B2AzqxFI.mjs → about-DN8n8sN8.mjs} +8 -8
  43. package/dist/client/{about-B2AzqxFI.mjs.map → about-DN8n8sN8.mjs.map} +1 -1
  44. package/dist/client/{build-Cf7iWbpH.mjs → build-CZRvXrAL.mjs} +1047 -1042
  45. package/dist/client/{build-Cf7iWbpH.mjs.map → build-CZRvXrAL.mjs.map} +1 -1
  46. package/dist/client/{constants-CCgR6fKI.mjs → constants-akdIBeTX.mjs} +6 -6
  47. package/dist/client/{constants-CCgR6fKI.mjs.map → constants-akdIBeTX.mjs.map} +1 -1
  48. package/dist/client/{containers-BWERuY0O.mjs → containers-BE2QiLBs.mjs} +748 -748
  49. package/dist/client/{containers-BWERuY0O.mjs.map → containers-BE2QiLBs.mjs.map} +1 -1
  50. package/dist/client/containers-Cs5vOeR2.mjs.map +1 -1
  51. package/dist/client/{containers-DovytjVP.mjs → containers-DmwhE9Uz.mjs} +6 -6
  52. package/dist/client/containers-DmwhE9Uz.mjs.map +1 -0
  53. package/dist/client/flavors-BXPYAFyQ.mjs.map +1 -1
  54. package/dist/client/{flavors-CUiALHcB.mjs → flavors-CY7A6--v.mjs} +2 -2
  55. package/dist/client/{flavors-CUiALHcB.mjs.map → flavors-CY7A6--v.mjs.map} +1 -1
  56. package/dist/client/{flavors-Bovz-I2U.mjs → flavors-pEcGkCut.mjs} +150 -150
  57. package/dist/client/{flavors-Bovz-I2U.mjs.map → flavors-pEcGkCut.mjs.map} +1 -1
  58. package/dist/client/{floatingips-BrjDiY2t.mjs → floatingips-BtL4d4m-.mjs} +118 -118
  59. package/dist/client/{floatingips-BrjDiY2t.mjs.map → floatingips-BtL4d4m-.mjs.map} +1 -1
  60. package/dist/client/{images-DaaCUXMI.mjs → images-C19gCFSy.mjs} +389 -389
  61. package/dist/client/{images-DaaCUXMI.mjs.map → images-C19gCFSy.mjs.map} +1 -1
  62. package/dist/client/images-DM9I8G0p.mjs.map +1 -1
  63. package/dist/client/{images-BZP3pBqj.mjs → images-I9gQfRa7.mjs} +2 -2
  64. package/dist/client/{images-BZP3pBqj.mjs.map → images-I9gQfRa7.mjs.map} +1 -1
  65. package/dist/client/index.d.ts +2 -1
  66. package/dist/client/index.js +416 -418
  67. package/dist/client/index.js.map +1 -1
  68. package/dist/client/objects-B4yrYf_a.mjs.map +1 -1
  69. package/dist/client/{objects-D4zBka5e.mjs → objects-BrYe_RaJ.mjs} +6 -6
  70. package/dist/client/objects-BrYe_RaJ.mjs.map +1 -0
  71. package/dist/client/{objects-Cw4Vu01M.mjs → objects-DOYFFn3Y.mjs} +1013 -1013
  72. package/dist/client/{objects-Cw4Vu01M.mjs.map → objects-DOYFFn3Y.mjs.map} +1 -1
  73. package/dist/client/{overview-2J54-loz.mjs → overview-BjRSFSBh.mjs} +2 -2
  74. package/dist/client/{overview-2J54-loz.mjs.map → overview-BjRSFSBh.mjs.map} +1 -1
  75. package/dist/client/{overview-BnmukbFh.mjs → overview-C4gjtc2q.mjs} +7 -7
  76. package/dist/client/{overview-BnmukbFh.mjs.map → overview-C4gjtc2q.mjs.map} +1 -1
  77. package/dist/client/{overview-D0AAvsmL.mjs → overview-CmQkJ4Hh.mjs} +2 -2
  78. package/dist/client/{overview-D0AAvsmL.mjs.map → overview-CmQkJ4Hh.mjs.map} +1 -1
  79. package/dist/client/{overview-BtIXpYBo.mjs → overview-DTLIAKkJ.mjs} +2 -2
  80. package/dist/client/{overview-BtIXpYBo.mjs.map → overview-DTLIAKkJ.mjs.map} +1 -1
  81. package/dist/client/{pca-V2aaOxZA.mjs → pca-CYFJxSZ2.mjs} +70 -70
  82. package/dist/client/{pca-V2aaOxZA.mjs.map → pca-CYFJxSZ2.mjs.map} +1 -1
  83. package/dist/client/{pca-BqZycwCu.mjs → pca-DzixU9Dl.mjs} +2 -2
  84. package/dist/client/{pca-BqZycwCu.mjs.map → pca-DzixU9Dl.mjs.map} +1 -1
  85. package/dist/client/projects-BsN4bvU2.mjs.map +1 -1
  86. package/dist/client/{projects-0feOw_b6.mjs → projects-BtyjXGq2.mjs} +2 -2
  87. package/dist/client/projects-BtyjXGq2.mjs.map +1 -0
  88. package/dist/client/projects-D3hOC1cy.mjs +95 -0
  89. package/dist/client/projects-D3hOC1cy.mjs.map +1 -0
  90. package/dist/client/{projects-jyIHL6DE.mjs → projects-DwVawmll.mjs} +2 -2
  91. package/dist/client/projects-DwVawmll.mjs.map +1 -0
  92. package/dist/client/{securitygroups-B4MkSBtI.mjs → securitygroups-DMCIDHQS.mjs} +114 -114
  93. package/dist/client/{securitygroups-B4MkSBtI.mjs.map → securitygroups-DMCIDHQS.mjs.map} +1 -1
  94. package/package.json +3 -3
  95. package/dist/client/_flavorId-B-1fYadl.mjs.map +0 -1
  96. package/dist/client/_flavorId-BYfIHIV_.mjs.map +0 -1
  97. package/dist/client/_imageId-Tx_9bqEc.mjs.map +0 -1
  98. package/dist/client/_projectId-Bs4W9hos.mjs +0 -283
  99. package/dist/client/_projectId-Bs4W9hos.mjs.map +0 -1
  100. package/dist/client/containers-DovytjVP.mjs.map +0 -1
  101. package/dist/client/objects-D4zBka5e.mjs.map +0 -1
  102. package/dist/client/projects-0feOw_b6.mjs.map +0 -1
  103. package/dist/client/projects-C1IYOvFQ.mjs +0 -144
  104. package/dist/client/projects-C1IYOvFQ.mjs.map +0 -1
  105. package/dist/client/projects-jyIHL6DE.mjs.map +0 -1
@@ -0,0 +1,316 @@
1
+ import { B as e, E as t, G as n, O as r, R as i, Y as a, g as o, tt as s } from "./build-CZRvXrAL.mjs";
2
+ import { t as c } from "./_projectId-Dha4XqX4.mjs";
3
+ import { t as l } from "./helpers--JWXi40U.mjs";
4
+ import { Fragment as u, jsx as d, jsxs as f } from "react/jsx-runtime";
5
+ import { useEffect as p, useMemo as m, useRef as h, useState as g } from "react";
6
+ import { createPortal as _ } from "react-dom";
7
+ import { Outlet as v, useLoaderData as y, useMatches as b, useNavigate as x, useParams as S, useRouteContext as C } from "@tanstack/react-router";
8
+ import { useLingui as w } from "@lingui/react";
9
+ import { z as T } from "zod";
10
+ var E = T.object({
11
+ labelKey: T.enum([
12
+ "Compute",
13
+ "Network",
14
+ "Storage",
15
+ "Services",
16
+ "Images",
17
+ "Flavors",
18
+ "Security Groups",
19
+ "Floating IPs",
20
+ "PCA (Clavis)"
21
+ ]).optional(),
22
+ to: T.string().optional(),
23
+ useParamAsLabel: T.string().optional()
24
+ }), D = T.object({
25
+ section: T.string(),
26
+ service: T.string().optional(),
27
+ isDetail: T.boolean().optional(),
28
+ crumb: E.optional(),
29
+ sectionCrumb: E.optional()
30
+ });
31
+ function O(e) {
32
+ return D.safeParse(e).success;
33
+ }
34
+ //#endregion
35
+ //#region src/client/components/Slot.tsx
36
+ function k({ children: e }) {
37
+ let t = h(null), [n, r] = g(null);
38
+ return p(() => {
39
+ t.current && r(t.current.shadowRoot ?? t.current.attachShadow({ mode: "open" }));
40
+ }, []), /* @__PURE__ */ d("div", {
41
+ ref: t,
42
+ style: { display: "contents" },
43
+ children: n && /* @__PURE__ */ _(e, n)
44
+ });
45
+ }
46
+ function A({ component: e }) {
47
+ let { trpcClient: t } = C({ strict: !1 });
48
+ return t ? /* @__PURE__ */ d(k, { children: /* @__PURE__ */ d(e, { auroraContext: { client: t } }) }) : null;
49
+ }
50
+ //#endregion
51
+ //#region src/client/routes/_auth/projects/-components/SideNavBar.tsx
52
+ var j = ({ projectId: t, projectName: i, availableServices: a }) => {
53
+ let { i18n: o, _: s } = w(), c = x(), m = b(), { provider: h } = S({ strict: !1 }), { slots: _ } = C({ strict: !1 }), v = [...m].reverse().find((e) => O(e.staticData)), y = v && O(v.staticData) ? v.staticData : void 0, T = y?.section ?? null, E = y?.service ?? null, D = l(a), [k, j] = g({
54
+ compute: !0,
55
+ network: !0,
56
+ storage: !0,
57
+ services: !0
58
+ }), M = (e) => j((t) => ({
59
+ ...t,
60
+ [e]: !t[e]
61
+ })), [N, P] = g({
62
+ compute: 0,
63
+ network: 0,
64
+ storage: 0,
65
+ services: 0
66
+ });
67
+ p(() => {
68
+ T && T in N && (P((e) => ({
69
+ ...e,
70
+ [T]: e[T] + 1
71
+ })), j((e) => ({
72
+ ...e,
73
+ [T]: !0
74
+ })));
75
+ }, [T]);
76
+ let F = T === null, I = (e) => (t) => {
77
+ let n = t.target.closest(".juno-sidenavigation-item");
78
+ n && n.parentElement?.querySelector(".expand-icon") && M(e);
79
+ }, L = [...D.image?.glance ? [{
80
+ service: "images",
81
+ label: o._({ id: "an5hVd" }),
82
+ to: "/projects/$projectId/compute/images",
83
+ params: { projectId: t }
84
+ }] : [], ...D?.compute?.nova ? [{
85
+ service: "flavors",
86
+ label: o._({ id: "neiJm0" }),
87
+ to: "/projects/$projectId/compute/flavors",
88
+ params: { projectId: t }
89
+ }] : []], R = [...D.network ? [{
90
+ service: "securitygroups",
91
+ label: o._({ id: "4opp4r" }),
92
+ to: "/projects/$projectId/network/securitygroups",
93
+ params: { projectId: t }
94
+ }, {
95
+ service: "floatingips",
96
+ label: o._({ id: "u77/s4" }),
97
+ to: "/projects/$projectId/network/floatingips",
98
+ params: { projectId: t }
99
+ }] : []], z = [...D?.["object-store"]?.swift ? [{
100
+ service: "containers",
101
+ label: o._({ id: "+OEi73" }),
102
+ to: "/projects/$projectId/storage/$provider/containers",
103
+ params: {
104
+ projectId: t,
105
+ provider: "swift"
106
+ }
107
+ }] : [], {
108
+ service: "ceph-containers",
109
+ label: o._({ id: "KhNDX4" }),
110
+ to: "/projects/$projectId/storage/$provider/containers",
111
+ params: {
112
+ projectId: t,
113
+ provider: "ceph"
114
+ }
115
+ }], B = [...D.pca?.["clavis-beta"] || D.pca?.["clavis-dev"] ? [{
116
+ service: "pca",
117
+ label: o._({ id: "miy5mb" }),
118
+ to: "/projects/$projectId/services/pca",
119
+ params: { projectId: t }
120
+ }] : []];
121
+ return /* @__PURE__ */ d(n, {
122
+ ariaLabel: "Project Side Navigation",
123
+ children: /* @__PURE__ */ f(u, { children: [/* @__PURE__ */ d(r, { children: /* @__PURE__ */ f(u, { children: [
124
+ /* @__PURE__ */ d(e, {
125
+ icon: "home",
126
+ label: i,
127
+ onClick: () => c({
128
+ to: "/projects/$projectId",
129
+ params: { projectId: t }
130
+ }),
131
+ selected: F
132
+ }),
133
+ /* @__PURE__ */ d("div", {
134
+ onClickCapture: I("compute"),
135
+ children: /* @__PURE__ */ d(e, {
136
+ label: o._({ id: "rp0Bd0" }),
137
+ open: k.compute,
138
+ children: L.map(({ service: t, label: n, to: r, params: i }) => /* @__PURE__ */ d(e, {
139
+ onClick: () => c({
140
+ to: r,
141
+ params: i
142
+ }),
143
+ label: n,
144
+ selected: T === "compute" && E === t
145
+ }, n))
146
+ }, N.compute)
147
+ }),
148
+ R.length > 0 && /* @__PURE__ */ d("div", {
149
+ onClickCapture: I("network"),
150
+ children: /* @__PURE__ */ d(e, {
151
+ label: o._({ id: "OR475H" }),
152
+ open: k.network,
153
+ children: R.map(({ service: t, label: n, to: r, params: i }) => /* @__PURE__ */ d(e, {
154
+ onClick: () => c({
155
+ to: r,
156
+ params: i
157
+ }),
158
+ label: n,
159
+ selected: T === "network" && E === t
160
+ }, n))
161
+ }, N.network)
162
+ }),
163
+ z.length > 0 && /* @__PURE__ */ d("div", {
164
+ onClickCapture: I("storage"),
165
+ children: /* @__PURE__ */ d(e, {
166
+ label: o._({ id: "BrrIs8" }),
167
+ open: k.storage,
168
+ children: z.map(({ service: t, label: n, to: r, params: i }) => /* @__PURE__ */ d(e, {
169
+ onClick: () => c({
170
+ to: r,
171
+ params: i
172
+ }),
173
+ label: n,
174
+ selected: T === "storage" && E === "containers" ? i.provider === h : E === t
175
+ }, n))
176
+ }, N.storage)
177
+ }),
178
+ B.length > 0 && /* @__PURE__ */ d("div", {
179
+ onClickCapture: I("services"),
180
+ children: /* @__PURE__ */ d(e, {
181
+ label: o._({ id: "MILoeL" }),
182
+ open: k.services,
183
+ children: B.map(({ service: t, label: n, to: r, params: i }) => /* @__PURE__ */ d(e, {
184
+ onClick: () => c({
185
+ to: r,
186
+ params: i
187
+ }),
188
+ label: n,
189
+ selected: T === "services" && E === t
190
+ }, n))
191
+ }, N.services)
192
+ })
193
+ ] }) }), _?.sideNavBanner && /* @__PURE__ */ d(A, { component: _.sideNavBanner })] })
194
+ });
195
+ };
196
+ //#endregion
197
+ //#region src/client/components/ProjectView/ProjectInfoBox.tsx
198
+ function M({ projectInfo: e }) {
199
+ let { i18n: n, _: r } = w(), i = x(), o = b(), { projectId: s } = S({ strict: !1 });
200
+ return /* @__PURE__ */ d(a, {
201
+ className: "mt-8 mb-4",
202
+ children: m(() => {
203
+ let t = {
204
+ Compute: n._({ id: "rp0Bd0" }),
205
+ Network: n._({ id: "OR475H" }),
206
+ Storage: n._({ id: "BrrIs8" }),
207
+ Services: n._({ id: "MILoeL" }),
208
+ Images: n._({ id: "an5hVd" }),
209
+ Flavors: n._({ id: "neiJm0" }),
210
+ "Security Groups": n._({ id: "4opp4r" }),
211
+ "Floating IPs": n._({ id: "u77/s4" }),
212
+ "PCA (Clavis)": n._({ id: "miy5mb" })
213
+ }, r = (e) => e === "swift" ? n._({ id: "+OEi73" }) : e === "ceph" ? n._({ id: "KhNDX4" }) : n._({ id: "BrrIs8" }), a = [];
214
+ a.push({
215
+ icon: "home",
216
+ label: n._({ id: "i0qMbr" }),
217
+ onClick: () => i({ to: "/projects" })
218
+ }), e.domain?.name && a.push({ label: e.domain.name }), a.push({
219
+ label: e.name,
220
+ onClick: () => i({
221
+ to: "/projects/$projectId",
222
+ params: { projectId: s }
223
+ })
224
+ });
225
+ let c = o.filter((e) => e.routeId !== "/_auth/projects/$projectId" && e.routeId.startsWith("/_auth/projects/$projectId")), l = c[c.length - 1];
226
+ if (!l) return a;
227
+ let u = O(l.staticData) ? l.staticData : void 0;
228
+ if (!u) return a;
229
+ let d = l.params;
230
+ if (u.sectionCrumb) {
231
+ let { labelKey: e, to: n } = u.sectionCrumb, r = e ? t[e] : void 0, o = !u.crumb;
232
+ a.push(n ? {
233
+ label: r,
234
+ onClick: () => i({
235
+ to: n,
236
+ params: d
237
+ })
238
+ } : {
239
+ label: r,
240
+ active: o
241
+ });
242
+ }
243
+ if (u.crumb) {
244
+ let { labelKey: e, to: n, useParamAsLabel: o } = u.crumb, s = o ? r(d[o]) : e ? t[e] : void 0;
245
+ if (u.isDetail) {
246
+ a.push({
247
+ label: s,
248
+ onClick: () => i({
249
+ to: n,
250
+ params: d
251
+ })
252
+ });
253
+ let e = l.meta?.find((e) => e != null && "title" in e)?.title;
254
+ e && a.push({
255
+ label: e,
256
+ active: !0
257
+ });
258
+ } else a.push(n ? {
259
+ label: s,
260
+ onClick: () => i({
261
+ to: n,
262
+ params: d
263
+ })
264
+ } : {
265
+ label: s,
266
+ active: !0
267
+ });
268
+ }
269
+ return a;
270
+ }, [
271
+ o,
272
+ e,
273
+ s,
274
+ i,
275
+ r
276
+ ]).map((e, n) => /* @__PURE__ */ d(t, {
277
+ label: e.label,
278
+ icon: e.icon,
279
+ onClick: e.onClick,
280
+ active: e.active
281
+ }, n))
282
+ });
283
+ }
284
+ //#endregion
285
+ //#region src/client/routes/_auth/projects/$projectId.tsx?tsr-split=component
286
+ function N() {
287
+ let { availableServices: e, projectId: t, crumbProject: n } = y({ from: c.id });
288
+ return /* @__PURE__ */ d(o, {
289
+ embedded: !0,
290
+ sideNavigation: /* @__PURE__ */ d(j, {
291
+ availableServices: e,
292
+ projectId: t,
293
+ projectName: n?.name || t
294
+ }),
295
+ className: "h-min-screen",
296
+ children: /* @__PURE__ */ d(i, { children: /* @__PURE__ */ d(s, {
297
+ direction: "vertical",
298
+ distribution: "start",
299
+ alignment: "stretch",
300
+ className: "xl:flex-row",
301
+ gap: "6",
302
+ children: /* @__PURE__ */ f("div", {
303
+ className: "min-w-0 flex-1",
304
+ children: [/* @__PURE__ */ d(M, { projectInfo: {
305
+ id: t,
306
+ name: n?.name || t,
307
+ domain: n?.domain
308
+ } }), /* @__PURE__ */ d(v, {})]
309
+ })
310
+ }) })
311
+ });
312
+ }
313
+ //#endregion
314
+ export { N as component };
315
+
316
+ //# sourceMappingURL=_projectId-DsSVV2EA.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_projectId-DsSVV2EA.mjs","names":["z","CRUMB_LABEL_KEYS","CrumbSchema","object","labelKey","enum","optional","to","string","useParamAsLabel","RouteInfoSchema","section","service","isDetail","boolean","crumb","sectionCrumb","isRouteInfo","data","safeParse","success","useRef","useState","useEffect","createPortal","useRouteContext","SlotShadowRoot","children","ref","root","setRoot","current","shadowRoot","attachShadow","mode","div","style","display","Slot","component","Component","trpcClient","strict","auroraContext","client","useNavigate","useMatches","useParams","useRouteContext","useState","useEffect","getServiceIndex","SideNavigation","SideNavigationList","SideNavigationItem","isRouteInfo","Slot","SideNavBar","projectId","projectName","availableServices","useLingui","navigate","matches","provider","strict","slots","activeMatch","reverse","find","m","staticData","activeRouteInfo","undefined","activeSection","section","activeService","service","serviceIndex","openSections","setOpenSections","compute","network","storage","services","toggle","prev","sectionKeys","setSectionKeys","isOverviewActive","handleSectionClick","e","clickedItem","target","closest","parentElement","querySelector","computeServices","label","t","to","params","networkServices","storageServices","pcaServices","clavisServices","ariaLabel","icon","onClick","selected","div","onClickCapture","open","map","length","isStorageContainers","isSelected","sideNavBanner","component","Breadcrumb","BreadcrumbItem","useMatches","useNavigate","useParams","useMemo","isRouteInfo","ProjectInfoBox","projectInfo","useLingui","navigate","matches","projectId","strict","breadcrumbs","crumbLabels","Compute","t","Network","Storage","Services","Images","Flavors","resolveProviderLabel","provider","items","push","icon","label","onClick","to","domain","name","params","projectMatches","filter","m","routeId","startsWith","deepest","length","info","staticData","undefined","sectionCrumb","labelKey","isLeaf","crumb","active","useParamAsLabel","resolvedLabel","isDetail","title","meta","find","className","map","item","index","Outlet","useLoaderData","AppShell","Container","Stack","SideNavBar","ProjectInfoBox","Route","RouteComponent","availableServices","projectId","crumbProject","from","id","name","domain","component"],"sources":["../../src/client/routes/routeInfo.ts","../../src/client/components/Slot.tsx","../../src/client/routes/_auth/projects/-components/SideNavBar.tsx","../../src/client/components/ProjectView/ProjectInfoBox.tsx","../../src/client/routes/_auth/projects/$projectId.tsx?tsr-split=component"],"sourcesContent":["import { z } from \"zod\"\n\nexport const CRUMB_LABEL_KEYS = [\n \"Compute\",\n \"Network\",\n \"Storage\",\n \"Services\",\n \"Images\",\n \"Flavors\",\n \"Security Groups\",\n \"Floating IPs\",\n \"PCA (Clavis)\",\n] as const\n\nexport type CrumbLabelKey = (typeof CRUMB_LABEL_KEYS)[number]\n\nconst CrumbSchema = z.object({\n labelKey: z.enum(CRUMB_LABEL_KEYS).optional(),\n to: z.string().optional(),\n useParamAsLabel: z.string().optional(),\n})\n\nconst RouteInfoSchema = z.object({\n section: z.string(),\n service: z.string().optional(),\n isDetail: z.boolean().optional(),\n crumb: CrumbSchema.optional(),\n sectionCrumb: CrumbSchema.optional(),\n})\n\nexport type Crumb = z.infer<typeof CrumbSchema>\nexport type RouteInfo = z.infer<typeof RouteInfoSchema>\n\nexport function isRouteInfo(data: unknown): data is RouteInfo {\n return RouteInfoSchema.safeParse(data).success\n}\n","import { useRef, useState, useEffect, type ReactNode, type FC } from \"react\"\nimport { createPortal } from \"react-dom\"\nimport { useRouteContext } from \"@tanstack/react-router\"\nimport type { SlotProps } from \"../AuroraApp\"\n\nfunction SlotShadowRoot({ children }: { children: ReactNode }) {\n const ref = useRef<HTMLDivElement>(null)\n const [root, setRoot] = useState<ShadowRoot | null>(null)\n\n useEffect(() => {\n if (!ref.current) return\n setRoot(ref.current.shadowRoot ?? ref.current.attachShadow({ mode: \"open\" }))\n }, [])\n\n return (\n <div ref={ref} style={{ display: \"contents\" }}>\n {root && createPortal(children, root)}\n </div>\n )\n}\n\nexport function Slot({ component: Component }: { component: FC<SlotProps> }) {\n const { trpcClient } = useRouteContext({ strict: false })\n\n if (!trpcClient) return null\n\n return (\n <SlotShadowRoot>\n <Component auroraContext={{ client: trpcClient }} />\n </SlotShadowRoot>\n )\n}\n","import { useNavigate, useMatches, useParams, useRouteContext } from \"@tanstack/react-router\"\nimport { type MouseEvent, useState, useEffect } from \"react\"\nimport { getServiceIndex } from \"@/server/Authentication/helpers\"\nimport { SideNavigation, SideNavigationList, SideNavigationItem } from \"@cloudoperators/juno-ui-components/index\"\nimport { useLingui } from \"@lingui/react/macro\"\nimport { isRouteInfo } from \"@/client/routes/routeInfo\"\nimport { Slot } from \"@/client/components/Slot\"\n\ninterface SideNavBarProps {\n projectId: string\n projectName: string\n availableServices: {\n type: string\n name: string\n }[]\n}\n\nexport const SideNavBar = ({ projectId, projectName, availableServices }: SideNavBarProps) => {\n const { t } = useLingui()\n const navigate = useNavigate()\n const matches = useMatches()\n const { provider } = useParams({ strict: false }) as { provider?: string }\n const { slots } = useRouteContext({ strict: false })\n\n // Read active section/service from the deepest match that has valid RouteInfo staticData\n const activeMatch = [...matches].reverse().find((m) => isRouteInfo(m.staticData))\n const activeRouteInfo = activeMatch && isRouteInfo(activeMatch.staticData) ? activeMatch.staticData : undefined\n const activeSection = activeRouteInfo?.section ?? null\n const activeService = activeRouteInfo?.service ?? null\n\n const serviceIndex = getServiceIndex(availableServices)\n\n const [openSections, setOpenSections] = useState({ compute: true, network: true, storage: true, services: true })\n const toggle = (section: keyof typeof openSections) =>\n setOpenSections((prev) => ({ ...prev, [section]: !prev[section] }))\n\n // When navigating into a section, force Juno to re-open it by resetting the key\n const [sectionKeys, setSectionKeys] = useState({ compute: 0, network: 0, storage: 0, services: 0 })\n\n useEffect(() => {\n if (activeSection && activeSection in sectionKeys) {\n setSectionKeys((prev) => ({ ...prev, [activeSection]: prev[activeSection as keyof typeof sectionKeys] + 1 }))\n setOpenSections((prev) => ({ ...prev, [activeSection]: true }))\n }\n }, [activeSection])\n\n const isOverviewActive = activeSection === null\n\n const handleSectionClick = (section: keyof typeof openSections) => (e: MouseEvent<HTMLDivElement>) => {\n // Only toggle if the click is on the header row (has expand-icon sibling), not on child items\n const clickedItem = (e.target as HTMLElement).closest(\".juno-sidenavigation-item\")\n if (clickedItem && clickedItem.parentElement?.querySelector(\".expand-icon\")) {\n toggle(section)\n }\n }\n\n const computeServices = [\n ...(serviceIndex[\"image\"]?.[\"glance\"]\n ? [\n {\n service: \"images\",\n label: t`Images`,\n to: \"/projects/$projectId/compute/images\" as const,\n params: { projectId },\n },\n ]\n : []),\n ...(serviceIndex?.[\"compute\"]?.[\"nova\"]\n ? [\n {\n service: \"flavors\",\n label: t`Flavors`,\n to: \"/projects/$projectId/compute/flavors\" as const,\n params: { projectId },\n },\n ]\n : []),\n ]\n\n const networkServices = [\n ...(serviceIndex[\"network\"]\n ? [\n {\n service: \"securitygroups\",\n label: t`Security Groups`,\n to: \"/projects/$projectId/network/securitygroups\" as const,\n params: { projectId },\n },\n {\n service: \"floatingips\",\n label: t`Floating IPs`,\n to: \"/projects/$projectId/network/floatingips\" as const,\n params: { projectId },\n },\n ]\n : []),\n ]\n\n const storageServices = [\n ...(serviceIndex?.[\"object-store\"]?.[\"swift\"]\n ? [\n {\n service: \"containers\",\n label: t`Object Storage (Swift)`,\n to: \"/projects/$projectId/storage/$provider/containers\" as const,\n params: { projectId, provider: \"swift\" },\n },\n ]\n : []),\n {\n service: \"ceph-containers\",\n label: t`Object Storage (Ceph)`,\n to: \"/projects/$projectId/storage/$provider/containers\" as const,\n params: { projectId, provider: \"ceph\" },\n },\n ]\n\n // temporary as clavis is not fully GA, after GA replace with [\"pca\"]?.[\"clavis\"]\n const pcaServices = serviceIndex[\"pca\"]?.[\"clavis-beta\"] || serviceIndex[\"pca\"]?.[\"clavis-dev\"]\n const clavisServices = [\n ...(pcaServices\n ? [\n {\n service: \"pca\",\n label: t`PCA (Clavis)`,\n to: \"/projects/$projectId/services/pca\" as const,\n params: { projectId },\n },\n ]\n : []),\n ]\n\n return (\n <SideNavigation ariaLabel=\"Project Side Navigation\">\n <>\n <SideNavigationList>\n <>\n <SideNavigationItem\n icon=\"home\"\n label={projectName}\n onClick={() => navigate({ to: \"/projects/$projectId\", params: { projectId } })}\n selected={isOverviewActive}\n />\n {/* onClickCapture fires before Juno's chevron stopPropagation, keeping our state in sync */}\n <div onClickCapture={handleSectionClick(\"compute\")}>\n <SideNavigationItem key={sectionKeys.compute} label={t`Compute`} open={openSections.compute}>\n {computeServices.map(({ service, label, to, params }) => (\n <SideNavigationItem\n key={label}\n onClick={() => navigate({ to, params })}\n label={label}\n selected={activeSection === \"compute\" && activeService === service}\n />\n ))}\n </SideNavigationItem>\n </div>\n\n {networkServices.length > 0 && (\n <div onClickCapture={handleSectionClick(\"network\")}>\n <SideNavigationItem key={sectionKeys.network} label={t`Network`} open={openSections.network}>\n {networkServices.map(({ service, label, to, params }) => (\n <SideNavigationItem\n key={label}\n onClick={() => navigate({ to, params })}\n label={label}\n selected={activeSection === \"network\" && activeService === service}\n />\n ))}\n </SideNavigationItem>\n </div>\n )}\n\n {storageServices.length > 0 && (\n <div onClickCapture={handleSectionClick(\"storage\")}>\n <SideNavigationItem key={sectionKeys.storage} label={t`Storage`} open={openSections.storage}>\n {storageServices.map(({ service, label, to, params }) => {\n // For storage services with provider param, match against current provider\n const isStorageContainers = activeSection === \"storage\" && activeService === \"containers\"\n const isSelected = isStorageContainers ? params.provider === provider : activeService === service\n\n return (\n <SideNavigationItem\n key={label}\n onClick={() => navigate({ to, params })}\n label={label}\n selected={isSelected}\n />\n )\n })}\n </SideNavigationItem>\n </div>\n )}\n\n {clavisServices.length > 0 && (\n <div onClickCapture={handleSectionClick(\"services\")}>\n <SideNavigationItem key={sectionKeys.services} label={t`Services`} open={openSections.services}>\n {clavisServices.map(({ service, label, to, params }) => (\n <SideNavigationItem\n key={label}\n onClick={() => navigate({ to, params })}\n label={label}\n selected={activeSection === \"services\" && activeService === service}\n />\n ))}\n </SideNavigationItem>\n </div>\n )}\n </>\n </SideNavigationList>\n {slots?.sideNavBanner && <Slot component={slots.sideNavBanner} />}\n </>\n </SideNavigation>\n )\n}\n","import { Breadcrumb, BreadcrumbItem, KnownIcons } from \"@cloudoperators/juno-ui-components\"\nimport { useMatches, useNavigate, useParams } from \"@tanstack/react-router\"\nimport { useMemo } from \"react\"\nimport { useLingui } from \"@lingui/react/macro\"\nimport { isRouteInfo, CrumbLabelKey } from \"@/client/routes/routeInfo\"\n\ninterface ProjectInfoBoxProps {\n projectInfo: {\n id: string\n name: string\n description?: string\n domain?: {\n name?: string\n }\n }\n}\n\nexport function ProjectInfoBox({ projectInfo }: ProjectInfoBoxProps) {\n const { t } = useLingui()\n const navigate = useNavigate()\n const matches = useMatches()\n const { projectId } = useParams({ strict: false }) as { projectId: string }\n\n const breadcrumbs = useMemo(() => {\n const crumbLabels: Record<CrumbLabelKey, string> = {\n Compute: t`Compute`,\n Network: t`Network`,\n Storage: t`Storage`,\n Services: t`Services`,\n Images: t`Images`,\n Flavors: t`Flavors`,\n \"Security Groups\": t`Security Groups`,\n \"Floating IPs\": t`Floating IPs`,\n \"PCA (Clavis)\": t`PCA (Clavis)`,\n }\n\n const resolveProviderLabel = (provider: string | undefined) => {\n if (provider === \"swift\") return t`Object Storage (Swift)`\n if (provider === \"ceph\") return t`Object Storage (Ceph)`\n return t`Storage`\n }\n\n const items: Array<{ label?: string; icon?: KnownIcons; onClick?: () => void; active?: boolean }> = []\n\n items.push({ icon: \"home\", label: t`Home`, onClick: () => navigate({ to: \"/projects\" }) })\n\n if (projectInfo.domain?.name) {\n items.push({ label: projectInfo.domain.name })\n }\n\n items.push({\n label: projectInfo.name,\n onClick: () => navigate({ to: \"/projects/$projectId\", params: { projectId } }),\n })\n\n const projectMatches = matches.filter(\n (m) => m.routeId !== \"/_auth/projects/$projectId\" && m.routeId.startsWith(\"/_auth/projects/$projectId\")\n )\n const deepest = projectMatches[projectMatches.length - 1]\n if (!deepest) return items\n\n const info = isRouteInfo(deepest.staticData) ? deepest.staticData : undefined\n if (!info) return items\n\n const params = deepest.params as Record<string, string>\n\n if (info.sectionCrumb) {\n const { labelKey, to } = info.sectionCrumb\n const label = labelKey ? crumbLabels[labelKey] : undefined\n const isLeaf = !info.crumb\n items.push(\n to\n ? { label, onClick: () => navigate({ to: to as never, params: params as never }) }\n : { label, active: isLeaf }\n )\n }\n\n if (info.crumb) {\n const { labelKey, to, useParamAsLabel } = info.crumb\n const resolvedLabel = useParamAsLabel\n ? resolveProviderLabel(params[useParamAsLabel])\n : labelKey\n ? crumbLabels[labelKey]\n : undefined\n\n if (info.isDetail) {\n items.push({ label: resolvedLabel, onClick: () => navigate({ to: to as never, params: params as never }) })\n const title = deepest.meta?.find((m) => m != null && \"title\" in m)?.title as string | undefined\n if (title) items.push({ label: title, active: true })\n } else {\n items.push(\n to\n ? { label: resolvedLabel, onClick: () => navigate({ to: to as never, params: params as never }) }\n : { label: resolvedLabel, active: true }\n )\n }\n }\n\n return items\n }, [matches, projectInfo, projectId, navigate, t])\n\n return (\n <Breadcrumb className=\"mt-8 mb-4\">\n {breadcrumbs.map((item, index) => (\n <BreadcrumbItem key={index} label={item.label} icon={item.icon} onClick={item.onClick} active={item.active} />\n ))}\n </Breadcrumb>\n )\n}\n","import { createFileRoute, Outlet, useLoaderData } from \"@tanstack/react-router\"\nimport { AppShell, Container, Stack } from \"@cloudoperators/juno-ui-components\"\nimport { SideNavBar } from \"@/client/routes/_auth/projects/-components/SideNavBar\"\nimport { ProjectInfoBox } from \"@/client/components/ProjectView/ProjectInfoBox\"\nimport { RouteError } from \"@/client/components/Error/RouteError\"\n\nexport const Route = createFileRoute(\"/_auth/projects/$projectId\")({\n component: RouteComponent,\n errorComponent: ({ error }) => {\n return <RouteError error={error} />\n },\n loader: async (options) => {\n const { context, params } = options\n const data = await context.trpcClient?.auth.setCurrentScope.mutate({\n type: \"project\",\n projectId: params.projectId || \"\",\n })\n const availableServices = await context.trpcClient?.auth.getAvailableServices.query()\n\n // Extract accountId (domain id) from the rescoped token\n // This is needed for SideNavBar navigation until we refactor it\n const accountId = data?.domain?.id || \"\"\n\n return {\n trpcClient: context.trpcClient,\n crumbDomain: { path: `/projects`, name: data?.domain?.name },\n crumbProject: data?.project,\n availableServices,\n accountId, // Keep for SideNavBar compatibility\n projectId: params.projectId,\n }\n },\n})\n\nfunction RouteComponent() {\n const { availableServices, projectId, crumbProject } = useLoaderData({ from: Route.id })\n\n return (\n <AppShell\n embedded\n sideNavigation={\n <SideNavBar\n availableServices={availableServices!}\n projectId={projectId}\n projectName={crumbProject?.name || projectId}\n />\n }\n className=\"h-min-screen\"\n >\n <Container>\n <Stack direction=\"vertical\" distribution=\"start\" alignment=\"stretch\" className=\"xl:flex-row\" gap=\"6\">\n {/* Main content area */}\n <div className=\"min-w-0 flex-1\">\n <ProjectInfoBox\n projectInfo={{\n id: projectId,\n name: crumbProject?.name || projectId,\n domain: crumbProject?.domain,\n }}\n />\n <Outlet />\n </div>\n </Stack>\n </Container>\n </AppShell>\n )\n}\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
- import { t as e } from "./RouteError-Cyto623-.mjs";
1
+ import { t as e } from "./RouteError-iP1Vd6bY.mjs";
2
2
  import { jsx as t } from "react/jsx-runtime";
3
3
  //#region src/client/routes/_auth/projects/$projectId.tsx?tsr-split=errorComponent
4
4
  var n = ({ error: n }) => /* @__PURE__ */ t(e, { error: n });
5
5
  //#endregion
6
6
  export { n as errorComponent };
7
7
 
8
- //# sourceMappingURL=_projectId-Pxp-RXK4.mjs.map
8
+ //# sourceMappingURL=_projectId-KH5si25Q.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"_projectId-Pxp-RXK4.mjs","names":["RouteError","SplitErrorComponent","error","errorComponent"],"sources":["../../src/client/routes/_auth/projects/$projectId.tsx?tsr-split=errorComponent"],"sourcesContent":["import { createFileRoute, Outlet, useLoaderData } from \"@tanstack/react-router\"\nimport { AppShell, Container, Stack } from \"@cloudoperators/juno-ui-components\"\nimport { SideNavBar } from \"@/client/routes/_auth/projects/-components/SideNavBar\"\nimport { ProjectInfoBox } from \"@/client/components/ProjectView/ProjectInfoBox\"\nimport { RouteError } from \"@/client/components/Error/RouteError\"\n\nexport const Route = createFileRoute(\"/_auth/projects/$projectId\")({\n component: RouteComponent,\n errorComponent: ({ error }) => {\n return <RouteError error={error} />\n },\n loader: async (options) => {\n const { context, params } = options\n const data = await context.trpcClient?.auth.setCurrentScope.mutate({\n type: \"project\",\n projectId: params.projectId || \"\",\n })\n const availableServices = await context.trpcClient?.auth.getAvailableServices.query()\n\n // Extract accountId (domain id) from the rescoped token\n // This is needed for SideNavBar navigation until we refactor it\n const accountId = data?.domain?.id || \"\"\n\n return {\n trpcClient: context.trpcClient,\n crumbDomain: { path: `/projects`, name: data?.domain?.name },\n crumbProject: data?.project,\n availableServices,\n accountId, // Keep for SideNavBar compatibility\n projectId: params.projectId,\n }\n },\n})\n\nfunction RouteComponent() {\n const { availableServices, projectId, crumbProject } = useLoaderData({ from: Route.id })\n\n return (\n <AppShell\n embedded\n sideNavigation={\n <SideNavBar\n availableServices={availableServices!}\n projectId={projectId}\n projectName={crumbProject?.name || projectId}\n />\n }\n className=\"h-min-screen\"\n >\n <Container>\n <Stack direction=\"vertical\" distribution=\"start\" alignment=\"stretch\" className=\"xl:flex-row\" gap=\"6\">\n {/* Main content area */}\n <div className=\"min-w-0 flex-1\">\n <ProjectInfoBox\n projectInfo={{\n id: projectId,\n name: crumbProject?.name || projectId,\n domain: crumbProject?.domain,\n }}\n />\n <Outlet />\n </div>\n </Stack>\n </Container>\n </AppShell>\n )\n}\n"],"mappings":";;;AAIiE,IAAAC,KAI9C,EAAEC,eACV,kBAAC,GAAA,EAAkBA,UAAAA,CAAAA"}
1
+ {"version":3,"file":"_projectId-KH5si25Q.mjs","names":["RouteError","SplitErrorComponent","error","errorComponent"],"sources":["../../src/client/routes/_auth/projects/$projectId.tsx?tsr-split=errorComponent"],"sourcesContent":["import { createFileRoute, Outlet, useLoaderData } from \"@tanstack/react-router\"\nimport { AppShell, Container, Stack } from \"@cloudoperators/juno-ui-components\"\nimport { SideNavBar } from \"@/client/routes/_auth/projects/-components/SideNavBar\"\nimport { ProjectInfoBox } from \"@/client/components/ProjectView/ProjectInfoBox\"\nimport { RouteError } from \"@/client/components/Error/RouteError\"\n\nexport const Route = createFileRoute(\"/_auth/projects/$projectId\")({\n component: RouteComponent,\n errorComponent: ({ error }) => {\n return <RouteError error={error} />\n },\n loader: async (options) => {\n const { context, params } = options\n const data = await context.trpcClient?.auth.setCurrentScope.mutate({\n type: \"project\",\n projectId: params.projectId || \"\",\n })\n const availableServices = await context.trpcClient?.auth.getAvailableServices.query()\n\n // Extract accountId (domain id) from the rescoped token\n // This is needed for SideNavBar navigation until we refactor it\n const accountId = data?.domain?.id || \"\"\n\n return {\n trpcClient: context.trpcClient,\n crumbDomain: { path: `/projects`, name: data?.domain?.name },\n crumbProject: data?.project,\n availableServices,\n accountId, // Keep for SideNavBar compatibility\n projectId: params.projectId,\n }\n },\n})\n\nfunction RouteComponent() {\n const { availableServices, projectId, crumbProject } = useLoaderData({ from: Route.id })\n\n return (\n <AppShell\n embedded\n sideNavigation={\n <SideNavBar\n availableServices={availableServices!}\n projectId={projectId}\n projectName={crumbProject?.name || projectId}\n />\n }\n className=\"h-min-screen\"\n >\n <Container>\n <Stack direction=\"vertical\" distribution=\"start\" alignment=\"stretch\" className=\"xl:flex-row\" gap=\"6\">\n {/* Main content area */}\n <div className=\"min-w-0 flex-1\">\n <ProjectInfoBox\n projectInfo={{\n id: projectId,\n name: crumbProject?.name || projectId,\n domain: crumbProject?.domain,\n }}\n />\n <Outlet />\n </div>\n </Stack>\n </Container>\n </AppShell>\n )\n}\n"],"mappings":";;;AAIiE,IAAAC,KAI9C,EAAEC,eACV,kBAAC,GAAA,EAAkBA,UAAAA,CAAAA"}
@@ -19,7 +19,7 @@ var i = t("/_auth/projects/$projectId/network/securitygroups/$securityGroupId/")
19
19
  return { sgTitle: n?.name || n?.id || null };
20
20
  },
21
21
  head: ({ loaderData: e }) => ({ meta: [{ title: e?.sgTitle ?? "Security Group" }] }),
22
- component: n(() => import("./_securityGroupId-Dqi6ddw4.mjs"), "component"),
22
+ component: n(() => import("./_securityGroupId-O7FXfx0M.mjs"), "component"),
23
23
  beforeLoad: async ({ context: t, params: n }) => {
24
24
  let { trpcClient: i } = t, a = e(await i?.auth.getAvailableServices.query() || []);
25
25
  if (!a.network || !a.network.neutron) throw r({
@@ -31,4 +31,4 @@ var i = t("/_auth/projects/$projectId/network/securitygroups/$securityGroupId/")
31
31
  //#endregion
32
32
  export { i as t };
33
33
 
34
- //# sourceMappingURL=_securityGroupId-VV2lUcGQ.mjs.map
34
+ //# sourceMappingURL=_securityGroupId-DgaSqYex.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"_securityGroupId-VV2lUcGQ.mjs","names":["createFileRoute","redirect","getServiceIndex","Route","staticData","section","service","isDetail","sectionCrumb","labelKey","crumb","to","RouteInfo","loader","context","params","sg","trpcClient","network","securityGroup","getById","query","project_id","projectId","securityGroupId","sgTitle","name","id","head","loaderData","meta","title","component","lazyRouteComponent","$$splitComponentImporter","beforeLoad","availableServices","auth","getAvailableServices","serviceIndex"],"sources":["../../src/client/routes/_auth/projects/$projectId/network/securitygroups/$securityGroupId/index.tsx"],"sourcesContent":["import {\n Breadcrumb,\n BreadcrumbItem,\n Button,\n ContentHeading,\n Stack,\n Spinner,\n} from \"@cloudoperators/juno-ui-components/index\"\nimport { createFileRoute, redirect, useNavigate } from \"@tanstack/react-router\"\nimport type { RouteInfo } from \"@/client/routes/routeInfo\"\nimport { Trans, useLingui } from \"@lingui/react/macro\"\nimport { useMemo } from \"react\"\nimport { getServiceIndex } from \"@/server/Authentication/helpers\"\nimport { useProjectId } from \"@/client/hooks\"\nimport { SecurityGroupDetailsView } from \"./-components/SecurityGroupDetailsView\"\nimport { EditSecurityGroupModal } from \"../-components/-modals/EditSecurityGroupModal\"\nimport { useSecurityGroupDetails } from \"./-hooks/useSecurityGroupDetails\"\nimport { useListWithFiltering } from \"@/client/utils/useListWithFiltering\"\nimport { trpcReact } from \"@/client/trpcClient\"\n\nexport const Route = createFileRoute(\"/_auth/projects/$projectId/network/securitygroups/$securityGroupId/\")({\n staticData: {\n section: \"network\",\n service: \"securitygroups\",\n isDetail: true,\n sectionCrumb: { labelKey: \"Network\" },\n crumb: { labelKey: \"Security Groups\", to: \"/projects/$projectId/network/securitygroups\" },\n } satisfies RouteInfo,\n loader: async ({ context, params }) => {\n const sg = await context.trpcClient?.network.securityGroup.getById.query({\n project_id: params.projectId,\n securityGroupId: params.securityGroupId,\n })\n return { sgTitle: sg?.name || sg?.id || null }\n },\n head: ({ loaderData }) => ({\n meta: [{ title: loaderData?.sgTitle ?? \"Security Group\" }],\n }),\n component: RouteComponent,\n beforeLoad: async ({ context, params }) => {\n const { trpcClient } = context\n\n const availableServices = (await trpcClient?.auth.getAvailableServices.query()) || []\n\n const serviceIndex = getServiceIndex(availableServices)\n\n // Redirect to the \"Projects Overview\" page if network service not available\n if (!serviceIndex[\"network\"]) {\n throw redirect({\n to: \"/projects/$projectId/network/securitygroups\",\n params: { projectId: params.projectId },\n })\n }\n\n if (!serviceIndex[\"network\"][\"neutron\"]) {\n // Redirect to the \"Network Services Overview\" page if the \"Neutron\" service is not available\n throw redirect({\n to: \"/projects/$projectId/network/securitygroups\",\n params: { projectId: params.projectId },\n })\n }\n },\n})\n\nfunction RouteComponent() {\n const { securityGroupId } = Route.useParams()\n const projectId = useProjectId()\n const navigate = useNavigate()\n const { t } = useLingui()\n\n // Rules filtering using the same pattern as List page\n const {\n searchTerm: rulesSearchTerm,\n sortSettings,\n filterSettings,\n handleSearchChange,\n handleSortChange,\n handleFilterChange,\n } = useListWithFiltering<\"direction\" | \"protocol\" | \"description\">({\n defaultSortKey: \"direction\",\n defaultSortDir: \"asc\",\n sortOptions: [\n { label: t`Direction`, value: \"direction\" },\n { label: t`Protocol`, value: \"protocol\" },\n { label: t`Description`, value: \"description\" },\n ],\n filterSettings: {\n filters: [\n {\n displayName: t`Direction`,\n filterName: \"direction\",\n values: [\"ingress\", \"egress\"],\n supportsMultiValue: false,\n },\n {\n displayName: t`Ethertype`,\n filterName: \"ethertype\",\n values: [\"IPv4\", \"IPv6\"],\n supportsMultiValue: false,\n },\n {\n displayName: t`Protocol`,\n filterName: \"protocol\",\n values: [\"tcp\", \"udp\", \"icmp\", \"ipv6-icmp\"],\n supportsMultiValue: false,\n },\n ],\n },\n })\n\n // Group filter controls for the hook\n const filterControls = {\n searchTerm: rulesSearchTerm,\n onSearchChange: handleSearchChange,\n sortSettings,\n onSortChange: handleSortChange,\n filterSettings,\n onFilterChange: handleFilterChange,\n }\n\n // Use custom hook for logic (now includes filtering/sorting)\n const {\n securityGroup,\n filteredAndSortedRules,\n isLoading,\n isError,\n error,\n isUpdating,\n updateError,\n isDeletingRule,\n deleteRuleError,\n isCreatingRule,\n createRuleError,\n editModalOpen,\n handleEdit,\n handleCloseEditModal,\n handleUpdate,\n handleDeleteRule,\n handleCreateRule,\n } = useSecurityGroupDetails({\n securityGroupId,\n filterControls,\n })\n\n // Fetch available security groups for the Add Rule dropdown\n const { data: securityGroups } = trpcReact.network.securityGroup.list.useQuery({ project_id: projectId })\n const availableSecurityGroups = useMemo(() => {\n return (securityGroups || [])\n .filter((sg) => sg.id !== securityGroupId) // Exclude current group\n .map((sg) => ({\n id: sg.id,\n name: sg.name || sg.id,\n }))\n }, [securityGroups, securityGroupId])\n\n const handleBack = () => {\n navigate({\n to: \"/projects/$projectId/network/securitygroups\",\n params: { projectId },\n })\n }\n\n // Handle loading state\n if (isLoading) {\n return (\n <Stack className=\"fixed inset-0\" distribution=\"center\" alignment=\"center\" direction=\"vertical\">\n <Spinner variant=\"primary\" size=\"large\" className=\"mb-2\" />\n <Trans>Loading Security Group Details...</Trans>\n </Stack>\n )\n }\n\n // Handle error state\n if (isError) {\n const errorMessage = error?.message || \"Unknown error\"\n\n return (\n <Stack className=\"fixed inset-0\" distribution=\"center\" alignment=\"center\" direction=\"vertical\" gap=\"5\">\n <p className=\"text-theme-error font-semibold\">\n <Trans>Error loading security group</Trans>\n </p>\n <p className=\"text-theme-highest\">{errorMessage}</p>\n <Button onClick={handleBack} variant=\"primary\">\n <Trans>Back to Security Groups</Trans>\n </Button>\n </Stack>\n )\n }\n\n // Handle no data state\n if (!securityGroup) {\n return (\n <Stack className=\"fixed inset-0\" distribution=\"center\" alignment=\"center\" direction=\"vertical\" gap=\"5\">\n <p className=\"text-theme-secondary\">\n <Trans>Security group not found</Trans>\n </p>\n <Button onClick={handleBack} variant=\"primary\">\n <Trans>Back to Security Groups</Trans>\n </Button>\n </Stack>\n )\n }\n\n // Render success state\n return (\n <Stack direction=\"vertical\">\n <ContentHeading>{securityGroup.name || securityGroup.id}</ContentHeading>\n <Breadcrumb className=\"my-6\">\n <BreadcrumbItem onClick={handleBack} label={t`Security Groups`} />\n <BreadcrumbItem active label={securityGroup.id} />\n </Breadcrumb>\n\n <SecurityGroupDetailsView\n securityGroup={securityGroup}\n filteredAndSortedRules={filteredAndSortedRules}\n onEdit={handleEdit}\n onDeleteRule={handleDeleteRule}\n isDeletingRule={isDeletingRule}\n deleteRuleError={deleteRuleError}\n filterControls={filterControls}\n onCreateRule={handleCreateRule}\n isCreatingRule={isCreatingRule}\n createRuleError={createRuleError}\n availableSecurityGroups={availableSecurityGroups}\n currentProjectId={projectId}\n />\n\n <EditSecurityGroupModal\n securityGroup={securityGroup}\n open={editModalOpen}\n onClose={handleCloseEditModal}\n onUpdate={handleUpdate}\n isLoading={isUpdating}\n error={updateError}\n />\n </Stack>\n )\n}\n"],"mappings":";;AAoBA,IAAaG,IAAQH,EAAgB,sEAAA,CAAuE;CAC1GI,YAAY;EACVC,SAAS;EACTC,SAAS;EACTC,UAAU;EACVC,cAAc,EAAEC,UAAU,WAAU;EACpCC,OAAO;GAAED,UAAU;GAAmBE,IAAI;GAA8C;EAC1F;CACAE,QAAQ,OAAO,EAAEC,YAASC,gBAAQ;EAChC,IAAMC,IAAK,MAAMF,EAAQG,YAAYC,QAAQC,cAAcC,QAAQC,MAAM;GACvEC,YAAYP,EAAOQ;GACnBC,iBAAiBT,EAAOS;GAC1B,CAAA;AACA,SAAO,EAAEC,SAAST,GAAIU,QAAQV,GAAIW,MAAM,MAAK;;CAE/CC,OAAO,EAAEC,qBAAkB,EACzBC,MAAM,CAAC,EAAEC,OAAOF,GAAYJ,WAAW,kBAAiB,CAAC,EAC3D;CACAO,WAASC,mDAAA,YAAA;CACTE,YAAY,OAAO,EAAErB,YAASC,gBAAQ;EACpC,IAAM,EAAEE,kBAAeH,GAIjByB,IAAerC,EAFK,MAAOe,GAAYoB,KAAKC,qBAAqBjB,OAAAA,IAAY,EAAE,CAEhDe;AAUrC,MAPI,CAACG,EAAa,WAOd,CAACA,EAAa,QAAW,QAE3B,OAAMtC,EAAS;GACbU,IAAI;GACJI,QAAQ,EAAEQ,WAAWR,EAAOQ,WAAU;GACxC,CAAA;;CAGN,CAAA"}
1
+ {"version":3,"file":"_securityGroupId-DgaSqYex.mjs","names":["createFileRoute","redirect","getServiceIndex","Route","staticData","section","service","isDetail","sectionCrumb","labelKey","crumb","to","RouteInfo","loader","context","params","sg","trpcClient","network","securityGroup","getById","query","project_id","projectId","securityGroupId","sgTitle","name","id","head","loaderData","meta","title","component","lazyRouteComponent","$$splitComponentImporter","beforeLoad","availableServices","auth","getAvailableServices","serviceIndex"],"sources":["../../src/client/routes/_auth/projects/$projectId/network/securitygroups/$securityGroupId/index.tsx"],"sourcesContent":["import {\n Breadcrumb,\n BreadcrumbItem,\n Button,\n ContentHeading,\n Stack,\n Spinner,\n} from \"@cloudoperators/juno-ui-components/index\"\nimport { createFileRoute, redirect, useNavigate } from \"@tanstack/react-router\"\nimport type { RouteInfo } from \"@/client/routes/routeInfo\"\nimport { Trans, useLingui } from \"@lingui/react/macro\"\nimport { useMemo } from \"react\"\nimport { getServiceIndex } from \"@/server/Authentication/helpers\"\nimport { useProjectId } from \"@/client/hooks\"\nimport { SecurityGroupDetailsView } from \"./-components/SecurityGroupDetailsView\"\nimport { EditSecurityGroupModal } from \"../-components/-modals/EditSecurityGroupModal\"\nimport { useSecurityGroupDetails } from \"./-hooks/useSecurityGroupDetails\"\nimport { useListWithFiltering } from \"@/client/utils/useListWithFiltering\"\nimport { trpcReact } from \"@/client/trpcClient\"\n\nexport const Route = createFileRoute(\"/_auth/projects/$projectId/network/securitygroups/$securityGroupId/\")({\n staticData: {\n section: \"network\",\n service: \"securitygroups\",\n isDetail: true,\n sectionCrumb: { labelKey: \"Network\" },\n crumb: { labelKey: \"Security Groups\", to: \"/projects/$projectId/network/securitygroups\" },\n } satisfies RouteInfo,\n loader: async ({ context, params }) => {\n const sg = await context.trpcClient?.network.securityGroup.getById.query({\n project_id: params.projectId,\n securityGroupId: params.securityGroupId,\n })\n return { sgTitle: sg?.name || sg?.id || null }\n },\n head: ({ loaderData }) => ({\n meta: [{ title: loaderData?.sgTitle ?? \"Security Group\" }],\n }),\n component: RouteComponent,\n beforeLoad: async ({ context, params }) => {\n const { trpcClient } = context\n\n const availableServices = (await trpcClient?.auth.getAvailableServices.query()) || []\n\n const serviceIndex = getServiceIndex(availableServices)\n\n // Redirect to the \"Projects Overview\" page if network service not available\n if (!serviceIndex[\"network\"]) {\n throw redirect({\n to: \"/projects/$projectId/network/securitygroups\",\n params: { projectId: params.projectId },\n })\n }\n\n if (!serviceIndex[\"network\"][\"neutron\"]) {\n // Redirect to the \"Network Services Overview\" page if the \"Neutron\" service is not available\n throw redirect({\n to: \"/projects/$projectId/network/securitygroups\",\n params: { projectId: params.projectId },\n })\n }\n },\n})\n\nfunction RouteComponent() {\n const { securityGroupId } = Route.useParams()\n const projectId = useProjectId()\n const navigate = useNavigate()\n const { t } = useLingui()\n\n // Rules filtering using the same pattern as List page\n const {\n searchTerm: rulesSearchTerm,\n sortSettings,\n filterSettings,\n handleSearchChange,\n handleSortChange,\n handleFilterChange,\n } = useListWithFiltering<\"direction\" | \"protocol\" | \"description\">({\n defaultSortKey: \"direction\",\n defaultSortDir: \"asc\",\n sortOptions: [\n { label: t`Direction`, value: \"direction\" },\n { label: t`Protocol`, value: \"protocol\" },\n { label: t`Description`, value: \"description\" },\n ],\n filterSettings: {\n filters: [\n {\n displayName: t`Direction`,\n filterName: \"direction\",\n values: [\"ingress\", \"egress\"],\n supportsMultiValue: false,\n },\n {\n displayName: t`Ethertype`,\n filterName: \"ethertype\",\n values: [\"IPv4\", \"IPv6\"],\n supportsMultiValue: false,\n },\n {\n displayName: t`Protocol`,\n filterName: \"protocol\",\n values: [\"tcp\", \"udp\", \"icmp\", \"ipv6-icmp\"],\n supportsMultiValue: false,\n },\n ],\n },\n })\n\n // Group filter controls for the hook\n const filterControls = {\n searchTerm: rulesSearchTerm,\n onSearchChange: handleSearchChange,\n sortSettings,\n onSortChange: handleSortChange,\n filterSettings,\n onFilterChange: handleFilterChange,\n }\n\n // Use custom hook for logic (now includes filtering/sorting)\n const {\n securityGroup,\n filteredAndSortedRules,\n isLoading,\n isError,\n error,\n isUpdating,\n updateError,\n isDeletingRule,\n deleteRuleError,\n isCreatingRule,\n createRuleError,\n editModalOpen,\n handleEdit,\n handleCloseEditModal,\n handleUpdate,\n handleDeleteRule,\n handleCreateRule,\n } = useSecurityGroupDetails({\n securityGroupId,\n filterControls,\n })\n\n // Fetch available security groups for the Add Rule dropdown\n const { data: securityGroups } = trpcReact.network.securityGroup.list.useQuery({ project_id: projectId })\n const availableSecurityGroups = useMemo(() => {\n return (securityGroups || [])\n .filter((sg) => sg.id !== securityGroupId) // Exclude current group\n .map((sg) => ({\n id: sg.id,\n name: sg.name || sg.id,\n }))\n }, [securityGroups, securityGroupId])\n\n const handleBack = () => {\n navigate({\n to: \"/projects/$projectId/network/securitygroups\",\n params: { projectId },\n })\n }\n\n // Handle loading state\n if (isLoading) {\n return (\n <Stack className=\"fixed inset-0\" distribution=\"center\" alignment=\"center\" direction=\"vertical\">\n <Spinner variant=\"primary\" size=\"large\" className=\"mb-2\" />\n <Trans>Loading Security Group Details...</Trans>\n </Stack>\n )\n }\n\n // Handle error state\n if (isError) {\n const errorMessage = error?.message || \"Unknown error\"\n\n return (\n <Stack className=\"fixed inset-0\" distribution=\"center\" alignment=\"center\" direction=\"vertical\" gap=\"5\">\n <p className=\"text-theme-error font-semibold\">\n <Trans>Error loading security group</Trans>\n </p>\n <p className=\"text-theme-highest\">{errorMessage}</p>\n <Button onClick={handleBack} variant=\"primary\">\n <Trans>Back to Security Groups</Trans>\n </Button>\n </Stack>\n )\n }\n\n // Handle no data state\n if (!securityGroup) {\n return (\n <Stack className=\"fixed inset-0\" distribution=\"center\" alignment=\"center\" direction=\"vertical\" gap=\"5\">\n <p className=\"text-theme-secondary\">\n <Trans>Security group not found</Trans>\n </p>\n <Button onClick={handleBack} variant=\"primary\">\n <Trans>Back to Security Groups</Trans>\n </Button>\n </Stack>\n )\n }\n\n // Render success state\n return (\n <Stack direction=\"vertical\">\n <ContentHeading>{securityGroup.name || securityGroup.id}</ContentHeading>\n <Breadcrumb className=\"my-6\">\n <BreadcrumbItem onClick={handleBack} label={t`Security Groups`} />\n <BreadcrumbItem active label={securityGroup.id} />\n </Breadcrumb>\n\n <SecurityGroupDetailsView\n securityGroup={securityGroup}\n filteredAndSortedRules={filteredAndSortedRules}\n onEdit={handleEdit}\n onDeleteRule={handleDeleteRule}\n isDeletingRule={isDeletingRule}\n deleteRuleError={deleteRuleError}\n filterControls={filterControls}\n onCreateRule={handleCreateRule}\n isCreatingRule={isCreatingRule}\n createRuleError={createRuleError}\n availableSecurityGroups={availableSecurityGroups}\n currentProjectId={projectId}\n />\n\n <EditSecurityGroupModal\n securityGroup={securityGroup}\n open={editModalOpen}\n onClose={handleCloseEditModal}\n onUpdate={handleUpdate}\n isLoading={isUpdating}\n error={updateError}\n />\n </Stack>\n )\n}\n"],"mappings":";;AAoBA,IAAaG,IAAQH,EAAgB,sEAAA,CAAuE;CAC1GI,YAAY;EACVC,SAAS;EACTC,SAAS;EACTC,UAAU;EACVC,cAAc,EAAEC,UAAU,WAAU;EACpCC,OAAO;GAAED,UAAU;GAAmBE,IAAI;GAA8C;EAC1F;CACAE,QAAQ,OAAO,EAAEC,YAASC,gBAAQ;EAChC,IAAMC,IAAK,MAAMF,EAAQG,YAAYC,QAAQC,cAAcC,QAAQC,MAAM;GACvEC,YAAYP,EAAOQ;GACnBC,iBAAiBT,EAAOS;GAC1B,CAAA;AACA,SAAO,EAAEC,SAAST,GAAIU,QAAQV,GAAIW,MAAM,MAAK;;CAE/CC,OAAO,EAAEC,qBAAkB,EACzBC,MAAM,CAAC,EAAEC,OAAOF,GAAYJ,WAAW,kBAAiB,CAAC,EAC3D;CACAO,WAASC,mDAAA,YAAA;CACTE,YAAY,OAAO,EAAErB,YAASC,gBAAQ;EACpC,IAAM,EAAEE,kBAAeH,GAIjByB,IAAerC,EAFK,MAAOe,GAAYoB,KAAKC,qBAAqBjB,OAAAA,IAAY,EAAE,CAEhDe;AAUrC,MAPI,CAACG,EAAa,WAOd,CAACA,EAAa,QAAW,QAE3B,OAAMtC,EAAS;GACbU,IAAI;GACJI,QAAQ,EAAEQ,WAAWR,EAAOQ,WAAU;GACxC,CAAA;;CAGN,CAAA"}