diff --git a/frontend/webapp/components/common/card-details/index.tsx b/frontend/webapp/components/common/card-details/index.tsx deleted file mode 100644 index 752e3bca0..000000000 --- a/frontend/webapp/components/common/card-details/index.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react'; -import styled from 'styled-components'; -import { Text } from '@/reuseable-components'; -import { ConfiguredFields } from '@/components'; - -interface Props { - title?: string; - data: { - title: string; - tooltip?: string; - value: string; - }[]; -} - -const Container = styled.div` - display: flex; - flex-direction: column; - padding: 16px 24px 24px 24px; - flex-direction: column; - align-items: flex-start; - gap: 16px; - align-self: stretch; - border-radius: 24px; - border: 1px solid ${({ theme }) => theme.colors.border}; -`; - -const TitleWrapper = styled.div``; - -export const CardDetails: React.FC = ({ title = 'Details', data }) => { - return ( - - - {title} - - - - ); -}; diff --git a/frontend/webapp/components/common/configured-fields/index.tsx b/frontend/webapp/components/common/configured-fields/index.tsx deleted file mode 100644 index e268efc7c..000000000 --- a/frontend/webapp/components/common/configured-fields/index.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import React, { Fragment } from 'react'; -import styled from 'styled-components'; -import { Text, Status, Tooltip } from '@/reuseable-components'; -import { MonitorsLegend } from '@/components/overview'; -import theme from '@/styles/theme'; - -interface Detail { - title: string; - tooltip?: string; - value: string; -} - -interface Props { - details: Detail[]; -} - -const ListContainer = styled.div` - display: flex; - flex-wrap: wrap; - gap: 24px 40px; -`; - -const ListItem = styled.div``; - -const ItemTitle = styled(Text)` - color: ${({ theme }) => theme.text.grey}; - font-size: 10px; - line-height: 16px; -`; - -const ItemValue = styled(Text)` - color: ${({ theme }) => theme.colors.text}; - font-size: 12px; - line-height: 18px; -`; - -export const ConfiguredFields: React.FC = ({ details }) => { - const parseValue = (value: string) => { - let str = ''; - - try { - const parsed = JSON.parse(value); - - // Handle arrays - if (Array.isArray(parsed)) { - str = parsed - .map((item) => { - if (typeof item === 'object' && item !== null) return `${item.key}: ${item.value}`; - else return item; - }) - .join(', '); - } - - // Handle objects (non-array JSON objects) - else if (typeof parsed === 'object' && parsed !== null) { - str = Object.entries(parsed) - .map(([key, val]) => `${key}: ${val}`) - .join(', '); - } - - // Should never reach this if it's a string (it will throw) - else { - str = value; - } - } catch (error) { - str = value; - } - - const strSplitted = str.split('\n'); - - return strSplitted.map((str, idx) => ( - - {str} - {idx < strSplitted.length - 1 ?
: null} -
- )); - }; - - const renderValue = (title: string, value: string) => { - switch (title) { - case 'Status': - return ; - case 'Monitors': - return ; - default: - return {parseValue(value)}; - } - }; - - return ( - - {details.map((detail, index) => ( - - - {detail.title} - - {renderValue(detail.title, detail.value)} - - ))} - - ); -}; diff --git a/frontend/webapp/components/common/index.ts b/frontend/webapp/components/common/index.ts index a9076a4d9..00da52a3e 100644 --- a/frontend/webapp/components/common/index.ts +++ b/frontend/webapp/components/common/index.ts @@ -1,3 +1 @@ -export * from './card-details'; -export * from './configured-fields'; export * from './dropdowns'; diff --git a/frontend/webapp/containers/main/actions/action-drawer/build-card.ts b/frontend/webapp/containers/main/actions/action-drawer/build-card.ts index 50953951c..409e49ee4 100644 --- a/frontend/webapp/containers/main/actions/action-drawer/build-card.ts +++ b/frontend/webapp/containers/main/actions/action-drawer/build-card.ts @@ -1,4 +1,6 @@ +import { DISPLAY_TITLES } from '@/utils'; import type { ActionDataParsed } from '@/types'; +import { DataCardFieldTypes, type DataCardRow } from '@/reuseable-components'; const buildCard = (action: ActionDataParsed) => { const { @@ -19,19 +21,20 @@ const buildCard = (action: ActionDataParsed) => { }, } = action; - const arr = [ - { title: 'Type', value: type }, - { title: 'Status', value: String(!disabled) }, - { title: 'Monitors', value: signals.map((str) => str.toLowerCase()).join(', ') }, - { title: 'Name', value: actionName || 'N/A' }, - { title: 'Notes', value: notes || 'N/A' }, + const arr: DataCardRow[] = [ + { title: DISPLAY_TITLES.TYPE, value: type }, + { title: DISPLAY_TITLES.NAME, value: actionName }, + { title: DISPLAY_TITLES.NOTES, value: notes }, + { type: DataCardFieldTypes.DIVIDER, width: '100%' }, + { type: DataCardFieldTypes.ACTIVE_STATUS, title: DISPLAY_TITLES.STATUS, value: String(!disabled) }, + { type: DataCardFieldTypes.MONITORS, title: DISPLAY_TITLES.SIGNALS_FOR_PROCESSING, value: signals.map((str) => str.toLowerCase()).join(', ') }, ]; if (clusterAttributes) { let str = ''; clusterAttributes.forEach(({ attributeName, attributeStringValue }, idx) => { str += `${attributeName}: ${attributeStringValue}`; - if (idx < clusterAttributes.length - 1) str += '\n'; + if (idx < clusterAttributes.length - 1) str += ', '; }); arr.push({ title: 'Attributes', value: str }); diff --git a/frontend/webapp/containers/main/actions/action-drawer/index.tsx b/frontend/webapp/containers/main/actions/action-drawer/index.tsx index 9204193a2..d0b0936e7 100644 --- a/frontend/webapp/containers/main/actions/action-drawer/index.tsx +++ b/frontend/webapp/containers/main/actions/action-drawer/index.tsx @@ -3,9 +3,9 @@ import buildCard from './build-card'; import { ActionFormBody } from '../'; import styled from 'styled-components'; import { useDrawerStore } from '@/store'; -import { CardDetails } from '@/components'; -import { ACTION, getActionIcon } from '@/utils'; +import { ACTION, DATA_CARDS, getActionIcon } from '@/utils'; import buildDrawerItem from './build-drawer-item'; +import { DataCard } from '@/reuseable-components'; import { useActionCRUD, useActionFormData } from '@/hooks'; import OverviewDrawer from '../../overview/overview-drawer'; import { ACTION_OPTIONS } from '../action-modal/action-options'; @@ -120,7 +120,7 @@ export const ActionDrawer: React.FC = () => { /> ) : ( - + )} ); 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 517fe575f..89360955a 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,10 @@ import React, { useState } from 'react'; import Image from 'next/image'; import styled from 'styled-components'; +import { DeleteWarning } from '@/components'; import { IAppState, useAppStore } from '@/store'; -import { ConfiguredFields, DeleteWarning } from '@/components'; -import { Button, Divider, ExtendIcon, Text } from '@/reuseable-components'; import { OVERVIEW_ENTITY_TYPES, type ConfiguredDestination } from '@/types'; +import { Button, DataCardFields, Divider, ExtendIcon, Text } from '@/reuseable-components'; const Container = styled.div` display: flex; @@ -12,10 +12,10 @@ const Container = styled.div` align-items: flex-start; gap: 12px; margin-top: 24px; - align-self: stretch; + max-height: calc(100vh - 400px); height: 100%; - max-height: 548px; - overflow-y: auto; + overflow-x: hidden; + overflow-y: scroll; `; const ListItem = styled.div` @@ -38,9 +38,9 @@ const ListItemHeader = styled.div` `; const ListItemContent = styled.div` - margin-left: 16px; display: flex; gap: 12px; + margin-left: 16px; `; const DestinationIconWrapper = styled.div` @@ -135,8 +135,8 @@ const ConfiguredDestinationsListItem: React.FC<{ item: ConfiguredDestination; is {expand && ( - - + + )} diff --git a/frontend/webapp/containers/main/destinations/destination-drawer/build-card.ts b/frontend/webapp/containers/main/destinations/destination-drawer/build-card.ts index c9868e1ea..0cf3a2cd0 100644 --- a/frontend/webapp/containers/main/destinations/destination-drawer/build-card.ts +++ b/frontend/webapp/containers/main/destinations/destination-drawer/build-card.ts @@ -1,17 +1,19 @@ -import { ActualDestination, DestinationDetailsResponse, ExportedSignals } from '@/types'; -import { safeJsonParse } from '@/utils'; +import { DISPLAY_TITLES, safeJsonParse } from '@/utils'; +import { DataCardRow, DataCardFieldTypes } from '@/reuseable-components'; +import type { ActualDestination, DestinationDetailsResponse, ExportedSignals } from '@/types'; const buildMonitorsList = (exportedSignals: ExportedSignals): string => Object.keys(exportedSignals) .filter((key) => exportedSignals[key]) - .join(', ') || 'N/A'; + .join(', '); const buildCard = (destination: ActualDestination, destinationTypeDetails: DestinationDetailsResponse['destinationTypeDetails']) => { const { exportedSignals, destinationType, fields } = destination; - const arr = [ - { title: 'Destination', value: destinationType.displayName }, - { title: 'Monitors', value: buildMonitorsList(exportedSignals) }, + const arr: DataCardRow[] = [ + { title: DISPLAY_TITLES.DESTINATION, value: destinationType.displayName }, + { type: DataCardFieldTypes.MONITORS, title: DISPLAY_TITLES.MONITORS, value: buildMonitorsList(exportedSignals) }, + { type: DataCardFieldTypes.DIVIDER, width: '100%' }, ]; Object.entries(safeJsonParse>(fields, {})).map(([key, value]) => { @@ -22,7 +24,7 @@ const buildCard = (destination: ActualDestination, destinationTypeDetails: Desti arr.push({ title: found?.displayName || key, - value: secret || value || 'N/A', + value: secret || value, }); }); diff --git a/frontend/webapp/containers/main/destinations/destination-drawer/index.tsx b/frontend/webapp/containers/main/destinations/destination-drawer/index.tsx index fba80220d..77884b9cc 100644 --- a/frontend/webapp/containers/main/destinations/destination-drawer/index.tsx +++ b/frontend/webapp/containers/main/destinations/destination-drawer/index.tsx @@ -1,13 +1,12 @@ import React, { useMemo, useState } from 'react'; -import { ACTION } from '@/utils'; +import { ACTION, DATA_CARDS } from '@/utils'; import buildCard from './build-card'; import styled from 'styled-components'; import { useDrawerStore } from '@/store'; -import { CardDetails } from '@/components'; import buildDrawerItem from './build-drawer-item'; -import { ConditionDetails } from '@/reuseable-components'; import OverviewDrawer from '../../overview/overview-drawer'; import { DestinationFormBody } from '../destination-form-body'; +import { ConditionDetails, DataCard } from '@/reuseable-components'; import { OVERVIEW_ENTITY_TYPES, type ActualDestination } from '@/types'; import { useDestinationCRUD, useDestinationFormData, useDestinationTypes } from '@/hooks'; @@ -138,7 +137,7 @@ export const DestinationDrawer: React.FC = () => { ) : ( - + )} diff --git a/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/build-card.ts b/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/build-card.ts index b1f617598..75378c81e 100644 --- a/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/build-card.ts +++ b/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/build-card.ts @@ -1,13 +1,16 @@ +import { DISPLAY_TITLES } from '@/utils'; import type { InstrumentationRuleSpec } from '@/types'; +import { DataCardRow, DataCardFieldTypes } from '@/reuseable-components'; const buildCard = (rule: InstrumentationRuleSpec) => { const { type, ruleName, notes, disabled, payloadCollection } = rule; - const arr = [ - { title: 'Type', value: type || 'N/A' }, - { title: 'Status', value: String(!disabled) }, - { title: 'Name', value: ruleName || 'N/A' }, - { title: 'Notes', value: notes || 'N/A' }, + const arr: DataCardRow[] = [ + { title: DISPLAY_TITLES.TYPE, value: type }, + { title: DISPLAY_TITLES.NAME, value: ruleName }, + { title: DISPLAY_TITLES.NOTES, value: notes }, + { type: DataCardFieldTypes.DIVIDER, width: '100%' }, + { type: DataCardFieldTypes.ACTIVE_STATUS, title: DISPLAY_TITLES.STATUS, value: String(!disabled) }, ]; if (payloadCollection) { diff --git a/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/index.tsx b/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/index.tsx index af379dfce..7a5921f9d 100644 --- a/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/index.tsx +++ b/frontend/webapp/containers/main/instrumentation-rules/rule-drawer/index.tsx @@ -3,8 +3,8 @@ import buildCard from './build-card'; import { RuleFormBody } from '../'; import styled from 'styled-components'; import { useDrawerStore } from '@/store'; -import { CardDetails } from '@/components'; -import { ACTION, getRuleIcon } from '@/utils'; +import { ACTION, DATA_CARDS, getRuleIcon } from '@/utils'; +import { DataCard } from '@/reuseable-components'; import buildDrawerItem from './build-drawer-item'; import { RULE_OPTIONS } from '../rule-modal/rule-options'; import OverviewDrawer from '../../overview/overview-drawer'; @@ -117,7 +117,7 @@ export const RuleDrawer: React.FC = () => { /> ) : ( - + )} ); diff --git a/frontend/webapp/containers/main/overview/overview-drawer/drawer-header/index.tsx b/frontend/webapp/containers/main/overview/overview-drawer/drawer-header/index.tsx index d1a4cc9fe..427002838 100644 --- a/frontend/webapp/containers/main/overview/overview-drawer/drawer-header/index.tsx +++ b/frontend/webapp/containers/main/overview/overview-drawer/drawer-header/index.tsx @@ -26,6 +26,10 @@ const InputWrapper = styled(SectionItemsWrapper)` const Title = styled(Text)` font-size: 18px; line-height: 26px; + max-width: 400px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; `; const DrawerItemImageWrapper = styled.div` diff --git a/frontend/webapp/containers/main/sources/source-drawer-container/build-card.ts b/frontend/webapp/containers/main/sources/source-drawer-container/build-card.ts index 151c7c615..0f27272ef 100644 --- a/frontend/webapp/containers/main/sources/source-drawer-container/build-card.ts +++ b/frontend/webapp/containers/main/sources/source-drawer-container/build-card.ts @@ -1,15 +1,17 @@ +import { DISPLAY_TITLES } from '@/utils'; import type { K8sActualSource } from '@/types'; +import { DataCardRow } from '@/reuseable-components'; const buildCard = (source: K8sActualSource) => { const { name, kind, namespace, instrumentedApplicationDetails } = source; const { containerName, language } = instrumentedApplicationDetails?.containers?.[0] || {}; - const arr = [ - { title: 'Namespace', value: namespace }, - { title: 'Kind', value: kind }, - { title: 'Name', value: name }, - { title: 'Container Name', value: containerName || 'N/A' }, - { title: 'Language', value: language || 'N/A' }, + const arr: DataCardRow[] = [ + { title: DISPLAY_TITLES.NAMESPACE, value: namespace }, + { title: DISPLAY_TITLES.KIND, value: kind }, + { title: DISPLAY_TITLES.CONTAINER_NAME, value: containerName }, + { title: DISPLAY_TITLES.NAME, value: name }, + { title: DISPLAY_TITLES.LANGUAGE, value: language }, ]; return arr; 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 2656912fa..6f9381e93 100644 --- a/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx +++ b/frontend/webapp/containers/main/sources/source-drawer-container/index.tsx @@ -3,13 +3,12 @@ import buildCard from './build-card'; import styled from 'styled-components'; import { useSourceCRUD } from '@/hooks'; import { useDrawerStore } from '@/store'; -import { CardDetails } from '@/components'; import buildDrawerItem from './build-drawer-item'; import { UpdateSourceBody } from '../update-source-body'; import OverviewDrawer from '../../overview/overview-drawer'; -import { ConditionDetails, ContainerDetails } from '@/reuseable-components'; -import { ACTION, getMainContainerLanguage, getProgrammingLanguageIcon } from '@/utils'; import { OVERVIEW_ENTITY_TYPES, type WorkloadId, type K8sActualSource } from '@/types'; +import { ACTION, DATA_CARDS, getMainContainerLanguage, getProgrammingLanguageIcon } from '@/utils'; +import { ConditionDetails, DataCard, DataCardRow, DataCardFieldTypes } from '@/reuseable-components'; interface Props {} @@ -75,6 +74,23 @@ export const SourceDrawer: React.FC = () => { return arr; }, [selectedItem]); + const containersData = useMemo(() => { + if (!selectedItem) return []; + + const { item } = selectedItem as { item: K8sActualSource }; + + return ( + item.instrumentedApplicationDetails.containers.map( + (container) => + ({ + type: DataCardFieldTypes.SOURCE_CONTAINER, + width: '100%', + value: JSON.stringify(container), + } as DataCardRow), + ) || [] + ); + }, [selectedItem]); + if (!selectedItem?.item) return null; const { id, item } = selectedItem as { id: WorkloadId; item: K8sActualSource }; @@ -123,8 +139,8 @@ export const SourceDrawer: React.FC = () => { ) : ( - - + + )} diff --git a/frontend/webapp/reuseable-components/container-details/index.tsx b/frontend/webapp/reuseable-components/container-details/index.tsx deleted file mode 100644 index 649dbeb02..000000000 --- a/frontend/webapp/reuseable-components/container-details/index.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import React from 'react'; -import Image from 'next/image'; -import theme from '@/styles/theme'; -import styled from 'styled-components'; -import type { SourceContainer } from '@/types'; -import { Badge, DataTab, Text } from '@/reuseable-components'; -import { capitalizeFirstLetter, getProgrammingLanguageIcon, getStatusIcon, INSTUMENTATION_STATUS, WORKLOAD_PROGRAMMING_LANGUAGES } from '@/utils'; - -interface Props { - containers: SourceContainer[]; -} - -const Container = styled.div` - display: flex; - flex-direction: column; - gap: 16px; - padding: 16px 24px 24px 24px; - border-radius: 24px; - border: 1px solid ${({ theme }) => theme.colors.border}; -`; - -const Header = styled.div` - display: flex; - flex-direction: column; - gap: 4px; -`; - -const Title = styled(Text)` - display: flex; - align-items: center; - gap: 8px; - font-size: 16px; -`; - -const Description = styled(Text)` - font-size: 12px; - color: ${({ theme }) => theme.text.grey}; -`; - -const Body = styled.div` - display: flex; - flex-direction: column; - gap: 12px; -`; - -const InstrumentStatus = styled.div<{ $active: boolean }>` - display: flex; - align-items: center; - gap: 2px; - padding: 2px 6px; - margin-left: auto; - border-radius: 360px; - border: 1px solid ${({ $active, theme }) => ($active ? theme.colors.dark_green : theme.colors.border)}; -`; - -export const ContainerDetails: React.FC = ({ containers }) => { - return ( - -
- - Detected Containers <Badge label={containers.length} /> - - The system automatically instruments the containers it detects with a supported programming language. -
- - - {containers.map(({ containerName, language, runtimeVersion }, idx) => { - const active = ![ - WORKLOAD_PROGRAMMING_LANGUAGES.IGNORED, - WORKLOAD_PROGRAMMING_LANGUAGES.UNKNOWN, - WORKLOAD_PROGRAMMING_LANGUAGES.PROCESSING, - WORKLOAD_PROGRAMMING_LANGUAGES.NO_CONTAINERS, - WORKLOAD_PROGRAMMING_LANGUAGES.NO_RUNNING_PODS, - ].includes(language); - - return ( - - - - - {active ? INSTUMENTATION_STATUS.INSTRUMENTED : INSTUMENTATION_STATUS.UNINSTRUMENTED} - - - - ); - })} - -
- ); -}; 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 new file mode 100644 index 000000000..0aa35fa3e --- /dev/null +++ b/frontend/webapp/reuseable-components/data-card/data-card-fields/index.tsx @@ -0,0 +1,100 @@ +import React, { useId } from 'react'; +import styled from 'styled-components'; +import { DataTab, Divider, InstrumentStatus, MonitorsIcons, Status, Text, Tooltip } from '@/reuseable-components'; +import { capitalizeFirstLetter, getProgrammingLanguageIcon, parseJsonStringToPrettyString, safeJsonParse, WORKLOAD_PROGRAMMING_LANGUAGES } from '@/utils'; + +export enum DataCardFieldTypes { + DIVIDER = 'divider', + MONITORS = 'monitors', + ACTIVE_STATUS = 'active-status', + SOURCE_CONTAINER = 'source-container', +} + +export interface DataCardRow { + type?: DataCardFieldTypes; + title?: string; + tooltip?: string; + value?: string; + width?: string; +} + +interface Props { + data: DataCardRow[]; +} + +const ListContainer = styled.div` + display: flex; + flex-wrap: wrap; + gap: 16px 32px; + width: 100%; +`; + +const ListItem = styled.div<{ $width: string }>` + display: flex; + flex-direction: column; + gap: 2px; + width: ${({ $width }) => $width}; +`; + +const ItemTitle = styled(Text)` + color: ${({ theme }) => theme.text.grey}; + font-size: 10px; + line-height: 16px; +`; + +export const DataCardFields: React.FC = ({ data }) => { + return ( + + {data.map(({ type, title, tooltip, value, width = 'unset' }) => { + const id = useId(); + + return ( + + + {!!title && {title}} + + {renderValue(type, value)} + + ); + })} + + ); +}; + +const PreWrap = styled(Text)` + font-size: 12px; + white-space: pre-wrap; +`; + +const renderValue = (type: DataCardRow['type'], value: DataCardRow['value']) => { + // We need to maintain this with new components every time we add a new type to "DataCardFieldTypes" + + switch (type) { + case DataCardFieldTypes.DIVIDER: + return ; + + case DataCardFieldTypes.MONITORS: + return ; + + case DataCardFieldTypes.ACTIVE_STATUS: + return ; + + case DataCardFieldTypes.SOURCE_CONTAINER: { + const { containerName, language, runtimeVersion } = safeJsonParse(value, { + containerName: '-', + language: WORKLOAD_PROGRAMMING_LANGUAGES.UNKNOWN, + runtimeVersion: '-', + }); + + return ( + + + + ); + } + + default: { + return {parseJsonStringToPrettyString(value || '-')}; + } + } +}; diff --git a/frontend/webapp/reuseable-components/data-card/index.tsx b/frontend/webapp/reuseable-components/data-card/index.tsx new file mode 100644 index 000000000..7105834e4 --- /dev/null +++ b/frontend/webapp/reuseable-components/data-card/index.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import styled from 'styled-components'; +import { Badge, Text } from '@/reuseable-components'; +import { DataCardFields, type DataCardRow, DataCardFieldTypes } from './data-card-fields'; +export { DataCardFields, type DataCardRow, DataCardFieldTypes }; + +interface Props { + title?: string; + titleBadge?: string | number; + description?: string; + data: DataCardRow[]; +} + +const CardContainer = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + align-self: stretch; + gap: 16px; + padding: 24px; + border-radius: 24px; + border: 1px solid ${({ theme }) => theme.colors.border}; +`; + +const Header = styled.div` + display: flex; + flex-direction: column; + gap: 4px; +`; + +const Title = styled(Text)` + display: flex; + align-items: center; + gap: 8px; + font-size: 16px; +`; + +const Description = styled(Text)` + font-size: 12px; + color: ${({ theme }) => theme.text.grey}; +`; + +export const DataCard: React.FC = ({ title = 'Details', titleBadge, description, data }) => { + return ( + +
+ + {title} + {/* NOT undefined, because we should allow zero (0) values */} + {titleBadge !== undefined && <Badge label={titleBadge} />} + + {!!description && {description}} +
+ + +
+ ); +}; diff --git a/frontend/webapp/reuseable-components/index.ts b/frontend/webapp/reuseable-components/index.ts index 25f953446..2c024e97d 100644 --- a/frontend/webapp/reuseable-components/index.ts +++ b/frontend/webapp/reuseable-components/index.ts @@ -35,5 +35,5 @@ export * from './field-label'; export * from './field-error'; export * from './extend-icon'; export * from './condition-details'; -export * from './container-details'; +export * from './data-card'; export * from './data-tab'; diff --git a/frontend/webapp/utils/constants/string.tsx b/frontend/webapp/utils/constants/string.tsx index 05d5b183b..b74a35243 100644 --- a/frontend/webapp/utils/constants/string.tsx +++ b/frontend/webapp/utils/constants/string.tsx @@ -56,3 +56,35 @@ export const INSTUMENTATION_STATUS = { INSTRUMENTED: 'Instrumented', UNINSTRUMENTED: 'Uninstrumented', }; + +export const DATA_CARDS = { + ACTION_DETAILS: 'Action Details', + RULE_DETAILS: 'Instrumentation Rule Details', + DESTINATION_DETAILS: 'Destination Details', + SOURCE_DETAILS: 'Source Details', + + DETECTED_CONTAINERS: 'Detected Containers', + DETECTED_CONTAINERS_DESCRIPTION: 'The system automatically instruments the containers it detects with a supported programming language.', +}; + +export const DISPLAY_TITLES = { + ACTION: 'Action', + ACTIONS: 'Actions', + INSTRUMENTATION_RULE: 'Instrumentation Rule', + INSTRUMENTATION_RULES: 'Instrumentation Rules', + DESTINATION: 'Destination', + DESTINATIONS: 'Destinations', + SOURCE: 'Source', + SOURCES: 'Sources', + + NAMESPACE: 'Namespace', + CONTAINER_NAME: 'Container Name', + KIND: 'Kind', + TYPE: 'Type', + NAME: 'Name', + NOTES: 'Notes', + STATUS: 'Status', + LANGUAGE: 'Language', + MONITORS: 'Monitors', + SIGNALS_FOR_PROCESSING: 'Signals for Processing', +};