Skip to content

Commit c954a3e

Browse files
authored
fix(ui): ceph finding fix (#927)
* fix(ui): remove monospace font styling * fix(ui): error messages * fix(ui): remove delete credentials button * fix(ui): ceph modals * feat(aurora-portal): changeset * fix(aurora-portal): ceph tests * fix(ui): Prevent dismissing destructive/committing modals during pending mutations
1 parent 42471fb commit c954a3e

26 files changed

Lines changed: 186 additions & 137 deletions

.changeset/smart-peas-walk.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cobaltcore-dev/aurora": minor
3+
---
4+
5+
Ceph fixes multiple ui/ux issues

packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Containers/ContainerListView.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ describe("ContainerListView", () => {
191191
test("shows error message for other errors", () => {
192192
mockState.error = { message: "Network error" }
193193
renderListView()
194-
expect(screen.getByText(/Error Loading Containers/)).toBeInTheDocument()
194+
expect(screen.getByText(/Error Loading Buckets/)).toBeInTheDocument()
195195
expect(screen.getByText(/Network error/)).toBeInTheDocument()
196196
})
197197

packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Containers/ContainerListView.tsx

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
Button,
1414
Toast,
1515
ToastProps,
16+
Message,
1617
} from "@cloudoperators/juno-ui-components"
1718
import type { Container } from "@/server/Storage/types/ceph"
1819
import { CredentialPrompt } from "./CredentialPrompt"
@@ -73,33 +74,28 @@ export function ContainerListView() {
7374
const isAccessDenied = errorMessage.includes("Access denied") || errorMessage.includes("AccessDenied")
7475
const isAuthError = errorMessage.includes("Invalid access key") || errorMessage.includes("InvalidAccessKeyId")
7576

77+
const errorTitle = isAccessDenied
78+
? t`Access Denied`
79+
: isAuthError
80+
? t`Authentication Failed`
81+
: t`Error Loading Buckets`
82+
7683
return (
77-
<div className="bg-juno-red bg-opacity-10 border-juno-red mt-4 rounded border p-4">
78-
<h4 className="text-juno-red mb-1 text-sm font-semibold">
79-
{isAccessDenied ? (
80-
<Trans>Access Denied</Trans>
81-
) : isAuthError ? (
82-
<Trans>Authentication Failed</Trans>
83-
) : (
84-
<Trans>Error Loading Containers</Trans>
85-
)}
86-
</h4>
87-
<p className="text-juno-grey-light-1 text-sm">
88-
{isAccessDenied ? (
89-
<Trans>
90-
Your credentials are valid but you don't have permission to perform this operation. Please contact your
91-
administrator to grant you the necessary permissions.
92-
</Trans>
93-
) : isAuthError ? (
94-
<Trans>
95-
Your S3 credentials are invalid or expired. Please try creating new credentials or contact your
96-
administrator.
97-
</Trans>
98-
) : (
99-
errorMessage
100-
)}
101-
</p>
102-
</div>
84+
<Message variant="error" title={errorTitle}>
85+
{isAccessDenied ? (
86+
<Trans>
87+
Your credentials are valid but you don't have permission to perform this operation. Please contact your
88+
administrator to grant you the necessary permissions.
89+
</Trans>
90+
) : isAuthError ? (
91+
<Trans>
92+
Your S3 credentials are invalid or expired. Please try creating new credentials or contact your
93+
administrator.
94+
</Trans>
95+
) : (
96+
errorMessage
97+
)}
98+
</Message>
10399
)
104100
}
105101

@@ -189,7 +185,7 @@ export function ContainerListView() {
189185
containerName: bucket.name,
190186
}}
191187
>
192-
<span className="hover:text-juno-blue cursor-pointer font-mono text-sm underline">{bucket.name}</span>
188+
<span className="hover:text-juno-blue cursor-pointer text-sm underline">{bucket.name}</span>
193189
</Link>
194190
</DataGridCell>
195191
<DataGridCell>

packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Containers/CredentialPrompt.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export function CredentialPrompt({ onSuccess }: CredentialPromptProps) {
3131
})
3232

3333
return (
34-
<Stack direction="vertical" gap="4" className="mx-auto mt-16 max-w-lg">
34+
<Stack direction="vertical" gap="4" className="mt-8 max-w-lg">
3535
{toast && <Toast {...toast} />}
3636
<h2 className="text-lg font-semibold">
3737
<Trans>S3 Object Storage — Setup Required</Trans>

packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Containers/DeleteBucketModal.test.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -315,10 +315,11 @@ describe("DeleteBucketModal", () => {
315315
expect(screen.getByRole("button", { name: /Copy/i })).toBeInTheDocument()
316316
})
317317

318-
test("displays bucket name in a code block", () => {
318+
test("displays bucket name in styled block", () => {
319319
renderModal()
320-
const codeBlock = screen.getByText(mockEmptyBucket.name).closest("div.font-mono")
321-
expect(codeBlock).toBeInTheDocument()
320+
const styledBlock = screen.getByText(mockEmptyBucket.name).closest("div")
321+
expect(styledBlock).toBeInTheDocument()
322+
expect(styledBlock).toHaveClass("bg-theme-background-lvl-1")
322323
})
323324
})
324325

packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Containers/DeleteBucketModal.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ export const DeleteBucketModal = ({ isOpen, bucket, onClose, onSuccess, onError
159159

160160
<Stack direction="vertical" gap="2">
161161
<div className="flex items-center justify-between">
162-
<span className="text-juno-grey-light-1 text-sm">
162+
<span className="text-theme-light text-sm">
163163
<Trans>Bucket to delete:</Trans>
164164
</span>
165165
<Button
@@ -170,7 +170,7 @@ export const DeleteBucketModal = ({ isOpen, bucket, onClose, onSuccess, onError
170170
label={copied ? t`Copied` : t`Copy`}
171171
/>
172172
</div>
173-
<div className="bg-juno-grey-blue-10 rounded p-2 font-mono text-sm">{bucket.name}</div>
173+
<div className="bg-theme-background-lvl-1 rounded p-2 text-sm">{bucket.name}</div>
174174
</Stack>
175175

176176
<TextInput

packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Containers/EmptyBucketModal.test.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,10 +251,11 @@ describe("EmptyBucketModal", () => {
251251
expect(screen.getByRole("button", { name: /Copy/i })).toBeInTheDocument()
252252
})
253253

254-
test("displays bucket name in a code block", () => {
254+
test("displays bucket name in styled block", () => {
255255
renderModal()
256-
const codeBlock = screen.getByText(mockNonEmptyBucket.name).closest("div.font-mono")
257-
expect(codeBlock).toBeInTheDocument()
256+
const styledBlock = screen.getByText(mockNonEmptyBucket.name).closest("div")
257+
expect(styledBlock).toBeInTheDocument()
258+
expect(styledBlock).toHaveClass("bg-theme-background-lvl-1")
258259
})
259260
})
260261

packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Containers/EmptyBucketModal.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ export const EmptyBucketModal = ({ isOpen, bucket, onClose, onSuccess, onError }
127127

128128
<Stack direction="vertical" gap="2">
129129
<div className="flex items-center justify-between">
130-
<span className="text-juno-grey-light-1 text-sm">
130+
<span className="text-theme-light text-sm">
131131
<Trans>Bucket to empty:</Trans>
132132
</span>
133133
<Button
@@ -138,7 +138,7 @@ export const EmptyBucketModal = ({ isOpen, bucket, onClose, onSuccess, onError }
138138
label={copied ? t`Copied` : t`Copy`}
139139
/>
140140
</div>
141-
<div className="bg-juno-grey-blue-10 rounded p-2 font-mono text-sm">{bucket.name}</div>
141+
<div className="bg-theme-background-lvl-1 rounded p-2 text-sm">{bucket.name}</div>
142142
</Stack>
143143

144144
<TextInput

packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Containers/EmptyBucketsModal.test.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,10 @@ describe("EmptyBucketsModal", () => {
202202
const user = userEvent.setup({ delay: null })
203203
renderModal({ buckets: mockBuckets })
204204

205+
// Type "empty" to enable the button
206+
const confirmInput = screen.getByLabelText(/Type "empty" to confirm/i)
207+
await user.type(confirmInput, "empty")
208+
205209
const emptyButton = screen.getByRole("button", { name: /^Empty$/i })
206210
await user.click(emptyButton)
207211

@@ -232,6 +236,10 @@ describe("EmptyBucketsModal", () => {
232236
const mockOnComplete = vi.fn()
233237
renderModal({ buckets: mockBuckets, onComplete: mockOnComplete })
234238

239+
// Type "empty" to enable the button
240+
const confirmInput = screen.getByLabelText(/Type "empty" to confirm/i)
241+
await user.type(confirmInput, "empty")
242+
235243
const emptyButton = screen.getByRole("button", { name: /^Empty$/i })
236244
await user.click(emptyButton)
237245

@@ -251,6 +259,10 @@ describe("EmptyBucketsModal", () => {
251259
const user = userEvent.setup({ delay: null })
252260
renderModal({ buckets: mockBuckets })
253261

262+
// Type "empty" to enable the button
263+
const confirmInput = screen.getByLabelText(/Type "empty" to confirm/i)
264+
await user.type(confirmInput, "empty")
265+
254266
const emptyButton = screen.getByRole("button", { name: /^Empty$/i })
255267
await user.click(emptyButton)
256268

@@ -267,6 +279,10 @@ describe("EmptyBucketsModal", () => {
267279
const mockOnClose = vi.fn()
268280
renderModal({ buckets: mockBuckets, onClose: mockOnClose })
269281

282+
// Type "empty" to enable the button
283+
const confirmInput = screen.getByLabelText(/Type "empty" to confirm/i)
284+
await user.type(confirmInput, "empty")
285+
270286
const emptyButton = screen.getByRole("button", { name: /^Empty$/i })
271287
await user.click(emptyButton)
272288

@@ -286,6 +302,10 @@ describe("EmptyBucketsModal", () => {
286302
mockState.failOnBucket = "bucket-2"
287303
renderModal({ buckets: mockBuckets, onComplete: mockOnComplete })
288304

305+
// Type "empty" to enable the button
306+
const confirmInput = screen.getByLabelText(/Type "empty" to confirm/i)
307+
await user.type(confirmInput, "empty")
308+
289309
const emptyButton = screen.getByRole("button", { name: /^Empty$/i })
290310
await user.click(emptyButton)
291311

@@ -307,6 +327,10 @@ describe("EmptyBucketsModal", () => {
307327
mockState.shouldFail = true
308328
renderModal({ buckets: mockBuckets, onComplete: mockOnComplete })
309329

330+
// Type "empty" to enable the button
331+
const confirmInput = screen.getByLabelText(/Type "empty" to confirm/i)
332+
await user.type(confirmInput, "empty")
333+
310334
const emptyButton = screen.getByRole("button", { name: /^Empty$/i })
311335
await user.click(emptyButton)
312336

@@ -331,6 +355,10 @@ describe("EmptyBucketsModal", () => {
331355
mockState.shouldFail = true
332356
renderModal({ buckets: mockBuckets })
333357

358+
// Type "empty" to enable the button
359+
const confirmInput = screen.getByLabelText(/Type "empty" to confirm/i)
360+
await user.type(confirmInput, "empty")
361+
334362
const emptyButton = screen.getByRole("button", { name: /^Empty$/i })
335363
await user.click(emptyButton)
336364

@@ -347,6 +375,10 @@ describe("EmptyBucketsModal", () => {
347375
mockState.failOnBucket = "bucket-2"
348376
renderModal({ buckets: mockBuckets })
349377

378+
// Type "empty" to enable the button
379+
const confirmInput = screen.getByLabelText(/Type "empty" to confirm/i)
380+
await user.type(confirmInput, "empty")
381+
350382
const emptyButton = screen.getByRole("button", { name: /^Empty$/i })
351383
await user.click(emptyButton)
352384

packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Containers/EmptyBucketsModal.tsx

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useState } from "react"
22
import { Trans, useLingui, Plural } from "@lingui/react/macro"
33
import { trpcReact } from "@/client/trpcClient"
4-
import { Modal, Spinner, Stack } from "@cloudoperators/juno-ui-components"
4+
import { Modal, Spinner, Stack, Message, TextInput } from "@cloudoperators/juno-ui-components"
55
import { Container } from "@/server/Storage/types/ceph"
66
import { useProjectId } from "@/client/hooks/useProjectId"
77

@@ -24,13 +24,15 @@ export const EmptyBucketsModal = ({ isOpen, buckets, onClose, onComplete }: Empt
2424
const { t } = useLingui()
2525
const projectId = useProjectId()
2626
const [progress, setProgress] = useState<{ current: number; total: number } | null>(null)
27+
const [confirmText, setConfirmText] = useState("")
2728

2829
const utils = trpcReact.useUtils()
2930
const emptyBucketMutation = trpcReact.storage.ceph.objects.deleteAll.useMutation()
3031

3132
const handleClose = () => {
3233
emptyBucketMutation.reset()
3334
setProgress(null)
35+
setConfirmText("")
3436
onClose()
3537
}
3638

@@ -73,6 +75,7 @@ export const EmptyBucketsModal = ({ isOpen, buckets, onClose, onComplete }: Empt
7375
const isPending = emptyBucketMutation.isPending || progress !== null
7476
const progressCurrent = progress?.current
7577
const progressTotal = progress?.total
78+
const isConfirmValid = confirmText === "empty"
7679

7780
return (
7881
<Modal
@@ -83,7 +86,7 @@ export const EmptyBucketsModal = ({ isOpen, buckets, onClose, onComplete }: Empt
8386
confirmButtonVariant="primary-danger"
8487
cancelButtonLabel={t`Cancel`}
8588
onConfirm={handleConfirm}
86-
disableConfirmButton={isPending}
89+
disableConfirmButton={isPending || !isConfirmValid}
8790
disableCancelButton={isPending}
8891
disableCloseButton={isPending}
8992
size="small"
@@ -101,12 +104,12 @@ export const EmptyBucketsModal = ({ isOpen, buckets, onClose, onComplete }: Empt
101104
</Stack>
102105
) : (
103106
<Stack direction="vertical" gap="4">
104-
<p className="text-theme-default">
107+
<Message variant="danger">
105108
<Trans>
106109
This will permanently delete all objects from {totalCount} selected{" "}
107110
<Plural value={totalCount} one="bucket" other="buckets" />. This action cannot be undone.
108111
</Trans>
109-
</p>
112+
</Message>
110113

111114
<div>
112115
<p className="text-theme-light mb-2 text-sm">
@@ -117,7 +120,7 @@ export const EmptyBucketsModal = ({ isOpen, buckets, onClose, onComplete }: Empt
117120
const bucketName = bucket.name
118121
const bucketCount = bucket.count
119122
return (
120-
<li key={bucketName} className="font-mono">
123+
<li key={bucketName}>
121124
{bucketName}
122125
{bucketCount > 0 && (
123126
<span className="text-theme-light ml-2">
@@ -134,6 +137,14 @@ export const EmptyBucketsModal = ({ isOpen, buckets, onClose, onComplete }: Empt
134137
)}
135138
</ul>
136139
</div>
140+
141+
<TextInput
142+
label={t`Type "empty" to confirm`}
143+
value={confirmText}
144+
onChange={(e) => setConfirmText(e.target.value)}
145+
placeholder="empty"
146+
autoFocus
147+
/>
137148
</Stack>
138149
)}
139150
</Modal>

0 commit comments

Comments
 (0)