Skip to content

Commit

Permalink
Merge branch 'beta-ui-bugs-party' into old-ui-onboarding-issue
Browse files Browse the repository at this point in the history
  • Loading branch information
alonkeyval authored Nov 20, 2024
2 parents 5c85a2d + 572d477 commit c05d30d
Show file tree
Hide file tree
Showing 13 changed files with 126 additions and 121 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export const SourcesList: React.FC<Props> = ({
onSelectFutureApps,

searchText,
selectAllForNamespace,
onSelectAll,
showSelectedOnly,

Expand All @@ -124,10 +125,10 @@ export const SourcesList: React.FC<Props> = ({
const selected = selectedSources[namespace] || [];
const futureApps = selectedFutureApps[namespace] || false;

const namespacePassesFilters = (!searchText || namespace.toLowerCase().includes(searchText)) && (!showSelectedOnly || !!selected.length);
const namespacePassesFilters = !searchText || namespace.toLowerCase().includes(searchText);
if (!namespacePassesFilters) return null;

const isNamespaceSelected = selectedNamespace === namespace;
const isNamespaceSelected = selectedNamespace === namespace && !selectAllForNamespace;
const isNamespaceCanSelect = namespaceLoaded && !!available.length;
const isNamespaceAllSourcesSelected = isNamespaceCanSelect && selected.length === sources.length;

Expand All @@ -138,14 +139,7 @@ export const SourcesList: React.FC<Props> = ({
<Group key={`namespace-${namespace}`} $selected={isNamespaceAllSourcesSelected} $isOpen={isNamespaceSelected && hasFilteredSources}>
<NamespaceItem $selected={isNamespaceAllSourcesSelected} onClick={() => onSelectNamespace(namespace)}>
<FlexRow>
<Checkbox
// disabled={!isNamespaceCanSelect}
initialValue={isNamespaceAllSourcesSelected}
onChange={(bool) => {
if (bool) onSelectNamespace(namespace);
onSelectAll(bool, namespace);
}}
/>
<Checkbox disabled={namespaceLoaded && !isNamespaceCanSelect} initialValue={isNamespaceAllSourcesSelected} onChange={(bool) => onSelectAll(bool, namespace)} />
<Text>{namespace}</Text>
</FlexRow>

Expand Down Expand Up @@ -175,7 +169,7 @@ export const SourcesList: React.FC<Props> = ({
<Checkbox initialValue={isSourceSelected} onChange={() => onSelectSource(source, namespace)} />
<Text>{source.name}</Text>
<Text opacity={0.8} size={10}>
{source.numberOfInstances} running instances · {source.kind}
{source.numberOfInstances} running instance{source.numberOfInstances !== 1 && 's'} · {source.kind}
</Text>
</FlexRow>
</SourceItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export const SourcesList: React.FC<Props> = ({
<TextWrapper>
<Text>{source.name}</Text>
<Text opacity={0.8} size={10}>
{source.numberOfInstances} running instances · {source.kind}
{source.numberOfInstances} running instance{source.numberOfInstances !== 1 && 's'} · {source.kind}
</Text>
</TextWrapper>
</ListItemContent>
Expand Down
50 changes: 29 additions & 21 deletions frontend/webapp/hooks/sources/useSourceFormData.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
import { Dispatch, SetStateAction, useCallback, useEffect, useState } from 'react';
import { useAppStore } from '@/store';
import type { K8sActualSource } from '@/types';
import { useNamespace } from '../compute-platform';
Expand Down Expand Up @@ -28,6 +28,7 @@ export interface UseSourceFormDataResponse {

searchText: string;
selectAll: boolean;
selectAllForNamespace: string;
showSelectedOnly: boolean;
setSearchText: Dispatch<SetStateAction<string>>;
onSelectAll: (bool: boolean, namespace?: string) => void;
Expand Down Expand Up @@ -107,35 +108,41 @@ export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFo
});
};

const onSelectAll: UseSourceFormDataResponse['onSelectAll'] = (bool, namespace) => {
if (!!namespace) {
const nsAvailableSources = availableSources[namespace];
const nsSelectedSources = selectedSources[namespace];

if (!nsAvailableSources.length && !nsSelectedSources && bool) {
setSelectAllForNamespace(namespace);
const onSelectAll: UseSourceFormDataResponse['onSelectAll'] = useCallback(
(bool, namespace) => {
if (!!namespace) {
const nsAvailableSources = availableSources[namespace];
const nsSelectedSources = selectedSources[namespace];

if (!nsSelectedSources && bool) {
onSelectNamespace(namespace);
setSelectAllForNamespace(namespace);
} else {
setSelectedSources((prev) => ({ ...prev, [namespace]: bool ? nsAvailableSources : [] }));
setSelectAllForNamespace('');
if (!!nsAvailableSources.length) setSelectedNamespace('');
}
} else {
setSelectedSources((prev) => ({ ...prev, [namespace]: bool ? nsAvailableSources : [] }));
}
} else {
setSelectAll(bool);
setSelectAll(bool);

if (bool) {
doSelectAll();
} else {
doUnselectAll();
if (bool) {
doSelectAll();
} else {
doUnselectAll();
}
}
}
};
},
[availableSources, selectedSources],
);

// this is to keep trying "select all" per namespace until the sources are loaded (allows for 1-click, better UX).
// if selectedSources returns an emtpy array, it will stop to prevent inifnite loop where no availableSources ever exist for that namespace
useEffect(() => {
if (!!selectAllForNamespace) {
setSelectAllForNamespace('');
setTimeout(() => onSelectAll(true, selectAllForNamespace), 100);
const interval = setInterval(() => onSelectAll(true, selectAllForNamespace), 100);
return () => clearInterval(interval);
}
}, [selectAllForNamespace]);
}, [selectAllForNamespace, onSelectAll]);

const onSelectNamespace: UseSourceFormDataResponse['onSelectNamespace'] = (namespace) => {
const alreadySelected = selectedNamespace === namespace;
Expand Down Expand Up @@ -194,6 +201,7 @@ export const useSourceFormData = (params?: UseSourceFormDataParams): UseSourceFo

searchText,
selectAll,
selectAllForNamespace,
showSelectedOnly,
setSearchText,
onSelectAll,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Image from 'next/image';
import { DOCS_LINK } from '@/utils';
import styled from 'styled-components';
import { Button, ButtonProps } from '..';
import { useRef } from 'react';

const StyledButton = styled(Button)`
display: flex;
Expand All @@ -13,8 +14,17 @@ const StyledButton = styled(Button)`
`;

export const DocsButton = ({ endpoint = '/', variant = 'secondary' }: { endpoint?: string; variant?: ButtonProps['variant'] }) => {
const ref = useRef<HTMLButtonElement>(null);

return (
<StyledButton variant={variant} onClick={() => window.open(`${DOCS_LINK}${endpoint}`, '_blank', 'noopener noreferrer')}>
<StyledButton
ref={ref}
variant={variant}
onClick={() => {
window.open(`${DOCS_LINK}${endpoint}`, '_blank', 'noopener noreferrer');
ref.current?.blur();
}}
>
<Image src='/icons/common/notebook.svg' alt='Docs' width={18} height={18} />
Docs
</StyledButton>
Expand Down
8 changes: 4 additions & 4 deletions frontend/webapp/reuseable-components/button/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { ButtonHTMLAttributes } from 'react';
import React, { ButtonHTMLAttributes, forwardRef, LegacyRef } from 'react';
import styled, { css } from 'styled-components';

export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
Expand Down Expand Up @@ -117,12 +117,12 @@ const ButtonContainer = styled.div<{ $variant: ButtonProps['variant'] }>`
}
`;

export const Button: React.FC<ButtonProps> = ({ children, variant = 'primary', isDisabled = false, ...props }) => {
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(({ children, variant = 'primary', isDisabled = false, ...props }, ref) => {
return (
<ButtonContainer $variant={variant}>
<StyledButton $variant={variant} disabled={isDisabled || props.disabled} {...props}>
<StyledButton ref={ref} $variant={variant} disabled={isDisabled || props.disabled} {...props}>
{children}
</StyledButton>
</ButtonContainer>
);
};
});
5 changes: 4 additions & 1 deletion frontend/webapp/reuseable-components/checkbox/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ const CheckboxWrapper = styled.div<{ $isChecked: boolean; $disabled?: CheckboxPr

const Checkbox: React.FC<CheckboxProps> = ({ title, titleColor, tooltip, initialValue = false, onChange, disabled, style }) => {
const [isChecked, setIsChecked] = useState(initialValue);
useEffect(() => setIsChecked(initialValue), [initialValue]);

useEffect(() => {
if (isChecked !== initialValue) setIsChecked(initialValue);
}, [isChecked, initialValue]);

const handleToggle: React.MouseEventHandler<HTMLDivElement> = (e) => {
if (disabled) return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,9 +281,7 @@ export const buildNodesAndEdges = ({
}

// Connect sources to actions
if (!sources.length) {
edges.push(createEdge(`source-0-to-action-${actions.length ? 'group' : 0}`));
} else {
if (!!sources.length) {
tempNodes['sources'].forEach((node, idx) => {
if (idx > 0) {
const sourceIndex = idx - 1;
Expand Down Expand Up @@ -312,9 +310,7 @@ export const buildNodesAndEdges = ({
}

// Connect actions to destinations
if (!destinations.length) {
edges.push(createEdge(`action-${actions.length ? 'group' : 0}-to-destination-0`));
} else {
if (!!destinations.length) {
tempNodes['destinations'].forEach((node, idx) => {
if (idx > 0) {
const destinationIndex = idx - 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@ import React from 'react';
import styled from 'styled-components';
import { EdgeLabelRenderer, BaseEdge, type EdgeProps, type Edge, getSmoothStepPath } from '@xyflow/react';

interface Props
extends EdgeProps<
Edge<
{
label: string;
isMultiTarget?: boolean;
isError?: boolean;
},
'labeled'
>
> {}

const Label = styled.div<{ $labelX: number; $labelY: number; $isError?: boolean }>`
position: absolute;
transform: ${({ $labelX, $labelY }) => `translate(-50%, -50%) translate(${$labelX}px, ${$labelY}px)`};
Expand All @@ -20,17 +32,7 @@ const Label = styled.div<{ $labelX: number; $labelY: number; $isError?: boolean
justify-content: center;
`;

const LabeledEdge: React.FC<EdgeProps<Edge<{ label: string; isMultiTarget?: boolean; isError?: boolean }>>> = ({
id,
sourceX,
sourceY,
targetX,
targetY,
sourcePosition,
targetPosition,
data,
style,
}) => {
const LabeledEdge: React.FC<Props> = ({ id, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, data, style }) => {
const [edgePath] = getSmoothStepPath({
sourceX,
sourceY,
Expand All @@ -44,12 +46,7 @@ const LabeledEdge: React.FC<EdgeProps<Edge<{ label: string; isMultiTarget?: bool
<>
<BaseEdge id={id} path={edgePath} style={style} />
<EdgeLabelRenderer>
<Label
$labelX={data?.isMultiTarget ? targetX - 50 : sourceX + 50}
$labelY={data?.isMultiTarget ? targetY : sourceY}
$isError={data?.isError}
className='nodrag nopan'
>
<Label $labelX={data?.isMultiTarget ? targetX - 50 : sourceX + 50} $labelY={data?.isMultiTarget ? targetY : sourceY} $isError={data?.isError} className='nodrag nopan'>
{data?.label}
</Label>
</EdgeLabelRenderer>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,25 @@ import React from 'react';
import Image from 'next/image';
import styled from 'styled-components';
import { Text } from '@/reuseable-components';
import { Handle, Position } from '@xyflow/react';

interface BaseNodeProps {
data: Record<string, any>;

id: string;
parentId?: any;
type: string;

isConnectable: boolean;
selectable: boolean;
selected?: any;
deletable: boolean;
draggable: boolean;
dragging: boolean;
dragHandle?: any;

width: number;
height: number;
zIndex: number;
positionAbsoluteX: number;
positionAbsoluteY: number;
sourcePosition?: any;
targetPosition?: any;
import { OVERVIEW_NODE_TYPES, STATUSES } from '@/types';
import { Handle, type Node, type NodeProps, Position } from '@xyflow/react';

interface Props
extends NodeProps<
Node<
{
type: OVERVIEW_NODE_TYPES;
status: STATUSES;
title: string;
subTitle: string;
},
'add'
>
> {
nodeWidth: number;
}

const BaseNodeContainer = styled.div<{ $nodeWidth: BaseNodeProps['nodeWidth'] }>`
const BaseNodeContainer = styled.div<{ $nodeWidth: Props['nodeWidth'] }>`
width: ${({ $nodeWidth }) => `${$nodeWidth}px`};
padding: 16px 24px 16px 16px;
display: flex;
Expand Down Expand Up @@ -68,7 +58,7 @@ const SubTitle = styled(Text)`
text-align: center;
`;

const AddNode = ({ id, isConnectable, data, nodeWidth }: BaseNodeProps) => {
const AddNode: React.FC<Props> = ({ nodeWidth, data, id, isConnectable }) => {
return (
<BaseNodeContainer $nodeWidth={nodeWidth}>
<TitleWrapper>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,31 @@ import Image from 'next/image';
import { useAppStore } from '@/store';
import styled from 'styled-components';
import { getStatusIcon } from '@/utils';
import { Handle, Position } from '@xyflow/react';
import { Checkbox, Status, Text } from '@/reuseable-components';
import { Handle, type Node, type NodeProps, Position } from '@xyflow/react';
import { type ActionDataParsed, type ActualDestination, type InstrumentationRuleSpec, type K8sActualSource, STATUSES } from '@/types';

export interface NodeDataProps {
id: string;
type: 'source' | 'action' | 'destination';
status: STATUSES;
title: string;
subTitle: string;
imageUri: string;
monitors?: string[];
isActive?: boolean;
raw: InstrumentationRuleSpec | K8sActualSource | ActionDataParsed | ActualDestination;
}

interface BaseNodeProps {
id: string;
interface Props
extends NodeProps<
Node<
{
id: string;
type: 'source' | 'action' | 'destination';
status: STATUSES;
title: string;
subTitle: string;
imageUri: string;
monitors?: string[];
isActive?: boolean;
raw: InstrumentationRuleSpec | K8sActualSource | ActionDataParsed | ActualDestination;
},
'base'
>
> {
nodeWidth: number;
isConnectable: boolean;
data: NodeDataProps;
}

const Container = styled.div<{ $nodeWidth: number; $isError?: boolean }>`
const Container = styled.div<{ $nodeWidth: Props['nodeWidth']; $isError?: boolean }>`
display: flex;
align-items: center;
align-self: stretch;
Expand Down Expand Up @@ -85,7 +86,7 @@ const ActionsWrapper = styled.div`
margin-left: auto;
`;

const BaseNode = ({ nodeWidth, isConnectable, data }: BaseNodeProps) => {
const BaseNode: React.FC<Props> = ({ nodeWidth, data, isConnectable }) => {
const { type, status, title, subTitle, imageUri, monitors, isActive, raw } = data;
const isError = status === STATUSES.UNHEALTHY;

Expand Down
Loading

0 comments on commit c05d30d

Please sign in to comment.