diff --git a/ui/api/rebalance/actions.ts b/ui/api/rebalance/actions.ts index 5bf90e28e..b0a4c1e24 100644 --- a/ui/api/rebalance/actions.ts +++ b/ui/api/rebalance/actions.ts @@ -9,6 +9,7 @@ import { } from "./schema"; import { filterUndefinedFromObj } from "@/utils/filterUndefinedFromObj"; import { getHeaders } from "@/api/api"; +import { RebalanceMode } from "./schema"; const log = logger.child({ module: "rebalance-api" }); @@ -16,7 +17,7 @@ export async function getRebalancesList( kafkaId: string, params: { name?: string; - mode?: string; + mode?: RebalanceMode[]; status?: RebalanceStatus[]; pageSize?: number; pageCursor?: string; @@ -27,13 +28,16 @@ export async function getRebalancesList( const sp = new URLSearchParams( filterUndefinedFromObj({ "fields[kafkaRebalances]": - "name,namespace,creationTimestamp,status,mode,brokers", + "name,namespace,creationTimestamp,status,mode,brokers,optimizationResult", "filter[name]": params.name ? `like,*${params.name}*` : undefined, "filter[status]": params.status && params.status.length > 0 ? `in,${params.status.join(",")}` : undefined, - "filter[mode]": params.mode ? `like,*${params.mode}*` : undefined, + "filter[mode]": + params.mode && params.mode.length > 0 + ? `in,${params.mode.join(",")}` + : undefined, "page[size]": params.pageSize, "page[after]": params.pageCursor, sort: params.sort diff --git a/ui/api/rebalance/schema.ts b/ui/api/rebalance/schema.ts index dbde02efc..2ba2844a7 100644 --- a/ui/api/rebalance/schema.ts +++ b/ui/api/rebalance/schema.ts @@ -11,22 +11,28 @@ const RebalanceStatusSchema = z.union([ z.literal("ReconciliationPaused"), ]); +const ModeSchema = z.union([ + z.literal("full"), + z.literal("add-brokers"), + z.literal("remove-brokers"), +]); + const OptimizationResultSchema = z.object({ - numIntraBrokerReplicaMovements: z.number(), - numReplicaMovements: z.number(), - onDemandBalancednessScoreAfter: z.number(), - afterBeforeLoadConfigMap: z.string(), - intraBrokerDataToMoveMB: z.number(), - monitoredPartitionsPercentage: z.number(), - provisionRecommendation: z.string(), - excludedBrokersForReplicaMove: z.array(z.string()).nullable(), - excludedBrokersForLeadership: z.array(z.string()).nullable(), - provisionStatus: z.string(), - onDemandBalancednessScoreBefore: z.number(), - recentWindows: z.number(), - dataToMoveMB: z.number(), - excludedTopics: z.array(z.string()).nullable(), - numLeaderMovements: z.number(), + numIntraBrokerReplicaMovements: z.number().optional(), + numReplicaMovements: z.number().optional(), + onDemandBalancednessScoreAfter: z.number().optional(), + afterBeforeLoadConfigMap: z.string().optional(), + intraBrokerDataToMoveMB: z.number().optional(), + monitoredPartitionsPercentage: z.number().optional(), + provisionRecommendation: z.string().optional(), + excludedBrokersForReplicaMove: z.array(z.string()).nullable().optional(), + excludedBrokersForLeadership: z.array(z.string()).nullable().optional(), + provisionStatus: z.string().optional(), + onDemandBalancednessScoreBefore: z.number().optional(), + recentWindows: z.number().optional(), + dataToMoveMB: z.number().optional(), + excludedTopics: z.array(z.string()).nullable().optional(), + numLeaderMovements: z.number().optional(), }); export const RebalanceSchema = z.object({ @@ -43,7 +49,7 @@ export const RebalanceSchema = z.object({ namespace: z.string(), creationTimestamp: z.string(), status: RebalanceStatusSchema, - mode: z.string().optional(), + mode: ModeSchema, brokers: z.array(z.number()).nullable(), sessionId: z.string().nullable(), optimizationResult: OptimizationResultSchema, @@ -67,6 +73,7 @@ const RebalancesListSchema = z.object({ creationTimestamp: true, mode: true, brokers: true, + optimizationResult: true }), }); @@ -91,3 +98,5 @@ export type RebalancesResponse = z.infer; export type RebalanceResponse = z.infer; export type RebalanceStatus = z.infer; + +export type RebalanceMode = z.infer; diff --git a/ui/app/[locale]/(authorized)/kafka/[kafkaId]/@activeBreadcrumb/nodes/page.tsx b/ui/app/[locale]/(authorized)/kafka/[kafkaId]/@activeBreadcrumb/nodes/page.tsx index a483126a0..e548c93ba 100644 --- a/ui/app/[locale]/(authorized)/kafka/[kafkaId]/@activeBreadcrumb/nodes/page.tsx +++ b/ui/app/[locale]/(authorized)/kafka/[kafkaId]/@activeBreadcrumb/nodes/page.tsx @@ -1,5 +1,5 @@ import { BreadcrumbItem } from "@/libs/patternfly/react-core"; -export default function TopicsActiveBreadcrumb() { +export default function NodesActiveBreadcrumb() { return Brokers; } diff --git a/ui/app/[locale]/(authorized)/kafka/[kafkaId]/@activeBreadcrumb/nodes/rebalances/page.tsx b/ui/app/[locale]/(authorized)/kafka/[kafkaId]/@activeBreadcrumb/nodes/rebalances/page.tsx new file mode 100644 index 000000000..35953781e --- /dev/null +++ b/ui/app/[locale]/(authorized)/kafka/[kafkaId]/@activeBreadcrumb/nodes/rebalances/page.tsx @@ -0,0 +1,5 @@ +import { BreadcrumbItem } from "@/libs/patternfly/react-core"; + +export default function RebalanceActiveBreadcrumb() { + return Brokers; +} diff --git a/ui/app/[locale]/(authorized)/kafka/[kafkaId]/@header/nodes/rebalances/page.tsx b/ui/app/[locale]/(authorized)/kafka/[kafkaId]/@header/nodes/rebalances/page.tsx new file mode 100644 index 000000000..eb1a58cea --- /dev/null +++ b/ui/app/[locale]/(authorized)/kafka/[kafkaId]/@header/nodes/rebalances/page.tsx @@ -0,0 +1,87 @@ +import { getKafkaCluster } from "@/api/kafka/actions"; +import { KafkaParams } from "@/app/[locale]/(authorized)/kafka/[kafkaId]/kafka.params"; +import { AppHeader } from "@/components/AppHeader"; +import { Number } from "@/components/Format/Number"; +import { NavItemLink } from "@/components/Navigation/NavItemLink"; +import { + Label, + Nav, + NavList, + PageNavigation, + Spinner, + Split, + SplitItem, +} from "@/libs/patternfly/react-core"; +import { CheckCircleIcon } from "@/libs/patternfly/react-icons"; +import { Suspense } from "react"; + +export default function NodesHeader({ params }: { params: KafkaParams }) { + return ( + } + > + + + ); +} + +async function ConnectedHeader({ params }: { params: KafkaParams }) { + const cluster = await getKafkaCluster(params.kafkaId); + return ( +
+ ); +} + +function Header({ + total, + kafkaId, + cruiseControlEnable, +}: { + total?: number; + kafkaId: string | undefined; + cruiseControlEnable: boolean; +}) { + return ( + + Brokers + + + + + } + navigation={ + + + + } + /> + ); +} diff --git a/ui/app/[locale]/(authorized)/kafka/[kafkaId]/nodes/rebalances/ConnectedRebalancesTable.tsx b/ui/app/[locale]/(authorized)/kafka/[kafkaId]/nodes/rebalances/ConnectedRebalancesTable.tsx index 83236f127..0931ac7e6 100644 --- a/ui/app/[locale]/(authorized)/kafka/[kafkaId]/nodes/rebalances/ConnectedRebalancesTable.tsx +++ b/ui/app/[locale]/(authorized)/kafka/[kafkaId]/nodes/rebalances/ConnectedRebalancesTable.tsx @@ -16,7 +16,11 @@ import { } from "@/libs/patternfly/react-core"; import { useTranslations } from "next-intl"; import { RebalancesCountCard } from "./RebalancesCountCard"; -import { RebalanceList, RebalanceStatus } from "@/api/rebalance/schema"; +import { + RebalanceList, + RebalanceMode, + RebalanceStatus, +} from "@/api/rebalance/schema"; import { ValidationModal } from "./ValidationModal"; import { getRebalanceDetails } from "@/api/rebalance/actions"; import { EmptyStateNoKafkaRebalance } from "./EmptyStateNoKafkaRebalance"; @@ -26,7 +30,7 @@ export type ConnectedReabalancesTableProps = { rebalancesCount: number; page: number; perPage: number; - mode: string | undefined; + mode: RebalanceMode[] | undefined; name: string | undefined; sort: RebalanceTableColumn; sortDir: "asc" | "desc"; @@ -44,7 +48,7 @@ type State = { sort: RebalanceTableColumn; sortDir: "asc" | "desc"; status: RebalanceStatus[] | undefined; - mode: string | undefined; + mode: RebalanceMode[] | undefined; }; export function ConnectedReabalancesTable({ @@ -134,139 +138,135 @@ export function ConnectedReabalancesTable({ return ( - {rebalances?.length === 0 ? ( - - ) : ( - - - {isAlertVisible && ( - } - actionLinks={ - - {t("learn_more_about_cruisecontrol_enablement")} - - } - /> - )} - - - + + {isAlertVisible && ( + } + actionLinks={ + + {t("learn_more_about_cruisecontrol_enablement")} + + } /> - - - { - startTransition(() => { - const pageDiff = newPage - page; - switch (pageDiff) { - case -1: - updateUrl({ perPage, page: prevPageCursor }); - break; - case 1: - updateUrl({ perPage, page: nextPageCursor }); - break; - default: - updateUrl({ perPage }); - break; - } - addOptimistic({ perPage }); - }); - }} - rebalanceList={state.rebalances} - rebalancesCount={rebalancesCount} - isColumnSortable={(col) => { - if (!RebalanceTableColumns.includes(col)) { - return undefined; + )} + + + + + + { + startTransition(() => { + const pageDiff = newPage - page; + switch (pageDiff) { + case -1: + updateUrl({ perPage, page: prevPageCursor }); + break; + case 1: + updateUrl({ perPage, page: nextPageCursor }); + break; + default: + updateUrl({ perPage }); + break; } - const activeIndex = RebalanceTableColumns.indexOf(state.sort); - const columnIndex = RebalanceTableColumns.indexOf(col); - return { - label: col as string, - columnIndex, - onSort: () => { - startTransition(() => { - const newSortDir = - activeIndex === columnIndex - ? state.sortDir === "asc" - ? "desc" - : "asc" - : "asc"; - updateUrl({ - sort: col, - sortDir: newSortDir, - }); - addOptimistic({ sort: col, sortDir: newSortDir }); + addOptimistic({ perPage }); + }); + }} + onClearAllFilters={clearFilters} + rebalanceList={state.rebalances} + rebalancesCount={rebalancesCount} + isColumnSortable={(col) => { + if (!RebalanceTableColumns.includes(col)) { + return undefined; + } + const activeIndex = RebalanceTableColumns.indexOf(state.sort); + const columnIndex = RebalanceTableColumns.indexOf(col); + return { + label: col as string, + columnIndex, + onSort: () => { + startTransition(() => { + const newSortDir = + activeIndex === columnIndex + ? state.sortDir === "asc" + ? "desc" + : "asc" + : "asc"; + updateUrl({ + sort: col, + sortDir: newSortDir, }); - }, - sortBy: { - index: activeIndex, - direction: state.sortDir, - defaultDirection: "asc", - }, - isFavorites: undefined, - }; - }} - filterName={state.name} - onFilterNameChange={(name) => { - startTransition(() => { - updateUrl({ name }); - addOptimistic({ name }); - }); - }} - filterMode={state.mode} - onFilterModeChange={(mode) => { - startTransition(() => { - updateUrl({ mode }); - addOptimistic({ mode }); - }); - }} - filterStatus={state.status} - onFilterStatusChange={(status) => { - startTransition(() => { - updateUrl({ status }); - addOptimistic({ status }); - }); - }} - onClearAllFilters={clearFilters} - onApprove={(row) => { - setModalOpen(true); - setApprovalStatus("approve"); - setRebalanceId(row.id); - }} - onRefresh={(row) => { - setModalOpen(true); - setApprovalStatus("refresh"); - setRebalanceId(row.id); - }} - onStop={(row) => { - setModalOpen(true); - setApprovalStatus("stop"); - setRebalanceId(row.id); - }} - baseurl={baseurl} - /> - - {isModalOpen && ( - setModalOpen(false)} - /> - )} - - )} + addOptimistic({ sort: col, sortDir: newSortDir }); + }); + }, + sortBy: { + index: activeIndex, + direction: state.sortDir, + defaultDirection: "asc", + }, + isFavorites: undefined, + }; + }} + filterName={state.name} + onFilterNameChange={(name) => { + startTransition(() => { + updateUrl({ name }); + addOptimistic({ name }); + }); + }} + filterMode={state.mode} + onFilterModeChange={(mode) => { + startTransition(() => { + updateUrl({ mode }); + addOptimistic({ mode }); + }); + }} + filterStatus={state.status} + onFilterStatusChange={(status) => { + startTransition(() => { + updateUrl({ status }); + addOptimistic({ status }); + }); + }} + onApprove={(row) => { + setModalOpen(true); + setApprovalStatus("approve"); + setRebalanceId(row.id); + }} + onRefresh={(row) => { + setModalOpen(true); + setApprovalStatus("refresh"); + setRebalanceId(row.id); + }} + onStop={(row) => { + setModalOpen(true); + setApprovalStatus("stop"); + setRebalanceId(row.id); + }} + baseurl={baseurl} + /> + + {isModalOpen && ( + setModalOpen(false)} + /> + )} + ); } diff --git a/ui/app/[locale]/(authorized)/kafka/[kafkaId]/nodes/rebalances/RebalanceTable.tsx b/ui/app/[locale]/(authorized)/kafka/[kafkaId]/nodes/rebalances/RebalanceTable.tsx index 4493ec5a2..7bad53ed9 100644 --- a/ui/app/[locale]/(authorized)/kafka/[kafkaId]/nodes/rebalances/RebalanceTable.tsx +++ b/ui/app/[locale]/(authorized)/kafka/[kafkaId]/nodes/rebalances/RebalanceTable.tsx @@ -1,6 +1,10 @@ import { TableView, TableViewProps } from "@/components/Table"; import { useTranslations } from "next-intl"; -import { RebalanceList, RebalanceStatus } from "@/api/rebalance/schema"; +import { + RebalanceList, + RebalanceMode, + RebalanceStatus, +} from "@/api/rebalance/schema"; import { Flex, FlexItem, @@ -20,6 +24,8 @@ import { } from "@/libs/patternfly/react-icons"; import Link from "next/link"; import React, { ReactNode } from "react"; +import { EmptyStateNoMatchFound } from "@/components/Table/EmptyStateNoMatchFound"; +import { EmptyStateNoKafkaRebalance } from "./EmptyStateNoKafkaRebalance"; export const RebalanceTableColumns = ["name", "status", "createdAt"] as const; @@ -92,6 +98,12 @@ const StatusLabel: Record = { ), }; +const ModeLabel: Record = { + full: <>full, + "add-brokers": <>add-brokers, + "remove-brokers": <>remove-brokers, +}; + export type RebalanceTabelProps = { baseurl: string; rebalanceList: RebalanceList[] | undefined; @@ -103,9 +115,9 @@ export type RebalanceTabelProps = { onRefresh: (rebalanceName: RebalanceList) => void; filterName: string | undefined; filterStatus: RebalanceStatus[] | undefined; - filterMode: string | undefined; + filterMode: RebalanceMode[] | undefined; onFilterNameChange: (name: string | undefined) => void; - onFilterModeChange: (mode: string | undefined) => void; + onFilterModeChange: (mode: RebalanceMode[] | undefined) => void; onFilterStatusChange: (status: RebalanceStatus[] | undefined) => void; } & Pick< TableViewProps, @@ -128,6 +140,7 @@ export function RebalanceTable({ perPage, onPageChange, onRefresh, + onClearAllFilters, }: RebalanceTabelProps) { const t = useTranslations("Rebalancing"); return ( @@ -140,8 +153,11 @@ export function RebalanceTable({ columns={RebalanceTableColumns} isColumnSortable={isColumnSortable} data={rebalanceList} - emptyStateNoData={<>} - emptyStateNoResults={<>} + emptyStateNoData={} + emptyStateNoResults={ + + } + onClearAllFilters={onClearAllFilters} ariaLabel={"Rebalance brokers"} renderHeader={({ Th, column, key }) => { switch (column) { @@ -209,19 +225,28 @@ export function RebalanceTable({ ]} /> )} + isFiltered={ + filterName !== undefined || + filterStatus?.length !== 0 || + filterMode?.length !== 0 + } getExpandedRow={({ row }) => { return ( - - - + + + {t("mode")} - {row.attributes.mode} + {ModeLabel[row.attributes.mode]} - + {t("auto_approval_enabled")} @@ -231,16 +256,19 @@ export function RebalanceTable({ - + {t("brokers")} - {row.attributes.brokers?.join(",")} + {!row.attributes.brokers || + row.attributes.brokers.length === 0 + ? "N/A" + : row.attributes.brokers.join(",")} - - + + ); }} filters={{ @@ -276,17 +304,22 @@ export function RebalanceTable({ options: StatusLabel, }, mode: { - type: "search", - chips: filterMode ? [filterMode] : [], - onSearch: onFilterModeChange, - onRemoveChip: () => { - onFilterModeChange(undefined); + type: "checkbox", + chips: filterMode || [], + onToggle: (mode) => { + const newMode = filterMode?.includes(mode) + ? filterMode.filter((m) => m !== mode) + : [...filterMode!, mode]; + onFilterModeChange(newMode); + }, + onRemoveChip: (mode) => { + const newMode = (filterMode || []).filter((m) => m !== mode); + onFilterModeChange(newMode); }, onRemoveGroup: () => { onFilterModeChange(undefined); }, - validate: (value) => true, - errorMessage: "At least 3 characters", + options: ModeLabel, }, }} /> diff --git a/ui/app/[locale]/(authorized)/kafka/[kafkaId]/nodes/rebalances/RebalancesCountCard.stories.tsx b/ui/app/[locale]/(authorized)/kafka/[kafkaId]/nodes/rebalances/RebalancesCountCard.stories.tsx new file mode 100644 index 000000000..01dd4b738 --- /dev/null +++ b/ui/app/[locale]/(authorized)/kafka/[kafkaId]/nodes/rebalances/RebalancesCountCard.stories.tsx @@ -0,0 +1,19 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { RebalancesCountCard } from "./RebalancesCountCard"; + +const meta: Meta = { + component: RebalancesCountCard, +}; + +export default meta; +type Story = StoryObj; + +export const stopRebalance: Story = { + args: { + TotalRebalancing: 10, + proposalReady: 2, + rebalancing: 5, + ready: 0, + stopped: 1, + }, +}; diff --git a/ui/app/[locale]/(authorized)/kafka/[kafkaId]/nodes/rebalances/RebalancesCountCard.tsx b/ui/app/[locale]/(authorized)/kafka/[kafkaId]/nodes/rebalances/RebalancesCountCard.tsx index 5c493c3cd..62818b16d 100644 --- a/ui/app/[locale]/(authorized)/kafka/[kafkaId]/nodes/rebalances/RebalancesCountCard.tsx +++ b/ui/app/[locale]/(authorized)/kafka/[kafkaId]/nodes/rebalances/RebalancesCountCard.tsx @@ -7,8 +7,8 @@ import { DescriptionListGroup, DescriptionListTerm, Divider, - Level, - LevelItem, + Flex, + FlexItem, } from "@/libs/patternfly/react-core"; import { useTranslations } from "next-intl"; @@ -30,8 +30,8 @@ export function RebalancesCountCard({ - - + + {t("total_rebalances")} @@ -40,60 +40,73 @@ export function RebalancesCountCard({ {TotalRebalancing} - + - - - {t("proposal_ready")} - - {proposalReady} - - - + + + + + {t("proposal_ready")} + + + {proposalReady} + + + + - - - {t("rebalancing")} - - {rebalancing} - - - + + + + {t("rebalancing")} + + {rebalancing} + + + + - - - {t("ready")} - - {ready} - - - + + + + {t("ready")} + + {ready} + + + + - - - {t("stopped")} - - {stopped} - - - - + + + + + {" "} + {t("stopped")} + + + {stopped} + + + + + diff --git a/ui/app/[locale]/(authorized)/kafka/[kafkaId]/nodes/rebalances/[rebalanceId]/OptimizationProposal.tsx b/ui/app/[locale]/(authorized)/kafka/[kafkaId]/nodes/rebalances/[rebalanceId]/OptimizationProposal.tsx index 57764e36f..448336daa 100644 --- a/ui/app/[locale]/(authorized)/kafka/[kafkaId]/nodes/rebalances/[rebalanceId]/OptimizationProposal.tsx +++ b/ui/app/[locale]/(authorized)/kafka/[kafkaId]/nodes/rebalances/[rebalanceId]/OptimizationProposal.tsx @@ -28,20 +28,20 @@ export function OptimizationProposal({ sessionId, baseurl, }: { - numIntraBrokerReplicaMovements: number; - numReplicaMovements: number; - onDemandBalancednessScoreAfter: number; - intraBrokerDataToMoveMB: number; - monitoredPartitionsPercentage: number; + numIntraBrokerReplicaMovements: number | undefined; + numReplicaMovements: number | undefined; + onDemandBalancednessScoreAfter: number | undefined; + intraBrokerDataToMoveMB: number | undefined; + monitoredPartitionsPercentage: number | undefined; excludedBrokersForReplicaMove: string[] | null | undefined; excludedBrokersForLeadership: string[] | null | undefined; - onDemandBalancednessScoreBefore: number; - recentWindows: number; - dataToMoveMB: number; + onDemandBalancednessScoreBefore: number | undefined; + recentWindows: number | undefined; + dataToMoveMB: number | undefined; excludedTopics: string[] | null | undefined; - numLeaderMovements: number; - isModalOpen: boolean; - sessionId: string | null | undefined; + numLeaderMovements: number | undefined; + isModalOpen: boolean; + sessionId: string | null | undefined; baseurl: string; }) { const t = useTranslations("Rebalancing"); @@ -79,7 +79,7 @@ export function OptimizationProposal({ {t("data_to_move")} - {dataToMoveMB} + {dataToMoveMB || 0} {" MB"} @@ -88,7 +88,10 @@ export function OptimizationProposal({ {t("excluded_brokers_for_leadership")} - {excludedBrokersForLeadership} + {excludedBrokersForLeadership && + excludedBrokersForLeadership.length > 0 + ? excludedBrokersForLeadership.join(", ") + : "-"} @@ -96,13 +99,18 @@ export function OptimizationProposal({ {t("excluded_brokers_for_replica_move")} - {excludedBrokersForReplicaMove} + {excludedBrokersForReplicaMove && + excludedBrokersForReplicaMove.length > 0 + ? excludedBrokersForReplicaMove.join(", ") + : "-"} {t("excluded_topics")} - {excludedTopics} + {excludedTopics && excludedTopics.length > 0 + ? excludedTopics.join(", ") + : "-"} @@ -110,7 +118,7 @@ export function OptimizationProposal({ {t("intra_broker_data_to_move")} - {intraBrokerDataToMoveMB} + {intraBrokerDataToMoveMB || 0} @@ -118,7 +126,7 @@ export function OptimizationProposal({ {t("monitored_partitions_percentage")} - {monitoredPartitionsPercentage} + {monitoredPartitionsPercentage || 0} @@ -126,13 +134,13 @@ export function OptimizationProposal({ {t("num_intra_broker_replica_movements")} - {numIntraBrokerReplicaMovements} + {numIntraBrokerReplicaMovements || 0} {t("num_leader_movements")} - {numLeaderMovements} + {numLeaderMovements || 0} @@ -140,7 +148,7 @@ export function OptimizationProposal({ {t("num_replica_movements")} - {numReplicaMovements} + {numReplicaMovements || 0} @@ -148,7 +156,7 @@ export function OptimizationProposal({ {t("on_demand_balancedness_score_after")} - {onDemandBalancednessScoreAfter} + {onDemandBalancednessScoreAfter || 0} @@ -156,18 +164,20 @@ export function OptimizationProposal({ {t("on_demand_balancedness_Score_before")} - {onDemandBalancednessScoreBefore} + {onDemandBalancednessScoreBefore || 0} {t("recent_windows")} - {recentWindows} + {recentWindows || 0} {t("session_id")} - {sessionId} + + {sessionId ?? "-"} + diff --git a/ui/app/[locale]/(authorized)/kafka/[kafkaId]/nodes/rebalances/page.tsx b/ui/app/[locale]/(authorized)/kafka/[kafkaId]/nodes/rebalances/page.tsx index d9b046e93..15d0acf10 100644 --- a/ui/app/[locale]/(authorized)/kafka/[kafkaId]/nodes/rebalances/page.tsx +++ b/ui/app/[locale]/(authorized)/kafka/[kafkaId]/nodes/rebalances/page.tsx @@ -5,7 +5,7 @@ import { stringToInt } from "@/utils/stringToInt"; import { Suspense } from "react"; import { ConnectedReabalancesTable } from "./ConnectedRebalancesTable"; import { getRebalancesList } from "@/api/rebalance/actions"; -import { RebalanceStatus } from "@/api/rebalance/schema"; +import { RebalanceMode, RebalanceStatus } from "@/api/rebalance/schema"; export const dynamic = "force-dynamic"; const sortMap: Record<(typeof RebalanceTableColumns)[number], string> = { @@ -31,7 +31,9 @@ export default function RebalancesPage({ }; }) { const name = searchParams["name"]; - const mode = searchParams["mode"]; + const mode = (searchParams["mode"] || "") + .split(",") + .filter((v) => !!v) as RebalanceMode[] | undefined;; const pageSize = stringToInt(searchParams.perPage) || 20; const sort = (searchParams["sort"] || "name") as RebalanceTableColumn; const sortDir = (searchParams["sortDir"] || "asc") as "asc" | "desc"; @@ -90,7 +92,7 @@ async function AsyncReabalanceTable({ pageSize: number; pageCursor: string | undefined; status: RebalanceStatus[] | undefined; - mode: string | undefined; + mode: RebalanceMode[] | undefined; } & KafkaParams) { const rebalance = await getRebalancesList(kafkaId, { name,