Skip to content

Commit c8aff8f

Browse files
committed
feat: add /view/config and /view/views iframe embed routes
- Remove /view/topology/:id embed route and TopologyCard page - Add /view/config/:id/[changes|relationships|access|playbooks] routes without sidebar - Add /view/views/:id and /view/views/:namespace/:name routes without sidebar - Parameterise useConfigDetailsTabs with baseRoute (default /catalog) so tab links stay within the embed layout via ConfigDetailsBaseRouteContext
1 parent f5f9c44 commit c8aff8f

File tree

8 files changed

+223
-113
lines changed

8 files changed

+223
-113
lines changed

src/App.tsx

Lines changed: 84 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import {
4040
schemaResourceTypes
4141
} from "./components/SchemaResourcePage/resourceTypes";
4242
import { ConfigPageContextProvider } from "./context/ConfigPageContext";
43+
import { ConfigDetailsBaseRouteProvider } from "./components/Configs/ConfigDetailsBaseRouteContext";
4344
import { useFeatureFlagsContext } from "./context/FeatureFlagsContext";
4445
import { HealthPageContextProvider } from "./context/HealthPageContext";
4546
import { IncidentPageContextProvider } from "./context/IncidentPageContext";
@@ -61,12 +62,6 @@ const TopologyPage = dynamic(
6162
import("@flanksource-ui/pages/TopologyPage").then((mod) => mod.TopologyPage)
6263
);
6364

64-
const TopologyCardPage = dynamic(
65-
import("@flanksource-ui/pages/TopologyCard").then(
66-
(mod) => mod.TopologyCardPage
67-
)
68-
);
69-
7065
const IncidentDetailsPage = dynamic(
7166
import("@flanksource-ui/pages/incident/IncidentDetails").then(
7267
(mod) => mod.IncidentDetailsPage
@@ -495,10 +490,7 @@ export function HealthRoutes({ sidebar }: { sidebar: ReactNode }) {
495490
export function IncidentManagerRoutes({ sidebar }: { sidebar: ReactNode }) {
496491
const { featureFlagsLoaded } = useFeatureFlagsContext();
497492

498-
if (
499-
!featureFlagsLoaded &&
500-
!window.location.pathname.startsWith("/view/topology")
501-
) {
493+
if (!featureFlagsLoaded) {
502494
return <FullPageSkeletonLoader />;
503495
}
504496

@@ -508,17 +500,94 @@ export function IncidentManagerRoutes({ sidebar }: { sidebar: ReactNode }) {
508500
<Route index element={<HomepageRedirect />} />
509501
</Route>
510502

503+
<Route path="/embed/health" element={<HealthPage url={CANARY_API} />} />
504+
505+
{/* Config embed routes — no sidebar */}
506+
<Route
507+
path="/embed/config/:id"
508+
element={
509+
<ConfigDetailsBaseRouteProvider baseRoute="/embed/config" embedded>
510+
{withAuthorizationAccessCheck(
511+
<ConfigDetailsPage />,
512+
tables.database,
513+
"read",
514+
true
515+
)}
516+
</ConfigDetailsBaseRouteProvider>
517+
}
518+
/>
519+
<Route
520+
path="/embed/config/:id/changes"
521+
element={
522+
<ConfigDetailsBaseRouteProvider baseRoute="/embed/config" embedded>
523+
{withAuthorizationAccessCheck(
524+
<ConfigDetailsChangesPage />,
525+
tables.database,
526+
"read",
527+
true
528+
)}
529+
</ConfigDetailsBaseRouteProvider>
530+
}
531+
/>
532+
<Route
533+
path="/embed/config/:id/relationships"
534+
element={
535+
<ConfigDetailsBaseRouteProvider baseRoute="/embed/config" embedded>
536+
{withAuthorizationAccessCheck(
537+
<ConfigDetailsRelationshipsPage />,
538+
tables.database,
539+
"read",
540+
true
541+
)}
542+
</ConfigDetailsBaseRouteProvider>
543+
}
544+
/>
545+
<Route
546+
path="/embed/config/:id/access"
547+
element={
548+
<ConfigDetailsBaseRouteProvider baseRoute="/embed/config" embedded>
549+
{withAuthorizationAccessCheck(
550+
<ConfigDetailsAccessPage />,
551+
tables.database,
552+
"read",
553+
true
554+
)}
555+
</ConfigDetailsBaseRouteProvider>
556+
}
557+
/>
558+
<Route
559+
path="/embed/config/:id/playbooks"
560+
element={
561+
<ConfigDetailsBaseRouteProvider baseRoute="/embed/config" embedded>
562+
{withAuthorizationAccessCheck(
563+
<ConfigDetailsPlaybooksPage />,
564+
tables.database,
565+
"read",
566+
true
567+
)}
568+
</ConfigDetailsBaseRouteProvider>
569+
}
570+
/>
571+
572+
{/* Views embed routes — no sidebar */}
511573
<Route
512-
path="/view/topology/:id"
574+
path="/embed/views/:namespace/:name"
513575
element={withAuthorizationAccessCheck(
514-
<TopologyCardPage />,
515-
tables.topologies,
576+
<ViewPage />,
577+
tables.views,
578+
"read",
579+
true
580+
)}
581+
/>
582+
<Route
583+
path="/embed/views/:id"
584+
element={withAuthorizationAccessCheck(
585+
<ViewPage />,
586+
tables.views,
516587
"read",
517588
true
518589
)}
519590
/>
520-
521-
<Route path="/view/health" element={<HealthPage url={CANARY_API} />} />
522591

523592
<Route path="topology" element={sidebar}>
524593
<Route
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { createContext, ReactNode, useContext } from "react";
2+
3+
type ConfigDetailsContext = { baseRoute: string; embedded: boolean };
4+
5+
const ConfigDetailsBaseRouteContext = createContext<ConfigDetailsContext>({
6+
baseRoute: "/catalog",
7+
embedded: false
8+
});
9+
10+
export function useConfigDetailsBaseRoute() {
11+
return useContext(ConfigDetailsBaseRouteContext).baseRoute;
12+
}
13+
14+
export function useConfigDetailsEmbedded() {
15+
return useContext(ConfigDetailsBaseRouteContext).embedded;
16+
}
17+
18+
export function ConfigDetailsBaseRouteProvider({
19+
baseRoute,
20+
embedded = false,
21+
children
22+
}: {
23+
baseRoute: string;
24+
embedded?: boolean;
25+
children: ReactNode;
26+
}) {
27+
return (
28+
<ConfigDetailsBaseRouteContext.Provider value={{ baseRoute, embedded }}>
29+
{children}
30+
</ConfigDetailsBaseRouteContext.Provider>
31+
);
32+
}

src/components/Configs/ConfigDetailsTabs.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ import PlaybooksDropdownMenu from "../Playbooks/Runs/Submit/PlaybooksDropdownMen
1212
import { ErrorBoundary } from "../ErrorBoundary";
1313
import { useConfigDetailsTabs } from "./ConfigTabsLinks";
1414
import ConfigSidebar from "./Sidebar/ConfigSidebar";
15+
import {
16+
useConfigDetailsBaseRoute,
17+
useConfigDetailsEmbedded
18+
} from "./ConfigDetailsBaseRouteContext";
1519

1620
type ConfigDetailsTabsProps = {
1721
refetch?: () => void;
@@ -30,6 +34,8 @@ type ConfigDetailsTabsProps = {
3034
| string; // Views
3135
className?: string;
3236
extra?: ReactNode;
37+
/** Override the base route for tab links. Defaults to context value (usually "/catalog"). */
38+
baseRoute?: string;
3339
};
3440

3541
export function ConfigDetailsTabs({
@@ -39,8 +45,12 @@ export function ConfigDetailsTabs({
3945
pageTitlePrefix,
4046
activeTabName = "Spec",
4147
className = "p-2",
42-
extra
48+
extra,
49+
baseRoute
4350
}: ConfigDetailsTabsProps) {
51+
const contextBaseRoute = useConfigDetailsBaseRoute();
52+
const embedded = useConfigDetailsEmbedded();
53+
const resolvedBaseRoute = baseRoute ?? contextBaseRoute;
4454
const { id } = useParams();
4555

4656
const [, setRefreshButtonClickedTrigger] = useAtom(
@@ -50,7 +60,10 @@ export function ConfigDetailsTabs({
5060
const { data: configItem, isLoading: isLoadingConfig } =
5161
useGetConfigByIdQuery(id!);
5262

53-
const { tabs: configTabList } = useConfigDetailsTabs(configItem?.summary);
63+
const { tabs: configTabList } = useConfigDetailsTabs(
64+
configItem?.summary,
65+
resolvedBaseRoute
66+
);
5467

5568
const playbooksButton = id ? (
5669
<PlaybooksDropdownMenu config_id={id} containerClassName="my-0" />
@@ -87,6 +100,7 @@ export function ConfigDetailsTabs({
87100
loading={isLoading}
88101
extra={layoutExtra}
89102
contentClass="p-0 h-full overflow-y-hidden"
103+
hideChrome={embedded}
90104
>
91105
<div className="flex min-h-0 min-w-0 flex-1 flex-row overflow-y-hidden">
92106
<div className="flex min-h-0 min-w-0 flex-1 flex-col">

src/components/Configs/ConfigTabsLinks.tsx

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ type ConfigDetailsTab = {
1616
search?: string;
1717
};
1818

19-
export function useConfigDetailsTabs(countSummary?: ConfigItem["summary"]): {
19+
export function useConfigDetailsTabs(
20+
countSummary?: ConfigItem["summary"],
21+
baseRoute = "/catalog"
22+
): {
2023
isLoading: boolean;
2124
isError: boolean;
2225
tabs: ConfigDetailsTab[];
@@ -41,8 +44,10 @@ export function useConfigDetailsTabs(countSummary?: ConfigItem["summary"]): {
4144
const accessLogsCount =
4245
accessLogsData?.totalEntries ?? accessLogsData?.data?.length ?? 0;
4346

47+
const base = `${baseRoute}/${id}`;
48+
4449
const staticTabs: ConfigDetailsTab[] = [
45-
{ label: "Spec", key: "Spec", path: `/catalog/${id}/spec` },
50+
{ label: "Spec", key: "Spec", path: `${base}/spec` },
4651
{
4752
label: (
4853
<>
@@ -51,7 +56,7 @@ export function useConfigDetailsTabs(countSummary?: ConfigItem["summary"]): {
5156
</>
5257
),
5358
key: "Changes",
54-
path: `/catalog/${id}/changes`
59+
path: `${base}/changes`
5560
},
5661
{
5762
label: (
@@ -61,7 +66,7 @@ export function useConfigDetailsTabs(countSummary?: ConfigItem["summary"]): {
6166
</>
6267
),
6368
key: "Insights",
64-
path: `/catalog/${id}/insights`
69+
path: `${base}/insights`
6570
},
6671
{
6772
label: (
@@ -71,7 +76,7 @@ export function useConfigDetailsTabs(countSummary?: ConfigItem["summary"]): {
7176
</>
7277
),
7378
key: "Relationships",
74-
path: `/catalog/${id}/relationships`
79+
path: `${base}/relationships`
7580
},
7681
{
7782
label: (
@@ -81,7 +86,7 @@ export function useConfigDetailsTabs(countSummary?: ConfigItem["summary"]): {
8186
</>
8287
),
8388
key: "Playbooks",
84-
path: `/catalog/${id}/playbooks`
89+
path: `${base}/playbooks`
8590
},
8691
{
8792
label: (
@@ -91,7 +96,7 @@ export function useConfigDetailsTabs(countSummary?: ConfigItem["summary"]): {
9196
</>
9297
),
9398
key: "Checks",
94-
path: `/catalog/${id}/checks`
99+
path: `${base}/checks`
95100
}
96101
];
97102

@@ -104,7 +109,7 @@ export function useConfigDetailsTabs(countSummary?: ConfigItem["summary"]): {
104109
</>
105110
),
106111
key: "Access",
107-
path: `/catalog/${id}/access`
112+
path: `${base}/access`
108113
});
109114
}
110115

@@ -117,7 +122,7 @@ export function useConfigDetailsTabs(countSummary?: ConfigItem["summary"]): {
117122
</>
118123
),
119124
key: "Access Logs",
120-
path: `/catalog/${id}/access-logs`
125+
path: `${base}/access-logs`
121126
});
122127
}
123128

@@ -142,7 +147,7 @@ export function useConfigDetailsTabs(countSummary?: ConfigItem["summary"]): {
142147
const viewTabs: ConfigDetailsTab[] = orderedViews.map((view) => ({
143148
label: view.title || view.name,
144149
key: view.id,
145-
path: `/catalog/${id}/view/${view.id}`,
150+
path: `${base}/view/${view.id}`,
146151
icon: <Icon name={view.icon || "workflow"} />
147152
}));
148153

src/pages/TopologyCard.tsx

Lines changed: 0 additions & 34 deletions
This file was deleted.

src/pages/views/components/SingleView.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, { useEffect, useState } from "react";
2+
import { useLocation } from "react-router-dom";
23
import Age from "../../../ui/Age/Age";
34
import ViewLayout from "./ViewLayout";
45
import ViewWithSections from "./ViewWithSections";
@@ -17,6 +18,8 @@ interface SingleViewProps {
1718
}
1819

1920
const SingleView: React.FC<SingleViewProps> = ({ id }) => {
21+
const location = useLocation();
22+
const hideChrome = location.pathname.startsWith("/embed/views/");
2023
const {
2124
viewResult,
2225
isLoading,
@@ -45,6 +48,7 @@ const SingleView: React.FC<SingleViewProps> = ({ id }) => {
4548
icon="workflow"
4649
onRefresh={handleForceRefresh}
4750
centered
51+
hideChrome={hideChrome}
4852
>
4953
<ErrorViewer error={error} className="mx-auto max-w-3xl" />
5054
</ViewLayout>
@@ -58,6 +62,7 @@ const SingleView: React.FC<SingleViewProps> = ({ id }) => {
5862
icon="workflow"
5963
onRefresh={handleForceRefresh}
6064
centered
65+
hideChrome={hideChrome}
6166
>
6267
<div className="text-center">
6368
<div className="mx-auto mb-4 h-12 w-12 animate-spin rounded-full border-b-2 border-blue-600"></div>
@@ -74,6 +79,7 @@ const SingleView: React.FC<SingleViewProps> = ({ id }) => {
7479
icon="workflow"
7580
onRefresh={handleForceRefresh}
7681
centered
82+
hideChrome={hideChrome}
7783
>
7884
<ErrorViewer
7985
error="The requested view could not be found."
@@ -106,6 +112,7 @@ const SingleView: React.FC<SingleViewProps> = ({ id }) => {
106112
icon={icon || "workflow"}
107113
onRefresh={handleForceRefresh}
108114
loading={isFetching}
115+
hideChrome={hideChrome}
109116
extra={
110117
viewResult.lastRefreshedAt && (
111118
<p className="text-sm text-gray-500">

0 commit comments

Comments
 (0)