Skip to content

Commit

Permalink
refactor: feed selection in in-app editor
Browse files Browse the repository at this point in the history
  • Loading branch information
djabarovgeorge committed Jul 20, 2022
1 parent 09068de commit 21ec138
Show file tree
Hide file tree
Showing 6 changed files with 4,547 additions and 3,348 deletions.
2 changes: 1 addition & 1 deletion .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 0 additions & 46 deletions apps/web/src/components/templates/in-app-editor/Chips.styles.ts

This file was deleted.

81 changes: 81 additions & 0 deletions apps/web/src/components/templates/in-app-editor/FeedChip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import React from 'react';
import { FeedEntity } from '@novu/dal';
import styled from '@emotion/styled';
import { ColorScheme, useMantineTheme } from '@mantine/core';
import { colors, shadows } from '../../../design-system';
import { DotsHorizontal } from '../../../design-system/icons';
import { useEnvController } from '../../../store/use-env-controller';

interface IFeedItemProps {
showFeed: boolean;
index: number;
setOpened: (hover: boolean) => void;
item: FeedEntity;
feedIndex: number;
onEditClick: () => void;
field: any;
setValue: (key: string, value: string, options: { shouldDirty: boolean }) => void;
}

export function FeedChip(props: IFeedItemProps) {
const colorScheme = useMantineTheme().colorScheme;
const { readonly } = useEnvController();

const selectedItem = props.field.value === props.item._id;

return (
<Wrapper
selectedItem={selectedItem}
showFeed={props.showFeed}
colorScheme={colorScheme}
readonly={readonly}
onClick={() => {
props.setValue(`steps.${props.index}.template.feedId`, props?.item?._id || '', { shouldDirty: true });
}}
>
<div>{props.item?.name}</div>
<DotsHorizontal
onClick={(e) => {
if (props.showFeed) {
e.stopPropagation();
props.onEditClick();
}
}}
style={{
color: colorScheme === 'dark' ? colors.white : colors.B80,
cursor: `${props.showFeed ? 'pointer' : 'default'}`,
}}
/>
</Wrapper>
);
}

const Wrapper = styled.div<{ colorScheme: ColorScheme; showFeed: boolean; selectedItem: boolean; readonly: boolean }>`
${({ showFeed, readonly }) =>
(!showFeed || readonly) &&
`
pointer-events: none;
opacity: 0.4;
`}
display: flex;
align-items: center;
width: 100%;
height: 45px;
border-radius: 7px;
cursor: default;
justify-content: space-evenly;
box-shadow: ${({ colorScheme }) => (colorScheme === 'dark' ? shadows.dark : shadows.medium)};
background: ${({ colorScheme }) => (colorScheme === 'dark' ? colors.B17 : colors.white)};
${({ selectedItem, colorScheme }) =>
selectedItem &&
`
background: ${
colorScheme === 'dark'
? `linear-gradient(0deg, ${colors.B20} 0%, ${colors.B20} 100%)`
: `linear-gradient(0deg, ${colors.white} 0%, ${colors.white} 100%)`
} padding-box,
${colors.horizontal} border-box`};
border: 1px solid transparent;
`;
237 changes: 237 additions & 0 deletions apps/web/src/components/templates/in-app-editor/FeedItems.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
import React, { useState } from 'react';
import { Popover, useMantineTheme, Grid, ColorScheme } from '@mantine/core';
import { FeedChip } from './FeedChip';
import { colors, shadows, Text, Tooltip, Button } from '../../../design-system';
import { Copy, Trash } from '../../../design-system/icons';
import { useClipboard } from '@mantine/hooks';
import { FeedEntity } from '@novu/dal';
import styled from '@emotion/styled';
import { showNotification } from '@mantine/notifications';
import * as Sentry from '@sentry/react';
import { useMutation, useQuery, useQueryClient } from 'react-query';
import { deleteFeed, getFeeds } from '../../../api/feeds';
import { QueryKeys } from '../../../api/query.keys';

interface IFeedItemPopoverProps {
showFeed: boolean;
index: number;
setValue: (key: string, value: string, options: { shouldDirty: boolean }) => void;
field: any;
}

export function FeedItems(props: IFeedItemPopoverProps) {
const { data: feeds } = useQuery<FeedEntity[]>(QueryKeys.getFeeds, getFeeds);

return (
<FeedsBlock>
<Grid gutter={10}>
{(feeds || []).map((item, feedIndex) => {
return (
<Grid.Col span={4}>
<FeedPopover
field={props.field}
item={item}
feedIndex={feedIndex}
showFeed={props.showFeed}
index={props.index}
setValue={props.setValue}
/>
</Grid.Col>
);
})}
</Grid>
</FeedsBlock>
);
}

function FeedPopover(props: IFeedPopoverProps) {
const [opened, setOpened] = useState(false);
const colorScheme = useMantineTheme().colorScheme;

return (
<Popover
opened={opened}
onClose={() => setOpened(false)}
target={
<FeedChip
item={props.item}
feedIndex={props.feedIndex}
setOpened={setOpened}
index={props.index}
showFeed={props.showFeed}
field={props.field}
setValue={props.setValue}
onEditClick={() => {
setOpened((prevCheck) => !prevCheck);
}}
/>
}
width={260}
position={'bottom'}
placement={'center'}
withArrow
styles={{
root: {
width: '100%',
height: '1px',
},
inner: { margin: 0, padding: 0, height: '95px' },
arrow: {
backgroundColor: colorScheme === 'dark' ? colors.B20 : colors.white,
height: '-22px',
border: 'none',
margin: '0px',
top: '-3px',
},
body: {
backgroundColor: colorScheme === 'dark' ? colors.B20 : colors.white,
color: colorScheme === 'dark' ? colors.white : colors.B40,
border: 'none',
marginTop: '1px',
width: '100%',
},
popover: { width: '170px', height: '95px' },
}}
>
<PopoverActionBlock setOpened={setOpened} showFeed={props.showFeed} feedItem={props.item} />
</Popover>
);
}

function PopoverActionBlock({
setOpened,
showFeed,
feedItem,
}: {
setOpened: (boolean) => void;
showFeed: boolean;
feedItem?: FeedEntity;
}) {
const colorScheme = useMantineTheme().colorScheme;

return (
<ActionBlockWrapper colorScheme={colorScheme} onMouseLeave={() => setOpened(false)}>
<CopyBlock showFeed={showFeed} feedItem={feedItem} />
<DeleteBlock setOpened={setOpened} showFeed={showFeed} feedItem={feedItem} />
</ActionBlockWrapper>
);
}

function CopyBlock({ showFeed, feedItem }: { showFeed: boolean; feedItem?: FeedEntity }) {
const [opened, setOpened] = useState(false);

const colorScheme = useMantineTheme().colorScheme;
const clipboardIdentifier = useClipboard({ timeout: 1000 });

function handleOnclick() {
clipboardIdentifier.copy(feedItem?.identifier);
setOpened(true);
setTimeout(() => {
setOpened(false);
}, 1000);
}

return (
<Row disabled={!showFeed} onClick={handleOnclick}>
<Tooltip label={'Copied!'} opened={opened}>
<Copy
style={{
color: colorScheme === 'dark' ? colors.white : colors.B80,
}}
/>
</Tooltip>
<Text>Copy ID</Text>
</Row>
);
}

function DeleteBlock({
setOpened,
showFeed,
feedItem,
}: {
setOpened: (boolean) => void;
showFeed: boolean;
feedItem?: FeedEntity;
}) {
const theme = useMantineTheme();
const queryClient = useQueryClient();

const { mutateAsync: deleteFeedById } = useMutation<
FeedEntity[],
{ error: string; message: string; statusCode: number },
string
>((feedId) => deleteFeed(feedId), {
onSuccess: (data) => {
queryClient.refetchQueries([QueryKeys.getFeeds]);
},
});

async function deleteFeedHandler(feedId: string) {
try {
await deleteFeedById(feedId);
setOpened(false);
showNotification({
message: 'Feed deleted successfully',
color: 'green',
});
} catch (e: any) {
Sentry.captureException(e);

showNotification({
message: e.message || 'Un-expected error occurred',
color: 'red',
});
}
}

return (
<Row disabled={!showFeed} onClick={() => deleteFeedHandler(feedItem?._id || '')}>
<Trash
style={{
color: theme.colorScheme === 'dark' ? colors.white : colors.B80,
}}
/>
<Text>Delete Feed</Text>
</Row>
);
}

const ActionBlockWrapper = styled.div<{ colorScheme: ColorScheme }>`
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-around;
border-radius: 7px;
box-shadow: ${({ colorScheme }) => (colorScheme === 'dark' ? shadows.dark : shadows.medium)};
`;

const Row = styled(Button)`
display: flex;
justify-content: start;
align-items: center;
z-index: 2;
margin-right: 5px;
margin-left: 5px;
background: ${colors.B20};
box-shadow: none;
:hover {
background: ${colors.B40};
}
`;

const FeedsBlock = styled.div`
margin-bottom: 20px;
`;

interface IFeedPopoverProps {
setValue: (key: string, value: string, options: { shouldDirty: boolean }) => void;
showFeed: boolean;
feedIndex: number;
index: number;
item: FeedEntity;
field: any;
}
Loading

0 comments on commit 21ec138

Please sign in to comment.