Skip to content

Commit

Permalink
fix: startPolling() and destination modal/drawer (#1877)
Browse files Browse the repository at this point in the history
  • Loading branch information
BenElferink authored Nov 27, 2024
1 parent e9958d6 commit b4163e1
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 91 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ 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 { OVERVIEW_ENTITY_TYPES, type ActualDestination } from '@/types';
Expand All @@ -20,6 +21,12 @@ const FormContainer = styled.div`
overflow-y: auto;
`;

const DataContainer = styled.div`
display: flex;
flex-direction: column;
gap: 12px;
`;

export const DestinationDrawer: React.FC<Props> = () => {
const { selectedItem, setSelectedItem } = useDrawerStore();
const { destinations: destinationTypes } = useDestinationTypes();
Expand Down Expand Up @@ -128,7 +135,10 @@ export const DestinationDrawer: React.FC<Props> = () => {
/>
</FormContainer>
) : (
<CardDetails title='Destination Details' data={cardData} />
<DataContainer>
<ConditionDetails conditions={item.conditions} />
<CardDetails title='Destination Details' data={cardData} />
</DataContainer>
)}
</OverviewDrawer>
);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,59 +2,83 @@ import React, { useMemo, useState } from 'react';
import styled from 'styled-components';
import { SignalUppercase } from '@/utils';
import { useDestinationTypes } from '@/hooks';
import type { DestinationTypeItem } from '@/types';
import { DestinationsList } from './destinations-list';
import { Divider, SectionTitle } from '@/reuseable-components';
import type { DropdownOption, DestinationTypeItem } from '@/types';
import { ChooseDestinationFilters } from './choose-destination-filters';
import { Divider, Dropdown, Input, MonitoringCheckboxes, SectionTitle } from '@/reuseable-components';

interface Props {
onSelect: (item: DestinationTypeItem) => void;
hidden?: boolean;
}

const DEFAULT_MONITORS: SignalUppercase[] = ['LOGS', 'METRICS', 'TRACES'];
const DEFAULT_DROPDOWN_VALUE = { id: 'all', value: 'All types' };

const Container = styled.div`
display: flex;
flex-direction: column;
gap: 24px;
`;

export const ChooseDestinationBody: React.FC<Props> = ({ onSelect }) => {
const [searchValue, setSearchValue] = useState('');
const Filters = styled.div`
display: flex;
align-items: center;
gap: 12px;
`;

const WidthConstraint = styled.div`
width: 160px;
margin-right: 8px;
`;

const DROPDOWN_OPTIONS = [
{ value: 'All types', id: 'all' },
{ value: 'Managed', id: 'managed' },
{ value: 'Self-hosted', id: 'self hosted' },
];

const DEFAULT_CATEGORY = DROPDOWN_OPTIONS[0];
const DEFAULT_MONITORS: SignalUppercase[] = ['LOGS', 'METRICS', 'TRACES'];

export const ChooseDestinationBody: React.FC<Props> = ({ onSelect, hidden }) => {
const [search, setSearch] = useState('');
const [selectedCategory, setSelectedCategory] = useState(DEFAULT_CATEGORY);
const [selectedMonitors, setSelectedMonitors] = useState<SignalUppercase[]>(DEFAULT_MONITORS);
const [dropdownValue, setDropdownValue] = useState<DropdownOption>(DEFAULT_DROPDOWN_VALUE);

const { destinations } = useDestinationTypes();
const { destinations: destinationTypes } = useDestinationTypes();

const filteredDestinations = useMemo(() => {
return destinations
return destinationTypes
.map((category) => {
const filteredItems = category.items.filter((item) => {
const matchesSearch = searchValue ? item.displayName.toLowerCase().includes(searchValue.toLowerCase()) : true;
const matchesDropdown = dropdownValue.id !== 'all' ? category.name === dropdownValue.id : true;
const matchesMonitor = selectedMonitors.length ? selectedMonitors.some((monitor) => item.supportedSignals[monitor.toLowerCase()]?.supported) : true;
const matchesSearch = !search || item.displayName.toLowerCase().includes(search.toLowerCase());
const matchesCategory = selectedCategory.id === 'all' || selectedCategory.id === category.name;
const matchesMonitor = selectedMonitors.some((monitor) => item.supportedSignals[monitor.toLowerCase()]?.supported);

return matchesSearch && matchesDropdown && matchesMonitor;
return matchesSearch && matchesCategory && matchesMonitor;
});

return { ...category, items: filteredItems };
})
.filter((category) => category.items.length > 0); // Filter out empty categories
}, [destinations, searchValue, dropdownValue, selectedMonitors]);
.filter(({ items }) => !!items.length); // Filter out empty categories
}, [destinationTypes, search, selectedCategory, selectedMonitors]);

if (hidden) return null;

return (
<Container>
<SectionTitle title='Choose destination' description='Add backend destination you want to connect with Odigos.' />
<ChooseDestinationFilters
selectedTag={dropdownValue}
onTagSelect={(opt) => setDropdownValue(opt)}
onSearch={setSearchValue}
selectedMonitors={selectedMonitors}
setSelectedMonitors={setSelectedMonitors}
/>

<Filters>
<WidthConstraint>
<Input placeholder='Search...' icon='/icons/common/search.svg' value={search} onChange={({ target: { value } }) => setSearch(value)} />
</WidthConstraint>
<WidthConstraint>
<Dropdown options={DROPDOWN_OPTIONS} value={selectedCategory} onSelect={(opt) => setSelectedCategory(opt)} onDeselect={() => {}} />
</WidthConstraint>
<MonitoringCheckboxes title='' selectedSignals={selectedMonitors} setSelectedSignals={setSelectedMonitors} />
</Filters>

<Divider />

<DestinationsList items={filteredDestinations} setSelectedItems={onSelect} />
</Container>
);
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -125,17 +125,21 @@ export const DestinationModal: React.FC<AddDestinationModalProps> = ({ isOnboard
</SideMenuWrapper>

<ModalBody style={{ margin: '32px 24px 0 24px' }}>
{!!selectedItem ? (
{/*
in other modals we would render this out, but for this case we will use "hidden" instead,
this is to preserve the filters-state when going back-and-forth between selections
*/}
<ChooseDestinationBody onSelect={handleSelect} hidden={!!selectedItem} />

{!!selectedItem && (
<DestinationFormBody
destination={selectedItem}
formData={formData}
formErrors={formErrors}
formData={formData}
handleFormChange={handleFormChange}
dynamicFields={dynamicFields}
setDynamicFields={setDynamicFields}
/>
) : (
<ChooseDestinationBody onSelect={handleSelect} />
)}
</ModalBody>
</Container>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'use client';
import React, { useMemo } from 'react';
import React, { useEffect, useMemo } from 'react';
import styled from 'styled-components';
import MultiSourceControl from '../multi-source-control';
import { OverviewActionMenuContainer } from '../overview-actions-menu';
Expand All @@ -17,10 +17,16 @@ const NODE_HEIGHT = 80;

export default function OverviewDataFlowContainer() {
const { containerRef, containerWidth, containerHeight } = useContainerSize();
const { data, filteredData, startPolling } = useComputePlatform();
const { handleNodeClick } = useNodeDataFlowHandlers();
const { data, filteredData } = useComputePlatform();
const { metrics } = useMetrics();

useEffect(() => {
// this is to start polling on component mount in an attempt to fix any initial errors with sources/destinations
if (!!data?.computePlatform.k8sActualSources.length || !!data?.computePlatform.destinations.length) startPolling();
// only on-mount, if we include "data" this might trigger on every refetch
}, []);

// Memoized node and edge builder to improve performance
const { nodes, edges } = useMemo(() => {
return buildNodesAndEdges({
Expand Down
9 changes: 2 additions & 7 deletions frontend/webapp/hooks/compute-platform/useComputePlatform.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useMemo } from 'react';
import { useCallback, useMemo } from 'react';
import { useQuery } from '@apollo/client';
import { useBooleanStore } from '@/store';
import { GET_COMPUTE_PLATFORM } from '@/graphql';
Expand All @@ -25,7 +25,7 @@ export const useComputePlatform = (): UseComputePlatformHook => {

let retries = 0;
const maxRetries = 5;
const retryInterval = 2 * 1000; // time in milliseconds
const retryInterval = 3 * 1000; // time in milliseconds

while (retries < maxRetries) {
await new Promise((resolve) => setTimeout(resolve, retryInterval));
Expand All @@ -36,11 +36,6 @@ export const useComputePlatform = (): UseComputePlatformHook => {
togglePolling(false);
}, [refetch, togglePolling]);

// this is to start polling on component mount in an attempt to fix any initial errors with sources/destinations
useEffect(() => {
startPolling();
}, []);

const mappedData = useMemo(() => {
if (!data) return undefined;

Expand Down

0 comments on commit b4163e1

Please sign in to comment.