Skip to content

Commit

Permalink
[GEN-1517]: actions add via gql (#1635)
Browse files Browse the repository at this point in the history
Co-authored-by: Alon Braymok <138359965+alonkeyval@users.noreply.github.com>
  • Loading branch information
BenElferink and alonkeyval authored Oct 27, 2024
1 parent 40f2215 commit 2d4b1bb
Show file tree
Hide file tree
Showing 17 changed files with 400 additions and 259 deletions.
9 changes: 8 additions & 1 deletion frontend/webapp/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@
"extends": "next/core-web-vitals",
"rules": {
"semi": ["error", "always"],
"quotes": ["error", "single", { "avoidEscape": true }]
"quotes": [
"error",
"single",
{
"avoidEscape": true,
"allowTemplateLiterals": true
}
]
}
}
12 changes: 4 additions & 8 deletions frontend/webapp/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ThemeProvider } from 'styled-components';
import { NotificationManager } from '@/components';
import ReduxProvider from '@/store/redux-provider';
import { QueryClient, QueryClientProvider } from 'react-query';
import { ThemeProviderWrapper } from '@keyval-dev/design-system';
// import { ThemeProviderWrapper } from '@keyval-dev/design-system';

const LAYOUT_STYLE: React.CSSProperties = {
margin: 0,
Expand All @@ -18,11 +18,7 @@ const LAYOUT_STYLE: React.CSSProperties = {
height: '100vh',
};

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
export default function RootLayout({ children }: { children: React.ReactNode }) {
const queryClient = new QueryClient({
defaultOptions: {
queries: {
Expand All @@ -35,15 +31,15 @@ export default function RootLayout({
useSSE();

return (
<html lang="en">
<html lang='en'>
<ReduxProvider>
<ApolloWrapper>
<QueryClientProvider client={queryClient}>
<ThemeProvider theme={theme}>
{/* <ThemeProviderWrapper> */}
<body suppressHydrationWarning={true} style={LAYOUT_STYLE}>
{children}
{/* <NotificationManager /> */}
<NotificationManager />
</body>
{/* </ThemeProviderWrapper> */}
</ThemeProvider>
Expand Down
18 changes: 10 additions & 8 deletions frontend/webapp/components/notification/notification-manager.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react';
import React, { useEffect } from 'react';
import { useSelector } from 'react-redux';

import Notification from './notification';
// import Notification from './notification';
import styled from 'styled-components';
import { RootState } from '@/store';

Expand All @@ -16,17 +15,20 @@ const NotificationsWrapper = styled.div`
`;

export const NotificationManager: React.FC = () => {
const notifications = useSelector(
(state: RootState) => state.notification.notifications
);
const notifications = useSelector((state: RootState) => state.notification.notifications);

// temporary - until we fix the "theme" error on import from "design.system"
useEffect(() => {
if (notifications.length) alert(notifications[notifications.length - 1].message);
}, [notifications.length]);

return (
<NotificationsWrapper>
{notifications
{/* {notifications
.filter((notification) => notification.isNew)
.map((notification) => (
<Notification key={notification.id} {...notification} />
))}
))} */}
</NotificationsWrapper>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React, { useMemo } from 'react';
import { safeJsonParse } from '@/utils';
import { Input } from '@/reuseable-components';
import { FieldTitle, FieldWrapper } from './styled';

type Props = {
value: string;
setValue: (value: string) => void;
};

type Parsed = {
fallback_sampling_ratio: number;
};

const MIN = 0,
MAX = 100;

const ErrorSampler: React.FC<Props> = ({ value, setValue }) => {
const mappedValue = useMemo(() => safeJsonParse<Parsed>(value, { fallback_sampling_ratio: 0 }).fallback_sampling_ratio, [value]);

const handleChange = (val: string) => {
let num = Number(val);

if (Number.isNaN(num) || num < MIN || num > MAX) {
num = MIN;
}

const payload: Parsed = {
fallback_sampling_ratio: num,
};

setValue(JSON.stringify(payload));
};

return (
<FieldWrapper>
<FieldTitle>Fallback sampling ratio</FieldTitle>
<Input type='number' min={MIN} max={MAX} value={mappedValue} onChange={({ target: { value: v } }) => handleChange(v)} />
</FieldWrapper>
);
};

export default ErrorSampler;
Original file line number Diff line number Diff line change
@@ -1,45 +1,41 @@
import React from 'react';
import { ActionsType } from '@/types';
import AddClusterInfo from './add-cluster-info';
import DeleteAttributes from './delete-attributes';
import RenameAttributes from './rename-attributes';
import PiiMasking from './pii-masking';
import ErrorSampler from './error-sampler';
import ProbabilisticSampler from './probabilistic-sampler';

interface ActionCustomFieldsProps {
actionType?: ActionsType;
value: string;
setValue: (value: string) => void;
}

const ActionCustomFields: React.FC<ActionCustomFieldsProps> = ({ actionType, value, setValue }) => {
switch (actionType) {
case ActionsType.ADD_CLUSTER_INFO: {
return <AddClusterInfo value={value} setValue={setValue} />;
}

case ActionsType.DELETE_ATTRIBUTES: {
return <DeleteAttributes value={value} setValue={setValue} />;
}

case ActionsType.RENAME_ATTRIBUTES: {
return <RenameAttributes value={value} setValue={setValue} />;
}
type ComponentProps = {
value: string;
setValue: (value: string) => void;
};

case ActionsType.PII_MASKING: {
return <PiiMasking value={value} setValue={setValue} />;
}
type ComponentType = React.FC<ComponentProps> | null;

case ActionsType.ERROR_SAMPLER:
return null;
const componentsMap: Record<ActionsType, ComponentType> = {
[ActionsType.ADD_CLUSTER_INFO]: AddClusterInfo,
[ActionsType.DELETE_ATTRIBUTES]: DeleteAttributes,
[ActionsType.RENAME_ATTRIBUTES]: RenameAttributes,
[ActionsType.PII_MASKING]: PiiMasking,
[ActionsType.ERROR_SAMPLER]: ErrorSampler,
[ActionsType.PROBABILISTIC_SAMPLER]: ProbabilisticSampler,
[ActionsType.LATENCY_SAMPLER]: null,
};

case ActionsType.PROBABILISTIC_SAMPLER:
return null;
const ActionCustomFields: React.FC<ActionCustomFieldsProps> = ({ actionType, value, setValue }) => {
if (!actionType) return null;

case ActionsType.LATENCY_SAMPLER:
return null;
const Component = componentsMap[actionType];

default:
return null;
}
return Component ? <Component value={value} setValue={setValue} /> : null;
};

export default ActionCustomFields;
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, { useMemo } from 'react';
import styled from 'styled-components';
import { safeJsonParse } from '@/utils';
import { InputList } from '@/reuseable-components';
import { Checkbox } from '@/reuseable-components';
import { FieldTitle, FieldWrapper } from './styled';
import React, { useEffect, useMemo, useState } from 'react';

type Props = {
value: string;
Expand All @@ -12,21 +13,61 @@ type Parsed = {
piiCategories: string[];
};

const ListContainer = styled.div`
display: flex;
flex-direction: row;
gap: 32px;
`;

const strictPicklist = [
{
id: 'CREDIT_CARD',
label: 'Credit Card',
},
];

const PiiMasking: React.FC<Props> = ({ value, setValue }) => {
const mappedValue = useMemo(() => safeJsonParse<Parsed>(value, { piiCategories: [] }).piiCategories, [value]);
const [isLastSelection, setIsLastSelection] = useState(mappedValue.length === 1);

useEffect(() => {
if (!mappedValue.length) {
const payload: Parsed = {
piiCategories: strictPicklist.map(({ id }) => id),
};

setValue(JSON.stringify(payload));
setIsLastSelection(payload.piiCategories.length === 1);
}
// eslint-disable-next-line
}, []);

const handleChange = (id: string, isAdd: boolean) => {
const arr = isAdd ? [...mappedValue, id] : mappedValue.filter((str) => str !== id);

const handleChange = (arr: string[]) => {
const payload: Parsed = {
piiCategories: arr,
};

setValue(JSON.stringify(payload));
setIsLastSelection(arr.length === 1);
};

return (
<FieldWrapper>
<FieldTitle>Attributes to mask</FieldTitle>
<InputList value={mappedValue} onChange={handleChange} />

<ListContainer>
{strictPicklist.map(({ id, label }) => (
<Checkbox
key={id}
title={label}
disabled={isLastSelection && mappedValue.includes(id)}
initialValue={mappedValue.includes(id)}
onChange={(bool) => handleChange(id, bool)}
/>
))}
</ListContainer>
</FieldWrapper>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { useMemo } from 'react';
import { safeJsonParse } from '@/utils';
import { Input } from '@/reuseable-components';
import { FieldTitle, FieldWrapper } from './styled';

type Props = {
value: string;
setValue: (value: string) => void;
};

type Parsed = {
sampling_percentage: string;
};

const MIN = 0,
MAX = 100;

const ProbabilisticSampler: React.FC<Props> = ({ value, setValue }) => {
const mappedValue = useMemo(() => safeJsonParse<Parsed>(value, { sampling_percentage: '0' }).sampling_percentage, [value]);

const handleChange = (val: string) => {
const num = Math.max(MIN, Math.min(Number(val), MAX)) || MIN;

const payload: Parsed = {
sampling_percentage: String(num),
};

setValue(JSON.stringify(payload));
};

return (
<FieldWrapper>
<FieldTitle>Sampling percentage</FieldTitle>
<Input type='number' min={MIN} max={MAX} value={mappedValue} onChange={({ target: { value: v } }) => handleChange(v)} />
</FieldWrapper>
);
};

export default ProbabilisticSampler;
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import styled from 'styled-components';
import { type ActionInput } from '@/types';
import ActionCustomFields from './custom-fields';
import { ActionFormData } from '@/hooks/actions/useActionFormData';
import { type ActionOption } from '../choose-action-modal/action-options';
import { DocsButton, Input, Text, TextArea } from '@/reuseable-components';
import { MonitoringCheckboxes } from '@/reuseable-components/monitoring-checkboxes';
Expand All @@ -23,8 +23,8 @@ const FieldTitle = styled(Text)`

interface ChooseActionContentProps {
action: ActionOption;
formData: ActionFormData;
handleFormChange: (key: keyof ActionFormData, val: any) => void;
formData: ActionInput;
handleFormChange: (key: keyof ActionInput, val: any) => void;
}

const ChooseActionBody: React.FC<ChooseActionContentProps> = ({ action, formData, handleFormChange }) => {
Expand Down
Loading

0 comments on commit 2d4b1bb

Please sign in to comment.