diff --git a/frontend/webapp/containers/main/destinations/add-destination/configured-destinations-list/index.tsx b/frontend/webapp/containers/main/destinations/add-destination/configured-destinations-list/index.tsx
index 89360955a..0991e1761 100644
--- a/frontend/webapp/containers/main/destinations/add-destination/configured-destinations-list/index.tsx
+++ b/frontend/webapp/containers/main/destinations/add-destination/configured-destinations-list/index.tsx
@@ -1,10 +1,11 @@
import React, { useState } from 'react';
import Image from 'next/image';
import styled from 'styled-components';
+import { extractMonitors } from '@/utils';
import { DeleteWarning } from '@/components';
import { IAppState, useAppStore } from '@/store';
import { OVERVIEW_ENTITY_TYPES, type ConfiguredDestination } from '@/types';
-import { Button, DataCardFields, Divider, ExtendIcon, Text } from '@/reuseable-components';
+import { DataCardFields, DataTab, IconButton, Text } from '@/reuseable-components';
const Container = styled.div`
display: flex;
@@ -18,128 +19,26 @@ const Container = styled.div`
overflow-y: scroll;
`;
-const ListItem = styled.div`
- width: 100%;
- border-radius: 16px;
- background: ${({ theme }) => theme.colors.translucent_bg};
-`;
-
-const ListItemBody = styled.div`
- width: 100%;
- padding: 16px;
-`;
-
-const ListItemHeader = styled.div`
- display: flex;
- align-items: center;
- justify-content: space-between;
- width: 100%;
- padding: 16px 0px;
-`;
-
-const ListItemContent = styled.div`
- display: flex;
- gap: 12px;
- margin-left: 16px;
-`;
-
-const DestinationIconWrapper = styled.div`
- display: flex;
- width: 36px;
- height: 36px;
- justify-content: center;
- align-items: center;
- gap: 8px;
- border-radius: 8px;
- background: linear-gradient(180deg, rgba(249, 249, 249, 0.06) 0%, rgba(249, 249, 249, 0.02) 100%);
-`;
-
-const SignalsWrapper = styled.div`
- display: flex;
- align-items: center;
- gap: 4px;
-`;
-
-const SignalText = styled(Text)`
- color: rgba(249, 249, 249, 0.8);
- font-size: 10px;
- text-transform: capitalize;
-`;
-
-const TextWrapper = styled.div`
- display: flex;
- flex-direction: column;
- height: 36px;
- justify-content: space-between;
-`;
-
-const IconsContainer = styled.div`
- display: flex;
- justify-content: center;
- align-items: center;
- margin-right: 16px;
-`;
-
-const IconButton = styled(Button)<{ $expand?: boolean }>`
- transition: background 0.3s ease 0s, transform 0.3s ease 0s;
- transform: ${({ $expand }) => ($expand ? 'rotate(-180deg)' : 'rotate(0deg)')};
-`;
-
-const ConfiguredDestinationsListItem: React.FC<{ item: ConfiguredDestination; isLastItem: boolean }> = ({ item, isLastItem }) => {
- const [expand, setExpand] = useState(false);
- const [deleteWarning, setDeleteWarning] = useState(false);
+const ListItem: React.FC<{ item: ConfiguredDestination; isLastItem: boolean }> = ({ item, isLastItem }) => {
const { removeConfiguredDestination } = useAppStore((state) => state);
-
- function renderSupportedSignals(item: ConfiguredDestination) {
- const supportedSignals = item.exportedSignals;
- const signals = Object.keys(supportedSignals);
- const supportedSignalsList = signals.filter((signal) => supportedSignals[signal].supported);
-
- return Object.keys(supportedSignals).map(
- (signal, index) =>
- supportedSignals[signal] && (
-
-
-
- {signal}
- {index < supportedSignalsList.length - 1 && ·}
-
- ),
- );
- }
+ const [deleteWarning, setDeleteWarning] = useState(false);
return (
<>
-
-
-
-
-
-
-
- {item.displayName}
- {renderSupportedSignals(item)}
-
-
-
-
- setDeleteWarning(true)}>
-
-
-
- setExpand(!expand)}>
-
-
-
-
-
- {expand && (
-
-
-
-
+ }
+ renderActions={() => (
+ setDeleteWarning(true)}>
+
+
)}
-
+ />
{data.map(({ stored }) => (
-
+
))}
);
diff --git a/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx b/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx
index b1feeeb42..5a86fbf4e 100644
--- a/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx
+++ b/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx
@@ -8,7 +8,7 @@ import { useDescribeSource, useSourceCRUD } from '@/hooks';
import OverviewDrawer from '../../overview/overview-drawer';
import { OVERVIEW_ENTITY_TYPES, type WorkloadId, type K8sActualSource } from '@/types';
import { ConditionDetails, DataCard, DataCardRow, DataCardFieldTypes } from '@/reuseable-components';
-import { ACTION, DATA_CARDS, getMainContainerLanguage, getProgrammingLanguageIcon, safeJsonStringify } from '@/utils';
+import { ACTION, BACKEND_BOOLEAN, DATA_CARDS, getMainContainerLanguage, getProgrammingLanguageIcon, safeJsonStringify } from '@/utils';
interface Props {}
@@ -79,13 +79,17 @@ export const SourceDrawer: React.FC = () => {
const { item } = selectedItem as { item: K8sActualSource };
+ const hasPresenceOfOtherAgent = item.instrumentedApplicationDetails.conditions.some(
+ (condition) => condition.status === BACKEND_BOOLEAN.FALSE && condition.message.includes('device not added to any container due to the presence of another agent'),
+ );
+
return (
item.instrumentedApplicationDetails.containers.map(
(container) =>
({
type: DataCardFieldTypes.SOURCE_CONTAINER,
width: '100%',
- value: JSON.stringify(container),
+ value: JSON.stringify({ ...container, hasPresenceOfOtherAgent }),
} as DataCardRow),
) || []
);
diff --git a/frontend/webapp/reuseable-components/condition-details/index.tsx b/frontend/webapp/reuseable-components/condition-details/index.tsx
index 392a66c06..3c91cb800 100644
--- a/frontend/webapp/reuseable-components/condition-details/index.tsx
+++ b/frontend/webapp/reuseable-components/condition-details/index.tsx
@@ -60,7 +60,7 @@ export const ConditionDetails: React.FC = ({ conditions }) => {
({hasErrors ? errors.length : conditions.length}/{conditions.length})
-
+
{extend && (
diff --git a/frontend/webapp/reuseable-components/data-card/data-card-fields/index.tsx b/frontend/webapp/reuseable-components/data-card/data-card-fields/index.tsx
index c09ce002e..cfdfb8ceb 100644
--- a/frontend/webapp/reuseable-components/data-card/data-card-fields/index.tsx
+++ b/frontend/webapp/reuseable-components/data-card/data-card-fields/index.tsx
@@ -1,7 +1,8 @@
import React, { useId } from 'react';
import styled from 'styled-components';
-import { ActiveStatus, Code, DataTab, Divider, InstrumentStatus, MonitorsIcons, Text, Tooltip } from '@/reuseable-components';
+import { ActiveStatus, Code, DataTab, Divider, InstrumentStatus, MonitorsIcons, NotificationNote, Text, Tooltip } from '@/reuseable-components';
import { capitalizeFirstLetter, getProgrammingLanguageIcon, parseJsonStringToPrettyString, safeJsonParse, WORKLOAD_PROGRAMMING_LANGUAGES } from '@/utils';
+import { NOTIFICATION_TYPE } from '@/types';
export enum DataCardFieldTypes {
DIVIDER = 'divider',
@@ -81,20 +82,37 @@ const renderValue = (type: DataCardRow['type'], value: DataCardRow['value']) =>
return ;
case DataCardFieldTypes.SOURCE_CONTAINER: {
- const { containerName, language, runtimeVersion } = safeJsonParse(value, {
+ const { containerName, language, runtimeVersion, otherAgent, hasPresenceOfOtherAgent } = safeJsonParse(value, {
containerName: '-',
language: WORKLOAD_PROGRAMMING_LANGUAGES.UNKNOWN,
runtimeVersion: '-',
+ otherAgent: null,
+ hasPresenceOfOtherAgent: false,
});
+ // Determine if running concurrently is possible based on language and other_agent
+ const canRunInParallel = !hasPresenceOfOtherAgent && (language === WORKLOAD_PROGRAMMING_LANGUAGES.PYTHON || language === WORKLOAD_PROGRAMMING_LANGUAGES.JAVA);
+
return (
-
-
+ isExtended={!!otherAgent}
+ renderExtended={() => (
+
+ )}
+ renderActions={() => }
+ />
);
}
diff --git a/frontend/webapp/reuseable-components/data-tab/index.tsx b/frontend/webapp/reuseable-components/data-tab/index.tsx
index 71575cc7e..1fa6cbf4b 100644
--- a/frontend/webapp/reuseable-components/data-tab/index.tsx
+++ b/frontend/webapp/reuseable-components/data-tab/index.tsx
@@ -1,24 +1,28 @@
-import React, { PropsWithChildren, useCallback } from 'react';
+import React, { Fragment, useCallback, useState } from 'react';
import Image from 'next/image';
-import { FlexColumn } from '@/styles';
+import { FlexColumn, FlexRow } from '@/styles';
import styled, { css } from 'styled-components';
-import { ActiveStatus, MonitorsIcons, Text } from '@/reuseable-components';
+import { ActiveStatus, Divider, ExtendIcon, IconButton, MonitorsIcons, Text } from '@/reuseable-components';
-interface Props extends PropsWithChildren {
+interface Props {
title: string;
subTitle: string;
logo: string;
monitors?: string[];
+ monitorsWithLabels?: boolean;
isActive?: boolean;
isError?: boolean;
+ withExtend?: boolean;
+ isExtended?: boolean;
+ renderExtended?: () => JSX.Element;
+ renderActions?: () => JSX.Element;
onClick?: () => void;
}
const Container = styled.div<{ $withClick: boolean; $isError: Props['isError'] }>`
display: flex;
- align-items: center;
+ flex-direction: column;
align-self: stretch;
- gap: 8px;
padding: 16px;
width: calc(100% - 32px);
border-radius: 16px;
@@ -51,6 +55,7 @@ const Title = styled(Text)`
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
+ font-size: 14px;
`;
const SubTitleWrapper = styled.div`
@@ -71,45 +76,72 @@ const ActionsWrapper = styled.div`
margin-left: auto;
`;
-export const DataTab: React.FC = ({ title, subTitle, logo, monitors, isActive, isError, onClick, children }) => {
- const renderMonitors = useCallback(() => {
- if (!monitors) return null;
-
- return (
- <>
- {'•'}
-
- >
- );
- }, [monitors]);
-
- const renderActiveStatus = useCallback(() => {
- if (typeof isActive !== 'boolean') return null;
-
- return (
- <>
- {'•'}
-
- >
- );
- }, [isActive]);
+export const DataTab: React.FC = ({ title, subTitle, logo, monitors, monitorsWithLabels, isActive, isError, withExtend, isExtended, renderExtended, renderActions, onClick }) => {
+ const [extend, setExtend] = useState(isExtended || false);
+
+ const renderMonitors = useCallback(
+ (withSeperator: boolean) => {
+ if (!monitors || !monitors.length) return null;
+
+ return (
+ <>
+ {withSeperator && {'•'}}
+
+ >
+ );
+ },
+ [monitors],
+ );
+
+ const renderActiveStatus = useCallback(
+ (withSeperator: boolean) => {
+ if (typeof isActive !== 'boolean') return null;
+
+ return (
+ <>
+ {withSeperator && {'•'}}
+
+ >
+ );
+ },
+ [isActive],
+ );
return (
-
-
-
-
-
- {title}
-
- {subTitle}
- {renderMonitors()}
- {renderActiveStatus()}
-
-
-
- {children}
+
+
+
+
+
+
+ {title}
+
+ {subTitle && {subTitle}}
+ {renderMonitors(!!subTitle)}
+ {renderActiveStatus(!!monitors?.length)}
+
+
+
+
+ {renderActions && renderActions()}
+ {withExtend && (
+
+
+ setExtend((prev) => !prev)}>
+
+
+
+ )}
+
+
+
+ {extend && renderExtended && (
+
+
+ {renderExtended()}
+
+ )}
);
};
diff --git a/frontend/webapp/reuseable-components/extend-icon/index.tsx b/frontend/webapp/reuseable-components/extend-icon/index.tsx
index c36caae5e..62b93d8e4 100644
--- a/frontend/webapp/reuseable-components/extend-icon/index.tsx
+++ b/frontend/webapp/reuseable-components/extend-icon/index.tsx
@@ -5,10 +5,11 @@ import styled from 'styled-components';
interface Props {
extend: boolean;
size?: number;
- align?: 'left' | 'right';
+ align?: 'left' | 'right' | 'center';
}
const Icon = styled(Image)<{ $align?: Props['align'] }>`
+ margin: ${({ $align }) => ($align === 'right' ? 'auto 0 auto auto' : $align === 'left' ? 'auto auto auto 0' : 'auto')};
&.open {
transform: rotate(180deg);
}
@@ -16,9 +17,8 @@ const Icon = styled(Image)<{ $align?: Props['align'] }>`
transform: rotate(0deg);
}
transition: transform 0.3s;
- margin-${({ $align }) => ($align === 'right' ? 'left' : 'right')}: auto;
`;
-export const ExtendIcon: React.FC = ({ extend, size = 14, align = 'right' }) => {
+export const ExtendIcon: React.FC = ({ extend, size = 14, align = 'center' }) => {
return ;
};
diff --git a/frontend/webapp/reuseable-components/monitors-icons/index.tsx b/frontend/webapp/reuseable-components/monitors-icons/index.tsx
index 9c580d451..ccd8ae1fa 100644
--- a/frontend/webapp/reuseable-components/monitors-icons/index.tsx
+++ b/frontend/webapp/reuseable-components/monitors-icons/index.tsx
@@ -1,5 +1,6 @@
import React from 'react';
import Image from 'next/image';
+import theme from '@/styles/theme';
import { FlexRow } from '@/styles';
import { capitalizeFirstLetter } from '@/utils';
import { Text, Tooltip } from '@/reuseable-components';
@@ -15,14 +16,18 @@ export const MonitorsIcons: React.FC = ({ monitors, withTooltips, withLab
return (
{monitors.map((str) => {
- const signal = str.toLocaleLowerCase();
+ const signal = str.toLowerCase();
const signalDisplayName = capitalizeFirstLetter(signal);
return (
- {withLabels && {signalDisplayName}}
+ {withLabels && (
+
+ {signalDisplayName}
+
+ )}
);
diff --git a/frontend/webapp/reuseable-components/nodes-data-flow/nodes/base-node.tsx b/frontend/webapp/reuseable-components/nodes-data-flow/nodes/base-node.tsx
index 583103e5b..e7ea1a084 100644
--- a/frontend/webapp/reuseable-components/nodes-data-flow/nodes/base-node.tsx
+++ b/frontend/webapp/reuseable-components/nodes-data-flow/nodes/base-node.tsx
@@ -74,10 +74,7 @@ const BaseNode: React.FC = ({ id: nodeId, data }) => {
return (
- {}}>
- {renderActions()}
-
-
+ {}} renderActions={renderActions} />