@cobaltcore-dev/aurora 0.10.0 → 0.12.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 (122) hide show
  1. package/README.md +2 -1
  2. package/dist/client/AuroraApp.d.ts +2 -0
  3. package/dist/client/AuthProvider-DZqOvceF.mjs +64 -0
  4. package/dist/client/AuthProvider-DZqOvceF.mjs.map +1 -0
  5. package/dist/client/ContentHeader-D2nIdJKJ.mjs +110 -0
  6. package/dist/client/ContentHeader-D2nIdJKJ.mjs.map +1 -0
  7. package/dist/client/{DeleteFlavorModal-C3m7bQJu.mjs → DeleteFlavorModal-Dwm0Hh84.mjs} +158 -158
  8. package/dist/client/{DeleteFlavorModal-C3m7bQJu.mjs.map → DeleteFlavorModal-Dwm0Hh84.mjs.map} +1 -1
  9. package/dist/client/{DeleteVersionsModal-CyYZfB8d.mjs → DeleteVersionsModal-DQO8lGmS.mjs} +58 -58
  10. package/dist/client/{DeleteVersionsModal-CyYZfB8d.mjs.map → DeleteVersionsModal-DQO8lGmS.mjs.map} +1 -1
  11. package/dist/client/{EditSecurityGroupModal-DKusxfta.mjs → EditSecurityGroupModal-BGPXglju.mjs} +16 -16
  12. package/dist/client/{EditSecurityGroupModal-DKusxfta.mjs.map → EditSecurityGroupModal-BGPXglju.mjs.map} +1 -1
  13. package/dist/client/{FiltersInput-GzR4D0q6.mjs → FiltersInput-cOaaRpcQ.mjs} +20 -20
  14. package/dist/client/{FiltersInput-GzR4D0q6.mjs.map → FiltersInput-cOaaRpcQ.mjs.map} +1 -1
  15. package/dist/client/{FloatingIpActionModals-CRvROJ3H.mjs → FloatingIpActionModals-RCvaamOg.mjs} +51 -51
  16. package/dist/client/{FloatingIpActionModals-CRvROJ3H.mjs.map → FloatingIpActionModals-RCvaamOg.mjs.map} +1 -1
  17. package/dist/client/{ImageToastNotifications-BuDXpTkl.mjs → ImageToastNotifications-ClO-1yOH.mjs} +341 -341
  18. package/dist/client/{ImageToastNotifications-BuDXpTkl.mjs.map → ImageToastNotifications-ClO-1yOH.mjs.map} +1 -1
  19. package/dist/client/{RouteError-DVAiT0mT.mjs → RouteError-BmZRk_ss.mjs} +2 -2
  20. package/dist/client/{RouteError-DVAiT0mT.mjs.map → RouteError-BmZRk_ss.mjs.map} +1 -1
  21. package/dist/client/{SortInput-DLcusjGZ.mjs → SortInput-DjaEHvcO.mjs} +6 -6
  22. package/dist/client/{SortInput-DLcusjGZ.mjs.map → SortInput-DjaEHvcO.mjs.map} +1 -1
  23. package/dist/client/{_auth-DXJkv9QO.mjs → _auth-DnImOqR-.mjs} +2 -2
  24. package/dist/client/_auth-DnImOqR-.mjs.map +1 -0
  25. package/dist/client/{_flavorId-Dy7EYQum.mjs → _flavorId-CtQEd28h.mjs} +9 -9
  26. package/dist/client/{_flavorId-Dy7EYQum.mjs.map → _flavorId-CtQEd28h.mjs.map} +1 -1
  27. package/dist/client/{_flavorId-DsD2VTKA.mjs → _flavorId-DVsGdXUf.mjs} +42 -42
  28. package/dist/client/{_flavorId-DsD2VTKA.mjs.map → _flavorId-DVsGdXUf.mjs.map} +1 -1
  29. package/dist/client/{_floatingIpId-j17rCQqG2.mjs → _floatingIpId-CKMq7-4p2.mjs} +32 -32
  30. package/dist/client/{_floatingIpId-j17rCQqG2.mjs.map → _floatingIpId-CKMq7-4p2.mjs.map} +1 -1
  31. package/dist/client/{_floatingIpId-BjVbeNw_.mjs → _floatingIpId-DvTh6vdB.mjs} +2 -2
  32. package/dist/client/{_floatingIpId-BjVbeNw_.mjs.map → _floatingIpId-DvTh6vdB.mjs.map} +1 -1
  33. package/dist/client/{_imageId-BjfhqAje.mjs → _imageId-xG8hC49j.mjs} +129 -129
  34. package/dist/client/_imageId-xG8hC49j.mjs.map +1 -0
  35. package/dist/client/{_pcaId-Bo7yHkNW.mjs → _pcaId-Db_t0x6O.mjs} +149 -149
  36. package/dist/client/{_pcaId-Bo7yHkNW.mjs.map → _pcaId-Db_t0x6O.mjs.map} +1 -1
  37. package/dist/client/{_pcaId-CKkCVC7b.mjs → _pcaId-WCJoukIk.mjs} +2 -2
  38. package/dist/client/{_pcaId-CKkCVC7b.mjs.map → _pcaId-WCJoukIk.mjs.map} +1 -1
  39. package/dist/client/{_projectId-B_2sZKk-.mjs → _projectId-B2cvskLV.mjs} +2 -2
  40. package/dist/client/_projectId-B2cvskLV.mjs.map +1 -0
  41. package/dist/client/{_projectId-Dbck_MFa.mjs → _projectId-BCMkaH_h.mjs} +85 -85
  42. package/dist/client/_projectId-BCMkaH_h.mjs.map +1 -0
  43. package/dist/client/{_projectId-CARHuZTU.mjs → _projectId-BdqRntcN.mjs} +15 -14
  44. package/dist/client/_projectId-BdqRntcN.mjs.map +1 -0
  45. package/dist/client/{_projectId-CY8W0IF6.mjs → _projectId-C8RGOqZb.mjs} +3 -3
  46. package/dist/client/{_projectId-CY8W0IF6.mjs.map → _projectId-C8RGOqZb.mjs.map} +1 -1
  47. package/dist/client/{_securityGroupId-gSEZbBII.mjs → _securityGroupId-CyQ3XmpB.mjs} +2 -2
  48. package/dist/client/{_securityGroupId-gSEZbBII.mjs.map → _securityGroupId-CyQ3XmpB.mjs.map} +1 -1
  49. package/dist/client/{_securityGroupId-CkN0CGVg.mjs → _securityGroupId-DMi7YRux.mjs} +352 -352
  50. package/dist/client/{_securityGroupId-CkN0CGVg.mjs.map → _securityGroupId-DMi7YRux.mjs.map} +1 -1
  51. package/dist/client/_storageType-Bb6YZgXf.mjs +3002 -0
  52. package/dist/client/_storageType-Bb6YZgXf.mjs.map +1 -0
  53. package/dist/client/{_storageType-6k8lUnQo.mjs → _storageType-g1zkFFRh.mjs} +2 -2
  54. package/dist/client/{_storageType-6k8lUnQo.mjs.map → _storageType-g1zkFFRh.mjs.map} +1 -1
  55. package/dist/client/_storageType-zeSZe--V.mjs.map +1 -1
  56. package/dist/client/{about-Nsxkyh9U.mjs → about-B4sGnxeW.mjs} +8 -8
  57. package/dist/client/about-B4sGnxeW.mjs.map +1 -0
  58. package/dist/client/{aurora-DDzsst74.mjs → aurora-6RsAZtnz.mjs} +2 -2
  59. package/dist/client/aurora-6RsAZtnz.mjs.map +1 -0
  60. package/dist/client/{build-BdRRmNf5.mjs → build-DgeVmENN.mjs} +3632 -3159
  61. package/dist/client/{build-BdRRmNf5.mjs.map → build-DgeVmENN.mjs.map} +1 -1
  62. package/dist/client/{constants-PMXUGI4Q.mjs → constants-D6IO9bPs.mjs} +17 -17
  63. package/dist/client/{constants-PMXUGI4Q.mjs.map → constants-D6IO9bPs.mjs.map} +1 -1
  64. package/dist/client/{flavors-p2TKcqP-.mjs → flavors-CO5l_6d0.mjs} +164 -164
  65. package/dist/client/{flavors-p2TKcqP-.mjs.map → flavors-CO5l_6d0.mjs.map} +1 -1
  66. package/dist/client/{flavors-BclEwobO.mjs → flavors-D4nGSs0r.mjs} +2 -2
  67. package/dist/client/{flavors-BclEwobO.mjs.map → flavors-D4nGSs0r.mjs.map} +1 -1
  68. package/dist/client/flavors-DWMZ6TuJ.mjs.map +1 -1
  69. package/dist/client/{floatingips-ph0ZxJw8.mjs → floatingips-ClkRV7a7.mjs} +115 -115
  70. package/dist/client/floatingips-ClkRV7a7.mjs.map +1 -0
  71. package/dist/client/{hooks-dSArr2Ca.mjs → hooks-CMgoYcDG.mjs} +1 -1
  72. package/dist/client/{images-Dbjo4yKn.mjs → images-B5ktfqg6.mjs} +2 -2
  73. package/dist/client/images-B5ktfqg6.mjs.map +1 -0
  74. package/dist/client/{images-BblnyWJq.mjs → images-C_OYLPqv.mjs} +425 -425
  75. package/dist/client/{images-BblnyWJq.mjs.map → images-C_OYLPqv.mjs.map} +1 -1
  76. package/dist/client/{images-CXdghaMW.mjs → images-CvPxoPC0.mjs} +2 -2
  77. package/dist/client/{images-CXdghaMW.mjs.map → images-CvPxoPC0.mjs.map} +1 -1
  78. package/dist/client/images-NBf2bV43.mjs.map +1 -1
  79. package/dist/client/index.js +337 -337
  80. package/dist/client/index.js.map +1 -1
  81. package/dist/client/network-DuZm76BZ.mjs.map +1 -1
  82. package/dist/client/{objects-Bw25cE1m.mjs → objects-D0grS6pk.mjs} +1645 -1617
  83. package/dist/client/objects-D0grS6pk.mjs.map +1 -0
  84. package/dist/client/{objects-B9Jh3SMG.mjs → objects-UQyUQxBj.mjs} +2 -2
  85. package/dist/client/{objects-B9Jh3SMG.mjs.map → objects-UQyUQxBj.mjs.map} +1 -1
  86. package/dist/client/objects-o2Cj_ndZ.mjs.map +1 -1
  87. package/dist/client/{pca-CiLPHmK7.mjs → pca-BJPxOIgq.mjs} +68 -68
  88. package/dist/client/{pca-CiLPHmK7.mjs.map → pca-BJPxOIgq.mjs.map} +1 -1
  89. package/dist/client/{pca-DUrQ_tIg.mjs → pca-C9MKOi3Z.mjs} +2 -2
  90. package/dist/client/{pca-DUrQ_tIg.mjs.map → pca-C9MKOi3Z.mjs.map} +1 -1
  91. package/dist/client/{projects-DNd3UTas.mjs → projects-C3wR-lvM.mjs} +6 -9
  92. package/dist/client/projects-C3wR-lvM.mjs.map +1 -0
  93. package/dist/client/{projects-B5topuei.mjs → projects-CGjBlG2w.mjs} +2 -2
  94. package/dist/client/{projects-B5topuei.mjs.map → projects-CGjBlG2w.mjs.map} +1 -1
  95. package/dist/client/{projects-CHYn7L5e.mjs → projects-DvSiI5Vg.mjs} +2 -2
  96. package/dist/client/projects-DvSiI5Vg.mjs.map +1 -0
  97. package/dist/client/{securitygroups-CcA2TpAt.mjs → securitygroups-n8ujBpJP.mjs} +119 -119
  98. package/dist/client/securitygroups-n8ujBpJP.mjs.map +1 -0
  99. package/dist/client/{useListWithFiltering-CVzhMyEA.mjs → useListWithFiltering-BuhE81I6.mjs} +12 -12
  100. package/dist/client/{useListWithFiltering-CVzhMyEA.mjs.map → useListWithFiltering-BuhE81I6.mjs.map} +1 -1
  101. package/dist/server/index.d.ts +6 -2
  102. package/dist/server/index.js +701 -423
  103. package/package.json +4 -4
  104. package/dist/client/AuthProvider-Co4d0WzB.mjs +0 -100
  105. package/dist/client/AuthProvider-Co4d0WzB.mjs.map +0 -1
  106. package/dist/client/ContentHeader-D4jlOG-9.mjs +0 -96
  107. package/dist/client/ContentHeader-D4jlOG-9.mjs.map +0 -1
  108. package/dist/client/_auth-DXJkv9QO.mjs.map +0 -1
  109. package/dist/client/_imageId-BjfhqAje.mjs.map +0 -1
  110. package/dist/client/_projectId-B_2sZKk-.mjs.map +0 -1
  111. package/dist/client/_projectId-CARHuZTU.mjs.map +0 -1
  112. package/dist/client/_projectId-Dbck_MFa.mjs.map +0 -1
  113. package/dist/client/_storageType-CLTxXjQZ.mjs +0 -3048
  114. package/dist/client/_storageType-CLTxXjQZ.mjs.map +0 -1
  115. package/dist/client/about-Nsxkyh9U.mjs.map +0 -1
  116. package/dist/client/aurora-DDzsst74.mjs.map +0 -1
  117. package/dist/client/floatingips-ph0ZxJw8.mjs.map +0 -1
  118. package/dist/client/images-Dbjo4yKn.mjs.map +0 -1
  119. package/dist/client/objects-Bw25cE1m.mjs.map +0 -1
  120. package/dist/client/projects-CHYn7L5e.mjs.map +0 -1
  121. package/dist/client/projects-DNd3UTas.mjs.map +0 -1
  122. package/dist/client/securitygroups-CcA2TpAt.mjs.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cobaltcore-dev/aurora",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "private": false,
5
5
  "description": "Aurora OpenStack dashboard — server and client library",
6
6
  "license": "Apache-2.0",
@@ -70,7 +70,7 @@
70
70
  "zod": "^4.0.0"
71
71
  },
72
72
  "devDependencies": {
73
- "@cloudoperators/juno-ui-components": "9.0.1",
73
+ "@cloudoperators/juno-ui-components": "9.1.0",
74
74
  "@fastify/vite": "^8.4.1",
75
75
  "@lingui/cli": "^5.9.5",
76
76
  "@lingui/format-po": "^5.9.5",
@@ -108,9 +108,9 @@
108
108
  "vite-plugin-svgr": "^5.0.0",
109
109
  "vite-tsconfig-paths": "^6.1.1",
110
110
  "vitest": "^4.1.2",
111
+ "@cobaltcore-dev/aurora-config": "0.0.1",
111
112
  "@cobaltcore-dev/policy-engine": "2.0.0",
112
- "@cobaltcore-dev/signal-openstack": "1.0.0",
113
- "@cobaltcore-dev/aurora-config": "0.0.1"
113
+ "@cobaltcore-dev/signal-openstack": "1.0.0"
114
114
  },
115
115
  "scripts": {
116
116
  "build": "pnpm build:server && pnpm build:client",
@@ -1,100 +0,0 @@
1
- import { jsx as e } from "react/jsx-runtime";
2
- import t, { useCallback as n, useEffect as r, useRef as i, useState as a } from "react";
3
- import { useRouter as o } from "@tanstack/react-router";
4
- //#region src/client/store/AuthProvider.tsx
5
- var s = /*#__PURE__*/ t.createContext(null), c = 3600 * 1e3;
6
- function l({ children: l }) {
7
- let u = o(), [d, f] = t.useState(null), [p, m] = t.useState(void 0), [h, g] = t.useState(void 0), [_, v] = a(!1), [y, b] = a(void 0), x = i(null), S = i(null), C = !!d, w = n(() => {
8
- x.current &&= (clearTimeout(x.current), null);
9
- }, []), T = n(() => {
10
- S.current &&= (clearTimeout(S.current), null);
11
- }, []), E = n(() => {
12
- v(!1), u.navigate({
13
- to: "/",
14
- search: y ? { redirect: y } : void 0
15
- });
16
- }, [y]), D = n(async (e = "manual") => {
17
- if (w(), T(), f(null), m(void 0), g(e), e === "inactive" || e === "expired") {
18
- let e = window.location.pathname + window.location.search;
19
- e && e.startsWith("/") && b(e), v(!0);
20
- } else u.invalidate();
21
- }, [w, T]), O = n(() => {
22
- C && (T(), S.current = setTimeout(() => {
23
- D("inactive");
24
- }, c));
25
- }, [
26
- C,
27
- T,
28
- D
29
- ]), k = n(async (e, t) => {
30
- f(e), g(void 0), v(!1), b(void 0), m(t ? new Date(t) : void 0), O();
31
- }, [O]);
32
- return r(() => {
33
- if (w(), d && p) {
34
- let e = p.getTime() - Date.now();
35
- if (e <= 0) {
36
- D("expired");
37
- return;
38
- }
39
- x.current = setTimeout(() => {
40
- D("expired");
41
- }, e);
42
- }
43
- return () => {
44
- w();
45
- };
46
- }, [
47
- d,
48
- p,
49
- D,
50
- w
51
- ]), r(() => {
52
- if (!C) {
53
- T();
54
- return;
55
- }
56
- let e = [
57
- "mousedown",
58
- "mousemove",
59
- "keypress",
60
- "scroll",
61
- "touchstart",
62
- "click"
63
- ], t = () => {
64
- O();
65
- };
66
- return O(), e.forEach((e) => {
67
- document.addEventListener(e, t, !0);
68
- }), () => {
69
- T(), e.forEach((e) => {
70
- document.removeEventListener(e, t, !0);
71
- });
72
- };
73
- }, [
74
- C,
75
- O,
76
- T
77
- ]), /*#__PURE__*/ e(s.Provider, {
78
- value: {
79
- isAuthenticated: C,
80
- user: d,
81
- login: k,
82
- logout: D,
83
- expiresAt: p,
84
- logoutReason: h,
85
- showInactivityModal: _,
86
- closeInactivityModal: E,
87
- redirectAfterModal: y
88
- },
89
- children: l
90
- });
91
- }
92
- function u() {
93
- let e = t.useContext(s);
94
- if (!e) throw Error("useAuth must be used within an AuthProvider");
95
- return e;
96
- }
97
- //#endregion
98
- export { u as n, l as t };
99
-
100
- //# sourceMappingURL=AuthProvider-Co4d0WzB.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AuthProvider-Co4d0WzB.mjs","names":["React","useEffect","useRef","useCallback","useState","useRouter","AuthContext","createContext","INACTIVITY_TIMEOUT","AuthProvider","children","router","user","setUser","expiresAt","setExpiresAt","undefined","logoutReason","setLogoutReason","showInactivityModal","setShowInactivityModal","redirectAfterModal","setRedirectAfterModal","logoutTimerRef","inactivityTimerRef","isAuthenticated","clearLogoutTimer","current","clearTimeout","clearInactivityTimer","closeInactivityModal","navigate","to","search","redirect","logout","reason","currentPath","window","location","pathname","startsWith","invalidate","resetInactivityTimer","setTimeout","login","expires_at","expiration","Date","timeUntilExpiry","getTime","now","events","handleActivity","forEach","event","document","addEventListener","removeEventListener","Provider","value","useAuth","context","useContext","Error"],"sources":["../../src/client/store/AuthProvider.tsx"],"sourcesContent":["import React, { useEffect, useRef, useCallback, useState } from \"react\"\nimport { TokenData } from \"../../server/Authentication/types/models\"\nimport { useRouter } from \"@tanstack/react-router\"\n\nexport type User = TokenData[\"user\"] | null\n\nexport interface AuthContext {\n isAuthenticated: boolean\n login: (user: User, expires_at?: string) => Promise<void>\n logout: (reason?: \"inactive\" | \"expired\" | \"manual\") => Promise<void>\n user?: User\n expiresAt?: Date\n logoutReason?: \"inactive\" | \"expired\" | \"manual\"\n showInactivityModal: boolean\n closeInactivityModal: () => void\n redirectAfterModal?: string\n}\n\nconst AuthContext = React.createContext<AuthContext | null>(null)\n\nconst INACTIVITY_TIMEOUT = 60 * 60 * 1000 // 60 minutes\n\nexport function AuthProvider({ children }: { children: React.ReactNode }) {\n const router = useRouter()\n const [user, setUser] = React.useState<User | null>(null)\n const [expiresAt, setExpiresAt] = React.useState<Date | undefined>(undefined)\n const [logoutReason, setLogoutReason] = React.useState<\"inactive\" | \"expired\" | \"manual\" | undefined>(undefined)\n const [showInactivityModal, setShowInactivityModal] = useState(false)\n const [redirectAfterModal, setRedirectAfterModal] = useState<string | undefined>(undefined)\n\n const logoutTimerRef = useRef<NodeJS.Timeout | null>(null)\n const inactivityTimerRef = useRef<NodeJS.Timeout | null>(null)\n\n const isAuthenticated = !!user\n\n const clearLogoutTimer = useCallback(() => {\n if (logoutTimerRef.current) {\n clearTimeout(logoutTimerRef.current)\n logoutTimerRef.current = null\n }\n }, [])\n\n const clearInactivityTimer = useCallback(() => {\n if (inactivityTimerRef.current) {\n clearTimeout(inactivityTimerRef.current)\n inactivityTimerRef.current = null\n }\n }, [])\n\n const closeInactivityModal = useCallback(() => {\n setShowInactivityModal(false)\n\n router.navigate({\n to: \"/\",\n search: redirectAfterModal ? { redirect: redirectAfterModal } : undefined,\n })\n }, [redirectAfterModal])\n\n const logout = useCallback(\n async (reason: \"inactive\" | \"expired\" | \"manual\" = \"manual\") => {\n clearLogoutTimer()\n clearInactivityTimer()\n\n setUser(null)\n setExpiresAt(undefined)\n setLogoutReason(reason)\n\n // For inactive/expired: Show modal instead of direct navigation\n if (reason === \"inactive\" || reason === \"expired\") {\n const currentPath = window.location.pathname + window.location.search\n if (currentPath && currentPath.startsWith(\"/\")) {\n setRedirectAfterModal(currentPath)\n }\n\n setShowInactivityModal(true)\n } else {\n // Manual logout: direct navigation\n router.invalidate()\n }\n },\n [clearLogoutTimer, clearInactivityTimer]\n )\n\n const resetInactivityTimer = useCallback(() => {\n if (!isAuthenticated) return\n\n clearInactivityTimer()\n\n inactivityTimerRef.current = setTimeout(() => {\n logout(\"inactive\")\n }, INACTIVITY_TIMEOUT)\n }, [isAuthenticated, clearInactivityTimer, logout])\n\n const login = useCallback(\n async (user: User, expires_at?: string) => {\n setUser(user)\n setLogoutReason(undefined)\n setShowInactivityModal(false)\n setRedirectAfterModal(undefined)\n\n if (expires_at) {\n const expiration = new Date(expires_at)\n setExpiresAt(expiration)\n } else {\n setExpiresAt(undefined)\n }\n\n resetInactivityTimer()\n },\n [resetInactivityTimer]\n )\n\n useEffect(() => {\n clearLogoutTimer()\n\n if (user && expiresAt) {\n const timeUntilExpiry = expiresAt.getTime() - Date.now()\n\n if (timeUntilExpiry <= 0) {\n logout(\"expired\")\n return\n }\n\n logoutTimerRef.current = setTimeout(() => {\n logout(\"expired\")\n }, timeUntilExpiry)\n }\n\n return () => {\n clearLogoutTimer()\n }\n }, [user, expiresAt, logout, clearLogoutTimer])\n\n useEffect(() => {\n if (!isAuthenticated) {\n clearInactivityTimer()\n return\n }\n\n const events = [\"mousedown\", \"mousemove\", \"keypress\", \"scroll\", \"touchstart\", \"click\"]\n\n const handleActivity = () => {\n resetInactivityTimer()\n }\n\n resetInactivityTimer()\n\n events.forEach((event) => {\n document.addEventListener(event, handleActivity, true)\n })\n\n return () => {\n clearInactivityTimer()\n events.forEach((event) => {\n document.removeEventListener(event, handleActivity, true)\n })\n }\n }, [isAuthenticated, resetInactivityTimer, clearInactivityTimer])\n\n return (\n <AuthContext.Provider\n value={{\n isAuthenticated,\n user,\n login,\n logout,\n expiresAt,\n logoutReason,\n showInactivityModal,\n closeInactivityModal,\n redirectAfterModal,\n }}\n >\n {children}\n </AuthContext.Provider>\n )\n}\n\nexport function useAuth() {\n const context = React.useContext(AuthContext)\n if (!context) {\n throw new Error(\"useAuth must be used within an AuthProvider\")\n }\n return context\n}\n"],"mappings":";;;;AAkBA,IAAMM,IAAcN,gBAAMO,cAAkC,IAAA,GAEtDC,IAAqB,OAAU;AAErC,SAAgBC,EAAa,EAAEC,eAAyC;CACtE,IAAMC,IAASN,EAAAA,GACT,CAACO,GAAMC,KAAWb,EAAMI,SAAsB,IAAA,GAC9C,CAACU,GAAWC,KAAgBf,EAAMI,SAA2BY,KAAAA,CAAAA,GAC7D,CAACC,GAAcC,KAAmBlB,EAAMI,SAAwDY,KAAAA,CAAAA,GAChG,CAACG,GAAqBC,KAA0BhB,EAAS,EAAA,GACzD,CAACiB,GAAoBC,KAAyBlB,EAA6BY,KAAAA,CAAAA,GAE3EO,IAAiBrB,EAA8B,IAAA,GAC/CsB,IAAqBtB,EAA8B,IAAA,GAEnDuB,IAAkB,CAAC,CAACb,GAEpBc,IAAmBvB,QAAY;EACnC,AAEEoB,EAAeI,aADfC,aAAaL,EAAeI,OAAO,GACV;CAE7B,GAAG,CAAA,CAAE,GAECE,IAAuB1B,QAAY;EACvC,AAEEqB,EAAmBG,aADnBC,aAAaJ,EAAmBG,OAAO,GACV;CAEjC,GAAG,CAAA,CAAE,GAECG,IAAuB3B,QAAY;EAGvCQ,AAFAS,EAAuB,EAAA,GAEvBT,EAAOoB,SAAS;GACdC,IAAI;GACJC,QAAQZ,IAAqB,EAAEa,UAAUb,EAAmB,IAAIL,KAAAA;EAClE,CAAA;CACF,GAAG,CAACK,CAAAA,CAAmB,GAEjBc,IAAShC,EACb,OAAOiC,IAA4C,aAAQ;EASzD,IARAV,EAAAA,GACAG,EAAAA,GAEAhB,EAAQ,IAAA,GACRE,EAAaC,KAAAA,CAAAA,GACbE,EAAgBkB,CAAAA,GAGZA,MAAW,cAAcA,MAAW,WAAW;GACjD,IAAMC,IAAcC,OAAOC,SAASC,WAAWF,OAAOC,SAASN;GAK/Db,AAJIiB,KAAeA,EAAYI,WAAW,GAAA,KACxCnB,EAAsBe,CAAAA,GAGxBjB,EAAuB,EAAA;EACzB,OAEET,EAAO+B,WAAU;CAErB,GACA,CAAChB,GAAkBG,CAAAA,CAAqB,GAGpCc,IAAuBxC,QAAY;EAClCsB,MAELI,EAAAA,GAEAL,EAAmBG,UAAUiB,iBAAW;GACtCT,EAAO,UAAA;EACT,GAAG3B,CAAAA;CACL,GAAG;EAACiB;EAAiBI;EAAsBM;EAAO,GAE5CU,IAAQ1C,EACZ,OAAOS,GAAYkC,MAAAA;EAajBH,AAZA9B,EAAQD,CAAAA,GACRM,EAAgBF,KAAAA,CAAAA,GAChBI,EAAuB,EAAA,GACvBE,EAAsBN,KAAAA,CAAAA,GAIpBD,EAFE+B,IAEWC,IADUC,KAAKF,CACfC,IAEA/B,KAAAA,CAAAA,GAGf2B,EAAAA;CACF,GACA,CAACA,CAAAA,CAAqB;CAkDxB,OA/CA1C,QAAU;EAGR,IAFAyB,EAAAA,GAEId,KAAQE,GAAW;GACrB,IAAMmC,IAAkBnC,EAAUoC,QAAO,IAAKF,KAAKG,IAAG;GAEtD,IAAIF,KAAmB,GAAG;IACxBd,EAAO,SAAA;IACP;GACF;GAEAZ,EAAeI,UAAUiB,iBAAW;IAClCT,EAAO,SAAA;GACT,GAAGc,CAAAA;EACL;EAEA,aAAO;GACLvB,EAAAA;EACF;CACF,GAAG;EAACd;EAAME;EAAWqB;EAAQT;EAAiB,GAE9CzB,QAAU;EACR,IAAI,CAACwB,GAAiB;GACpBI,EAAAA;GACA;EACF;EAEA,IAAMuB,IAAS;GAAC;GAAa;GAAa;GAAY;GAAU;GAAc;KAExEC,UAAiB;GACrBV,EAAAA;EACF;EAQA,OANAA,EAAAA,GAEAS,EAAOE,SAASC,MAAAA;GACdC,SAASC,iBAAiBF,GAAOF,GAAgB,EAAA;EACnD,CAAA,SAEO;GAELD,AADAvB,EAAAA,GACAuB,EAAOE,SAASC,MAAAA;IACdC,SAASE,oBAAoBH,GAAOF,GAAgB,EAAA;GACtD,CAAA;EACF;CACF,GAAG;EAAC5B;EAAiBkB;EAAsBd;EAAqB,GAG9D,gBAACvB,EAAYqD,UAAQ;EACnBC,OAAO;GACLnC;GACAb;GACAiC;GACAV;GACArB;GACAG;GACAE;GACAW;GACAT;EACF;EAECX;;AAGP;AAEA,SAAgBmD,IAAAA;CACd,IAAMC,IAAU9D,EAAM+D,WAAWzD,CAAAA;CACjC,IAAI,CAACwD,GACH,MAAUE,MAAM,6CAAA;CAElB,OAAOF;AACT"}
@@ -1,96 +0,0 @@
1
- import { A as e, C as t, Q as n, Y as r, c as i, o as a, st as o } from "./build-BdRRmNf5.mjs";
2
- import { t as s } from "./Slot-CWb612-_.mjs";
3
- import { t as c } from "./routeInfo-Dy9l-wFB.mjs";
4
- import { jsx as l, jsxs as u } from "react/jsx-runtime";
5
- import { useState as d } from "react";
6
- import { useMatches as f, useParams as p, useRouteContext as m } from "@tanstack/react-router";
7
- import { Trans as h, useLingui as g } from "@lingui/react";
8
- //#region src/client/components/ClipboardText.tsx
9
- var _ = ({ text: n, tooltipContent: o, className: s, truncateAt: c, showTooltip: f = !0, ...p }) => {
10
- let { i18n: m, _: h } = g(), [_, v] = d(!1), [y, b] = d(!1), x = `copyableTooltip inline-flex items-center ${s || ""}`, S = async (e) => {
11
- e.preventDefault(), e.stopPropagation();
12
- try {
13
- await navigator.clipboard.writeText(n), v(!0), b(!1), setTimeout(() => v(!1), 2e3);
14
- } catch (e) {
15
- console.error("Failed to copy text:", e);
16
- }
17
- }, C = c && n.length > c ? `${n.slice(0, c)}...` : n, w = () => _ ? o || m._({ id: "u+VWhB" }) : m._({ id: "he3ygx" }), T = _ && f || y && f;
18
- return /*#__PURE__*/ l("div", {
19
- ...p,
20
- className: x,
21
- children: /*#__PURE__*/ u(e, {
22
- open: T,
23
- children: [/*#__PURE__*/ l(a, {
24
- onClick: S,
25
- onMouseEnter: () => !_ && b(!0),
26
- onMouseLeave: () => b(!1),
27
- "aria-label": m._({
28
- id: "Wbg1jv",
29
- values: { text: n }
30
- }),
31
- className: "cursor-pointer",
32
- asChild: !0,
33
- "data-testid": "clipboard-copy-trigger",
34
- children: /*#__PURE__*/ l("div", {
35
- className: "group",
36
- children: /*#__PURE__*/ u(r, {
37
- direction: "horizontal",
38
- gap: "1",
39
- className: "items-center hover:underline",
40
- children: [/*#__PURE__*/ l("span", {
41
- className: "select-none",
42
- children: C
43
- }), /*#__PURE__*/ l(i, {
44
- icon: _ ? "check" : "contentCopy",
45
- size: "18"
46
- })]
47
- })
48
- })
49
- }), /*#__PURE__*/ l(t, { children: w() })]
50
- })
51
- });
52
- };
53
- //#endregion
54
- //#region src/client/components/ContentHeader/ContentHeader.tsx
55
- function v({ title: e, projectId: t, description: r, actions: i }) {
56
- let { slots: a } = m({ strict: !1 }), d = f(), { provider: g } = p({ strict: !1 }), v = [...d].reverse().find((e) => c(e.staticData)), y = v && c(v.staticData) ? v.staticData.service : void 0, b = y === "containers" && g === "ceph" ? "ceph-containers" : y, x = a?.servicePageActions ? /*#__PURE__*/ l(s, {
57
- component: a.servicePageActions,
58
- useShadowDOM: !1,
59
- currentService: b
60
- }) : null;
61
- return /*#__PURE__*/ u("header", { children: [
62
- /*#__PURE__*/ u("div", {
63
- className: "flex flex-col sm:flex-row sm:items-center sm:justify-between",
64
- children: [/*#__PURE__*/ u("div", {
65
- className: "flex items-center gap-3",
66
- children: [/*#__PURE__*/ l(n, { children: e }), x]
67
- }), /*#__PURE__*/ u("div", {
68
- className: "text-theme-light flex shrink-0 items-center gap-1 text-sm",
69
- children: [/*#__PURE__*/ u("span", {
70
- className: "font-semibold",
71
- children: [
72
- /*#__PURE__*/ l(h, { id: "mSfwLL" }),
73
- ":",
74
- " "
75
- ]
76
- }), /*#__PURE__*/ l(_, {
77
- text: t,
78
- truncateAt: 15
79
- })]
80
- })]
81
- }),
82
- r && /*#__PURE__*/ l("p", {
83
- className: "text-sm font-normal",
84
- children: r
85
- }),
86
- /*#__PURE__*/ l(o, { className: "mt-4" }),
87
- i && /*#__PURE__*/ l("div", {
88
- className: "mt-3 flex justify-end",
89
- children: i
90
- })
91
- ] });
92
- }
93
- //#endregion
94
- export { _ as n, v as t };
95
-
96
- //# sourceMappingURL=ContentHeader-D4jlOG-9.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ContentHeader-D4jlOG-9.mjs","names":["React","useState","Tooltip","TooltipTrigger","TooltipContent","Icon","Stack","ClipboardText","text","tooltipContent","className","truncateAt","showTooltip","props","useLingui","copied","setCopied","isHovering","setIsHovering","combinedClassName","handleCopy","e","preventDefault","stopPropagation","navigator","clipboard","writeText","setTimeout","err","console","error","displayText","length","slice","getTooltipContent","t","tooltipIsOpen","div","open","onClick","onMouseEnter","onMouseLeave","aria-label","asChild","data-testid","direction","gap","span","icon","size","Divider","ContentHeading","useRouteContext","useMatches","useParams","ClipboardText","Slot","isRouteInfo","ContentHeader","title","projectId","description","actions","slots","strict","matches","provider","activeMatch","reverse","find","m","staticData","routeService","service","undefined","currentService","slotActions","servicePageActions","component","useShadowDOM","header","div","className","span","text","truncateAt","p"],"sources":["../../src/client/components/ClipboardText.tsx","../../src/client/components/ContentHeader/ContentHeader.tsx"],"sourcesContent":["import React, { useState } from \"react\"\nimport { Tooltip, TooltipTrigger, TooltipContent, Icon, Stack } from \"@cloudoperators/juno-ui-components\"\nimport { useLingui } from \"@lingui/react/macro\"\n\nexport interface ClipboardTextProps extends React.HTMLAttributes<HTMLDivElement> {\n tooltipContent?: string\n text: string\n className?: string\n truncateAt?: number\n showTooltip?: boolean\n}\n\nconst ClipboardText: React.FC<ClipboardTextProps> = ({\n text,\n tooltipContent,\n className,\n truncateAt,\n showTooltip = true,\n ...props\n}) => {\n const { t } = useLingui()\n const [copied, setCopied] = useState(false)\n const [isHovering, setIsHovering] = useState(false)\n\n const combinedClassName = `copyableTooltip inline-flex items-center ${className || \"\"}`\n\n const handleCopy = async (e: React.MouseEvent) => {\n e.preventDefault()\n e.stopPropagation()\n\n try {\n await navigator.clipboard.writeText(text)\n setCopied(true)\n setIsHovering(false)\n setTimeout(() => setCopied(false), 2000)\n } catch (err) {\n console.error(\"Failed to copy text:\", err)\n }\n }\n\n const displayText = truncateAt && text.length > truncateAt ? `${text.slice(0, truncateAt)}...` : text\n\n // Determine tooltip content based on state\n const getTooltipContent = () => {\n if (copied) {\n return tooltipContent || t`Copied to clipboard!`\n }\n return t`Copy`\n }\n\n const tooltipIsOpen = (copied && showTooltip) || (isHovering && showTooltip)\n\n return (\n <div {...props} className={combinedClassName}>\n <Tooltip open={tooltipIsOpen}>\n <TooltipTrigger\n onClick={handleCopy}\n onMouseEnter={() => !copied && setIsHovering(true)}\n onMouseLeave={() => setIsHovering(false)}\n aria-label={t`Copy ${text} to clipboard`}\n className=\"cursor-pointer\"\n asChild\n data-testid=\"clipboard-copy-trigger\"\n >\n <div className=\"group\">\n <Stack direction=\"horizontal\" gap=\"1\" className=\"items-center hover:underline\">\n <span className=\"select-none\">{displayText}</span>\n <Icon icon={copied ? \"check\" : \"contentCopy\"} size=\"18\" />\n </Stack>\n </div>\n </TooltipTrigger>\n <TooltipContent>{getTooltipContent()}</TooltipContent>\n </Tooltip>\n </div>\n )\n}\n\nexport default ClipboardText\n","import type { ReactNode } from \"react\"\nimport { Divider, ContentHeading } from \"@cloudoperators/juno-ui-components\"\nimport { Trans } from \"@lingui/react/macro\"\nimport { useRouteContext, useMatches, useParams } from \"@tanstack/react-router\"\nimport ClipboardText from \"../ClipboardText\"\nimport { Slot } from \"../Slot\"\nimport { isRouteInfo } from \"@/client/routes/routeInfo\"\n\ninterface ContentHeaderProps {\n title: string\n projectId: string\n description?: string | null\n actions?: ReactNode\n}\n\nexport function ContentHeader({ title, projectId, description, actions }: ContentHeaderProps) {\n const { slots } = useRouteContext({ strict: false })\n const matches = useMatches()\n const { provider } = useParams({ strict: false }) as { provider?: string }\n\n const activeMatch = [...matches].reverse().find((m) => isRouteInfo(m.staticData))\n const routeService = activeMatch && isRouteInfo(activeMatch.staticData) ? activeMatch.staticData.service : undefined\n\n // Storage routes share service: \"containers\" for both Swift and Ceph.\n // Distinguish them by the $provider param.\n const currentService = routeService === \"containers\" && provider === \"ceph\" ? \"ceph-containers\" : routeService\n\n const slotActions = slots?.servicePageActions ? (\n <Slot component={slots.servicePageActions} useShadowDOM={false} currentService={currentService} />\n ) : null\n\n return (\n <header>\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-3\">\n <ContentHeading>{title}</ContentHeading>\n {slotActions}\n </div>\n <div className=\"text-theme-light flex shrink-0 items-center gap-1 text-sm\">\n <span className=\"font-semibold\">\n <Trans>Project ID</Trans>:{\" \"}\n </span>\n <ClipboardText text={projectId} truncateAt={15} />\n </div>\n </div>\n {description && <p className=\"text-sm font-normal\">{description}</p>}\n <Divider className=\"mt-4\" />\n {actions && <div className=\"mt-3 flex justify-end\">{actions}</div>}\n </header>\n )\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"_auth-DXJkv9QO.mjs","names":["Outlet","useAuth","RouteComponent","component"],"sources":["../../src/client/routes/_auth.tsx?tsr-split=component"],"sourcesContent":["import { createFileRoute, Outlet, redirect } from \"@tanstack/react-router\"\nimport { useAuth } from \"../store/AuthProvider\"\n\nexport const Route = createFileRoute(\"/_auth\")({\n component: RouteComponent,\n beforeLoad: async ({ context, location }) => {\n if (!context.auth?.isAuthenticated) {\n const token = await context.trpcClient?.auth.getCurrentUserSession.query()\n if (!token) {\n const redirectPath = location.pathname ? location.pathname : \"\"\n\n throw redirect({\n to: \"/\",\n search: { redirect: redirectPath },\n })\n }\n context.auth?.login(token.user, token.expires_at)\n }\n },\n})\n\nfunction RouteComponent() {\n useAuth()\n\n return <Outlet />\n}\n"],"mappings":";;;;AAqBA,SAASE,IAAAA;CAGP,OAFAD,EAAAA,GAEO,gBAAC,GAAA,CAAA,CAAA;AACV"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"_imageId-BjfhqAje.mjs","names":["React","useState","DescriptionList","DescriptionTerm","DescriptionDefinition","Container","ContentHeading","Stack","Message","Box","Button","ButtonRow","SizeDisplay","trpcReact","MEMBER_STATUSES","ClipboardText","ImageMembersTable","SharedImageBox","image","myMemberData","canUpdateMember","onStatusChange","isLoading","useLingui","isPending","status","PENDING","isRejected","REJECTED","sharedAt","created_at","Date","toLocaleString","t","updatedAt","updated_at","ownerProject","owner","p","className","span","ul","li","onClick","ACCEPTED","disabled","GeneralImageData","px","py","alignTerms","text","id","name","size","min_disk","min_ram","disk_format","container_format","toLocaleDateString","SecuritySection","currentProjectId","isSharedWithMe","visibility","undefined","protected","checksum","CustomPropertiesSection","knownFields","Set","customProperties","Object","entries","filter","key","has","sort","a","b","localeCompare","hasProperties","length","map","value","Fragment","JSON","stringify","String","getTabClassName","active","base","SharingDetailsTab","permissions","isAddingMember","setIsAddingMember","message","setMessage","data","imageMembers","isMembersLoading","compute","listImageMembers","useQuery","project_id","imageId","enabled","variant","type","onDismiss","canAdd","canCreateMember","canRemove","canDeleteMember","projectId","ImageDetailsView","activeTab","onTabChange","onMemberStatusChange","isMemberStatusChanging","actions","isImageOwner","showTabs","direction","gap","div","button","Button","ButtonRow","Stack","Spinner","PopupMenu","PopupMenuToggle","PopupMenuOptions","PopupMenuItem","Toast","ToastProps","useNavigate","useParams","useSearch","Trans","useLingui","trpcReact","ImageDetailsView","EditImageDetailsModal","EditImageMetadataModal","DeleteImageModal","ActivateImageModal","DeactivateImageModal","IMAGE_STATUSES","IMAGE_VISIBILITY","GlanceImage","MemberStatus","TRPCClientError","InferrableClientTypes","getImageAccessStatusUpdatedToast","getImageAccessStatusErrorToast","useState","ContentHeader","RouteComponent","projectId","imageId","from","tab","navigate","t","data","image","status","error","compute","getImageById","useQuery","project_id","permissionsData","canUser","permission","permissions","canDelete","canUpdate","canCreateMember","canDeleteMember","canUpdateMember","utils","useUtils","editDetailsModalOpen","setEditDetailsModalOpen","editMetadataModalOpen","setEditMetadataModalOpen","deleteModalOpen","setDeleteModalOpen","activateModalOpen","setActivateModalOpen","deactivateModalOpen","setDeactivateModalOpen","toastData","setToastData","updateImageMutation","updateImage","useMutation","onSuccess","updatedImage","setData","listImagesWithPagination","invalidate","deleteImageMutation","deleteImage","deactivateImageMutation","deactivateImage","reactivateImageMutation","reactivateImage","updateImageVisibilityMutation","updateImageVisibility","isSharedWithMe","visibility","SHARED","owner","undefined","myMemberData","getImageMember","memberId","enabled","updateMemberMutation","updateImageMember","listImageMembers","listSharedImagesByMemberStatus","handleMemberStatusChange","newStatus","mutateAsync","onDismiss","errorMessage","message","handleBack","to","params","isLoading","isPending","convertToJsonPatchOperations","updatedProperties","Partial","originalImage","Array","op","path","value","operations","Object","entries","forEach","key","push","propertyExists","handleSaveEdit","Promise","handleDelete","deletedImage","id","handleActivate","img","handleDeactivate","handleUpdateVisibility","newVisibility","isDeactivated","DEACTIVATED","isPrivate","PRIVATE","isMemberAccepted","isImageOwner","canRejectSharedImage","canUpdateOwnImage","canDeleteOwnImage","protected","canManageSharing","hasMoreActions","headerActions","search","String","name","newTab","component"],"sources":["../../src/client/routes/_auth/projects/$projectId/compute/-components/Images/-components/ImageDetailsView.tsx","../../src/client/routes/_auth/projects/$projectId/compute/images/$imageId.tsx?tsr-split=component"],"sourcesContent":["import React, { useState } from \"react\"\nimport {\n DescriptionList,\n DescriptionTerm,\n DescriptionDefinition,\n Container,\n ContentHeading,\n Stack,\n Message,\n Box,\n Button,\n ButtonRow,\n} from \"@cloudoperators/juno-ui-components\"\nimport { Trans, useLingui } from \"@lingui/react/macro\"\nimport { GlanceImage, ImageMember, MemberStatus } from \"@/server/Compute/types/image\"\nimport { SizeDisplay } from \"./SizeDisplay\"\nimport { trpcReact } from \"@/client/trpcClient\"\nimport { MEMBER_STATUSES } from \"../../../-constants/filters\"\nimport ClipboardText from \"@/client/components/ClipboardText\"\nimport { ImageMembersTable } from \"./ImageMembersTable\"\n\ninterface ImageDetailsViewProps {\n image: GlanceImage\n currentProjectId?: string\n activeTab?: \"details\" | \"sharing\"\n onTabChange?: (tab: \"details\" | \"sharing\") => void\n permissions?: {\n canCreateMember: boolean\n canDeleteMember: boolean\n canUpdateMember: boolean\n }\n myMemberData?: ImageMember\n onMemberStatusChange?: (status: MemberStatus) => void\n isMemberStatusChanging?: boolean\n actions?: React.ReactNode\n}\n\nconst SharedImageBox: React.FC<{\n image: GlanceImage\n myMemberData: ImageMember\n canUpdateMember: boolean\n onStatusChange: (status: MemberStatus) => void\n isLoading: boolean\n}> = ({ image, myMemberData, canUpdateMember, onStatusChange, isLoading }) => {\n const { t } = useLingui()\n const isPending = myMemberData.status === MEMBER_STATUSES.PENDING\n const isRejected = myMemberData.status === MEMBER_STATUSES.REJECTED\n\n const sharedAt = myMemberData.created_at ? new Date(myMemberData.created_at).toLocaleString() : t`N/A`\n const updatedAt = myMemberData.updated_at ? new Date(myMemberData.updated_at).toLocaleString() : t`N/A`\n const ownerProject = image.owner ?? \"\"\n\n return (\n <Box>\n {isPending && (\n <p className=\"text-theme-highest font-semibold\">\n <Trans>Your action is required</Trans>\n </p>\n )}\n <p>\n <Trans>\n This image was shared with you by <span className=\"font-semibold\">{ownerProject}</span> on {sharedAt}.\n </Trans>\n </p>\n <ul>\n <li>\n <span className=\"font-semibold\">\n <Trans>Access Status:</Trans>\n </span>{\" \"}\n {myMemberData.status}\n </li>\n <li>\n <span className=\"font-semibold\">\n <Trans>Shared:</Trans>\n </span>{\" \"}\n {sharedAt}\n </li>\n <li>\n <span className=\"font-semibold\">\n <Trans>Updated:</Trans>\n </span>{\" \"}\n {updatedAt}\n </li>\n </ul>\n\n {canUpdateMember && (isPending || isRejected) && (\n <ButtonRow>\n <Button onClick={() => onStatusChange(MEMBER_STATUSES.ACCEPTED)} disabled={isLoading}>\n <Trans>Accept</Trans>\n </Button>\n {isPending && (\n <Button onClick={() => onStatusChange(MEMBER_STATUSES.REJECTED)} disabled={isLoading}>\n <Trans>Reject</Trans>\n </Button>\n )}\n </ButtonRow>\n )}\n </Box>\n )\n}\n\nexport const GeneralImageData: React.FC<{ image: GlanceImage }> = ({ image }) => {\n const { t } = useLingui()\n\n return (\n <Container px={false} py>\n <ContentHeading>{t`General Image Data`}</ContentHeading>\n <DescriptionList alignTerms=\"right\">\n <DescriptionTerm>{t`ID`}</DescriptionTerm>\n <DescriptionDefinition>\n <ClipboardText text={image.id} />\n </DescriptionDefinition>\n\n <DescriptionTerm>{t`Name`}</DescriptionTerm>\n <DescriptionDefinition>{image.name}</DescriptionDefinition>\n\n <DescriptionTerm>{t`Status`}</DescriptionTerm>\n <DescriptionDefinition>{image.status}</DescriptionDefinition>\n\n <DescriptionTerm>{t`Size`}</DescriptionTerm>\n <DescriptionDefinition>\n <SizeDisplay size={image.size} />\n </DescriptionDefinition>\n\n <DescriptionTerm>{t`Min. Disk`}</DescriptionTerm>\n <DescriptionDefinition>{image.min_disk} GB</DescriptionDefinition>\n\n <DescriptionTerm>{t`Min. RAM`}</DescriptionTerm>\n <DescriptionDefinition>{image.min_ram} MB</DescriptionDefinition>\n\n <DescriptionTerm>{t`Disk Format`}</DescriptionTerm>\n <DescriptionDefinition>\n <span className=\"uppercase\">{image.disk_format}</span>\n </DescriptionDefinition>\n\n <DescriptionTerm>{t`Container Format`}</DescriptionTerm>\n <DescriptionDefinition>\n <span className=\"uppercase\">{image.container_format}</span>\n </DescriptionDefinition>\n\n <DescriptionTerm>{t`Created At`}</DescriptionTerm>\n <DescriptionDefinition>\n {image.created_at ? new Date(image.created_at).toLocaleDateString() : t`N/A`}\n </DescriptionDefinition>\n\n <DescriptionTerm>{t`Updated At`}</DescriptionTerm>\n <DescriptionDefinition>\n {image.updated_at ? new Date(image.updated_at).toLocaleDateString() : t`N/A`}\n </DescriptionDefinition>\n </DescriptionList>\n </Container>\n )\n}\n\nexport const SecuritySection: React.FC<{ image: GlanceImage; currentProjectId?: string }> = ({\n image,\n currentProjectId,\n}) => {\n const { t } = useLingui()\n\n const isSharedWithMe = image.visibility === \"shared\" && image.owner !== undefined && image.owner !== currentProjectId\n\n return (\n <Container px={false} py>\n <ContentHeading>{t`Security`}</ContentHeading>\n <DescriptionList alignTerms=\"right\">\n <DescriptionTerm>{isSharedWithMe ? t`Shared by Project` : t`Owner Project ID`}</DescriptionTerm>\n <DescriptionDefinition>{image.owner ? <ClipboardText text={image.owner} /> : \"\"}</DescriptionDefinition>\n\n <DescriptionTerm>{t`Visibility`}</DescriptionTerm>\n <DescriptionDefinition>{image.visibility}</DescriptionDefinition>\n\n <DescriptionTerm>{t`Protected`}</DescriptionTerm>\n <DescriptionDefinition>{image.protected ? t`Yes` : t`No`}</DescriptionDefinition>\n\n <DescriptionTerm>{t`Checksum`}</DescriptionTerm>\n <DescriptionDefinition>{image?.checksum ? image.checksum : \"\"}</DescriptionDefinition>\n </DescriptionList>\n </Container>\n )\n}\n\nexport const CustomPropertiesSection: React.FC<{ image: GlanceImage }> = ({ image }) => {\n const { t } = useLingui()\n\n const knownFields = new Set([\n \"id\",\n \"name\",\n \"status\",\n \"visibility\",\n \"size\",\n \"disk_format\",\n \"container_format\",\n \"min_disk\",\n \"min_ram\",\n \"owner\",\n \"protected\",\n \"created_at\",\n \"updated_at\",\n \"checksum\",\n ])\n\n const customProperties = Object.entries(image)\n .filter(([key]) => !knownFields.has(key))\n .sort(([a], [b]) => a.localeCompare(b))\n\n const hasProperties = customProperties.length > 0\n\n return (\n <Container px={false} py>\n <ContentHeading>{t`Custom Properties / Metadata`}</ContentHeading>\n {hasProperties ? (\n <DescriptionList alignTerms=\"right\" className=\"grid-cols-4\">\n {customProperties.map(([key, value]) => (\n <React.Fragment key={key}>\n <DescriptionTerm className=\"col-span-1\">{key}</DescriptionTerm>\n <DescriptionDefinition className=\"col-span-1\">\n {value === null || value === undefined ? (\n <span>null</span>\n ) : typeof value === \"object\" ? (\n <span className=\"break-all\">{JSON.stringify(value)}</span>\n ) : typeof value === \"boolean\" ? (\n value ? (\n t`True`\n ) : (\n t`False`\n )\n ) : (\n <span className=\"break-all\">{String(value)}</span>\n )}\n </DescriptionDefinition>\n </React.Fragment>\n ))}\n </DescriptionList>\n ) : (\n <p className=\"text-theme-light\">{t`No custom properties defined`}</p>\n )}\n </Container>\n )\n}\n\nconst getTabClassName = (active: boolean) => {\n const base = \"px-6 py-3 font-semibold border-b-2 transition-colors\"\n return active\n ? `${base} border-theme-accent text-theme-highest`\n : `${base} border-transparent text-theme-secondary hover:text-theme-high`\n}\n\nconst SharingDetailsTab: React.FC<ImageDetailsViewProps> = ({ image, permissions, currentProjectId }) => {\n const [isAddingMember, setIsAddingMember] = useState(false)\n const [message, setMessage] = useState<{ text: string; type: \"error\" | \"info\" } | null>(null)\n\n const { data: imageMembers, isLoading: isMembersLoading } = trpcReact.compute.listImageMembers.useQuery(\n { project_id: currentProjectId!, imageId: image.id },\n { enabled: !!image.id && !!currentProjectId }\n )\n\n return (\n <Container px={false} py>\n {message && (\n <Message text={message.text} variant={message.type} onDismiss={() => setMessage(null)} className=\"mb-4\" />\n )}\n <ImageMembersTable\n image={image}\n imageMembers={imageMembers}\n isMembersLoading={isMembersLoading}\n canAdd={permissions?.canCreateMember ?? false}\n canRemove={permissions?.canDeleteMember ?? false}\n isAddingMember={isAddingMember}\n setIsAddingMember={setIsAddingMember}\n setMessage={setMessage}\n projectId={currentProjectId!}\n />\n </Container>\n )\n}\n\nexport const ImageDetailsView: React.FC<ImageDetailsViewProps> = ({\n image,\n currentProjectId,\n activeTab = \"details\",\n onTabChange,\n permissions,\n myMemberData,\n onMemberStatusChange,\n isMemberStatusChanging,\n actions,\n}) => {\n const { t } = useLingui()\n\n const isSharedWithMe = image.visibility === \"shared\" && image.owner !== undefined && image.owner !== currentProjectId\n const isImageOwner = image.owner === currentProjectId\n const showTabs = isImageOwner && image.visibility === \"shared\"\n\n return (\n <Stack direction=\"vertical\" gap=\"6\">\n {isSharedWithMe && myMemberData && onMemberStatusChange && (\n <SharedImageBox\n image={image}\n myMemberData={myMemberData}\n canUpdateMember={permissions?.canUpdateMember ?? false}\n onStatusChange={onMemberStatusChange}\n isLoading={isMemberStatusChanging ?? false}\n />\n )}\n\n {showTabs && (\n <div className=\"border-theme-background-lvl-3 border-b\">\n <Stack direction=\"horizontal\" gap=\"0\">\n <button className={getTabClassName(activeTab === \"details\")} onClick={() => onTabChange?.(\"details\")}>\n {t`Details`}\n </button>\n <button className={getTabClassName(activeTab === \"sharing\")} onClick={() => onTabChange?.(\"sharing\")}>\n {t`Sharing Details`}\n </button>\n </Stack>\n </div>\n )}\n\n {(activeTab === \"details\" || !showTabs) && (\n <>\n {actions}\n <GeneralImageData image={image} />\n <SecuritySection image={image} currentProjectId={currentProjectId} />\n <CustomPropertiesSection image={image} />\n </>\n )}\n\n {activeTab === \"sharing\" && showTabs && <SharingDetailsTab image={image} permissions={permissions} />}\n </Stack>\n )\n}\n","import {\n Button,\n ButtonRow,\n Stack,\n Spinner,\n PopupMenu,\n PopupMenuToggle,\n PopupMenuOptions,\n PopupMenuItem,\n Toast,\n ToastProps,\n} from \"@cloudoperators/juno-ui-components/index\"\nimport { createFileRoute, redirect, useNavigate, useParams, useSearch } from \"@tanstack/react-router\"\nimport { z } from \"zod\"\nimport type { RouteInfo } from \"@/client/routes/routeInfo\"\nimport { Trans, useLingui } from \"@lingui/react/macro\"\nimport { getServiceIndex } from \"@/server/Authentication/helpers\"\nimport { trpcReact } from \"@/client/trpcClient\"\nimport { ImageDetailsView } from \"../-components/Images/-components/ImageDetailsView\"\nimport { EditImageDetailsModal } from \"../-components/Images/-components/EditImageDetailsModal\"\nimport { EditImageMetadataModal } from \"../-components/Images/-components/EditImageMetadataModal\"\nimport { DeleteImageModal } from \"../-components/Images/-components/DeleteImageModal\"\nimport { ActivateImageModal } from \"../-components/Images/-components/ActivateImageModal\"\nimport { DeactivateImageModal } from \"../-components/Images/-components/DeactivateImageModal\"\nimport { IMAGE_STATUSES, IMAGE_VISIBILITY } from \"../-constants/filters\"\nimport { GlanceImage, MemberStatus } from \"@/server/Compute/types/image\"\nimport { TRPCClientError } from \"@trpc/client\"\nimport { InferrableClientTypes } from \"@trpc/server/unstable-core-do-not-import\"\nimport {\n getImageAccessStatusUpdatedToast,\n getImageAccessStatusErrorToast,\n} from \"../-components/Images/-components/ImageToastNotifications\"\nimport { useState } from \"react\"\nimport { ContentHeader } from \"@/client/components/ContentHeader/ContentHeader\"\n\nexport const Route = createFileRoute(\"/_auth/projects/$projectId/compute/images/$imageId\")({\n staticData: {\n section: \"compute\",\n service: \"images\",\n isDetail: true,\n sectionCrumb: { labelKey: \"Compute\" },\n crumb: { labelKey: \"Images\", to: \"/projects/$projectId/compute/images\" },\n } satisfies RouteInfo,\n validateSearch: z.object({\n tab: z.enum([\"details\", \"sharing\"]).optional(),\n }),\n loader: async ({ context, params }) => {\n try {\n const image = await context.trpcClient?.compute.getImageById.query({\n project_id: params.projectId,\n imageId: params.imageId,\n })\n return { imageTitle: (image?.name as string | undefined) ?? image?.id ?? null }\n } catch {\n return { imageTitle: null }\n }\n },\n head: ({ loaderData }) => ({\n meta: [{ title: loaderData?.imageTitle ?? \"Image Details\" }],\n }),\n component: RouteComponent,\n beforeLoad: async ({ context, params }) => {\n const { trpcClient } = context\n const { projectId } = params\n\n const availableServices = (await trpcClient?.auth.getAvailableServices.query()) || []\n\n const serviceIndex = getServiceIndex(availableServices)\n\n // Redirect to the \"Projects Overview\" page if none of compute services available\n if (!serviceIndex[\"image\"] && !serviceIndex[\"compute\"]) {\n throw redirect({\n to: \"/projects/$projectId\",\n params: { projectId },\n })\n }\n\n if (!serviceIndex[\"image\"][\"glance\"]) {\n // Redirect to the \"Compute Services Overview\" page if the \"Glance\" service is not available\n throw redirect({\n to: \"/projects/$projectId\",\n params: { projectId: params.projectId },\n })\n }\n },\n})\n\nfunction RouteComponent() {\n const { projectId, imageId } = useParams({\n from: \"/_auth/projects/$projectId/compute/images/$imageId\",\n })\n const { tab } = useSearch({\n from: \"/_auth/projects/$projectId/compute/images/$imageId\",\n })\n\n const navigate = useNavigate()\n const { t } = useLingui()\n\n const {\n data: image,\n status,\n error,\n } = trpcReact.compute.getImageById.useQuery({ project_id: projectId, imageId: imageId })\n\n const { data: permissionsData } = trpcReact.compute.canUser.useQuery({\n project_id: projectId,\n permission: [\n \"images:delete\",\n \"images:update\",\n \"images:create_member\",\n \"images:delete_member\",\n \"images:update_member\",\n ],\n })\n\n const permissions = {\n canDelete: permissionsData?.[0] ?? false,\n canUpdate: permissionsData?.[1] ?? false,\n canCreateMember: permissionsData?.[2] ?? false,\n canDeleteMember: permissionsData?.[3] ?? false,\n canUpdateMember: permissionsData?.[4] ?? false,\n }\n\n const utils = trpcReact.useUtils()\n\n const [editDetailsModalOpen, setEditDetailsModalOpen] = useState(false)\n const [editMetadataModalOpen, setEditMetadataModalOpen] = useState(false)\n const [deleteModalOpen, setDeleteModalOpen] = useState(false)\n const [activateModalOpen, setActivateModalOpen] = useState(false)\n const [deactivateModalOpen, setDeactivateModalOpen] = useState(false)\n const [toastData, setToastData] = useState<ToastProps | null>(null)\n\n const updateImageMutation = trpcReact.compute.updateImage.useMutation({\n onSuccess: (updatedImage) => {\n utils.compute.getImageById.setData({ project_id: projectId, imageId }, updatedImage)\n utils.compute.listImagesWithPagination.invalidate()\n },\n })\n\n const deleteImageMutation = trpcReact.compute.deleteImage.useMutation({\n onSuccess: () => {\n utils.compute.listImagesWithPagination.invalidate()\n },\n })\n\n const deactivateImageMutation = trpcReact.compute.deactivateImage.useMutation({\n onSuccess: () => {\n utils.compute.getImageById.invalidate({ project_id: projectId, imageId })\n },\n })\n\n const reactivateImageMutation = trpcReact.compute.reactivateImage.useMutation({\n onSuccess: () => {\n utils.compute.getImageById.invalidate({ project_id: projectId, imageId })\n },\n })\n\n const updateImageVisibilityMutation = trpcReact.compute.updateImageVisibility.useMutation({\n onSuccess: (updatedImage) => {\n utils.compute.getImageById.setData({ project_id: projectId, imageId }, updatedImage)\n },\n })\n\n const isSharedWithMe =\n image?.visibility === IMAGE_VISIBILITY.SHARED && image?.owner !== undefined && image?.owner !== projectId\n\n const { data: myMemberData } = trpcReact.compute.getImageMember.useQuery(\n { project_id: projectId, imageId: imageId, memberId: projectId },\n { enabled: isSharedWithMe && !!imageId && !!projectId }\n )\n\n const updateMemberMutation = trpcReact.compute.updateImageMember.useMutation({\n onSuccess: () => {\n utils.compute.getImageMember.invalidate({ project_id: projectId, imageId: imageId, memberId: projectId })\n utils.compute.listImageMembers.invalidate({ project_id: projectId, imageId: imageId })\n utils.compute.listImagesWithPagination.invalidate()\n utils.compute.listSharedImagesByMemberStatus.invalidate()\n },\n })\n\n const handleMemberStatusChange = async (newStatus: MemberStatus) => {\n try {\n await updateMemberMutation.mutateAsync({ project_id: projectId, imageId, memberId: projectId, status: newStatus })\n setToastData(getImageAccessStatusUpdatedToast(newStatus, { onDismiss: () => setToastData(null) }))\n } catch (error) {\n const errorMessage = (error as TRPCClientError<InferrableClientTypes>)?.message\n setToastData(getImageAccessStatusErrorToast(errorMessage, { onDismiss: () => setToastData(null) }))\n }\n }\n\n const handleBack = () => {\n navigate({\n to: \"/projects/$projectId/compute/images\",\n params: { projectId },\n })\n }\n\n const isLoading =\n updateImageMutation.isPending ||\n deleteImageMutation.isPending ||\n deactivateImageMutation.isPending ||\n reactivateImageMutation.isPending ||\n updateImageVisibilityMutation.isPending\n\n const convertToJsonPatchOperations = (\n updatedProperties: Partial<GlanceImage>,\n originalImage: GlanceImage\n ): Array<{ op: \"add\" | \"replace\" | \"remove\"; path: string; value?: unknown }> => {\n const operations: Array<{ op: \"add\" | \"replace\" | \"remove\"; path: string; value?: unknown }> = []\n Object.entries(updatedProperties).forEach(([key, value]) => {\n const path = `/${key}`\n if (value === null || value === undefined) {\n if (key in originalImage) operations.push({ op: \"remove\", path })\n } else {\n const propertyExists = key in originalImage\n operations.push({ op: propertyExists ? \"replace\" : \"add\", path, value })\n }\n })\n return operations\n }\n\n const handleSaveEdit = async (updatedProperties: Partial<GlanceImage>): Promise<boolean> => {\n if (!image) return false\n const operations = convertToJsonPatchOperations(updatedProperties, image)\n try {\n await updateImageMutation.mutateAsync({ project_id: projectId, imageId, operations })\n setEditDetailsModalOpen(false)\n return true\n } catch {\n return false\n }\n }\n\n const handleDelete = async (deletedImage: GlanceImage) => {\n try {\n await deleteImageMutation.mutateAsync({ project_id: projectId, imageId: deletedImage.id })\n setDeleteModalOpen(false)\n handleBack()\n } catch {\n setDeleteModalOpen(false)\n }\n }\n\n const handleActivate = async (img: GlanceImage) => {\n try {\n await reactivateImageMutation.mutateAsync({ project_id: projectId, imageId: img.id })\n setActivateModalOpen(false)\n } catch {\n setActivateModalOpen(false)\n }\n }\n\n const handleDeactivate = async (img: GlanceImage) => {\n try {\n await deactivateImageMutation.mutateAsync({ project_id: projectId, imageId: img.id })\n setDeactivateModalOpen(false)\n } catch {\n setDeactivateModalOpen(false)\n }\n }\n\n const handleUpdateVisibility = async (newVisibility: \"public\" | \"private\" | \"shared\" | \"community\") => {\n if (!image) return\n try {\n await updateImageVisibilityMutation.mutateAsync({\n project_id: projectId,\n imageId: image.id,\n visibility: newVisibility,\n })\n } catch {\n // error handled by mutation state\n }\n }\n\n // Handle loading state\n if (status === \"pending\") {\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 Image Details...</Trans>\n </Stack>\n )\n }\n\n // Handle error state\n if (status === \"error\") {\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 image</Trans>\n </p>\n <p className=\"text-theme-highest\">{errorMessage}</p>\n <Button onClick={handleBack} variant=\"primary\">\n <Trans>Back to Images</Trans>\n </Button>\n </Stack>\n )\n }\n\n // Handle no data state\n if (!image) {\n return (\n <Stack className=\"fixed inset-0\" distribution=\"center\" alignment=\"center\" direction=\"vertical\" gap=\"5\">\n <p className=\"text-theme-highest\">\n <Trans>Image not found</Trans>\n </p>\n <Button onClick={handleBack} variant=\"primary\">\n <Trans>Back to Images</Trans>\n </Button>\n </Stack>\n )\n }\n\n const isDeactivated = image.status === IMAGE_STATUSES.DEACTIVATED\n const isPrivate = image.visibility === IMAGE_VISIBILITY.PRIVATE\n const isMemberAccepted = myMemberData?.status === \"accepted\"\n const isImageOwner = image.owner === projectId\n\n const canRejectSharedImage = isSharedWithMe && isMemberAccepted && permissions.canUpdateMember\n const canUpdateOwnImage = !isSharedWithMe && permissions.canUpdate\n const canDeleteOwnImage = !isSharedWithMe && permissions.canDelete && !image.protected\n const canManageSharing =\n !isSharedWithMe &&\n isImageOwner &&\n image.visibility === IMAGE_VISIBILITY.SHARED &&\n (permissions.canCreateMember || permissions.canDeleteMember)\n\n const hasMoreActions = canRejectSharedImage || canUpdateOwnImage || canDeleteOwnImage || canManageSharing\n\n const headerActions =\n hasMoreActions || (!isSharedWithMe && permissions.canUpdate) ? (\n <ButtonRow>\n {hasMoreActions && (\n <PopupMenu>\n <PopupMenuToggle as=\"div\">\n <Button icon=\"moreVert\" disabled={isLoading}>\n <Trans>More Actions</Trans>\n </Button>\n </PopupMenuToggle>\n <PopupMenuOptions>\n {canRejectSharedImage && (\n <PopupMenuItem label={t`Reject`} onClick={() => handleMemberStatusChange(\"rejected\")} />\n )}\n {!isSharedWithMe && permissions.canUpdate && (\n <PopupMenuItem\n label={isDeactivated ? t`Activate` : t`Deactivate`}\n onClick={() => (isDeactivated ? setActivateModalOpen(true) : setDeactivateModalOpen(true))}\n />\n )}\n {!isSharedWithMe && permissions.canUpdate && isPrivate && (\n <PopupMenuItem label={t`Set to \"Shared\"`} onClick={() => handleUpdateVisibility(\"shared\")} />\n )}\n {!isSharedWithMe &&\n isImageOwner &&\n image.visibility === IMAGE_VISIBILITY.SHARED &&\n (permissions.canCreateMember || permissions.canDeleteMember) && (\n <PopupMenuItem\n label={t`Manage Access`}\n onClick={() =>\n navigate({\n to: \"/projects/$projectId/compute/images/$imageId\",\n params: { projectId, imageId: image.id },\n search: { tab: \"sharing\" },\n })\n }\n />\n )}\n {!isSharedWithMe && permissions.canDelete && !image.protected && (\n <PopupMenuItem label={t`Delete`} onClick={() => setDeleteModalOpen(true)} />\n )}\n </PopupMenuOptions>\n </PopupMenu>\n )}\n {!isSharedWithMe && permissions.canUpdate && (\n <Button onClick={() => setEditMetadataModalOpen(true)} disabled={isLoading}>\n <Trans>Edit Metadata</Trans>\n </Button>\n )}\n {!isSharedWithMe && permissions.canUpdate && (\n <Button onClick={() => setEditDetailsModalOpen(true)} variant=\"primary\" disabled={isLoading}>\n <Trans>Edit Details</Trans>\n </Button>\n )}\n </ButtonRow>\n ) : undefined\n\n // Render success state\n return (\n <>\n <ContentHeader title={String(image.name ?? image.id)} projectId={projectId} actions={headerActions} />\n <div className=\"mt-3\">\n <ImageDetailsView\n key={image.id}\n image={image}\n currentProjectId={projectId}\n activeTab={tab ?? \"details\"}\n onTabChange={(newTab) =>\n navigate({\n search: { tab: newTab === \"details\" ? undefined : newTab } as unknown as true,\n })\n }\n permissions={{\n canCreateMember: permissions.canCreateMember,\n canDeleteMember: permissions.canDeleteMember,\n canUpdateMember: permissions.canUpdateMember,\n }}\n myMemberData={myMemberData}\n onMemberStatusChange={handleMemberStatusChange}\n isMemberStatusChanging={updateMemberMutation.isPending}\n />\n </div>\n\n {toastData && <Toast {...toastData} />}\n\n {editDetailsModalOpen && (\n <EditImageDetailsModal\n image={image}\n isOpen={editDetailsModalOpen}\n isLoading={updateImageMutation.isPending}\n onClose={() => setEditDetailsModalOpen(false)}\n onSave={handleSaveEdit}\n />\n )}\n\n {editMetadataModalOpen && (\n <EditImageMetadataModal\n image={image}\n isOpen={editMetadataModalOpen}\n isLoading={updateImageMutation.isPending}\n onClose={() => setEditMetadataModalOpen(false)}\n onSave={handleSaveEdit}\n />\n )}\n\n {deleteModalOpen && (\n <DeleteImageModal\n image={image}\n isOpen={deleteModalOpen}\n isLoading={deleteImageMutation.isPending}\n isDisabled={!!image.protected}\n onClose={() => setDeleteModalOpen(false)}\n onDelete={handleDelete}\n />\n )}\n\n {activateModalOpen && (\n <ActivateImageModal\n image={image}\n isOpen={activateModalOpen}\n isLoading={reactivateImageMutation.isPending}\n onClose={() => setActivateModalOpen(false)}\n onActivate={handleActivate}\n />\n )}\n\n {deactivateModalOpen && (\n <DeactivateImageModal\n image={image}\n isOpen={deactivateModalOpen}\n isLoading={deactivateImageMutation.isPending}\n onClose={() => setDeactivateModalOpen(false)}\n onDeactivate={handleDeactivate}\n />\n )}\n </>\n )\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"_projectId-B_2sZKk-.mjs","names":["RouteError","SplitErrorComponent","error","errorComponent"],"sources":["../../src/client/routes/_auth/projects/$projectId.tsx?tsr-split=errorComponent"],"sourcesContent":["import { createFileRoute, Outlet, useLoaderData, useRouteContext } from \"@tanstack/react-router\"\nimport { AppShell, Container, Stack } from \"@cloudoperators/juno-ui-components\"\nimport { SideNavBar } from \"@/client/routes/_auth/projects/-components/SideNavBar\"\nimport { buildNavSections } from \"@/client/routes/_auth/projects/-components/buildNavSections\"\nimport { ProjectInfoBox } from \"@/client/components/ProjectView/ProjectInfoBox\"\nimport { RouteError } from \"@/client/components/Error/RouteError\"\nimport { useMemo } from \"react\"\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\n const [availableServices, projects] = await Promise.all([\n context.trpcClient?.auth.getAvailableServices.query(),\n context.trpcClient?.project.getAuthProjects.query().catch(() => null),\n ])\n\n const accountId = data?.domain?.id || \"\"\n const description = projects?.find((p) => p.id === params.projectId)?.description ?? null\n\n return {\n trpcClient: context.trpcClient,\n crumbDomain: { path: `/projects`, name: data?.domain?.name },\n crumbProject: data?.project,\n availableServices,\n accountId,\n projectId: params.projectId,\n description,\n }\n },\n})\n\nfunction RouteComponent() {\n const { availableServices, projectId, crumbProject, crumbDomain } = useLoaderData({ from: Route.id })\n const { enabledServices } = useRouteContext({ strict: false })\n\n const sections = useMemo(\n () => buildNavSections(projectId, availableServices!, enabledServices),\n [projectId, availableServices, enabledServices]\n )\n\n return (\n <AppShell\n embedded\n sideNavigation={\n <SideNavBar\n sections={sections}\n projectId={projectId}\n projectName={crumbProject?.name || projectId}\n domainName={crumbDomain?.name}\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":";;;AAKiE,IAAAC,KAK9C,EAAEC,eACV,gBAAC,GAAA,EAAkBA,SAAAA,CAAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"_projectId-CARHuZTU.mjs","names":["useLoaderData","useNavigate","Card","Icon","Stack","getServiceIndex","Trans","useLingui","useRouteContext","ContentHeader","Slot","ServiceCardProps","group","label","to","service","ServiceCard","navigate","slots","strict","serviceBadge","RouteComponent","crumbProject","availableServices","projectId","description","from","t","enabledServices","isEnabled","includes","serviceIndex","base","cards","push","name","projectOverviewBanner","length","map","card","component"],"sources":["../../src/client/routes/_auth/projects/$projectId/index.tsx?tsr-split=component"],"sourcesContent":["import { createFileRoute, useLoaderData, useNavigate } from \"@tanstack/react-router\"\nimport { Card, Icon, Stack } from \"@cloudoperators/juno-ui-components\"\nimport { getServiceIndex } from \"@/server/Authentication/helpers\"\nimport { Trans } from \"@lingui/react/macro\"\nimport { useLingui } from \"@lingui/react/macro\"\nimport { useRouteContext } from \"@tanstack/react-router\"\nimport { ContentHeader } from \"@/client/components/ContentHeader/ContentHeader\"\nimport { Slot } from \"@/client/components/Slot\"\nexport const Route = createFileRoute(\"/_auth/projects/$projectId/\")({\n component: RouteComponent,\n})\n\ninterface ServiceCardProps {\n group: string\n label: string\n to: string\n service: string\n}\n\nfunction ServiceCard({ group, label, to, service }: ServiceCardProps) {\n const navigate = useNavigate()\n const { slots } = useRouteContext({ strict: false })\n\n return (\n <Card\n onClick={() => navigate({ to: to as never })}\n className=\"flex min-h-40 flex-col gap-4 px-3 pt-2 pb-3\"\n data-testid=\"service-card\"\n >\n <div className=\"flex min-w-0 flex-col\">\n <div className=\"flex items-center gap-1\">\n <p className=\"text-theme-light text-xs leading-5 font-medium\">{group}</p>\n <Icon icon=\"expandLess\" size=\"16\" className=\"text-theme-high\" />\n </div>\n <div className=\"flex items-center gap-2\">\n <p className=\"text-theme-high text-lg leading-7 font-bold\" data-testid=\"service-card-label\">\n {label}\n </p>\n {slots?.serviceBadge && <Slot component={slots.serviceBadge} useShadowDOM={false} currentService={service} />}\n </div>\n </div>\n <div className=\"flex-1\" />\n </Card>\n )\n}\n\nfunction RouteComponent() {\n const { crumbProject, availableServices, projectId, description } = useLoaderData({\n from: \"/_auth/projects/$projectId\",\n })\n const { t } = useLingui()\n const { slots, enabledServices } = useRouteContext({ strict: false })\n const isEnabled = (service: string) => !enabledServices || enabledServices.includes(service)\n\n const serviceIndex = getServiceIndex(availableServices ?? [])\n const base = `/projects/${projectId}`\n\n const cards: ServiceCardProps[] = []\n\n if (serviceIndex[\"image\"]?.[\"glance\"] && isEnabled(\"images\"))\n cards.push({ group: t`Compute`, label: t`Images`, to: `${base}/compute/images`, service: \"images\" })\n if (serviceIndex[\"compute\"]?.[\"nova\"] && isEnabled(\"flavors\"))\n cards.push({ group: t`Compute`, label: t`Flavors`, to: `${base}/compute/flavors`, service: \"flavors\" })\n if (serviceIndex[\"network\"]) {\n if (isEnabled(\"securitygroups\"))\n cards.push({\n group: t`Network`,\n label: t`Security Groups`,\n to: `${base}/network/securitygroups`,\n service: \"securitygroups\",\n })\n if (isEnabled(\"floatingips\"))\n cards.push({\n group: t`Network`,\n label: t`Floating IPs`,\n to: `${base}/network/floatingips`,\n service: \"floatingips\",\n })\n }\n if (serviceIndex[\"object-store\"]?.[\"swift\"] && isEnabled(\"containers\"))\n cards.push({\n group: t`Storage`,\n label: t`Object Storage (Swift)`,\n to: `${base}/storage/swift/containers`,\n service: \"containers\",\n })\n if (serviceIndex[\"object-store-ceph\"]?.[\"ceph\"] && isEnabled(\"ceph-containers\"))\n cards.push({\n group: t`Storage`,\n label: t`Object Storage (Ceph)`,\n to: `${base}/storage/ceph/buckets`,\n service: \"ceph-containers\",\n })\n if ((serviceIndex[\"pca\"]?.[\"clavis-dev\"] || serviceIndex[\"pca\"]?.[\"clavis-beta\"]) && isEnabled(\"pca\")) {\n cards.push({ group: t`Services`, label: t`PCA (Clavis)`, to: `${base}/services/pca`, service: \"pca\" })\n }\n\n return (\n <Stack direction=\"vertical\" gap=\"6\" className=\"pb-4\">\n <ContentHeader title={crumbProject?.name ?? t`Project`} projectId={projectId} description={description} />\n {slots?.projectOverviewBanner && <Slot component={slots.projectOverviewBanner} useShadowDOM={false} />}\n {cards.length > 0 ? (\n <div className=\"grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4\">\n {cards.map((card) => (\n <ServiceCard key={card.to} {...card} />\n ))}\n </div>\n ) : (\n <p className=\"text-theme-light text-sm\">\n <Trans>No services available for this project.</Trans>\n </p>\n )}\n </Stack>\n )\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"_projectId-Dbck_MFa.mjs","names":["useNavigate","useMatches","useParams","useRouteContext","useState","useEffect","SideNavigation","SideNavigationList","SideNavigationGroup","SideNavigationItem","Divider","isRouteInfo","Slot","SideNavBar","projectId","projectName","domainName","sections","navigate","matches","provider","strict","slots","navBadge","service","serviceBadge","component","useShadowDOM","currentService","activeMatch","reverse","find","m","staticData","activeRouteInfo","undefined","activeSection","section","activeService","forceOpenCounter","setForceOpenCounter","Object","fromEntries","map","s","prev","ariaLabel","onClick","to","params","label","p","className","spacing","services","open","item","isStorageContainers","isSelected","span","selected","sideNavBanner","getServiceIndex","buildNavSections","projectId","availableServices","enabledServices","serviceIndex","isEnabled","service","includes","computeServices","label","t","navigate","nav","to","params","networkServices","storageServices","provider","storageType","pcaServices","clavisServices","section","services","filter","s","length","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","projectMatches","filter","m","routeId","startsWith","deepest","length","info","staticData","undefined","active","params","sectionCrumb","labelKey","crumb","useParamAsLabel","resolvedLabel","isDetail","intermediateCrumb","iTo","iParam","useParentTitleAsLabel","parentMatch","parentTitle","meta","find","title","iLabel","className","map","item","index","Outlet","useLoaderData","useRouteContext","AppShell","Container","Stack","SideNavBar","buildNavSections","ProjectInfoBox","useMemo","Route","RouteComponent","availableServices","projectId","crumbProject","crumbDomain","from","id","enabledServices","strict","sections","name","domain","component"],"sources":["../../src/client/routes/_auth/projects/-components/SideNavBar.tsx","../../src/client/routes/_auth/projects/-components/buildNavSections.ts","../../src/client/components/ProjectView/ProjectInfoBox.tsx","../../src/client/routes/_auth/projects/$projectId.tsx?tsr-split=component"],"sourcesContent":["import { useNavigate, useMatches, useParams, useRouteContext } from \"@tanstack/react-router\"\nimport { useState, useEffect } from \"react\"\nimport {\n SideNavigation,\n SideNavigationList,\n SideNavigationGroup,\n SideNavigationItem,\n Divider,\n} from \"@cloudoperators/juno-ui-components/index\"\nimport { isRouteInfo } from \"@/client/routes/routeInfo\"\nimport { Slot } from \"@/client/components/Slot\"\nimport type { NavSection } from \"./buildNavSections\"\n\ninterface SideNavBarProps {\n projectId: string\n projectName: string\n domainName?: string\n sections: NavSection[]\n}\n\nexport const SideNavBar = ({ projectId, projectName, domainName, sections }: SideNavBarProps) => {\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 const navBadge = (service: string) => {\n if (!slots?.serviceBadge) return null\n return <Slot component={slots.serviceBadge} useShadowDOM={false} currentService={service} />\n }\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 // Track which sections should be forced open by incrementing a counter\n // This forces a remount of SideNavigationGroup since it doesn't expose onToggle\n const [forceOpenCounter, setForceOpenCounter] = useState<Record<string, number>>(() =>\n Object.fromEntries(sections.map((s) => [s.section, 0]))\n )\n\n // When navigating to a section, force it to expand by remounting\n useEffect(() => {\n if (activeSection) {\n setForceOpenCounter((prev) => ({ ...prev, [activeSection]: (prev[activeSection] || 0) + 1 }))\n }\n }, [activeSection])\n\n return (\n <SideNavigation ariaLabel=\"Project Side Navigation\">\n <>\n <SideNavigationList>\n <>\n <SideNavigationItem\n onClick={() => navigate({ to: \"/projects/$projectId\", params: { projectId } })}\n label={\n <>\n {domainName && <p className=\"text-theme-light text-xs leading-4 font-bold\">{domainName} /</p>}\n <p className=\"leading-5 font-normal\">{projectName}</p>\n </>\n }\n />\n <Divider spacing=\"1\" />\n {sections.map(({ section, label, services }) => (\n <SideNavigationGroup key={`${section}-${forceOpenCounter[section]}`} label={label} open={true}>\n {services.map((item) => {\n const isStorageContainers = activeSection === \"storage\" && activeService === \"containers\"\n const isSelected =\n activeSection === section &&\n (isStorageContainers ? item.params.provider === provider : activeService === item.service)\n\n return (\n <SideNavigationItem\n key={item.service}\n onClick={() => item.navigate(navigate)}\n label={\n <span className=\"flex items-start gap-2\">\n {item.label}\n {navBadge(item.service)}\n </span>\n }\n selected={isSelected}\n />\n )\n })}\n </SideNavigationGroup>\n ))}\n </>\n </SideNavigationList>\n {slots?.sideNavBanner && <Slot component={slots.sideNavBanner} />}\n </>\n </SideNavigation>\n )\n}\n","import { getServiceIndex } from \"@/server/Authentication/helpers\"\nimport { t } from \"@lingui/core/macro\"\nimport type { NavigateFn } from \"@tanstack/react-router\"\n\nexport type NavItem = {\n service: string\n label: string\n navigate: (navigateFn: NavigateFn) => void\n params: Record<string, string>\n}\n\nexport type NavSection = {\n section: string\n label: string\n services: NavItem[]\n}\n\nexport function buildNavSections(\n projectId: string,\n availableServices: { type: string; name: string }[],\n enabledServices?: string[]\n): NavSection[] {\n const serviceIndex = getServiceIndex(availableServices)\n const isEnabled = (service: string) => !enabledServices || enabledServices.includes(service)\n\n const computeServices: NavItem[] = [\n ...(serviceIndex[\"image\"]?.[\"glance\"] && isEnabled(\"images\")\n ? [\n {\n service: \"images\",\n label: t`Images`,\n navigate: (nav: NavigateFn) => nav({ to: \"/projects/$projectId/compute/images\", params: { projectId } }),\n params: { projectId },\n },\n ]\n : []),\n ...(serviceIndex?.[\"compute\"]?.[\"nova\"] && isEnabled(\"flavors\")\n ? [\n {\n service: \"flavors\",\n label: t`Flavors`,\n navigate: (nav: NavigateFn) => nav({ to: \"/projects/$projectId/compute/flavors\", params: { projectId } }),\n params: { projectId },\n },\n ]\n : []),\n ]\n\n const networkServices: NavItem[] = serviceIndex[\"network\"]\n ? [\n ...(isEnabled(\"securitygroups\")\n ? [\n {\n service: \"securitygroups\",\n label: t`Security Groups`,\n navigate: (nav: NavigateFn) =>\n nav({ to: \"/projects/$projectId/network/securitygroups\", params: { projectId } }),\n params: { projectId },\n },\n ]\n : []),\n ...(isEnabled(\"floatingips\")\n ? [\n {\n service: \"floatingips\",\n label: t`Floating IPs`,\n navigate: (nav: NavigateFn) =>\n nav({ to: \"/projects/$projectId/network/floatingips\", params: { projectId } }),\n params: { projectId },\n },\n ]\n : []),\n ]\n : []\n\n const storageServices: NavItem[] = [\n ...(serviceIndex?.[\"object-store\"]?.[\"swift\"] && isEnabled(\"containers\")\n ? [\n {\n service: \"containers\",\n label: t`Object Storage (Swift)`,\n navigate: (nav: NavigateFn) =>\n nav({\n to: \"/projects/$projectId/storage/$provider/$storageType\",\n params: { projectId, provider: \"swift\", storageType: \"containers\" },\n }),\n params: { projectId, provider: \"swift\", storageType: \"containers\" },\n },\n ]\n : []),\n ...(isEnabled(\"ceph-containers\")\n ? [\n {\n service: \"ceph-containers\",\n label: t`Object Storage (Ceph)`,\n navigate: (nav: NavigateFn) =>\n nav({\n to: \"/projects/$projectId/storage/$provider/$storageType\",\n params: { projectId, provider: \"ceph\", storageType: \"buckets\" },\n }),\n params: { projectId, provider: \"ceph\", storageType: \"buckets\" },\n },\n ]\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: NavItem[] =\n pcaServices && isEnabled(\"pca\")\n ? [\n {\n service: \"pca\",\n label: t`PCA (Clavis)`,\n navigate: (nav: NavigateFn) => nav({ to: \"/projects/$projectId/services/pca\", params: { projectId } }),\n params: { projectId },\n },\n ]\n : []\n\n return [\n { section: \"compute\", label: t`Compute`, services: computeServices },\n { section: \"network\", label: t`Network`, services: networkServices },\n { section: \"storage\", label: t`Storage`, services: storageServices },\n { section: \"services\", label: t`Services`, services: clavisServices },\n ].filter((s) => s.services.length > 0)\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 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\n const info = deepest ? (isRouteInfo(deepest.staticData) ? deepest.staticData : undefined) : undefined\n\n if (!deepest || !info) {\n items.push({ label: projectInfo.name, active: true })\n return items\n }\n\n items.push({\n label: projectInfo.name,\n onClick: () => navigate({ to: \"/projects/$projectId\", params: { projectId } }),\n })\n\n const params = deepest.params as Record<string, string>\n\n if (info.sectionCrumb?.to) {\n const { labelKey, to } = info.sectionCrumb\n const label = labelKey ? crumbLabels[labelKey] : undefined\n items.push({ label, onClick: () => navigate({ to: to as never, params: params as never }) })\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\n if (info.intermediateCrumb) {\n const { to: iTo, useParamAsLabel: iParam, useParentTitleAsLabel } = info.intermediateCrumb\n const parentMatch = projectMatches[projectMatches.length - 2]\n const parentTitle = parentMatch?.meta?.find((m) => m != null && \"title\" in m)?.title as string | undefined\n const iLabel = useParentTitleAsLabel\n ? (parentTitle ?? (iParam ? params[iParam] : undefined))\n : iParam\n ? params[iParam]\n : undefined\n items.push(\n iTo\n ? { label: iLabel, onClick: () => navigate({ to: iTo as never, params: params as never }) }\n : { label: iLabel }\n )\n }\n\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=\"relative z-1 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, useRouteContext } from \"@tanstack/react-router\"\nimport { AppShell, Container, Stack } from \"@cloudoperators/juno-ui-components\"\nimport { SideNavBar } from \"@/client/routes/_auth/projects/-components/SideNavBar\"\nimport { buildNavSections } from \"@/client/routes/_auth/projects/-components/buildNavSections\"\nimport { ProjectInfoBox } from \"@/client/components/ProjectView/ProjectInfoBox\"\nimport { RouteError } from \"@/client/components/Error/RouteError\"\nimport { useMemo } from \"react\"\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\n const [availableServices, projects] = await Promise.all([\n context.trpcClient?.auth.getAvailableServices.query(),\n context.trpcClient?.project.getAuthProjects.query().catch(() => null),\n ])\n\n const accountId = data?.domain?.id || \"\"\n const description = projects?.find((p) => p.id === params.projectId)?.description ?? null\n\n return {\n trpcClient: context.trpcClient,\n crumbDomain: { path: `/projects`, name: data?.domain?.name },\n crumbProject: data?.project,\n availableServices,\n accountId,\n projectId: params.projectId,\n description,\n }\n },\n})\n\nfunction RouteComponent() {\n const { availableServices, projectId, crumbProject, crumbDomain } = useLoaderData({ from: Route.id })\n const { enabledServices } = useRouteContext({ strict: false })\n\n const sections = useMemo(\n () => buildNavSections(projectId, availableServices!, enabledServices),\n [projectId, availableServices, enabledServices]\n )\n\n return (\n <AppShell\n embedded\n sideNavigation={\n <SideNavBar\n sections={sections}\n projectId={projectId}\n projectName={crumbProject?.name || projectId}\n domainName={crumbDomain?.name}\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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"}