Skip to content

Commit

Permalink
Selected cluster details (#238)
Browse files Browse the repository at this point in the history
* fix: a couple of fields should have been updated with reset

* set selected node when graph node is clicked on graph view/ open cluster details panel. disregard node layout stab - wip
  • Loading branch information
D-B-Hawk authored Aug 17, 2023
1 parent 893c1e1 commit e94bc20
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 72 deletions.
195 changes: 137 additions & 58 deletions components/flow/index.tsx
Original file line number Diff line number Diff line change
@@ -1,105 +1,184 @@
import React, { useCallback, type FunctionComponent } from 'react';
import React, { useCallback, type FunctionComponent, MouseEvent } from 'react';
import ReactFlow, {
addEdge,
Edge,
Connection,
NodeTypes,
useNodesState,
useEdgesState,
Node,
} from 'reactflow';

import { GraphNode, type CustomGraphNode } from '../graphNode';
import 'reactflow/dist/style.css';
import { ClusterStatus, ClusterType } from '../../types/provision';
import { InstallationType } from '../../types/redux';
import { ClusterInfo } from '../../components/clusterTable/clusterTable';

const initialNodes: CustomGraphNode[] = [
const clusters: ClusterInfo[] = [
{
id: '1',
type: 'custom',
data: {
clusterName: 'kuberfirst-mgmt2',
type: ClusterType.MANAGEMENT,
cloudProvider: InstallationType.AWS,
cloudRegion: 'ap-southeast-1',
creationDate: '05 Apr 2023, 12:24:56',
gitUser: 'Eleanor Carroll',
status: ClusterStatus.PROVISIONED,
nodes: 2,
},
position: { x: 5, y: 0 },
draggable: false,
clusterName: 'kuberfirst-mgmt',
type: ClusterType.MANAGEMENT,
cloudProvider: InstallationType.AWS,
cloudRegion: 'ap-southeast-1',
creationDate: '05 Apr 2023, 12:24:56',
gitUser: 'Eleanor Carroll',
status: ClusterStatus.PROVISIONED,
adminEmail: '[email protected]',
gitProvider: 'Github',
domainName: 'yourdomain.com',
nodes: 2,
},
{
id: '2',
type: 'custom',
data: {
clusterName: 'kuberfirst-worker-1',
type: ClusterType.WORKLOAD,
cloudProvider: InstallationType.AWS,
cloudRegion: 'ap-southeast-1',
creationDate: '05 Apr 2023, 12:24:56',
gitUser: 'Eleanor Carroll',
status: ClusterStatus.ERROR,
nodes: 2,
},
position: { x: 500, y: -100 },
draggable: false,
clusterName: 'kuberfirst-worker-1',
type: ClusterType.WORKLOAD,
cloudProvider: InstallationType.CIVO,
cloudRegion: 'ap-southeast-1',
nodes: 2,
creationDate: '05 Apr 2023, 12:24:56',
gitUser: 'Eleanor Carroll',
status: ClusterStatus.ERROR,
adminEmail: '[email protected]',
gitProvider: 'Github',
domainName: 'yourdomain.com',
},
{
id: '3',
type: 'custom',
data: {
clusterName: 'kuberfirst-worker-2',
type: ClusterType.WORKLOAD,
cloudProvider: InstallationType.AWS,
cloudRegion: 'ap-southeast-1',
creationDate: '05 Apr 2023, 12:24:56',
gitUser: 'Eleanor Carroll',
status: ClusterStatus.PROVISIONED,
nodes: 2,
},
position: { x: 500, y: 100 },
clusterName: 'kuberfirst-worker-2',
type: ClusterType.WORKLOAD,
cloudProvider: InstallationType.DIGITAL_OCEAN,
cloudRegion: 'ap-southeast-1',
nodes: 2,
creationDate: '05 Apr 2023, 12:24:56',
gitUser: 'Eleanor Carroll',
status: ClusterStatus.DELETING,
adminEmail: '[email protected]',
gitProvider: 'Github',
domainName: 'yourdomain.com',
},
];

const initialEdges: Edge[] = [
{
id: 'edge-1',
source: '1',
target: '2',
animated: false,
type: 'straight',
style: { strokeWidth: 2, stroke: '#CBD5E1' },
clusterName: 'kuberfirst-worker-3',
type: ClusterType.WORKLOAD,
cloudProvider: InstallationType.DIGITAL_OCEAN,
cloudRegion: 'ap-southeast-1',
nodes: 2,
creationDate: '05 Apr 2023, 12:24:56',
gitUser: 'Eleanor Carroll',
status: ClusterStatus.PROVISIONED,
adminEmail: '[email protected]',
gitProvider: 'Github',
domainName: 'yourdomain.com',
},
{
id: 'edge-2',
source: '1',
target: '3',
clusterName: 'kuberfirst-worker-4',
type: ClusterType.WORKLOAD,
cloudProvider: InstallationType.VULTR,
cloudRegion: 'ap-southeast-1',
nodes: 2,
creationDate: '05 Apr 2023, 12:24:56',
gitUser: 'Eleanor Carroll',
status: ClusterStatus.PROVISIONED,
adminEmail: '[email protected]',
gitProvider: 'Github',
domainName: 'yourdomain.com',
},
{
clusterName: 'kuberfirst-worker-5',
type: ClusterType.WORKLOAD,
cloudProvider: InstallationType.VULTR,
cloudRegion: 'ap-southeast-1',
nodes: 2,
creationDate: '05 Apr 2023, 12:24:56',
gitUser: 'Eleanor Carroll',
status: ClusterStatus.PROVISIONED,
adminEmail: '[email protected]',
gitProvider: 'Github',
domainName: 'yourdomain.com',
},
];

function generateNode(
cluster: ClusterInfo,
id: string,
position: { x: number; y: number },
): CustomGraphNode {
return {
id,
type: 'custom',
data: cluster,
position,
draggable: false,
};
}

function generateEdge(id: string, source: string, target: string): Edge {
return {
id,
source,
target,
animated: false,
type: 'straight',
style: { strokeWidth: 2, stroke: '#CBD5E1' },
},
];
};
}

function generateNodesConfig(
managementCluster: ClusterInfo,
workloadClusters: ClusterInfo[],
): [CustomGraphNode[], Edge[]] {
const managementNodeId = '1';
const nodes: CustomGraphNode[] = [
generateNode(managementCluster, managementNodeId, {
x: 0,
y: (workloadClusters.length * 200) / 2,
}),
];

const edges: Edge[] = [];

for (let i = 0; i < workloadClusters.length; i += 1) {
const generatedId = `${i + 2}`;

nodes.push(generateNode(workloadClusters[i], generatedId, { x: 600, y: i * 200 }));
edges.push(generateEdge(`edge-${i + 1}`, managementNodeId, generatedId));
}

return [nodes, edges];
}

const [managemenCluster, ...workloadClusters] = clusters;

const [initialNodes, initialEdges] = generateNodesConfig(managemenCluster, workloadClusters);

const nodeTypes: NodeTypes = {
custom: GraphNode,
};

export const Flow: FunctionComponent = () => {
interface FlowProps {
onNodeClick: (clusterInfo: ClusterInfo) => void;
}

export const Flow: FunctionComponent<FlowProps> = ({ onNodeClick }) => {
const [nodes, , onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect = useCallback(
(params: Connection | Edge) => setEdges((eds) => addEdge(params, eds)),
[setEdges],
);

const handleNodeClick = useCallback(
(e: MouseEvent, node: Node) => {
onNodeClick(node.data);
},
[onNodeClick],
);

return (
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onNodeClick={handleNodeClick}
onConnect={onConnect}
fitView
nodeTypes={nodeTypes}
Expand Down
14 changes: 10 additions & 4 deletions containers/clusterManagement/createClusterFlow/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@ const actionButtonText: Record<ClusterCreationStep, string> = {

interface CreateClusterFlowProps {
onMenuClose: () => void;
onClusterDelete: () => void;
cluster?: ClusterInfo;
}

export const CreateClusterFlow: FunctionComponent<CreateClusterFlowProps> = ({
cluster,
onMenuClose,
onClusterDelete,
}) => {
const { clusterCreationStep } = useAppSelector(({ api }) => api);

Expand All @@ -48,16 +50,20 @@ export const CreateClusterFlow: FunctionComponent<CreateClusterFlowProps> = ({
}, [dispatch, clusterCreationStep, onMenuClose]);

const handleClick = useCallback(() => {
if (clusterCreationStep !== ClusterCreationStep.DETAILS && methods.formState.isValid) {
if (clusterCreationStep === ClusterCreationStep.DETAILS) {
onClusterDelete();
} else if (methods.formState.isValid) {
dispatch(setClusterCreationStep(clusterCreationStep + 1));
}
}, [dispatch, clusterCreationStep, methods]);
}, [onClusterDelete, dispatch, clusterCreationStep, methods]);

const handleSubmit = useCallback(
(config: NewClusterConfig) => {
dispatch(createWorkloadCluster(config));
if (clusterCreationStep !== ClusterCreationStep.DETAILS) {
dispatch(createWorkloadCluster(config));
}
},
[dispatch],
[clusterCreationStep, dispatch],
);

const showingClusterDetails = clusterCreationStep === ClusterCreationStep.DETAILS;
Expand Down
32 changes: 23 additions & 9 deletions containers/clusterManagement/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Typography from '../../components/typography';
import { useAppDispatch, useAppSelector } from '../../redux/store';
import { deleteCluster, getCluster, getClusters } from '../../redux/thunks/api.thunk';
import { resetInstallState } from '../../redux/slices/installation.slice';
import { ClusterRequestProps } from '../../types/provision';
import { ClusterCreationStep, ClusterRequestProps } from '../../types/provision';
import useToggle from '../../hooks/useToggle';
import Drawer from '../../components/drawer';
import useModal from '../../hooks/useModal';
Expand All @@ -16,7 +16,7 @@ import TabPanel, { Tab, a11yProps } from '../../components/tab';
import { BISCAY, SALTBOX_BLUE } from '../../constants/colors';
import { Flow } from '../../components/flow';
import { ClusterInfo, ClusterTable } from '../../components/clusterTable/clusterTable';
import { sortClustersByType } from '../../utils/sortClusterByType';
import { setClusterCreationStep } from '../../redux/slices/api.slice';

import { CreateClusterFlow } from './createClusterFlow';
import { Container, Content, Header } from './clusterManagement.styled';
Expand All @@ -32,10 +32,11 @@ const ClusterManagement: FunctionComponent = () => {
const isClusterZero = useAppSelector(({ config }) => config.isClusterZero);

const {
isOpen: isDetailsPanelOpen,
open: openDetailsPanel,
close: closeDetailsPanel,
isOpen: createClusterFlowOpen,
open: openCreateClusterFlow,
close: closeCreateClusterFlow,
} = useToggle();

const {
isOpen: isDeleteModalOpen,
openModal: openDeleteModal,
Expand Down Expand Up @@ -103,6 +104,15 @@ const ClusterManagement: FunctionComponent = () => {
setActiveTab(newValue);
};

const handleNodeClick = useCallback(
(info: ClusterInfo) => {
setSelectedCluster(info);
dispatch(setClusterCreationStep(ClusterCreationStep.DETAILS));
openCreateClusterFlow();
},
[dispatch, openCreateClusterFlow],
);

return (
<Container>
<Header>
Expand All @@ -127,7 +137,7 @@ const ClusterManagement: FunctionComponent = () => {
color="primary"
variant="contained"
style={{ marginRight: '24px' }}
onClick={openDetailsPanel}
onClick={openCreateClusterFlow}
>
Add workload cluster
</Button>
Expand All @@ -144,7 +154,7 @@ const ClusterManagement: FunctionComponent = () => {
)}
</TabPanel>
<TabPanel value={activeTab} index={MANAGEMENT_TABS.GRAPH_VIEW}>
<Flow />
<Flow onNodeClick={handleNodeClick} />
</TabPanel>
</Content>
<Snackbar
Expand All @@ -157,7 +167,7 @@ const ClusterManagement: FunctionComponent = () => {
message={`Cluster ${selectedCluster?.clusterName} has been deleted`}
/>
<Drawer
open={isDetailsPanelOpen}
open={createClusterFlowOpen}
anchor="right"
hideBackdrop
PaperProps={{
Expand All @@ -169,7 +179,11 @@ const ClusterManagement: FunctionComponent = () => {
},
}}
>
<CreateClusterFlow onMenuClose={closeDetailsPanel} />
<CreateClusterFlow
onMenuClose={closeCreateClusterFlow}
onClusterDelete={openDeleteModal}
cluster={selectedCluster}
/>
</Drawer>
{selectedCluster && (
<DeleteCluster
Expand Down
3 changes: 2 additions & 1 deletion redux/slices/api.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ const apiSlice = createSlice({
state.isDeleted = false;
state.status = undefined;
state.loading = false;
state.clusters = [];
state.managementCluster = undefined;
state.workloadClusters = [];
state.completedSteps = [];
state.cloudDomains = [];
state.cloudRegions = [];
Expand Down

0 comments on commit e94bc20

Please sign in to comment.