@cobaltcore-dev/aurora 0.8.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/README.md +47 -13
  2. package/dist/client/AuroraApp.d.ts +14 -0
  3. package/dist/client/ContentHeader-D4jlOG-9.mjs +96 -0
  4. package/dist/client/ContentHeader-D4jlOG-9.mjs.map +1 -0
  5. package/dist/client/DeleteVersionsModal-CyYZfB8d.mjs +331 -0
  6. package/dist/client/DeleteVersionsModal-CyYZfB8d.mjs.map +1 -0
  7. package/dist/client/Slot-CWb612-_.mjs +28 -0
  8. package/dist/client/Slot-CWb612-_.mjs.map +1 -0
  9. package/dist/client/{SortInput-VK7IYqQv.mjs → SortInput-DLcusjGZ.mjs} +8 -8
  10. package/dist/client/SortInput-DLcusjGZ.mjs.map +1 -0
  11. package/dist/client/_auth-DXJkv9QO.mjs.map +1 -1
  12. package/dist/client/{_flavorId-iZE2j210.mjs → _flavorId-DsD2VTKA.mjs} +3 -3
  13. package/dist/client/{_flavorId-iZE2j210.mjs.map → _flavorId-DsD2VTKA.mjs.map} +1 -1
  14. package/dist/client/{_flavorId-DU4gcFna.mjs → _flavorId-Dy7EYQum.mjs} +2 -2
  15. package/dist/client/{_flavorId-DU4gcFna.mjs.map → _flavorId-Dy7EYQum.mjs.map} +1 -1
  16. package/dist/client/{_floatingIpId-B5GMSLeQ.mjs → _floatingIpId-BjVbeNw_.mjs} +2 -2
  17. package/dist/client/{_floatingIpId-B5GMSLeQ.mjs.map → _floatingIpId-BjVbeNw_.mjs.map} +1 -1
  18. package/dist/client/{_floatingIpId-C2-BeRmF.mjs → _floatingIpId-j17rCQqG2.mjs} +2 -2
  19. package/dist/client/_floatingIpId-j17rCQqG2.mjs.map +1 -0
  20. package/dist/client/{_imageId-zmaSymWe.mjs → _imageId-BjfhqAje.mjs} +2 -2
  21. package/dist/client/{_imageId-zmaSymWe.mjs.map → _imageId-BjfhqAje.mjs.map} +1 -1
  22. package/dist/client/{_pcaId-BwTvJJgh.mjs → _pcaId-Bo7yHkNW.mjs} +3 -3
  23. package/dist/client/{_pcaId-BwTvJJgh.mjs.map → _pcaId-Bo7yHkNW.mjs.map} +1 -1
  24. package/dist/client/{_pcaId-DUHQd0rB.mjs → _pcaId-CKkCVC7b.mjs} +2 -2
  25. package/dist/client/{_pcaId-DUHQd0rB.mjs.map → _pcaId-CKkCVC7b.mjs.map} +1 -1
  26. package/dist/client/_projectId-B_2sZKk-.mjs.map +1 -1
  27. package/dist/client/_projectId-CARHuZTU.mjs +106 -0
  28. package/dist/client/_projectId-CARHuZTU.mjs.map +1 -0
  29. package/dist/client/_projectId-CY8W0IF6.mjs +27 -0
  30. package/dist/client/_projectId-CY8W0IF6.mjs.map +1 -0
  31. package/dist/client/_projectId-Dbck_MFa.mjs +290 -0
  32. package/dist/client/_projectId-Dbck_MFa.mjs.map +1 -0
  33. package/dist/client/{_securityGroupId-DYxmXUOP.mjs → _securityGroupId-CkN0CGVg.mjs} +3 -3
  34. package/dist/client/{_securityGroupId-DYxmXUOP.mjs.map → _securityGroupId-CkN0CGVg.mjs.map} +1 -1
  35. package/dist/client/{_securityGroupId-fhK1CuZh.mjs → _securityGroupId-gSEZbBII.mjs} +2 -2
  36. package/dist/client/{_securityGroupId-fhK1CuZh.mjs.map → _securityGroupId-gSEZbBII.mjs.map} +1 -1
  37. package/dist/client/{_storageType-CepuevDG.mjs → _storageType-6k8lUnQo.mjs} +2 -2
  38. package/dist/client/{_storageType-CepuevDG.mjs.map → _storageType-6k8lUnQo.mjs.map} +1 -1
  39. package/dist/client/_storageType-CLTxXjQZ.mjs +3048 -0
  40. package/dist/client/_storageType-CLTxXjQZ.mjs.map +1 -0
  41. package/dist/client/{constants-J5nm9hbP.mjs → constants-PMXUGI4Q.mjs} +2 -2
  42. package/dist/client/{constants-J5nm9hbP.mjs.map → constants-PMXUGI4Q.mjs.map} +1 -1
  43. package/dist/client/{flavors-8bZVlzzb.mjs → flavors-BclEwobO.mjs} +2 -2
  44. package/dist/client/{flavors-8bZVlzzb.mjs.map → flavors-BclEwobO.mjs.map} +1 -1
  45. package/dist/client/{flavors-BfsEBUE-.mjs → flavors-p2TKcqP-.mjs} +4 -4
  46. package/dist/client/{flavors-BfsEBUE-.mjs.map → flavors-p2TKcqP-.mjs.map} +1 -1
  47. package/dist/client/{floatingips-Dq4DXQYb.mjs → floatingips-ph0ZxJw8.mjs} +3 -3
  48. package/dist/client/{floatingips-Dq4DXQYb.mjs.map → floatingips-ph0ZxJw8.mjs.map} +1 -1
  49. package/dist/client/{images-BPnTuKFO2.mjs → images-BblnyWJq.mjs} +4 -4
  50. package/dist/client/images-BblnyWJq.mjs.map +1 -0
  51. package/dist/client/{images-8FOgju2f.mjs → images-CXdghaMW.mjs} +2 -2
  52. package/dist/client/{images-8FOgju2f.mjs.map → images-CXdghaMW.mjs.map} +1 -1
  53. package/dist/client/index.js +261 -247
  54. package/dist/client/index.js.map +1 -1
  55. package/dist/client/{md-CYTrL5dq.mjs → md-CyCflQee.mjs} +10 -28
  56. package/dist/client/{md-CYTrL5dq.mjs.map → md-CyCflQee.mjs.map} +1 -1
  57. package/dist/client/{objects-DKWp9RtR.mjs → objects-B9Jh3SMG.mjs} +4 -3
  58. package/dist/client/objects-B9Jh3SMG.mjs.map +1 -0
  59. package/dist/client/objects-Bw25cE1m.mjs +5970 -0
  60. package/dist/client/objects-Bw25cE1m.mjs.map +1 -0
  61. package/dist/client/objects-o2Cj_ndZ.mjs.map +1 -1
  62. package/dist/client/{pca-5wOBf_KI.mjs → pca-CiLPHmK7.mjs} +4 -4
  63. package/dist/client/{pca-5wOBf_KI.mjs.map → pca-CiLPHmK7.mjs.map} +1 -1
  64. package/dist/client/{pca-dhrOFfrE.mjs → pca-DUrQ_tIg.mjs} +2 -2
  65. package/dist/client/{pca-dhrOFfrE.mjs.map → pca-DUrQ_tIg.mjs.map} +1 -1
  66. package/dist/client/{projects-B_PPyZD1.mjs → projects-B5topuei.mjs} +2 -2
  67. package/dist/client/projects-B5topuei.mjs.map +1 -0
  68. package/dist/client/projects-CHYn7L5e.mjs.map +1 -1
  69. package/dist/client/projects-DNd3UTas.mjs +110 -0
  70. package/dist/client/projects-DNd3UTas.mjs.map +1 -0
  71. package/dist/client/projects-yiK0HGSA.mjs.map +1 -1
  72. package/dist/client/routeInfo-Dy9l-wFB.mjs +31 -0
  73. package/dist/client/routeInfo-Dy9l-wFB.mjs.map +1 -0
  74. package/dist/client/{securitygroups-CNFLu9zS.mjs → securitygroups-CcA2TpAt.mjs} +2 -2
  75. package/dist/client/{securitygroups-CNFLu9zS.mjs.map → securitygroups-CcA2TpAt.mjs.map} +1 -1
  76. package/dist/client/{useListWithFiltering-v2A0-SZb.mjs → useListWithFiltering-CVzhMyEA.mjs} +2 -2
  77. package/dist/client/{useListWithFiltering-v2A0-SZb.mjs.map → useListWithFiltering-CVzhMyEA.mjs.map} +1 -1
  78. package/dist/server/index.js +282 -48
  79. package/package.json +3 -3
  80. package/dist/client/ContentHeader-C51H95X8.mjs +0 -85
  81. package/dist/client/ContentHeader-C51H95X8.mjs.map +0 -1
  82. package/dist/client/SortInput-VK7IYqQv.mjs.map +0 -1
  83. package/dist/client/_floatingIpId-C2-BeRmF.mjs.map +0 -1
  84. package/dist/client/_projectId-C8BaEHUj.mjs +0 -273
  85. package/dist/client/_projectId-C8BaEHUj.mjs.map +0 -1
  86. package/dist/client/_projectId-COt93OEF.mjs +0 -84
  87. package/dist/client/_projectId-COt93OEF.mjs.map +0 -1
  88. package/dist/client/_storageType-B-qGcGUQ.mjs +0 -3244
  89. package/dist/client/_storageType-B-qGcGUQ.mjs.map +0 -1
  90. package/dist/client/images-BPnTuKFO2.mjs.map +0 -1
  91. package/dist/client/objects-DKWp9RtR.mjs.map +0 -1
  92. package/dist/client/objects-DaCuy_CB.mjs +0 -5708
  93. package/dist/client/objects-DaCuy_CB.mjs.map +0 -1
  94. package/dist/client/projects-B_PPyZD1.mjs.map +0 -1
  95. package/dist/client/projects-Dmewygrp.mjs +0 -105
  96. package/dist/client/projects-Dmewygrp.mjs.map +0 -1
  97. package/dist/client/routeInfo-CHiJfum5.mjs +0 -73
  98. package/dist/client/routeInfo-CHiJfum5.mjs.map +0 -1
package/README.md CHANGED
@@ -114,14 +114,15 @@ await server.listen({ host: "0.0.0.0", port: 4000 })
114
114
 
115
115
  ### `<AuroraApp />`
116
116
 
117
- | Prop | Type | Default | Description |
118
- | --------------- | ------------------------------- | ---------------- | ----------------------------------------------------------------------- |
119
- | `bffEndpoint` | `string` | `"/polaris-bff"` | Must match the server's `bffEndpoint` |
120
- | `theme` | `"theme-light" \| "theme-dark"` | `"theme-light"` | Initial theme |
121
- | `onThemeChange` | `(theme) => void` | — | Called when the user toggles the theme |
122
- | `appName` | `string` | `"Aurora"` | App name shown in the header breadcrumb and logo |
123
- | `slots` | `Slots` | — | Optional UI extension points — see [Slots](#slots) |
124
- | `onTrackEvent` | `OnTrackEventCallback` | — | Called on user interactions for analytics — see [Analytics](#analytics) |
117
+ | Prop | Type | Default | Description |
118
+ | ----------------- | ------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------- |
119
+ | `bffEndpoint` | `string` | `"/polaris-bff"` | Must match the server's `bffEndpoint` |
120
+ | `theme` | `"theme-light" \| "theme-dark"` | `"theme-light"` | Initial theme |
121
+ | `onThemeChange` | `(theme) => void` | — | Called when the user toggles the theme |
122
+ | `appName` | `string` | `"Aurora"` | App name shown in the header breadcrumb and logo |
123
+ | `slots` | `Slots` | — | Optional UI extension points — see [Slots](#slots) |
124
+ | `onTrackEvent` | `OnTrackEventCallback` | — | Called on user interactions for analytics — see [Analytics](#analytics) |
125
+ | `enabledServices` | `string[]` | — | Whitelist of service keys to show. When omitted, all services are shown — see [Enabled services](#enabled-services) |
125
126
 
126
127
  ## Slots
127
128
 
@@ -158,11 +159,20 @@ function MyFooter(_props: SlotProps) {
158
159
 
159
160
  ### Available slots
160
161
 
161
- | Slot | Location | Renders in shadow DOM |
162
- | --------------- | ----------------------------------------------- | --------------------- |
163
- | `logo` | Page header, replacing the default Aurora logo | No |
164
- | `sideNavBanner` | Bottom of the project side navigation | Yes |
165
- | `pageFooter` | Page footer, replacing the default empty footer | No |
162
+ | Slot | Location | `auroraContext` extras | Renders in shadow DOM |
163
+ | ----------------------- | ---------------------------------------------------------------------------- | ------------------------------ | --------------------- |
164
+ | `logo` | Page header, replacing the default Aurora logo | — | No |
165
+ | `sideNavBanner` | Bottom of the project side navigation | — | Yes |
166
+ | `pageFooter` | Page footer, replacing the default empty footer | | No |
167
+ | `login` | Replaces the default login form — use in OIDC environments | — | No |
168
+ | `serviceBadge` | Inline next to each service label in the side nav and project home cards | `currentService` (service key) | No |
169
+ | `servicePageActions` | Beside the page title in the service page header | `currentService` (service key) | No |
170
+ | `projectsBanner` | Below the "Projects" heading on the projects list page | — | No |
171
+ | `projectOverviewBanner` | Below the project description on the project overview page (`/projects/:id`) | — | No |
172
+
173
+ The `serviceBadge` and `servicePageActions` slots receive `auroraContext.currentService` — a string identifying which service is being rendered (e.g. `"images"`, `"ceph-containers"`). Return `null` from these slots to suppress rendering for specific services.
174
+
175
+ **Service key reference:** `"images"`, `"flavors"`, `"securitygroups"`, `"floatingips"`, `"containers"`, `"ceph-containers"`, `"pca"`
166
176
 
167
177
  **Shadow DOM isolation:** Slots rendered in a shadow DOM cannot inherit styles from the host page. If your slot component uses a CSS framework, inject the styles inline:
168
178
 
@@ -179,6 +189,30 @@ function MyBanner(_props: SlotProps) {
179
189
  }
180
190
  ```
181
191
 
192
+ ## Enabled services
193
+
194
+ By default all services available in the OpenStack catalog are shown in the side navigation and project home page. Pass `enabledServices` to restrict which services are visible:
195
+
196
+ ```tsx
197
+ <AuroraApp enabledServices={["ceph-containers", "securitygroups"]} />
198
+ ```
199
+
200
+ Only services whose key appears in the array will be shown. Services absent from the catalog are always hidden regardless of this list.
201
+
202
+ **Available service keys:** `"images"`, `"flavors"`, `"securitygroups"`, `"floatingips"`, `"containers"`, `"ceph-containers"`, `"pca"`
203
+
204
+ In a Vite-based consumer app you can drive this from an environment variable:
205
+
206
+ ```ts
207
+ // vite consumer
208
+ enabledServices={import.meta.env.VITE_ENABLED_SERVICES?.split(",").map(s => s.trim())}
209
+ ```
210
+
211
+ ```bash
212
+ # .env
213
+ VITE_ENABLED_SERVICES="ceph-containers,securitygroups"
214
+ ```
215
+
182
216
  ## Analytics
183
217
 
184
218
  Aurora provides an optional `onTrackEvent` callback to track user interactions and feature usage. The API is source-agnostic, supporting various event types like navigation, link clicks, modals, and more.
@@ -5,6 +5,8 @@ export type SlotProps = {
5
5
  auroraContext: {
6
6
  /** tRPC client for making API calls to the Aurora BFF. */
7
7
  client: TrpcClient;
8
+ /** Current service key of the slot component*/
9
+ currentService?: string;
8
10
  };
9
11
  };
10
12
  /** Named extension points where consumers can inject their own React components. */
@@ -15,6 +17,16 @@ export type Slots = {
15
17
  sideNavBanner?: FC<SlotProps>;
16
18
  /** Replaces the default page footer. Renders outside shadow DOM — inherits page styles. */
17
19
  pageFooter?: FC<SlotProps>;
20
+ /** Replaces the default login form. Use in OIDC environments where password-based login is not needed. Renders outside shadow DOM. */
21
+ login?: FC<SlotProps>;
22
+ /** Rendered inline next to each side navigation item label and service card. Receives `auroraContext.currentService` — return null to hide for that service. Renders outside shadow DOM. */
23
+ serviceBadge?: FC<SlotProps>;
24
+ /** Rendered in the service page header actions area, beside the page title. Use to inject custom actions like "View in Elektra". Renders outside shadow DOM. */
25
+ servicePageActions?: FC<SlotProps>;
26
+ /** Rendered below the "Projects" heading on the projects list page. Renders outside shadow DOM. */
27
+ projectsBanner?: FC<SlotProps>;
28
+ /** Rendered below the project description on the project overview page (/projects/$projectId). Renders outside shadow DOM. */
29
+ projectOverviewBanner?: FC<SlotProps>;
18
30
  };
19
31
  /**
20
32
  * Payload for analytics tracking events.
@@ -66,5 +78,7 @@ export type AuroraAppProps = {
66
78
  * ```
67
79
  */
68
80
  onTrackEvent?: OnTrackEventCallback;
81
+ /** Whitelist of service keys to show in the side nav and project home page. When omitted, all available services are shown. */
82
+ enabledServices?: string[];
69
83
  };
70
84
  export declare const AuroraApp: FC<AuroraAppProps>;
@@ -0,0 +1,96 @@
1
+ import { A as e, C as t, Q as n, Y as r, c as i, o as a, st as o } from "./build-BdRRmNf5.mjs";
2
+ import { t as s } from "./Slot-CWb612-_.mjs";
3
+ import { t as c } from "./routeInfo-Dy9l-wFB.mjs";
4
+ import { jsx as l, jsxs as u } from "react/jsx-runtime";
5
+ import { useState as d } from "react";
6
+ import { useMatches as f, useParams as p, useRouteContext as m } from "@tanstack/react-router";
7
+ import { Trans as h, useLingui as g } from "@lingui/react";
8
+ //#region src/client/components/ClipboardText.tsx
9
+ var _ = ({ text: n, tooltipContent: o, className: s, truncateAt: c, showTooltip: f = !0, ...p }) => {
10
+ let { i18n: m, _: h } = g(), [_, v] = d(!1), [y, b] = d(!1), x = `copyableTooltip inline-flex items-center ${s || ""}`, S = async (e) => {
11
+ e.preventDefault(), e.stopPropagation();
12
+ try {
13
+ await navigator.clipboard.writeText(n), v(!0), b(!1), setTimeout(() => v(!1), 2e3);
14
+ } catch (e) {
15
+ console.error("Failed to copy text:", e);
16
+ }
17
+ }, C = c && n.length > c ? `${n.slice(0, c)}...` : n, w = () => _ ? o || m._({ id: "u+VWhB" }) : m._({ id: "he3ygx" }), T = _ && f || y && f;
18
+ return /*#__PURE__*/ l("div", {
19
+ ...p,
20
+ className: x,
21
+ children: /*#__PURE__*/ u(e, {
22
+ open: T,
23
+ children: [/*#__PURE__*/ l(a, {
24
+ onClick: S,
25
+ onMouseEnter: () => !_ && b(!0),
26
+ onMouseLeave: () => b(!1),
27
+ "aria-label": m._({
28
+ id: "Wbg1jv",
29
+ values: { text: n }
30
+ }),
31
+ className: "cursor-pointer",
32
+ asChild: !0,
33
+ "data-testid": "clipboard-copy-trigger",
34
+ children: /*#__PURE__*/ l("div", {
35
+ className: "group",
36
+ children: /*#__PURE__*/ u(r, {
37
+ direction: "horizontal",
38
+ gap: "1",
39
+ className: "items-center hover:underline",
40
+ children: [/*#__PURE__*/ l("span", {
41
+ className: "select-none",
42
+ children: C
43
+ }), /*#__PURE__*/ l(i, {
44
+ icon: _ ? "check" : "contentCopy",
45
+ size: "18"
46
+ })]
47
+ })
48
+ })
49
+ }), /*#__PURE__*/ l(t, { children: w() })]
50
+ })
51
+ });
52
+ };
53
+ //#endregion
54
+ //#region src/client/components/ContentHeader/ContentHeader.tsx
55
+ function v({ title: e, projectId: t, description: r, actions: i }) {
56
+ let { slots: a } = m({ strict: !1 }), d = f(), { provider: g } = p({ strict: !1 }), v = [...d].reverse().find((e) => c(e.staticData)), y = v && c(v.staticData) ? v.staticData.service : void 0, b = y === "containers" && g === "ceph" ? "ceph-containers" : y, x = a?.servicePageActions ? /*#__PURE__*/ l(s, {
57
+ component: a.servicePageActions,
58
+ useShadowDOM: !1,
59
+ currentService: b
60
+ }) : null;
61
+ return /*#__PURE__*/ u("header", { children: [
62
+ /*#__PURE__*/ u("div", {
63
+ className: "flex flex-col sm:flex-row sm:items-center sm:justify-between",
64
+ children: [/*#__PURE__*/ u("div", {
65
+ className: "flex items-center gap-3",
66
+ children: [/*#__PURE__*/ l(n, { children: e }), x]
67
+ }), /*#__PURE__*/ u("div", {
68
+ className: "text-theme-light flex shrink-0 items-center gap-1 text-sm",
69
+ children: [/*#__PURE__*/ u("span", {
70
+ className: "font-semibold",
71
+ children: [
72
+ /*#__PURE__*/ l(h, { id: "mSfwLL" }),
73
+ ":",
74
+ " "
75
+ ]
76
+ }), /*#__PURE__*/ l(_, {
77
+ text: t,
78
+ truncateAt: 15
79
+ })]
80
+ })]
81
+ }),
82
+ r && /*#__PURE__*/ l("p", {
83
+ className: "text-sm font-normal",
84
+ children: r
85
+ }),
86
+ /*#__PURE__*/ l(o, { className: "mt-4" }),
87
+ i && /*#__PURE__*/ l("div", {
88
+ className: "mt-3 flex justify-end",
89
+ children: i
90
+ })
91
+ ] });
92
+ }
93
+ //#endregion
94
+ export { _ as n, v as t };
95
+
96
+ //# sourceMappingURL=ContentHeader-D4jlOG-9.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ContentHeader-D4jlOG-9.mjs","names":["React","useState","Tooltip","TooltipTrigger","TooltipContent","Icon","Stack","ClipboardText","text","tooltipContent","className","truncateAt","showTooltip","props","useLingui","copied","setCopied","isHovering","setIsHovering","combinedClassName","handleCopy","e","preventDefault","stopPropagation","navigator","clipboard","writeText","setTimeout","err","console","error","displayText","length","slice","getTooltipContent","t","tooltipIsOpen","div","open","onClick","onMouseEnter","onMouseLeave","aria-label","asChild","data-testid","direction","gap","span","icon","size","Divider","ContentHeading","useRouteContext","useMatches","useParams","ClipboardText","Slot","isRouteInfo","ContentHeader","title","projectId","description","actions","slots","strict","matches","provider","activeMatch","reverse","find","m","staticData","routeService","service","undefined","currentService","slotActions","servicePageActions","component","useShadowDOM","header","div","className","span","text","truncateAt","p"],"sources":["../../src/client/components/ClipboardText.tsx","../../src/client/components/ContentHeader/ContentHeader.tsx"],"sourcesContent":["import React, { useState } from \"react\"\nimport { Tooltip, TooltipTrigger, TooltipContent, Icon, Stack } from \"@cloudoperators/juno-ui-components\"\nimport { useLingui } from \"@lingui/react/macro\"\n\nexport interface ClipboardTextProps extends React.HTMLAttributes<HTMLDivElement> {\n tooltipContent?: string\n text: string\n className?: string\n truncateAt?: number\n showTooltip?: boolean\n}\n\nconst ClipboardText: React.FC<ClipboardTextProps> = ({\n text,\n tooltipContent,\n className,\n truncateAt,\n showTooltip = true,\n ...props\n}) => {\n const { t } = useLingui()\n const [copied, setCopied] = useState(false)\n const [isHovering, setIsHovering] = useState(false)\n\n const combinedClassName = `copyableTooltip inline-flex items-center ${className || \"\"}`\n\n const handleCopy = async (e: React.MouseEvent) => {\n e.preventDefault()\n e.stopPropagation()\n\n try {\n await navigator.clipboard.writeText(text)\n setCopied(true)\n setIsHovering(false)\n setTimeout(() => setCopied(false), 2000)\n } catch (err) {\n console.error(\"Failed to copy text:\", err)\n }\n }\n\n const displayText = truncateAt && text.length > truncateAt ? `${text.slice(0, truncateAt)}...` : text\n\n // Determine tooltip content based on state\n const getTooltipContent = () => {\n if (copied) {\n return tooltipContent || t`Copied to clipboard!`\n }\n return t`Copy`\n }\n\n const tooltipIsOpen = (copied && showTooltip) || (isHovering && showTooltip)\n\n return (\n <div {...props} className={combinedClassName}>\n <Tooltip open={tooltipIsOpen}>\n <TooltipTrigger\n onClick={handleCopy}\n onMouseEnter={() => !copied && setIsHovering(true)}\n onMouseLeave={() => setIsHovering(false)}\n aria-label={t`Copy ${text} to clipboard`}\n className=\"cursor-pointer\"\n asChild\n data-testid=\"clipboard-copy-trigger\"\n >\n <div className=\"group\">\n <Stack direction=\"horizontal\" gap=\"1\" className=\"items-center hover:underline\">\n <span className=\"select-none\">{displayText}</span>\n <Icon icon={copied ? \"check\" : \"contentCopy\"} size=\"18\" />\n </Stack>\n </div>\n </TooltipTrigger>\n <TooltipContent>{getTooltipContent()}</TooltipContent>\n </Tooltip>\n </div>\n )\n}\n\nexport default ClipboardText\n","import type { ReactNode } from \"react\"\nimport { Divider, ContentHeading } from \"@cloudoperators/juno-ui-components\"\nimport { Trans } from \"@lingui/react/macro\"\nimport { useRouteContext, useMatches, useParams } from \"@tanstack/react-router\"\nimport ClipboardText from \"../ClipboardText\"\nimport { Slot } from \"../Slot\"\nimport { isRouteInfo } from \"@/client/routes/routeInfo\"\n\ninterface ContentHeaderProps {\n title: string\n projectId: string\n description?: string | null\n actions?: ReactNode\n}\n\nexport function ContentHeader({ title, projectId, description, actions }: ContentHeaderProps) {\n const { slots } = useRouteContext({ strict: false })\n const matches = useMatches()\n const { provider } = useParams({ strict: false }) as { provider?: string }\n\n const activeMatch = [...matches].reverse().find((m) => isRouteInfo(m.staticData))\n const routeService = activeMatch && isRouteInfo(activeMatch.staticData) ? activeMatch.staticData.service : undefined\n\n // Storage routes share service: \"containers\" for both Swift and Ceph.\n // Distinguish them by the $provider param.\n const currentService = routeService === \"containers\" && provider === \"ceph\" ? \"ceph-containers\" : routeService\n\n const slotActions = slots?.servicePageActions ? (\n <Slot component={slots.servicePageActions} useShadowDOM={false} currentService={currentService} />\n ) : null\n\n return (\n <header>\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-3\">\n <ContentHeading>{title}</ContentHeading>\n {slotActions}\n </div>\n <div className=\"text-theme-light flex shrink-0 items-center gap-1 text-sm\">\n <span className=\"font-semibold\">\n <Trans>Project ID</Trans>:{\" \"}\n </span>\n <ClipboardText text={projectId} truncateAt={15} />\n </div>\n </div>\n {description && <p className=\"text-sm font-normal\">{description}</p>}\n <Divider className=\"mt-4\" />\n {actions && <div className=\"mt-3 flex justify-end\">{actions}</div>}\n </header>\n )\n}\n"],"mappings":"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"}
@@ -0,0 +1,331 @@
1
+ import { H as e, J as t, Y as n, _ as r, ct as i, f as a, it as o, lt as s, m as c } from "./build-BdRRmNf5.mjs";
2
+ import { r as l } from "./trpcClient-BzPUgiM2.mjs";
3
+ import { t as u } from "./useProjectId-DBc5lpoU.mjs";
4
+ import { Fragment as d, jsx as f, jsxs as p } from "react/jsx-runtime";
5
+ import { useEffect as m, useState as h } from "react";
6
+ import { Trans as g, useLingui as _ } from "@lingui/react";
7
+ //#region src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/DeleteBucketPolicyModal.tsx
8
+ var v = ({ isOpen: r, bucketName: a, onClose: o, onSuccess: s, onError: c }) => {
9
+ let { i18n: h, _: v } = _(), y = u(), b = l.useUtils(), { data: x, isLoading: S, error: C } = l.storage.ceph.bucketPolicy.get.useQuery({
10
+ project_id: y,
11
+ bucketName: a
12
+ }, {
13
+ enabled: r && !!y,
14
+ retry: !1
15
+ }), w = l.storage.ceph.bucketPolicy.delete.useMutation({
16
+ onSuccess: () => {
17
+ b.storage.ceph.bucketPolicy.get.invalidate(), s?.(a), T();
18
+ },
19
+ onError: (e) => {
20
+ c?.(a, e.message);
21
+ }
22
+ });
23
+ m(() => {
24
+ r || w.reset();
25
+ }, [r, a]);
26
+ let T = () => {
27
+ w.reset(), o();
28
+ }, E = () => {
29
+ w.mutate({
30
+ project_id: y,
31
+ bucketName: a
32
+ });
33
+ };
34
+ if (!r) return null;
35
+ let D = !!x?.policy, O = w.isPending;
36
+ return /*#__PURE__*/ f(i, {
37
+ title: h._({ id: "On9AgT" }),
38
+ open: r,
39
+ onCancel: T,
40
+ confirmButtonLabel: h._({ id: "VMoZmg" }),
41
+ confirmButtonVariant: "primary-danger",
42
+ onConfirm: E,
43
+ cancelButtonLabel: h._({ id: "dEgA5A" }),
44
+ size: "small",
45
+ disableConfirmButton: O || S || !D || !!C,
46
+ children: /*#__PURE__*/ p(n, {
47
+ direction: "vertical",
48
+ gap: "4",
49
+ children: [
50
+ S && /*#__PURE__*/ f("div", {
51
+ className: "flex items-center justify-center py-4",
52
+ children: /*#__PURE__*/ f(e, {
53
+ variant: "primary",
54
+ size: "large"
55
+ })
56
+ }),
57
+ C && /*#__PURE__*/ f(t, {
58
+ variant: "error",
59
+ title: h._({ id: "XicAqm" }),
60
+ children: C.message
61
+ }),
62
+ !S && !C && !D && /*#__PURE__*/ f(t, {
63
+ variant: "warning",
64
+ title: h._({ id: "GZjazt" }),
65
+ children: /*#__PURE__*/ f(g, { id: "/HejCc" })
66
+ }),
67
+ !S && !C && D && /*#__PURE__*/ p(d, { children: [/*#__PURE__*/ f("p", {
68
+ className: "text-theme-default",
69
+ children: /*#__PURE__*/ f(g, {
70
+ id: "QDUXIq",
71
+ values: { bucketName: a },
72
+ components: { 0: /*#__PURE__*/ f("strong", {}) }
73
+ })
74
+ }), /*#__PURE__*/ f("p", {
75
+ className: "text-theme-default",
76
+ children: /*#__PURE__*/ f(g, { id: "Rsh494" })
77
+ })] }),
78
+ w.isError && /*#__PURE__*/ f(t, {
79
+ variant: "error",
80
+ title: h._({ id: "+Pe7SS" }),
81
+ children: w.error?.message
82
+ })
83
+ ]
84
+ })
85
+ });
86
+ }, y = ({ isOpen: e, bucket: t, onClose: r, onSuccess: a, onError: s }) => {
87
+ let { i18n: d, _: m } = _(), v = u(), [y, b] = h(""), [x, S] = h(null), [C, w] = h(!1), T = l.useUtils(), E = l.storage.ceph.objects.deleteAll.useMutation({ onSettled: () => {
88
+ T.storage.ceph.containers.list.invalidate(), T.storage.ceph.objects.list.invalidate(), D();
89
+ } }), D = () => {
90
+ b(""), S(null), w(!1), E.reset(), r();
91
+ }, O = (e) => {
92
+ let t = e.target.value;
93
+ b(t), x && S(null);
94
+ }, k = () => {
95
+ if (!t) return;
96
+ if (y.trim() !== t.name) {
97
+ S(d._({ id: "wxVsr5" }));
98
+ return;
99
+ }
100
+ let e = t.name;
101
+ E.mutate({
102
+ project_id: v,
103
+ containerName: e,
104
+ includeVersionsAndDeleteMarkers: C
105
+ }, {
106
+ onSuccess: (t) => {
107
+ a?.(e, t);
108
+ },
109
+ onError: (t) => {
110
+ s?.(e, t.message);
111
+ }
112
+ });
113
+ }, A = (e) => {
114
+ e.key === "Enter" && k();
115
+ };
116
+ if (!e || !t) return null;
117
+ let j = t.name;
118
+ return /*#__PURE__*/ f(i, {
119
+ title: d._({ id: "CfKRC1" }),
120
+ open: e,
121
+ onCancel: D,
122
+ confirmButtonLabel: d._({ id: "CfKRC1" }),
123
+ confirmButtonVariant: "primary-danger",
124
+ onConfirm: k,
125
+ cancelButtonLabel: d._({ id: "dEgA5A" }),
126
+ size: "small",
127
+ disableConfirmButton: E.isPending || y.trim() !== t.name,
128
+ children: /*#__PURE__*/ p(n, {
129
+ direction: "vertical",
130
+ gap: "6",
131
+ children: [
132
+ /*#__PURE__*/ f("p", {
133
+ className: "text-theme-default m-0",
134
+ children: /*#__PURE__*/ f(g, {
135
+ id: "i9I/nL",
136
+ values: { bucketName: j }
137
+ })
138
+ }),
139
+ /*#__PURE__*/ f(o, {
140
+ checked: C,
141
+ onChange: (e) => w(e.target.checked),
142
+ label: d._({ id: "mbmb/h" }),
143
+ disabled: E.isPending
144
+ }),
145
+ /*#__PURE__*/ f(c, {
146
+ label: d._({ id: "dEHa3L" }),
147
+ required: !0,
148
+ value: y,
149
+ onChange: O,
150
+ onKeyDown: A,
151
+ invalid: !!x,
152
+ errortext: x || void 0,
153
+ disabled: E.isPending,
154
+ placeholder: t.name,
155
+ autoFocus: !0
156
+ })
157
+ ]
158
+ })
159
+ });
160
+ }, b = ({ isOpen: t, bucket: o, onClose: v, onSuccess: y, onError: b }) => {
161
+ let { i18n: x, _: S } = _(), C = u(), [w, T] = h(""), [E, D] = h(null), O = l.useUtils(), { data: k, isLoading: A, error: j } = l.storage.ceph.objects.list.useQuery({
162
+ project_id: C ?? "",
163
+ containerName: o?.name ?? "",
164
+ maxKeys: 1,
165
+ delimiter: "",
166
+ showVersions: !0
167
+ }, { enabled: t && o !== null }), M = l.storage.ceph.containers.delete.useMutation({ onSettled: () => {
168
+ O.storage.ceph.containers.list.invalidate();
169
+ } });
170
+ m(() => {
171
+ t || (T(""), D(null), M.reset());
172
+ }, [t, o?.name]);
173
+ let N = () => {
174
+ T(""), D(null), M.reset(), v();
175
+ }, P = (e) => {
176
+ let t = e.target.value;
177
+ T(t), E && D(null);
178
+ }, F = () => {
179
+ if (!o || j) return;
180
+ if (w.trim() !== o.name) {
181
+ D(x._({ id: "wxVsr5" }));
182
+ return;
183
+ }
184
+ let e = o.name;
185
+ M.mutate({
186
+ project_id: C,
187
+ bucketName: e
188
+ }, {
189
+ onSuccess: () => {
190
+ N(), y?.(e);
191
+ },
192
+ onError: (t) => {
193
+ N(), b?.(e, t.message);
194
+ }
195
+ });
196
+ }, I = (e) => {
197
+ e.key === "Enter" && F();
198
+ };
199
+ if (!t || !o) return null;
200
+ let L = k?.objects?.length ?? 0, R = k?.versions?.length ?? 0, z = L > 0, B = L === 0 && R > 0, V = j?.message;
201
+ return /*#__PURE__*/ f(i, {
202
+ title: x._({ id: "IwlPLb" }),
203
+ open: t,
204
+ onCancel: N,
205
+ confirmButtonLabel: z || B ? void 0 : x._({ id: "IwlPLb" }),
206
+ confirmButtonVariant: z || B ? void 0 : "primary-danger",
207
+ onConfirm: z || B ? void 0 : F,
208
+ cancelButtonLabel: z || B ? void 0 : x._({ id: "dEgA5A" }),
209
+ modalFooter: z || B ? /*#__PURE__*/ f(a, {
210
+ className: "flex justify-end",
211
+ children: /*#__PURE__*/ f(s, { children: /*#__PURE__*/ f(r, {
212
+ variant: "primary",
213
+ onClick: N,
214
+ "data-testid": "delete-has-objects-close-button",
215
+ children: /*#__PURE__*/ f(g, { id: "yz7wBu" })
216
+ }) })
217
+ }) : void 0,
218
+ size: "small",
219
+ disableConfirmButton: M.isPending || A || !!j || w.trim() !== o.name,
220
+ children: /*#__PURE__*/ p(n, {
221
+ direction: "vertical",
222
+ gap: "6",
223
+ children: [j && /*#__PURE__*/ f("p", {
224
+ className: "text-theme-error",
225
+ role: "alert",
226
+ "aria-live": "assertive",
227
+ children: /*#__PURE__*/ f(g, {
228
+ id: "MJLqeY",
229
+ values: { errorMessage: V }
230
+ })
231
+ }), A ? /*#__PURE__*/ p(n, {
232
+ direction: "horizontal",
233
+ gap: "2",
234
+ alignment: "center",
235
+ children: [/*#__PURE__*/ f(e, {}), /*#__PURE__*/ f("span", {
236
+ className: "text-juno-grey-light-1 text-sm",
237
+ children: /*#__PURE__*/ f(g, { id: "rPuPb+" })
238
+ })]
239
+ }) : z ? /*#__PURE__*/ f("p", {
240
+ className: "text-theme-default",
241
+ children: /*#__PURE__*/ f(g, {
242
+ id: "EpRlc1",
243
+ components: { 0: /*#__PURE__*/ f("strong", {}) }
244
+ })
245
+ }) : B ? /*#__PURE__*/ f("p", {
246
+ className: "text-theme-default",
247
+ children: /*#__PURE__*/ f(g, {
248
+ id: "QEfrEi",
249
+ components: { 0: /*#__PURE__*/ f("strong", {}) }
250
+ })
251
+ }) : /*#__PURE__*/ p(d, { children: [/*#__PURE__*/ f("p", {
252
+ className: "text-theme-default",
253
+ children: /*#__PURE__*/ f(g, { id: "/pOQrn" })
254
+ }), /*#__PURE__*/ f(c, {
255
+ label: x._({ id: "dEHa3L" }),
256
+ required: !0,
257
+ value: w,
258
+ onChange: P,
259
+ onKeyDown: I,
260
+ invalid: !!E,
261
+ errortext: E || void 0,
262
+ disabled: M.isPending,
263
+ placeholder: o.name
264
+ })] })]
265
+ })
266
+ });
267
+ }, x = ({ isOpen: e, bucket: t, onClose: r, onSuccess: a, onError: o }) => {
268
+ let { i18n: s, _: d } = _(), m = u(), [v, y] = h(""), [b, x] = h(null), S = l.useUtils(), C = l.storage.ceph.objects.deleteAll.useMutation({ onSettled: () => {
269
+ S.storage.ceph.containers.list.invalidate(), S.storage.ceph.objects.list.invalidate(), w();
270
+ } }), w = () => {
271
+ y(""), x(null), C.reset(), r();
272
+ }, T = (e) => {
273
+ let t = e.target.value;
274
+ y(t), b && x(null);
275
+ }, E = () => {
276
+ if (!t) return;
277
+ if (v.trim() !== t.name) {
278
+ x(s._({ id: "wxVsr5" }));
279
+ return;
280
+ }
281
+ let e = t.name;
282
+ C.mutate({
283
+ project_id: m,
284
+ containerName: e,
285
+ includeVersionsAndDeleteMarkers: !0
286
+ }, {
287
+ onSuccess: (t) => {
288
+ a?.(e, t);
289
+ },
290
+ onError: (t) => {
291
+ o?.(e, t.message);
292
+ }
293
+ });
294
+ };
295
+ return !e || !t ? null : /*#__PURE__*/ f(i, {
296
+ title: s._({ id: "1mFMeP" }),
297
+ open: e,
298
+ onCancel: w,
299
+ confirmButtonLabel: s._({ id: "1mFMeP" }),
300
+ confirmButtonVariant: "primary-danger",
301
+ onConfirm: E,
302
+ cancelButtonLabel: s._({ id: "dEgA5A" }),
303
+ size: "small",
304
+ disableConfirmButton: C.isPending || v.trim() !== t.name,
305
+ children: /*#__PURE__*/ p(n, {
306
+ direction: "vertical",
307
+ gap: "6",
308
+ children: [/*#__PURE__*/ f("p", {
309
+ className: "text-theme-default m-0",
310
+ children: /*#__PURE__*/ f(g, { id: "T3+VKR" })
311
+ }), /*#__PURE__*/ f(c, {
312
+ label: s._({ id: "dEHa3L" }),
313
+ required: !0,
314
+ value: v,
315
+ onChange: T,
316
+ onKeyDown: (e) => {
317
+ e.key === "Enter" && E();
318
+ },
319
+ invalid: !!b,
320
+ errortext: b || void 0,
321
+ disabled: C.isPending,
322
+ placeholder: t.name,
323
+ autoFocus: !0
324
+ })]
325
+ })
326
+ });
327
+ };
328
+ //#endregion
329
+ export { v as i, b as n, y as r, x as t };
330
+
331
+ //# sourceMappingURL=DeleteVersionsModal-CyYZfB8d.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DeleteVersionsModal-CyYZfB8d.mjs","names":["useEffect","trpcReact","Modal","Stack","Spinner","Message","useProjectId","DeleteBucketPolicyModal","isOpen","bucketName","onClose","onSuccess","onError","useLingui","projectId","utils","useUtils","data","policyData","isLoading","isPolicyLoading","error","policyError","storage","ceph","bucketPolicy","get","useQuery","project_id","enabled","retry","deleteMutation","delete","useMutation","invalidate","name","handleClose","message","reset","handleDelete","mutate","hasPolicy","policy","isDeleting","isPending","title","t","open","onCancel","confirmButtonLabel","confirmButtonVariant","onConfirm","cancelButtonLabel","size","disableConfirmButton","direction","gap","div","className","variant","p","strong","isError","useState","trpcReact","Modal","TextInput","Stack","Checkbox","useProjectId","EmptyBucketModal","isOpen","bucket","onClose","onSuccess","onError","useLingui","projectId","confirmName","setConfirmName","nameError","setNameError","deleteVersionsAndMarkers","setDeleteVersionsAndMarkers","utils","useUtils","emptyBucketMutation","storage","ceph","objects","deleteAll","useMutation","onSettled","containers","list","invalidate","handleClose","reset","handleConfirmNameChange","e","value","target","handleSubmit","trim","name","t","bucketName","mutate","project_id","containerName","includeVersionsAndDeleteMarkers","deletedCount","error","message","handleKeyDown","key","title","open","onCancel","confirmButtonLabel","confirmButtonVariant","onConfirm","cancelButtonLabel","size","disableConfirmButton","isPending","direction","gap","p","className","checked","onChange","label","disabled","required","onKeyDown","invalid","errortext","undefined","placeholder","autoFocus","useState","useEffect","trpcReact","Modal","ModalFooter","ButtonRow","TextInput","Stack","Spinner","Button","useProjectId","DeleteBucketModal","isOpen","bucket","onClose","onSuccess","onError","useLingui","projectId","confirmName","setConfirmName","nameError","setNameError","utils","useUtils","data","objects","isLoading","isLoadingObjects","error","objectsError","storage","ceph","list","useQuery","project_id","containerName","name","maxKeys","delimiter","showVersions","enabled","deleteBucketMutation","containers","delete","useMutation","onSettled","invalidate","reset","handleClose","handleConfirmNameChange","e","value","target","handleSubmit","trim","t","bucketName","mutate","message","handleKeyDown","key","currentObjectCount","length","versionCount","versions","isNonEmpty","hasOnlyVersions","errorMessage","title","open","onCancel","confirmButtonLabel","undefined","confirmButtonVariant","onConfirm","cancelButtonLabel","modalFooter","className","variant","onClick","data-testid","size","disableConfirmButton","isPending","direction","gap","p","role","aria-live","alignment","span","strong","label","required","onChange","onKeyDown","invalid","errortext","disabled","placeholder","useState","trpcReact","Modal","TextInput","Stack","useProjectId","DeleteVersionsModal","isOpen","bucket","onClose","onSuccess","onError","useLingui","projectId","confirmName","setConfirmName","nameError","setNameError","utils","useUtils","deleteVersionsMutation","storage","ceph","objects","deleteAll","useMutation","onSettled","containers","list","invalidate","handleClose","reset","handleConfirmNameChange","e","value","target","handleSubmit","trim","name","t","bucketName","mutate","project_id","containerName","includeVersionsAndDeleteMarkers","deletedCount","error","message","handleKeyDown","key","title","open","onCancel","confirmButtonLabel","confirmButtonVariant","onConfirm","cancelButtonLabel","size","disableConfirmButton","isPending","direction","gap","p","className","label","required","onChange","onKeyDown","invalid","errortext","undefined","disabled","placeholder","autoFocus"],"sources":["../../src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/DeleteBucketPolicyModal.tsx","../../src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/EmptyBucketModal.tsx","../../src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/DeleteBucketModal.tsx","../../src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/DeleteVersionsModal.tsx"],"sourcesContent":["import { useEffect } from \"react\"\nimport { Trans, useLingui } from \"@lingui/react/macro\"\nimport { trpcReact } from \"@/client/trpcClient\"\nimport { Modal, Stack, Spinner, Message } from \"@cloudoperators/juno-ui-components\"\nimport { useProjectId } from \"@/client/hooks/useProjectId\"\n\ninterface DeleteBucketPolicyModalProps {\n isOpen: boolean\n bucketName: string\n onClose: () => void\n onSuccess?: (bucketName: string) => void\n onError?: (bucketName: string, errorMessage: string) => void\n}\n\nexport const DeleteBucketPolicyModal = ({\n isOpen,\n bucketName,\n onClose,\n onSuccess,\n onError,\n}: DeleteBucketPolicyModalProps) => {\n const { t } = useLingui()\n const projectId = useProjectId()\n const utils = trpcReact.useUtils()\n\n // Query to verify policy exists\n const {\n data: policyData,\n isLoading: isPolicyLoading,\n error: policyError,\n } = trpcReact.storage.ceph.bucketPolicy.get.useQuery(\n {\n project_id: projectId,\n bucketName,\n },\n {\n enabled: isOpen && !!projectId,\n retry: false,\n }\n )\n\n // Delete mutation\n const deleteMutation = trpcReact.storage.ceph.bucketPolicy.delete.useMutation({\n onSuccess: () => {\n utils.storage.ceph.bucketPolicy.get.invalidate()\n const name = bucketName\n onSuccess?.(name)\n handleClose()\n },\n onError: (error) => {\n const name = bucketName\n onError?.(name, error.message)\n },\n })\n\n useEffect(() => {\n if (!isOpen) {\n deleteMutation.reset()\n }\n }, [isOpen, bucketName])\n\n const handleClose = () => {\n deleteMutation.reset()\n onClose()\n }\n\n const handleDelete = () => {\n deleteMutation.mutate({\n project_id: projectId,\n bucketName,\n })\n }\n\n if (!isOpen) return null\n\n const hasPolicy = !!policyData?.policy\n const isDeleting = deleteMutation.isPending\n\n return (\n <Modal\n title={t`Delete Bucket Policy`}\n open={isOpen}\n onCancel={handleClose}\n confirmButtonLabel={t`Delete Policy`}\n confirmButtonVariant=\"primary-danger\"\n onConfirm={handleDelete}\n cancelButtonLabel={t`Cancel`}\n size=\"small\"\n disableConfirmButton={isDeleting || isPolicyLoading || !hasPolicy || !!policyError}\n >\n <Stack direction=\"vertical\" gap=\"4\">\n {isPolicyLoading && (\n <div className=\"flex items-center justify-center py-4\">\n <Spinner variant=\"primary\" size=\"large\" />\n </div>\n )}\n\n {policyError && (\n <Message variant=\"error\" title={t`Failed to load policy`}>\n {policyError.message}\n </Message>\n )}\n\n {!isPolicyLoading && !policyError && !hasPolicy && (\n <Message variant=\"warning\" title={t`No policy found`}>\n <Trans>This bucket does not have a policy attached.</Trans>\n </Message>\n )}\n\n {!isPolicyLoading && !policyError && hasPolicy && (\n <>\n <p className=\"text-theme-default\">\n <Trans>\n Are you sure you want to delete the policy for bucket <strong>{bucketName}</strong>?\n </Trans>\n </p>\n <p className=\"text-theme-default\">\n <Trans>\n Deleting the policy will remove all access restrictions defined by it. This action cannot be undone.\n </Trans>\n </p>\n </>\n )}\n\n {deleteMutation.isError && (\n <Message variant=\"error\" title={t`Failed to delete policy`}>\n {deleteMutation.error?.message}\n </Message>\n )}\n </Stack>\n </Modal>\n )\n}\n","import { useState } from \"react\"\nimport { Trans, useLingui } from \"@lingui/react/macro\"\nimport { trpcReact } from \"@/client/trpcClient\"\nimport { Modal, TextInput, Stack, Checkbox } from \"@cloudoperators/juno-ui-components\"\nimport { Bucket } from \"@/server/Storage/types/ceph\"\nimport { useProjectId } from \"@/client/hooks/useProjectId\"\n\ninterface EmptyBucketModalProps {\n isOpen: boolean\n bucket: Bucket | null\n onClose: () => void\n onSuccess?: (bucketName: string, deletedCount: number) => void\n onError?: (bucketName: string, errorMessage: string) => void\n}\n\nexport const EmptyBucketModal = ({ isOpen, bucket, onClose, onSuccess, onError }: EmptyBucketModalProps) => {\n const { t } = useLingui()\n const projectId = useProjectId()\n const [confirmName, setConfirmName] = useState(\"\")\n const [nameError, setNameError] = useState<string | null>(null)\n const [deleteVersionsAndMarkers, setDeleteVersionsAndMarkers] = useState(false)\n\n const utils = trpcReact.useUtils()\n\n const emptyBucketMutation = trpcReact.storage.ceph.objects.deleteAll.useMutation({\n onSettled: () => {\n // Invalidate both containers.list (to update bucket metadata) and objects.list (to refresh empty state)\n utils.storage.ceph.containers.list.invalidate()\n utils.storage.ceph.objects.list.invalidate()\n handleClose()\n },\n })\n\n const handleClose = () => {\n setConfirmName(\"\")\n setNameError(null)\n setDeleteVersionsAndMarkers(false)\n emptyBucketMutation.reset()\n onClose()\n }\n\n const handleConfirmNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const value = e.target.value\n setConfirmName(value)\n if (nameError) setNameError(null)\n }\n\n const handleSubmit = () => {\n if (!bucket) return\n if (confirmName.trim() !== bucket.name) {\n setNameError(t`Bucket name does not match`)\n return\n }\n\n // Capture bucket name before async operation to avoid dereferencing null bucket in callbacks\n const bucketName = bucket.name\n\n emptyBucketMutation.mutate(\n {\n project_id: projectId,\n containerName: bucketName,\n includeVersionsAndDeleteMarkers: deleteVersionsAndMarkers,\n },\n {\n onSuccess: (deletedCount) => {\n onSuccess?.(bucketName, deletedCount)\n },\n onError: (error) => {\n onError?.(bucketName, error.message)\n },\n }\n )\n }\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === \"Enter\") handleSubmit()\n }\n\n if (!isOpen || !bucket) return null\n\n const bucketName = bucket.name\n\n return (\n <Modal\n title={t`Empty Bucket`}\n open={isOpen}\n onCancel={handleClose}\n confirmButtonLabel={t`Empty Bucket`}\n confirmButtonVariant=\"primary-danger\"\n onConfirm={handleSubmit}\n cancelButtonLabel={t`Cancel`}\n size=\"small\"\n disableConfirmButton={emptyBucketMutation.isPending || confirmName.trim() !== bucket.name}\n >\n <Stack direction=\"vertical\" gap=\"6\">\n <p className=\"text-theme-default m-0\">\n <Trans>\n This action will permanently delete all objects from {bucketName}. You may choose to also delete all\n versions and delete markers. This will enable you to delete the bucket. This action cannot be undone.\n </Trans>\n </p>\n\n <Checkbox\n checked={deleteVersionsAndMarkers}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => setDeleteVersionsAndMarkers(e.target.checked)}\n label={t`Also delete all versions and all delete markers`}\n disabled={emptyBucketMutation.isPending}\n />\n\n <TextInput\n label={t`Type the bucket name to confirm`}\n required\n value={confirmName}\n onChange={handleConfirmNameChange}\n onKeyDown={handleKeyDown}\n invalid={!!nameError}\n errortext={nameError || undefined}\n disabled={emptyBucketMutation.isPending}\n placeholder={bucket.name}\n autoFocus\n />\n </Stack>\n </Modal>\n )\n}\n","import { useState, useEffect } from \"react\"\nimport { Trans, useLingui } from \"@lingui/react/macro\"\nimport { trpcReact } from \"@/client/trpcClient\"\nimport { Modal, ModalFooter, ButtonRow, TextInput, Stack, Spinner, Button } from \"@cloudoperators/juno-ui-components\"\nimport type { Bucket } from \"@/server/Storage/types/ceph\"\nimport { useProjectId } from \"@/client/hooks/useProjectId\"\n\ninterface DeleteBucketModalProps {\n isOpen: boolean\n bucket: Bucket | null\n onClose: () => void\n onSuccess?: (bucketName: string) => void\n onError?: (bucketName: string, errorMessage: string) => void\n}\n\nexport const DeleteBucketModal = ({ isOpen, bucket, onClose, onSuccess, onError }: DeleteBucketModalProps) => {\n const { t } = useLingui()\n const projectId = useProjectId()\n const [confirmName, setConfirmName] = useState(\"\")\n const [nameError, setNameError] = useState<string | null>(null)\n\n const utils = trpcReact.useUtils()\n\n // Fetch actual objects and versions to get accurate real-time state\n // Use showVersions=true to also detect delete markers in versioned buckets\n // Use delimiter=\"\" to get ALL objects including folder markers (zero-byte objects ending in \"/\")\n // Without this, folders are returned as CommonPrefixes and we can't accurately check if bucket is empty\n const {\n data: objects,\n isLoading: isLoadingObjects,\n error: objectsError,\n } = trpcReact.storage.ceph.objects.list.useQuery(\n { project_id: projectId ?? \"\", containerName: bucket?.name ?? \"\", maxKeys: 1, delimiter: \"\", showVersions: true },\n { enabled: isOpen && bucket !== null }\n )\n\n const deleteBucketMutation = trpcReact.storage.ceph.containers.delete.useMutation({\n onSettled: () => {\n utils.storage.ceph.containers.list.invalidate()\n },\n })\n\n useEffect(() => {\n if (!isOpen) {\n setConfirmName(\"\")\n setNameError(null)\n deleteBucketMutation.reset()\n }\n }, [isOpen, bucket?.name])\n\n const handleClose = () => {\n setConfirmName(\"\")\n setNameError(null)\n deleteBucketMutation.reset()\n onClose()\n }\n\n const handleConfirmNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const value = e.target.value\n setConfirmName(value)\n if (nameError) setNameError(null)\n }\n\n const handleSubmit = () => {\n if (!bucket) return\n if (objectsError) return\n if (confirmName.trim() !== bucket.name) {\n setNameError(t`Bucket name does not match`)\n return\n }\n\n // Capture bucket name before async operation to avoid dereferencing null bucket in callbacks\n const bucketName = bucket.name\n\n deleteBucketMutation.mutate(\n { project_id: projectId, bucketName },\n {\n onSuccess: () => {\n handleClose()\n onSuccess?.(bucketName)\n },\n onError: (error) => {\n handleClose()\n onError?.(bucketName, error.message)\n },\n }\n )\n }\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === \"Enter\") handleSubmit()\n }\n\n if (!isOpen || !bucket) return null\n\n // Check if bucket has current objects (not just versions/delete markers)\n // When delimiter=\"\", folders are returned as objects (keys ending in \"/\")\n const currentObjectCount = objects?.objects?.length ?? 0\n const versionCount = objects?.versions?.length ?? 0\n\n // Bucket is non-empty if it has current objects\n // If it only has versions/delete markers (no current objects), it's considered \"empty\" for deletion purposes\n const isNonEmpty = currentObjectCount > 0\n const hasOnlyVersions = currentObjectCount === 0 && versionCount > 0\n const errorMessage = objectsError?.message\n\n return (\n <Modal\n title={t`Delete Bucket`}\n open={isOpen}\n onCancel={handleClose}\n confirmButtonLabel={isNonEmpty || hasOnlyVersions ? undefined : t`Delete Bucket`}\n confirmButtonVariant={isNonEmpty || hasOnlyVersions ? undefined : \"primary-danger\"}\n onConfirm={isNonEmpty || hasOnlyVersions ? undefined : handleSubmit}\n cancelButtonLabel={isNonEmpty || hasOnlyVersions ? undefined : t`Cancel`}\n modalFooter={\n isNonEmpty || hasOnlyVersions ? (\n <ModalFooter className=\"flex justify-end\">\n <ButtonRow>\n <Button variant=\"primary\" onClick={handleClose} data-testid=\"delete-has-objects-close-button\">\n <Trans>Close</Trans>\n </Button>\n </ButtonRow>\n </ModalFooter>\n ) : undefined\n }\n size=\"small\"\n disableConfirmButton={\n deleteBucketMutation.isPending || isLoadingObjects || !!objectsError || confirmName.trim() !== bucket.name\n }\n >\n <Stack direction=\"vertical\" gap=\"6\">\n {objectsError && (\n <p className=\"text-theme-error\" role=\"alert\" aria-live=\"assertive\">\n <Trans>Failed to check bucket contents: {errorMessage}</Trans>\n </p>\n )}\n\n {isLoadingObjects ? (\n <Stack direction=\"horizontal\" gap=\"2\" alignment=\"center\">\n <Spinner />\n <span className=\"text-juno-grey-light-1 text-sm\">\n <Trans>Checking bucket contents...</Trans>\n </span>\n </Stack>\n ) : isNonEmpty ? (\n <p className=\"text-theme-default\">\n <Trans>\n This bucket contains objects and cannot be deleted. Use <strong>Empty Bucket</strong> action to remove all\n content first.\n </Trans>\n </p>\n ) : hasOnlyVersions ? (\n <p className=\"text-theme-default\">\n <Trans>\n This bucket contains old versions and delete markers. Use <strong>Delete Versions</strong> action to\n remove them before deleting the bucket.\n </Trans>\n </p>\n ) : (\n <>\n <p className=\"text-theme-default\">\n <Trans>\n This action is irreversible. Deleting a bucket permanently removes it and cannot be undone. The bucket\n must be empty before deletion.\n </Trans>\n </p>\n\n <TextInput\n label={t`Type the bucket name to confirm`}\n required\n value={confirmName}\n onChange={handleConfirmNameChange}\n onKeyDown={handleKeyDown}\n invalid={!!nameError}\n errortext={nameError || undefined}\n disabled={deleteBucketMutation.isPending}\n placeholder={bucket.name}\n />\n </>\n )}\n </Stack>\n </Modal>\n )\n}\n","import { useState } from \"react\"\nimport { Trans, useLingui } from \"@lingui/react/macro\"\nimport { trpcReact } from \"@/client/trpcClient\"\nimport { Modal, TextInput, Stack } from \"@cloudoperators/juno-ui-components\"\nimport { Bucket } from \"@/server/Storage/types/ceph\"\nimport { useProjectId } from \"@/client/hooks/useProjectId\"\n\ninterface DeleteVersionsModalProps {\n isOpen: boolean\n bucket: Bucket | null\n onClose: () => void\n onSuccess?: (bucketName: string, deletedCount: number) => void\n onError?: (bucketName: string, errorMessage: string) => void\n}\n\nexport const DeleteVersionsModal = ({ isOpen, bucket, onClose, onSuccess, onError }: DeleteVersionsModalProps) => {\n const { t } = useLingui()\n const projectId = useProjectId()\n const [confirmName, setConfirmName] = useState(\"\")\n const [nameError, setNameError] = useState<string | null>(null)\n\n const utils = trpcReact.useUtils()\n\n const deleteVersionsMutation = trpcReact.storage.ceph.objects.deleteAll.useMutation({\n onSettled: () => {\n // Invalidate both containers.list (to update bucket metadata) and objects.list (to refresh empty state)\n utils.storage.ceph.containers.list.invalidate()\n utils.storage.ceph.objects.list.invalidate()\n handleClose()\n },\n })\n\n const handleClose = () => {\n setConfirmName(\"\")\n setNameError(null)\n deleteVersionsMutation.reset()\n onClose()\n }\n\n const handleConfirmNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const value = e.target.value\n setConfirmName(value)\n if (nameError) setNameError(null)\n }\n\n const handleSubmit = () => {\n if (!bucket) return\n if (confirmName.trim() !== bucket.name) {\n setNameError(t`Bucket name does not match`)\n return\n }\n\n // Capture bucket name before async operation to avoid dereferencing null bucket in callbacks\n const bucketName = bucket.name\n\n // Always delete all versions and delete markers (includeVersionsAndDeleteMarkers: true)\n deleteVersionsMutation.mutate(\n {\n project_id: projectId,\n containerName: bucketName,\n includeVersionsAndDeleteMarkers: true,\n },\n {\n onSuccess: (deletedCount) => {\n onSuccess?.(bucketName, deletedCount)\n },\n onError: (error) => {\n onError?.(bucketName, error.message)\n },\n }\n )\n }\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === \"Enter\") handleSubmit()\n }\n\n if (!isOpen || !bucket) return null\n\n return (\n <Modal\n title={t`Delete Versions`}\n open={isOpen}\n onCancel={handleClose}\n confirmButtonLabel={t`Delete Versions`}\n confirmButtonVariant=\"primary-danger\"\n onConfirm={handleSubmit}\n cancelButtonLabel={t`Cancel`}\n size=\"small\"\n disableConfirmButton={deleteVersionsMutation.isPending || confirmName.trim() !== bucket.name}\n >\n <Stack direction=\"vertical\" gap=\"6\">\n <p className=\"text-theme-default m-0\">\n <Trans>\n This action will permanently delete all versions and delete markers. This will enable you to delete the\n bucket. This action cannot be undone.\n </Trans>\n </p>\n\n <TextInput\n label={t`Type the bucket name to confirm`}\n required\n value={confirmName}\n onChange={handleConfirmNameChange}\n onKeyDown={handleKeyDown}\n invalid={!!nameError}\n errortext={nameError || undefined}\n disabled={deleteVersionsMutation.isPending}\n placeholder={bucket.name}\n autoFocus\n />\n </Stack>\n </Modal>\n )\n}\n"],"mappings":"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"}