diff --git a/src/api/rif-marketplace-cache/notifier/offers/api.ts b/src/api/rif-marketplace-cache/notifier/offers/api.ts index 2ece8a1d5..c40444008 100644 --- a/src/api/rif-marketplace-cache/notifier/offers/api.ts +++ b/src/api/rif-marketplace-cache/notifier/offers/api.ts @@ -8,6 +8,7 @@ import { MinMaxFilter } from 'models/Filters' import { SupportedFiatSymbol } from 'models/Fiat' import { NotifierOffersFilters } from 'models/marketItems/NotifierFilters' import { parseToBigDecimal } from 'utils/parsers' +import { SupportedEventChannel } from 'config/notifier' import { NotifierAPIService } from '../interfaces' import { PlanDTO } from './models' @@ -31,7 +32,7 @@ export const mapFromTransport = ({ name, provider: provider.toLocaleLowerCase(), url, - channels: channels.map((channel) => channel.name), + channels: channels.map((channel) => channel.name as SupportedEventChannel), limit: quantity, priceOptions: prices.map((price) => ({ token: getSupportedTokenByName( diff --git a/src/components/organisms/RemovableRow.tsx b/src/components/organisms/RemovableRow.tsx new file mode 100644 index 000000000..b0b38db8a --- /dev/null +++ b/src/components/organisms/RemovableRow.tsx @@ -0,0 +1,51 @@ +import React, { FC } from 'react' +import { makeStyles, Theme } from '@material-ui/core/styles' +import Grid from '@material-ui/core/Grid' +import { colors } from '@rsksmart/rif-ui' +import RemoveButton from 'components/atoms/RemoveButton' +import { SelectRowButtonProps } from 'components/molecules/SelectRowButton' + +const useStyles = makeStyles((theme: Theme) => ({ + innerContainer: { + backgroundColor: colors.transparent, + borderRadius: 10, + padding: theme.spacing(2), + border: `1px solid ${colors.gray3}`, + height: '25%', + }, + removeButton: { + maxWidth: '20px', + maxHeight: '20px', + minWidth: '20px', + minHeight: '20px', + }, +})) + +const RemovableRow: FC = ({ + className = '', children, id, handleSelect, +}) => { + const classes = useStyles() + return ( + + + + {children} + + + + + + + ) +} + +export default RemovableRow diff --git a/src/components/organisms/notifier/NotificationChannel.tsx b/src/components/organisms/notifier/NotificationChannel.tsx new file mode 100644 index 000000000..948aed470 --- /dev/null +++ b/src/components/organisms/notifier/NotificationChannel.tsx @@ -0,0 +1,32 @@ +import React, { FC } from 'react' +import Grid from '@material-ui/core/Grid' +import Typography from '@material-ui/core/Typography' +import Box from '@material-ui/core/Box' +import { colors, shortenString } from '@rsksmart/rif-ui' +import { NotifierChannel } from 'models/marketItems/NotifierItem' +import RemovableRow from 'components/organisms/RemovableRow' + +export interface NotificationChannelProps { + className?: string + onRemoveClick: (e: any) => void + channel: NotifierChannel +} + +const NotificationChannel: FC = ({ + channel, onRemoveClick, +}) => ( + + + + + {channel.type} + + + + + {shortenString(channel.destination, 30, 25)} + + +) + +export default NotificationChannel diff --git a/src/components/organisms/notifier/NotificationChannelCreate.tsx b/src/components/organisms/notifier/NotificationChannelCreate.tsx new file mode 100644 index 000000000..0245243f5 --- /dev/null +++ b/src/components/organisms/notifier/NotificationChannelCreate.tsx @@ -0,0 +1,119 @@ +import React, { FC, useState } from 'react' +import { makeStyles, Theme } from '@material-ui/core/styles' +import Grid from '@material-ui/core/Grid' +import { NotifierChannel } from 'models/marketItems/NotifierItem' +import { FormControl } from '@material-ui/core' +import Select from '@material-ui/core/Select' +import MenuItem from '@material-ui/core/MenuItem' +import TextField from '@material-ui/core/TextField' +import { SelectRowButton } from 'components/molecules' +import { useForm } from 'react-hook-form' +import Typography from '@material-ui/core/Typography' +import validateURL from 'utils/validationUtils' +import { notifierChannelPlaceHolder } from 'constants/notifier/strings' +import { SUPPORTED_API_CHANNEL_PROTOCOLS, SupportedEventChannel } from 'config/notifier' + +interface Props { + channelAdd: (notifierChannel: NotifierChannel) => void + availableChannels: Array +} + +const useStyles = makeStyles((theme: Theme) => ({ + formControl: { + margin: theme.spacing(1), + minWidth: 120, + }, + select: { + textAlign: 'center', + height: 40, + }, +})) + +type Inputs = { + destination: string +} + +const NotificationChannelCreate: FC = ({ + availableChannels, channelAdd, +}) => { + const [notifierChannel, setNotifierChannel] = useState({ + type: availableChannels[0], + destination: '', + }) + + const handleChannelChange = ({ target: { value } }) => { + setNotifierChannel({ + ...notifierChannel, + type: value, + }) + } + const handleDestinationChange = ({ target: { value } }) => { + setNotifierChannel({ + ...notifierChannel, + destination: value, + }) + } + + const validateDestination = (destination: string) => { + if (notifierChannel.type === 'API') { + return validateURL(destination, SUPPORTED_API_CHANNEL_PROTOCOLS) + } + return true + } + const classes = useStyles() + const { register, handleSubmit, errors } = useForm() + + return ( + + + + + + + + + { + errors.destination && ( + + Invalid destination + + ) + } + + + channelAdd(notifierChannel)) + } + size="large" + variant="outlined" + > + Add Channel + + + + + ) +} + +export default NotificationChannelCreate diff --git a/src/components/organisms/notifier/NotificationChannelsList.tsx b/src/components/organisms/notifier/NotificationChannelsList.tsx new file mode 100644 index 000000000..039ad57fb --- /dev/null +++ b/src/components/organisms/notifier/NotificationChannelsList.tsx @@ -0,0 +1,95 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ +import { + Grid, makeStyles, Typography, +} from '@material-ui/core' +import React, { + FC, useState, +} from 'react' +import { createStyles, Theme } from '@material-ui/core/styles' +import { NotifierChannel } from 'models/marketItems/NotifierItem' +import RoundBtn from 'components/atoms/RoundBtn' +import GridRow from 'components/atoms/GridRow' +import NotificationChannel from 'components/organisms/notifier/NotificationChannel' +import { colors } from '@rsksmart/rif-ui' +import NotificationChannelCreate from 'components/organisms/notifier/NotificationChannelCreate' +import { SupportedEventChannel, SUPPORTED_EVENT_CHANNELS } from 'config/notifier' + +const useStyles = makeStyles((theme: Theme) => createStyles({ + + channelsList: { + marginTop: theme.spacing(2), + marginBottom: theme.spacing(4), + marginLeft: theme.spacing(15), + borderTop: `1px solid ${colors.gray3}`, + padding: theme.spacing(3, 0), + }, + +})) + +type Props = { + channels?: Array +} + +const NotificationChannelsList: FC = ({ channels }) => { + const [addedChannels, setChannels] = useState>([]) + + const classes = useStyles() + + if (!channels) { + return + } + const availableChannels: Array = channels.filter((channel) => SUPPORTED_EVENT_CHANNELS.includes(channel as SupportedEventChannel)) + + const addChannel = (notifierChannel: NotifierChannel) => { + const index = addedChannels.findIndex((channel) => channel.type === notifierChannel.type) + + if (index === -1) addedChannels.push(notifierChannel) + else addedChannels[index].destination = notifierChannel.destination + const newChannels = [...addedChannels] + setChannels(newChannels) + } + + const removeChannel = (event): void => { + const newChannels = addedChannels.filter((i) => i.type !== event.currentTarget.id) + setChannels(newChannels) + } + + const collection = addedChannels.map((channel) => ({ + type: channel.type, + destination: channel.destination, + })) + + return (availableChannels.length ? ( + + + Notification Channels + + + Select the channel that you want to receive your notification through + + + + + { + collection.map((channel) => ( + + )) + } + + + + + Submit + + + + ) : + ) +} +export default NotificationChannelsList diff --git a/src/components/pages/notifier/buy/NotifierOffersSelectedPage.tsx b/src/components/pages/notifier/buy/NotifierOffersSelectedPage.tsx index 2558c514a..1453e88af 100644 --- a/src/components/pages/notifier/buy/NotifierOffersSelectedPage.tsx +++ b/src/components/pages/notifier/buy/NotifierOffersSelectedPage.tsx @@ -3,7 +3,7 @@ import React, { } from 'react' import { - Grid, + Grid, makeStyles, Typography, } from '@material-ui/core' import { NotifierOffersContextProps as ContextProps, NotifierOffersContext } from 'context/Services/notifier/offers' @@ -16,6 +16,15 @@ import TableContainer from '@material-ui/core/TableContainer' import { Item } from 'models/Market' import RemoveButton from 'components/atoms/RemoveButton' import Tooltip from '@material-ui/core/Tooltip' +import NotificationChannelsList from 'components/organisms/notifier/NotificationChannelsList' +import { createStyles, Theme } from '@material-ui/core/styles' + +const useStyles = makeStyles((theme: Theme) => createStyles({ + + eventsSection: { + marginTop: theme.spacing(4), + }, +})) const eventHeaders = { name: 'Name', @@ -31,6 +40,7 @@ type EventItem = Item & { } const NotifierOffersSelectedPage: FC = () => { + const classes = useStyles() const { state: { exchangeRates: { @@ -90,9 +100,7 @@ const NotifierOffersSelectedPage: FC = () => { {/* Header */ } - -
-
+ Notification events added @@ -109,6 +117,9 @@ const NotifierOffersSelectedPage: FC = () => { +
+
+ ) } diff --git a/src/config/notifier/index.ts b/src/config/notifier/index.ts new file mode 100644 index 000000000..8368db5ac --- /dev/null +++ b/src/config/notifier/index.ts @@ -0,0 +1,4 @@ +export type SupportedEventChannel = | 'API' + +export const SUPPORTED_EVENT_CHANNELS: Array = ['API'] +export const SUPPORTED_API_CHANNEL_PROTOCOLS = ['http:', 'https:'] diff --git a/src/constants/notifier/strings.ts b/src/constants/notifier/strings.ts new file mode 100644 index 000000000..4c6230b1e --- /dev/null +++ b/src/constants/notifier/strings.ts @@ -0,0 +1,7 @@ +import { SupportedEventChannel } from 'config/notifier' + +export const notifierChannelPlaceHolder: Record = { + API: 'Enter api destination', +} + +export default {} diff --git a/src/models/marketItems/NotifierItem.ts b/src/models/marketItems/NotifierItem.ts index 2df1d02a6..dd35a0185 100644 --- a/src/models/marketItems/NotifierItem.ts +++ b/src/models/marketItems/NotifierItem.ts @@ -2,6 +2,7 @@ import { Item } from 'models/Market' import Big from 'big.js' import { SupportedToken } from 'contracts/interfaces' import { SubscriptionDTO } from 'api/rif-marketplace-cache/notifier/subscriptions/models' +import { SupportedEventChannel } from 'config/notifier' export type PriceOption = { token: SupportedToken @@ -16,7 +17,7 @@ export type Provider = { export type NotifierPlan = Provider & { name: string - channels: string[] + channels: Array limit: number priceOptions: PriceOption[] daysLeft: number @@ -29,3 +30,8 @@ export type NotifierSubscriptionItem = Item & Omit): boolean => { + try { + const { protocol } = new URL(url) + return validProtocols.includes(protocol) + } catch { + return false + } +} + +export default validateURL