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

refactor: conditions component #4105

Merged
Merged
Show file tree
Hide file tree
Changes from all 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
305 changes: 144 additions & 161 deletions apps/web/src/components/conditions/Conditions.tsx

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions apps/web/src/components/conditions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './Conditions';
export * from './types';
19 changes: 19 additions & 0 deletions apps/web/src/components/conditions/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { BuilderFieldType, BuilderGroupValues, FilterParts, FilterPartTypeEnum } from '@novu/shared';

export interface IConditions {
isNegated?: boolean;
type?: BuilderFieldType;
value?: BuilderGroupValues;
children?: FilterParts[];
}

export enum ConditionsContextEnum {
INTEGRATIONS = 'INTEGRATIONS',
}

export const ConditionsContextFields = {
[ConditionsContextEnum.INTEGRATIONS]: {
label: 'provider instance',
filterPartsList: [FilterPartTypeEnum.TENANT],
},
};
15 changes: 11 additions & 4 deletions apps/web/src/design-system/tooltip/Tooltip.styles.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
import { createStyles, MantineTheme } from '@mantine/core';
import { colors, shadows } from '../config';
import { getGradient } from '../config/helper';

export default createStyles((theme: MantineTheme) => {
export default createStyles((theme: MantineTheme, { error }: { error: boolean }) => {
const dark = theme.colorScheme === 'dark';
const opacityErrorColor = theme.fn.rgba(colors.error, 0.2);
const errorGradient = getGradient(opacityErrorColor);
const backgroundErrorColor = dark ? colors.B17 : colors.white;
const backgroundColor = dark ? colors.B20 : colors.white;
const background = error ? `${errorGradient}, ${backgroundErrorColor}` : backgroundColor;
const color = error ? colors.error : colors.B60;

return {
tooltip: {
backgroundColor: dark ? colors.B20 : theme.white,
color: colors.B60,
background,
color,
boxShadow: dark ? shadows.dark : shadows.medium,
padding: '12px 15px',
fontSize: '14px',
fontWeight: 400,
},
arrow: {
backgroundColor: dark ? colors.B20 : theme.white,
background,
},
};
});
39 changes: 19 additions & 20 deletions apps/web/src/design-system/tooltip/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,29 @@ import { Tooltip as MantineTooltip, TooltipProps } from '@mantine/core';

import useStyles from './Tooltip.styles';

interface ITooltipProps
extends Pick<
TooltipProps,
| 'multiline'
| 'width'
| 'label'
| 'opened'
| 'position'
| 'disabled'
| 'children'
| 'sx'
| 'withinPortal'
| 'offset'
| 'classNames'
> {
error?: boolean;
}
/**
* Tooltip component
*
*/
export function Tooltip({
children,
label,
opened = undefined,
...props
}: Pick<
TooltipProps,
| 'multiline'
| 'width'
| 'label'
| 'opened'
| 'position'
| 'disabled'
| 'children'
| 'sx'
| 'withinPortal'
| 'offset'
| 'classNames'
>) {
const { classes } = useStyles();
export function Tooltip({ children, label, opened = undefined, error = false, ...props }: ITooltipProps) {
const { classes } = useStyles({ error });

return (
<MantineTooltip
Expand Down
20 changes: 14 additions & 6 deletions apps/web/src/design-system/typography/title/Title.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
import { Title as MantineTitle } from '@mantine/core';
import { MantineColor, Title as MantineTitle, useMantineTheme } from '@mantine/core';
import { colors } from '../../config';
import { SpacingProps } from '../../shared/spacing.props';

interface ITitleProps extends JSX.ElementChildrenAttribute, SpacingProps {
size?: 1 | 2;
color?: MantineColor;
}
/**
* Use Title to create headers.
*
*/
export function Title({ size = 1, children, ...rest }: ITitleProps) {
export function Title({ size = 1, children, ...props }: ITitleProps) {
const { colorScheme } = useMantineTheme();

let textColor = props.color;
if (!textColor) {
textColor = colorScheme === 'dark' ? colors.white : colors.B40;
}

return (
<MantineTitle
sx={(theme) => ({
sx={{
fontWeight: size === 1 ? 800 : 700,
color: theme.colorScheme === 'dark' ? colors.white : colors.B40,
})}
}}
order={size}
{...rest}
color={textColor}
{...props}
>
{children}
</MantineTitle>
Expand Down
34 changes: 14 additions & 20 deletions apps/web/src/pages/integrations/components/ConditionIconButton.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { useMemo, useState } from 'react';
import { useState } from 'react';
import styled from '@emotion/styled';
import { Group, ActionIcon, Title } from '@mantine/core';
import { Group, ActionIcon, Center } from '@mantine/core';
import { When } from '../../../components/utils/When';
import { colors, Tooltip, Text, Modal, Button } from '../../../design-system';
import { colors, Tooltip, Text, Modal, Button, Title } from '../../../design-system';
import { Condition, ConditionPlus, Warning } from '../../../design-system/icons';
import { IConditions } from '../types';

const IconButton = styled(Group)`
text-align: center;
Expand All @@ -29,29 +28,22 @@ const RemovesPrimary = () => {
};

export const ConditionIconButton = ({
conditions,
conditions = 0,
primary = false,
onClick,
}: {
conditions?: IConditions[];
conditions?: number;
primary?: boolean;
onClick: () => void;
}) => {
const [modalOpen, setModalOpen] = useState(false);
const numOfConditions: number = useMemo(() => {
if (conditions && conditions[0] && conditions[0].children) {
return conditions[0].children.length;
}

return 0;
}, [conditions]);

return (
<>
<Tooltip
label={
<>
{numOfConditions > 0 ? 'Edit' : 'Add'} Conditions
{conditions > 0 ? 'Edit' : 'Add'} Conditions
<When truthy={primary}>
<RemovesPrimary />
</When>
Expand All @@ -71,14 +63,14 @@ export const ConditionIconButton = ({
variant="transparent"
>
<IconButton position="center" spacing={4}>
<When truthy={numOfConditions === 0}>
<When truthy={conditions === 0}>
<ConditionPlus />
</When>
<When truthy={numOfConditions > 0}>
<Group spacing={4}>
<When truthy={conditions > 0}>
<Center inline>
<Condition />
<div>{numOfConditions}</div>
</Group>
<div>{conditions}</div>
</Center>
</When>
</IconButton>
</ActionIcon>
Expand All @@ -88,7 +80,9 @@ export const ConditionIconButton = ({
title={
<Group spacing={8}>
<Warning color="#EAA900" />
<Title color="#EAA900">Primary will be removed</Title>
<Title size={2} color="#EAA900">
Primary will be removed
</Title>
</Group>
}
size="lg"
Expand Down
12 changes: 6 additions & 6 deletions apps/web/src/pages/integrations/components/PrimaryIconButton.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import styled from '@emotion/styled';
import { Group, ActionIcon, Text, Title } from '@mantine/core';
import { Group, ActionIcon, Text } from '@mantine/core';
import { useState } from 'react';
import { When } from '../../../components/utils/When';
import { Tooltip, Button, colors, Modal } from '../../../design-system';
import { Tooltip, Button, colors, Modal, Title } from '../../../design-system';
import { RemoveCondition, StarEmpty, Warning } from '../../../design-system/icons';

const IconButton = styled(Group)`
Expand All @@ -28,11 +28,11 @@ const RemovesCondition = () => {
};

export const PrimaryIconButton = ({
conditions,
conditions = 0,
primary = false,
onClick,
}: {
conditions?: any[];
conditions?: number;
primary?: boolean;
onClick: () => void;
}) => {
Expand All @@ -48,7 +48,7 @@ export const PrimaryIconButton = ({
label={
<>
Mark as Primary
<When truthy={conditions && conditions.length > 0}>
<When truthy={conditions > 0}>
<RemovesCondition />
</When>
</>
Expand All @@ -57,7 +57,7 @@ export const PrimaryIconButton = ({
>
<ActionIcon
onClick={() => {
if (conditions && conditions.length > 0) {
if (conditions > 0) {
setModalOpen(true);

return;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ReactNode, useMemo, useState } from 'react';
import { Group, useMantineTheme } from '@mantine/core';
import { Controller, useFormContext } from 'react-hook-form';
import { Controller, useFormContext, useWatch } from 'react-hook-form';
import { CHANNELS_WITH_PRIMARY } from '@novu/shared';

import { Button, colors, Dropdown, Modal, NameInput, Text, Title } from '../../../design-system';
Expand Down Expand Up @@ -36,6 +36,15 @@ export const UpdateIntegrationSidebarHeader = ({
const canMarkAsPrimary = provider && !provider.primary && CHANNELS_WITH_PRIMARY.includes(provider.channel);
const { openModal, SelectPrimaryIntegrationModal } = useSelectPrimaryIntegrationModal();

const watchedConditions = useWatch({ control, name: 'conditions' });
const numOfConditions: number = useMemo(() => {
if (watchedConditions && watchedConditions[0] && watchedConditions[0].children) {
return watchedConditions[0].children.length;
}

return 0;
}, [watchedConditions]);

const shouldSetNewPrimary = useMemo(() => {
if (!provider) return false;

Expand Down Expand Up @@ -117,9 +126,9 @@ export const UpdateIntegrationSidebarHeader = ({
onClick={() => {
makePrimaryIntegration({ id: provider.integrationId });
}}
conditions={provider.conditions}
conditions={numOfConditions}
/>
<ConditionIconButton primary={provider.primary} onClick={openConditions} conditions={provider.conditions} />
<ConditionIconButton primary={provider.primary} onClick={openConditions} conditions={numOfConditions} />
<div>
<Dropdown
withArrow={false}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { ActionIcon, Group, Radio, Text, Input, useMantineTheme } from '@mantine/core';
import { useEffect, useMemo, useState } from 'react';
import { useEffect, useMemo } from 'react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { Controller, useForm } from 'react-hook-form';
import styled from '@emotion/styled';
import { useDisclosure } from '@mantine/hooks';
import { ChannelTypeEnum, ICreateIntegrationBodyDto, InAppProviderIdEnum, providers } from '@novu/shared';
import { Button, colors, NameInput, Sidebar } from '../../../../design-system';
import { ConditionPlus, ArrowLeft, Condition } from '../../../../design-system/icons';
Expand All @@ -15,10 +16,10 @@ import { errorMessage, successMessage } from '../../../../utils/notifications';
import { QueryKeys } from '../../../../api/query.keys';
import { ProviderImage } from './SelectProviderSidebar';
import { CHANNEL_TYPE_TO_STRING } from '../../../../utils/channels';
import type { IConditions, IntegrationEntity } from '../../types';
import type { IntegrationEntity } from '../../types';
import { useProviders } from '../../useProviders';
import { When } from '../../../../components/utils/When';
import { Conditions } from '../../../../components/conditions/Conditions';
import { Conditions, IConditions } from '../../../../components/conditions';
import { ConditionIconButton } from '../ConditionIconButton';

interface ICreateProviderInstanceForm {
Expand All @@ -45,10 +46,10 @@ export function CreateProviderInstanceSidebar({
const { colorScheme } = useMantineTheme();
const { environments, isLoading: areEnvironmentsLoading } = useFetchEnvironments();
const { isLoading: areIntegrationsLoading, providers: integrations } = useProviders();
const [openConditions, setOpenConditions] = useState(false);
const isLoading = areEnvironmentsLoading || areIntegrationsLoading;
const queryClient = useQueryClient();
const segment = useSegment();
const [conditionsFormOpened, { close: closeConditionsForm, open: openConditionsForm }] = useDisclosure(false);

const provider = useMemo(
() => providers.find((el) => el.channel === channel && el.id === providerId),
Expand Down Expand Up @@ -145,17 +146,20 @@ export function CreateProviderInstanceSidebar({
if (!provider) {
return null;
}
const updateConditions = (conditions: IConditions[]) => {
setValue('conditions', conditions, { shouldDirty: true });
};

if (conditionsFormOpened) {
const [conditions, name] = getValues(['conditions', 'name']);

if (openConditions) {
return (
<Conditions
conditions={getValues('conditions')}
name={getValues('name')}
isOpened={openConditions}
setConditions={(data) => {
setValue('conditions', data, { shouldDirty: true });
}}
onClose={() => setOpenConditions(false)}
conditions={conditions}
name={name}
isOpened={conditionsFormOpened}
setConditions={updateConditions}
onClose={closeConditionsForm}
/>
);
}
Expand Down Expand Up @@ -192,7 +196,7 @@ export function CreateProviderInstanceSidebar({
}}
/>
<Group mt={-10} spacing={12} align="start" noWrap ml="auto">
<ConditionIconButton onClick={() => setOpenConditions(true)} />
<ConditionIconButton onClick={openConditionsForm} />
</Group>
</Group>
}
Expand Down Expand Up @@ -281,7 +285,7 @@ export function CreateProviderInstanceSidebar({
<Group mt={16} position="left">
<Button
variant="outline"
onClick={() => setOpenConditions(true)}
onClick={openConditionsForm}
icon={
<>
<When truthy={numOfConditions === 0}>
Expand Down
Loading