Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
ButtonRow,
TextInput,
Stack,
Message,
Spinner,
Checkbox,
} from "@cloudoperators/juno-ui-components"
Expand Down Expand Up @@ -161,22 +160,22 @@ export const DeleteContainerModal = ({ isOpen, container, onClose, onSuccess, on
}
>
{(objectsError || metaError) && (
<Stack direction="vertical" gap="2" className="mb-2">
<Stack direction="vertical" gap="2" className="mb-4">
{objectsError && (
<Message variant="danger">
<p className="text-theme-error">
{(() => {
const errorMessage = objectsError.message
return <Trans>Failed to load container objects: {errorMessage}</Trans>
})()}
</Message>
</p>
)}
{metaError && (
<Message variant="danger">
<p className="text-theme-error">
{(() => {
const errorMessage = metaError.message
return <Trans>Failed to load container properties: {errorMessage}</Trans>
})()}
</Message>
</p>
Comment on lines +163 to +178
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Restore live error announcement semantics for preflight failures.

On Line 165 and Line 173, the async error content is now rendered as plain <p> text, so failures may no longer be announced to assistive tech when they appear. Please add alert/live semantics to this block.

♿ Suggested fix
-        <Stack direction="vertical" gap="2" className="mb-4">
+        <Stack direction="vertical" gap="2" className="mb-4" role="alert" aria-live="assertive">
           {objectsError && (
             <p className="text-theme-error">
               {(() => {
                 const errorMessage = objectsError.message
                 return <Trans>Failed to load container objects: {errorMessage}</Trans>
               })()}
             </p>
           )}
           {metaError && (
             <p className="text-theme-error">
               {(() => {
                 const errorMessage = metaError.message
                 return <Trans>Failed to load container properties: {errorMessage}</Trans>
               })()}
             </p>
           )}
         </Stack>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Stack direction="vertical" gap="2" className="mb-4">
{objectsError && (
<Message variant="danger">
<p className="text-theme-error">
{(() => {
const errorMessage = objectsError.message
return <Trans>Failed to load container objects: {errorMessage}</Trans>
})()}
</Message>
</p>
)}
{metaError && (
<Message variant="danger">
<p className="text-theme-error">
{(() => {
const errorMessage = metaError.message
return <Trans>Failed to load container properties: {errorMessage}</Trans>
})()}
</Message>
</p>
<Stack direction="vertical" gap="2" className="mb-4" role="alert" aria-live="assertive">
{objectsError && (
<p className="text-theme-error">
{(() => {
const errorMessage = objectsError.message
return <Trans>Failed to load container objects: {errorMessage}</Trans>
})()}
</p>
)}
{metaError && (
<p className="text-theme-error">
{(() => {
const errorMessage = metaError.message
return <Trans>Failed to load container properties: {errorMessage}</Trans>
})()}
</p>
)}
</Stack>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/aurora-portal/src/client/routes/_auth/projects/`$projectId/storage/-components/Swift/Containers/DeleteContainerModal.tsx
around lines 163 - 178, The error messages for objectsError and metaError in
DeleteContainerModal are rendered as plain <p> elements and need live/alert
semantics so assistive tech is notified; update the rendering of both error
blocks (the branches that reference objectsError.message and metaError.message)
to use an element with role="alert" and aria-live="assertive" (or
aria-live="polite" if you prefer softer notification) and keep the existing
"text-theme-error" styling and Trans wrapper so the error text is announced when
inserted.

)}
</Stack>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
Modal,
TextInput,
Stack,
Message,
Spinner,
DataGrid,
DataGridRow,
Expand Down Expand Up @@ -435,20 +434,26 @@ export const EditContainerMetadataModal = ({
size="large"
disableConfirmButton={isBusy || isAddingNew || hasEditing || isUnchanged || isMetaFailed}
>
{updateMutation.isError && (
<p className="text-theme-error mb-4">
{(() => {
const errorMessage = updateMutation.error.message
return <Trans>Failed to update container: {errorMessage}</Trans>
})()}
</p>
)}
{isLoading ? (
<Stack direction="horizontal" alignment="center" gap="2" className="py-8">
<Spinner size="small" />
<Trans>Loading container properties...</Trans>
</Stack>
) : isMetaFailed ? (
<Stack direction="vertical" alignment="center" gap="3" className="py-8">
<Message variant="danger">
{(() => {
const errorMessage = metaError?.message ?? "Unknown error"
return <Trans>Failed to load container properties: {errorMessage}</Trans>
})()}
</Message>
</Stack>
<p className="text-theme-error py-8 text-center">
{(() => {
const errorMessage = metaError?.message ?? "Unknown error"
return <Trans>Failed to load container properties: {errorMessage}</Trans>
})()}
</p>
) : (
<div className="max-h-[65vh] overflow-y-auto pr-1 pl-1">
<Stack direction="vertical" gap="6">
Expand Down Expand Up @@ -812,17 +817,6 @@ export const EditContainerMetadataModal = ({
)}
</DataGrid>
</div>

{/* Mutation error */}
{updateMutation.isError &&
(() => {
const errorMessage = updateMutation.error.message
return (
<Message variant="danger">
<Trans>Failed to update container: {errorMessage}</Trans>
</Message>
)
})()}
</Stack>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
ButtonRow,
TextInput,
Stack,
Message,
Spinner,
DataGrid,
DataGridRow,
Expand Down Expand Up @@ -162,12 +161,12 @@ export const EmptyContainerModal = ({ isOpen, container, onClose, onSuccess, onE
}
>
{objectsError && (
<Message variant="danger" className="mb-2">
<p className="text-theme-error mb-4">
{(() => {
const errorMessage = objectsError.message
return <Trans>Failed to load container objects: {errorMessage}</Trans>
})()}
</Message>
</p>
)}
{isLoadingObjects ? (
<Stack direction="horizontal" alignment="center" gap="2" className="py-4">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ describe("EmptyContainersModal", () => {
describe("Content", () => {
test("renders warning message", () => {
renderModal()
expect(screen.getByText(/Are you sure/i)).toBeInTheDocument()
expect(screen.getByText(/cannot be undone/i)).toBeInTheDocument()
})

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useState } from "react"
import { Trans, useLingui } from "@lingui/react/macro"
import { trpcReact } from "@/client/trpcClient"
import { Modal, Message, Spinner, Stack } from "@cloudoperators/juno-ui-components"
import { Modal, Spinner, Stack } from "@cloudoperators/juno-ui-components"
import { ContainerSummary } from "@/server/Storage/types/swift"
import { useProjectId } from "@/client/hooks/useProjectId"

Expand Down Expand Up @@ -81,6 +81,7 @@ export const EmptyContainersModal = ({ isOpen, containers, onClose, onComplete }
open={isOpen}
onCancel={handleClose}
confirmButtonLabel={isPending ? t`Emptying...` : t`Empty`}
confirmButtonVariant="primary-danger"
cancelButtonLabel={t`Cancel`}
onConfirm={handleConfirm}
disableConfirmButton={isPending}
Expand All @@ -101,17 +102,14 @@ export const EmptyContainersModal = ({ isOpen, containers, onClose, onComplete }
</Stack>
) : (
<div className="my-6">
<Message variant="warning" className="mb-6">
<Trans>
<strong>Are you sure?</strong> All objects in the selected containers will be permanently deleted. This
cannot be undone.
</Trans>
<p className="text-theme-default mb-6">
<Trans>All objects in the selected containers will be permanently deleted. This cannot be undone.</Trans>
<br />
<Trans>
Please note: for <strong>dynamic</strong> and <strong>static large objects</strong> only the manifests are
deleted. The related segments are not deleted.
</Trans>
</Message>
</p>

<div className="mb-6">
<h3 className="jn:text-theme-high mb-3 font-semibold">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
Modal,
Textarea,
Stack,
Message,
Spinner,
Checkbox,
Badge,
Expand Down Expand Up @@ -332,6 +331,14 @@ export const ManageContainerAccessModal = ({
size="xl"
disableConfirmButton={isBusy || isMetaError}
>
{updateMutation.isError && (
<p className="text-theme-error mb-4">
{(() => {
const errorMessage = updateMutation.error.message
return <Trans>Failed to update ACLs: {errorMessage}</Trans>
})()}
</p>
)}
<div className="max-h-[70vh] overflow-y-auto pr-1 pl-1">
{/* ── Info message ─────────────────────────────────────────────────── */}
<div className="mb-4">
Expand All @@ -355,12 +362,12 @@ export const ManageContainerAccessModal = ({
<Trans>Loading ACLs...</Trans>
</Stack>
) : isMetaError ? (
<Message variant="danger">
<p className="text-theme-error py-2">
{(() => {
const errorMessage = metaError?.message ?? ""
return <Trans>Failed to load container ACLs: {errorMessage}</Trans>
})()}
</Message>
</p>
) : (
<>
<div className="flex gap-6">
Expand Down Expand Up @@ -522,16 +529,6 @@ export const ManageContainerAccessModal = ({
</p>
</div>
</div>

{/* Mutation error */}
{updateMutation.isError && (
<Message variant="danger" className="mt-4">
{(() => {
const errorMessage = updateMutation.error.message
return <Trans>Failed to update ACLs: {errorMessage}</Trans>
})()}
</Message>
)}
</>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,7 @@ import { useState, useRef, useEffect, useCallback } from "react"
import { Trans, useLingui } from "@lingui/react/macro"
import { trpcReact } from "@/client/trpcClient"
import { useProjectId } from "@/client/hooks/useProjectId"
import {
Modal,
Stack,
Spinner,
ComboBox,
ComboBoxOption,
TextInput,
Message,
Button,
} from "@cloudoperators/juno-ui-components"
import { Modal, Stack, Spinner, ComboBox, ComboBoxOption, TextInput, Button } from "@cloudoperators/juno-ui-components"
import { useParams } from "@tanstack/react-router"
import { MdFolder, MdDescription, MdCreateNewFolder, MdArrowBack } from "react-icons/md"
import { useVirtualizer } from "@tanstack/react-virtual"
Expand Down Expand Up @@ -319,6 +310,16 @@ export const CopyObjectModal = ({ isOpen, object, onClose, onSuccess, onError }:
</Stack>
) : (
<Stack direction="vertical" gap="4">
{copyMutation.isError &&
(() => {
const errorMessage = copyMutation.error.message
return (
<p className="text-theme-error text-sm">
<Trans>Failed to copy object: {errorMessage}</Trans>
</p>
)
})()}

{/* Target container — ComboBox with debounced search to handle large lists */}
<ComboBox
label={t`Target container`}
Expand Down Expand Up @@ -485,13 +486,12 @@ export const CopyObjectModal = ({ isOpen, object, onClose, onSuccess, onError }:
</div>

{/* Read-only target path */}
<TextInput
label={t`Target path`}
value={targetPathDisplay}
readOnly
className="font-mono"
helptext={t`The object will be copied to this path. Navigate folders above to change the destination.`}
/>
<div>
<p className="text-theme-light mb-1">
<Trans>The object will be copied to this path. Navigate folders above to change the destination.</Trans>
</p>
<TextInput label={t`Target path`} value={targetPathDisplay} readOnly className="font-mono" />
</div>

{/* Copy metadata checkbox */}
<label className="flex cursor-pointer items-center gap-2">
Expand All @@ -505,16 +505,6 @@ export const CopyObjectModal = ({ isOpen, object, onClose, onSuccess, onError }:
<Trans>Copy metadata</Trans>
</span>
</label>

{copyMutation.isError &&
(() => {
const errorMessage = copyMutation.error.message
return (
<Message variant="danger">
<Trans>Failed to copy object: {errorMessage}</Trans>
</Message>
)
})()}
</Stack>
)}
</Modal>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ describe("CreateFolderModal", () => {
expect(screen.getByRole("button", { name: /Cancel/i })).toBeInTheDocument()
})

test("renders info message about virtual folders", () => {
test("renders info text about virtual folders", () => {
renderModal()
expect(screen.getByText(/zero-byte placeholder objects/i)).toBeInTheDocument()
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useState, useRef } from "react"
import { Trans, useLingui } from "@lingui/react/macro"
import { trpcReact } from "@/client/trpcClient"
import { useProjectId } from "@/client/hooks/useProjectId"
import { Modal, TextInput, Stack, Message } from "@cloudoperators/juno-ui-components"
import { Modal, TextInput, Stack } from "@cloudoperators/juno-ui-components"
import { useParams } from "@tanstack/react-router"

interface CreateFolderModalProps {
Expand Down Expand Up @@ -110,12 +110,12 @@ export const CreateFolderModal = ({ isOpen, currentPrefix, onClose, onSuccess, o
disableConfirmButton={createFolderMutation.isPending || !folderName.trim()}
>
<Stack direction="vertical" gap="6">
<Message variant="info">
<p className="text-theme-default">
<Trans>
Folders in object storage are virtual — they are created as zero-byte placeholder objects with a trailing
slash. The folder will appear once created.
</Trans>
</Message>
</p>
<TextInput
label={t`Folder name`}
required
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,16 +156,13 @@ describe("DeleteFolderModal", () => {
expect(screen.getByRole("button", { name: /Cancel/i })).toBeInTheDocument()
})

test("renders warning message with folder name", () => {
test("renders plain text warning with folder name", () => {
renderModal()
expect(screen.getByText(/Are you sure\?/i)).toBeInTheDocument()
// The folder name appears in the warning body as "documents" inside a <span>
expect(screen.getByText(/permanently deleted/i)).toBeInTheDocument()
expect(screen.getByText(/"documents"/)).toBeInTheDocument()
expect(screen.getByText(/permanently deleted/i)).toBeInTheDocument()
})

test("renders SLO/DLO segments info message", () => {
test("renders SLO/DLO segments plain text note", () => {
renderModal()
expect(screen.getByText(/static and dynamic large objects/i)).toBeInTheDocument()
expect(screen.getByText(/only the manifests are deleted/i)).toBeInTheDocument()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useEffect, useRef } from "react"
import { Trans, useLingui } from "@lingui/react/macro"
import { trpcReact } from "@/client/trpcClient"
import { useProjectId } from "@/client/hooks/useProjectId"
import { Modal, Message, Stack, Spinner } from "@cloudoperators/juno-ui-components"
import { Modal, Stack, Spinner } from "@cloudoperators/juno-ui-components"
import { useParams } from "@tanstack/react-router"
import { FolderRow } from "./"

Expand Down Expand Up @@ -81,6 +81,7 @@ export const DeleteFolderModal = ({ isOpen, folder, onClose, onSuccess, onError
open={isOpen}
onCancel={handleClose}
confirmButtonLabel={deleteFolderMutation.isPending ? t`Deleting...` : t`Delete`}
confirmButtonVariant="primary-danger"
onConfirm={handleConfirm}
cancelButtonLabel={t`Cancel`}
size="small"
Expand All @@ -93,19 +94,18 @@ export const DeleteFolderModal = ({ isOpen, folder, onClose, onSuccess, onError
</Stack>
) : (
<Stack direction="vertical" gap="4">
<Message variant="warning">
<p className="text-theme-default">
<Trans>
<strong>Are you sure?</strong> Folder{" "}
<span className="font-mono font-semibold">"{folderDisplayName}"</span> and all objects within it will be
permanently deleted. This cannot be undone.
Folder <span className="font-mono font-semibold">"{folderDisplayName}"</span> and all objects within it
will be permanently deleted. This cannot be undone.
</Trans>
</Message>
<Message variant="info">
</p>
<p className="text-theme-default">
<Trans>
Note: for <strong>static and dynamic large objects</strong> only the manifests are deleted — their
segments outside this folder prefix are not affected.
</Trans>
</Message>
</p>
</Stack>
)}
</Modal>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,10 @@ describe("DeleteObjectModal", () => {
expect(screen.getByTitle("report.pdf")).toBeInTheDocument()
})

it("shows warning about permanent deletion", () => {
it("shows plain text warning about permanent deletion", () => {
renderModal()
expect(screen.getByText(/will be permanently deleted/i)).toBeInTheDocument()
expect(screen.queryByText(/Are you sure/i)).not.toBeInTheDocument()
})

it("does not show SLO or DLO info notes for regular objects", () => {
Expand Down
Loading