Skip to content

Commit

Permalink
Align the topics page to the designs (#207)
Browse files Browse the repository at this point in the history
* Align the topics page to the designs

Update the Topics page header to be in sync with the designs. The warning icon will always show zero as for the moment we don't have the count of topics that are in-sync and under replicated.

Connect the search input to search in the topic names.

Add a switch to show/hide internal topics (hide is the default).
not in sync. Part of #165.

Add a status column to show the in-sync or under replicated state of a topic. This is done by looking for a replica amongs all partitions that hold the topic that is flagged as not in sync.

Reorder the columns.

Change the "Edit properties" action to "Edit configuration". Part of #142.

Change the column name "Messages" to "Record count".

Add the right empty states for when there are no topics and for when a search doesn't produce results.

* Add an option to search by topic id

* Update icons

* Minor tweaks
  • Loading branch information
riccardo-forina authored Nov 22, 2023
1 parent 82fcc5f commit 2930196
Show file tree
Hide file tree
Showing 12 changed files with 466 additions and 102 deletions.
14 changes: 11 additions & 3 deletions ui/api/topics/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,24 @@ const log = logger.child({ module: "topics-api" });
export async function getTopics(
kafkaId: string,
params: {
name?: string;
id?: string;
pageSize?: number;
pageCursor?: string;
sort?: string;
sortDir?: string;
includeHidden?: boolean;
},
): Promise<TopicsResponse> {
const sp = new URLSearchParams(
filterUndefinedFromObj({
"fields[topics]":
"name,visibility,partitions,recordCount,totalLeaderLogBytes,consumerGroups",
"filter[id]": params.id ? `eq,${params.id}` : undefined,
"filter[name]": params.name ? `like,*${params.name}*` : undefined,
"filter[visibility]": params.includeHidden
? "in,external,internal"
: "eq,external",
"page[size]": params.pageSize,
"page[after]": params.pageCursor,
sort: params.sort
Expand All @@ -50,7 +58,7 @@ export async function getTopics(
});
log.debug({ url }, "getTopics");
const rawData = await res.json();
log.trace({ url, rawData }, "getTopics response");
log.debug({ url, rawData }, "getTopics response");
return TopicsResponseSchema.parse(rawData);
}

Expand Down Expand Up @@ -102,7 +110,7 @@ export async function createTopic(
const rawData = await res.json();
log.debug({ url, rawData }, "createTopic response");
const response = TopicCreateResponseSchema.parse(rawData);
log.trace(response, "createTopic response parsed");
log.debug(response, "createTopic response parsed");
if (validateOnly === false) {
revalidateTag("topics");
}
Expand Down Expand Up @@ -134,7 +142,7 @@ export async function updateTopic(
method: "PATCH",
body: JSON.stringify(body),
});
log.trace({ status: res.status }, "updateTopic response");
log.debug({ status: res.status }, "updateTopic response");
try {
if (res.status === 204) {
revalidateTag(`topic-${topicId}`);
Expand Down
2 changes: 1 addition & 1 deletion ui/api/topics/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export const TopicsResponseSchema = z.object({
meta: z.object({
page: z.object({
total: z.number(),
pageNumber: z.number(),
pageNumber: z.number().optional(),
}),
}),
links: z.object({
Expand Down
96 changes: 93 additions & 3 deletions ui/app/[locale]/kafka/[kafkaId]/@header/topics/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,94 @@
import { KafkaHeader } from "@/app/[locale]/kafka/[kafkaId]/@header/KafkaHeader";
import { getTopics } from "@/api/topics/actions";
import { KafkaParams } from "@/app/[locale]/kafka/[kafkaId]/kafka.params";
import { AppHeader } from "@/components/AppHeader";
import { Number } from "@/components/Number";
import {
Label,
Spinner,
Split,
SplitItem,
Tooltip,
} from "@/libs/patternfly/react-core";
import {
CheckCircleIcon,
ExclamationTriangleIcon,
} from "@/libs/patternfly/react-icons";
import { Suspense } from "react";

export { fetchCache } from "@/app/[locale]/kafka/[kafkaId]/@header/KafkaHeader";
export default KafkaHeader;
export default function TopicsHeader({ params }: { params: KafkaParams }) {
return (
<Suspense fallback={<Header />}>
<ConnectedHeader params={params} />
</Suspense>
);
}

async function ConnectedHeader({ params }: { params: KafkaParams }) {
const topics = await getTopics(params.kafkaId, {});
return (
<Header
total={topics.meta.page.total}
ok={topics.meta.page.total}
warning={0}
/>
);
}

function Header({
total,
ok,
warning,
}: {
total?: number;
ok?: number;
warning?: number;
}) {
return (
<AppHeader
title={
<Split hasGutter={true}>
<SplitItem>Topics</SplitItem>
<SplitItem>
<Label
icon={total === undefined ? <Spinner size={"sm"} /> : undefined}
>
{total !== undefined && <Number value={total} />}&nbsp;total
</Label>
</SplitItem>
<SplitItem>
<Tooltip content={"Number of topics in sync"}>
<Label
icon={
ok === undefined ? (
<Spinner size={"sm"} />
) : (
<CheckCircleIcon />
)
}
color={"cyan"}
>
{ok !== undefined && <Number value={ok} />}
</Label>
</Tooltip>
</SplitItem>
<SplitItem>
<Tooltip content={"Number of topics under replicated"}>
<Label
icon={
warning === undefined ? (
<Spinner size={"sm"} />
) : (
<ExclamationTriangleIcon />
)
}
color={"orange"}
>
{warning !== undefined && <Number value={warning} />}
</Label>
</Tooltip>
</SplitItem>
</Split>
}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ButtonLink } from "@/components/ButtonLink";
import {
EmptyState,
EmptyStateActions,
EmptyStateBody,
EmptyStateFooter,
EmptyStateHeader,
EmptyStateIcon,
} from "@/libs/patternfly/react-core";
import { PlusCircleIcon } from "@patternfly/react-icons";

export function EmptyStateNoTopics({ createHref }: { createHref: string }) {
return (
<EmptyState>
<EmptyStateHeader
titleText="No topics"
headingLevel="h4"
icon={<EmptyStateIcon icon={PlusCircleIcon} />}
/>
<EmptyStateBody>To get started, create your first topic </EmptyStateBody>
<EmptyStateFooter>
<EmptyStateActions>
<ButtonLink variant="primary" href={createHref}>
Create a topic
</ButtonLink>
</EmptyStateActions>
</EmptyStateFooter>
</EmptyState>
);
}
Loading

0 comments on commit 2930196

Please sign in to comment.