Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GEN-1517]: actions add via gql #1635

Merged
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;

BenElferink marked this conversation as resolved.
Show resolved Hide resolved
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 { safeJsonParse } from '@/utils';
import { InputList } from '@/reuseable-components';
import React, { useEffect, useMemo, useState } from 'react';
import { Checkbox } from '@/reuseable-components';
import { FieldTitle, FieldWrapper } from './styled';
import styled from 'styled-components';

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

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

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

const isSelected = (id: string, selected: string[]) => {
return !!selected?.find((str) => str === id);
};

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

const handleChange = (arr: string[]) => {
const [isLastSelection, setIsLastSelection] = useState(false);

BenElferink marked this conversation as resolved.
Show resolved Hide resolved
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 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 }) => {
const selected = isSelected(id, mappedValue);

return (
<Checkbox
key={id}
title={label}
disabled={isLastSelection && selected}
initialValue={mappedValue.includes(id)}
BenElferink marked this conversation as resolved.
Show resolved Hide resolved
onChange={(bool) => handleChange(id, bool)}
/>
);
})}
</ListContainer>
</FieldWrapper>
);
};
Expand Down
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 = {
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) => {
let num = Number(val);
BenElferink marked this conversation as resolved.
Show resolved Hide resolved

if (Number.isNaN(num) || num < MIN || num > MAX) {
num = 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
Loading