mirror of
https://github.com/rustfs/console.git
synced 2026-09-21 06:06:37 +08:00
refactor: page components and events shared module
- Home: usePerformanceData hook + performance UI components in _components - License: extract enterprise section to components/license/enterprise-section - Login: extract LoginForm to components/auth/login-form - Events: lib/events (types, constants, getDisplayEvents) + components/events/columns - Events page and buckets/events-tab use shared events module and getEventsColumns
This commit is contained in:
@@ -2,48 +2,16 @@
|
||||
|
||||
import * as React from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { RiAddLine, RiRefreshLine, RiDeleteBin7Line } from "@remixicon/react"
|
||||
import { RiAddLine, RiRefreshLine } from "@remixicon/react"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { DataTable } from "@/components/data-table/data-table"
|
||||
import { useDataTable } from "@/hooks/use-data-table"
|
||||
import { EventsNewForm } from "@/components/events/new-form"
|
||||
import { getEventsColumns } from "@/components/events/columns"
|
||||
import { useBucket } from "@/hooks/use-bucket"
|
||||
import { useDialog } from "@/lib/feedback/dialog"
|
||||
import { useMessage } from "@/lib/feedback/message"
|
||||
import type { ColumnDef } from "@tanstack/react-table"
|
||||
|
||||
interface NotificationItem {
|
||||
id: string
|
||||
type: "Lambda" | "SQS" | "SNS" | "Topic"
|
||||
arn: string
|
||||
events: string[]
|
||||
prefix?: string
|
||||
suffix?: string
|
||||
filterRules?: Array<{ Name: string; Value: string }>
|
||||
}
|
||||
|
||||
const EVENT_DISPLAY_MAP: Record<string, string> = {
|
||||
"s3:ObjectCreated:*": "PUT",
|
||||
"s3:ObjectAccessed:*": "GET",
|
||||
"s3:ObjectRemoved:*": "DELETE",
|
||||
"s3:Replication:*": "REPLICA",
|
||||
"s3:ObjectRestore:*": "RESTORE",
|
||||
"s3:ObjectTransition:*": "RESTORE",
|
||||
"s3:Scanner:ManyVersions": "SCANNER",
|
||||
"s3:Scanner:BigPrefix": "SCANNER",
|
||||
}
|
||||
|
||||
const TYPE_BADGE_CLASSES: Record<NotificationItem["type"], string> = {
|
||||
Lambda: "bg-amber-100 text-amber-900 dark:bg-amber-900/40 dark:text-amber-100",
|
||||
SQS: "bg-sky-100 text-sky-900 dark:bg-sky-900/40 dark:text-sky-100",
|
||||
SNS: "bg-emerald-100 text-emerald-900 dark:bg-emerald-900/40 dark:text-emerald-100",
|
||||
Topic: "bg-indigo-100 text-indigo-900 dark:bg-indigo-900/40 dark:text-indigo-100",
|
||||
}
|
||||
|
||||
function getDisplayEvents(events: string[]) {
|
||||
return [...new Set(events.map((e) => EVENT_DISPLAY_MAP[e] || e))]
|
||||
}
|
||||
import type { NotificationItem } from "@/lib/events"
|
||||
|
||||
interface BucketEventsTabProps {
|
||||
bucketName: string
|
||||
@@ -137,79 +105,76 @@ export function BucketEventsTab({ bucketName }: BucketEventsTabProps) {
|
||||
loadData()
|
||||
}, [loadData])
|
||||
|
||||
const columns: ColumnDef<NotificationItem>[] = React.useMemo(
|
||||
() => [
|
||||
{
|
||||
id: "type",
|
||||
header: () => t("Type"),
|
||||
cell: ({ row }) => (
|
||||
<Badge className={TYPE_BADGE_CLASSES[row.original.type] ?? ""}>
|
||||
{row.original.type}
|
||||
</Badge>
|
||||
),
|
||||
meta: { maxWidth: "7rem" },
|
||||
},
|
||||
{
|
||||
id: "arn",
|
||||
header: () => t("ARN"),
|
||||
cell: ({ row }) => (
|
||||
<span className="line-clamp-2 break-all font-medium">
|
||||
{row.original.arn}
|
||||
</span>
|
||||
),
|
||||
meta: { maxWidth: "180px" },
|
||||
},
|
||||
{
|
||||
id: "events",
|
||||
header: () => t("Events"),
|
||||
cell: ({ row }) => (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{getDisplayEvents(row.original.events).map((event) => (
|
||||
<Badge key={event} variant="secondary">
|
||||
{event}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
meta: { maxWidth: "13rem" },
|
||||
},
|
||||
{
|
||||
id: "prefix",
|
||||
header: () => t("Prefix"),
|
||||
cell: ({ row }) => <span>{row.original.prefix || "-"}</span>,
|
||||
meta: { maxWidth: "9rem" },
|
||||
},
|
||||
{
|
||||
id: "suffix",
|
||||
header: () => t("Suffix"),
|
||||
cell: ({ row }) => <span>{row.original.suffix || "-"}</span>,
|
||||
meta: { maxWidth: "9rem" },
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
header: () => t("Actions"),
|
||||
enableSorting: false,
|
||||
meta: { maxWidth: "6rem" },
|
||||
cell: ({ row }) => (
|
||||
<div className="flex justify-center">
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="gap-2"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
handleRowDelete(row.original)
|
||||
}}
|
||||
>
|
||||
<RiDeleteBin7Line className="size-4" aria-hidden />
|
||||
<span>{t("Delete")}</span>
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
],
|
||||
[t]
|
||||
const handleRowDelete = React.useCallback(
|
||||
async (row: NotificationItem) => {
|
||||
const confirmed = await new Promise<boolean>((resolve) => {
|
||||
dialog.warning({
|
||||
title: t("Confirm Delete"),
|
||||
content: t(
|
||||
"Are you sure you want to delete this notification configuration?"
|
||||
),
|
||||
positiveText: t("Delete"),
|
||||
negativeText: t("Cancel"),
|
||||
onPositiveClick: () => resolve(true),
|
||||
onNegativeClick: () => resolve(false),
|
||||
})
|
||||
})
|
||||
|
||||
if (!confirmed) return
|
||||
|
||||
try {
|
||||
setLoading(true)
|
||||
const currentResponse = await listBucketNotifications(bucketName)
|
||||
const currentNotifications = (currentResponse ?? {}) as unknown as Record<
|
||||
string,
|
||||
unknown[]
|
||||
>
|
||||
|
||||
const configKey =
|
||||
row.type === "Lambda"
|
||||
? "LambdaFunctionConfigurations"
|
||||
: row.type === "SQS"
|
||||
? "QueueConfigurations"
|
||||
: "TopicConfigurations"
|
||||
const configs = (
|
||||
currentNotifications as Record<string, Array<{ Id?: string }>>
|
||||
)[configKey]
|
||||
const updated = configs?.filter((c) => c.Id !== row.id) ?? []
|
||||
|
||||
const newConfig = {
|
||||
...currentNotifications,
|
||||
...(row.type === "Lambda"
|
||||
? { LambdaFunctionConfigurations: updated }
|
||||
: row.type === "SQS"
|
||||
? { QueueConfigurations: updated }
|
||||
: { TopicConfigurations: updated }),
|
||||
}
|
||||
|
||||
await putBucketNotifications(bucketName, newConfig)
|
||||
message.success(t("Delete Success"))
|
||||
loadData()
|
||||
} catch (error) {
|
||||
message.error(
|
||||
`${t("Delete Failed")}: ${(error as Error).message ?? error}`
|
||||
)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
},
|
||||
[
|
||||
bucketName,
|
||||
dialog,
|
||||
listBucketNotifications,
|
||||
loadData,
|
||||
message,
|
||||
putBucketNotifications,
|
||||
t,
|
||||
]
|
||||
)
|
||||
|
||||
const columns = React.useMemo(
|
||||
() => getEventsColumns(t, handleRowDelete),
|
||||
[t, handleRowDelete]
|
||||
)
|
||||
|
||||
const { table } = useDataTable<NotificationItem>({
|
||||
@@ -218,62 +183,6 @@ export function BucketEventsTab({ bucketName }: BucketEventsTabProps) {
|
||||
getRowId: (row) => row.id,
|
||||
})
|
||||
|
||||
const handleRowDelete = async (row: NotificationItem) => {
|
||||
const confirmed = await new Promise<boolean>((resolve) => {
|
||||
dialog.warning({
|
||||
title: t("Confirm Delete"),
|
||||
content: t(
|
||||
"Are you sure you want to delete this notification configuration?"
|
||||
),
|
||||
positiveText: t("Delete"),
|
||||
negativeText: t("Cancel"),
|
||||
onPositiveClick: () => resolve(true),
|
||||
onNegativeClick: () => resolve(false),
|
||||
})
|
||||
})
|
||||
|
||||
if (!confirmed) return
|
||||
|
||||
try {
|
||||
setLoading(true)
|
||||
const currentResponse = await listBucketNotifications(bucketName)
|
||||
const currentNotifications = (currentResponse ?? {}) as unknown as Record<
|
||||
string,
|
||||
unknown[]
|
||||
>
|
||||
|
||||
const configKey =
|
||||
row.type === "Lambda"
|
||||
? "LambdaFunctionConfigurations"
|
||||
: row.type === "SQS"
|
||||
? "QueueConfigurations"
|
||||
: "TopicConfigurations"
|
||||
const configs = (
|
||||
currentNotifications as Record<string, Array<{ Id?: string }>>
|
||||
)[configKey]
|
||||
const updated = configs?.filter((c) => c.Id !== row.id) ?? []
|
||||
|
||||
const newConfig = {
|
||||
...currentNotifications,
|
||||
...(row.type === "Lambda"
|
||||
? { LambdaFunctionConfigurations: updated }
|
||||
: row.type === "SQS"
|
||||
? { QueueConfigurations: updated }
|
||||
: { TopicConfigurations: updated }),
|
||||
}
|
||||
|
||||
await putBucketNotifications(bucketName, newConfig)
|
||||
message.success(t("Delete Success"))
|
||||
loadData()
|
||||
} catch (error) {
|
||||
message.error(
|
||||
`${t("Delete Failed")}: ${(error as Error).message ?? error}`
|
||||
)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center justify-between">
|
||||
|
||||
Reference in New Issue
Block a user