@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
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { A as e, i as t, rt as n } from "./build-WuxXRAqf.mjs";
|
|
2
|
-
import { n as r, t as i } from "./_projectId-BYccdAtX.mjs";
|
|
3
|
-
import { jsx as a } from "react/jsx-runtime";
|
|
4
|
-
import { TRPCClientError as o } from "@trpc/client";
|
|
5
|
-
import { Trans as s, useLingui as c } from "@lingui/react";
|
|
6
|
-
//#region src/client/routes/_auth/projects/$projectId.tsx?tsr-split=errorComponent
|
|
7
|
-
function l({ error: l }) {
|
|
8
|
-
let { i18n: u, _: d } = c(), f = i.useNavigate();
|
|
9
|
-
return l.statusCode === 404 || l.message === "Project not found" ? /*#__PURE__*/ a(e, {
|
|
10
|
-
className: "py-8",
|
|
11
|
-
children: /*#__PURE__*/ a(n, {
|
|
12
|
-
status: "error",
|
|
13
|
-
code: 404,
|
|
14
|
-
title: u._({ id: "u30W7+" }),
|
|
15
|
-
body: u._({ id: "Jrn0cT" }),
|
|
16
|
-
action: /*#__PURE__*/ a(t, {
|
|
17
|
-
variant: "primary",
|
|
18
|
-
onClick: () => f({ to: "/projects" }),
|
|
19
|
-
children: /*#__PURE__*/ a(s, { id: "aKznJS" })
|
|
20
|
-
})
|
|
21
|
-
})
|
|
22
|
-
}) : /*#__PURE__*/ a(r, {
|
|
23
|
-
error: l,
|
|
24
|
-
safeErrorMessage: l instanceof o ? l.message : void 0
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
//#endregion
|
|
28
|
-
export { l as errorComponent };
|
|
29
|
-
|
|
30
|
-
//# sourceMappingURL=_projectId-CLEa1zgw.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"_projectId-CLEa1zgw.mjs","names":["Button","Container","Status","RouteError","TRPCClientError","useLingui","Trans","ScopeError","type","code","message","currentDomain","id","name","Route","ProjectErrorComponent","error","Error","statusCode","t","navigate","useNavigate","to","undefined","errorComponent"],"sources":["../../src/client/routes/_auth/projects/$projectId.tsx?tsr-split=errorComponent"],"sourcesContent":["import { createFileRoute, Outlet, useLoaderData, useRouteContext } from \"@tanstack/react-router\"\nimport { AppShell, Button, Container, Stack, Status } from \"@cloudoperators/juno-ui-components\"\nimport { SideNavBar } from \"@/client/routes/_auth/projects/-components/SideNavBar\"\nimport { buildNavSections } from \"@/client/routes/_auth/projects/-components/buildNavSections\"\nimport { Breadcrumbs } from \"@/client/components/Breadcrumbs\"\nimport { RouteError } from \"@/client/components/Errors/RouteError\"\nimport { useMemo } from \"react\"\nimport { TRPCClientError } from \"@trpc/client\"\nimport { useLingui, Trans } from \"@lingui/react/macro\"\nimport { BreadcrumbExtensionProvider } from \"@/client/context/BreadcrumbExtensionContext\"\nimport { useSetBreadcrumb } from \"@/client/hooks/useSetBreadcrumb\"\n\n// Type for scope error that we catch and handle gracefully\ntype ScopeError = {\n type: \"scope_error\"\n code: string\n message: string\n currentDomain?: { id: string; name: string } | null\n}\n\nexport const Route = createFileRoute(\"/_auth/projects/$projectId\")({\n component: RouteComponent,\n errorComponent: ProjectErrorComponent,\n loader: async (options) => {\n const { context, params } = options\n\n // Catch rescope errors and return them as data instead of throwing\n // This allows the component to render a friendly error UI\n let scopeData\n let scopeError: ScopeError | undefined\n\n try {\n scopeData = await context.trpcClient?.auth.setCurrentScope.mutate({\n type: \"project\",\n projectId: params.projectId || \"\",\n })\n } catch (error) {\n if (error instanceof TRPCClientError) {\n // Try to get the current session's domain to show in the error message\n let currentDomain: { id: string; name: string } | null | undefined\n try {\n const currentScope = await context.trpcClient?.auth.getCurrentScope.query()\n if (currentScope?.domain?.id && currentScope?.domain?.name) {\n currentDomain = { id: currentScope.domain.id, name: currentScope.domain.name }\n }\n } catch {\n // Ignore errors fetching current scope\n }\n\n // Return error as data so the component can handle it\n scopeError = {\n type: \"scope_error\",\n code: error.data?.code || \"UNKNOWN\",\n message: error.message,\n currentDomain,\n }\n } else {\n // Unknown errors should still propagate\n throw error\n }\n }\n\n // If scope failed, return early with error state\n if (scopeError) {\n return {\n scopeError,\n projectId: params.projectId,\n // Provide minimal data for error rendering\n trpcClient: context.trpcClient,\n crumbDomain: undefined,\n crumbProject: undefined,\n availableServices: undefined,\n accountId: \"\",\n description: null,\n }\n }\n\n const [availableServices, project] = await Promise.all([\n context.trpcClient?.auth.getAvailableServices.query(),\n context.trpcClient?.project.getProject.query({ projectId: params.projectId }).catch(() => null),\n ])\n\n const accountId = scopeData?.domain?.id || \"\"\n const description = project?.description ?? null\n\n return {\n scopeError: undefined,\n trpcClient: context.trpcClient,\n crumbDomain: { path: `/projects`, name: scopeData?.domain?.name },\n crumbProject: scopeData?.project,\n availableServices,\n accountId,\n projectId: params.projectId,\n description,\n }\n },\n})\n\nfunction RouteComponent() {\n const { t } = useLingui()\n const loaderData = useLoaderData({ from: Route.id })\n const navigate = Route.useNavigate()\n const { enabledServices, serviceExtensions } = useRouteContext({ strict: false })\n\n const projectName = loaderData.crumbProject?.name || loaderData.projectId\n const projectLabel = loaderData.crumbDomain?.name ? `${loaderData.crumbDomain.name}/${projectName}` : projectName\n useSetBreadcrumb(Route.id, projectLabel)\n\n // Handle scope errors with a friendly UI using Juno's Status component\n if (loaderData.scopeError) {\n const { code, message, currentDomain } = loaderData.scopeError\n const domainName = currentDomain?.name\n\n if (code === \"UNAUTHORIZED\") {\n return (\n <Container className=\"py-8\">\n <Status\n status=\"error\"\n title={t`Session Changed`}\n body={t`Your session was changed or ended in another browser tab. This can happen if you logged out or switched to a different account or domain elsewhere. Log in again to continue.`}\n action={\n <Button variant=\"primary\" onClick={() => navigate({ to: \"/\" })}>\n <Trans>Sign in</Trans>\n </Button>\n }\n />\n </Container>\n )\n }\n\n if (code === \"NOT_FOUND\") {\n const bodyText = domainName\n ? t`This project doesn't exist or is not accessible from your current domain. Your current domain is ${domainName}. Select a project from your current domain.`\n : t`This project doesn't exist or is not accessible from your current domain. Select a project from your current domain.`\n\n return (\n <Container className=\"py-8\">\n <Status\n status=\"error\"\n code={404}\n title={t`Project Not Accessible`}\n body={bodyText}\n action={\n <Button variant=\"primary\" onClick={() => navigate({ to: \"/projects\" })}>\n <Trans>Go to Projects</Trans>\n </Button>\n }\n />\n </Container>\n )\n }\n\n if (code === \"FORBIDDEN\") {\n return (\n <Container className=\"py-8\">\n <Status\n status=\"error\"\n code={403}\n title={t`Access Denied`}\n body={t`You don't have permission to access this project.`}\n action={\n <Button variant=\"primary\" onClick={() => navigate({ to: \"/projects\" })}>\n <Trans>Go to Projects</Trans>\n </Button>\n }\n />\n </Container>\n )\n }\n\n // Fallback for other error codes\n return <RouteError error={new Error(message)} />\n }\n\n const { availableServices, projectId, crumbProject, crumbDomain } = loaderData\n\n const sections = useMemo(\n () => buildNavSections(projectId, availableServices!, enabledServices, serviceExtensions),\n [projectId, availableServices, enabledServices, serviceExtensions]\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 <BreadcrumbExtensionProvider>\n <Breadcrumbs />\n <Outlet />\n </BreadcrumbExtensionProvider>\n </div>\n </Stack>\n </Container>\n </AppShell>\n )\n}\n\nfunction ProjectErrorComponent({ error }: { error: Error & { statusCode?: number } }) {\n const { t } = useLingui()\n const navigate = Route.useNavigate()\n\n // Handle 404 errors with a custom UI\n if (error.statusCode === 404 || error.message === \"Project not found\") {\n return (\n <Container className=\"py-8\">\n <Status\n status=\"error\"\n code={404}\n title={t`Project Not Found`}\n body={t`The project you are looking for doesn't exist or you don't have access to it.`}\n action={\n <Button variant=\"primary\" onClick={() => navigate({ to: \"/projects\" })}>\n <Trans>Go to Projects</Trans>\n </Button>\n }\n />\n </Container>\n )\n }\n\n // For all other errors, use the default RouteError component\n return <RouteError error={error} safeErrorMessage={error instanceof TRPCClientError ? error.message : undefined} />\n}\n"],"mappings":";;;;;;AAiNA,SAASe,EAAsB,EAAEC,YAAmD;CAClF,IAAM,EAAA,MAAA,GAAA,GAAA,MAAQX,EAAAA,GACRe,IAAWN,EAAMO,YAAW;CAsBlC,OAnBIL,EAAME,eAAe,OAAOF,EAAMN,YAAY,sBAE9C,gBAAC,GAAA;EAAU,WAAU;EACnB,UAAA,gBAAC,GAAA;GACC,QAAO;GACP,MAAM;GACN,OAAOS,EAAAA,EAAC,EAAA,IAAA,SAAA,CAAA;GACR,MAAMA,EAAAA,EAAC,EAAA,IAAA,SAAA,CAAA;GACP,QACE,gBAAC,GAAA;IAAO,SAAQ;IAAU,eAAeC,EAAS,EAAEE,IAAI,YAAY,CAAA;IAClE,UAAA,gBAAA,GAAA,EAAA,IAAA,SAAA,CAAA;;;MASL,gBAAC,GAAA;EAAkBN;EAAO,kBAAkBA,aAAiBZ,IAAkBY,EAAMN,UAAUa,KAAAA;;AACxG"}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { Q as e, f as t } from "./build-WuxXRAqf.mjs";
|
|
2
|
-
import { t as n } from "./Slot-CWb612-_.mjs";
|
|
3
|
-
import { t as r } from "./helpers-1PpYf-fC.mjs";
|
|
4
|
-
import { t as i } from "./serviceCatalog-B3lKWC9V.mjs";
|
|
5
|
-
import { n as a, t as o } from "./storageProviders-CJ5rOh13.mjs";
|
|
6
|
-
import { t as s } from "./ContentHeader-fKKZVu-o.mjs";
|
|
7
|
-
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
8
|
-
import { useLoaderData as u, useNavigate as d, useRouteContext as f } from "@tanstack/react-router";
|
|
9
|
-
import { Trans as p, useLingui as m } from "@lingui/react";
|
|
10
|
-
//#region src/client/routes/_auth/projects/$projectId/index.tsx?tsr-split=component
|
|
11
|
-
function h({ group: e, label: r, to: i, service: a }) {
|
|
12
|
-
let o = d(), { slots: s } = f({ strict: !1 });
|
|
13
|
-
return /*#__PURE__*/ l(t, {
|
|
14
|
-
onClick: () => o({ to: i }),
|
|
15
|
-
className: "flex min-h-40 flex-col gap-4 px-3 pt-2 pb-3",
|
|
16
|
-
"data-testid": "service-card",
|
|
17
|
-
children: [/*#__PURE__*/ l("div", {
|
|
18
|
-
className: "flex min-w-0 flex-col",
|
|
19
|
-
children: [/*#__PURE__*/ c("p", {
|
|
20
|
-
className: "text-theme-light text-xs leading-5 font-medium",
|
|
21
|
-
children: e
|
|
22
|
-
}), /*#__PURE__*/ l("div", {
|
|
23
|
-
className: "flex items-center gap-2",
|
|
24
|
-
children: [/*#__PURE__*/ c("p", {
|
|
25
|
-
className: "text-theme-high text-lg leading-7 font-bold",
|
|
26
|
-
"data-testid": "service-card-label",
|
|
27
|
-
children: r
|
|
28
|
-
}), s?.serviceBadge && /*#__PURE__*/ c(n, {
|
|
29
|
-
component: s.serviceBadge,
|
|
30
|
-
useShadowDOM: !1,
|
|
31
|
-
currentService: a
|
|
32
|
-
})]
|
|
33
|
-
})]
|
|
34
|
-
}), /*#__PURE__*/ c("div", { className: "flex-1" })]
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
function g() {
|
|
38
|
-
let { crumbProject: t, availableServices: n, projectId: d, description: g } = u({ from: "/_auth/projects/$projectId" }), { i18n: _, _: v } = m(), { enabledServices: y, serviceExtensions: b } = f({ strict: !1 }), x = (e) => !y || y.includes(e), S = r(n ?? []), C = `/projects/${d}`, w = [];
|
|
39
|
-
S.image?.glance && x("images") && w.push({
|
|
40
|
-
group: _._({ id: "rp0Bd0" }),
|
|
41
|
-
label: _._({ id: "an5hVd" }),
|
|
42
|
-
to: `${C}/compute/images`,
|
|
43
|
-
service: "images"
|
|
44
|
-
}), S.compute?.nova && x("flavors") && w.push({
|
|
45
|
-
group: _._({ id: "rp0Bd0" }),
|
|
46
|
-
label: _._({ id: "neiJm0" }),
|
|
47
|
-
to: `${C}/compute/flavors`,
|
|
48
|
-
service: "flavors"
|
|
49
|
-
}), S.network && (x("securitygroups") && w.push({
|
|
50
|
-
group: _._({ id: "OR475H" }),
|
|
51
|
-
label: _._({ id: "4opp4r" }),
|
|
52
|
-
to: `${C}/network/securitygroups`,
|
|
53
|
-
service: "securitygroups"
|
|
54
|
-
}), x("floatingips") && w.push({
|
|
55
|
-
group: _._({ id: "OR475H" }),
|
|
56
|
-
label: _._({ id: "u77/s4" }),
|
|
57
|
-
to: `${C}/network/floatingips`,
|
|
58
|
-
service: "floatingips"
|
|
59
|
-
})), i(S, o.SWIFT) && x("containers") && w.push({
|
|
60
|
-
group: _._({ id: "BrrIs8" }),
|
|
61
|
-
label: _._({ id: "+OEi73" }),
|
|
62
|
-
to: `${C}/storage/${o.SWIFT}/${a[o.SWIFT]}`,
|
|
63
|
-
service: "containers"
|
|
64
|
-
}), i(S, o.CEPH) && x("ceph-containers") && w.push({
|
|
65
|
-
group: _._({ id: "BrrIs8" }),
|
|
66
|
-
label: _._({ id: "KhNDX4" }),
|
|
67
|
-
to: `${C}/storage/${o.CEPH}/${a[o.CEPH]}`,
|
|
68
|
-
service: "ceph-containers"
|
|
69
|
-
});
|
|
70
|
-
for (let e of b ?? []) S[e.serviceType]?.[e.serviceName] && (!y || y.includes(e.serviceType)) && w.push({
|
|
71
|
-
group: _._({ id: "MILoeL" }),
|
|
72
|
-
label: e.label,
|
|
73
|
-
to: `/projects/${d}/services/${e.serviceType}`,
|
|
74
|
-
service: e.serviceType
|
|
75
|
-
});
|
|
76
|
-
return /*#__PURE__*/ l(e, {
|
|
77
|
-
direction: "vertical",
|
|
78
|
-
children: [/*#__PURE__*/ c(s, {
|
|
79
|
-
title: t?.name ?? _._({ id: "e0NrBM" }),
|
|
80
|
-
projectId: d,
|
|
81
|
-
description: g
|
|
82
|
-
}), w.length > 0 ? /*#__PURE__*/ c("div", {
|
|
83
|
-
className: "grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4",
|
|
84
|
-
children: w.map((e) => /*#__PURE__*/ c(h, { ...e }, e.to))
|
|
85
|
-
}) : /*#__PURE__*/ c("p", {
|
|
86
|
-
className: "text-theme-light text-sm",
|
|
87
|
-
children: /*#__PURE__*/ c(p, { id: "Q1W//7" })
|
|
88
|
-
})]
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
//#endregion
|
|
92
|
-
export { g as component };
|
|
93
|
-
|
|
94
|
-
//# sourceMappingURL=_projectId-D3VOH5lN.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"_projectId-D3VOH5lN.mjs","names":["useLoaderData","useNavigate","Card","Stack","getServiceIndex","Trans","useLingui","useRouteContext","ContentHeader","Slot","hasServiceByName","STORAGE_PROVIDER","STORAGE_TYPE_BY_PROVIDER","ServiceCardProps","group","label","to","service","ServiceCard","navigate","slots","strict","serviceBadge","RouteComponent","crumbProject","availableServices","projectId","description","from","t","enabledServices","serviceExtensions","isEnabled","includes","serviceIndex","base","cards","push","SWIFT","CEPH","extension","serviceType","serviceName","name","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, 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\"\nimport type { RouteInfo } from \"@/client/routes/routeInfo\"\nimport { hasServiceByName } from \"@/client/utils/serviceCatalog\"\nimport { STORAGE_PROVIDER, STORAGE_TYPE_BY_PROVIDER } from \"@/client/utils/storageProviders\"\n\nexport const Route = createFileRoute(\"/_auth/projects/$projectId/\")({\n staticData: {\n analytics: {\n name: \"projects.detail\",\n },\n } satisfies RouteInfo,\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 <p className=\"text-theme-light text-xs leading-5 font-medium\">{group}</p>\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 { enabledServices, serviceExtensions } = 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 (hasServiceByName(serviceIndex, STORAGE_PROVIDER.SWIFT) && isEnabled(\"containers\"))\n cards.push({\n group: t`Storage`,\n label: t`Object Storage (Swift)`,\n to: `${base}/storage/${STORAGE_PROVIDER.SWIFT}/${STORAGE_TYPE_BY_PROVIDER[STORAGE_PROVIDER.SWIFT]}`,\n service: \"containers\",\n })\n if (hasServiceByName(serviceIndex, STORAGE_PROVIDER.CEPH) && isEnabled(\"ceph-containers\"))\n cards.push({\n group: t`Storage`,\n label: t`Object Storage (Ceph)`,\n to: `${base}/storage/${STORAGE_PROVIDER.CEPH}/${STORAGE_TYPE_BY_PROVIDER[STORAGE_PROVIDER.CEPH]}`,\n service: \"ceph-containers\",\n })\n\n for (const extension of serviceExtensions ?? []) {\n // if the extension's service is in the catalog and enabled, display a card for it\n if (\n serviceIndex[extension.serviceType]?.[extension.serviceName] &&\n (!enabledServices || enabledServices.includes(extension.serviceType))\n )\n cards.push({\n group: t`Services`,\n label: extension.label,\n to: `/projects/${projectId}/services/${extension.serviceType}`,\n service: extension.serviceType,\n })\n }\n\n return (\n <Stack direction=\"vertical\">\n <ContentHeader title={crumbProject?.name ?? t`Project`} projectId={projectId} description={description} />\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,29 +0,0 @@
|
|
|
1
|
-
import { t as e } from "./helpers-1PpYf-fC.mjs";
|
|
2
|
-
import { createFileRoute as t, lazyRouteComponent as n, redirect as r } from "@tanstack/react-router";
|
|
3
|
-
var i = t("/_auth/projects/$projectId/network/securitygroups/$securityGroupId/")({
|
|
4
|
-
staticData: {
|
|
5
|
-
section: "network",
|
|
6
|
-
service: "securitygroups",
|
|
7
|
-
analytics: { name: "network.securitygroups.detail" }
|
|
8
|
-
},
|
|
9
|
-
loader: async ({ context: e, params: t }) => {
|
|
10
|
-
let n = await e.trpcClient?.network.securityGroup.getById.query({
|
|
11
|
-
project_id: t.projectId,
|
|
12
|
-
securityGroupId: t.securityGroupId
|
|
13
|
-
});
|
|
14
|
-
return { sgTitle: n?.name || n?.id || null };
|
|
15
|
-
},
|
|
16
|
-
head: ({ loaderData: e }) => ({ meta: [{ title: e?.sgTitle ?? "Security Group" }] }),
|
|
17
|
-
component: n(() => import("./_securityGroupId-Cz2NUHdB.mjs"), "component"),
|
|
18
|
-
beforeLoad: async ({ context: t, params: n }) => {
|
|
19
|
-
let { trpcClient: i } = t, a = await i?.auth.getAvailableServices.query() || [], o = e(a);
|
|
20
|
-
if (!o.network || !o.network.neutron) throw r({
|
|
21
|
-
to: "/projects/$projectId/network/securitygroups",
|
|
22
|
-
params: { projectId: n.projectId }
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
//#endregion
|
|
27
|
-
export { i as t };
|
|
28
|
-
|
|
29
|
-
//# sourceMappingURL=_securityGroupId-BwgZnoiR.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"_securityGroupId-BwgZnoiR.mjs","names":["createFileRoute","redirect","getServiceIndex","Route","staticData","section","service","analytics","name","RouteInfo","loader","context","params","sg","trpcClient","network","securityGroup","getById","query","project_id","projectId","securityGroupId","sgTitle","id","head","loaderData","meta","title","component","lazyRouteComponent","$$splitComponentImporter","beforeLoad","availableServices","auth","getAvailableServices","serviceIndex","to"],"sources":["../../src/client/routes/_auth/projects/$projectId/network/securitygroups/$securityGroupId/index.tsx"],"sourcesContent":["import {\n Button,\n PopupMenu,\n PopupMenuItem,\n PopupMenuOptions,\n PopupMenuToggle,\n Stack,\n Status,\n} from \"@cloudoperators/juno-ui-components/index\"\nimport { createFileRoute, redirect, useNavigate } from \"@tanstack/react-router\"\nimport { useMemo } from \"react\"\nimport { Trans, useLingui } from \"@lingui/react/macro\"\nimport { getServiceIndex } from \"@/server/Authentication/helpers\"\nimport { useProjectId } from \"@/client/hooks\"\nimport type { RouteInfo } from \"@/client/routes/routeInfo\"\nimport { ContentHeader } from \"@/client/components/ContentHeader/ContentHeader\"\nimport { useListWithFiltering } from \"@/client/utils/useListWithFiltering\"\nimport { trpcReact } from \"@/client/trpcClient\"\nimport { SecurityGroupDetailsView } from \"./-components/SecurityGroupDetailsView\"\nimport { EditSecurityGroupModal } from \"../-components/-modals/EditSecurityGroupModal\"\nimport { DeleteSecurityGroupDialog } from \"../-components/-modals/DeleteSecurityGroupDialog\"\nimport { useSecurityGroupDetails } from \"./-hooks/useSecurityGroupDetails\"\nimport { useSecurityGroupPermissions } from \"../-hooks/useSecurityGroupPermissions\"\nimport { useSetBreadcrumb } from \"@/client/hooks/useSetBreadcrumb\"\n\nexport const Route = createFileRoute(\"/_auth/projects/$projectId/network/securitygroups/$securityGroupId/\")({\n staticData: {\n section: \"network\",\n service: \"securitygroups\",\n analytics: {\n name: \"network.securitygroups.detail\",\n },\n } satisfies RouteInfo,\n loader: async ({ context, params }) => {\n const sg = await context.trpcClient?.network.securityGroup.getById.query({\n project_id: params.projectId,\n securityGroupId: params.securityGroupId,\n })\n return { sgTitle: sg?.name || sg?.id || null }\n },\n head: ({ loaderData }) => ({\n meta: [{ title: loaderData?.sgTitle ?? \"Security Group\" }],\n }),\n component: RouteComponent,\n beforeLoad: async ({ context, params }) => {\n const { trpcClient } = context\n\n const availableServices = (await trpcClient?.auth.getAvailableServices.query()) || []\n\n const serviceIndex = getServiceIndex(availableServices)\n\n // Redirect to the \"Projects Overview\" page if network service not available\n if (!serviceIndex[\"network\"]) {\n throw redirect({\n to: \"/projects/$projectId/network/securitygroups\",\n params: { projectId: params.projectId },\n })\n }\n\n if (!serviceIndex[\"network\"][\"neutron\"]) {\n // Redirect to the \"Network Services Overview\" page if the \"Neutron\" service is not available\n throw redirect({\n to: \"/projects/$projectId/network/securitygroups\",\n params: { projectId: params.projectId },\n })\n }\n },\n})\n\nfunction RouteComponent() {\n const { securityGroupId } = Route.useParams()\n const { trpcClient } = Route.useRouteContext()\n const projectId = useProjectId()\n const navigate = useNavigate()\n const { t } = useLingui()\n\n if (!trpcClient) {\n throw new Error(\"trpcClient is not available in route context\")\n }\n\n const {\n permissions,\n isLoading: isLoadingPermissions,\n isError: isPermissionsError,\n } = useSecurityGroupPermissions(projectId)\n\n // Rules filtering using the same pattern as List page\n const {\n searchTerm: rulesSearchTerm,\n sortSettings,\n filterSettings,\n handleSearchChange,\n handleSortChange,\n handleFilterChange,\n } = useListWithFiltering<\"direction\" | \"protocol\" | \"description\">({\n defaultSortKey: \"direction\",\n defaultSortDir: \"asc\",\n sortOptions: [\n { label: t`Direction`, value: \"direction\" },\n { label: t`Protocol`, value: \"protocol\" },\n { label: t`Description`, value: \"description\" },\n ],\n filterSettings: {\n filters: [\n {\n displayName: t`Direction`,\n filterName: \"direction\",\n values: [\"ingress\", \"egress\"],\n supportsMultiValue: false,\n },\n {\n displayName: t`Ethertype`,\n filterName: \"ethertype\",\n values: [\"IPv4\", \"IPv6\"],\n supportsMultiValue: false,\n },\n {\n displayName: t`Protocol`,\n filterName: \"protocol\",\n values: [\"tcp\", \"udp\", \"icmp\", \"ipv6-icmp\"],\n supportsMultiValue: false,\n },\n ],\n },\n })\n\n // Group filter controls for the hook\n const filterControls = {\n searchTerm: rulesSearchTerm,\n onSearchChange: handleSearchChange,\n sortSettings,\n onSortChange: handleSortChange,\n filterSettings,\n onFilterChange: handleFilterChange,\n }\n\n // Use custom hook for logic (now includes filtering/sorting)\n const {\n securityGroup,\n filteredAndSortedRules,\n isLoading,\n isError,\n error,\n isUpdating,\n updateError,\n isDeleting,\n deleteError,\n isDeletingRule,\n deleteRuleError,\n isCreatingRule,\n createRuleError,\n editModalOpen,\n deleteModalOpen,\n handleEdit,\n handleCloseEditModal,\n handleUpdate,\n handleDelete,\n handleCloseDeleteModal,\n handleDeleteSecurityGroup,\n handleDeleteRule,\n handleCreateRule,\n } = useSecurityGroupDetails({\n securityGroupId,\n filterControls,\n })\n\n useSetBreadcrumb(Route.id, securityGroup?.name ?? undefined)\n\n // Fetch available security groups for the Add Rule dropdown\n const { data: securityGroups } = trpcReact.network.securityGroup.list.useQuery({ project_id: projectId })\n const availableSecurityGroups = useMemo(() => {\n return (securityGroups || [])\n .filter((sg) => sg.id !== securityGroupId) // Exclude current group\n .map((sg) => ({\n id: sg.id,\n name: sg.name || sg.id,\n }))\n }, [securityGroups, securityGroupId])\n\n const handleBack = () => {\n navigate({\n to: \"/projects/$projectId/network/securitygroups\",\n params: { projectId },\n })\n }\n\n // Handle loading permissions\n if (isLoadingPermissions) {\n return <Status status=\"progress\" title={t`Loading Permissions...`} />\n }\n\n // Handle permissions error - default to no permissions\n const safePermissions = isPermissionsError\n ? {\n canView: false,\n canCreate: false,\n canUpdate: false,\n canDelete: false,\n canCreateRule: false,\n canDeleteRule: false,\n canManageAccess: false,\n canViewRBAC: false,\n }\n : permissions!\n\n // Handle loading state\n if (isLoading) {\n return <Status status=\"progress\" title={t`Loading Security Group Details...`} />\n }\n\n // Handle error state\n if (isError) {\n const errorMessage = error?.message || t`Unknown Error`\n\n return (\n <Status\n status=\"error\"\n title={t`Error Loading Security Group`}\n body={errorMessage}\n action={\n <Button onClick={handleBack} variant=\"primary\">\n <Trans>Back to Security Groups</Trans>\n </Button>\n }\n />\n )\n }\n\n // Handle no data state\n if (!securityGroup) {\n return (\n <Status\n status=\"empty\"\n title={t`Security Group Not Found`}\n action={\n <Button onClick={handleBack} variant=\"primary\">\n <Trans>Back to Security Groups</Trans>\n </Button>\n }\n />\n )\n }\n\n // Groups owned by another project are shared read-only copies: they cannot be edited or deleted,\n // the same rule the list applies to its row actions.\n const isReadOnly = Boolean(securityGroup.project_id && securityGroup.project_id !== projectId)\n const canEdit = safePermissions.canUpdate && !isReadOnly\n const canDelete = safePermissions.canDelete && !isReadOnly\n\n const headerActions = (canEdit || canDelete) && (\n <Stack gap=\"0.5\" alignment=\"center\">\n {canDelete && (\n <PopupMenu className=\"flex items-center\">\n <PopupMenuToggle as=\"div\">\n <Button icon=\"moreVert\" title={t`More Actions`} disabled={isDeleting} />\n </PopupMenuToggle>\n <PopupMenuOptions>\n <PopupMenuItem label={t`Delete Group`} onClick={handleDelete} />\n </PopupMenuOptions>\n </PopupMenu>\n )}\n\n {canEdit && (\n <Button variant=\"primary\" onClick={handleEdit} disabled={isDeleting}>\n <Trans>Edit Details</Trans>\n </Button>\n )}\n </Stack>\n )\n\n const handleConfirmDelete = async () => {\n try {\n await handleDeleteSecurityGroup()\n handleBack()\n } catch {\n // onError surfaces the toast; the dialog keeps showing deleteError\n }\n }\n\n // Render success state\n return (\n <Stack direction=\"vertical\">\n <ContentHeader title={securityGroup.name || securityGroup.id} projectId={projectId} actions={headerActions} />\n\n <SecurityGroupDetailsView\n securityGroup={securityGroup}\n filteredAndSortedRules={filteredAndSortedRules}\n onDeleteRule={handleDeleteRule}\n isDeletingRule={isDeletingRule}\n deleteRuleError={deleteRuleError}\n filterControls={filterControls}\n onCreateRule={handleCreateRule}\n isCreatingRule={isCreatingRule}\n createRuleError={createRuleError}\n availableSecurityGroups={availableSecurityGroups}\n currentProjectId={projectId}\n permissions={safePermissions}\n />\n\n <EditSecurityGroupModal\n securityGroup={securityGroup}\n open={editModalOpen}\n onClose={handleCloseEditModal}\n onUpdate={handleUpdate}\n isLoading={isUpdating}\n error={updateError}\n />\n\n {deleteModalOpen && (\n <DeleteSecurityGroupDialog\n securityGroup={securityGroup}\n isOpen={deleteModalOpen}\n onClose={handleCloseDeleteModal}\n onDelete={handleConfirmDelete}\n isDeleting={isDeleting}\n error={deleteError}\n />\n )}\n </Stack>\n )\n}\n"],"mappings":";;AAyBA,IAAaG,IAAQH,EAAgB,qEAAA,CAAA,CAAuE;CAC1GI,YAAY;EACVC,SAAS;EACTC,SAAS;EACTC,WAAW,EACTC,MAAM,gCACR;CACF;CACAE,QAAQ,OAAO,EAAEC,YAASC,gBAAQ;EAChC,IAAMC,IAAK,MAAMF,EAAQG,YAAYC,QAAQC,cAAcC,QAAQC,MAAM;GACvEC,YAAYP,EAAOQ;GACnBC,iBAAiBT,EAAOS;EAC1B,CAAA;EACA,OAAO,EAAEC,SAAST,GAAIL,QAAQK,GAAIU,MAAM,KAAK;CAC/C;CACAC,OAAO,EAAEC,qBAAkB,EACzBC,MAAM,CAAC,EAAEC,OAAOF,GAAYH,WAAW,iBAAiB,CAAC,EAC3D;CACAM,WAASC,mDAAA,WAAA;CACTE,YAAY,OAAO,EAAEpB,YAASC,gBAAQ;EACpC,IAAM,EAAEE,kBAAeH,GAEjBqB,IAAoB,MAAOlB,GAAYmB,KAAKC,qBAAqBhB,MAAAA,KAAY,CAAA,GAE7EiB,IAAejC,EAAgB8B,CAAAA;EAUrC,IAPI,CAACG,EAAa,WAOd,CAACA,EAAa,QAAW,SAE3B,MAAMlC,EAAS;GACbmC,IAAI;GACJxB,QAAQ,EAAEQ,WAAWR,EAAOQ,UAAU;EACxC,CAAA;CAEJ;AACF,CAAA"}
|