@cobaltcore-dev/aurora 0.20.3 → 0.22.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 (91) hide show
  1. package/dist/client/AuthProvider-p1e1DuKU.mjs +69 -0
  2. package/dist/client/AuthProvider-p1e1DuKU.mjs.map +1 -0
  3. package/dist/client/{ContentHeader-CIAzorGI.mjs → ContentHeader-BIScbvb4.mjs} +3 -2
  4. package/dist/client/{ContentHeader-CIAzorGI.mjs.map → ContentHeader-BIScbvb4.mjs.map} +1 -1
  5. package/dist/client/DeleteFlavorModal-lFMExcq_.mjs +713 -0
  6. package/dist/client/DeleteFlavorModal-lFMExcq_.mjs.map +1 -0
  7. package/dist/client/{DeletePcaModal-YUlNw8cw.mjs → DeletePcaModal-Dg9dej-H.mjs} +2 -2
  8. package/dist/client/{DeletePcaModal-YUlNw8cw.mjs.map → DeletePcaModal-Dg9dej-H.mjs.map} +1 -1
  9. package/dist/client/{DetailsInfo-Cv3oke-R.mjs → DetailsInfo-DwCo9P1o.mjs} +2 -2
  10. package/dist/client/{DetailsInfo-Cv3oke-R.mjs.map → DetailsInfo-DwCo9P1o.mjs.map} +1 -1
  11. package/dist/client/{FloatingIpActionModals-PLN6CWZ9.mjs → FloatingIpActionModals-CDF8LVtX.mjs} +2 -2
  12. package/dist/client/{FloatingIpActionModals-PLN6CWZ9.mjs.map → FloatingIpActionModals-CDF8LVtX.mjs.map} +1 -1
  13. package/dist/client/{ImageToastNotifications-BQM7HOyE.mjs → ImageToastNotifications-Dzq6jNTm.mjs} +2 -2
  14. package/dist/client/{ImageToastNotifications-BQM7HOyE.mjs.map → ImageToastNotifications-Dzq6jNTm.mjs.map} +1 -1
  15. package/dist/client/Slot-CWb612-_.mjs +28 -0
  16. package/dist/client/Slot-CWb612-_.mjs.map +1 -0
  17. package/dist/client/{_auth-BtRCgHQe.mjs → _auth-Dut7CaMR.mjs} +2 -2
  18. package/dist/client/{_auth-BtRCgHQe.mjs.map → _auth-Dut7CaMR.mjs.map} +1 -1
  19. package/dist/client/{_flavorId-BaNJ1CDY.mjs → _flavorId-BMJcnI7H.mjs} +2 -2
  20. package/dist/client/{_flavorId-BaNJ1CDY.mjs.map → _flavorId-BMJcnI7H.mjs.map} +1 -1
  21. package/dist/client/{_flavorId-BMX239RH.mjs → _flavorId-DUWdFEMH.mjs} +13 -14
  22. package/dist/client/{_flavorId-BMX239RH.mjs.map → _flavorId-DUWdFEMH.mjs.map} +1 -1
  23. package/dist/client/{_floatingIpId-DQFtZNeg.mjs → _floatingIpId-CjQLzpC5.mjs} +2 -2
  24. package/dist/client/{_floatingIpId-DQFtZNeg.mjs.map → _floatingIpId-CjQLzpC5.mjs.map} +1 -1
  25. package/dist/client/{_floatingIpId-E6x99cmW.mjs → _floatingIpId-KcWWq9ZW2.mjs} +4 -4
  26. package/dist/client/_floatingIpId-KcWWq9ZW2.mjs.map +1 -0
  27. package/dist/client/{_imageId-C2662dZr.mjs → _imageId-BkEpwbCr.mjs} +3 -3
  28. package/dist/client/{_imageId-C2662dZr.mjs.map → _imageId-BkEpwbCr.mjs.map} +1 -1
  29. package/dist/client/{_pcaId-CNbmhUF8.mjs → _pcaId-BcwGho7g.mjs} +4 -4
  30. package/dist/client/{_pcaId-CNbmhUF8.mjs.map → _pcaId-BcwGho7g.mjs.map} +1 -1
  31. package/dist/client/{_projectId-B4ScifQf.mjs → _projectId-CoMD6nq6.mjs} +3 -3
  32. package/dist/client/{_projectId-B4ScifQf.mjs.map → _projectId-CoMD6nq6.mjs.map} +1 -1
  33. package/dist/client/{_projectId-CYq4KZXB.mjs → _projectId-D8FHe0gr.mjs} +3 -3
  34. package/dist/client/{_projectId-CYq4KZXB.mjs.map → _projectId-D8FHe0gr.mjs.map} +1 -1
  35. package/dist/client/{_projectId-DxgPaiA7.mjs → _projectId-MjFv839t.mjs} +2 -2
  36. package/dist/client/{_projectId-DxgPaiA7.mjs.map → _projectId-MjFv839t.mjs.map} +1 -1
  37. package/dist/client/{_projectId-DJLjspKf.mjs → _projectId-hMtrqW6m.mjs} +24 -23
  38. package/dist/client/{_projectId-DJLjspKf.mjs.map → _projectId-hMtrqW6m.mjs.map} +1 -1
  39. package/dist/client/{_securityGroupId-ju2ZSIYe.mjs → _securityGroupId-B5-gJe1u.mjs} +3 -3
  40. package/dist/client/{_securityGroupId-ju2ZSIYe.mjs.map → _securityGroupId-B5-gJe1u.mjs.map} +1 -1
  41. package/dist/client/{_securityGroupId-a46ExjsA.mjs → _securityGroupId-fPJWtDPU.mjs} +2 -2
  42. package/dist/client/{_securityGroupId-a46ExjsA.mjs.map → _securityGroupId-fPJWtDPU.mjs.map} +1 -1
  43. package/dist/client/{_storageType-BkhbKG_G.mjs → _storageType-CI5f4CsI.mjs} +2 -2
  44. package/dist/client/{_storageType-BkhbKG_G.mjs.map → _storageType-CI5f4CsI.mjs.map} +1 -1
  45. package/dist/client/{_storageType-ByLL106s.mjs → _storageType-DePX1Aah.mjs} +3 -3
  46. package/dist/client/{_storageType-ByLL106s.mjs.map → _storageType-DePX1Aah.mjs.map} +1 -1
  47. package/dist/client/{aurora-Owys0YMN.mjs → aurora-Dp_NPGe8.mjs} +2 -2
  48. package/dist/client/{aurora-Owys0YMN.mjs.map → aurora-Dp_NPGe8.mjs.map} +1 -1
  49. package/dist/client/components/Auth/LoginForm.d.ts +1 -0
  50. package/dist/client/{flavors-C7PVJUqZ.mjs → flavors-DLxUiBhE.mjs} +2 -2
  51. package/dist/client/{flavors-C7PVJUqZ.mjs.map → flavors-DLxUiBhE.mjs.map} +1 -1
  52. package/dist/client/{flavors-Bwjig3k2.mjs → flavors-DkGuWvge.mjs} +11 -12
  53. package/dist/client/{flavors-Bwjig3k2.mjs.map → flavors-DkGuWvge.mjs.map} +1 -1
  54. package/dist/client/{floatingips-D98DrjA0.mjs → floatingips-DC5OrR8h.mjs} +3 -3
  55. package/dist/client/{floatingips-D98DrjA0.mjs.map → floatingips-DC5OrR8h.mjs.map} +1 -1
  56. package/dist/client/{hooks-BsbKkpZc.mjs → hooks-D0wGUhRF.mjs} +2 -2
  57. package/dist/client/{hooks-BsbKkpZc.mjs.map → hooks-D0wGUhRF.mjs.map} +1 -1
  58. package/dist/client/{images-CfBrglGJ.mjs → images-D9nfOtSC.mjs} +5 -5
  59. package/dist/client/{images-CfBrglGJ.mjs.map → images-D9nfOtSC.mjs.map} +1 -1
  60. package/dist/client/{images-CD7DNwru.mjs → images-h9i7S4LN.mjs} +2 -2
  61. package/dist/client/{images-CD7DNwru.mjs.map → images-h9i7S4LN.mjs.map} +1 -1
  62. package/dist/client/index.js +302 -502
  63. package/dist/client/index.js.map +1 -1
  64. package/dist/client/{objects-CEBrL7k-.mjs → objects-B3Z8h4RU.mjs} +2 -2
  65. package/dist/client/{objects-CEBrL7k-.mjs.map → objects-B3Z8h4RU.mjs.map} +1 -1
  66. package/dist/client/{objects-DvIxpzGI.mjs → objects-CGOfO00N.mjs} +3 -3
  67. package/dist/client/{objects-DvIxpzGI.mjs.map → objects-CGOfO00N.mjs.map} +1 -1
  68. package/dist/client/{pca-ZgGU0UPF.mjs → pca-C7cMT8fW.mjs} +2 -2
  69. package/dist/client/{pca-ZgGU0UPF.mjs.map → pca-C7cMT8fW.mjs.map} +1 -1
  70. package/dist/client/{pca-CpMqoF00.mjs → pca-Nppb8aVa.mjs} +5 -5
  71. package/dist/client/{pca-CpMqoF00.mjs.map → pca-Nppb8aVa.mjs.map} +1 -1
  72. package/dist/client/routeInfo-CdTRlSxv.mjs +32 -0
  73. package/dist/client/routeInfo-CdTRlSxv.mjs.map +1 -0
  74. package/dist/client/routes-Bz4zb2H4.mjs +10 -0
  75. package/dist/client/routes-Bz4zb2H4.mjs.map +1 -0
  76. package/dist/client/routes-CaqfgBYu.mjs +112 -0
  77. package/dist/client/routes-CaqfgBYu.mjs.map +1 -0
  78. package/dist/client/{securitygroups-BwhVyNmZ.mjs → securitygroups-JMvcJLpb.mjs} +2 -2
  79. package/dist/client/{securitygroups-BwhVyNmZ.mjs.map → securitygroups-JMvcJLpb.mjs.map} +1 -1
  80. package/dist/client/store/AuthProvider.d.ts +10 -11
  81. package/dist/server/index.js +1 -10
  82. package/package.json +1 -2
  83. package/dist/client/AuthProvider-Cj_Btfnc.mjs +0 -56
  84. package/dist/client/AuthProvider-Cj_Btfnc.mjs.map +0 -1
  85. package/dist/client/DeleteFlavorModal-Dwm0Hh84.mjs +0 -638
  86. package/dist/client/DeleteFlavorModal-Dwm0Hh84.mjs.map +0 -1
  87. package/dist/client/_floatingIpId-E6x99cmW.mjs.map +0 -1
  88. package/dist/client/routeInfo-Ct5r4Gen.mjs +0 -56
  89. package/dist/client/routeInfo-Ct5r4Gen.mjs.map +0 -1
  90. package/dist/client/useErrorTranslation-Dc0eE8Zt.mjs +0 -83
  91. package/dist/client/useErrorTranslation-Dc0eE8Zt.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"_projectId-B4ScifQf.mjs","names":["resolveProjectScope","input","scopeProject","id","projectId","userProject","createFileRoute","TRPCClientError","resolveProjectScope","Route","component","lazyRouteComponent","$$splitComponentImporter","errorComponent","$$splitErrorComponentImporter","loader","options","context","params","scopeData","trpcClient","auth","setCurrentScope","mutate","type","projectId","error","data","code","undefined","availableServices","project","Promise","all","getAvailableServices","query","getProject","catch","projectScopeStatus","scopeProject","userProject","Error","statusCode","accountId","domain","id","description","crumbDomain","path","name","crumbProject"],"sources":["../../src/client/routes/_auth/projects/-components/resolveProjectScope.ts","../../src/client/routes/_auth/projects/$projectId.tsx"],"sourcesContent":["export type ProjectScopeStatus = \"valid\" | \"not_found\" | \"scope_failed\"\n\n/**\n * Determines the status of a project scope operation by comparing the requested project\n * against the successfully scoped project and the user's available projects.\n *\n * @param input - The scope resolution input\n * @param input.projectId - The project ID that was requested to be scoped\n * @param input.scopeProject - The project object returned from the scope operation (null/undefined if scope failed)\n * @param input.userProject - The project fetched by ID (null/undefined if not found or couldn't be fetched)\n *\n * @returns ProjectScopeStatus\n * - \"valid\": The scope operation succeeded and the scoped project matches the requested project ID\n * - \"not_found\": The project doesn't exist or the user doesn't have access to it (only when userProject was successfully fetched)\n * - \"scope_failed\": The project exists but the scope operation failed, OR we couldn't fetch the project record\n *\n * @example\n * // Successful scope\n * resolveProjectScope({\n * projectId: \"project-123\",\n * scopeProject: { id: \"project-123\" },\n * userProject: { id: \"project-123\" }\n * }) // returns \"valid\"\n *\n * @example\n * // Project not found (when we successfully fetched the project record)\n * resolveProjectScope({\n * projectId: \"project-999\",\n * scopeProject: undefined,\n * userProject: { id: \"project-123\" }\n * }) // returns \"not_found\"\n *\n * @example\n * // Scope operation failed for an accessible project\n * resolveProjectScope({\n * projectId: \"project-123\",\n * scopeProject: undefined,\n * userProject: { id: \"project-123\" }\n * }) // returns \"scope_failed\"\n *\n * @example\n * // Can't verify project access (userProject fetch failed)\n * resolveProjectScope({\n * projectId: \"project-123\",\n * scopeProject: undefined,\n * userProject: null\n * }) // returns \"scope_failed\"\n */\nexport const resolveProjectScope = (input: {\n projectId: string\n scopeProject: { id?: string } | null | undefined\n userProject: { id: string } | null | undefined\n}): ProjectScopeStatus => {\n if (input.scopeProject?.id === input.projectId) {\n return \"valid\"\n }\n\n // If we couldn't fetch userProject at all, we can't verify the project's existence.\n // Treat this as a scope failure rather than \"not_found\" to avoid misleading 404 errors\n // when the real issue is a backend/network failure.\n if (input.userProject == null) {\n return \"scope_failed\"\n }\n\n return input.userProject.id === input.projectId ? \"scope_failed\" : \"not_found\"\n}\n","import { createFileRoute, Outlet, useLoaderData, useRouteContext, useRouter } 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\"\nimport { TRPCClientError } from \"@trpc/client\"\nimport { resolveProjectScope } from \"./-components/resolveProjectScope\"\nimport { StatusError } from \"@/client/components/Error/StatusError\"\nimport { useLingui } from \"@lingui/react/macro\"\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 let scopeData\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 && error.data?.code === \"NOT_FOUND\") {\n scopeData = undefined\n } else {\n throw error\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 projectScopeStatus = resolveProjectScope({\n projectId: params.projectId,\n scopeProject: scopeData?.project,\n userProject: project,\n })\n\n if (projectScopeStatus === \"scope_failed\") {\n throw new Error(\"Failed to scope session to project\")\n }\n\n if (projectScopeStatus === \"not_found\") {\n const error = new Error(\"Project not found\") as Error & { statusCode?: number }\n error.statusCode = 404\n throw error\n }\n\n const accountId = scopeData?.domain?.id || \"\"\n const description = project?.description ?? null\n\n return {\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 { availableServices, projectId, crumbProject, crumbDomain } = useLoaderData({\n from: Route.id,\n })\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\nfunction ProjectErrorComponent({ error }: { error: Error & { statusCode?: number } }) {\n const { t } = useLingui()\n const navigate = Route.useNavigate()\n const router = useRouter()\n\n // Handle 404 errors with a custom UI\n if (error.statusCode === 404 || error.message === \"Project not found\") {\n return (\n <StatusError\n statusCode={404}\n title={t`Project Not Found`}\n message={t`The project you are looking for doesn't exist or you don't have access to it.`}\n onHomeClick={() => {\n navigate({ to: \"/projects\" })\n }}\n onBackClick={() => {\n router.history.back()\n }}\n />\n )\n }\n\n // For all other errors, use the default RouteError component\n return <RouteError error={error} />\n}\n"],"mappings":";;;AAgDA,IAAaA,KAAuBC,MAK9BA,EAAMC,cAAcC,OAAOF,EAAMG,YAC5B,UAMLH,EAAMI,eAAe,QAIlBJ,EAAMI,YAAYF,OAAOF,EAAMG,YAH7B,iBAG0D,aCpDxDK,IAAQH,EAAgB,4BAAA,EAA8B;CACjEI,WAASC,6CAAA,WAAA;CACTE,gBAAcF,6CAAA,gBAAA;CACdI,QAAQ,OAAOC,MAAAA;EACb,IAAM,EAAEC,YAASC,cAAWF,GAExBG;EAEJ,IAAI;GACFA,IAAY,MAAMF,EAAQG,YAAYC,KAAKC,gBAAgBC,OAAO;IAChEC,MAAM;IACNC,WAAWP,EAAOO,aAAa;GACjC,CAAA;EACF,SAASC,GAAO;GACd,IAAIA,aAAiBnB,KAAmBmB,EAAMC,MAAMC,SAAS,aAC3DT,IAAYU,KAAAA;QAEZ,MAAMH;EAEV;EAEA,IAAM,CAACI,GAAmBC,KAAW,MAAMC,QAAQC,IAAI,CACrDhB,EAAQG,YAAYC,KAAKa,qBAAqBC,MAAAA,GAC9ClB,EAAQG,YAAYW,QAAQK,WAAWD,MAAM,EAAEV,WAAWP,EAAOO,UAAU,CAAA,EAAGY,YAAY,IAAA,CAAA,CAC3F,GAEKC,IAAqB9B,EAAoB;GAC7CiB,WAAWP,EAAOO;GAClBc,cAAcpB,GAAWY;GACzBS,aAAaT;EACf,CAAA;EAEA,IAAIO,MAAuB,gBACzB,MAAUG,MAAM,oCAAA;EAGlB,IAAIH,MAAuB,aAAa;GACtC,IAAMZ,IAAQ,gBAAIe,MAAM,mBAAA;GAExB,MADAf,EAAMgB,aAAa,KACbhB;EACR;EAEA,IAAMiB,IAAYxB,GAAWyB,QAAQC,MAAM,IACrCC,IAAcf,GAASe,eAAe;EAE5C,OAAO;GACL1B,YAAYH,EAAQG;GACpB2B,aAAa;IAAEC,MAAM;IAAaC,MAAM9B,GAAWyB,QAAQK;GAAK;GAChEC,cAAc/B,GAAWY;GACzBD;GACAa;GACAlB,WAAWP,EAAOO;GAClBqB;EACF;CACF;AACF,CAAA"}
1
+ {"version":3,"file":"_projectId-CoMD6nq6.mjs","names":["resolveProjectScope","input","scopeProject","id","projectId","userProject","createFileRoute","TRPCClientError","resolveProjectScope","Route","component","lazyRouteComponent","$$splitComponentImporter","errorComponent","$$splitErrorComponentImporter","loader","options","context","params","scopeData","trpcClient","auth","setCurrentScope","mutate","type","projectId","error","data","code","undefined","availableServices","project","Promise","all","getAvailableServices","query","getProject","catch","projectScopeStatus","scopeProject","userProject","Error","statusCode","accountId","domain","id","description","crumbDomain","path","name","crumbProject"],"sources":["../../src/client/routes/_auth/projects/-components/resolveProjectScope.ts","../../src/client/routes/_auth/projects/$projectId.tsx"],"sourcesContent":["export type ProjectScopeStatus = \"valid\" | \"not_found\" | \"scope_failed\"\n\n/**\n * Determines the status of a project scope operation by comparing the requested project\n * against the successfully scoped project and the user's available projects.\n *\n * @param input - The scope resolution input\n * @param input.projectId - The project ID that was requested to be scoped\n * @param input.scopeProject - The project object returned from the scope operation (null/undefined if scope failed)\n * @param input.userProject - The project fetched by ID (null/undefined if not found or couldn't be fetched)\n *\n * @returns ProjectScopeStatus\n * - \"valid\": The scope operation succeeded and the scoped project matches the requested project ID\n * - \"not_found\": The project doesn't exist or the user doesn't have access to it (only when userProject was successfully fetched)\n * - \"scope_failed\": The project exists but the scope operation failed, OR we couldn't fetch the project record\n *\n * @example\n * // Successful scope\n * resolveProjectScope({\n * projectId: \"project-123\",\n * scopeProject: { id: \"project-123\" },\n * userProject: { id: \"project-123\" }\n * }) // returns \"valid\"\n *\n * @example\n * // Project not found (when we successfully fetched the project record)\n * resolveProjectScope({\n * projectId: \"project-999\",\n * scopeProject: undefined,\n * userProject: { id: \"project-123\" }\n * }) // returns \"not_found\"\n *\n * @example\n * // Scope operation failed for an accessible project\n * resolveProjectScope({\n * projectId: \"project-123\",\n * scopeProject: undefined,\n * userProject: { id: \"project-123\" }\n * }) // returns \"scope_failed\"\n *\n * @example\n * // Can't verify project access (userProject fetch failed)\n * resolveProjectScope({\n * projectId: \"project-123\",\n * scopeProject: undefined,\n * userProject: null\n * }) // returns \"scope_failed\"\n */\nexport const resolveProjectScope = (input: {\n projectId: string\n scopeProject: { id?: string } | null | undefined\n userProject: { id: string } | null | undefined\n}): ProjectScopeStatus => {\n if (input.scopeProject?.id === input.projectId) {\n return \"valid\"\n }\n\n // If we couldn't fetch userProject at all, we can't verify the project's existence.\n // Treat this as a scope failure rather than \"not_found\" to avoid misleading 404 errors\n // when the real issue is a backend/network failure.\n if (input.userProject == null) {\n return \"scope_failed\"\n }\n\n return input.userProject.id === input.projectId ? \"scope_failed\" : \"not_found\"\n}\n","import { createFileRoute, Outlet, useLoaderData, useRouteContext, useRouter } 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\"\nimport { TRPCClientError } from \"@trpc/client\"\nimport { resolveProjectScope } from \"./-components/resolveProjectScope\"\nimport { StatusError } from \"@/client/components/Error/StatusError\"\nimport { useLingui } from \"@lingui/react/macro\"\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 let scopeData\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 && error.data?.code === \"NOT_FOUND\") {\n scopeData = undefined\n } else {\n throw error\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 projectScopeStatus = resolveProjectScope({\n projectId: params.projectId,\n scopeProject: scopeData?.project,\n userProject: project,\n })\n\n if (projectScopeStatus === \"scope_failed\") {\n throw new Error(\"Failed to scope session to project\")\n }\n\n if (projectScopeStatus === \"not_found\") {\n const error = new Error(\"Project not found\") as Error & { statusCode?: number }\n error.statusCode = 404\n throw error\n }\n\n const accountId = scopeData?.domain?.id || \"\"\n const description = project?.description ?? null\n\n return {\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 { availableServices, projectId, crumbProject, crumbDomain } = useLoaderData({\n from: Route.id,\n })\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\nfunction ProjectErrorComponent({ error }: { error: Error & { statusCode?: number } }) {\n const { t } = useLingui()\n const navigate = Route.useNavigate()\n const router = useRouter()\n\n // Handle 404 errors with a custom UI\n if (error.statusCode === 404 || error.message === \"Project not found\") {\n return (\n <StatusError\n statusCode={404}\n title={t`Project Not Found`}\n message={t`The project you are looking for doesn't exist or you don't have access to it.`}\n onHomeClick={() => {\n navigate({ to: \"/projects\" })\n }}\n onBackClick={() => {\n router.history.back()\n }}\n />\n )\n }\n\n // For all other errors, use the default RouteError component\n return <RouteError error={error} />\n}\n"],"mappings":";;;AAgDA,IAAaA,KAAuBC,MAK9BA,EAAMC,cAAcC,OAAOF,EAAMG,YAC5B,UAMLH,EAAMI,eAAe,QAIlBJ,EAAMI,YAAYF,OAAOF,EAAMG,YAH7B,iBAG0D,aCpDxDK,IAAQH,EAAgB,4BAAA,EAA8B;CACjEI,WAASC,6CAAA,WAAA;CACTE,gBAAcF,6CAAA,gBAAA;CACdI,QAAQ,OAAOC,MAAAA;EACb,IAAM,EAAEC,YAASC,cAAWF,GAExBG;EAEJ,IAAI;GACFA,IAAY,MAAMF,EAAQG,YAAYC,KAAKC,gBAAgBC,OAAO;IAChEC,MAAM;IACNC,WAAWP,EAAOO,aAAa;GACjC,CAAA;EACF,SAASC,GAAO;GACd,IAAIA,aAAiBnB,KAAmBmB,EAAMC,MAAMC,SAAS,aAC3DT,IAAYU,KAAAA;QAEZ,MAAMH;EAEV;EAEA,IAAM,CAACI,GAAmBC,KAAW,MAAMC,QAAQC,IAAI,CACrDhB,EAAQG,YAAYC,KAAKa,qBAAqBC,MAAAA,GAC9ClB,EAAQG,YAAYW,QAAQK,WAAWD,MAAM,EAAEV,WAAWP,EAAOO,UAAU,CAAA,EAAGY,YAAY,IAAA,CAAA,CAC3F,GAEKC,IAAqB9B,EAAoB;GAC7CiB,WAAWP,EAAOO;GAClBc,cAAcpB,GAAWY;GACzBS,aAAaT;EACf,CAAA;EAEA,IAAIO,MAAuB,gBACzB,MAAUG,MAAM,oCAAA;EAGlB,IAAIH,MAAuB,aAAa;GACtC,IAAMZ,IAAQ,gBAAIe,MAAM,mBAAA;GAExB,MADAf,EAAMgB,aAAa,KACbhB;EACR;EAEA,IAAMiB,IAAYxB,GAAWyB,QAAQC,MAAM,IACrCC,IAAcf,GAASe,eAAe;EAE5C,OAAO;GACL1B,YAAYH,EAAQG;GACpB2B,aAAa;IAAEC,MAAM;IAAaC,MAAM9B,GAAWyB,QAAQK;GAAK;GAChEC,cAAc/B,GAAWY;GACzBD;GACAa;GACAlB,WAAWP,EAAOO;GAClBqB;EACF;CACF;AACF,CAAA"}
@@ -1,8 +1,8 @@
1
1
  import { f as e, tt as t, x as n } from "./build-DgeVmENN.mjs";
2
- import { n as r } from "./routeInfo-Ct5r4Gen.mjs";
2
+ import { t as r } from "./Slot-CWb612-_.mjs";
3
3
  import { t as i } from "./helpers-1PpYf-fC.mjs";
4
4
  import { t as a } from "./pcaAccess-Br4kVjLQ.mjs";
5
- import { t as o } from "./ContentHeader-CIAzorGI.mjs";
5
+ import { t as o } from "./ContentHeader-BIScbvb4.mjs";
6
6
  import { jsx as s, jsxs as c } from "react/jsx-runtime";
7
7
  import { useLoaderData as l, useNavigate as u, useRouteContext as d } from "@tanstack/react-router";
8
8
  import { Trans as f, useLingui as p } from "@lingui/react";
@@ -95,4 +95,4 @@ function h() {
95
95
  //#endregion
96
96
  export { h as component };
97
97
 
98
- //# sourceMappingURL=_projectId-CYq4KZXB.mjs.map
98
+ //# sourceMappingURL=_projectId-D8FHe0gr.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"_projectId-CYq4KZXB.mjs","names":["useLoaderData","useNavigate","Card","Icon","Stack","getServiceIndex","Trans","useLingui","useRouteContext","ContentHeader","Slot","canAccessClavisPca","Route","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","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\"\nimport type { RouteInfo } from \"@/client/routes/routeInfo\"\nimport { canAccessClavisPca } from \"@/client/routes/_auth/projects/$projectId/services/pca/-components/pcaAccess\"\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 <div className=\"flex items-center gap-1\">\n <p className=\"text-theme-light text-xs leading-5 font-medium\">{group}</p>\n <Icon icon=\"expandMore\" 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 { 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 (canAccessClavisPca(serviceIndex, enabledServices)) {\n cards.push({ group: t`Services`, label: t`PCA (Clavis)`, to: `${base}/services/pca`, service: \"pca\" })\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
+ {"version":3,"file":"_projectId-D8FHe0gr.mjs","names":["useLoaderData","useNavigate","Card","Icon","Stack","getServiceIndex","Trans","useLingui","useRouteContext","ContentHeader","Slot","canAccessClavisPca","Route","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","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\"\nimport type { RouteInfo } from \"@/client/routes/routeInfo\"\nimport { canAccessClavisPca } from \"@/client/routes/_auth/projects/$projectId/services/pca/-components/pcaAccess\"\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 <div className=\"flex items-center gap-1\">\n <p className=\"text-theme-light text-xs leading-5 font-medium\">{group}</p>\n <Icon icon=\"expandMore\" 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 { 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 (canAccessClavisPca(serviceIndex, enabledServices)) {\n cards.push({ group: t`Services`, label: t`PCA (Clavis)`, to: `${base}/services/pca`, service: \"pca\" })\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,6 +1,6 @@
1
1
  import { t as e } from "./RouteError-BmZRk_ss.mjs";
2
2
  import { t } from "./StatusError-CLB026fg.mjs";
3
- import { t as n } from "./_projectId-B4ScifQf.mjs";
3
+ import { t as n } from "./_projectId-CoMD6nq6.mjs";
4
4
  import { jsx as r } from "react/jsx-runtime";
5
5
  import { useRouter as i } from "@tanstack/react-router";
6
6
  import { useLingui as a } from "@lingui/react";
@@ -22,4 +22,4 @@ function o({ error: o }) {
22
22
  //#endregion
23
23
  export { o as errorComponent };
24
24
 
25
- //# sourceMappingURL=_projectId-DxgPaiA7.mjs.map
25
+ //# sourceMappingURL=_projectId-MjFv839t.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"_projectId-DxgPaiA7.mjs","names":["useRouter","RouteError","StatusError","useLingui","Route","ProjectErrorComponent","error","Error","statusCode","t","navigate","useNavigate","router","message","to","history","back","errorComponent"],"sources":["../../src/client/routes/_auth/projects/$projectId.tsx?tsr-split=errorComponent"],"sourcesContent":["import { createFileRoute, Outlet, useLoaderData, useRouteContext, useRouter } 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\"\nimport { TRPCClientError } from \"@trpc/client\"\nimport { resolveProjectScope } from \"./-components/resolveProjectScope\"\nimport { StatusError } from \"@/client/components/Error/StatusError\"\nimport { useLingui } from \"@lingui/react/macro\"\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 let scopeData\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 && error.data?.code === \"NOT_FOUND\") {\n scopeData = undefined\n } else {\n throw error\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 projectScopeStatus = resolveProjectScope({\n projectId: params.projectId,\n scopeProject: scopeData?.project,\n userProject: project,\n })\n\n if (projectScopeStatus === \"scope_failed\") {\n throw new Error(\"Failed to scope session to project\")\n }\n\n if (projectScopeStatus === \"not_found\") {\n const error = new Error(\"Project not found\") as Error & { statusCode?: number }\n error.statusCode = 404\n throw error\n }\n\n const accountId = scopeData?.domain?.id || \"\"\n const description = project?.description ?? null\n\n return {\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 { availableServices, projectId, crumbProject, crumbDomain } = useLoaderData({\n from: Route.id,\n })\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\nfunction ProjectErrorComponent({ error }: { error: Error & { statusCode?: number } }) {\n const { t } = useLingui()\n const navigate = Route.useNavigate()\n const router = useRouter()\n\n // Handle 404 errors with a custom UI\n if (error.statusCode === 404 || error.message === \"Project not found\") {\n return (\n <StatusError\n statusCode={404}\n title={t`Project Not Found`}\n message={t`The project you are looking for doesn't exist or you don't have access to it.`}\n onHomeClick={() => {\n navigate({ to: \"/projects\" })\n }}\n onBackClick={() => {\n router.history.back()\n }}\n />\n )\n }\n\n // For all other errors, use the default RouteError component\n return <RouteError error={error} />\n}\n"],"mappings":";;;;;;;AAgHA,SAASK,EAAsB,EAAEC,YAAmD;CAClF,IAAM,EAAA,MAAA,GAAA,GAAA,MAAQH,EAAAA,GACRO,IAAWN,EAAMO,YAAW,GAC5BC,IAASZ,EAAAA;CAoBf,OAjBIM,EAAME,eAAe,OAAOF,EAAMO,YAAY,sBAE9C,gBAAC,GAAA;EACC,YAAY;EACZ,OAAOJ,EAAAA,EAAC,EAAA,IAAA,SAAA,CAAA;EACR,SAASA,EAAAA,EAAC,EAAA,IAAA,SAAA,CAAA;EACV,mBAAa;GACXC,EAAS,EAAEI,IAAI,YAAY,CAAA;EAC7B;EACA,mBAAa;GACXF,EAAOG,QAAQC,KAAI;EACrB;MAMC,gBAAC,GAAA,EAAkBV,SAAAA,CAAAA;AAC5B"}
1
+ {"version":3,"file":"_projectId-MjFv839t.mjs","names":["useRouter","RouteError","StatusError","useLingui","Route","ProjectErrorComponent","error","Error","statusCode","t","navigate","useNavigate","router","message","to","history","back","errorComponent"],"sources":["../../src/client/routes/_auth/projects/$projectId.tsx?tsr-split=errorComponent"],"sourcesContent":["import { createFileRoute, Outlet, useLoaderData, useRouteContext, useRouter } 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\"\nimport { TRPCClientError } from \"@trpc/client\"\nimport { resolveProjectScope } from \"./-components/resolveProjectScope\"\nimport { StatusError } from \"@/client/components/Error/StatusError\"\nimport { useLingui } from \"@lingui/react/macro\"\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 let scopeData\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 && error.data?.code === \"NOT_FOUND\") {\n scopeData = undefined\n } else {\n throw error\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 projectScopeStatus = resolveProjectScope({\n projectId: params.projectId,\n scopeProject: scopeData?.project,\n userProject: project,\n })\n\n if (projectScopeStatus === \"scope_failed\") {\n throw new Error(\"Failed to scope session to project\")\n }\n\n if (projectScopeStatus === \"not_found\") {\n const error = new Error(\"Project not found\") as Error & { statusCode?: number }\n error.statusCode = 404\n throw error\n }\n\n const accountId = scopeData?.domain?.id || \"\"\n const description = project?.description ?? null\n\n return {\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 { availableServices, projectId, crumbProject, crumbDomain } = useLoaderData({\n from: Route.id,\n })\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\nfunction ProjectErrorComponent({ error }: { error: Error & { statusCode?: number } }) {\n const { t } = useLingui()\n const navigate = Route.useNavigate()\n const router = useRouter()\n\n // Handle 404 errors with a custom UI\n if (error.statusCode === 404 || error.message === \"Project not found\") {\n return (\n <StatusError\n statusCode={404}\n title={t`Project Not Found`}\n message={t`The project you are looking for doesn't exist or you don't have access to it.`}\n onHomeClick={() => {\n navigate({ to: \"/projects\" })\n }}\n onBackClick={() => {\n router.history.back()\n }}\n />\n )\n }\n\n // For all other errors, use the default RouteError component\n return <RouteError error={error} />\n}\n"],"mappings":";;;;;;;AAgHA,SAASK,EAAsB,EAAEC,YAAmD;CAClF,IAAM,EAAA,MAAA,GAAA,GAAA,MAAQH,EAAAA,GACRO,IAAWN,EAAMO,YAAW,GAC5BC,IAASZ,EAAAA;CAoBf,OAjBIM,EAAME,eAAe,OAAOF,EAAMO,YAAY,sBAE9C,gBAAC,GAAA;EACC,YAAY;EACZ,OAAOJ,EAAAA,EAAC,EAAA,IAAA,SAAA,CAAA;EACR,SAASA,EAAAA,EAAC,EAAA,IAAA,SAAA,CAAA;EACV,mBAAa;GACXC,EAAS,EAAEI,IAAI,YAAY,CAAA;EAC7B;EACA,mBAAa;GACXF,EAAOG,QAAQC,KAAI;EACrB;MAMC,gBAAC,GAAA,EAAkBV,SAAAA,CAAAA;AAC5B"}
@@ -1,8 +1,9 @@
1
1
  import { Q as e, T as t, Z as n, _ as r, i, j as a, nt as o, t as s, tt as c, z as l } from "./build-DgeVmENN.mjs";
2
- import { n as u, t as d } from "./routeInfo-Ct5r4Gen.mjs";
3
- import { t as f } from "./_projectId-B4ScifQf.mjs";
4
- import { t as p } from "./helpers-1PpYf-fC.mjs";
5
- import { t as m } from "./pcaAccess-Br4kVjLQ.mjs";
2
+ import { t as u } from "./Slot-CWb612-_.mjs";
3
+ import { t as d } from "./_projectId-CoMD6nq6.mjs";
4
+ import { t as f } from "./helpers-1PpYf-fC.mjs";
5
+ import { t as p } from "./pcaAccess-Br4kVjLQ.mjs";
6
+ import { t as m } from "./routeInfo-CdTRlSxv.mjs";
6
7
  import { Fragment as h, jsx as g, jsxs as _ } from "react/jsx-runtime";
7
8
  import { useEffect as v, useMemo as y, useState as b } from "react";
8
9
  import { Outlet as x, useLoaderData as S, useMatches as C, useNavigate as w, useParams as T, useRouteContext as E } from "@tanstack/react-router";
@@ -10,11 +11,11 @@ import { useLingui as D } from "@lingui/react";
10
11
  import { i18n as O } from "@lingui/core";
11
12
  //#region src/client/routes/_auth/projects/-components/SideNavBar.tsx
12
13
  var k = ({ projectId: e, projectName: n, domainName: o, sections: c }) => {
13
- let l = w(), f = C(), { provider: p } = T({ strict: !1 }), { slots: m } = E({ strict: !1 }), y = (e) => m?.serviceBadge ? /*#__PURE__*/ g(u, {
14
- component: m.serviceBadge,
14
+ let l = w(), d = C(), { provider: f } = T({ strict: !1 }), { slots: p } = E({ strict: !1 }), y = (e) => p?.serviceBadge ? /*#__PURE__*/ g(u, {
15
+ component: p.serviceBadge,
15
16
  useShadowDOM: !1,
16
17
  currentService: e
17
- }) : null, x = [...f].reverse().find((e) => d(e.staticData)), S = x && d(x.staticData) ? x.staticData : void 0, D = S?.section ?? null, O = S?.service ?? null, [k, A] = b(() => Object.fromEntries(c.map((e) => [e.section, 0])));
18
+ }) : null, x = [...d].reverse().find((e) => m(e.staticData)), S = x && m(x.staticData) ? x.staticData : void 0, D = S?.section ?? null, O = S?.service ?? null, [k, A] = b(() => Object.fromEntries(c.map((e) => [e.section, 0])));
18
19
  return v(() => {
19
20
  D && A((e) => ({
20
21
  ...e,
@@ -41,7 +42,7 @@ var k = ({ projectId: e, projectName: n, domainName: o, sections: c }) => {
41
42
  label: t,
42
43
  open: !0,
43
44
  children: n.map((t) => {
44
- let n = D === e && (D === "storage" && O === "containers" ? t.params.provider === p : O === t.service);
45
+ let n = D === e && (D === "storage" && O === "containers" ? t.params.provider === f : O === t.service);
45
46
  return /*#__PURE__*/ g(r, {
46
47
  onClick: () => t.navigate(l),
47
48
  label: /*#__PURE__*/ _("span", {
@@ -52,13 +53,13 @@ var k = ({ projectId: e, projectName: n, domainName: o, sections: c }) => {
52
53
  }, t.service);
53
54
  })
54
55
  }, `${e}-${k[e]}`))
55
- ] }) }), m?.sideNavBanner && /*#__PURE__*/ g(u, { component: m.sideNavBanner })] })
56
+ ] }) }), p?.sideNavBanner && /*#__PURE__*/ g(u, { component: p.sideNavBanner })] })
56
57
  });
57
58
  };
58
59
  //#endregion
59
60
  //#region src/client/routes/_auth/projects/-components/buildNavSections.ts
60
61
  function A(e, t, n) {
61
- let r = p(t), i = (e) => !n || n.includes(e), a = [...r.image?.glance && i("images") ? [{
62
+ let r = f(t), i = (e) => !n || n.includes(e), a = [...r.image?.glance && i("images") ? [{
62
63
  service: "images",
63
64
  label: O._({ id: "an5hVd" }),
64
65
  navigate: (t) => t({
@@ -122,7 +123,7 @@ function A(e, t, n) {
122
123
  provider: "ceph",
123
124
  storageType: "buckets"
124
125
  }
125
- }] : []], c = m(r, n) ? [{
126
+ }] : []], c = p(r, n) ? [{
126
127
  service: "pca",
127
128
  label: O._({ id: "miy5mb" }),
128
129
  navigate: (t) => t({
@@ -177,45 +178,45 @@ function j({ projectInfo: t }) {
177
178
  label: n._({ id: "i0qMbr" }),
178
179
  onClick: () => i({ to: "/projects" })
179
180
  });
180
- let c = a.filter((e) => e.routeId !== "/_auth/projects/$projectId" && e.routeId.startsWith("/_auth/projects/$projectId")), l = c[c.length - 1], u = l && d(l.staticData) ? l.staticData : void 0, f = t.domain?.name ? `${t.domain.name}/${t.name}` : t.name;
181
+ let c = a.filter((e) => e.routeId !== "/_auth/projects/$projectId" && e.routeId.startsWith("/_auth/projects/$projectId")), l = c[c.length - 1], u = l && m(l.staticData) ? l.staticData : void 0, d = t.domain?.name ? `${t.domain.name}/${t.name}` : t.name;
181
182
  if (!l || !u) return o.push({
182
- label: f,
183
+ label: d,
183
184
  active: !0
184
185
  }), o;
185
186
  o.push({
186
- label: f,
187
+ label: d,
187
188
  onClick: () => i({
188
189
  to: "/projects/$projectId",
189
190
  params: { projectId: s }
190
191
  })
191
192
  });
192
- let p = l.params;
193
+ let f = l.params;
193
194
  if (u.sectionCrumb?.to) {
194
195
  let { labelKey: t, to: n } = u.sectionCrumb, r = t ? e[t] : void 0;
195
196
  o.push({
196
197
  label: r,
197
198
  onClick: () => i({
198
199
  to: n,
199
- params: p
200
+ params: f
200
201
  })
201
202
  });
202
203
  }
203
204
  if (u.crumb) {
204
- let { labelKey: t, to: n, useParamAsLabel: a } = u.crumb, s = a ? r(p[a]) : t ? e[t] : void 0;
205
+ let { labelKey: t, to: n, useParamAsLabel: a } = u.crumb, s = a ? r(f[a]) : t ? e[t] : void 0;
205
206
  if (u.isDetail) {
206
207
  if (o.push({
207
208
  label: s,
208
209
  onClick: () => i({
209
210
  to: n,
210
- params: p
211
+ params: f
211
212
  })
212
213
  }), u.intermediateCrumb) {
213
- let { to: e, useParamAsLabel: t, useParentTitleAsLabel: n } = u.intermediateCrumb, r = c[c.length - 2]?.meta?.find((e) => e != null && "title" in e)?.title, a = n ? r ?? (t ? p[t] : void 0) : t ? p[t] : void 0;
214
+ let { to: e, useParamAsLabel: t, useParentTitleAsLabel: n } = u.intermediateCrumb, r = c[c.length - 2]?.meta?.find((e) => e != null && "title" in e)?.title, a = n ? r ?? (t ? f[t] : void 0) : t ? f[t] : void 0;
214
215
  o.push(e ? {
215
216
  label: a,
216
217
  onClick: () => i({
217
218
  to: e,
218
- params: p
219
+ params: f
219
220
  })
220
221
  } : { label: a });
221
222
  }
@@ -228,7 +229,7 @@ function j({ projectInfo: t }) {
228
229
  label: s,
229
230
  onClick: () => i({
230
231
  to: n,
231
- params: p
232
+ params: f
232
233
  })
233
234
  } : {
234
235
  label: s,
@@ -253,7 +254,7 @@ function j({ projectInfo: t }) {
253
254
  //#endregion
254
255
  //#region src/client/routes/_auth/projects/$projectId.tsx?tsr-split=component
255
256
  function M() {
256
- let { availableServices: e, projectId: t, crumbProject: r, crumbDomain: i } = S({ from: f.id }), { enabledServices: a } = E({ strict: !1 });
257
+ let { availableServices: e, projectId: t, crumbProject: r, crumbDomain: i } = S({ from: d.id }), { enabledServices: a } = E({ strict: !1 });
257
258
  return /*#__PURE__*/ g(l, {
258
259
  embedded: !0,
259
260
  sideNavigation: /*#__PURE__*/ g(k, {
@@ -287,4 +288,4 @@ function M() {
287
288
  //#endregion
288
289
  export { M as component };
289
290
 
290
- //# sourceMappingURL=_projectId-DJLjspKf.mjs.map
291
+ //# sourceMappingURL=_projectId-hMtrqW6m.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"_projectId-DJLjspKf.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","canAccessClavisPca","buildNavSections","projectId","availableServices","enabledServices","serviceIndex","isEnabled","service","includes","computeServices","label","t","navigate","nav","to","params","networkServices","storageServices","provider","storageType","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","projectMatches","filter","m","routeId","startsWith","deepest","length","info","staticData","undefined","combinedLabel","domain","name","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\"\nimport { canAccessClavisPca } from \"@/client/routes/_auth/projects/$projectId/services/pca/-components/pcaAccess\"\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 ...(serviceIndex?.[\"object-store-ceph\"]?.[\"ceph\"] && 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 const clavisServices: NavItem[] = canAccessClavisPca(serviceIndex, enabledServices)\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 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 const combinedLabel = projectInfo.domain?.name ? `${projectInfo.domain.name}/${projectInfo.name}` : projectInfo.name\n\n if (!deepest || !info) {\n items.push({ label: combinedLabel, active: true })\n return items\n }\n\n items.push({\n label: combinedLabel,\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, useRouter } 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\"\nimport { TRPCClientError } from \"@trpc/client\"\nimport { resolveProjectScope } from \"./-components/resolveProjectScope\"\nimport { StatusError } from \"@/client/components/Error/StatusError\"\nimport { useLingui } from \"@lingui/react/macro\"\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 let scopeData\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 && error.data?.code === \"NOT_FOUND\") {\n scopeData = undefined\n } else {\n throw error\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 projectScopeStatus = resolveProjectScope({\n projectId: params.projectId,\n scopeProject: scopeData?.project,\n userProject: project,\n })\n\n if (projectScopeStatus === \"scope_failed\") {\n throw new Error(\"Failed to scope session to project\")\n }\n\n if (projectScopeStatus === \"not_found\") {\n const error = new Error(\"Project not found\") as Error & { statusCode?: number }\n error.statusCode = 404\n throw error\n }\n\n const accountId = scopeData?.domain?.id || \"\"\n const description = project?.description ?? null\n\n return {\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 { availableServices, projectId, crumbProject, crumbDomain } = useLoaderData({\n from: Route.id,\n })\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\nfunction ProjectErrorComponent({ error }: { error: Error & { statusCode?: number } }) {\n const { t } = useLingui()\n const navigate = Route.useNavigate()\n const router = useRouter()\n\n // Handle 404 errors with a custom UI\n if (error.statusCode === 404 || error.message === \"Project not found\") {\n return (\n <StatusError\n statusCode={404}\n title={t`Project Not Found`}\n message={t`The project you are looking for doesn't exist or you don't have access to it.`}\n onHomeClick={() => {\n navigate({ to: \"/projects\" })\n }}\n onBackClick={() => {\n router.history.back()\n }}\n />\n )\n }\n\n // For all other errors, use the default RouteError component\n return <RouteError error={error} />\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"_projectId-hMtrqW6m.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","canAccessClavisPca","buildNavSections","projectId","availableServices","enabledServices","serviceIndex","isEnabled","service","includes","computeServices","label","t","navigate","nav","to","params","networkServices","storageServices","provider","storageType","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","projectMatches","filter","m","routeId","startsWith","deepest","length","info","staticData","undefined","combinedLabel","domain","name","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\"\nimport { canAccessClavisPca } from \"@/client/routes/_auth/projects/$projectId/services/pca/-components/pcaAccess\"\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 ...(serviceIndex?.[\"object-store-ceph\"]?.[\"ceph\"] && 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 const clavisServices: NavItem[] = canAccessClavisPca(serviceIndex, enabledServices)\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 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 const combinedLabel = projectInfo.domain?.name ? `${projectInfo.domain.name}/${projectInfo.name}` : projectInfo.name\n\n if (!deepest || !info) {\n items.push({ label: combinedLabel, active: true })\n return items\n }\n\n items.push({\n label: combinedLabel,\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, useRouter } 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\"\nimport { TRPCClientError } from \"@trpc/client\"\nimport { resolveProjectScope } from \"./-components/resolveProjectScope\"\nimport { StatusError } from \"@/client/components/Error/StatusError\"\nimport { useLingui } from \"@lingui/react/macro\"\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 let scopeData\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 && error.data?.code === \"NOT_FOUND\") {\n scopeData = undefined\n } else {\n throw error\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 projectScopeStatus = resolveProjectScope({\n projectId: params.projectId,\n scopeProject: scopeData?.project,\n userProject: project,\n })\n\n if (projectScopeStatus === \"scope_failed\") {\n throw new Error(\"Failed to scope session to project\")\n }\n\n if (projectScopeStatus === \"not_found\") {\n const error = new Error(\"Project not found\") as Error & { statusCode?: number }\n error.statusCode = 404\n throw error\n }\n\n const accountId = scopeData?.domain?.id || \"\"\n const description = project?.description ?? null\n\n return {\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 { availableServices, projectId, crumbProject, crumbDomain } = useLoaderData({\n from: Route.id,\n })\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\nfunction ProjectErrorComponent({ error }: { error: Error & { statusCode?: number } }) {\n const { t } = useLingui()\n const navigate = Route.useNavigate()\n const router = useRouter()\n\n // Handle 404 errors with a custom UI\n if (error.statusCode === 404 || error.message === \"Project not found\") {\n return (\n <StatusError\n statusCode={404}\n title={t`Project Not Found`}\n message={t`The project you are looking for doesn't exist or you don't have access to it.`}\n onHomeClick={() => {\n navigate({ to: \"/projects\" })\n }}\n onBackClick={() => {\n router.history.back()\n }}\n />\n )\n }\n\n // For all other errors, use the default RouteError component\n return <RouteError error={error} />\n}\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { A as e, B as t, E as n, G as r, H as i, K as a, P as o, Q as s, R as c, U as l, Y as u, Z as d, at as f, c as p, dt as m, et as h, g, h as _, m as v, nt as y, o as b, p as x, r as S, rt as C, tt as w, ut as T, y as E } from "./build-DgeVmENN.mjs";
2
2
  import { r as D } from "./trpcClient-BxcNKnhv.mjs";
3
- import { t as O } from "./_securityGroupId-a46ExjsA.mjs";
3
+ import { t as O } from "./_securityGroupId-fPJWtDPU.mjs";
4
4
  import { t as k } from "./useProjectId-DBc5lpoU.mjs";
5
- import "./hooks-BsbKkpZc.mjs";
5
+ import "./hooks-D0wGUhRF.mjs";
6
6
  import { t as A } from "./useModal-DCs1OJh7.mjs";
7
7
  import { n as j, t as M } from "./useListWithFiltering-BuhE81I6.mjs";
8
8
  import { t as N } from "./EditSecurityGroupModal-BGPXglju.mjs";
@@ -1643,4 +1643,4 @@ function Te() {
1643
1643
  //#endregion
1644
1644
  export { Te as component };
1645
1645
 
1646
- //# sourceMappingURL=_securityGroupId-ju2ZSIYe.mjs.map
1646
+ //# sourceMappingURL=_securityGroupId-B5-gJe1u.mjs.map