Skip to content

Commit 7b6969c

Browse files
feat: add YAML splitter (#309)
Co-authored-by: Copilot <[email protected]>
1 parent 78e6e1f commit 7b6969c

26 files changed

+389
-321
lines changed

src/AppRouter.tsx

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { SentryRoutes } from './mount.ts';
66
import ProjectPage from './spaces/onboarding/pages/ProjectPage.tsx';
77
import McpPage from './spaces/mcp/pages/McpPage.tsx';
88
import { SearchParamToggleVisibility } from './components/Helper/FeatureToggleExistance.tsx';
9+
import { SplitterProvider } from './components/Splitter/SplitterContext.tsx';
10+
import { SplitterLayout } from './components/Splitter/SplitterLayout.tsx';
911

1012
function AppRouter() {
1113
return (
@@ -20,20 +22,24 @@ function AppRouter() {
2022
<ShellBarComponent />
2123
</SearchParamToggleVisibility>
2224

23-
<Router>
24-
<SentryRoutes>
25-
<Route path="/mcp" element={<GlobalProviderOutlet />}>
26-
<Route path="projects" element={<ProjectListView />} />
27-
<Route path="projects/:projectName" element={<ProjectPage />} />
28-
<Route
29-
path="projects/:projectName/workspaces/:workspaceName/mcps/:controlPlaneName"
30-
element={<McpPage />}
31-
/>
32-
</Route>
33-
<Route path="/" element={<Navigate to="/mcp/projects" />} />
34-
<Route path="*" element={<Navigate to="/" />} />
35-
</SentryRoutes>
36-
</Router>
25+
<SplitterProvider>
26+
<SplitterLayout>
27+
<Router>
28+
<SentryRoutes>
29+
<Route path="/mcp" element={<GlobalProviderOutlet />}>
30+
<Route path="projects" element={<ProjectListView />} />
31+
<Route path="projects/:projectName" element={<ProjectPage />} />
32+
<Route
33+
path="projects/:projectName/workspaces/:workspaceName/mcps/:controlPlaneName"
34+
element={<McpPage />}
35+
/>
36+
</Route>
37+
<Route path="/" element={<Navigate to="/mcp/projects" />} />
38+
<Route path="*" element={<Navigate to="/" />} />
39+
</SentryRoutes>
40+
</Router>
41+
</SplitterLayout>
42+
</SplitterProvider>
3743
</>
3844
);
3945
}

src/components/ControlPlane/FluxList.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { YamlViewButton } from '../Yaml/YamlViewButton.tsx';
1111
import { useMemo } from 'react';
1212
import StatusFilter from '../Shared/StatusFilter/StatusFilter.tsx';
1313
import { ResourceStatusCell } from '../Shared/ResourceStatusCell.tsx';
14+
import { Resource } from '../../utils/removeManagedFieldsAndFilterData.ts';
1415

1516
export default function FluxList() {
1617
const { data: gitReposData, error: repoErr, isLoading: repoIsLoading } = useApiResource(FluxRequest); //404 if component not enabled
@@ -81,7 +82,7 @@ export default function FluxList() {
8182
accessor: 'yaml',
8283
disableFilters: true,
8384
Cell: (cellData: CellData<KustomizationsResponse['items']>) => (
84-
<YamlViewButton resourceObject={cellData.cell.row.original?.item} />
85+
<YamlViewButton variant="resource" resource={cellData.cell.row.original?.item as Resource} />
8586
),
8687
},
8788
],
@@ -125,7 +126,9 @@ export default function FluxList() {
125126
width: 75,
126127
accessor: 'yaml',
127128
disableFilters: true,
128-
Cell: (cellData: CellData<FluxRow>) => <YamlViewButton resourceObject={cellData.cell.row.original?.item} />,
129+
Cell: (cellData: CellData<FluxRow>) => (
130+
<YamlViewButton variant="resource" resource={cellData.cell.row.original?.item as Resource} />
131+
),
129132
},
130133
],
131134
[t],
@@ -173,14 +176,14 @@ export default function FluxList() {
173176
<div className="crossplane-table-element">
174177
<FlexBox justifyContent={'Start'} alignItems={'Center'} gap={'0.5em'}>
175178
<Title level="H4">{t('FluxList.gitOpsTitle')}</Title>
176-
<YamlViewButton resourceObject={gitReposData} />
179+
<YamlViewButton variant="resource" resource={gitReposData as unknown as Resource} />
177180
</FlexBox>
178181
<ConfiguredAnalyticstable columns={gitReposColumns} isLoading={repoIsLoading} data={gitReposRows} />
179182
</div>
180183
<div className="crossplane-table-element">
181184
<FlexBox justifyContent={'Start'} alignItems={'Center'} gap={'0.5em'}>
182185
<Title level="H4">{t('FluxList.kustomizationsTitle')}</Title>
183-
<YamlViewButton resourceObject={kustmizationData} />
186+
<YamlViewButton variant="resource" resource={kustmizationData as unknown as Resource} />
184187
</FlexBox>
185188
<ConfiguredAnalyticstable
186189
columns={kustomizationsColumns}

src/components/ControlPlane/ManagedResources.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { YamlViewButton } from '../Yaml/YamlViewButton.tsx';
1717
import { useMemo } from 'react';
1818
import StatusFilter from '../Shared/StatusFilter/StatusFilter.tsx';
1919
import { ResourceStatusCell } from '../Shared/ResourceStatusCell.tsx';
20+
import { Resource } from '../../utils/removeManagedFieldsAndFilterData.ts';
2021

2122
interface CellData<T> {
2223
cell: {
@@ -107,7 +108,7 @@ export function ManagedResources() {
107108
disableFilters: true,
108109
Cell: (cellData: CellData<ResourceRow>) =>
109110
cellData.cell.row.original?.item ? (
110-
<YamlViewButton resourceObject={cellData.cell.row.original?.item} />
111+
<YamlViewButton variant="resource" resource={cellData.cell.row.original?.item as Resource} />
111112
) : undefined,
112113
},
113114
],

src/components/ControlPlane/Providers.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import '@ui5/webcomponents-icons/dist/sys-enter-2';
1919
import '@ui5/webcomponents-icons/dist/sys-cancel-2';
2020
import StatusFilter from '../Shared/StatusFilter/StatusFilter.tsx';
2121
import { ResourceStatusCell } from '../Shared/ResourceStatusCell.tsx';
22+
import { Resource } from '../../utils/removeManagedFieldsAndFilterData.ts';
2223

2324
interface CellData<T> {
2425
cell: {
@@ -110,7 +111,7 @@ export function Providers() {
110111
accessor: 'yaml',
111112
disableFilters: true,
112113
Cell: (cellData: CellData<ProvidersRow>) => (
113-
<YamlViewButton resourceObject={cellData.cell.row.original?.item} />
114+
<YamlViewButton variant="resource" resource={cellData.cell.row.original?.item as Resource} />
114115
),
115116
},
116117
],

src/components/ControlPlane/ProvidersConfig.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { formatDateAsTimeAgo } from '../../utils/i18n/timeAgo';
1313
import { YamlViewButton } from '../Yaml/YamlViewButton.tsx';
1414

1515
import { useMemo } from 'react';
16+
import { Resource } from '../../utils/removeManagedFieldsAndFilterData.ts';
1617

1718
type Rows = {
1819
parent: string;
@@ -79,7 +80,7 @@ export function ProvidersConfig() {
7980
disableFilters: true,
8081
Cell: (cellData: CellData<Rows>) =>
8182
cellData.cell.row.original?.resource ? (
82-
<YamlViewButton resourceObject={cellData.cell.row.original?.resource} />
83+
<YamlViewButton variant="resource" resource={cellData.cell.row.original?.resource as Resource} />
8384
) : undefined,
8485
},
8586
],

src/components/ControlPlanes/ControlPlaneCard/ControlPlaneCard.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ import {
2222
PatchMCPResourceForDeletionBody,
2323
} from '../../../lib/api/types/crate/deleteMCP.ts';
2424

25-
import { YamlViewButtonWithLoader } from '../../Yaml/YamlViewButtonWithLoader.tsx';
25+
import { YamlViewButton } from '../../Yaml/YamlViewButton.tsx';
2626
import { useToast } from '../../../context/ToastContext.tsx';
2727
import { canConnectToMCP } from '../controlPlanes.ts';
28+
2829
import { Infobox } from '../../Ui/Infobox/Infobox.tsx';
2930

3031
import { ControlPlaneCardMenu } from './ControlPlaneCardMenu.tsx';
31-
3232
import { EditManagedControlPlaneWizardDataLoader } from '../../Wizards/CreateManagedControlPlane/EditManagedControlPlaneWizardDataLoader.tsx';
3333
import { DISPLAY_NAME_ANNOTATION } from '../../../lib/api/types/shared/keyNames.ts';
3434

@@ -99,7 +99,8 @@ export const ControlPlaneCard = ({ controlPlane, workspace, projectName }: Props
9999
setIsEditManagedControlPlaneWizardOpen={handleIsManagedControlPlaneWizardOpen}
100100
/>
101101
<FlexBox direction="Row" justifyContent="SpaceBetween" alignItems="Center" gap={10}>
102-
<YamlViewButtonWithLoader
102+
<YamlViewButton
103+
variant="loader"
103104
workspaceName={controlPlane.metadata.namespace}
104105
resourceName={controlPlane.metadata.name}
105106
resourceType={'managedcontrolplanes'}

src/components/ControlPlanes/List/ControlPlaneListWorkspaceGridTile.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { ListControlPlanes } from '../../../lib/api/types/crate/controlPlanes.ts
1717
import IllustratedError from '../../Shared/IllustratedError.tsx';
1818
import { APIError } from '../../../lib/api/error.ts';
1919
import { useTranslation } from 'react-i18next';
20-
import { YamlViewButtonWithLoader } from '../../Yaml/YamlViewButtonWithLoader.tsx';
20+
import { YamlViewButton } from '../../Yaml/YamlViewButton.tsx';
2121
import { IllustratedBanner } from '../../Ui/IllustratedBanner/IllustratedBanner.tsx';
2222
import { useLink } from '../../../lib/shared/useLink.ts';
2323
import IllustrationMessageType from '@ui5/webcomponents-fiori/dist/types/IllustrationMessageType.js';
@@ -119,7 +119,8 @@ export function ControlPlaneListWorkspaceGridTile({ projectName, workspace }: Pr
119119

120120
<MembersAvatarView members={uniqueMembers} project={projectName} workspace={workspaceName} />
121121
<FlexBox justifyContent={'SpaceBetween'} gap={10}>
122-
<YamlViewButtonWithLoader
122+
<YamlViewButton
123+
variant="loader"
123124
workspaceName={workspace.metadata.namespace}
124125
resourceName={workspaceName}
125126
resourceType={'workspaces'}

src/components/Graphs/Graph.tsx

Lines changed: 13 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@ import '@xyflow/react/dist/style.css';
77
import { NodeData, ColorBy } from './types';
88
import CustomNode from './CustomNode';
99
import { Legend, LegendItem } from './Legend';
10-
import { YamlViewDialog } from '../Yaml/YamlViewDialog';
11-
import YamlViewer from '../Yaml/YamlViewer';
12-
import { stringify } from 'yaml';
13-
import { removeManagedFieldsAndFilterData, Resource } from '../../utils/removeManagedFieldsAndFilterData.ts';
1410
import { useTranslation } from 'react-i18next';
1511
import { useGraph } from './useGraph';
1612
import { ManagedResourceItem } from '../../lib/shared/types';
1713
import { useTheme } from '../../hooks/useTheme';
14+
import { useSplitter } from '../Splitter/SplitterContext.tsx';
15+
import { YamlSidePanel } from '../Yaml/YamlSidePanel.tsx';
16+
import { Resource } from '../../utils/removeManagedFieldsAndFilterData.ts';
1817

1918
const nodeTypes = {
2019
custom: (props: NodeProps<Node<NodeData, 'custom'>>) => (
@@ -31,33 +30,22 @@ const nodeTypes = {
3130

3231
const Graph: React.FC = () => {
3332
const { t } = useTranslation();
33+
const { openInAside } = useSplitter();
3434
const { isDarkTheme } = useTheme();
3535
const [colorBy, setColorBy] = useState<ColorBy>('provider');
36-
const [yamlDialogOpen, setYamlDialogOpen] = useState(false);
37-
const [yamlResource, setYamlResource] = useState<ManagedResourceItem | null>(null);
3836

39-
const handleYamlClick = useCallback((item: ManagedResourceItem) => {
40-
setYamlResource(item);
41-
setYamlDialogOpen(true);
42-
}, []);
37+
const handleYamlClick = useCallback(
38+
(item: ManagedResourceItem) => {
39+
const yamlFilename = item
40+
? `${item.kind ?? ''}${item.metadata?.name ? '_' : ''}${item.metadata?.name ?? ''}`
41+
: '';
4342

44-
const { nodes, edges, colorMap, loading, error } = useGraph(colorBy, handleYamlClick);
45-
46-
const yamlString = useMemo(
47-
() => (yamlResource ? stringify(removeManagedFieldsAndFilterData(yamlResource as unknown as Resource, true)) : ''),
48-
[yamlResource],
43+
openInAside(<YamlSidePanel resource={item as unknown as Resource} filename={yamlFilename} />);
44+
},
45+
[openInAside],
4946
);
5047

51-
const yamlStringToCopy = useMemo(
52-
() => (yamlResource ? stringify(removeManagedFieldsAndFilterData(yamlResource as unknown as Resource, false)) : ''),
53-
[yamlResource],
54-
);
55-
56-
const yamlFilename = useMemo(() => {
57-
if (!yamlResource) return '';
58-
const { kind, metadata } = yamlResource;
59-
return `${kind ?? ''}${metadata?.name ? '_' : ''}${metadata?.name ?? ''}`;
60-
}, [yamlResource]);
48+
const { nodes, edges, colorMap, loading, error } = useGraph(colorBy, handleYamlClick);
6149

6250
const legendItems: LegendItem[] = useMemo(
6351
() =>
@@ -136,13 +124,6 @@ const Graph: React.FC = () => {
136124
</Panel>
137125
</ReactFlow>
138126
</div>
139-
<YamlViewDialog
140-
isOpen={yamlDialogOpen}
141-
setIsOpen={setYamlDialogOpen}
142-
dialogContent={
143-
<YamlViewer yamlString={yamlString} yamlStringToCopy={yamlStringToCopy} filename={yamlFilename} />
144-
}
145-
/>
146127
</div>
147128
);
148129
};

src/components/Projects/ProjectsList.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import '@ui5/webcomponents-icons/dist/copy';
99
import '@ui5/webcomponents-icons/dist/arrow-right';
1010
import { ListProjectNames } from '../../lib/api/types/crate/listProjectNames';
1111
import { t } from 'i18next';
12-
import { YamlViewButtonWithLoader } from '../Yaml/YamlViewButtonWithLoader.tsx';
12+
import { YamlViewButton } from '../Yaml/YamlViewButton.tsx';
1313
import { useMemo } from 'react';
1414
import { ProjectsListItemMenu } from './ProjectsListItemMenu.tsx';
1515

@@ -98,7 +98,8 @@ export default function ProjectsList() {
9898
alignItems: 'center',
9999
}}
100100
>
101-
<YamlViewButtonWithLoader
101+
<YamlViewButton
102+
variant="loader"
102103
resourceType={'projects'}
103104
resourceName={instance.cell.row.original?.projectName}
104105
/>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { createContext, ReactNode, use, useCallback, useMemo, useState } from 'react';
2+
3+
interface SplitterContextType {
4+
isAsideVisible: boolean;
5+
asideContent: ReactNode;
6+
closeAside: () => void;
7+
openInAside: (content: ReactNode) => void;
8+
}
9+
10+
const SplitterContext = createContext<SplitterContextType | null>(null);
11+
12+
export function SplitterProvider({ children }: { children: ReactNode }) {
13+
const [isAsideVisible, setIsAsideVisible] = useState(false);
14+
const [asideContent, setAsideContent] = useState<ReactNode | null>(null);
15+
16+
const openInAside = useCallback((node: ReactNode) => {
17+
setAsideContent(node);
18+
setIsAsideVisible(true);
19+
}, []);
20+
21+
const closeAside = useCallback(() => {
22+
setIsAsideVisible(false);
23+
setAsideContent(null);
24+
}, []);
25+
26+
const value = useMemo(() => {
27+
return { isAsideVisible, asideContent, closeAside, openInAside };
28+
}, [isAsideVisible, asideContent, closeAside, openInAside]);
29+
30+
return <SplitterContext value={value}>{children}</SplitterContext>;
31+
}
32+
33+
export function useSplitter() {
34+
const context = use(SplitterContext);
35+
if (!context) {
36+
throw new Error('useSplitter must be used within an SplitterProvider.');
37+
}
38+
return context;
39+
}

0 commit comments

Comments
 (0)