@cobaltcore-dev/aurora 2.1.1 → 2.2.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.
- package/README.md +31 -25
- package/dist/client/AuroraApp.d.ts +9 -2
- package/dist/client/context/AppConfigContext.d.ts +44 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +41221 -845
- package/dist/client/index.js.map +1 -1
- package/dist/client/trpcClient.d.ts +314 -0
- package/dist/client/utils/appConfigGating.d.ts +21 -0
- package/dist/server/index.d.ts +694 -3
- package/dist/server/index.js +1748 -722
- package/package.json +3 -3
- package/dist/client/AuthProvider-CVSAwW9f.mjs +0 -83
- package/dist/client/AuthProvider-CVSAwW9f.mjs.map +0 -1
- package/dist/client/ContentHeader-fKKZVu-o.mjs +0 -116
- package/dist/client/ContentHeader-fKKZVu-o.mjs.map +0 -1
- package/dist/client/DeleteFlavorModal-C4nAi7gY.mjs +0 -724
- package/dist/client/DeleteFlavorModal-C4nAi7gY.mjs.map +0 -1
- package/dist/client/DynamicBreadcrumbContext-CVcUQFou.mjs +0 -26
- package/dist/client/DynamicBreadcrumbContext-CVcUQFou.mjs.map +0 -1
- package/dist/client/FiltersInput-Becwxo_J.mjs +0 -85
- package/dist/client/FiltersInput-Becwxo_J.mjs.map +0 -1
- package/dist/client/FloatingIpActionModals-Cq10WljB.mjs +0 -466
- package/dist/client/FloatingIpActionModals-Cq10WljB.mjs.map +0 -1
- package/dist/client/RouteIdLevelDefaultError-DPF_x2tG.mjs +0 -26
- package/dist/client/RouteIdLevelDefaultError-DPF_x2tG.mjs.map +0 -1
- package/dist/client/Slot-CWb612-_.mjs +0 -28
- package/dist/client/Slot-CWb612-_.mjs.map +0 -1
- package/dist/client/SortInput-4g7nSTHW.mjs +0 -34
- package/dist/client/SortInput-4g7nSTHW.mjs.map +0 -1
- package/dist/client/StorageNotFound-B-zVinVD.mjs +0 -30
- package/dist/client/StorageNotFound-B-zVinVD.mjs.map +0 -1
- package/dist/client/TwoColumnDescriptionList-DKS42RKa.mjs +0 -28
- package/dist/client/TwoColumnDescriptionList-DKS42RKa.mjs.map +0 -1
- package/dist/client/_-CAsCx-Ay.mjs +0 -6
- package/dist/client/_-CAsCx-Ay.mjs.map +0 -1
- package/dist/client/_auth-SrDjHL6j.mjs +0 -12
- package/dist/client/_auth-SrDjHL6j.mjs.map +0 -1
- package/dist/client/_flavorId-BPvssEzn.mjs +0 -277
- package/dist/client/_flavorId-BPvssEzn.mjs.map +0 -1
- package/dist/client/_flavorId-CFUBcU-l.mjs +0 -27
- package/dist/client/_flavorId-CFUBcU-l.mjs.map +0 -1
- package/dist/client/_flavorId-CZcIp0Ih.mjs +0 -22
- package/dist/client/_flavorId-CZcIp0Ih.mjs.map +0 -1
- package/dist/client/_floatingIpId-B8r64AL9.mjs +0 -196
- package/dist/client/_floatingIpId-B8r64AL9.mjs.map +0 -1
- package/dist/client/_floatingIpId-lEobpOER.mjs +0 -26
- package/dist/client/_floatingIpId-lEobpOER.mjs.map +0 -1
- package/dist/client/_imageId-BAezmV0Y.mjs +0 -526
- package/dist/client/_imageId-BAezmV0Y.mjs.map +0 -1
- package/dist/client/_imageId-D-jqz3e3.mjs +0 -40
- package/dist/client/_imageId-D-jqz3e3.mjs.map +0 -1
- package/dist/client/_imageId-gMdrdnVt.mjs +0 -22
- package/dist/client/_imageId-gMdrdnVt.mjs.map +0 -1
- package/dist/client/_projectId-BYccdAtX.mjs +0 -77
- package/dist/client/_projectId-BYccdAtX.mjs.map +0 -1
- package/dist/client/_projectId-CBjZC1tv.mjs +0 -283
- package/dist/client/_projectId-CBjZC1tv.mjs.map +0 -1
- package/dist/client/_projectId-CLEa1zgw.mjs +0 -30
- package/dist/client/_projectId-CLEa1zgw.mjs.map +0 -1
- package/dist/client/_projectId-D3VOH5lN.mjs +0 -94
- package/dist/client/_projectId-D3VOH5lN.mjs.map +0 -1
- package/dist/client/_securityGroupId-BwgZnoiR.mjs +0 -29
- package/dist/client/_securityGroupId-BwgZnoiR.mjs.map +0 -1
- package/dist/client/_securityGroupId-Cz2NUHdB.mjs +0 -1819
- package/dist/client/_securityGroupId-Cz2NUHdB.mjs.map +0 -1
- package/dist/client/_serviceType-DS8DxsrP.mjs +0 -20
- package/dist/client/_serviceType-DS8DxsrP.mjs.map +0 -1
- package/dist/client/_serviceType-M8d9rOT6.mjs +0 -6
- package/dist/client/_serviceType-M8d9rOT6.mjs.map +0 -1
- package/dist/client/_storageType-BNpP1A2N.mjs +0 -13
- package/dist/client/_storageType-BNpP1A2N.mjs.map +0 -1
- package/dist/client/_storageType-BxSGKhAI.mjs +0 -15
- package/dist/client/_storageType-BxSGKhAI.mjs.map +0 -1
- package/dist/client/_storageType-DZYCmiLt.mjs +0 -30
- package/dist/client/_storageType-DZYCmiLt.mjs.map +0 -1
- package/dist/client/_storageType-UqAMP-8d.mjs +0 -859
- package/dist/client/_storageType-UqAMP-8d.mjs.map +0 -1
- package/dist/client/_storageType-u51_7OwJ.mjs +0 -7
- package/dist/client/_storageType-u51_7OwJ.mjs.map +0 -1
- package/dist/client/about-BmQTyLWc.mjs +0 -92
- package/dist/client/about-BmQTyLWc.mjs.map +0 -1
- package/dist/client/assets/__mocks__/svgMock.d.ts +0 -3
- package/dist/client/aurora-CIml-f2f.mjs +0 -19
- package/dist/client/aurora-CIml-f2f.mjs.map +0 -1
- package/dist/client/build-WuxXRAqf.mjs +0 -14530
- package/dist/client/build-WuxXRAqf.mjs.map +0 -1
- package/dist/client/cn-DM4Cy3jv.mjs +0 -10
- package/dist/client/cn-DM4Cy3jv.mjs.map +0 -1
- package/dist/client/flavors-CJVSNz6R.mjs +0 -15
- package/dist/client/flavors-CJVSNz6R.mjs.map +0 -1
- package/dist/client/flavors-CflJDIEH.mjs +0 -667
- package/dist/client/flavors-CflJDIEH.mjs.map +0 -1
- package/dist/client/flavors-DWMZ6TuJ.mjs +0 -8
- package/dist/client/flavors-DWMZ6TuJ.mjs.map +0 -1
- package/dist/client/floatingips-Cz9k80cc.mjs +0 -8
- package/dist/client/floatingips-Cz9k80cc.mjs.map +0 -1
- package/dist/client/floatingips-DXnRlLvV.mjs +0 -551
- package/dist/client/floatingips-DXnRlLvV.mjs.map +0 -1
- package/dist/client/floatingips-HRbuLxiN.mjs +0 -22
- package/dist/client/floatingips-HRbuLxiN.mjs.map +0 -1
- package/dist/client/floatingips-nN1kQmsu.mjs +0 -14
- package/dist/client/floatingips-nN1kQmsu.mjs.map +0 -1
- package/dist/client/formatBytes-CZv_XyCY.mjs +0 -38
- package/dist/client/formatBytes-CZv_XyCY.mjs.map +0 -1
- package/dist/client/helpers-1PpYf-fC.mjs +0 -6
- package/dist/client/helpers-1PpYf-fC.mjs.map +0 -1
- package/dist/client/hooks-D0yKIumu.mjs +0 -22
- package/dist/client/hooks-D0yKIumu.mjs.map +0 -1
- package/dist/client/imageHelpers-DBhL2ebL.mjs +0 -1272
- package/dist/client/imageHelpers-DBhL2ebL.mjs.map +0 -1
- package/dist/client/images-BsylWUiH.mjs +0 -2011
- package/dist/client/images-BsylWUiH.mjs.map +0 -1
- package/dist/client/images-NBf2bV43.mjs +0 -8
- package/dist/client/images-NBf2bV43.mjs.map +0 -1
- package/dist/client/images-zQq1nSVv.mjs +0 -15
- package/dist/client/images-zQq1nSVv.mjs.map +0 -1
- package/dist/client/md-kvaGCNAK.mjs +0 -336
- package/dist/client/md-kvaGCNAK.mjs.map +0 -1
- package/dist/client/network-DuZm76BZ.mjs +0 -8
- package/dist/client/network-DuZm76BZ.mjs.map +0 -1
- package/dist/client/objects-BudUAfWY.mjs +0 -12909
- package/dist/client/objects-BudUAfWY.mjs.map +0 -1
- package/dist/client/objects-Dc7sKUr8.mjs +0 -67
- package/dist/client/objects-Dc7sKUr8.mjs.map +0 -1
- package/dist/client/objects-Dw7oeTq2.mjs +0 -80
- package/dist/client/objects-Dw7oeTq2.mjs.map +0 -1
- package/dist/client/objects-_g7nGIn5.mjs +0 -25
- package/dist/client/objects-_g7nGIn5.mjs.map +0 -1
- package/dist/client/objects-eBxvY6_n.mjs +0 -78
- package/dist/client/objects-eBxvY6_n.mjs.map +0 -1
- package/dist/client/projects-yiK0HGSA.mjs +0 -7
- package/dist/client/projects-yiK0HGSA.mjs.map +0 -1
- package/dist/client/routeInfo-DP3v74Wg.mjs +0 -19
- package/dist/client/routeInfo-DP3v74Wg.mjs.map +0 -1
- package/dist/client/routes-CJj7SR_K.mjs +0 -10
- package/dist/client/routes-CJj7SR_K.mjs.map +0 -1
- package/dist/client/routes-xofA3ksU.mjs +0 -110
- package/dist/client/routes-xofA3ksU.mjs.map +0 -1
- package/dist/client/securitygroups-CSmsBzUb.mjs +0 -581
- package/dist/client/securitygroups-CSmsBzUb.mjs.map +0 -1
- package/dist/client/securitygroups-CtKne8UQ.mjs +0 -8
- package/dist/client/securitygroups-CtKne8UQ.mjs.map +0 -1
- package/dist/client/securitygroups-T7kaNFEp.mjs +0 -15
- package/dist/client/securitygroups-T7kaNFEp.mjs.map +0 -1
- package/dist/client/securitygroups-WLB1_IM6.mjs +0 -22
- package/dist/client/securitygroups-WLB1_IM6.mjs.map +0 -1
- package/dist/client/securitygroups-o0IXZM6S.mjs +0 -14
- package/dist/client/securitygroups-o0IXZM6S.mjs.map +0 -1
- package/dist/client/serviceCatalog-B3lKWC9V.mjs +0 -6
- package/dist/client/serviceCatalog-B3lKWC9V.mjs.map +0 -1
- package/dist/client/services-D1PbooD1.mjs +0 -6
- package/dist/client/services-D1PbooD1.mjs.map +0 -1
- package/dist/client/services-DEMVtFyn.mjs +0 -9
- package/dist/client/services-DEMVtFyn.mjs.map +0 -1
- package/dist/client/storageProviders-CJ5rOh13.mjs +0 -15
- package/dist/client/storageProviders-CJ5rOh13.mjs.map +0 -1
- package/dist/client/trpcClient-D7gpR7YG.mjs +0 -71
- package/dist/client/trpcClient-D7gpR7YG.mjs.map +0 -1
- package/dist/client/useBreadcrumbs-ha3IlaxX.mjs +0 -54
- package/dist/client/useBreadcrumbs-ha3IlaxX.mjs.map +0 -1
- package/dist/client/useErrorTranslation-Dc0eE8Zt.mjs +0 -83
- package/dist/client/useErrorTranslation-Dc0eE8Zt.mjs.map +0 -1
- package/dist/client/useModal-DCs1OJh7.mjs +0 -12
- package/dist/client/useModal-DCs1OJh7.mjs.map +0 -1
- package/dist/client/useModalTracking-BYd37pXm.mjs +0 -37
- package/dist/client/useModalTracking-BYd37pXm.mjs.map +0 -1
- package/dist/client/useProjectId-DBc5lpoU.mjs +0 -11
- package/dist/client/useProjectId-DBc5lpoU.mjs.map +0 -1
- package/dist/client/useSecurityGroupPermissions--hK4aR8Y.mjs +0 -294
- package/dist/client/useSecurityGroupPermissions--hK4aR8Y.mjs.map +0 -1
- package/dist/client/useSetBreadcrumb-tn7lBaQV.mjs +0 -19
- package/dist/client/useSetBreadcrumb-tn7lBaQV.mjs.map +0 -1
- package/dist/client/utils/trpcErrors.d.ts +0 -13
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cobaltcore-dev/aurora",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Aurora OpenStack dashboard — server and client library",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"eslint-plugin-react": "^7.37.5",
|
|
91
91
|
"eslint-plugin-react-hooks": "^7.1.1",
|
|
92
92
|
"eslint-plugin-react-refresh": "^0.5.3",
|
|
93
|
-
"fastify": "^5.12.
|
|
93
|
+
"fastify": "^5.12.5",
|
|
94
94
|
"globals": "^17.8.0",
|
|
95
95
|
"jsdom": "30.0.1",
|
|
96
96
|
"postcss": "^8.5.25",
|
|
@@ -106,8 +106,8 @@
|
|
|
106
106
|
"vite-plugin-svgr": "^5.2.0",
|
|
107
107
|
"vite-tsconfig-paths": "^6.1.1",
|
|
108
108
|
"vitest": "^4.1.11",
|
|
109
|
-
"@cobaltcore-dev/signal-openstack": "1.0.1",
|
|
110
109
|
"@cobaltcore-dev/aurora-config": "0.0.1",
|
|
110
|
+
"@cobaltcore-dev/signal-openstack": "1.0.1",
|
|
111
111
|
"@cobaltcore-dev/policy-engine": "2.0.0"
|
|
112
112
|
},
|
|
113
113
|
"scripts": {
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { i as e } from "./trpcClient-D7gpR7YG.mjs";
|
|
2
|
-
import { jsx as t } from "react/jsx-runtime";
|
|
3
|
-
import n, { useEffect as r, useState as i } from "react";
|
|
4
|
-
//#region src/client/store/AuthProvider.tsx
|
|
5
|
-
var a = /*#__PURE__*/ n.createContext(null);
|
|
6
|
-
function o({ children: n }) {
|
|
7
|
-
let [o, s] = i(null), [c, l] = i(!0), [u, d] = i(null), [f, p] = i(null), m = (e = !1) => {
|
|
8
|
-
if (window.location.pathname !== "/") {
|
|
9
|
-
let t = e ? `/?redirect=${encodeURIComponent(window.location.pathname + window.location.search)}` : "/";
|
|
10
|
-
window.location.replace(t);
|
|
11
|
-
}
|
|
12
|
-
}, h = () => {
|
|
13
|
-
s(null), p(null);
|
|
14
|
-
};
|
|
15
|
-
return r(() => {
|
|
16
|
-
e.auth.getCurrentUserSession.query().then((e) => {
|
|
17
|
-
e && (s(e.user), p(e.expires_at ?? null));
|
|
18
|
-
}).catch((e) => {
|
|
19
|
-
d(e instanceof Error ? e.message : "Could not load session");
|
|
20
|
-
}).finally(() => l(!1));
|
|
21
|
-
}, []), r(() => {
|
|
22
|
-
if (!o || !f) return;
|
|
23
|
-
let t = new Date(f).getTime() - Date.now(), n = async () => {
|
|
24
|
-
l(!0);
|
|
25
|
-
try {
|
|
26
|
-
await e.auth.terminateUserSession.mutate();
|
|
27
|
-
} catch (e) {
|
|
28
|
-
d(e instanceof Error ? e.message : `Session termination failed: ${e}`);
|
|
29
|
-
} finally {
|
|
30
|
-
h(), l(!1), m(!0);
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
if (t <= 0) {
|
|
34
|
-
n();
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
let r = setTimeout(n, t);
|
|
38
|
-
return () => clearTimeout(r);
|
|
39
|
-
}, [o, f]), /*#__PURE__*/ t(a.Provider, {
|
|
40
|
-
value: {
|
|
41
|
-
user: o,
|
|
42
|
-
isAuthenticated: !!o,
|
|
43
|
-
isLoading: c,
|
|
44
|
-
error: u,
|
|
45
|
-
login: async ({ domain: t, user: n, password: r }) => {
|
|
46
|
-
d(null), l(!0);
|
|
47
|
-
try {
|
|
48
|
-
let i = await e.auth.createUserSession.mutate({
|
|
49
|
-
domainName: t,
|
|
50
|
-
user: n,
|
|
51
|
-
password: r
|
|
52
|
-
});
|
|
53
|
-
return s(i.user), p(i.expires_at), { success: !0 };
|
|
54
|
-
} catch (e) {
|
|
55
|
-
return d(e instanceof Error ? e.message : `Could not create session: ${e}`), { success: !1 };
|
|
56
|
-
} finally {
|
|
57
|
-
l(!1);
|
|
58
|
-
}
|
|
59
|
-
},
|
|
60
|
-
logout: async () => {
|
|
61
|
-
l(!0), d(null);
|
|
62
|
-
try {
|
|
63
|
-
await e.auth.terminateUserSession.mutate();
|
|
64
|
-
} catch (e) {
|
|
65
|
-
d(e instanceof Error ? e.message : `Logout failed: ${e}`);
|
|
66
|
-
} finally {
|
|
67
|
-
h(), l(!1), m(!1);
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
expiresAt: f ? new Date(f) : void 0
|
|
71
|
-
},
|
|
72
|
-
children: n
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
function s() {
|
|
76
|
-
let e = n.useContext(a);
|
|
77
|
-
if (!e) throw Error("useAuth must be used within an AuthProvider");
|
|
78
|
-
return e;
|
|
79
|
-
}
|
|
80
|
-
//#endregion
|
|
81
|
-
export { s as n, o as t };
|
|
82
|
-
|
|
83
|
-
//# sourceMappingURL=AuthProvider-CVSAwW9f.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AuthProvider-CVSAwW9f.mjs","names":["React","useEffect","useState","trpcClient","AuthContext","createContext","AuthProvider","children","user","setUser","isLoading","setIsLoading","error","setError","expiresAt","setExpiresAt","redirectToLogin","saveReturnUrl","window","location","pathname","returnUrl","encodeURIComponent","search","replace","clearLocalSession","auth","getCurrentUserSession","query","then","session","expires_at","catch","err","Error","message","finally","timeUntilExpiry","Date","getTime","now","handleExpiry","terminateUserSession","mutate","timer","setTimeout","clearTimeout","login","domain","password","createUserSession","domainName","success","logout","Provider","value","isAuthenticated","undefined","useAuth","context","useContext"],"sources":["../../src/client/store/AuthProvider.tsx"],"sourcesContent":["import React, { useEffect, useState } from \"react\"\nimport { TokenData } from \"../../server/Authentication/types/models\"\nimport { trpcClient } from \"../trpcClient\"\n\nexport type User = TokenData[\"user\"] | null\n\nexport interface AuthContext {\n isAuthenticated: boolean\n isLoading: boolean\n error: string | null\n login: (credentials: { domain: string; user: string; password: string }) => Promise<{ success: boolean }>\n logout: () => Promise<void>\n user?: User\n expiresAt?: Date\n}\n\nconst AuthContext = React.createContext<AuthContext | null>(null)\n\nexport function AuthProvider({ children }: { children: React.ReactNode }) {\n const [user, setUser] = useState<User | null>(null)\n const [isLoading, setIsLoading] = useState(true)\n const [error, setError] = useState<string | null>(null)\n const [expiresAt, setExpiresAt] = useState<string | null>(null)\n\n // Redirect to login, optionally with return URL\n const redirectToLogin = (saveReturnUrl: boolean = false) => {\n if (window.location.pathname !== \"/\") {\n const returnUrl = saveReturnUrl\n ? `/?redirect=${encodeURIComponent(window.location.pathname + window.location.search)}`\n : \"/\"\n window.location.replace(returnUrl)\n }\n }\n\n // Clear local session state\n const clearLocalSession = () => {\n setUser(null)\n setExpiresAt(null)\n }\n\n // Get current session on mount\n useEffect(() => {\n trpcClient.auth.getCurrentUserSession\n .query()\n .then((session) => {\n if (session) {\n setUser(session.user)\n setExpiresAt(session.expires_at ?? null)\n }\n })\n .catch((err) => {\n setError(err instanceof Error ? err.message : \"Could not load session\")\n })\n .finally(() => setIsLoading(false))\n }, [])\n\n // Auto-logout when session expires\n useEffect(() => {\n if (!user || !expiresAt) return\n\n const timeUntilExpiry = new Date(expiresAt).getTime() - Date.now()\n\n const handleExpiry = async () => {\n setIsLoading(true)\n try {\n await trpcClient.auth.terminateUserSession.mutate()\n } catch (err) {\n // Server invalidation failed - show error\n setError(err instanceof Error ? err.message : `Session termination failed: ${err}`)\n } finally {\n clearLocalSession()\n setIsLoading(false)\n redirectToLogin(true)\n }\n }\n\n if (timeUntilExpiry <= 0) {\n handleExpiry()\n return\n }\n\n const timer = setTimeout(handleExpiry, timeUntilExpiry)\n return () => clearTimeout(timer)\n }, [user, expiresAt])\n\n const login = async ({ domain, user, password }: { domain: string; user: string; password: string }) => {\n setError(null)\n setIsLoading(true)\n try {\n const session = await trpcClient.auth.createUserSession.mutate({ domainName: domain, user, password })\n setUser(session.user)\n setExpiresAt(session.expires_at)\n return { success: true }\n } catch (err) {\n setError(err instanceof Error ? err.message : `Could not create session: ${err}`)\n return { success: false }\n } finally {\n setIsLoading(false)\n }\n }\n\n const logout = async () => {\n setIsLoading(true)\n setError(null)\n try {\n await trpcClient.auth.terminateUserSession.mutate()\n } catch (err) {\n // Server invalidation failed - show error\n setError(err instanceof Error ? err.message : `Logout failed: ${err}`)\n } finally {\n clearLocalSession()\n setIsLoading(false)\n redirectToLogin(false) // Don't save return URL on manual logout\n }\n }\n\n return (\n <AuthContext.Provider\n value={{\n user,\n isAuthenticated: !!user,\n isLoading,\n error,\n login,\n logout,\n expiresAt: expiresAt ? new Date(expiresAt) : undefined,\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":"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"}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { H as e, Q as t, Z as n, _ as r, at as i, d as a, tt as o } from "./build-WuxXRAqf.mjs";
|
|
2
|
-
import { t as s } from "./Slot-CWb612-_.mjs";
|
|
3
|
-
import { t as c } from "./storageProviders-CJ5rOh13.mjs";
|
|
4
|
-
import { t as l } from "./routeInfo-DP3v74Wg.mjs";
|
|
5
|
-
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
6
|
-
import { useState as f } from "react";
|
|
7
|
-
import { useMatches as p, useParams as m, useRouteContext as h } from "@tanstack/react-router";
|
|
8
|
-
import { Trans as g, useLingui as _ } from "@lingui/react";
|
|
9
|
-
//#region src/client/components/ClipboardText.tsx
|
|
10
|
-
var v = ({ text: r, tooltipContent: i, className: s, truncateAt: c, showTooltip: l = !0, ...p }) => {
|
|
11
|
-
let { i18n: m, _: h } = _(), [g, v] = f(!1), [y, b] = f(!1), x = `copyableTooltip inline-flex items-center ${s || ""}`, S = async (e) => {
|
|
12
|
-
e.preventDefault(), e.stopPropagation();
|
|
13
|
-
try {
|
|
14
|
-
await navigator.clipboard.writeText(r), v(!0), b(!1), setTimeout(() => v(!1), 2e3);
|
|
15
|
-
} catch (e) {
|
|
16
|
-
console.error("Failed to copy text:", e);
|
|
17
|
-
}
|
|
18
|
-
}, C = c && r.length > c ? `${r.slice(0, c)}...` : r, w = () => g ? i || m._({ id: "u+VWhB" }) : m._({ id: "he3ygx" }), T = g && l || y && l;
|
|
19
|
-
return /*#__PURE__*/ u("div", {
|
|
20
|
-
...p,
|
|
21
|
-
className: x,
|
|
22
|
-
children: /*#__PURE__*/ d(n, {
|
|
23
|
-
open: T,
|
|
24
|
-
children: [/*#__PURE__*/ u(o, {
|
|
25
|
-
onClick: S,
|
|
26
|
-
onMouseEnter: () => !g && b(!0),
|
|
27
|
-
onMouseLeave: () => b(!1),
|
|
28
|
-
"aria-label": m._({
|
|
29
|
-
id: "Wbg1jv",
|
|
30
|
-
values: { text: r }
|
|
31
|
-
}),
|
|
32
|
-
className: "cursor-pointer",
|
|
33
|
-
asChild: !0,
|
|
34
|
-
"data-testid": "clipboard-copy-trigger",
|
|
35
|
-
children: /*#__PURE__*/ u("button", {
|
|
36
|
-
type: "button",
|
|
37
|
-
className: "group border-none bg-transparent p-0 text-inherit",
|
|
38
|
-
children: /*#__PURE__*/ d(t, {
|
|
39
|
-
direction: "horizontal",
|
|
40
|
-
gap: "1",
|
|
41
|
-
className: "items-center hover:underline",
|
|
42
|
-
children: [/*#__PURE__*/ u("span", {
|
|
43
|
-
className: "select-none",
|
|
44
|
-
children: C
|
|
45
|
-
}), /*#__PURE__*/ u(a, {
|
|
46
|
-
icon: g ? "check" : "contentCopy",
|
|
47
|
-
size: "18"
|
|
48
|
-
})]
|
|
49
|
-
})
|
|
50
|
-
})
|
|
51
|
-
}), /*#__PURE__*/ u(e, { children: w() })]
|
|
52
|
-
})
|
|
53
|
-
});
|
|
54
|
-
};
|
|
55
|
-
//#endregion
|
|
56
|
-
//#region src/client/components/ContentHeader/ContentHeader.tsx
|
|
57
|
-
function y({ title: e, projectId: t, description: n, actions: a, badges: o }) {
|
|
58
|
-
let { slots: f } = h({ strict: !1 }), _ = p(), { provider: y } = m({ strict: !1 }), b = [..._].reverse().find((e) => l(e.staticData)), x = b && l(b.staticData) ? b.staticData.service : void 0, S = x === "containers" && y === c.CEPH ? "ceph-containers" : x, C = f?.servicePageActions && S ? /*#__PURE__*/ u(s, {
|
|
59
|
-
component: f.servicePageActions,
|
|
60
|
-
useShadowDOM: !1,
|
|
61
|
-
currentService: S
|
|
62
|
-
}) : null, w = f?.serviceBanner && S ? /*#__PURE__*/ u(s, {
|
|
63
|
-
component: f.serviceBanner,
|
|
64
|
-
useShadowDOM: !1,
|
|
65
|
-
currentService: S
|
|
66
|
-
}) : null, T = f?.projectOverviewBanner && !S ? /*#__PURE__*/ u(s, {
|
|
67
|
-
component: f.projectOverviewBanner,
|
|
68
|
-
useShadowDOM: !1
|
|
69
|
-
}) : null;
|
|
70
|
-
return /*#__PURE__*/ d("header", {
|
|
71
|
-
className: "mb-8",
|
|
72
|
-
children: [
|
|
73
|
-
/*#__PURE__*/ d("div", {
|
|
74
|
-
className: "flex flex-col sm:flex-row sm:items-center sm:justify-between",
|
|
75
|
-
children: [/*#__PURE__*/ d("div", {
|
|
76
|
-
className: "flex items-center gap-3",
|
|
77
|
-
children: [/*#__PURE__*/ u(i, { children: e }), C]
|
|
78
|
-
}), /*#__PURE__*/ d("div", {
|
|
79
|
-
className: "text-theme-light flex shrink-0 items-center gap-1 text-sm",
|
|
80
|
-
children: [/*#__PURE__*/ d("span", {
|
|
81
|
-
className: "font-semibold",
|
|
82
|
-
children: [
|
|
83
|
-
/*#__PURE__*/ u(g, { id: "mSfwLL" }),
|
|
84
|
-
":",
|
|
85
|
-
" "
|
|
86
|
-
]
|
|
87
|
-
}), /*#__PURE__*/ u(v, {
|
|
88
|
-
text: t,
|
|
89
|
-
truncateAt: 15
|
|
90
|
-
})]
|
|
91
|
-
})]
|
|
92
|
-
}),
|
|
93
|
-
n && /*#__PURE__*/ u("p", {
|
|
94
|
-
className: "text-sm font-normal",
|
|
95
|
-
children: n
|
|
96
|
-
}),
|
|
97
|
-
w,
|
|
98
|
-
T,
|
|
99
|
-
/*#__PURE__*/ u(r, { className: "mt-4" }),
|
|
100
|
-
(o || a) && /*#__PURE__*/ d("div", {
|
|
101
|
-
className: "mt-3 flex items-start justify-between",
|
|
102
|
-
children: [/*#__PURE__*/ d("div", {
|
|
103
|
-
className: "flex items-center gap-2",
|
|
104
|
-
children: [o, " "]
|
|
105
|
-
}), a && /*#__PURE__*/ u("div", {
|
|
106
|
-
className: "flex gap-2",
|
|
107
|
-
children: a
|
|
108
|
-
})]
|
|
109
|
-
})
|
|
110
|
-
]
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
//#endregion
|
|
114
|
-
export { v as n, y as t };
|
|
115
|
-
|
|
116
|
-
//# sourceMappingURL=ContentHeader-fKKZVu-o.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ContentHeader-fKKZVu-o.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","button","type","direction","gap","span","icon","size","Divider","ContentHeading","useRouteContext","useMatches","useParams","ClipboardText","Slot","isRouteInfo","STORAGE_PROVIDER","ContentHeader","title","projectId","description","actions","badges","slots","strict","matches","provider","activeMatch","reverse","find","m","staticData","routeService","service","undefined","currentService","CEPH","slotActions","servicePageActions","component","useShadowDOM","serviceBanner","projectOverviewBanner","header","className","div","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 <button type=\"button\" className=\"group border-none bg-transparent p-0 text-inherit\">\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 </button>\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\"\nimport { STORAGE_PROVIDER } from \"@/client/utils/storageProviders\"\n\ninterface ContentHeaderProps {\n title: string\n projectId: string\n description?: string | null\n actions?: ReactNode\n badges?: ReactNode\n}\n\nexport function ContentHeader({ title, projectId, description, actions, badges }: 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. Both sides of this are nav service keys,\n // not `$storageType` URL nouns — see STORAGE_TYPE.\n const currentService =\n routeService === \"containers\" && provider === STORAGE_PROVIDER.CEPH ? \"ceph-containers\" : routeService\n\n const slotActions =\n slots?.servicePageActions && currentService ? (\n <Slot component={slots.servicePageActions} useShadowDOM={false} currentService={currentService} />\n ) : null\n\n const serviceBanner =\n slots?.serviceBanner && currentService ? (\n <Slot component={slots.serviceBanner} useShadowDOM={false} currentService={currentService} />\n ) : null\n\n const projectOverviewBanner =\n slots?.projectOverviewBanner && !currentService ? (\n <Slot component={slots.projectOverviewBanner} useShadowDOM={false} />\n ) : null\n\n return (\n <header className=\"mb-8\">\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 {serviceBanner}\n {projectOverviewBanner}\n <Divider className=\"mt-4\" />\n {(badges || actions) && (\n <div className=\"mt-3 flex items-start justify-between\">\n <div className=\"flex items-center gap-2\">{badges} </div>\n {actions && <div className=\"flex gap-2\">{actions}</div>}\n </div>\n )}\n </header>\n )\n}\n"],"mappings":"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"}
|