Skip to content

Commit

Permalink
[GEN-1591]: make "add action" in overview functional (#1650)
Browse files Browse the repository at this point in the history
  • Loading branch information
BenElferink authored Oct 29, 2024
1 parent 3bb2338 commit 2a83b84
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 153 deletions.
74 changes: 24 additions & 50 deletions frontend/webapp/containers/main/overview/add-entity/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useState, useRef, useCallback } from 'react';
import Image from 'next/image';
import theme from '@/styles/theme';
import { useModalStore } from '@/store';
import { AddActionModal } from '../../actions';
import styled, { css } from 'styled-components';
import { useActualSources, useOnClickOutside } from '@/hooks';
Expand Down Expand Up @@ -61,90 +62,63 @@ const ButtonText = styled(Text)`

// Default options for the dropdown
const DEFAULT_OPTIONS: DropdownOption[] = [
{ id: 'source', value: 'Source' },
{ id: 'action', value: 'Action' },
{ id: 'destination', value: 'Destination' },
{ id: OVERVIEW_ENTITY_TYPES.SOURCE, value: 'Source' },
{ id: OVERVIEW_ENTITY_TYPES.ACTION, value: 'Action' },
{ id: OVERVIEW_ENTITY_TYPES.DESTINATION, value: 'Destination' },
];

interface AddEntityButtonDropdownProps {
options?: DropdownOption[];
placeholder?: string;
}

const AddEntityButtonDropdown: React.FC<AddEntityButtonDropdownProps> = ({
options = DEFAULT_OPTIONS,
placeholder = 'ADD...',
}) => {
const [isOpen, setIsOpen] = useState(false);
const AddEntityButtonDropdown: React.FC<AddEntityButtonDropdownProps> = ({ options = DEFAULT_OPTIONS, placeholder = 'ADD...' }) => {
const { currentModal, setCurrentModal } = useModalStore();
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
const [currentModal, setCurrentModal] = useState<string>('');

const { isPolling, createSourcesForNamespace, persistNamespaceItems } =
useActualSources();
const { isPolling, createSourcesForNamespace, persistNamespaceItems } = useActualSources();

useOnClickOutside(dropdownRef, () => setIsOpen(false));
useOnClickOutside(dropdownRef, () => setIsDropdownOpen(false));

const handleToggle = useCallback(() => {
setIsOpen((prev) => !prev);
}, []);
const handleToggle = () => {
setIsDropdownOpen((prev) => !prev);
};

const handleSelect = useCallback((option: DropdownOption) => {
const handleSelect = (option: DropdownOption) => {
setCurrentModal(option.id);
setIsOpen(false);
}, []);
setIsDropdownOpen(false);
};

const handleCloseModal = useCallback(() => {
const handleCloseModal = () => {
setCurrentModal('');
}, []);
};

return (
<Container ref={dropdownRef}>
<StyledButton onClick={handleToggle}>
{isPolling ? (
<FadeLoader color={theme.colors.primary} />
) : (
<Image
src="/icons/common/plus-black.svg"
width={16}
height={16}
alt="Add"
/>
)}
{isPolling ? <FadeLoader color={theme.colors.primary} /> : <Image src='/icons/common/plus-black.svg' width={16} height={16} alt='Add' />}
<ButtonText size={14}>{placeholder}</ButtonText>
</StyledButton>
{isOpen && (
{isDropdownOpen && (
<DropdownListContainer>
{options.map((option) => (
<DropdownItem
key={option.id}
isSelected={false}
onClick={() => handleSelect(option)}
>
<Image
src={`/icons/overview/${option.id}s.svg`}
width={16}
height={16}
alt={`Add ${option.value}`}
/>
<DropdownItem key={option.id} isSelected={false} onClick={() => handleSelect(option)}>
<Image src={`/icons/overview/${option.id}s.svg`} width={16} height={16} alt={`Add ${option.value}`} />
<Text size={14}>{option.value}</Text>
</DropdownItem>
))}
</DropdownListContainer>
)}

<AddSourceModal
isOpen={currentModal === OVERVIEW_ENTITY_TYPES.SOURCE}
onClose={handleCloseModal}
createSourcesForNamespace={createSourcesForNamespace}
persistNamespaceItems={persistNamespaceItems}
/>
<AddDestinationModal
isModalOpen={currentModal === OVERVIEW_ENTITY_TYPES.DESTINATION}
handleCloseModal={handleCloseModal}
/>
<AddActionModal
isModalOpen={currentModal === OVERVIEW_ENTITY_TYPES.ACTION}
handleCloseModal={handleCloseModal}
/>
<AddDestinationModal isModalOpen={currentModal === OVERVIEW_ENTITY_TYPES.DESTINATION} handleCloseModal={handleCloseModal} />
<AddActionModal isModalOpen={currentModal === OVERVIEW_ENTITY_TYPES.ACTION} handleCloseModal={handleCloseModal} />
</Container>
);
};
Expand Down
19 changes: 11 additions & 8 deletions frontend/webapp/hooks/overview/useNodeDataFlowHandlers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// src/hooks/useNodeDataFlowHandlers.ts
import { useCallback } from 'react';
import { useDrawerStore } from '@/store';
import { K8sActualSource, ActualDestination, ActionDataParsed, OVERVIEW_ENTITY_TYPES } from '@/types';
import { useDrawerStore, useModalStore } from '@/store';
import { K8sActualSource, ActualDestination, ActionDataParsed, OVERVIEW_ENTITY_TYPES, OVERVIEW_NODE_TYPES } from '@/types';

export function useNodeDataFlowHandlers({
sources,
Expand All @@ -12,6 +12,7 @@ export function useNodeDataFlowHandlers({
actions: ActionDataParsed[];
destinations: ActualDestination[];
}) {
const { setCurrentModal } = useModalStore();
const setSelectedItem = useDrawerStore(({ setSelectedItem }) => setSelectedItem);

const handleNodeClick = useCallback(
Expand All @@ -31,9 +32,7 @@ export function useNodeDataFlowHandlers({
type,
item: selectedDrawerItem,
});
}

if (type === OVERVIEW_ENTITY_TYPES.ACTION) {
} else if (type === OVERVIEW_ENTITY_TYPES.ACTION) {
const selectedDrawerItem = actions.find((action) => action.id === id);
if (!selectedDrawerItem) return;

Expand All @@ -42,9 +41,7 @@ export function useNodeDataFlowHandlers({
type,
item: selectedDrawerItem,
});
}

if (type === OVERVIEW_ENTITY_TYPES.DESTINATION) {
} else if (type === OVERVIEW_ENTITY_TYPES.DESTINATION) {
const selectedDrawerItem = destinations.find((destination) => destination.id === id);
if (!selectedDrawerItem) return;

Expand All @@ -53,6 +50,12 @@ export function useNodeDataFlowHandlers({
type,
item: selectedDrawerItem,
});
} else if (type === OVERVIEW_NODE_TYPES.ADD_SOURCE) {
setCurrentModal(OVERVIEW_ENTITY_TYPES.SOURCE);
} else if (type === OVERVIEW_NODE_TYPES.ADD_ACTION) {
setCurrentModal(OVERVIEW_ENTITY_TYPES.ACTION);
} else if (type === OVERVIEW_NODE_TYPES.ADD_DESTIONATION) {
setCurrentModal(OVERVIEW_ENTITY_TYPES.DESTINATION);
}
},
[sources, actions, destinations, setSelectedItem]
Expand Down
121 changes: 69 additions & 52 deletions frontend/webapp/reuseable-components/nodes-data-flow/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import theme from '@/styles/theme';
import { getActionIcon } from '@/utils';
import { Node, Edge } from 'react-flow-renderer';
import { getMainContainerLanguageLogo } from '@/utils/constants/programming-languages';
import type { ActionData, ActionItem, ActualDestination, K8sActualSource } from '@/types';
import { OVERVIEW_ENTITY_TYPES, OVERVIEW_NODE_TYPES, type ActionData, type ActionItem, type ActualDestination, type K8sActualSource } from '@/types';

// Constants
const NODE_HEIGHT = 80;
Expand Down Expand Up @@ -56,20 +56,30 @@ export const buildNodesAndEdges = ({
title: 'Sources',
tagValue: sources.length,
}),
...sources.map((source, index) =>
createNode(`source-${index}`, 'base', leftColumnX, NODE_HEIGHT * (index + 1), {
type: 'source',
title: source.name + (source.reportedName ? ` (${source.reportedName})` : ''),
subTitle: source.kind,
imageUri: getMainContainerLanguageLogo(source),
status: 'healthy',
id: {
kind: source.kind,
name: source.name,
namespace: source.namespace,
},
})
),
...(!sources.length
? [
createNode(`source-0`, 'add', leftColumnX, NODE_HEIGHT, {
type: OVERVIEW_NODE_TYPES.ADD_SOURCE,
title: 'ADD SOURCE',
subTitle: 'Add first source to collect OpenTelemetry data',
imageUri: '',
status: 'healthy',
}),
]
: sources.map((source, index) =>
createNode(`source-${index}`, 'base', leftColumnX, NODE_HEIGHT * (index + 1), {
type: OVERVIEW_ENTITY_TYPES.SOURCE,
title: source.name + (source.reportedName ? ` (${source.reportedName})` : ''),
subTitle: source.kind,
imageUri: getMainContainerLanguageLogo(source),
status: 'healthy',
id: {
kind: source.kind,
name: source.name,
namespace: source.namespace,
},
})
)),
];

// Build Destination Nodes
Expand All @@ -79,17 +89,27 @@ export const buildNodesAndEdges = ({
title: 'Destinations',
tagValue: destinations.length,
}),
...destinations.map((destination, index) =>
createNode(`destination-${index}`, 'base', rightColumnX, NODE_HEIGHT * (index + 1), {
type: 'destination',
title: destination.name,
subTitle: destination.destinationType.displayName,
imageUri: destination.destinationType.imageUrl,
status: 'healthy',
monitors: extractMonitors(destination.exportedSignals),
id: destination.id,
})
),
...(!destinations.length
? [
createNode(`destination-0`, 'add', rightColumnX, NODE_HEIGHT, {
type: OVERVIEW_NODE_TYPES.ADD_DESTIONATION,
title: 'ADD DESTIONATION',
subTitle: 'Add first destination to monitor OpenTelemetry data',
imageUri: '',
status: 'healthy',
}),
]
: destinations.map((destination, index) =>
createNode(`destination-${index}`, 'base', rightColumnX, NODE_HEIGHT * (index + 1), {
type: OVERVIEW_ENTITY_TYPES.DESTINATION,
title: destination.name,
subTitle: destination.destinationType.displayName,
imageUri: destination.destinationType.imageUrl,
status: 'healthy',
monitors: extractMonitors(destination.exportedSignals),
id: destination.id,
})
)),
];

// Build Action Nodes
Expand All @@ -99,34 +119,31 @@ export const buildNodesAndEdges = ({
title: 'Actions',
tagValue: actions.length,
}),
...actions.map((action, index) => {
const actionSpec: ActionItem = typeof action.spec === 'string' ? JSON.parse(action.spec) : (action.spec as ActionItem);

return createNode(`action-${index}`, 'base', centerColumnX, NODE_HEIGHT * (index + 1), {
type: 'action',
title: actionSpec.actionName,
subTitle: action.type,
imageUri: getActionIcon(action.type),
monitors: actionSpec.signals,
status: 'healthy',
id: action.id,
});
}),
...(!actions.length
? [
createNode(`action-0`, 'add', centerColumnX, NODE_HEIGHT, {
type: OVERVIEW_NODE_TYPES.ADD_ACTION,
title: 'ADD ACTION',
subTitle: 'Add first action to modify the OpenTelemetry data',
imageUri: '',
status: 'healthy',
}),
]
: actions.map((action, index) => {
const actionSpec: ActionItem = typeof action.spec === 'string' ? JSON.parse(action.spec) : (action.spec as ActionItem);

return createNode(`action-${index}`, 'base', centerColumnX, NODE_HEIGHT * (index + 1), {
type: OVERVIEW_ENTITY_TYPES.ACTION,
title: actionSpec.actionName,
subTitle: action.type,
imageUri: getActionIcon(action.type),
monitors: actionSpec.signals,
status: 'healthy',
id: action.id,
});
})),
];

if (actionsNode.length === 1) {
actionsNode.push(
createNode(`action-0`, 'addAction', centerColumnX, NODE_HEIGHT * (actions.length + 1), {
type: 'addAction',
title: 'ADD ACTION',
subTitle: '',
imageUri: getActionIcon(),
status: 'healthy',
onClick: () => console.log('Add Action'),
})
);
}

// Combine all nodes
const nodes = [...sourcesNode, ...destinationNode, ...actionsNode];

Expand Down
20 changes: 5 additions & 15 deletions frontend/webapp/reuseable-components/nodes-data-flow/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import '@xyflow/react/dist/style.css';
import BaseNode from './nodes/base-node';
import { ReactFlow } from '@xyflow/react';
import headerNode from './nodes/header-node';
import AddActionNode from './nodes/add-action-node';
import AddNode from './nodes/add-node';

const nodeTypes = {
base: BaseNode,
header: headerNode,
addAction: AddActionNode,
add: AddNode,
base: BaseNode,
};

interface NodeBaseDataFlowProps {
Expand All @@ -18,20 +18,10 @@ interface NodeBaseDataFlowProps {
onNodeClick?: (event: React.MouseEvent, object: any) => void;
}

export function NodeBaseDataFlow({
nodes,
edges,
onNodeClick,
}: NodeBaseDataFlowProps) {
export function NodeBaseDataFlow({ nodes, edges, onNodeClick }: NodeBaseDataFlowProps) {
return (
<div style={{ height: 'calc(100vh - 100px)' }}>
<ReactFlow
nodeTypes={nodeTypes}
nodes={nodes}
edges={edges}
zoomOnScroll={false}
onNodeClick={onNodeClick}
/>
<ReactFlow nodeTypes={nodeTypes} nodes={nodes} edges={edges} zoomOnScroll={false} onNodeClick={onNodeClick} />
</div>
);
}
Expand Down
Loading

0 comments on commit 2a83b84

Please sign in to comment.