@cobaltcore-dev/aurora 2.1.0 → 2.1.1

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 (54) hide show
  1. package/dist/client/DeleteFlavorModal-C4nAi7gY.mjs +724 -0
  2. package/dist/client/DeleteFlavorModal-C4nAi7gY.mjs.map +1 -0
  3. package/dist/client/_flavorId-BPvssEzn.mjs +277 -0
  4. package/dist/client/_flavorId-BPvssEzn.mjs.map +1 -0
  5. package/dist/client/{_flavorId-CgueqKTs.mjs → _flavorId-CFUBcU-l.mjs} +3 -3
  6. package/dist/client/{_flavorId-CgueqKTs.mjs.map → _flavorId-CFUBcU-l.mjs.map} +1 -1
  7. package/dist/client/{_flavorId-B7fp6Nk8.mjs → _flavorId-CZcIp0Ih.mjs} +2 -2
  8. package/dist/client/{_flavorId-B7fp6Nk8.mjs.map → _flavorId-CZcIp0Ih.mjs.map} +1 -1
  9. package/dist/client/_imageId-BAezmV0Y.mjs +526 -0
  10. package/dist/client/_imageId-BAezmV0Y.mjs.map +1 -0
  11. package/dist/client/{_imageId-Ccv8PedW.mjs → _imageId-D-jqz3e3.mjs} +3 -3
  12. package/dist/client/_imageId-D-jqz3e3.mjs.map +1 -0
  13. package/dist/client/{_imageId-D9LR-02T.mjs → _imageId-gMdrdnVt.mjs} +2 -2
  14. package/dist/client/_imageId-gMdrdnVt.mjs.map +1 -0
  15. package/dist/client/{_projectId-BDARsteX.mjs → _projectId-BYccdAtX.mjs} +3 -3
  16. package/dist/client/{_projectId-BDARsteX.mjs.map → _projectId-BYccdAtX.mjs.map} +1 -1
  17. package/dist/client/{_projectId-ZBk6vka0.mjs → _projectId-CBjZC1tv.mjs} +5 -5
  18. package/dist/client/{_projectId-ZBk6vka0.mjs.map → _projectId-CBjZC1tv.mjs.map} +1 -1
  19. package/dist/client/{_projectId-CtyAHqhH.mjs → _projectId-CLEa1zgw.mjs} +2 -2
  20. package/dist/client/{_projectId-CtyAHqhH.mjs.map → _projectId-CLEa1zgw.mjs.map} +1 -1
  21. package/dist/client/{flavors-Ce3J-cFa.mjs → flavors-CJVSNz6R.mjs} +2 -2
  22. package/dist/client/{flavors-Ce3J-cFa.mjs.map → flavors-CJVSNz6R.mjs.map} +1 -1
  23. package/dist/client/{flavors-D5ThLaXO.mjs → flavors-CflJDIEH.mjs} +25 -24
  24. package/dist/client/{flavors-D5ThLaXO.mjs.map → flavors-CflJDIEH.mjs.map} +1 -1
  25. package/dist/client/imageHelpers-DBhL2ebL.mjs +1272 -0
  26. package/dist/client/imageHelpers-DBhL2ebL.mjs.map +1 -0
  27. package/dist/client/{images-C8M3ysWR.mjs → images-BsylWUiH.mjs} +836 -848
  28. package/dist/client/images-BsylWUiH.mjs.map +1 -0
  29. package/dist/client/{images-Cf5R28NR.mjs → images-zQq1nSVv.mjs} +2 -2
  30. package/dist/client/{images-Cf5R28NR.mjs.map → images-zQq1nSVv.mjs.map} +1 -1
  31. package/dist/client/index.js +10 -9
  32. package/dist/client/index.js.map +1 -1
  33. package/dist/client/{securitygroups-DXrfyuVF.mjs → securitygroups-CSmsBzUb.mjs} +104 -103
  34. package/dist/client/securitygroups-CSmsBzUb.mjs.map +1 -0
  35. package/dist/client/{securitygroups-67fFxiwt.mjs → securitygroups-T7kaNFEp.mjs} +2 -2
  36. package/dist/client/{securitygroups-67fFxiwt.mjs.map → securitygroups-T7kaNFEp.mjs.map} +1 -1
  37. package/dist/client/trpcClient.d.ts +0 -1
  38. package/dist/client/useErrorTranslation-Dc0eE8Zt.mjs +83 -0
  39. package/dist/client/useErrorTranslation-Dc0eE8Zt.mjs.map +1 -0
  40. package/dist/server/index.d.ts +0 -1
  41. package/dist/server/index.js +786 -534
  42. package/package.json +2 -2
  43. package/dist/client/DeleteFlavorModal-Be2GFiB_.mjs +0 -802
  44. package/dist/client/DeleteFlavorModal-Be2GFiB_.mjs.map +0 -1
  45. package/dist/client/ImageToastNotifications-BWfzOUMc.mjs +0 -1135
  46. package/dist/client/ImageToastNotifications-BWfzOUMc.mjs.map +0 -1
  47. package/dist/client/_flavorId-0PioWpEp.mjs +0 -276
  48. package/dist/client/_flavorId-0PioWpEp.mjs.map +0 -1
  49. package/dist/client/_imageId-Ccv8PedW.mjs.map +0 -1
  50. package/dist/client/_imageId-CiqEQB_J.mjs +0 -558
  51. package/dist/client/_imageId-CiqEQB_J.mjs.map +0 -1
  52. package/dist/client/_imageId-D9LR-02T.mjs.map +0 -1
  53. package/dist/client/images-C8M3ysWR.mjs.map +0 -1
  54. package/dist/client/securitygroups-DXrfyuVF.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"_projectId-CtyAHqhH.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. Please 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}. Please select a project from your current domain.`\n : t`This project doesn't exist or is not accessible from your current domain. Please 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
+ {"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"}
@@ -7,9 +7,9 @@ var r = e("/_auth/projects/$projectId/compute/flavors/")({
7
7
  analytics: { name: "compute.flavors.list" }
8
8
  },
9
9
  head: () => ({ meta: [{ title: n._({ id: "neiJm0" }) }] }),
10
- component: t(() => import("./flavors-D5ThLaXO.mjs"), "component")
10
+ component: t(() => import("./flavors-CflJDIEH.mjs"), "component")
11
11
  });
12
12
  //#endregion
13
13
  export { r as t };
14
14
 
15
- //# sourceMappingURL=flavors-Ce3J-cFa.mjs.map
15
+ //# sourceMappingURL=flavors-CJVSNz6R.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"flavors-Ce3J-cFa.mjs","names":["createFileRoute","t","Route","staticData","section","service","analytics","name","RouteInfo","head","meta","title","component","lazyRouteComponent","$$splitComponentImporter"],"sources":["../../src/client/routes/_auth/projects/$projectId/compute/flavors/index.tsx"],"sourcesContent":["import { createFileRoute } from \"@tanstack/react-router\"\nimport { t } from \"@lingui/core/macro\"\nimport { useLingui } from \"@lingui/react/macro\"\nimport { Flavors } from \"./-components/List\"\nimport type { RouteInfo } from \"@/client/routes/routeInfo\"\nimport { ContentHeader } from \"@/client/components/ContentHeader/ContentHeader\"\n\nexport const Route = createFileRoute(\"/_auth/projects/$projectId/compute/flavors/\")({\n staticData: {\n section: \"compute\",\n service: \"flavors\",\n analytics: {\n name: \"compute.flavors.list\",\n },\n } satisfies RouteInfo,\n head: () => ({ meta: [{ title: t`Flavors` }] }),\n component: RouteComponent,\n})\n\nfunction RouteComponent() {\n const { t } = useLingui()\n const { projectId } = Route.useParams()\n const { trpcClient } = Route.useRouteContext()\n return (\n <>\n <ContentHeader title={t`Flavors`} projectId={projectId} />\n <Flavors project={projectId} client={trpcClient!} />\n </>\n )\n}\n"],"mappings":";;AAOA,IAAaE,IAAQF,EAAgB,6CAAA,CAAA,CAA+C;CAClFG,YAAY;EACVC,SAAS;EACTC,SAAS;EACTC,WAAW,EACTC,MAAM,uBACR;CACF;CACAE,aAAa,EAAEC,MAAM,CAAC,EAAEC,OAAOV,EAAAA,EAAC,EAAA,IAAA,SAAA,CAAA,EAAU,CAAC,EAAE;CAC7CW,WAASC,0CAAA,WAAA;AACX,CAAA"}
1
+ {"version":3,"file":"flavors-CJVSNz6R.mjs","names":["createFileRoute","t","Route","staticData","section","service","analytics","name","RouteInfo","head","meta","title","component","lazyRouteComponent","$$splitComponentImporter"],"sources":["../../src/client/routes/_auth/projects/$projectId/compute/flavors/index.tsx"],"sourcesContent":["import { createFileRoute } from \"@tanstack/react-router\"\nimport { t } from \"@lingui/core/macro\"\nimport { useLingui } from \"@lingui/react/macro\"\nimport { Flavors } from \"./-components/List\"\nimport type { RouteInfo } from \"@/client/routes/routeInfo\"\nimport { ContentHeader } from \"@/client/components/ContentHeader/ContentHeader\"\n\nexport const Route = createFileRoute(\"/_auth/projects/$projectId/compute/flavors/\")({\n staticData: {\n section: \"compute\",\n service: \"flavors\",\n analytics: {\n name: \"compute.flavors.list\",\n },\n } satisfies RouteInfo,\n head: () => ({ meta: [{ title: t`Flavors` }] }),\n component: RouteComponent,\n})\n\nfunction RouteComponent() {\n const { t } = useLingui()\n const { projectId } = Route.useParams()\n const { trpcClient } = Route.useRouteContext()\n return (\n <>\n <ContentHeader title={t`Flavors`} projectId={projectId} />\n <Flavors project={projectId} client={trpcClient!} />\n </>\n )\n}\n"],"mappings":";;AAOA,IAAaE,IAAQF,EAAgB,6CAAA,CAAA,CAA+C;CAClFG,YAAY;EACVC,SAAS;EACTC,SAAS;EACTC,WAAW,EACTC,MAAM,uBACR;CACF;CACAE,aAAa,EAAEC,MAAM,CAAC,EAAEC,OAAOV,EAAAA,EAAC,EAAA,IAAA,SAAA,CAAA,EAAU,CAAC,EAAE;CAC7CW,WAASC,0CAAA,WAAA;AACX,CAAA"}
@@ -1,8 +1,9 @@
1
1
  import { C as e, D as t, L as n, Q as r, S as i, T as a, V as o, X as s, ft as c, g as l, h as u, i as d, l as f, m as p, o as m, ot as h, p as g, r as _, rt as v, st as y, t as b, x } from "./build-WuxXRAqf.mjs";
2
- import { t as S } from "./flavors-Ce3J-cFa.mjs";
2
+ import { t as S } from "./flavors-CJVSNz6R.mjs";
3
3
  import { t as C } from "./SortInput-4g7nSTHW.mjs";
4
4
  import { t as w } from "./ContentHeader-fKKZVu-o.mjs";
5
- import { a as T, i as E, n as D, o as O, r as k, t as A } from "./DeleteFlavorModal-Be2GFiB_.mjs";
5
+ import { t as T } from "./useErrorTranslation-Dc0eE8Zt.mjs";
6
+ import { a as E, i as D, n as O, r as k, t as A } from "./DeleteFlavorModal-C4nAi7gY.mjs";
6
7
  import { Fragment as j, jsx as M, jsxs as N } from "react/jsx-runtime";
7
8
  import P, { Suspense as F, startTransition as I, use as L, useCallback as R, useEffect as z, useRef as B, useState as V } from "react";
8
9
  import { useNavigate as H, useParams as ee, useSearch as U } from "@tanstack/react-router";
@@ -11,18 +12,18 @@ import { Trans as G, useLingui as K } from "@lingui/react";
11
12
  import { ErrorBoundary as q } from "react-error-boundary";
12
13
  //#region src/client/routes/_auth/projects/$projectId/compute/flavors/-components/FlavorListContainer.tsx
13
14
  var J = ({ flavors: e, isLoading: n, client: r, project: s, onFlavorDeleted: c, canDeleteFlavor: u, canMangageAccess: d, canManageSpecs: f, currentPage: p = 1, totalPages: h = 1, onPageChange: g }) => {
14
- let { i18n: y, _: S } = K(), C = H(), [w, E] = V(!1), [O, k] = V(!1), [P, F] = V(!1), [I, L] = V(null), [R, B] = V(p.toString());
15
+ let { i18n: y, _: S } = K(), C = H(), [w, T] = V(!1), [D, k] = V(!1), [P, F] = V(!1), [I, L] = V(null), [R, B] = V(p.toString());
15
16
  z(() => {
16
17
  B(p.toString());
17
18
  }, [p]);
18
19
  let { projectId: U } = ee({ from: "/_auth/projects/$projectId/compute/flavors/" }), W = (e) => {
19
- L(e), E(!0);
20
+ L(e), T(!0);
20
21
  }, q = (e) => {
21
22
  L(e), k(!0);
22
23
  }, J = (e) => {
23
24
  L(e), F(!0);
24
25
  }, Y = () => {
25
- E(!1), L(null);
26
+ T(!1), L(null);
26
27
  }, X = () => {
27
28
  I && c && c(I.name || ""), Y();
28
29
  }, Z = (e) => {
@@ -128,14 +129,14 @@ var J = ({ flavors: e, isLoading: n, client: r, project: s, onFlavorDeleted: c,
128
129
  flavor: I,
129
130
  onSuccess: X
130
131
  }),
131
- /*#__PURE__*/ M(T, {
132
+ /*#__PURE__*/ M(E, {
132
133
  client: r,
133
- isOpen: O,
134
+ isOpen: D,
134
135
  onClose: () => k(!1),
135
136
  project: s,
136
137
  flavor: I
137
138
  }),
138
- /*#__PURE__*/ M(D, {
139
+ /*#__PURE__*/ M(O, {
139
140
  client: r,
140
141
  isOpen: P,
141
142
  onClose: () => F(!1),
@@ -194,15 +195,15 @@ var J = ({ flavors: e, isLoading: n, client: r, project: s, onFlavorDeleted: c,
194
195
  }, n = (e) => e != null && e !== "" && String(e).trim() !== "", r = (e) => e != null && e !== "" && String(e).trim() !== "" && !isNaN(Number(e));
195
196
  return n(e.id) && (t.id = String(e.id).trim()), n(e.description) && (t.description = String(e.description).trim()), r(e.swap) && (t.swap = Number(e.swap)), r(e.rxtx_factor) && (t.rxtx_factor = Number(e.rxtx_factor)), r(e["OS-FLV-EXT-DATA:ephemeral"]) && (t["OS-FLV-EXT-DATA:ephemeral"] = Number(e["OS-FLV-EXT-DATA:ephemeral"])), e["os-flavor-access:is_public"] !== void 0 && (t["os-flavor-access:is_public"] = !!e["os-flavor-access:is_public"]), t;
196
197
  }, Z = ({ client: e, isOpen: t, onClose: r, project: i, onSuccess: a }) => {
197
- let { i18n: o, _: l } = K(), { translateError: d } = O(), [f, m] = V({ "os-flavor-access:is_public": !0 }), [h, _] = V({}), [b, x] = V(!1), [S, C] = V(null), [w, T] = V(!1);
198
+ let { i18n: o, _: l } = K(), { translateError: d } = T(), [f, m] = V({ "os-flavor-access:is_public": !0 }), [h, _] = V({}), [b, x] = V(!1), [S, C] = V(null), [w, E] = V(!1);
198
199
  P.useEffect(() => {
199
- t && e.compute.getComputeApiVersion.query({ project_id: i }).then((e) => T(e.supportsDescription)).catch(() => T(!1));
200
+ t && e.compute.getComputeApiVersion.query({ project_id: i }).then((e) => E(e.supportsDescription)).catch(() => E(!1));
200
201
  }, [
201
202
  t,
202
203
  e,
203
204
  i
204
205
  ]);
205
- let E = (e) => {
206
+ let D = (e) => {
206
207
  let { name: t, value: n, type: r, checked: i } = e.target;
207
208
  m((e) => ({
208
209
  ...e,
@@ -211,7 +212,7 @@ var J = ({ flavors: e, isLoading: n, client: r, project: s, onFlavorDeleted: c,
211
212
  ...e,
212
213
  [t]: void 0
213
214
  })), S && C(null);
214
- }, D = (e, t) => {
215
+ }, O = (e, t) => {
215
216
  m((n) => ({
216
217
  ...n,
217
218
  [e]: t
@@ -321,7 +322,7 @@ var J = ({ flavors: e, isLoading: n, client: r, project: s, onFlavorDeleted: c,
321
322
  name: "id",
322
323
  label: o._({ id: "s2ubkU" }),
323
324
  value: f.id || "",
324
- onChange: E,
325
+ onChange: D,
325
326
  onBlur: k,
326
327
  errortext: h.id,
327
328
  helptext: o._({ id: "hv2CnP" })
@@ -331,7 +332,7 @@ var J = ({ flavors: e, isLoading: n, client: r, project: s, onFlavorDeleted: c,
331
332
  name: "name",
332
333
  label: o._({ id: "lWxDDh" }),
333
334
  value: f.name || "",
334
- onChange: E,
335
+ onChange: D,
335
336
  onBlur: k,
336
337
  errortext: h.name,
337
338
  helptext: o._({ id: "nq24is" }),
@@ -342,7 +343,7 @@ var J = ({ flavors: e, isLoading: n, client: r, project: s, onFlavorDeleted: c,
342
343
  name: "description",
343
344
  label: o._({ id: "Nu4oKW" }),
344
345
  value: f.description || "",
345
- onChange: E,
346
+ onChange: D,
346
347
  onBlur: k,
347
348
  errortext: h.description
348
349
  }) }),
@@ -351,7 +352,7 @@ var J = ({ flavors: e, isLoading: n, client: r, project: s, onFlavorDeleted: c,
351
352
  label: o._({ id: "vbajgL" }),
352
353
  helptext: o._({ id: "Y8TptA" }),
353
354
  checked: !!f["os-flavor-access:is_public"],
354
- onChange: E
355
+ onChange: D
355
356
  }) })
356
357
  ]
357
358
  }),
@@ -363,7 +364,7 @@ var J = ({ flavors: e, isLoading: n, client: r, project: s, onFlavorDeleted: c,
363
364
  name: "vcpus",
364
365
  label: o._({ id: "k5nYwm" }),
365
366
  value: String(f.vcpus || ""),
366
- onChange: (e) => D("vcpus", Number(e.target.value)),
367
+ onChange: (e) => O("vcpus", Number(e.target.value)),
367
368
  onBlur: k,
368
369
  errortext: h.vcpus,
369
370
  type: "number",
@@ -374,7 +375,7 @@ var J = ({ flavors: e, isLoading: n, client: r, project: s, onFlavorDeleted: c,
374
375
  name: "ram",
375
376
  label: o._({ id: "K+e/0e" }),
376
377
  value: String(f.ram || ""),
377
- onChange: (e) => D("ram", Number(e.target.value)),
378
+ onChange: (e) => O("ram", Number(e.target.value)),
378
379
  onBlur: k,
379
380
  errortext: h.ram,
380
381
  helptext: o._({ id: "9DrU1K" }),
@@ -386,7 +387,7 @@ var J = ({ flavors: e, isLoading: n, client: r, project: s, onFlavorDeleted: c,
386
387
  name: "disk",
387
388
  label: o._({ id: "n0ZttO" }),
388
389
  value: String(f.disk || ""),
389
- onChange: (e) => D("disk", Number(e.target.value)),
390
+ onChange: (e) => O("disk", Number(e.target.value)),
390
391
  onBlur: k,
391
392
  errortext: h.disk,
392
393
  type: "number",
@@ -397,7 +398,7 @@ var J = ({ flavors: e, isLoading: n, client: r, project: s, onFlavorDeleted: c,
397
398
  name: "OS-FLV-EXT-DATA:ephemeral",
398
399
  label: o._({ id: "X2OnDx" }),
399
400
  value: String(f["OS-FLV-EXT-DATA:ephemeral"] || ""),
400
- onChange: (e) => D("OS-FLV-EXT-DATA:ephemeral", Number(e.target.value)),
401
+ onChange: (e) => O("OS-FLV-EXT-DATA:ephemeral", Number(e.target.value)),
401
402
  onBlur: k,
402
403
  errortext: h["OS-FLV-EXT-DATA:ephemeral"],
403
404
  type: "number"
@@ -407,7 +408,7 @@ var J = ({ flavors: e, isLoading: n, client: r, project: s, onFlavorDeleted: c,
407
408
  name: "swap",
408
409
  label: o._({ id: "p4nMut" }),
409
410
  value: String(f.swap || ""),
410
- onChange: (e) => D("swap", e.target.value ? Number(e.target.value) : void 0),
411
+ onChange: (e) => O("swap", e.target.value ? Number(e.target.value) : void 0),
411
412
  onBlur: k,
412
413
  errortext: h.swap,
413
414
  type: "number"
@@ -417,7 +418,7 @@ var J = ({ flavors: e, isLoading: n, client: r, project: s, onFlavorDeleted: c,
417
418
  name: "rxtx_factor",
418
419
  label: o._({ id: "u5HztT" }),
419
420
  value: String(f.rxtx_factor || ""),
420
- onChange: (e) => D("rxtx_factor", Number(e.target.value)),
421
+ onChange: (e) => O("rxtx_factor", Number(e.target.value)),
421
422
  onBlur: k,
422
423
  errortext: h.rxtx_factor,
423
424
  helptext: o._({ id: "IVw5fS" }),
@@ -575,7 +576,7 @@ var re = ({ client: e, project: t }) => {
575
576
  m($(e, t, o.sortBy, o.sortDirection, c));
576
577
  });
577
578
  }, y = (e) => {
578
- let { message: t, ...n } = E(e);
579
+ let { message: t, ...n } = D(e);
579
580
  h.success(t, n), _();
580
581
  }, b = (e) => {
581
582
  let { message: t, ...n } = k(e);
@@ -663,4 +664,4 @@ function ie() {
663
664
  //#endregion
664
665
  export { ie as component };
665
666
 
666
- //# sourceMappingURL=flavors-D5ThLaXO.mjs.map
667
+ //# sourceMappingURL=flavors-CflJDIEH.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"flavors-D5ThLaXO.mjs","names":["DataGrid","DataGridHeadCell","DataGridRow","DataGridCell","PopupMenu","PopupMenuOptions","PopupMenuItem","PopupMenuSectionSeparator","Status","Pagination","DeleteFlavorModal","useEffect","useState","EditSpecModal","ManageAccessModal","useParams","useNavigate","FlavorListContainer","flavors","isLoading","client","project","onFlavorDeleted","canDeleteFlavor","canMangageAccess","canManageSpecs","currentPage","totalPages","onPageChange","useLingui","navigate","deleteModalOpen","setDeleteModalOpen","specModalOpen","setSpecModalOpen","accessModalOpen","setAccessModalOpen","selectedFlavor","setSelectedFlavor","inputPage","setInputPage","toString","projectId","from","openDeleteModal","flavor","openSpecModal","openAccessModal","closeDeleteModal","handleDeleteSuccess","name","updateCurrentPage","newPage","status","title","t","columns","minContentColumns","className","data-testid","length","map","id","onClick","to","params","flavorId","vcpus","ram","disk","swap","e","stopPropagation","label","colSpan","body","div","variant","pages","onPressPrevious","Math","max","onPressNext","min","onSelectChange","selectedPage","onInputChange","newInputPage","undefined","String","onKeyDown","key","parseInt","isNaN","isOpen","onClose","onSuccess","validateField","field","value","t","undefined","idStr","String","trim","idRegex","test","msg","nameStr","length","vcpus","Number","isNaN","ram","disk","swap","rxtx","str","ephemeral","cleanFlavorData","flavor","result","name","isValidValue","isValidNumber","id","description","rxtx_factor","Boolean","React","useState","Modal","Form","FormRow","FormSection","TextInput","Message","Status","Checkbox","validateField","cleanFlavorData","useErrorTranslation","CreateFlavorModal","client","isOpen","onClose","project","onSuccess","useLingui","translateError","newFlavor","setNewFlavor","errors","setErrors","isLoading","setIsLoading","generalError","setGeneralError","supportsDescription","setSupportsDescription","useEffect","compute","getComputeApiVersion","query","project_id","then","result","catch","handleInputChange","e","name","value","type","checked","target","prev","newState","undefined","handleNumericInputChange","handleBlur","error","handleSubmit","preventDefault","newErrors","requiredFields","forEach","key","optionalFields","Object","keys","length","t","flavorData","createFlavor","mutate","flavor","handleClose","errorMessage","message","dismissError","isFormValid","field","onCancel","size","title","open","onConfirm","cancelButtonLabel","confirmButtonLabel","disableConfirmButton","disableCancelButton","disableCloseButton","status","className","onDismiss","text","variant","id","label","onChange","onBlur","errortext","helptext","required","description","String","vcpus","Number","ram","disk","swap","rxtx_factor","use","Suspense","useState","useRef","startTransition","useEffect","useCallback","ErrorBoundary","useSearch","useNavigate","TRPCClientError","toast","Message","Button","Stack","DataGridToolbar","SearchInput","Status","SortInput","FlavorListContainer","CreateFlavorModal","getFlavorCreatedToast","getFlavorDeletedToast","PAGE_SIZE","createFlavorsPromise","client","project","sortBy","sortDirection","searchTerm","compute","getFlavorsByProjectId","query","project_id","then","res","listError","undefined","catch","err","data","code","flavors","privateFlavorError","message","createPermissionsPromise","canUser","permission","canCreate","canDelete","canAddProject","canRemoveProject","canCreateSpecs","canDeleteSpecs","canManageAccess","canManageSpecs","FlavorsContent","flavorsPromise","permissionsPromise","onFlavorDeleted","onFlavorCreated","setSearchTerm","sortSettings","handleSortChange","createModalOpen","setCreateModalOpen","currentPage","onPageChange","useLingui","permissions","localSearchTerm","setLocalSearchTerm","debounceTimer","clearTimeout","current","p","totalPages","Math","max","ceil","length","safePage","min","paginatedFlavors","slice","isOpen","onClose","onSuccess","className","text","t","variant","distribution","alignment","gap","options","selectClassName","selectWidth","onSortByChange","v","onSortDirectionChange","dir","label","onClick","placeholder","data-testid","value","onInput","e","currentTarget","window","setTimeout","onSearch","onClear","isLoading","canDeleteFlavor","canMangageAccess","Flavors","navigate","searchParams","strict","setSortSettings","search","page","setFlavorsPromise","refetchFlavors","handleFlavorDeleted","flavorName","success","handleFlavorCreated","newSortSettings","settings","toString","prev","replace","handleSearchChange","term","searchValue","handlePageChange","div","fallbackRender","error","status","title","body","Error","fallback","useLingui","Flavors","ContentHeader","Route","RouteComponent","t","projectId","useParams","trpcClient","useRouteContext","component"],"sources":["../../src/client/routes/_auth/projects/$projectId/compute/flavors/-components/FlavorListContainer.tsx","../../src/client/routes/_auth/projects/$projectId/compute/flavors/-components/flavorValidation.ts","../../src/client/routes/_auth/projects/$projectId/compute/flavors/-components/CreateFlavorModal.tsx","../../src/client/routes/_auth/projects/$projectId/compute/flavors/-components/List.tsx","../../src/client/routes/_auth/projects/$projectId/compute/flavors/index.tsx?tsr-split=component"],"sourcesContent":["import { Flavor } from \"@/server/Compute/types/flavor\"\nimport {\n DataGrid,\n DataGridHeadCell,\n DataGridRow,\n DataGridCell,\n PopupMenu,\n PopupMenuOptions,\n PopupMenuItem,\n PopupMenuSectionSeparator,\n Status,\n Pagination,\n} from \"@cloudoperators/juno-ui-components\"\nimport { Trans, useLingui } from \"@lingui/react/macro\"\nimport { DeleteFlavorModal } from \"./DeleteFlavorModal\"\nimport { useEffect, useState } from \"react\"\nimport { TrpcClient } from \"@/client/trpcClient\"\nimport { EditSpecModal } from \"./EditSpecModal\"\nimport { ManageAccessModal } from \"./ManageAccessModal\"\nimport { useParams, useNavigate } from \"@tanstack/react-router\"\n\ninterface FlavorListContainerProps {\n flavors?: Flavor[]\n isLoading: boolean\n client: TrpcClient\n project: string\n onFlavorDeleted?: (flavorName: string) => void\n canDeleteFlavor?: boolean\n canMangageAccess?: boolean\n canManageSpecs?: boolean\n currentPage?: number\n totalPages?: number\n onPageChange?: (page: number) => void\n}\n\nexport const FlavorListContainer = ({\n flavors,\n isLoading,\n client,\n project,\n onFlavorDeleted,\n canDeleteFlavor,\n canMangageAccess,\n canManageSpecs,\n currentPage = 1,\n totalPages = 1,\n onPageChange,\n}: FlavorListContainerProps) => {\n const { t } = useLingui()\n const navigate = useNavigate()\n const [deleteModalOpen, setDeleteModalOpen] = useState(false)\n const [specModalOpen, setSpecModalOpen] = useState(false)\n const [accessModalOpen, setAccessModalOpen] = useState(false)\n const [selectedFlavor, setSelectedFlavor] = useState<Flavor | null>(null)\n const [inputPage, setInputPage] = useState<string>(currentPage.toString())\n\n useEffect(() => {\n setInputPage(currentPage.toString())\n }, [currentPage])\n\n const { projectId } = useParams({\n from: \"/_auth/projects/$projectId/compute/flavors/\",\n })\n\n const openDeleteModal = (flavor: Flavor) => {\n setSelectedFlavor(flavor)\n setDeleteModalOpen(true)\n }\n\n const openSpecModal = (flavor: Flavor) => {\n setSelectedFlavor(flavor)\n setSpecModalOpen(true)\n }\n\n const openAccessModal = (flavor: Flavor) => {\n setSelectedFlavor(flavor)\n setAccessModalOpen(true)\n }\n\n const closeDeleteModal = () => {\n setDeleteModalOpen(false)\n setSelectedFlavor(null)\n }\n\n const handleDeleteSuccess = () => {\n if (selectedFlavor && onFlavorDeleted) {\n onFlavorDeleted(selectedFlavor.name || \"\")\n }\n closeDeleteModal()\n }\n\n const updateCurrentPage = (newPage: number) => {\n if (newPage >= 1 && newPage <= totalPages) {\n onPageChange?.(newPage)\n setInputPage(newPage.toString())\n }\n }\n\n if (isLoading) {\n return <Status status=\"progress\" title={t`Loading...`} />\n }\n\n return (\n <>\n <DataGrid columns={7} minContentColumns={[6]} className=\"flavors\" data-testid=\"flavors-table\">\n <DataGridRow>\n <DataGridHeadCell>\n <Trans>Name</Trans>\n </DataGridHeadCell>\n <DataGridHeadCell>\n <Trans>vCPU</Trans>\n </DataGridHeadCell>\n <DataGridHeadCell>\n <Trans>RAM (MiB)</Trans>\n </DataGridHeadCell>\n <DataGridHeadCell>\n <Trans>Root Disk (GiB)</Trans>\n </DataGridHeadCell>\n <DataGridHeadCell>\n <Trans>Swap (MiB)</Trans>\n </DataGridHeadCell>\n <DataGridHeadCell>\n <Trans>Access Type</Trans>\n </DataGridHeadCell>\n <DataGridHeadCell></DataGridHeadCell>\n </DataGridRow>\n\n {flavors && flavors.length > 0 ? (\n flavors.map((flavor) => (\n <DataGridRow\n key={flavor.id}\n data-testid={`flavor-row-${flavor.id}`}\n onClick={() =>\n navigate({\n to: \"/projects/$projectId/compute/flavors/$flavorId\",\n params: { projectId, flavorId: flavor.id },\n })\n }\n >\n <DataGridCell>{flavor.name || flavor.id}</DataGridCell>\n <DataGridCell>{flavor.vcpus || \"–\"}</DataGridCell>\n <DataGridCell>{flavor.ram || \"–\"}</DataGridCell>\n <DataGridCell>{flavor.disk || \"–\"}</DataGridCell>\n <DataGridCell>{flavor.swap || \"–\"}</DataGridCell>\n <DataGridCell>{flavor[\"os-flavor-access:is_public\"] === false ? t`Private` : t`Public`}</DataGridCell>\n <DataGridCell onClick={(e) => e.stopPropagation()}>\n <PopupMenu>\n <PopupMenuOptions>\n {canManageSpecs && <PopupMenuItem label={t`Edit Metadata`} onClick={() => openSpecModal(flavor)} />}\n {canMangageAccess && flavor[\"os-flavor-access:is_public\"] === false && (\n <PopupMenuItem label={t`Manage Access`} onClick={() => openAccessModal(flavor)} />\n )}\n <PopupMenuItem\n label={t`Show Details`}\n onClick={() =>\n navigate({\n to: \"/projects/$projectId/compute/flavors/$flavorId\",\n params: { projectId, flavorId: flavor.id },\n })\n }\n />\n {canDeleteFlavor && (\n <>\n <PopupMenuSectionSeparator />\n <PopupMenuItem label={t`Delete Flavor`} onClick={() => openDeleteModal(flavor)} />\n </>\n )}\n </PopupMenuOptions>\n </PopupMenu>\n </DataGridCell>\n </DataGridRow>\n ))\n ) : (\n <DataGridRow>\n <DataGridCell colSpan={7}>\n <Status\n status=\"empty\"\n title={t`No flavors found`}\n body={t`There are no flavors available for this project with the current filters applied. Try adjusting your filter criteria or create a new flavor.`}\n />\n </DataGridCell>\n </DataGridRow>\n )}\n </DataGrid>\n {totalPages > 1 && (\n <div className=\"flex justify-center py-4\">\n <Pagination\n variant=\"input\"\n currentPage={currentPage}\n pages={totalPages}\n onPressPrevious={() => updateCurrentPage(Math.max(currentPage - 1, 1))}\n onPressNext={() => updateCurrentPage(Math.min(currentPage + 1, totalPages))}\n onSelectChange={(selectedPage: number) => {\n updateCurrentPage(selectedPage)\n }}\n onInputChange={(newInputPage?: number) => {\n setInputPage(newInputPage === undefined ? \"\" : String(newInputPage))\n }}\n onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === \"Enter\" && inputPage !== \"\") {\n const newPage = parseInt(inputPage, 10)\n if (!isNaN(newPage) && newPage >= 1 && newPage <= totalPages) {\n updateCurrentPage(newPage)\n }\n }\n }}\n />\n </div>\n )}\n <DeleteFlavorModal\n client={client}\n isOpen={deleteModalOpen}\n onClose={closeDeleteModal}\n project={project}\n flavor={selectedFlavor}\n onSuccess={handleDeleteSuccess}\n />\n <EditSpecModal\n client={client}\n isOpen={specModalOpen}\n onClose={() => setSpecModalOpen(false)}\n project={project}\n flavor={selectedFlavor}\n />\n\n <ManageAccessModal\n client={client}\n isOpen={accessModalOpen}\n onClose={() => setAccessModalOpen(false)}\n project={project}\n flavor={selectedFlavor}\n />\n </>\n )\n}\n","import { msg } from \"@lingui/core/macro\"\nimport { MessageDescriptor } from \"@lingui/core\"\nimport { Flavor } from \"@/server/Compute/types/flavor\"\n\nexport const validateField = (\n field: FlavorFormField,\n value: string | number | boolean | null | undefined,\n t: (descriptor: MessageDescriptor) => string\n): string | undefined => {\n switch (field) {\n case \"id\":\n if (!value) return undefined\n {\n const idStr = String(value).trim()\n const idRegex = /^[a-zA-Z0-9.\\-_]*$/\n return idRegex.test(idStr)\n ? undefined\n : t(msg`ID must only contain alphanumeric characters, hyphens, underscores, and dots.`)\n }\n\n case \"name\": {\n const nameStr = String(value || \"\").trim()\n return nameStr.length >= 2 && nameStr.length <= 50 ? undefined : t(msg`Name must be 2-50 characters long.`)\n }\n\n case \"vcpus\": {\n const vcpus = Number(value)\n return !isNaN(vcpus) && vcpus >= 1 ? undefined : t(msg`VCPUs must be an integer ≥ 1.`)\n }\n\n case \"ram\": {\n const ram = Number(value)\n return !isNaN(ram) && ram >= 128 ? undefined : t(msg`RAM must be an integer ≥ 128 MB.`)\n }\n\n case \"disk\": {\n const disk = Number(value)\n return !isNaN(disk) && disk >= 0 ? undefined : t(msg`Root Disk must be an integer ≥ 0.`)\n }\n\n case \"swap\":\n if (value === \"\" || value === undefined || value === null) return undefined\n {\n const swap = Number(value)\n return !isNaN(swap) && swap >= 0 ? undefined : t(msg`Swap Disk must be an integer ≥ 0.`)\n }\n\n case \"rxtx_factor\": {\n const rxtx = Number(value)\n return !isNaN(rxtx) && rxtx >= 1 ? undefined : t(msg`RX/TX Factor must be an integer ≥ 1.`)\n }\n\n case \"description\":\n if (!value) return undefined\n {\n const str = String(value)\n return str.length < 65535 ? undefined : t(msg`Description must be less than 65535 characters.`)\n }\n\n case \"OS-FLV-EXT-DATA:ephemeral\": {\n const ephemeral = Number(value)\n return !isNaN(ephemeral) && ephemeral >= 0 ? undefined : t(msg`Ephemeral Disk must be an integer ≥ 0.`)\n }\n\n case \"os-flavor-access:is_public\": {\n if (value === undefined || value === null) {\n return undefined\n }\n if (typeof value === \"boolean\") {\n return undefined\n }\n if (typeof value === \"string\" && (value === \"true\" || value === \"false\")) {\n return undefined\n }\n return t(msg`Invalid value for public flavor setting.`)\n }\n\n default:\n return undefined\n }\n}\n\nexport type FlavorFormField =\n | \"id\"\n | \"name\"\n | \"vcpus\"\n | \"ram\"\n | \"disk\"\n | \"swap\"\n | \"description\"\n | \"rxtx_factor\"\n | \"OS-FLV-EXT-DATA:ephemeral\"\n | \"os-flavor-access:is_public\"\n\nexport interface FieldErrors {\n id?: string\n name?: string\n vcpus?: string\n ram?: string\n disk?: string\n swap?: string\n rxtx_factor?: string\n description?: string\n \"OS-FLV-EXT-DATA:ephemeral\"?: string\n \"os-flavor-access:is_public\"?: string\n}\n\nexport const cleanFlavorData = (flavor: Partial<Flavor>) => {\n const result: {\n name: string\n vcpus: number\n ram: number\n disk: number\n \"OS-FLV-EXT-DATA:ephemeral\"?: number\n id?: string\n swap?: number\n rxtx_factor?: number\n description?: string\n \"os-flavor-access:is_public\"?: boolean\n } = {\n name: String(flavor.name),\n vcpus: Number(flavor.vcpus),\n ram: Number(flavor.ram),\n disk: Number(flavor.disk),\n }\n\n const isValidValue = (value: string | undefined | null) => {\n return value !== undefined && value !== null && value !== \"\" && String(value).trim() !== \"\"\n }\n\n const isValidNumber = (value: string | number | undefined | null) => {\n return value !== undefined && value !== null && value !== \"\" && String(value).trim() !== \"\" && !isNaN(Number(value))\n }\n\n if (isValidValue(flavor.id)) {\n result.id = String(flavor.id).trim()\n }\n\n if (isValidValue(flavor.description)) {\n result.description = String(flavor.description).trim()\n }\n\n if (isValidNumber(flavor.swap)) {\n result.swap = Number(flavor.swap)\n }\n\n if (isValidNumber(flavor.rxtx_factor)) {\n result.rxtx_factor = Number(flavor.rxtx_factor)\n }\n\n if (isValidNumber(flavor[\"OS-FLV-EXT-DATA:ephemeral\"])) {\n result[\"OS-FLV-EXT-DATA:ephemeral\"] = Number(flavor[\"OS-FLV-EXT-DATA:ephemeral\"])\n }\n\n if (flavor[\"os-flavor-access:is_public\"] !== undefined) {\n result[\"os-flavor-access:is_public\"] = Boolean(flavor[\"os-flavor-access:is_public\"])\n }\n\n return result\n}\n","import React, { useState } from \"react\"\nimport { useLingui } from \"@lingui/react/macro\"\nimport { TrpcClient } from \"@/client/trpcClient\"\nimport {\n Modal,\n Form,\n FormRow,\n FormSection,\n TextInput,\n Message,\n Status,\n Checkbox,\n} from \"@cloudoperators/juno-ui-components\"\nimport { Flavor } from \"@/server/Compute/types/flavor\"\nimport { validateField, FlavorFormField, FieldErrors } from \"./flavorValidation\"\nimport { cleanFlavorData } from \"./flavorValidation\"\nimport { useErrorTranslation } from \"@/client/utils/useErrorTranslation\"\n\ninterface CreateFlavorModalProps {\n client: TrpcClient\n isOpen: boolean\n onClose: () => void\n project: string\n onSuccess: (name: string) => void\n}\n\nexport const CreateFlavorModal: React.FC<CreateFlavorModalProps> = ({\n client,\n isOpen,\n onClose,\n project,\n onSuccess,\n}) => {\n const { t } = useLingui()\n const { translateError } = useErrorTranslation()\n const [newFlavor, setNewFlavor] = useState<Partial<Flavor>>({\n \"os-flavor-access:is_public\": true,\n })\n const [errors, setErrors] = useState<FieldErrors>({})\n const [isLoading, setIsLoading] = useState(false)\n const [generalError, setGeneralError] = useState<string | null>(null)\n const [supportsDescription, setSupportsDescription] = useState(false)\n\n // Check if Nova API supports description field (microversion 2.55+)\n React.useEffect(() => {\n if (isOpen) {\n client.compute.getComputeApiVersion\n .query({ project_id: project })\n .then((result) => setSupportsDescription(result.supportsDescription))\n .catch(() => setSupportsDescription(false))\n }\n }, [isOpen, client, project])\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const { name, value, type, checked } = e.target\n\n setNewFlavor((prev) => {\n const newState = {\n ...prev,\n [name]: type === \"checkbox\" ? checked : value,\n }\n\n return newState\n })\n // Clear error for this field when user changes the value\n setErrors((prev) => ({\n ...prev,\n [name]: undefined,\n }))\n if (generalError) setGeneralError(null)\n }\n\n const handleNumericInputChange = (name: FlavorFormField, value: number | undefined) => {\n setNewFlavor((prev) => ({\n ...prev,\n [name]: value,\n }))\n // Clear error for this field when user changes the value\n setErrors((prev) => ({\n ...prev,\n [name]: undefined,\n }))\n if (generalError) setGeneralError(null)\n }\n\n const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {\n const { name, value } = e.target\n const error = validateField(name as FlavorFormField, value, t)\n setErrors((prev) => ({\n ...prev,\n [name]: error,\n }))\n }\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault()\n setGeneralError(null)\n\n const newErrors: FieldErrors = {}\n\n const requiredFields: FlavorFormField[] = [\"name\", \"vcpus\", \"ram\", \"disk\"]\n requiredFields.forEach((key) => {\n const error = validateField(key, newFlavor[key], t)\n if (error) {\n newErrors[key] = error\n }\n })\n\n const optionalFields: FlavorFormField[] = [\n \"id\",\n \"swap\",\n \"OS-FLV-EXT-DATA:ephemeral\",\n \"rxtx_factor\",\n \"description\",\n \"os-flavor-access:is_public\",\n ]\n optionalFields.forEach((key) => {\n const value = newFlavor[key]\n\n if (value !== undefined && value !== \"\" && value !== null) {\n const error = validateField(key, value, t)\n if (error) {\n newErrors[key] = error\n }\n }\n })\n\n if (Object.keys(newErrors).length > 0) {\n setErrors(newErrors)\n setGeneralError(t`Please fix the validation errors below.`)\n return\n }\n\n try {\n setIsLoading(true)\n\n const flavorData = cleanFlavorData(newFlavor)\n\n await client.compute.createFlavor.mutate({\n project_id: project,\n flavor: flavorData,\n })\n\n onSuccess(flavorData.name)\n handleClose()\n } catch (error) {\n const errorMessage = (error as Error)?.message\n ? translateError((error as Error).message)\n : t`Failed to create flavor. Please try again.`\n setGeneralError(errorMessage)\n } finally {\n setIsLoading(false)\n }\n }\n\n const handleClose = () => {\n setNewFlavor({\n \"os-flavor-access:is_public\": true,\n })\n setErrors({})\n setGeneralError(null)\n onClose()\n }\n\n const dismissError = () => {\n setGeneralError(null)\n }\n\n const isFormValid = () => {\n // Check required fields are filled and have no errors\n const requiredFields: FlavorFormField[] = [\"name\", \"vcpus\", \"ram\", \"disk\"]\n\n for (const field of requiredFields) {\n const value = newFlavor[field]\n // Field must have a value\n if (value === undefined || value === \"\" || value === null) return false\n // Field must not have a validation error\n if (validateField(field, value, t)) return false\n }\n\n // Check optional fields for errors (if they have values)\n const optionalFields: FlavorFormField[] = [\"id\", \"swap\", \"OS-FLV-EXT-DATA:ephemeral\", \"rxtx_factor\", \"description\"]\n\n for (const field of optionalFields) {\n const value = newFlavor[field]\n // Skip empty optional fields\n if (value === undefined || value === \"\" || value === null) continue\n // Field must not have a validation error\n if (validateField(field, value, t)) return false\n }\n\n return true\n }\n\n return (\n <Modal\n onCancel={handleClose}\n size=\"large\"\n title={t`Create Flavor`}\n open={isOpen}\n onConfirm={handleSubmit}\n cancelButtonLabel={t`Cancel`}\n confirmButtonLabel={t`Create New Flavor`}\n disableConfirmButton={!isFormValid() || isLoading}\n disableCancelButton={isLoading}\n disableCloseButton={isLoading}\n >\n {isLoading && <Status status=\"progress\" title={t`Creating Flavor...`} className=\"mt-0\" />}\n {!isLoading && (\n <Form>\n {generalError && (\n <FormRow>\n <Message onDismiss={dismissError} text={generalError} variant=\"error\" />\n </FormRow>\n )}\n\n <FormSection title={t`Flavor Information`}>\n <FormRow>\n <TextInput\n id=\"id\"\n name=\"id\"\n label={t`Flavor ID`}\n value={newFlavor.id || \"\"}\n onChange={handleInputChange}\n onBlur={handleBlur}\n errortext={errors.id}\n helptext={t`Auto-generated if not provided. Only alphanumeric, hyphens, underscores, and dots allowed.`}\n />\n </FormRow>\n <FormRow>\n <TextInput\n id=\"name\"\n name=\"name\"\n label={t`Flavor Name`}\n value={newFlavor.name || \"\"}\n onChange={handleInputChange}\n onBlur={handleBlur}\n errortext={errors.name}\n helptext={t`2-50 characters.`}\n required\n />\n </FormRow>\n {supportsDescription && (\n <FormRow>\n <TextInput\n id=\"description\"\n name=\"description\"\n label={t`Description`}\n value={newFlavor.description || \"\"}\n onChange={handleInputChange}\n onBlur={handleBlur}\n errortext={errors.description}\n />\n </FormRow>\n )}\n <FormRow>\n <Checkbox\n name=\"os-flavor-access:is_public\"\n label={t`Public Flavor`}\n helptext={t`When checked, all tenants can use this flavor. Otherwise, grant access explicitly.`}\n checked={!!newFlavor[\"os-flavor-access:is_public\"]}\n onChange={handleInputChange}\n />\n </FormRow>\n </FormSection>\n\n <FormSection title={t`Hardware Specifications`}>\n <FormRow>\n <TextInput\n id=\"vcpus\"\n name=\"vcpus\"\n label={t`vCPU`}\n value={String(newFlavor.vcpus || \"\")}\n onChange={(e) => handleNumericInputChange(\"vcpus\", Number(e.target.value))}\n onBlur={handleBlur}\n errortext={errors.vcpus}\n type=\"number\"\n required\n />\n </FormRow>\n <FormRow>\n <TextInput\n id=\"ram\"\n name=\"ram\"\n label={t`RAM (MiB)`}\n value={String(newFlavor.ram || \"\")}\n onChange={(e) => handleNumericInputChange(\"ram\", Number(e.target.value))}\n onBlur={handleBlur}\n errortext={errors.ram}\n helptext={t`Minimum 128 MB.`}\n type=\"number\"\n required\n />\n </FormRow>\n <FormRow>\n <TextInput\n id=\"disk\"\n name=\"disk\"\n label={t`Root Disk (GiB)`}\n value={String(newFlavor.disk || \"\")}\n onChange={(e) => handleNumericInputChange(\"disk\", Number(e.target.value))}\n onBlur={handleBlur}\n errortext={errors.disk}\n type=\"number\"\n required\n />\n </FormRow>\n <FormRow>\n <TextInput\n id=\"OS-FLV-EXT-DATA:ephemeral\"\n name=\"OS-FLV-EXT-DATA:ephemeral\"\n label={t`Ephemeral Disk (GiB)`}\n value={String(newFlavor[\"OS-FLV-EXT-DATA:ephemeral\"] || \"\")}\n onChange={(e) => handleNumericInputChange(\"OS-FLV-EXT-DATA:ephemeral\", Number(e.target.value))}\n onBlur={handleBlur}\n errortext={errors[\"OS-FLV-EXT-DATA:ephemeral\"]}\n type=\"number\"\n />\n </FormRow>\n <FormRow>\n <TextInput\n id=\"swap\"\n name=\"swap\"\n label={t`Swap (MiB)`}\n value={String(newFlavor.swap || \"\")}\n onChange={(e) => handleNumericInputChange(\"swap\", e.target.value ? Number(e.target.value) : undefined)}\n onBlur={handleBlur}\n errortext={errors.swap}\n type=\"number\"\n />\n </FormRow>\n <FormRow>\n <TextInput\n id=\"rxtx_factor\"\n name=\"rxtx_factor\"\n label={t`RX/TX Factor`}\n value={String(newFlavor.rxtx_factor || \"\")}\n onChange={(e) => handleNumericInputChange(\"rxtx_factor\", Number(e.target.value))}\n onBlur={handleBlur}\n errortext={errors.rxtx_factor}\n helptext={t`Defaults to 1.`}\n type=\"number\"\n />\n </FormRow>\n </FormSection>\n </Form>\n )}\n </Modal>\n )\n}\n","import { use, Suspense, useState, useRef, startTransition, useEffect, useCallback } from \"react\"\nimport { ErrorBoundary } from \"react-error-boundary\"\nimport { useLingui } from \"@lingui/react/macro\"\nimport { useSearch, useNavigate } from \"@tanstack/react-router\"\nimport { TrpcClient } from \"@/client/trpcClient\"\nimport { Flavor } from \"@/server/Compute/types/flavor\"\nimport { TRPCClientError } from \"@trpc/client\"\nimport { toast, Message, Button, Stack, DataGridToolbar, SearchInput, Status } from \"@cloudoperators/juno-ui-components\"\nimport { SortInput } from \"@/client/components/ListToolbar/SortInput\"\nimport { SortSettings } from \"@/client/components/ListToolbar/types\"\nimport { FlavorListContainer } from \"./FlavorListContainer\"\nimport { CreateFlavorModal } from \"./CreateFlavorModal\"\nimport { getFlavorCreatedToast, getFlavorDeletedToast } from \"./FlavorToastNotifications\"\nimport type { FlavorsSearchParams } from \"@/client/routes/_auth/projects/$projectId/compute/flavors\"\n\nconst PAGE_SIZE = 50\n\ninterface FlavorsProps {\n client: TrpcClient\n project: string\n}\n\ntype RequiredSortSettings = {\n options: SortSettings[\"options\"]\n sortBy: string\n sortDirection: \"asc\" | \"desc\"\n}\n\nconst createFlavorsPromise = (\n client: TrpcClient,\n project: string,\n sortBy: string,\n sortDirection: string,\n searchTerm: string\n) => {\n return client.compute.getFlavorsByProjectId\n .query({ project_id: project, sortBy, sortDirection, searchTerm })\n .then((res) => ({ ...res, listError: undefined as string | undefined }))\n .catch((err: unknown) => {\n if (err instanceof TRPCClientError && err.data?.code === \"FORBIDDEN\") {\n return { flavors: [] as Flavor[], privateFlavorError: undefined, listError: err.message }\n }\n throw err\n })\n}\n\nconst createPermissionsPromise = (client: TrpcClient, project: string) => {\n return client.compute.canUser\n .query({\n project_id: project,\n permission: [\n \"flavors:create\",\n \"flavors:delete\",\n \"flavors:add_project\",\n \"flavors:remove_project\",\n \"flavor_specs:create\",\n \"flavor_specs:delete\",\n ],\n })\n .then(([canCreate, canDelete, canAddProject, canRemoveProject, canCreateSpecs, canDeleteSpecs]) => ({\n canCreate,\n canDelete,\n canManageAccess: canAddProject || canRemoveProject,\n canManageSpecs: canCreateSpecs || canDeleteSpecs,\n }))\n}\n\nfunction FlavorsContent({\n flavorsPromise,\n permissionsPromise,\n client,\n project,\n onFlavorDeleted,\n onFlavorCreated,\n searchTerm,\n setSearchTerm,\n sortSettings,\n handleSortChange,\n createModalOpen,\n setCreateModalOpen,\n currentPage,\n onPageChange,\n}: {\n flavorsPromise: Promise<{ flavors: Flavor[]; privateFlavorError?: string; listError?: string }>\n permissionsPromise: Promise<{\n canCreate: boolean\n canDelete: boolean\n canManageAccess: boolean\n canManageSpecs: boolean\n }>\n client: TrpcClient\n project: string\n onFlavorDeleted: (name: string) => void\n onFlavorCreated: (name: string) => void\n searchTerm: string\n setSearchTerm: (term: string) => void\n sortSettings: SortSettings\n handleSortChange: (settings: SortSettings) => void\n createModalOpen: boolean\n setCreateModalOpen: (open: boolean) => void\n currentPage: number\n onPageChange: (page: number) => void\n}) {\n const { t } = useLingui()\n const { flavors, privateFlavorError, listError } = use(flavorsPromise)\n const permissions = use(permissionsPromise)\n const [localSearchTerm, setLocalSearchTerm] = useState(searchTerm)\n const debounceTimer = useRef<number | undefined>(undefined)\n\n useEffect(() => () => clearTimeout(debounceTimer.current), [])\n\n if (listError) {\n return <p>{listError}</p>\n }\n\n const totalPages = Math.max(1, Math.ceil(flavors.length / PAGE_SIZE))\n const safePage = Math.min(currentPage, totalPages)\n const paginatedFlavors = flavors.slice((safePage - 1) * PAGE_SIZE, safePage * PAGE_SIZE)\n\n useEffect(() => {\n if (currentPage > totalPages) onPageChange(1)\n }, [totalPages, currentPage, onPageChange])\n\n return (\n <>\n <CreateFlavorModal\n client={client}\n isOpen={createModalOpen}\n project={project}\n onClose={() => setCreateModalOpen(false)}\n onSuccess={onFlavorCreated}\n />\n\n {privateFlavorError && (\n <Message\n className=\"mb-4\"\n text={t`Private flavors could not be loaded. You may be seeing an incomplete list.`}\n variant=\"warning\"\n />\n )}\n\n {/* Zone 1 — sort + create action, no background */}\n <Stack distribution=\"end\" alignment=\"center\" gap=\"2\" className=\"pb-2\">\n <Stack gap=\"2\">\n <SortInput\n options={sortSettings.options}\n sortBy={sortSettings.sortBy}\n sortDirection={sortSettings.sortDirection ?? \"asc\"}\n selectClassName=\"min-w-45\"\n selectWidth=\"auto\"\n onSortByChange={(v) =>\n handleSortChange({ ...sortSettings, sortBy: v, sortDirection: sortSettings.sortDirection })\n }\n onSortDirectionChange={(dir) => handleSortChange({ ...sortSettings, sortDirection: dir })}\n />\n {permissions.canCreate && (\n <Button variant=\"primary\" label={t`Create Flavor`} onClick={() => setCreateModalOpen(true)} />\n )}\n </Stack>\n </Stack>\n\n {/* Zone 2 — search bar */}\n <DataGridToolbar>\n <Stack distribution=\"end\" alignment=\"center\">\n <SearchInput\n placeholder={t`Search flavors...`}\n data-testid=\"searchbar\"\n value={localSearchTerm}\n onInput={(e: React.FormEvent<HTMLInputElement>) => {\n const v = e.currentTarget.value\n setLocalSearchTerm(v)\n clearTimeout(debounceTimer.current)\n debounceTimer.current = window.setTimeout(() => setSearchTerm(v), 500)\n }}\n onSearch={(v) => {\n clearTimeout(debounceTimer.current)\n setSearchTerm(typeof v === \"string\" ? v : \"\")\n }}\n onClear={() => {\n clearTimeout(debounceTimer.current)\n setLocalSearchTerm(\"\")\n setSearchTerm(\"\")\n }}\n />\n </Stack>\n </DataGridToolbar>\n\n <FlavorListContainer\n flavors={paginatedFlavors}\n isLoading={false}\n client={client}\n project={project}\n onFlavorDeleted={onFlavorDeleted}\n canDeleteFlavor={permissions.canDelete}\n canMangageAccess={permissions.canManageAccess}\n canManageSpecs={permissions.canManageSpecs}\n currentPage={safePage}\n totalPages={totalPages}\n onPageChange={onPageChange}\n />\n </>\n )\n}\nexport const Flavors = ({ client, project }: FlavorsProps) => {\n const { t } = useLingui()\n const navigate = useNavigate()\n const searchParams = useSearch({ strict: false }) as FlavorsSearchParams\n\n const [sortSettings, setSortSettings] = useState<RequiredSortSettings>({\n options: [\n { label: t`Name`, value: \"name\" },\n { label: t`vCPU`, value: \"vcpus\" },\n { label: t`RAM (MiB)`, value: \"ram\" },\n { label: t`Root Disk (GiB)`, value: \"disk\" },\n { label: t`Swap (MiB)`, value: \"swap\" },\n ],\n sortBy: searchParams.sortBy || \"name\",\n sortDirection: searchParams.sortDirection || \"asc\",\n })\n\n const [searchTerm, setSearchTerm] = useState(searchParams.search || \"\")\n const currentPage = searchParams.page ?? 1\n const [createModalOpen, setCreateModalOpen] = useState(false)\n\n const [flavorsPromise, setFlavorsPromise] = useState(() =>\n createFlavorsPromise(client, project, sortSettings.sortBy, sortSettings.sortDirection, searchTerm)\n )\n const [permissionsPromise] = useState(() => createPermissionsPromise(client, project))\n\n const refetchFlavors = () => {\n startTransition(() => {\n setFlavorsPromise(\n createFlavorsPromise(client, project, sortSettings.sortBy, sortSettings.sortDirection, searchTerm)\n )\n })\n }\n\n const handleFlavorDeleted = (flavorName: string) => {\n const { message, ...options } = getFlavorDeletedToast(flavorName)\n toast.success(message, options)\n refetchFlavors()\n }\n\n const handleFlavorCreated = (flavorName: string) => {\n const { message, ...options } = getFlavorCreatedToast(flavorName)\n toast.success(message, options)\n refetchFlavors()\n }\n\n const handleSortChange = (newSortSettings: SortSettings) => {\n const settings: RequiredSortSettings = {\n options: newSortSettings.options,\n sortBy: newSortSettings.sortBy?.toString() || \"name\",\n sortDirection: newSortSettings.sortDirection || \"asc\",\n }\n\n setSortSettings(settings)\n navigate({\n search: ((prev: FlavorsSearchParams) => ({\n ...prev,\n sortBy: settings.sortBy,\n sortDirection: settings.sortDirection,\n page: undefined,\n })) as unknown as true,\n replace: true,\n })\n startTransition(() => {\n setFlavorsPromise(createFlavorsPromise(client, project, settings.sortBy, settings.sortDirection, searchTerm))\n })\n }\n\n const handleSearchChange = (term: string | number | string[] | undefined) => {\n const searchValue = typeof term === \"string\" ? term : \"\"\n setSearchTerm(searchValue)\n\n navigate({\n search: ((prev: FlavorsSearchParams) => ({\n ...prev,\n search: searchValue || undefined,\n page: undefined,\n })) as unknown as true,\n replace: true,\n })\n startTransition(() => {\n setFlavorsPromise(\n createFlavorsPromise(client, project, sortSettings.sortBy, sortSettings.sortDirection, searchValue)\n )\n })\n }\n\n const handlePageChange = useCallback(\n (page: number) => {\n navigate({\n search: ((prev: FlavorsSearchParams) => ({\n ...prev,\n page: page === 1 ? undefined : page,\n })) as unknown as true,\n })\n },\n [navigate]\n )\n\n return (\n <div className=\"relative\">\n <ErrorBoundary\n fallbackRender={({ error }) => (\n <Status\n status=\"error\"\n title={t`Failed to Load Flavors`}\n body={error instanceof Error ? error.message : t`An unexpected error occurred.`}\n />\n )}\n >\n <Suspense fallback={<Status status=\"progress\" title={t`Loading Flavors...`} />}>\n <FlavorsContent\n flavorsPromise={flavorsPromise}\n permissionsPromise={permissionsPromise}\n client={client}\n project={project}\n onFlavorDeleted={handleFlavorDeleted}\n onFlavorCreated={handleFlavorCreated}\n searchTerm={searchTerm}\n setSearchTerm={handleSearchChange}\n sortSettings={sortSettings}\n handleSortChange={handleSortChange}\n createModalOpen={createModalOpen}\n setCreateModalOpen={setCreateModalOpen}\n currentPage={currentPage}\n onPageChange={handlePageChange}\n />\n </Suspense>\n </ErrorBoundary>\n </div>\n )\n}\n","import { createFileRoute } from \"@tanstack/react-router\"\nimport { t } from \"@lingui/core/macro\"\nimport { useLingui } from \"@lingui/react/macro\"\nimport { Flavors } from \"./-components/List\"\nimport type { RouteInfo } from \"@/client/routes/routeInfo\"\nimport { ContentHeader } from \"@/client/components/ContentHeader/ContentHeader\"\n\nexport const Route = createFileRoute(\"/_auth/projects/$projectId/compute/flavors/\")({\n staticData: {\n section: \"compute\",\n service: \"flavors\",\n analytics: {\n name: \"compute.flavors.list\",\n },\n } satisfies RouteInfo,\n head: () => ({ meta: [{ title: t`Flavors` }] }),\n component: RouteComponent,\n})\n\nfunction RouteComponent() {\n const { t } = useLingui()\n const { projectId } = Route.useParams()\n const { trpcClient } = Route.useRouteContext()\n return (\n <>\n <ContentHeader title={t`Flavors`} projectId={projectId} />\n <Flavors project={projectId} client={trpcClient!} />\n </>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"flavors-CflJDIEH.mjs","names":["DataGrid","DataGridHeadCell","DataGridRow","DataGridCell","PopupMenu","PopupMenuOptions","PopupMenuItem","PopupMenuSectionSeparator","Status","Pagination","DeleteFlavorModal","useEffect","useState","EditSpecModal","ManageAccessModal","useParams","useNavigate","FlavorListContainer","flavors","isLoading","client","project","onFlavorDeleted","canDeleteFlavor","canMangageAccess","canManageSpecs","currentPage","totalPages","onPageChange","useLingui","navigate","deleteModalOpen","setDeleteModalOpen","specModalOpen","setSpecModalOpen","accessModalOpen","setAccessModalOpen","selectedFlavor","setSelectedFlavor","inputPage","setInputPage","toString","projectId","from","openDeleteModal","flavor","openSpecModal","openAccessModal","closeDeleteModal","handleDeleteSuccess","name","updateCurrentPage","newPage","status","title","t","columns","minContentColumns","className","data-testid","length","map","id","onClick","to","params","flavorId","vcpus","ram","disk","swap","e","stopPropagation","label","colSpan","body","div","variant","pages","onPressPrevious","Math","max","onPressNext","min","onSelectChange","selectedPage","onInputChange","newInputPage","undefined","String","onKeyDown","key","parseInt","isNaN","isOpen","onClose","onSuccess","validateField","field","value","t","undefined","idStr","String","trim","idRegex","test","msg","nameStr","length","vcpus","Number","isNaN","ram","disk","swap","rxtx","str","ephemeral","cleanFlavorData","flavor","result","name","isValidValue","isValidNumber","id","description","rxtx_factor","Boolean","React","useState","Modal","Form","FormRow","FormSection","TextInput","Message","Status","Checkbox","validateField","cleanFlavorData","useErrorTranslation","CreateFlavorModal","client","isOpen","onClose","project","onSuccess","useLingui","translateError","newFlavor","setNewFlavor","errors","setErrors","isLoading","setIsLoading","generalError","setGeneralError","supportsDescription","setSupportsDescription","useEffect","compute","getComputeApiVersion","query","project_id","then","result","catch","handleInputChange","e","name","value","type","checked","target","prev","newState","undefined","handleNumericInputChange","handleBlur","error","handleSubmit","preventDefault","newErrors","requiredFields","forEach","key","optionalFields","Object","keys","length","t","flavorData","createFlavor","mutate","flavor","handleClose","errorMessage","message","dismissError","isFormValid","field","onCancel","size","title","open","onConfirm","cancelButtonLabel","confirmButtonLabel","disableConfirmButton","disableCancelButton","disableCloseButton","status","className","onDismiss","text","variant","id","label","onChange","onBlur","errortext","helptext","required","description","String","vcpus","Number","ram","disk","swap","rxtx_factor","use","Suspense","useState","useRef","startTransition","useEffect","useCallback","ErrorBoundary","useSearch","useNavigate","TRPCClientError","toast","Message","Button","Stack","DataGridToolbar","SearchInput","Status","SortInput","FlavorListContainer","CreateFlavorModal","getFlavorCreatedToast","getFlavorDeletedToast","PAGE_SIZE","createFlavorsPromise","client","project","sortBy","sortDirection","searchTerm","compute","getFlavorsByProjectId","query","project_id","then","res","listError","undefined","catch","err","data","code","flavors","privateFlavorError","message","createPermissionsPromise","canUser","permission","canCreate","canDelete","canAddProject","canRemoveProject","canCreateSpecs","canDeleteSpecs","canManageAccess","canManageSpecs","FlavorsContent","flavorsPromise","permissionsPromise","onFlavorDeleted","onFlavorCreated","setSearchTerm","sortSettings","handleSortChange","createModalOpen","setCreateModalOpen","currentPage","onPageChange","useLingui","permissions","localSearchTerm","setLocalSearchTerm","debounceTimer","clearTimeout","current","p","totalPages","Math","max","ceil","length","safePage","min","paginatedFlavors","slice","isOpen","onClose","onSuccess","className","text","t","variant","distribution","alignment","gap","options","selectClassName","selectWidth","onSortByChange","v","onSortDirectionChange","dir","label","onClick","placeholder","data-testid","value","onInput","e","currentTarget","window","setTimeout","onSearch","onClear","isLoading","canDeleteFlavor","canMangageAccess","Flavors","navigate","searchParams","strict","setSortSettings","search","page","setFlavorsPromise","refetchFlavors","handleFlavorDeleted","flavorName","success","handleFlavorCreated","newSortSettings","settings","toString","prev","replace","handleSearchChange","term","searchValue","handlePageChange","div","fallbackRender","error","status","title","body","Error","fallback","useLingui","Flavors","ContentHeader","Route","RouteComponent","t","projectId","useParams","trpcClient","useRouteContext","component"],"sources":["../../src/client/routes/_auth/projects/$projectId/compute/flavors/-components/FlavorListContainer.tsx","../../src/client/routes/_auth/projects/$projectId/compute/flavors/-components/flavorValidation.ts","../../src/client/routes/_auth/projects/$projectId/compute/flavors/-components/CreateFlavorModal.tsx","../../src/client/routes/_auth/projects/$projectId/compute/flavors/-components/List.tsx","../../src/client/routes/_auth/projects/$projectId/compute/flavors/index.tsx?tsr-split=component"],"sourcesContent":["import { Flavor } from \"@/server/Compute/types/flavor\"\nimport {\n DataGrid,\n DataGridHeadCell,\n DataGridRow,\n DataGridCell,\n PopupMenu,\n PopupMenuOptions,\n PopupMenuItem,\n PopupMenuSectionSeparator,\n Status,\n Pagination,\n} from \"@cloudoperators/juno-ui-components\"\nimport { Trans, useLingui } from \"@lingui/react/macro\"\nimport { DeleteFlavorModal } from \"./DeleteFlavorModal\"\nimport { useEffect, useState } from \"react\"\nimport { TrpcClient } from \"@/client/trpcClient\"\nimport { EditSpecModal } from \"./EditSpecModal\"\nimport { ManageAccessModal } from \"./ManageAccessModal\"\nimport { useParams, useNavigate } from \"@tanstack/react-router\"\n\ninterface FlavorListContainerProps {\n flavors?: Flavor[]\n isLoading: boolean\n client: TrpcClient\n project: string\n onFlavorDeleted?: (flavorName: string) => void\n canDeleteFlavor?: boolean\n canMangageAccess?: boolean\n canManageSpecs?: boolean\n currentPage?: number\n totalPages?: number\n onPageChange?: (page: number) => void\n}\n\nexport const FlavorListContainer = ({\n flavors,\n isLoading,\n client,\n project,\n onFlavorDeleted,\n canDeleteFlavor,\n canMangageAccess,\n canManageSpecs,\n currentPage = 1,\n totalPages = 1,\n onPageChange,\n}: FlavorListContainerProps) => {\n const { t } = useLingui()\n const navigate = useNavigate()\n const [deleteModalOpen, setDeleteModalOpen] = useState(false)\n const [specModalOpen, setSpecModalOpen] = useState(false)\n const [accessModalOpen, setAccessModalOpen] = useState(false)\n const [selectedFlavor, setSelectedFlavor] = useState<Flavor | null>(null)\n const [inputPage, setInputPage] = useState<string>(currentPage.toString())\n\n useEffect(() => {\n setInputPage(currentPage.toString())\n }, [currentPage])\n\n const { projectId } = useParams({\n from: \"/_auth/projects/$projectId/compute/flavors/\",\n })\n\n const openDeleteModal = (flavor: Flavor) => {\n setSelectedFlavor(flavor)\n setDeleteModalOpen(true)\n }\n\n const openSpecModal = (flavor: Flavor) => {\n setSelectedFlavor(flavor)\n setSpecModalOpen(true)\n }\n\n const openAccessModal = (flavor: Flavor) => {\n setSelectedFlavor(flavor)\n setAccessModalOpen(true)\n }\n\n const closeDeleteModal = () => {\n setDeleteModalOpen(false)\n setSelectedFlavor(null)\n }\n\n const handleDeleteSuccess = () => {\n if (selectedFlavor && onFlavorDeleted) {\n onFlavorDeleted(selectedFlavor.name || \"\")\n }\n closeDeleteModal()\n }\n\n const updateCurrentPage = (newPage: number) => {\n if (newPage >= 1 && newPage <= totalPages) {\n onPageChange?.(newPage)\n setInputPage(newPage.toString())\n }\n }\n\n if (isLoading) {\n return <Status status=\"progress\" title={t`Loading...`} />\n }\n\n return (\n <>\n <DataGrid columns={7} minContentColumns={[6]} className=\"flavors\" data-testid=\"flavors-table\">\n <DataGridRow>\n <DataGridHeadCell>\n <Trans>Name</Trans>\n </DataGridHeadCell>\n <DataGridHeadCell>\n <Trans>vCPU</Trans>\n </DataGridHeadCell>\n <DataGridHeadCell>\n <Trans>RAM (MiB)</Trans>\n </DataGridHeadCell>\n <DataGridHeadCell>\n <Trans>Root Disk (GiB)</Trans>\n </DataGridHeadCell>\n <DataGridHeadCell>\n <Trans>Swap (MiB)</Trans>\n </DataGridHeadCell>\n <DataGridHeadCell>\n <Trans>Access Type</Trans>\n </DataGridHeadCell>\n <DataGridHeadCell></DataGridHeadCell>\n </DataGridRow>\n\n {flavors && flavors.length > 0 ? (\n flavors.map((flavor) => (\n <DataGridRow\n key={flavor.id}\n data-testid={`flavor-row-${flavor.id}`}\n onClick={() =>\n navigate({\n to: \"/projects/$projectId/compute/flavors/$flavorId\",\n params: { projectId, flavorId: flavor.id },\n })\n }\n >\n <DataGridCell>{flavor.name || flavor.id}</DataGridCell>\n <DataGridCell>{flavor.vcpus || \"–\"}</DataGridCell>\n <DataGridCell>{flavor.ram || \"–\"}</DataGridCell>\n <DataGridCell>{flavor.disk || \"–\"}</DataGridCell>\n <DataGridCell>{flavor.swap || \"–\"}</DataGridCell>\n <DataGridCell>{flavor[\"os-flavor-access:is_public\"] === false ? t`Private` : t`Public`}</DataGridCell>\n <DataGridCell onClick={(e) => e.stopPropagation()}>\n <PopupMenu>\n <PopupMenuOptions>\n {canManageSpecs && <PopupMenuItem label={t`Edit Metadata`} onClick={() => openSpecModal(flavor)} />}\n {canMangageAccess && flavor[\"os-flavor-access:is_public\"] === false && (\n <PopupMenuItem label={t`Manage Access`} onClick={() => openAccessModal(flavor)} />\n )}\n <PopupMenuItem\n label={t`Show Details`}\n onClick={() =>\n navigate({\n to: \"/projects/$projectId/compute/flavors/$flavorId\",\n params: { projectId, flavorId: flavor.id },\n })\n }\n />\n {canDeleteFlavor && (\n <>\n <PopupMenuSectionSeparator />\n <PopupMenuItem label={t`Delete Flavor`} onClick={() => openDeleteModal(flavor)} />\n </>\n )}\n </PopupMenuOptions>\n </PopupMenu>\n </DataGridCell>\n </DataGridRow>\n ))\n ) : (\n <DataGridRow>\n <DataGridCell colSpan={7}>\n <Status\n status=\"empty\"\n title={t`No flavors found`}\n body={t`There are no flavors available for this project with the current filters applied. Try adjusting your filter criteria or create a new flavor.`}\n />\n </DataGridCell>\n </DataGridRow>\n )}\n </DataGrid>\n {totalPages > 1 && (\n <div className=\"flex justify-center py-4\">\n <Pagination\n variant=\"input\"\n currentPage={currentPage}\n pages={totalPages}\n onPressPrevious={() => updateCurrentPage(Math.max(currentPage - 1, 1))}\n onPressNext={() => updateCurrentPage(Math.min(currentPage + 1, totalPages))}\n onSelectChange={(selectedPage: number) => {\n updateCurrentPage(selectedPage)\n }}\n onInputChange={(newInputPage?: number) => {\n setInputPage(newInputPage === undefined ? \"\" : String(newInputPage))\n }}\n onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === \"Enter\" && inputPage !== \"\") {\n const newPage = parseInt(inputPage, 10)\n if (!isNaN(newPage) && newPage >= 1 && newPage <= totalPages) {\n updateCurrentPage(newPage)\n }\n }\n }}\n />\n </div>\n )}\n <DeleteFlavorModal\n client={client}\n isOpen={deleteModalOpen}\n onClose={closeDeleteModal}\n project={project}\n flavor={selectedFlavor}\n onSuccess={handleDeleteSuccess}\n />\n <EditSpecModal\n client={client}\n isOpen={specModalOpen}\n onClose={() => setSpecModalOpen(false)}\n project={project}\n flavor={selectedFlavor}\n />\n\n <ManageAccessModal\n client={client}\n isOpen={accessModalOpen}\n onClose={() => setAccessModalOpen(false)}\n project={project}\n flavor={selectedFlavor}\n />\n </>\n )\n}\n","import { msg } from \"@lingui/core/macro\"\nimport { MessageDescriptor } from \"@lingui/core\"\nimport { Flavor } from \"@/server/Compute/types/flavor\"\n\nexport const validateField = (\n field: FlavorFormField,\n value: string | number | boolean | null | undefined,\n t: (descriptor: MessageDescriptor) => string\n): string | undefined => {\n switch (field) {\n case \"id\":\n if (!value) return undefined\n {\n const idStr = String(value).trim()\n const idRegex = /^[a-zA-Z0-9.\\-_]*$/\n return idRegex.test(idStr)\n ? undefined\n : t(msg`ID must only contain alphanumeric characters, hyphens, underscores, and dots.`)\n }\n\n case \"name\": {\n const nameStr = String(value || \"\").trim()\n return nameStr.length >= 2 && nameStr.length <= 50 ? undefined : t(msg`Name must be 2-50 characters long.`)\n }\n\n case \"vcpus\": {\n const vcpus = Number(value)\n return !isNaN(vcpus) && vcpus >= 1 ? undefined : t(msg`VCPUs must be an integer ≥ 1.`)\n }\n\n case \"ram\": {\n const ram = Number(value)\n return !isNaN(ram) && ram >= 128 ? undefined : t(msg`RAM must be an integer ≥ 128 MB.`)\n }\n\n case \"disk\": {\n const disk = Number(value)\n return !isNaN(disk) && disk >= 0 ? undefined : t(msg`Root Disk must be an integer ≥ 0.`)\n }\n\n case \"swap\":\n if (value === \"\" || value === undefined || value === null) return undefined\n {\n const swap = Number(value)\n return !isNaN(swap) && swap >= 0 ? undefined : t(msg`Swap Disk must be an integer ≥ 0.`)\n }\n\n case \"rxtx_factor\": {\n const rxtx = Number(value)\n return !isNaN(rxtx) && rxtx >= 1 ? undefined : t(msg`RX/TX Factor must be an integer ≥ 1.`)\n }\n\n case \"description\":\n if (!value) return undefined\n {\n const str = String(value)\n return str.length < 65535 ? undefined : t(msg`Description must be less than 65535 characters.`)\n }\n\n case \"OS-FLV-EXT-DATA:ephemeral\": {\n const ephemeral = Number(value)\n return !isNaN(ephemeral) && ephemeral >= 0 ? undefined : t(msg`Ephemeral Disk must be an integer ≥ 0.`)\n }\n\n case \"os-flavor-access:is_public\": {\n if (value === undefined || value === null) {\n return undefined\n }\n if (typeof value === \"boolean\") {\n return undefined\n }\n if (typeof value === \"string\" && (value === \"true\" || value === \"false\")) {\n return undefined\n }\n return t(msg`Invalid value for public flavor setting.`)\n }\n\n default:\n return undefined\n }\n}\n\nexport type FlavorFormField =\n | \"id\"\n | \"name\"\n | \"vcpus\"\n | \"ram\"\n | \"disk\"\n | \"swap\"\n | \"description\"\n | \"rxtx_factor\"\n | \"OS-FLV-EXT-DATA:ephemeral\"\n | \"os-flavor-access:is_public\"\n\nexport interface FieldErrors {\n id?: string\n name?: string\n vcpus?: string\n ram?: string\n disk?: string\n swap?: string\n rxtx_factor?: string\n description?: string\n \"OS-FLV-EXT-DATA:ephemeral\"?: string\n \"os-flavor-access:is_public\"?: string\n}\n\nexport const cleanFlavorData = (flavor: Partial<Flavor>) => {\n const result: {\n name: string\n vcpus: number\n ram: number\n disk: number\n \"OS-FLV-EXT-DATA:ephemeral\"?: number\n id?: string\n swap?: number\n rxtx_factor?: number\n description?: string\n \"os-flavor-access:is_public\"?: boolean\n } = {\n name: String(flavor.name),\n vcpus: Number(flavor.vcpus),\n ram: Number(flavor.ram),\n disk: Number(flavor.disk),\n }\n\n const isValidValue = (value: string | undefined | null) => {\n return value !== undefined && value !== null && value !== \"\" && String(value).trim() !== \"\"\n }\n\n const isValidNumber = (value: string | number | undefined | null) => {\n return value !== undefined && value !== null && value !== \"\" && String(value).trim() !== \"\" && !isNaN(Number(value))\n }\n\n if (isValidValue(flavor.id)) {\n result.id = String(flavor.id).trim()\n }\n\n if (isValidValue(flavor.description)) {\n result.description = String(flavor.description).trim()\n }\n\n if (isValidNumber(flavor.swap)) {\n result.swap = Number(flavor.swap)\n }\n\n if (isValidNumber(flavor.rxtx_factor)) {\n result.rxtx_factor = Number(flavor.rxtx_factor)\n }\n\n if (isValidNumber(flavor[\"OS-FLV-EXT-DATA:ephemeral\"])) {\n result[\"OS-FLV-EXT-DATA:ephemeral\"] = Number(flavor[\"OS-FLV-EXT-DATA:ephemeral\"])\n }\n\n if (flavor[\"os-flavor-access:is_public\"] !== undefined) {\n result[\"os-flavor-access:is_public\"] = Boolean(flavor[\"os-flavor-access:is_public\"])\n }\n\n return result\n}\n","import React, { useState } from \"react\"\nimport { useLingui } from \"@lingui/react/macro\"\nimport { TrpcClient } from \"@/client/trpcClient\"\nimport {\n Modal,\n Form,\n FormRow,\n FormSection,\n TextInput,\n Message,\n Status,\n Checkbox,\n} from \"@cloudoperators/juno-ui-components\"\nimport { Flavor } from \"@/server/Compute/types/flavor\"\nimport { validateField, FlavorFormField, FieldErrors } from \"./flavorValidation\"\nimport { cleanFlavorData } from \"./flavorValidation\"\nimport { useErrorTranslation } from \"@/client/utils/useErrorTranslation\"\n\ninterface CreateFlavorModalProps {\n client: TrpcClient\n isOpen: boolean\n onClose: () => void\n project: string\n onSuccess: (name: string) => void\n}\n\nexport const CreateFlavorModal: React.FC<CreateFlavorModalProps> = ({\n client,\n isOpen,\n onClose,\n project,\n onSuccess,\n}) => {\n const { t } = useLingui()\n const { translateError } = useErrorTranslation()\n const [newFlavor, setNewFlavor] = useState<Partial<Flavor>>({\n \"os-flavor-access:is_public\": true,\n })\n const [errors, setErrors] = useState<FieldErrors>({})\n const [isLoading, setIsLoading] = useState(false)\n const [generalError, setGeneralError] = useState<string | null>(null)\n const [supportsDescription, setSupportsDescription] = useState(false)\n\n // Check if Nova API supports description field (microversion 2.55+)\n React.useEffect(() => {\n if (isOpen) {\n client.compute.getComputeApiVersion\n .query({ project_id: project })\n .then((result) => setSupportsDescription(result.supportsDescription))\n .catch(() => setSupportsDescription(false))\n }\n }, [isOpen, client, project])\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const { name, value, type, checked } = e.target\n\n setNewFlavor((prev) => {\n const newState = {\n ...prev,\n [name]: type === \"checkbox\" ? checked : value,\n }\n\n return newState\n })\n // Clear error for this field when user changes the value\n setErrors((prev) => ({\n ...prev,\n [name]: undefined,\n }))\n if (generalError) setGeneralError(null)\n }\n\n const handleNumericInputChange = (name: FlavorFormField, value: number | undefined) => {\n setNewFlavor((prev) => ({\n ...prev,\n [name]: value,\n }))\n // Clear error for this field when user changes the value\n setErrors((prev) => ({\n ...prev,\n [name]: undefined,\n }))\n if (generalError) setGeneralError(null)\n }\n\n const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {\n const { name, value } = e.target\n const error = validateField(name as FlavorFormField, value, t)\n setErrors((prev) => ({\n ...prev,\n [name]: error,\n }))\n }\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault()\n setGeneralError(null)\n\n const newErrors: FieldErrors = {}\n\n const requiredFields: FlavorFormField[] = [\"name\", \"vcpus\", \"ram\", \"disk\"]\n requiredFields.forEach((key) => {\n const error = validateField(key, newFlavor[key], t)\n if (error) {\n newErrors[key] = error\n }\n })\n\n const optionalFields: FlavorFormField[] = [\n \"id\",\n \"swap\",\n \"OS-FLV-EXT-DATA:ephemeral\",\n \"rxtx_factor\",\n \"description\",\n \"os-flavor-access:is_public\",\n ]\n optionalFields.forEach((key) => {\n const value = newFlavor[key]\n\n if (value !== undefined && value !== \"\" && value !== null) {\n const error = validateField(key, value, t)\n if (error) {\n newErrors[key] = error\n }\n }\n })\n\n if (Object.keys(newErrors).length > 0) {\n setErrors(newErrors)\n setGeneralError(t`Please fix the validation errors below.`)\n return\n }\n\n try {\n setIsLoading(true)\n\n const flavorData = cleanFlavorData(newFlavor)\n\n await client.compute.createFlavor.mutate({\n project_id: project,\n flavor: flavorData,\n })\n\n onSuccess(flavorData.name)\n handleClose()\n } catch (error) {\n const errorMessage = (error as Error)?.message\n ? translateError((error as Error).message)\n : t`Failed to create flavor. Please try again.`\n setGeneralError(errorMessage)\n } finally {\n setIsLoading(false)\n }\n }\n\n const handleClose = () => {\n setNewFlavor({\n \"os-flavor-access:is_public\": true,\n })\n setErrors({})\n setGeneralError(null)\n onClose()\n }\n\n const dismissError = () => {\n setGeneralError(null)\n }\n\n const isFormValid = () => {\n // Check required fields are filled and have no errors\n const requiredFields: FlavorFormField[] = [\"name\", \"vcpus\", \"ram\", \"disk\"]\n\n for (const field of requiredFields) {\n const value = newFlavor[field]\n // Field must have a value\n if (value === undefined || value === \"\" || value === null) return false\n // Field must not have a validation error\n if (validateField(field, value, t)) return false\n }\n\n // Check optional fields for errors (if they have values)\n const optionalFields: FlavorFormField[] = [\"id\", \"swap\", \"OS-FLV-EXT-DATA:ephemeral\", \"rxtx_factor\", \"description\"]\n\n for (const field of optionalFields) {\n const value = newFlavor[field]\n // Skip empty optional fields\n if (value === undefined || value === \"\" || value === null) continue\n // Field must not have a validation error\n if (validateField(field, value, t)) return false\n }\n\n return true\n }\n\n return (\n <Modal\n onCancel={handleClose}\n size=\"large\"\n title={t`Create Flavor`}\n open={isOpen}\n onConfirm={handleSubmit}\n cancelButtonLabel={t`Cancel`}\n confirmButtonLabel={t`Create New Flavor`}\n disableConfirmButton={!isFormValid() || isLoading}\n disableCancelButton={isLoading}\n disableCloseButton={isLoading}\n >\n {isLoading && <Status status=\"progress\" title={t`Creating Flavor...`} className=\"mt-0\" />}\n {!isLoading && (\n <Form>\n {generalError && (\n <FormRow>\n <Message onDismiss={dismissError} text={generalError} variant=\"error\" />\n </FormRow>\n )}\n\n <FormSection title={t`Flavor Information`}>\n <FormRow>\n <TextInput\n id=\"id\"\n name=\"id\"\n label={t`Flavor ID`}\n value={newFlavor.id || \"\"}\n onChange={handleInputChange}\n onBlur={handleBlur}\n errortext={errors.id}\n helptext={t`Auto-generated if not provided. Only alphanumeric, hyphens, underscores, and dots allowed.`}\n />\n </FormRow>\n <FormRow>\n <TextInput\n id=\"name\"\n name=\"name\"\n label={t`Flavor Name`}\n value={newFlavor.name || \"\"}\n onChange={handleInputChange}\n onBlur={handleBlur}\n errortext={errors.name}\n helptext={t`2-50 characters.`}\n required\n />\n </FormRow>\n {supportsDescription && (\n <FormRow>\n <TextInput\n id=\"description\"\n name=\"description\"\n label={t`Description`}\n value={newFlavor.description || \"\"}\n onChange={handleInputChange}\n onBlur={handleBlur}\n errortext={errors.description}\n />\n </FormRow>\n )}\n <FormRow>\n <Checkbox\n name=\"os-flavor-access:is_public\"\n label={t`Public Flavor`}\n helptext={t`When checked, all tenants can use this flavor. Otherwise, grant access explicitly.`}\n checked={!!newFlavor[\"os-flavor-access:is_public\"]}\n onChange={handleInputChange}\n />\n </FormRow>\n </FormSection>\n\n <FormSection title={t`Hardware Specifications`}>\n <FormRow>\n <TextInput\n id=\"vcpus\"\n name=\"vcpus\"\n label={t`vCPU`}\n value={String(newFlavor.vcpus || \"\")}\n onChange={(e) => handleNumericInputChange(\"vcpus\", Number(e.target.value))}\n onBlur={handleBlur}\n errortext={errors.vcpus}\n type=\"number\"\n required\n />\n </FormRow>\n <FormRow>\n <TextInput\n id=\"ram\"\n name=\"ram\"\n label={t`RAM (MiB)`}\n value={String(newFlavor.ram || \"\")}\n onChange={(e) => handleNumericInputChange(\"ram\", Number(e.target.value))}\n onBlur={handleBlur}\n errortext={errors.ram}\n helptext={t`Minimum 128 MB.`}\n type=\"number\"\n required\n />\n </FormRow>\n <FormRow>\n <TextInput\n id=\"disk\"\n name=\"disk\"\n label={t`Root Disk (GiB)`}\n value={String(newFlavor.disk || \"\")}\n onChange={(e) => handleNumericInputChange(\"disk\", Number(e.target.value))}\n onBlur={handleBlur}\n errortext={errors.disk}\n type=\"number\"\n required\n />\n </FormRow>\n <FormRow>\n <TextInput\n id=\"OS-FLV-EXT-DATA:ephemeral\"\n name=\"OS-FLV-EXT-DATA:ephemeral\"\n label={t`Ephemeral Disk (GiB)`}\n value={String(newFlavor[\"OS-FLV-EXT-DATA:ephemeral\"] || \"\")}\n onChange={(e) => handleNumericInputChange(\"OS-FLV-EXT-DATA:ephemeral\", Number(e.target.value))}\n onBlur={handleBlur}\n errortext={errors[\"OS-FLV-EXT-DATA:ephemeral\"]}\n type=\"number\"\n />\n </FormRow>\n <FormRow>\n <TextInput\n id=\"swap\"\n name=\"swap\"\n label={t`Swap (MiB)`}\n value={String(newFlavor.swap || \"\")}\n onChange={(e) => handleNumericInputChange(\"swap\", e.target.value ? Number(e.target.value) : undefined)}\n onBlur={handleBlur}\n errortext={errors.swap}\n type=\"number\"\n />\n </FormRow>\n <FormRow>\n <TextInput\n id=\"rxtx_factor\"\n name=\"rxtx_factor\"\n label={t`RX/TX Factor`}\n value={String(newFlavor.rxtx_factor || \"\")}\n onChange={(e) => handleNumericInputChange(\"rxtx_factor\", Number(e.target.value))}\n onBlur={handleBlur}\n errortext={errors.rxtx_factor}\n helptext={t`Defaults to 1.`}\n type=\"number\"\n />\n </FormRow>\n </FormSection>\n </Form>\n )}\n </Modal>\n )\n}\n","import { use, Suspense, useState, useRef, startTransition, useEffect, useCallback } from \"react\"\nimport { ErrorBoundary } from \"react-error-boundary\"\nimport { useLingui } from \"@lingui/react/macro\"\nimport { useSearch, useNavigate } from \"@tanstack/react-router\"\nimport { TrpcClient } from \"@/client/trpcClient\"\nimport { Flavor } from \"@/server/Compute/types/flavor\"\nimport { TRPCClientError } from \"@trpc/client\"\nimport { toast, Message, Button, Stack, DataGridToolbar, SearchInput, Status } from \"@cloudoperators/juno-ui-components\"\nimport { SortInput } from \"@/client/components/ListToolbar/SortInput\"\nimport { SortSettings } from \"@/client/components/ListToolbar/types\"\nimport { FlavorListContainer } from \"./FlavorListContainer\"\nimport { CreateFlavorModal } from \"./CreateFlavorModal\"\nimport { getFlavorCreatedToast, getFlavorDeletedToast } from \"./FlavorToastNotifications\"\nimport type { FlavorsSearchParams } from \"@/client/routes/_auth/projects/$projectId/compute/flavors\"\n\nconst PAGE_SIZE = 50\n\ninterface FlavorsProps {\n client: TrpcClient\n project: string\n}\n\ntype RequiredSortSettings = {\n options: SortSettings[\"options\"]\n sortBy: string\n sortDirection: \"asc\" | \"desc\"\n}\n\nconst createFlavorsPromise = (\n client: TrpcClient,\n project: string,\n sortBy: string,\n sortDirection: string,\n searchTerm: string\n) => {\n return client.compute.getFlavorsByProjectId\n .query({ project_id: project, sortBy, sortDirection, searchTerm })\n .then((res) => ({ ...res, listError: undefined as string | undefined }))\n .catch((err: unknown) => {\n if (err instanceof TRPCClientError && err.data?.code === \"FORBIDDEN\") {\n return { flavors: [] as Flavor[], privateFlavorError: undefined, listError: err.message }\n }\n throw err\n })\n}\n\nconst createPermissionsPromise = (client: TrpcClient, project: string) => {\n return client.compute.canUser\n .query({\n project_id: project,\n permission: [\n \"flavors:create\",\n \"flavors:delete\",\n \"flavors:add_project\",\n \"flavors:remove_project\",\n \"flavor_specs:create\",\n \"flavor_specs:delete\",\n ],\n })\n .then(([canCreate, canDelete, canAddProject, canRemoveProject, canCreateSpecs, canDeleteSpecs]) => ({\n canCreate,\n canDelete,\n canManageAccess: canAddProject || canRemoveProject,\n canManageSpecs: canCreateSpecs || canDeleteSpecs,\n }))\n}\n\nfunction FlavorsContent({\n flavorsPromise,\n permissionsPromise,\n client,\n project,\n onFlavorDeleted,\n onFlavorCreated,\n searchTerm,\n setSearchTerm,\n sortSettings,\n handleSortChange,\n createModalOpen,\n setCreateModalOpen,\n currentPage,\n onPageChange,\n}: {\n flavorsPromise: Promise<{ flavors: Flavor[]; privateFlavorError?: string; listError?: string }>\n permissionsPromise: Promise<{\n canCreate: boolean\n canDelete: boolean\n canManageAccess: boolean\n canManageSpecs: boolean\n }>\n client: TrpcClient\n project: string\n onFlavorDeleted: (name: string) => void\n onFlavorCreated: (name: string) => void\n searchTerm: string\n setSearchTerm: (term: string) => void\n sortSettings: SortSettings\n handleSortChange: (settings: SortSettings) => void\n createModalOpen: boolean\n setCreateModalOpen: (open: boolean) => void\n currentPage: number\n onPageChange: (page: number) => void\n}) {\n const { t } = useLingui()\n const { flavors, privateFlavorError, listError } = use(flavorsPromise)\n const permissions = use(permissionsPromise)\n const [localSearchTerm, setLocalSearchTerm] = useState(searchTerm)\n const debounceTimer = useRef<number | undefined>(undefined)\n\n useEffect(() => () => clearTimeout(debounceTimer.current), [])\n\n if (listError) {\n return <p>{listError}</p>\n }\n\n const totalPages = Math.max(1, Math.ceil(flavors.length / PAGE_SIZE))\n const safePage = Math.min(currentPage, totalPages)\n const paginatedFlavors = flavors.slice((safePage - 1) * PAGE_SIZE, safePage * PAGE_SIZE)\n\n useEffect(() => {\n if (currentPage > totalPages) onPageChange(1)\n }, [totalPages, currentPage, onPageChange])\n\n return (\n <>\n <CreateFlavorModal\n client={client}\n isOpen={createModalOpen}\n project={project}\n onClose={() => setCreateModalOpen(false)}\n onSuccess={onFlavorCreated}\n />\n\n {privateFlavorError && (\n <Message\n className=\"mb-4\"\n text={t`Private flavors could not be loaded. You may be seeing an incomplete list.`}\n variant=\"warning\"\n />\n )}\n\n {/* Zone 1 — sort + create action, no background */}\n <Stack distribution=\"end\" alignment=\"center\" gap=\"2\" className=\"pb-2\">\n <Stack gap=\"2\">\n <SortInput\n options={sortSettings.options}\n sortBy={sortSettings.sortBy}\n sortDirection={sortSettings.sortDirection ?? \"asc\"}\n selectClassName=\"min-w-45\"\n selectWidth=\"auto\"\n onSortByChange={(v) =>\n handleSortChange({ ...sortSettings, sortBy: v, sortDirection: sortSettings.sortDirection })\n }\n onSortDirectionChange={(dir) => handleSortChange({ ...sortSettings, sortDirection: dir })}\n />\n {permissions.canCreate && (\n <Button variant=\"primary\" label={t`Create Flavor`} onClick={() => setCreateModalOpen(true)} />\n )}\n </Stack>\n </Stack>\n\n {/* Zone 2 — search bar */}\n <DataGridToolbar>\n <Stack distribution=\"end\" alignment=\"center\">\n <SearchInput\n placeholder={t`Search flavors...`}\n data-testid=\"searchbar\"\n value={localSearchTerm}\n onInput={(e: React.FormEvent<HTMLInputElement>) => {\n const v = e.currentTarget.value\n setLocalSearchTerm(v)\n clearTimeout(debounceTimer.current)\n debounceTimer.current = window.setTimeout(() => setSearchTerm(v), 500)\n }}\n onSearch={(v) => {\n clearTimeout(debounceTimer.current)\n setSearchTerm(typeof v === \"string\" ? v : \"\")\n }}\n onClear={() => {\n clearTimeout(debounceTimer.current)\n setLocalSearchTerm(\"\")\n setSearchTerm(\"\")\n }}\n />\n </Stack>\n </DataGridToolbar>\n\n <FlavorListContainer\n flavors={paginatedFlavors}\n isLoading={false}\n client={client}\n project={project}\n onFlavorDeleted={onFlavorDeleted}\n canDeleteFlavor={permissions.canDelete}\n canMangageAccess={permissions.canManageAccess}\n canManageSpecs={permissions.canManageSpecs}\n currentPage={safePage}\n totalPages={totalPages}\n onPageChange={onPageChange}\n />\n </>\n )\n}\nexport const Flavors = ({ client, project }: FlavorsProps) => {\n const { t } = useLingui()\n const navigate = useNavigate()\n const searchParams = useSearch({ strict: false }) as FlavorsSearchParams\n\n const [sortSettings, setSortSettings] = useState<RequiredSortSettings>({\n options: [\n { label: t`Name`, value: \"name\" },\n { label: t`vCPU`, value: \"vcpus\" },\n { label: t`RAM (MiB)`, value: \"ram\" },\n { label: t`Root Disk (GiB)`, value: \"disk\" },\n { label: t`Swap (MiB)`, value: \"swap\" },\n ],\n sortBy: searchParams.sortBy || \"name\",\n sortDirection: searchParams.sortDirection || \"asc\",\n })\n\n const [searchTerm, setSearchTerm] = useState(searchParams.search || \"\")\n const currentPage = searchParams.page ?? 1\n const [createModalOpen, setCreateModalOpen] = useState(false)\n\n const [flavorsPromise, setFlavorsPromise] = useState(() =>\n createFlavorsPromise(client, project, sortSettings.sortBy, sortSettings.sortDirection, searchTerm)\n )\n const [permissionsPromise] = useState(() => createPermissionsPromise(client, project))\n\n const refetchFlavors = () => {\n startTransition(() => {\n setFlavorsPromise(\n createFlavorsPromise(client, project, sortSettings.sortBy, sortSettings.sortDirection, searchTerm)\n )\n })\n }\n\n const handleFlavorDeleted = (flavorName: string) => {\n const { message, ...options } = getFlavorDeletedToast(flavorName)\n toast.success(message, options)\n refetchFlavors()\n }\n\n const handleFlavorCreated = (flavorName: string) => {\n const { message, ...options } = getFlavorCreatedToast(flavorName)\n toast.success(message, options)\n refetchFlavors()\n }\n\n const handleSortChange = (newSortSettings: SortSettings) => {\n const settings: RequiredSortSettings = {\n options: newSortSettings.options,\n sortBy: newSortSettings.sortBy?.toString() || \"name\",\n sortDirection: newSortSettings.sortDirection || \"asc\",\n }\n\n setSortSettings(settings)\n navigate({\n search: ((prev: FlavorsSearchParams) => ({\n ...prev,\n sortBy: settings.sortBy,\n sortDirection: settings.sortDirection,\n page: undefined,\n })) as unknown as true,\n replace: true,\n })\n startTransition(() => {\n setFlavorsPromise(createFlavorsPromise(client, project, settings.sortBy, settings.sortDirection, searchTerm))\n })\n }\n\n const handleSearchChange = (term: string | number | string[] | undefined) => {\n const searchValue = typeof term === \"string\" ? term : \"\"\n setSearchTerm(searchValue)\n\n navigate({\n search: ((prev: FlavorsSearchParams) => ({\n ...prev,\n search: searchValue || undefined,\n page: undefined,\n })) as unknown as true,\n replace: true,\n })\n startTransition(() => {\n setFlavorsPromise(\n createFlavorsPromise(client, project, sortSettings.sortBy, sortSettings.sortDirection, searchValue)\n )\n })\n }\n\n const handlePageChange = useCallback(\n (page: number) => {\n navigate({\n search: ((prev: FlavorsSearchParams) => ({\n ...prev,\n page: page === 1 ? undefined : page,\n })) as unknown as true,\n })\n },\n [navigate]\n )\n\n return (\n <div className=\"relative\">\n <ErrorBoundary\n fallbackRender={({ error }) => (\n <Status\n status=\"error\"\n title={t`Failed to Load Flavors`}\n body={error instanceof Error ? error.message : t`An unexpected error occurred.`}\n />\n )}\n >\n <Suspense fallback={<Status status=\"progress\" title={t`Loading Flavors...`} />}>\n <FlavorsContent\n flavorsPromise={flavorsPromise}\n permissionsPromise={permissionsPromise}\n client={client}\n project={project}\n onFlavorDeleted={handleFlavorDeleted}\n onFlavorCreated={handleFlavorCreated}\n searchTerm={searchTerm}\n setSearchTerm={handleSearchChange}\n sortSettings={sortSettings}\n handleSortChange={handleSortChange}\n createModalOpen={createModalOpen}\n setCreateModalOpen={setCreateModalOpen}\n currentPage={currentPage}\n onPageChange={handlePageChange}\n />\n </Suspense>\n </ErrorBoundary>\n </div>\n )\n}\n","import { createFileRoute } from \"@tanstack/react-router\"\nimport { t } from \"@lingui/core/macro\"\nimport { useLingui } from \"@lingui/react/macro\"\nimport { Flavors } from \"./-components/List\"\nimport type { RouteInfo } from \"@/client/routes/routeInfo\"\nimport { ContentHeader } from \"@/client/components/ContentHeader/ContentHeader\"\n\nexport const Route = createFileRoute(\"/_auth/projects/$projectId/compute/flavors/\")({\n staticData: {\n section: \"compute\",\n service: \"flavors\",\n analytics: {\n name: \"compute.flavors.list\",\n },\n } satisfies RouteInfo,\n head: () => ({ meta: [{ title: t`Flavors` }] }),\n component: RouteComponent,\n})\n\nfunction RouteComponent() {\n const { t } = useLingui()\n const { projectId } = Route.useParams()\n const { trpcClient } = Route.useRouteContext()\n return (\n <>\n <ContentHeader title={t`Flavors`} projectId={projectId} />\n <Flavors project={projectId} client={trpcClient!} />\n </>\n )\n}\n"],"mappings":"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"}