Skip to content

Commit

Permalink
[GEN-2156]: upgrade code component to enhance readability for describ…
Browse files Browse the repository at this point in the history
…e odigos & describe source (#2131)

This pull request introduces a new feature to toggle between code view
and a more human-readable "pretty" view for JSON data, along with some
new icons and components to support this functionality. The main changes
include the addition of a `ToggleCodeComponent`, updates to the
`describe-drawer` and `source-drawer-container` components, and
enhancements to the `Code` component to support the pretty view.

### New Components and Icons:

*
[`frontend/webapp/assets/icons/common/code-brackets-icon.tsx`](diffhunk://#diff-b915df544c6055e9fefb19e39c8cb7f1bed551dc2c397bdf0fbebe08492d9933R1-R16):
Added a new `CodeBracketsIcon` component.
*
[`frontend/webapp/assets/icons/common/list-icon.tsx`](diffhunk://#diff-ac6e9406c5e3485aa9c7ec0bf308a01db7fbd80cd3c0476ebd762263a1dddb86R1-R11):
Added a new `ListIcon` component.
*
[`frontend/webapp/components/common/buttons/toggle-code-component/index.tsx`](diffhunk://#diff-41caeebe24ebfb15cef4fb61e0b64cef8522a865e6da945fd260c5366cc80995R1-R40):
Added a new `ToggleCodeComponent` for toggling between code and pretty
view.

### Component Updates:

*
[`frontend/webapp/components/overview/all-drawers/describe-drawer.tsx`](diffhunk://#diff-840f9f31ab7f0b7437b98105569846f658412a1c2ae133dfa36c129ef518a262L1-R6):
Updated to include the `ToggleCodeComponent` and handle the pretty view
for JSON data.
[[1]](diffhunk://#diff-840f9f31ab7f0b7437b98105569846f658412a1c2ae133dfa36c129ef518a262L1-R6)
[[2]](diffhunk://#diff-840f9f31ab7f0b7437b98105569846f658412a1c2ae133dfa36c129ef518a262L18-L29)
*
[`frontend/webapp/containers/main/sources/source-drawer-container/index.tsx`](diffhunk://#diff-d4745da2f63be90cb1f7af8ea29fb2f37cf0a80f0f04a207b6fc9149855ca6d5R6):
Updated to include the `ToggleCodeComponent` and handle the pretty view
for JSON data.
[[1]](diffhunk://#diff-d4745da2f63be90cb1f7af8ea29fb2f37cf0a80f0f04a207b6fc9149855ca6d5R6)
[[2]](diffhunk://#diff-d4745da2f63be90cb1f7af8ea29fb2f37cf0a80f0f04a207b6fc9149855ca6d5R53)
[[3]](diffhunk://#diff-d4745da2f63be90cb1f7af8ea29fb2f37cf0a80f0f04a207b6fc9149855ca6d5L103-R105)
[[4]](diffhunk://#diff-d4745da2f63be90cb1f7af8ea29fb2f37cf0a80f0f04a207b6fc9149855ca6d5R156-L158)

### Code Enhancements:

*
[`frontend/webapp/reuseable-components/code/index.tsx`](diffhunk://#diff-b2d854039ed7f8b3ce6c2926bf9b37b7a766e688bd6d66819db9488355bd31ecL1-R42):
Enhanced the `Code` component to support a pretty view for JSON data,
including new helper functions and styled components.
[[1]](diffhunk://#diff-b2d854039ed7f8b3ce6c2926bf9b37b7a766e688bd6d66819db9488355bd31ecL1-R42)
[[2]](diffhunk://#diff-b2d854039ed7f8b3ce6c2926bf9b37b7a766e688bd6d66819db9488355bd31ecR55-R66)
[[3]](diffhunk://#diff-b2d854039ed7f8b3ce6c2926bf9b37b7a766e688bd6d66819db9488355bd31ecR75-R191)

### Utility Functions:

*
[`frontend/webapp/hooks/describe/useDescribeOdigos.ts`](diffhunk://#diff-f96e0d4e8e6b30c653c21364c7479cd8656417588a42c68aa9346c90e838d4ccR10-R45):
Added a `restructureForPrettyMode` function to format JSON data for the
pretty view.
*
[`frontend/webapp/hooks/describe/useDescribeSource.ts`](diffhunk://#diff-d17d5d94f77fc9f854c37f3ff50fe36db633f9a234b0c469533138d6768fcc53R11-R56):
Added a `restructureForPrettyMode` function to format JSON data for the
pretty view.

<img width="657" alt="Screenshot 2025-01-05 at 13 35 41"
src="https://github.com/user-attachments/assets/2df38a03-0bc7-4ab8-bda2-95920b9fdf86"
/>
<img width="657" alt="Screenshot 2025-01-05 at 13 35 32"
src="https://github.com/user-attachments/assets/a5857591-18b4-413d-8d5c-b3f2c9d5e475"
/>
  • Loading branch information
BenElferink authored Jan 5, 2025
1 parent 0ac19a6 commit 4bbc40a
Show file tree
Hide file tree
Showing 13 changed files with 333 additions and 21 deletions.
16 changes: 16 additions & 0 deletions frontend/webapp/assets/icons/common/code-brackets-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import { SVG } from '@/assets';
import theme from '@/styles/theme';

export const CodeBracketsIcon: SVG = ({ size = 16, fill = theme.text.secondary, rotate = 0, onClick }) => {
return (
<svg width={size} height={size} viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg' fill='none' style={{ transform: `rotate(${rotate}deg)` }} onClick={onClick}>
<path
stroke={fill}
strokeLinecap='round'
strokeLinejoin='round'
d='M5.33333 2.66669C4.22876 2.66669 3.33333 3.46263 3.33333 4.44446V6.22224C3.33333 7.20408 2.4379 8.00002 1.33333 8.00002C2.4379 8.00002 3.33333 8.79596 3.33333 9.7778V11.5556C3.33333 12.5374 4.22876 13.3334 5.33333 13.3334M10.6667 2.66669C11.7712 2.66669 12.6667 3.46263 12.6667 4.44446V6.22224C12.6667 7.20408 13.5621 8.00002 14.6667 8.00002C13.5621 8.00002 12.6667 8.79596 12.6667 9.7778V11.5556C12.6667 12.5374 11.7712 13.3334 10.6667 13.3334'
/>
</svg>
);
};
2 changes: 1 addition & 1 deletion frontend/webapp/assets/icons/common/code-icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const CodeIcon: SVG = ({ size = 16, fill = theme.text.secondary, rotate =
stroke={fill}
strokeLinecap='round'
strokeLinejoin='round'
d='M5.33333 2.66669C4.22876 2.66669 3.33333 3.46263 3.33333 4.44446V6.22224C3.33333 7.20408 2.4379 8.00002 1.33333 8.00002C2.4379 8.00002 3.33333 8.79596 3.33333 9.7778V11.5556C3.33333 12.5374 4.22876 13.3334 5.33333 13.3334M10.6667 2.66669C11.7712 2.66669 12.6667 3.46263 12.6667 4.44446V6.22224C12.6667 7.20408 13.5621 8.00002 14.6667 8.00002C13.5621 8.00002 12.6667 8.79596 12.6667 9.7778V11.5556C12.6667 12.5374 11.7712 13.3334 10.6667 13.3334'
d='M11.334 12a18.802 18.802 0 0 0 3.231-3.66.62.62 0 0 0 0-.68A18.8 18.8 0 0 0 11.334 4m-6.665 8a18.803 18.803 0 0 1-3.231-3.66.62.62 0 0 1 0-.68A18.801 18.801 0 0 1 4.669 4m4.667-1.332L6.669 13.335'
/>
</svg>
);
Expand Down
2 changes: 2 additions & 0 deletions frontend/webapp/assets/icons/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export * from './avatar-icon';
export * from './check-circled-icon';
export * from './check-icon';
export * from './cross-circled-icon';
export * from './code-brackets-icon';
export * from './code-icon';
export * from './cross-icon';
export * from './edit-icon';
Expand All @@ -14,6 +15,7 @@ export * from './eye-open-icon';
export * from './filter-icon';
export * from './folder-icon';
export * from './info-icon';
export * from './list-icon';
export * from './no-data-icon';
export * from './notebook-icon';
export * from './notification-icon';
Expand Down
11 changes: 11 additions & 0 deletions frontend/webapp/assets/icons/common/list-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';
import { SVG } from '@/assets';
import theme from '@/styles/theme';

export const ListIcon: SVG = ({ size = 16, fill = theme.text.secondary, rotate = 0, onClick }) => {
return (
<svg width={size} height={size} viewBox='0 0 16 16' xmlns='http://www.w3.org/2000/svg' fill='none' style={{ transform: `rotate(${rotate}deg)` }} onClick={onClick}>
<path stroke={fill} strokeLinecap='round' strokeLinejoin='round' d='M2.667 6.001h10.666M2.666 9.335h10.667M2.667 12.668H8m-5.333-10h10.666' />
</svg>
);
};
1 change: 1 addition & 0 deletions frontend/webapp/components/common/buttons/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './toggle-code-component';
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import { FlexRow } from '@/styles';
import styled from 'styled-components';
import { CodeIcon, ListIcon } from '@/assets';

interface Props {
isCodeMode: boolean;
setIsCodeMode: (isCodeMode: boolean) => void;
}

const Container = styled(FlexRow)`
gap: 0;
`;

const Button = styled.button<{ $position: 'left' | 'right'; $selected: boolean }>`
padding: 4px 8px;
background-color: ${({ theme, $selected }) => ($selected ? theme.colors.white_opacity['008'] : 'transparent')};
border-radius: ${({ $position }) => ($position === 'left' ? '32px 0px 0px 32px' : $position === 'right' ? '0px 32px 32px 0px' : '0')};
border: 1px solid ${({ theme }) => theme.colors.border};
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
&:hover {
border: 1px solid ${({ theme }) => theme.colors.secondary};
}
`;

export const ToggleCodeComponent: React.FC<Props> = ({ isCodeMode, setIsCodeMode }) => {
return (
<Container>
<Button $position='left' $selected={!isCodeMode} onClick={() => setIsCodeMode(false)}>
<ListIcon />
</Button>
<Button $position='right' $selected={isCodeMode} onClick={() => setIsCodeMode(true)}>
<CodeIcon />
</Button>
</Container>
);
};
1 change: 1 addition & 0 deletions frontend/webapp/components/common/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './buttons';
export * from './dropdowns';
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React from 'react';
import { CodeIcon } from '@/assets';
import React, { useState } from 'react';
import styled from 'styled-components';
import { CodeBracketsIcon } from '@/assets';
import { useDescribeOdigos } from '@/hooks';
import { DATA_CARDS, safeJsonStringify } from '@/utils';
import { ToggleCodeComponent } from '@/components/common';
import { DataCard, DataCardFieldTypes } from '@/reuseable-components';
import OverviewDrawer from '@/containers/main/overview/overview-drawer';

Expand All @@ -15,18 +16,24 @@ const DataContainer = styled.div`
`;

export const DescribeDrawer: React.FC<Props> = () => {
const { data: describe } = useDescribeOdigos();
const { data: describe, restructureForPrettyMode } = useDescribeOdigos();
const [isCodeMode, setIsCodeMode] = useState(false);

return (
<OverviewDrawer title={DATA_CARDS.DESCRIBE_ODIGOS} icon={CodeIcon}>
<OverviewDrawer title={DATA_CARDS.DESCRIBE_ODIGOS} icon={CodeBracketsIcon}>
<DataContainer>
<DataCard
title=''
action={<ToggleCodeComponent isCodeMode={isCodeMode} setIsCodeMode={setIsCodeMode} />}
data={[
{
type: DataCardFieldTypes.CODE,
value: JSON.stringify({
language: 'json',
code: safeJsonStringify(isCodeMode ? describe : restructureForPrettyMode(describe)),
pretty: !isCodeMode,
}),
width: 'inherit',
value: JSON.stringify({ language: 'json', code: safeJsonStringify(describe) }),
},
]}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import buildCard from './build-card';
import styled from 'styled-components';
import { useDrawerStore } from '@/store';
import buildDrawerItem from './build-drawer-item';
import { ToggleCodeComponent } from '@/components';
import { UpdateSourceBody } from '../update-source-body';
import { useDescribeSource, useSourceCRUD } from '@/hooks';
import OverviewDrawer from '../../overview/overview-drawer';
Expand Down Expand Up @@ -49,6 +50,7 @@ export const SourceDrawer: React.FC<Props> = () => {
},
});

const [isCodeMode, setIsCodeMode] = useState(false); // for "describe source"
const [isEditing, setIsEditing] = useState(false);
const [isFormDirty, setIsFormDirty] = useState(false);
const [formData, setFormData] = useState({ ...EMPTY_FORM });
Expand Down Expand Up @@ -99,7 +101,7 @@ export const SourceDrawer: React.FC<Props> = () => {

if (!selectedItem?.item) return null;
const { id, item } = selectedItem as { id: WorkloadId; item: K8sActualSource };
const { data: describe } = useDescribeSource(id);
const { data: describe, restructureForPrettyMode } = useDescribeSource(id);

const handleEdit = (bool?: boolean) => {
setIsEditing(typeof bool === 'boolean' ? bool : true);
Expand Down Expand Up @@ -151,11 +153,16 @@ export const SourceDrawer: React.FC<Props> = () => {
<DataCard title={DATA_CARDS.DETECTED_CONTAINERS} titleBadge={containersData.length} description={DATA_CARDS.DETECTED_CONTAINERS_DESCRIPTION} data={containersData} />
<DataCard
title={DATA_CARDS.DESCRIBE_SOURCE}
action={<ToggleCodeComponent isCodeMode={isCodeMode} setIsCodeMode={setIsCodeMode} />}
data={[
{
type: DataCardFieldTypes.CODE,
value: JSON.stringify({
language: 'json',
code: safeJsonStringify(isCodeMode ? describe : restructureForPrettyMode(describe)),
pretty: !isCodeMode,
}),
width: 'inherit',
value: JSON.stringify({ language: 'json', code: safeJsonStringify(describe) }),
},
]}
/>
Expand Down
34 changes: 33 additions & 1 deletion frontend/webapp/hooks/describe/useDescribeOdigos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,41 @@ export const useDescribeOdigos = () => {
pollInterval: 5000,
});

// This function is used to restructure the data, so that it reflects the output given by "odigos describe" command in the CLI.
// This is not really needed, but it's a nice-to-have feature to make the data more readable.
const restructureForPrettyMode = (code?: DescribeOdigos['describeOdigos']) => {
if (!code) return {};

const payload: Record<string, any> = {
[code.odigosVersion.name]: code.odigosVersion.value,
'Number Of Sources': code.numberOfSources,
'Number Of Destinations': code.numberOfDestinations,
};

const mapObjects = (obj: any, objectName: string) => {
if (typeof obj === 'object' && !!obj?.name) {
let key = obj.name;
let val = obj.value;

if (obj.explain) key += `@tooltip=${obj.explain}`;
if (obj.status) val += `@status=${obj.status}`;
else val += '@status=none';

if (!payload[objectName]) payload[objectName] = {};
payload[objectName][key] = val;
}
};

Object.values(code.clusterCollector).forEach((val) => mapObjects(val, 'Cluster Collector'));
Object.values(code.nodeCollector).forEach((val) => mapObjects(val, 'Node Collector'));

return payload;
};

return {
data: data?.describeOdigos,
loading,
error,
data: data?.describeOdigos,
restructureForPrettyMode,
};
};
44 changes: 43 additions & 1 deletion frontend/webapp/hooks/describe/useDescribeSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,51 @@ export const useDescribeSource = ({ namespace, name, kind }: WorkloadId) => {
pollInterval: 5000,
});

// This function is used to restructure the data, so that it reflects the output given by "odigos describe" command in the CLI.
// This is not really needed, but it's a nice-to-have feature to make the data more readable.
const restructureForPrettyMode = (code?: DescribeSource['describeSource']) => {
if (!code) return {};

const payload: Record<string, any> = {};

const mapObjects = (obj: any, category?: string, options?: { keyPrefix?: string }) => {
if (typeof obj === 'object' && !!obj?.name) {
let key = options?.keyPrefix ? `${options?.keyPrefix}${obj.name}` : obj.name;
let val = obj.value;

if (obj.explain) key += `@tooltip=${obj.explain}`;
if (obj.status) val += `@status=${obj.status}`;
else val += '@status=none';

if (!!category && !payload[category]) payload[category] = {};
if (!!category) payload[category][key] = val;
else payload[key] = val;
}
};

Object.values(code).forEach((val) => mapObjects(val));
Object.values(code.labels).forEach((val) => mapObjects(val, 'Labels'));
Object.values(code.instrumentationConfig).forEach((val) => mapObjects(val, 'Instrumentation Config'));
code.runtimeInfo?.containers.forEach((obj, i) => Object.values(obj).forEach((val) => mapObjects(val, 'Runtime Info', { keyPrefix: `Container #${i + 1} - ` })));
Object.values(code.instrumentationDevice).forEach((val) => mapObjects(val, 'Instrumentation Device'));
code.instrumentationDevice?.containers.forEach((obj, i) => Object.values(obj).forEach((val) => mapObjects(val, 'Instrumentation Device', { keyPrefix: `Container #${i + 1} - ` })));

payload['Pods'] = { 'Total Pods': `${code.totalPods}@status=none` };
code.pods.forEach((obj) => {
Object.values(obj).forEach((val) => mapObjects(val, 'Pods'));
obj.containers.forEach((containers, i) => {
Object.values(containers).forEach((val) => mapObjects(val, 'Pods', { keyPrefix: `Container #${i + 1} - ` }));
containers.instrumentationInstances.forEach((obj, i) => Object.values(obj).forEach((val) => mapObjects(val, 'Pods', { keyPrefix: `Instrumentation Instance #${i + 1} - ` })));
});
});

return payload;
};

return {
data: data?.describeSource,
loading,
error,
data: data?.describeSource,
restructureForPrettyMode,
};
};
Loading

0 comments on commit 4bbc40a

Please sign in to comment.
  NODES
COMMUNITY 1
Note 2
Project 3
USERS 1