+ {labelsToShow.map((labelItem, index) => (
+
+
+ {labelItem.text}
+
+ {labelItem.percentage !== undefined && (
+
+ {labelItem.percentage}%
+
+ )}
+ {labelItem.count !== undefined && (
+
+ ({labelItem.count})
+
+ )}
+ {labelItem.customValue !== undefined && (
+
+ {labelItem.customValue}
+
+ )}
+
+ ))}
+
+ );
+ };
+
+ return (
+
- {t('FluxList.gitOpsTitle')}
+ {t('FluxList.repositoriesTitle') || 'Repositories'}
diff --git a/src/components/Core/ShellBar.tsx b/src/components/Core/ShellBar.tsx
index c08b8edf..4dd9c753 100644
--- a/src/components/Core/ShellBar.tsx
+++ b/src/components/Core/ShellBar.tsx
@@ -44,8 +44,8 @@ export function ShellBarComponent() {
startButton={
-

-
MCP
+

+
ManagedControlPlane UI
}
diff --git a/src/components/Graphs/Graph.module.css b/src/components/Graphs/Graph.module.css
index 15a3d0e9..89bbe087 100644
--- a/src/components/Graphs/Graph.module.css
+++ b/src/components/Graphs/Graph.module.css
@@ -1,17 +1,61 @@
.graphContainer {
display: flex;
- height: 600px;
+ flex-direction: column;
+ height: 500px;
border: 1px solid var(--sapList_BorderColor, #ddd);
border-radius: 16px;
overflow: hidden;
- background-color: var(--sapBackgroundColor, #fafafa);
+ background-color: #ffffff;
font-family: var(--sapFontFamily);
+ position: relative;
+ touch-action: none;
+ overflow: hidden;
}
.graphColumn {
flex: 1;
display: flex;
flex-direction: column;
+ background-color: #ffffff;
+ position: relative;
+ width: 100%;
+ height: 100%;
+ touch-action: none;
+ cursor: grab;
+ overflow: hidden;
+}
+
+.bottomLegendContainer {
+ position: absolute;
+ bottom: 1rem;
+ right: 1rem;
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ z-index: 10;
+}
+
+.topRightContainer {
+ position: absolute;
+ top: 1rem;
+ right: 1rem;
+ z-index: 10;
+}
+
+.filterIcon {
+ display: flex;
+ align-items: center;
+ cursor: pointer;
+ padding: 4px;
+ border-radius: 4px;
+ transition: background-color 0.2s ease;
+ background-color: rgba(255, 255, 255, 0.9);
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+ margin-left: 0.5rem;
+}
+
+.filterIcon:hover {
+ background-color: var(--sapButton_Hover_Background, #f0f0f0);
}
.graphHeader {
@@ -71,4 +115,59 @@
:global([data-theme='dark'] .react-flow__controls-button:hover) {
background: rgba(255, 255, 255, 0.08);
+}
+
+/* Animated edge styles */
+:global(.react-flow__edge-path) {
+ stroke-dasharray: 5 5;
+ animation: flowAnimation 3s linear infinite;
+ stroke: #888;
+ opacity: 0.8;
+}
+
+:global(.react-flow__edge.react-flow__edge-step .react-flow__edge-path) {
+ stroke-dasharray: 8 4;
+ animation: flowAnimation 2s linear infinite;
+ stroke: #888;
+ opacity: 0.8;
+}
+
+@keyframes flowAnimation {
+ 0% {
+ stroke-dashoffset: 0;
+ opacity: 0.6;
+ }
+ 50% {
+ opacity: 1;
+ }
+ 100% {
+ stroke-dashoffset: 12;
+ opacity: 0.6;
+ }
+}
+
+/* Blocky edge styling */
+:global(.react-flow__edge.react-flow__edge-step) {
+ stroke-width: 2px;
+}
+
+:global(.react-flow__background) {
+ background-color: #ffffff !important;
+}
+
+:global(.react-flow__pane) {
+ cursor: grab;
+ overflow: hidden;
+}
+
+:global(.react-flow__pane.dragging) {
+ cursor: grabbing;
+}
+
+:global(.react-flow) {
+ overflow: hidden;
+}
+
+:global(.react-flow__renderer) {
+ overflow: hidden;
}
\ No newline at end of file
diff --git a/src/components/Graphs/Graph.tsx b/src/components/Graphs/Graph.tsx
index f4974b54..fda6d56f 100644
--- a/src/components/Graphs/Graph.tsx
+++ b/src/components/Graphs/Graph.tsx
@@ -1,7 +1,7 @@
-import React, { useState, useCallback, useMemo } from 'react';
-import { ReactFlow, Background, Controls, MarkerType, Node, Panel } from '@xyflow/react';
+import React, { useState, useCallback, useMemo, useEffect } from 'react';
+import { ReactFlow, Background, Controls, Node, BackgroundVariant, SelectionMode } from '@xyflow/react';
import type { NodeProps } from '@xyflow/react';
-import { RadioButton, FlexBox, FlexBoxAlignItems } from '@ui5/webcomponents-react';
+import { Button, Popover } from '@ui5/webcomponents-react';
import styles from './Graph.module.css';
import '@xyflow/react/dist/style.css';
import { NodeData, ColorBy } from './types';
@@ -15,7 +15,6 @@ import { useTranslation } from 'react-i18next';
import { useGraph } from './useGraph';
import { ManagedResourceItem } from '../../lib/shared/types';
import { useTheme } from '../../hooks/useTheme';
-
const nodeTypes = {
custom: (props: NodeProps
>) => (
{
+interface GraphProps {
+ colorBy?: ColorBy;
+}
+
+const Graph: React.FC = ({ colorBy: initialColorBy = 'source' }) => {
const { t } = useTranslation();
const { isDarkTheme } = useTheme();
- const [colorBy, setColorBy] = useState('provider');
+ const [colorBy, setColorBy] = useState(initialColorBy);
const [yamlDialogOpen, setYamlDialogOpen] = useState(false);
const [yamlResource, setYamlResource] = useState(null);
+ const [filterPopoverOpen, setFilterPopoverOpen] = useState(false);
+
+ // Update colorBy when prop changes
+ useEffect(() => {
+ setColorBy(initialColorBy);
+ }, [initialColorBy]);
const handleYamlClick = useCallback((item: ManagedResourceItem) => {
setYamlResource(item);
@@ -83,53 +92,75 @@ const Graph: React.FC = () => {
nodes={nodes}
edges={edges}
nodeTypes={nodeTypes}
- defaultEdgeOptions={{
- style: { stroke: '#888', strokeWidth: 1.5 },
- markerEnd: { type: MarkerType.ArrowClosed },
- }}
- fitView
+ defaultViewport={{ x: 40, y: 40, zoom: 0.8 }}
+ minZoom={0.2}
+ maxZoom={4.0}
proOptions={{
hideAttribution: true,
}}
nodesDraggable={false}
nodesConnectable={false}
elementsSelectable={false}
- zoomOnScroll={true}
+ zoomOnScroll={false}
+ panOnScroll={false}
panOnDrag={true}
+ selectionOnDrag={false}
+ selectionMode={SelectionMode.Partial}
+ preventScrolling={true}
>
+
-
-
-
-
-
-
-
-
-
+
+ {/* Legend and filter in bottom-right */}
+
+
+
setFilterPopoverOpen(false)}
+ >
+
+
+
+
+
+
+
+
+