Skip to content

Commit cd9dadc

Browse files
authored
New breadcrumbs for IPAM pages (#7576)
1 parent 6580250 commit cd9dadc

File tree

6 files changed

+143
-29
lines changed

6 files changed

+143
-29
lines changed

frontend/app/src/app/router.tsx

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import type { BreadcrumbItem } from "@/shared/components/layout/breadcrumb-navig
1919

2020
import { RequireAuth } from "@/entities/authentication/ui/require-auth";
2121
import { BranchesProvider } from "@/entities/branches/ui/branches-provider";
22-
import { constructPathForIpam } from "@/entities/ipam/utils";
2322
import { RESOURCE_GENERIC_KIND } from "@/entities/resource-manager/constants";
2423
import { SchemaProvider } from "@/entities/schema/ui/providers/schema-provider";
2524

@@ -383,27 +382,9 @@ export const router = createBrowserRouter([
383382
},
384383
{
385384
path: "ipam",
386-
handle: {
387-
breadcrumb: () => {
388-
return {
389-
type: "link",
390-
label: "IP Address Manager",
391-
to: constructPathForIpam("/ipam"),
392-
} as BreadcrumbItem;
393-
},
394-
},
395385
children: [
396386
{
397387
path: "namespaces",
398-
handle: {
399-
breadcrumb: () => {
400-
return {
401-
type: "link",
402-
label: "namespaces",
403-
to: constructPath("/ipam/namespaces"),
404-
} satisfies BreadcrumbItem;
405-
},
406-
},
407388
children: [
408389
{
409390
index: true,
@@ -426,15 +407,6 @@ export const router = createBrowserRouter([
426407
{
427408
path: "ipam",
428409
lazy: () => import("@/pages/ipam/ipam-layout"),
429-
handle: {
430-
breadcrumb: () => {
431-
return {
432-
type: "link",
433-
label: "IP Address Manager",
434-
to: constructPathForIpam("/ipam"),
435-
} as BreadcrumbItem;
436-
},
437-
},
438410
children: [
439411
{
440412
index: true,

frontend/app/src/entities/ipam/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ export const IP_ADDRESS_AVAILABLE_KIND = "InternalIPRangeAvailable" as const;
88
export const IP_PREFIX_GENERIC = "BuiltinIPPrefix";
99
export const IP_PREFIX_AVAILABLE_KIND = "InternalIPPrefixAvailable";
1010

11+
export const IP_PREFIX_RELATIONSHIP_NAME = "ip_prefix";
12+
1113
export const TREE_ROOT_ID = "root" as const;
1214

1315
export const IPAM_QSP = {

frontend/app/src/entities/ipam/ip-addresses/utils/get-ip-address-relationships-visible-in-list-view.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
import { IP_PREFIX_RELATIONSHIP_NAME } from "@/entities/ipam/constants";
12
import { getRelationshipsVisibleInListView } from "@/entities/nodes/object/utils/get-relationships-visible-in-list-view";
23
import type { RelationshipSchema } from "@/entities/schema/types";
34

45
export function getIpAddressRelationshipsVisibleInListView(
56
relationships: Array<RelationshipSchema>
67
): Array<RelationshipSchema> {
78
const ipPrefixRelationshipSchema = relationships.find(
8-
(relationship) => relationship.name === "ip_prefix"
9+
(relationship) => relationship.name === IP_PREFIX_RELATIONSHIP_NAME
910
);
1011

1112
const otherRelationshipSchema = getRelationshipsVisibleInListView(relationships);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { BreadcrumbIpamBase } from "@/shared/components/layout/breadcrumb-navigation/breadcrumb-ipam";
2+
import { BreadcrumbObjects } from "@/shared/components/layout/breadcrumb-navigation/breadcrumb-objects";
3+
import { BreadcrumbItemSchema } from "@/shared/components/layout/breadcrumb-navigation/items/breadcrumb-item-schema";
4+
5+
import { IP_NAMESPACE_GENERIC } from "@/entities/ipam/constants";
6+
import { useSchema } from "@/entities/schema/ui/hooks/useSchema";
7+
8+
export function BreadcrumbIpNamespaces() {
9+
const { schema } = useSchema(IP_NAMESPACE_GENERIC);
10+
11+
if (!schema) return null;
12+
13+
return (
14+
<BreadcrumbIpamBase>
15+
<BreadcrumbItemSchema schema={schema} />
16+
<BreadcrumbObjects />
17+
</BreadcrumbIpamBase>
18+
);
19+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { keepPreviousData } from "@tanstack/react-query";
2+
import type React from "react";
3+
import { useParams } from "react-router";
4+
5+
import { BreadcrumbObjectDetailsHierarchy } from "@/shared/components/layout/breadcrumb-navigation/breadcrumb-object-details-hierarchy";
6+
import { BreadcrumbItemObject } from "@/shared/components/layout/breadcrumb-navigation/items/breadcrumb-item-object";
7+
import {
8+
Breadcrumb,
9+
BreadcrumbError,
10+
BreadcrumbItem,
11+
BreadcrumbLoading,
12+
} from "@/shared/components/ui/breadcrumb";
13+
14+
import {
15+
IP_ADDRESS_GENERIC,
16+
IP_PREFIX_GENERIC,
17+
IP_PREFIX_RELATIONSHIP_NAME,
18+
} from "@/entities/ipam/constants";
19+
import { constructPathForIpam } from "@/entities/ipam/utils";
20+
import { useGetObject } from "@/entities/nodes/object/domain/get-object.query";
21+
import type { NodeRelationshipOne } from "@/entities/nodes/types";
22+
import type { ModelSchema } from "@/entities/schema/types";
23+
import { useSchema } from "@/entities/schema/ui/hooks/useSchema";
24+
import { isOfKind } from "@/entities/schema/utils/is-of-kind";
25+
26+
export function BreadcrumbIpam() {
27+
const { objectKind, objectId } = useParams();
28+
const { schema } = useSchema(objectKind);
29+
30+
if (!schema || !objectId) return <BreadcrumbIpamBase />;
31+
32+
return (
33+
<BreadcrumbIpamBase>
34+
<BreadcrumbIpamContent objectSchema={schema} objectId={objectId} />
35+
</BreadcrumbIpamBase>
36+
);
37+
}
38+
39+
export function BreadcrumbIpamBase({ children }: { children?: React.ReactNode }) {
40+
return (
41+
<Breadcrumb data-testid="breadcrumb-ipam">
42+
<BreadcrumbItem href={constructPathForIpam("/ipam")}>IP Address Manager</BreadcrumbItem>
43+
{children}
44+
</Breadcrumb>
45+
);
46+
}
47+
48+
interface BreadcrumbIpamContentProps {
49+
objectSchema: ModelSchema;
50+
objectId: string;
51+
}
52+
53+
function BreadcrumbIpamContent({ objectSchema, objectId }: BreadcrumbIpamContentProps) {
54+
if (isOfKind(IP_PREFIX_GENERIC, objectSchema)) {
55+
return <BreadcrumbObjectDetailsHierarchy objectSchema={objectSchema} objectId={objectId} />;
56+
}
57+
58+
if (isOfKind(IP_ADDRESS_GENERIC, objectSchema)) {
59+
return <BreadcrumbIpAddress ipAddressSchema={objectSchema} ipAddressId={objectId} />;
60+
}
61+
62+
return null;
63+
}
64+
65+
interface BreadcrumbIpAddressProps {
66+
ipAddressSchema: ModelSchema;
67+
ipAddressId: string;
68+
}
69+
70+
export function BreadcrumbIpAddress({ ipAddressSchema, ipAddressId }: BreadcrumbIpAddressProps) {
71+
const { schema: ipPrefixSchema } = useSchema(IP_PREFIX_GENERIC);
72+
const { data, isPending, error } = useGetObject(
73+
{
74+
objectSchema: ipAddressSchema,
75+
objectId: ipAddressId,
76+
},
77+
{
78+
placeholderData: keepPreviousData,
79+
}
80+
);
81+
82+
if (isPending) {
83+
return <BreadcrumbLoading />;
84+
}
85+
86+
if (error) {
87+
return <BreadcrumbError error={error} />;
88+
}
89+
90+
const ipPrefixRelationshipSchema = ipAddressSchema.relationships?.find(
91+
({ name }) => name === IP_PREFIX_RELATIONSHIP_NAME
92+
);
93+
const ipPrefixNode = (data.ip_prefix as NodeRelationshipOne | undefined)?.node;
94+
95+
return (
96+
<>
97+
{ipPrefixSchema && ipPrefixNode && (
98+
<BreadcrumbObjectDetailsHierarchy
99+
objectSchema={ipPrefixSchema}
100+
objectId={ipPrefixNode.id}
101+
/>
102+
)}
103+
<BreadcrumbItemObject
104+
node={data}
105+
parentId={ipPrefixNode?.id}
106+
parentRelationshipSchema={ipPrefixRelationshipSchema}
107+
/>
108+
</>
109+
);
110+
}

frontend/app/src/shared/components/layout/breadcrumb-navigation/breadcrumb-navigation.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import React from "react";
22
import { matchPath, type UIMatch, useLocation, useMatches, useParams } from "react-router";
33

44
import { BreadcrumbBranches } from "@/shared/components/layout/breadcrumb-navigation/breadcrumb-branches";
5+
import { BreadcrumbIpNamespaces } from "@/shared/components/layout/breadcrumb-navigation/breadcrumb-ip-namespaces";
6+
import { BreadcrumbIpam } from "@/shared/components/layout/breadcrumb-navigation/breadcrumb-ipam";
57
import { BreadcrumbObjects } from "@/shared/components/layout/breadcrumb-navigation/breadcrumb-objects";
68
import {
79
BreadcrumbDynamicElement,
@@ -23,6 +25,14 @@ export default function BreadcrumbNavigation() {
2325
return <BreadcrumbBranches />;
2426
}
2527

28+
if (matchPath({ path: "/ipam/namespaces", end: false }, pathname)) {
29+
return <BreadcrumbIpNamespaces />;
30+
}
31+
32+
if (matchPath({ path: "/ipam", end: false }, pathname)) {
33+
return <BreadcrumbIpam />;
34+
}
35+
2636
if (objectKind) {
2737
return <BreadcrumbObjects />;
2838
}

0 commit comments

Comments
 (0)