From ecb431922e1b58443ab3579d1c2796e31a98466f Mon Sep 17 00:00:00 2001 From: Championrunner <27343592+Ishaan28malik@users.noreply.github.com> Date: Wed, 10 Jul 2024 17:37:37 +0530 Subject: [PATCH 01/96] feat: added ui component for inheritance accordion (#439) * feat: added ui component for inheritance accordion * feat: removed Flex * feat: fix * feat: ui fix * feat: pr review changes * feat: pr review changes * feat: pr review changes * feat: fixes * feat: theme used * feat: ui fixes * feat: fixed accordian ui --------- Co-authored-by: Md Irshad Ansari --- .../ui/src/components/atoms/Typography.tsx | 2 +- .../ui/src/components/molecules/Accordion.tsx | 123 ++++++++++++++++++ packages/ui/src/components/molecules/index.ts | 1 + .../stories/molecules/Accordion.stories.tsx | 21 +++ 4 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 packages/ui/src/components/molecules/Accordion.tsx create mode 100644 packages/ui/src/stories/molecules/Accordion.stories.tsx diff --git a/packages/ui/src/components/atoms/Typography.tsx b/packages/ui/src/components/atoms/Typography.tsx index 6e62b7408..1ff6fb91e 100644 --- a/packages/ui/src/components/atoms/Typography.tsx +++ b/packages/ui/src/components/atoms/Typography.tsx @@ -33,7 +33,7 @@ interface HeadingProps $textAlign?: 'center' | 'left' | 'right'; $letterSpacing?: string | number; $userSelect?: 'all' | 'auto' | 'none' | 'text'; - $whiteSpace?: 'normal' | 'nowrap' | 'pre-wrap'; + $whiteSpace?: 'normal' | 'nowrap' | 'pre-wrap' | 'pre-line'; $textOverflow?: 'clip' | 'ellipsis' | 'fade'; $filter?: string; } diff --git a/packages/ui/src/components/molecules/Accordion.tsx b/packages/ui/src/components/molecules/Accordion.tsx new file mode 100644 index 000000000..1f99291ea --- /dev/null +++ b/packages/ui/src/components/molecules/Accordion.tsx @@ -0,0 +1,123 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import PropTypes from 'prop-types'; +import React, { FC } from 'react'; +import { styled } from 'styled-components'; + +import { AngleDown, InfoItalicsIcon } from '../../assets'; +import { Typography, Flex } from '../atoms'; +import { + FlexProps, + flex, + utils, + width, + WidthProps, + UtilsProps, +} from '../utils'; + +export interface AccordionProps extends WidthProps { + id: string; + title: string; + content: string; +} + +const HeaderTitleContainer = styled.div` + display: flex; + justify-content: flex-start; + align-items: center; + color: ${({ theme }) => theme.palette.text.message}; + height: auto; + width: 100%; +`; + +const DetailsContainer = styled.div` + border-top: 0px solid ${({ theme }) => theme.palette.border.separator}; + transition: max-height ease-in-out 0.5s, border-top step-end 0.5s; + max-height: 0px; +`; + +const ArrowContainer = styled(Flex)` + transition: transform ease-in-out 0.5s; +`; + +const HeaderContainer = styled.label` + display: flex; + ${flex} + ${utils} +`; + +const StyledContainer = styled.div` + border-radius: 8px; + overflow: hidden; + width: 720px; + background: ${({ theme }) => theme.palette.background.separatorSecondary}; + border: 1px solid ${({ theme }) => theme.palette.border.separator}; + line-height: 18px; + text-align: left; + + ${width} + + input { + display: none; + } + + input:checked ~ ${HeaderContainer} ${ArrowContainer} { + transform: rotate(180deg); + } + + input:checked ~ ${DetailsContainer} { + max-height: 100vh; + transition: max-height ease-in-out 1s, border-top step-start 1s; + border-top: 1px solid ${({ theme }) => theme.palette.border.separator}; + } +`; + +export const Accordion: FC = ({ + id, + title, + content, + ...restProps +}) => { + const inputId = `accordion-${id}`; + + return ( + + + + + + + {title} + + + + + + + + + {content} + + + + ); +}; + +Accordion.propTypes = { + id: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, + content: PropTypes.string.isRequired, +}; diff --git a/packages/ui/src/components/molecules/index.ts b/packages/ui/src/components/molecules/index.ts index 5890307f7..83acdcb1a 100644 --- a/packages/ui/src/components/molecules/index.ts +++ b/packages/ui/src/components/molecules/index.ts @@ -40,4 +40,5 @@ export * from './Topbar'; export * from './UpdateBar'; export * from './WallectConnect'; export * from './WalletDialogMainContainer'; +export * from './Accordion'; export * from './SimpleJsonView'; diff --git a/packages/ui/src/stories/molecules/Accordion.stories.tsx b/packages/ui/src/stories/molecules/Accordion.stories.tsx new file mode 100644 index 000000000..87488cfba --- /dev/null +++ b/packages/ui/src/stories/molecules/Accordion.stories.tsx @@ -0,0 +1,21 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { Accordion } from '../../components'; + +const meta: Meta = { + component: Accordion, + tags: ['autodocs'], +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + id: 'reminder-time', + title: 'What is reminder time?', + content: + 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Maxime fuga magnam iure assumenda eligendi animi architecto facere quas dignissimos odio doloremque sapiente tenetur, totam temporibus. Accusantium ad a praesentium corporis mollitia magni dignissimos. Eos ut a exercitationem consequatur quod. Enim assumenda impedit maiores dolores nam doloremque eveniet quas quis culpa?', + }, +}; From f60bd496af6c0787234b35f9dcb3ff14af436777 Mon Sep 17 00:00:00 2001 From: Championrunner <27343592+Ishaan28malik@users.noreply.github.com> Date: Fri, 12 Jul 2024 18:42:58 +0530 Subject: [PATCH 02/96] feat: added oneinmany ui component (#437) * feat: added oneinmany ui component * feat: changes as per pr * feat: updated as per PR review * Delete packages/cysync-core-constants/dist/cjs/i18n/lang/ar-AE.json * Delete .changeset/fuzzy-stingrays-judge.md * Delete .changeset/lovely-toes-learn.md * Delete packages/cysync-core-constants/dist/cjs/constants.d.ts * Delete packages/cysync-core-constants/dist/cjs/constants.d.ts.map * Delete packages/cysync-core-constants/dist/cjs/constants.js * Delete packages/cysync-core-constants/dist/cjs/i18n/index.d.ts * Delete packages/cysync-core-constants/dist/cjs/i18n/index.d.ts.map * Delete packages/cysync-core-constants/dist/cjs/i18n/index.js * Delete packages/cysync-core-constants/dist/cjs/i18n/lang/de-DE.json * Delete packages/cysync-core-constants/dist/cjs/i18n/lang/en.json * Delete packages/cysync-core-constants/dist/cjs/i18n/lang/id-ID.json * Delete packages/cysync-core-constants/dist/cjs/i18n/lang/zh-CN.json * Delete packages/cysync-core-constants/dist/cjs/i18n/types.d.ts * Delete packages/cysync-core-constants/dist/cjs/i18n/types.d.ts.map * Delete packages/cysync-core-constants/dist/cjs/i18n/types.js * Delete packages/cysync-core-constants/dist/cjs/index.d.ts * Delete packages/cysync-core-constants/dist/cjs/index.d.ts.map * Delete packages/cysync-core-constants/dist/cjs/index.js * Delete packages/cysync-core-constants/dist/cjs/types/deviceError.d.ts * Delete packages/cysync-core-constants/dist/cjs/types/deviceError.d.ts.map * Delete packages/cysync-core-constants/dist/cjs/types/deviceError.js * Delete packages/cysync-core-constants/dist/cjs/types/index.d.ts * Delete packages/cysync-core-constants/dist/cjs/types/index.d.ts.map * Delete packages/cysync-core-constants/dist/cjs/types/index.js * Delete packages/cysync-core-constants/dist/cjs/types/serverError.d.ts * Delete packages/cysync-core-constants/dist/cjs/types/serverError.d.ts.map * Delete packages/cysync-core-constants/dist/cjs/types/serverError.js * Delete packages/cysync-core-constants/dist/esm/constants.d.ts * Delete packages/cysync-core-constants/dist/esm/constants.d.ts.map * Delete packages/cysync-core-constants/dist/esm/constants.js * Delete packages/cysync-core-constants/dist/esm/i18n/index.d.ts * Delete packages/cysync-core-constants/dist/esm/i18n/index.d.ts.map * Delete packages/cysync-core-constants/dist/esm/i18n/index.js * Delete packages/cysync-core-constants/dist/esm/i18n/lang/ar-AE.json * Delete packages/cysync-core-constants/dist/esm/i18n/lang/de-DE.json * Delete packages/cysync-core-constants/dist/esm/i18n/lang/en.json * Delete packages/cysync-core-constants/dist/esm/i18n/lang/id-ID.json * Delete packages/cysync-core-constants/dist/esm/i18n/lang/zh-CN.json * Delete packages/cysync-core-constants/dist/esm/i18n/types.d.ts * Delete packages/cysync-core-constants/dist/esm/i18n/types.d.ts.map * Delete packages/cysync-core-constants/dist/esm/i18n/types.js * Delete packages/cysync-core-constants/dist/esm/index.d.ts * Delete packages/cysync-core-constants/dist/esm/index.d.ts.map * Delete packages/cysync-core-constants/dist/esm/index.js * Delete packages/cysync-core-constants/dist/esm/types/deviceError.d.ts * Delete packages/cysync-core-constants/dist/esm/types/deviceError.d.ts.map * Delete packages/cysync-core-constants/dist/esm/types/deviceError.js * Delete packages/cysync-core-constants/dist/esm/types/index.d.ts * Delete packages/cysync-core-constants/dist/esm/types/index.d.ts.map * Delete packages/cysync-core-constants/dist/esm/types/index.js * Delete packages/cysync-core-constants/dist/esm/types/serverError.d.ts * Delete packages/cysync-core-constants/dist/esm/types/serverError.d.ts.map * Delete packages/cysync-core-constants/dist/esm/types/serverError.js * feat: pr review changes * feat: pr review changes * feat: image fixes * feat: fixes * feat: pr review changes * feat: pr review changes * feat: pr rview and added type 2 * feat: removed className * feat: variable naming * feat: ui fixes and updates * feat: ui fixes * feat: theme used * feat: fixed oneinmany component animation for style 1 --------- Co-authored-by: Md Irshad Ansari --- packages/ui/src/assets/images/common/index.ts | 8 + .../images/common/oneInMany2Default.svg | 31 +++ .../assets/images/common/oneInMany2Hover.svg | 27 ++ .../images/common/oneInmany1Default.svg | 11 + .../assets/images/common/oneInmany1Hover.svg | 11 + .../ui/src/components/molecules/OneInMany.tsx | 259 ++++++++++++++++++ packages/ui/src/components/molecules/index.ts | 1 + .../stories/molecules/OneInMany.stories.tsx | 28 ++ packages/ui/src/themes/color.styled.ts | 6 + packages/ui/src/themes/theme.styled.ts | 4 + 10 files changed, 386 insertions(+) create mode 100644 packages/ui/src/assets/images/common/oneInMany2Default.svg create mode 100644 packages/ui/src/assets/images/common/oneInMany2Hover.svg create mode 100644 packages/ui/src/assets/images/common/oneInmany1Default.svg create mode 100644 packages/ui/src/assets/images/common/oneInmany1Hover.svg create mode 100644 packages/ui/src/components/molecules/OneInMany.tsx create mode 100644 packages/ui/src/stories/molecules/OneInMany.stories.tsx diff --git a/packages/ui/src/assets/images/common/index.ts b/packages/ui/src/assets/images/common/index.ts index bd4ad8690..ac253ef87 100644 --- a/packages/ui/src/assets/images/common/index.ts +++ b/packages/ui/src/assets/images/common/index.ts @@ -44,6 +44,10 @@ import loaderIcon from './loader.png'; import cysyncLogoBig from './logo-big.svg'; import cysyncLogoSmall from './logo-small.png'; import LogoOutlinedAsideImage from './LogoOutlineAside.png'; +import oneInMany1Default from './oneInmany1Default.svg'; +import oneInMany1Hover from './oneInmany1Hover.svg'; +import oneInMany2Default from './oneInMany2Default.svg'; +import oneInMany2Hover from './oneInMany2Hover.svg'; import openExternalLink from './open-external-link.svg'; import qrImage from './qr.png'; import qrcodeIcon from './qrcode.svg'; @@ -144,4 +148,8 @@ export { UniSwapLogo, createWalletGraphics, recoverWalletFromSeedphraseGraphics, + oneInMany1Default, + oneInMany1Hover, + oneInMany2Hover, + oneInMany2Default, }; diff --git a/packages/ui/src/assets/images/common/oneInMany2Default.svg b/packages/ui/src/assets/images/common/oneInMany2Default.svg new file mode 100644 index 000000000..1da27194b --- /dev/null +++ b/packages/ui/src/assets/images/common/oneInMany2Default.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/ui/src/assets/images/common/oneInMany2Hover.svg b/packages/ui/src/assets/images/common/oneInMany2Hover.svg new file mode 100644 index 000000000..381171b08 --- /dev/null +++ b/packages/ui/src/assets/images/common/oneInMany2Hover.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/ui/src/assets/images/common/oneInmany1Default.svg b/packages/ui/src/assets/images/common/oneInmany1Default.svg new file mode 100644 index 000000000..0e48b5539 --- /dev/null +++ b/packages/ui/src/assets/images/common/oneInmany1Default.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packages/ui/src/assets/images/common/oneInmany1Hover.svg b/packages/ui/src/assets/images/common/oneInmany1Hover.svg new file mode 100644 index 000000000..7a3eb41de --- /dev/null +++ b/packages/ui/src/assets/images/common/oneInmany1Hover.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packages/ui/src/components/molecules/OneInMany.tsx b/packages/ui/src/components/molecules/OneInMany.tsx new file mode 100644 index 000000000..519fd728c --- /dev/null +++ b/packages/ui/src/components/molecules/OneInMany.tsx @@ -0,0 +1,259 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import PropTypes from 'prop-types'; +import React, { useState } from 'react'; +import { styled } from 'styled-components'; + +import { + oneInMany1Default, + oneInMany1Hover, + oneInMany2Default, + oneInMany2Hover, +} from '../../assets'; +import { ThemeType } from '../../themes'; +import { Flex } from '../atoms'; +import { WidthProps, width } from '../utils'; + +export type OneInManyStyleType = '1' | '2'; + +export interface OneInManyProps extends WidthProps { + title: string; + description: string; + styleType: OneInManyStyleType; +} + +interface BgStyleProps { + isSelected: boolean; + theme: ThemeType; +} + +const getBgStyle1 = ({ isSelected, theme }: BgStyleProps) => ` + &:before, + &:after { + color: ${theme.palette.text.white}; + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-position: initial; + background-repeat: no-repeat; + backface-visibility: hidden; + z-index: 0; + transition: opacity 0.5s ease-in-out; + background-position: right; + animation: rotateBounceOut 0.5s ease-in-out forwards; + ${isSelected && `animation: rotateBounceIn 0.5s ease-in-out forwards;`} + } + + &:before { + background-image: url(${oneInMany1Default}); + opacity: 1; + } + + &:after { + background-image: url(${oneInMany1Hover}); + opacity: 0; + } + + ${ + !isSelected && + ` + &:hover:before { + animation: rotateBounceIn 0.5s ease-in-out forwards; + opacity: 0; + } + + &:hover:after { + animation: rotateBounceIn 0.5s ease-in-out forwards; + opacity: 1; + } + ` + } + + @keyframes rotateBounceIn { + 0% { + transform: rotateZ(0deg); + } + 10% { + transform: rotateZ(10deg); + } + 20% { + transform: rotateZ(10deg); + } + 80% { + transform: rotateZ(-190deg); + } + 90% { + transform: rotateZ(-190deg); + } + 100% { + transform: rotateZ(-180deg); + } + } + + @keyframes rotateBounceOut { + 0% { + transform: rotateZ(-180deg); + } + 10% { + transform: rotateZ(-190deg); + } + 20% { + transform: rotateZ(-190deg); + } + 80% { + transform: rotateZ(10deg); + } + 90% { + transform: rotateZ(10deg); + } + 100% { + transform: rotateZ(0deg); + } + } +`; + +const getBgStyle2 = ({ isSelected, theme }: BgStyleProps) => ` + &:before, + &:after { + color: ${theme.palette.text.white}; + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-position: initial; + background-repeat: no-repeat; + transition: opacity 0.5s ease, transform 0.5s ease; + backface-visibility: hidden; + z-index: 0; + } + + &:before { + background-image: url(${oneInMany2Default}); + opacity: ${isSelected ? 0 : 1}; + transform: ${isSelected ? 'rotateY(180deg)' : 'rotateY(0deg)'}; + } + + &:after { + background-image: url(${oneInMany2Hover}); + opacity: ${isSelected ? 1 : 0}; + transform: ${isSelected ? 'rotateY(0deg)' : 'rotateY(180deg)'}; + background-position: initial; + } + + ${ + !isSelected && + ` + &:hover:before { + opacity: 0; + transform: rotateY(180deg); + } + &:hover:after { + opacity: 1; + transform: rotateY(0deg); + } + ` + } +`; + +const getBgStyleMap: Record< + OneInManyStyleType, + (props: BgStyleProps) => string +> = { + '1': getBgStyle1, + '2': getBgStyle2, +}; + +const StyledContainer = styled.div< + Omit & { isSelected: boolean } +>` + position: relative; + border: 1px solid + ${({ isSelected, theme }) => + isSelected ? `${theme.palette.border.cardSelected}` : 'transparent'}; + box-shadow: ${({ isSelected }) => + isSelected ? '0px 0px 12px 4px #1B1813 inset' : ''}; + background: ${({ theme }) => theme.palette.cardSelected}; + background-position: center; + background-repeat: no-repeat; + border-radius: 8px; + overflow: hidden; + cursor: pointer; + width: 348px; + height: 128px; + color: ${({ theme }) => theme.palette.text.white}; + transition: background 0.5s ease, box-shadow 0.5s ease; + + ${width} + + ${({ styleType, isSelected, theme }) => + getBgStyleMap[styleType]({ isSelected, theme } as any)} + + ${({ isSelected, theme }) => + !isSelected && + ` + &:hover { + background: ${theme.palette.cardHover}; + background-position: right; + + ${StyledTitle} { + position: relative; + z-index: 3; + background: ${theme.palette.title}; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + } + } + `} +`; + +const StyledTitle = styled.div` + font-size: 64px; + font-weight: 700; + line-height: 96px; + z-index: 3; + width: 120px; +`; + +const StyledDescription = styled.div` + width: 148px; + font-size: 12px; + font-weight: 400; + line-height: 18px; + color: ${({ theme }) => theme.palette.text.white}; + z-index: 3; + text-align: end; + margin-left: 16px; +`; + +export const OneInMany: React.FC = ({ + title, + description, + styleType, + ...restProps +}) => { + const [isSelected, setisSelected] = useState(false); + + return ( + setisSelected(!isSelected)} + {...restProps} + > + + {title} + {description} + + + ); +}; + +OneInMany.propTypes = { + title: PropTypes.string.isRequired, + description: PropTypes.string.isRequired, + styleType: PropTypes.oneOf(['1', '2']).isRequired, +}; diff --git a/packages/ui/src/components/molecules/index.ts b/packages/ui/src/components/molecules/index.ts index 83acdcb1a..fff2262e4 100644 --- a/packages/ui/src/components/molecules/index.ts +++ b/packages/ui/src/components/molecules/index.ts @@ -42,3 +42,4 @@ export * from './WallectConnect'; export * from './WalletDialogMainContainer'; export * from './Accordion'; export * from './SimpleJsonView'; +export * from './OneInMany'; diff --git a/packages/ui/src/stories/molecules/OneInMany.stories.tsx b/packages/ui/src/stories/molecules/OneInMany.stories.tsx new file mode 100644 index 000000000..a999b0a85 --- /dev/null +++ b/packages/ui/src/stories/molecules/OneInMany.stories.tsx @@ -0,0 +1,28 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { OneInMany } from '../../components'; + +const meta: Meta = { + component: OneInMany, + tags: ['autodocs'], +}; + +export default meta; + +type Story = StoryObj; + +export const StyleType1: Story = { + args: { + title: 'YES', + description: 'Description goes here', + styleType: '1', + }, +}; + +export const StyleType2: Story = { + args: { + title: 'NO', + description: 'Different description', + styleType: '2', + }, +}; diff --git a/packages/ui/src/themes/color.styled.ts b/packages/ui/src/themes/color.styled.ts index 9ae764c25..b86a6212b 100644 --- a/packages/ui/src/themes/color.styled.ts +++ b/packages/ui/src/themes/color.styled.ts @@ -17,6 +17,11 @@ export const colors = { silver: 'linear-gradient(180deg, #A2ADB3 -2.08%, #F3F1F2 34.27%, #BCC3C9 66.28%, #DCDFE4 102.08%)', stripe: 'linear-gradient(90deg, #211C18 1.69%, #242018 100%)', + cardSelected: + 'linear-gradient(285deg, rgba(96, 58, 23, 0.20) 0%, rgba(0, 0, 0, 0.00) 60.65%), #2A2827', + cardHover: + 'linear-gradient(105deg, rgba(96, 58, 23, 0.20) 0%, rgba(0, 0, 0, 0.00) 60.65%), #332F2D', + title: `linear-gradient(90deg,#e9b873 0.19%,#fedd8f 37.17%,#b78d51 100.19%)`, }, info: { main: '#F1AE4A', @@ -108,6 +113,7 @@ export const colors = { subMenuLeft: '#534B44', topbar: '#342C26', card: '#534A44', + cardSelected: `#e0bb74`, }, shadow: { dropdown: '#0f0d0b', diff --git a/packages/ui/src/themes/theme.styled.ts b/packages/ui/src/themes/theme.styled.ts index addd75215..dce68958d 100644 --- a/packages/ui/src/themes/theme.styled.ts +++ b/packages/ui/src/themes/theme.styled.ts @@ -59,6 +59,9 @@ export const theme = { shadow: { dropdown: colors.shadow.dropdown, }, + cardSelected: colors.gradients.cardSelected, + cardHover: colors.gradients.cardHover, + title: colors.gradients.title, background: { progress: colors.background.progressBar, input: colors.background.input, @@ -126,6 +129,7 @@ export const theme = { subMenuLeft: colors.border.subMenuLeft, topbar: colors.border.topbar, card: colors.border.card, + cardSelected: colors.border.cardSelected, }, contrastThreshold: 3, tonalOffset: 0.2, From d2e76bc9134e28ca7d033110dc79193866b05a79 Mon Sep 17 00:00:00 2001 From: Championrunner <27343592+Ishaan28malik@users.noreply.github.com> Date: Wed, 17 Jul 2024 13:16:34 +0530 Subject: [PATCH 03/96] feat: ui added for inheritance reminder component (#443) * feat: added ui for inheritance reminder component * feat: git issue * feat: css fixes * feat: wrokflow fix * feat: pr review changes * feat: pr review changes * feat: pr review changes * feat: used variables * feat: ui fixes * feat: animation updates and pr review changes * feat: disabled width * refactor: code refactor * refactor: using typography --------- Co-authored-by: Md Irshad Ansari --- packages/ui/icons/clock-icon.svg | 10 + .../src/assets/images/common/bgClockIcon.svg | 10 + packages/ui/src/assets/images/common/index.ts | 2 + .../ui/src/components/molecules/OneInMany.tsx | 8 +- .../ui/src/components/molecules/Reminder.tsx | 186 ++++++++++++++++++ packages/ui/src/components/molecules/index.ts | 1 + .../stories/molecules/Reminder.stories.tsx | 26 +++ packages/ui/src/themes/color.styled.ts | 7 +- packages/ui/src/themes/theme.styled.ts | 13 +- 9 files changed, 254 insertions(+), 9 deletions(-) create mode 100644 packages/ui/icons/clock-icon.svg create mode 100644 packages/ui/src/assets/images/common/bgClockIcon.svg create mode 100644 packages/ui/src/components/molecules/Reminder.tsx create mode 100644 packages/ui/src/stories/molecules/Reminder.stories.tsx diff --git a/packages/ui/icons/clock-icon.svg b/packages/ui/icons/clock-icon.svg new file mode 100644 index 000000000..2a8d712e2 --- /dev/null +++ b/packages/ui/icons/clock-icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/ui/src/assets/images/common/bgClockIcon.svg b/packages/ui/src/assets/images/common/bgClockIcon.svg new file mode 100644 index 000000000..ee78a4938 --- /dev/null +++ b/packages/ui/src/assets/images/common/bgClockIcon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/packages/ui/src/assets/images/common/index.ts b/packages/ui/src/assets/images/common/index.ts index ac253ef87..5eff1cdc6 100644 --- a/packages/ui/src/assets/images/common/index.ts +++ b/packages/ui/src/assets/images/common/index.ts @@ -5,6 +5,7 @@ import arrowGoldenForward from './arrow-golden-forward.svg'; import arrowWhiteBackward from './arrow-white-backward.svg'; import asideIcon from './aside.svg'; import backIcon from './back.svg'; +import bgClockIcon from './bgClockIcon.svg'; import binanceIcon from './binance.svg'; import bitcoinIcon from './bitcoin-icon.svg'; import blockIcon from './Block-unselectedsvg.svg'; @@ -148,6 +149,7 @@ export { UniSwapLogo, createWalletGraphics, recoverWalletFromSeedphraseGraphics, + bgClockIcon, oneInMany1Default, oneInMany1Hover, oneInMany2Hover, diff --git a/packages/ui/src/components/molecules/OneInMany.tsx b/packages/ui/src/components/molecules/OneInMany.tsx index 519fd728c..d0bd8999d 100644 --- a/packages/ui/src/components/molecules/OneInMany.tsx +++ b/packages/ui/src/components/molecules/OneInMany.tsx @@ -173,10 +173,10 @@ const StyledContainer = styled.div< position: relative; border: 1px solid ${({ isSelected, theme }) => - isSelected ? `${theme.palette.border.cardSelected}` : 'transparent'}; + isSelected ? `${theme.palette.border.selected}` : 'transparent'}; box-shadow: ${({ isSelected }) => isSelected ? '0px 0px 12px 4px #1B1813 inset' : ''}; - background: ${({ theme }) => theme.palette.cardSelected}; + background: ${({ theme }) => theme.palette.gradients.cardSelected}; background-position: center; background-repeat: no-repeat; border-radius: 8px; @@ -196,13 +196,13 @@ const StyledContainer = styled.div< !isSelected && ` &:hover { - background: ${theme.palette.cardHover}; + background: ${theme.palette.gradients.cardHover}; background-position: right; ${StyledTitle} { position: relative; z-index: 3; - background: ${theme.palette.title}; + background: ${theme.palette.gradients.title}; -webkit-background-clip: text; -webkit-text-fill-color: transparent; } diff --git a/packages/ui/src/components/molecules/Reminder.tsx b/packages/ui/src/components/molecules/Reminder.tsx new file mode 100644 index 000000000..a496d7626 --- /dev/null +++ b/packages/ui/src/components/molecules/Reminder.tsx @@ -0,0 +1,186 @@ +import React, { FC, useState } from 'react'; +import styled from 'styled-components'; + +import { ClockIcon, InformationIcon, bgClockIcon } from '../../assets'; +import { useTheme } from '../../themes'; +import { Typography, Flex } from '../atoms'; +import { svgGradients } from '../GlobalStyles'; +import { WidthProps, width } from '../utils'; + +const StyledContainer = styled.div< + WidthProps & { $isSelected: boolean; $isHover: boolean } +>` + position: relative; + background: ${props => + !props.$isSelected + ? `linear-gradient(273deg, rgba(96, 58, 23, 0.20) 1.52%, rgba(0, 0, 0, 0.00) 52.42%), #2A2A27` + : props.theme.palette.background.cardSelected}; + border-radius: 8px; + overflow: hidden; + cursor: pointer; + width: 348px; + height: 53px; + color: ${({ theme }) => theme.palette.bullet.white}; + z-index: 1; + box-shadow: ${props => + props.$isSelected + ? `0px 0px 12px 4px ${props.theme.palette.shadow.selected} inset` + : `0px 0px 12px 4px ${props.theme.palette.shadow.selected}`}; + border: 1px solid + ${props => + props.$isSelected + ? `${props.theme.palette.border.selected}` + : 'transparent'}; + + ${width} + + &:before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-image: ${props => + props.$isSelected + ? '' + : `url(${bgClockIcon}), url(${bgClockIcon}), url(${bgClockIcon})`}, + url(${bgClockIcon}), url(${bgClockIcon}), url(${bgClockIcon}); + background-size: 40px 40px; + background-repeat: no-repeat; + background-position: -20px -15px, 30px 30px, 80px -30px, 140px 30px, 200px 0, + 270px 30px; + z-index: -1; + animation: ${props => + !props.$isSelected && props.$isHover + ? 'moveBackgrounds 1s ease-in-out forwards' + : ''}; + } + + &:hover { + border-radius: var(--8-px, 8px); + background: ${props => + props.$isSelected + ? '' + : `linear-gradient(93deg, rgba(96, 58, 23, 0.20) 0%, rgba(0, 0, 0, 0.00) 52.46%), #332F2D`}; + &:before { + animation: moveBackgrounds 1s ease-in-out forwards; + } + } + + &:not(:hover):before { + animation: moveBackgroundsBack 1s ease-in-out forwards; + } + + @keyframes moveBackgrounds { + 0% { + background-position-y: -15px, 30px, -30px, 30px, 0, 30px; + background-position-x: -20px, 30px, 80px, 140px, 200px, 270px; + } + 100% { + background-position-y: 30px, -30px, 30px, -15px, 30px, -30px; + background-position-x: -20px, 50px, 110px, 160px, 230px, 290px; + } + } + + @keyframes moveBackgroundsBack { + 0% { + background-position-y: 30px, -30px, 30px, -15px, 30px, -30px; + background-position-x: -20px, 50px, 110px, 160px, 230px, 290px; + } + 100% { + background-position-y: -15px, 30px, -30px, 30px, 0, 30px; + background-position-x: -20px, 30px, 80px, 140px, 200px, 270px; + } + } +`; + +const DisableContainer = styled.div` + position: relative; + border-radius: 8px; + overflow: hidden; + width: 348px; + height: 53px; + background: ${({ theme }) => theme.palette.background.cardDisabled}; + display: flex; + justify-content: left; + align-items: center; + cursor: not-allowed; + ${width} +`; + +const ClockInfoContainer = styled.div` + position: absolute; + display: flex; + justify-content: center; + top: 50%; + right: 18px; + transform: translateY(-50%); +`; + +const DateLabel = styled(Typography)` + font-size: 14px; + font-weight: 700; + line-height: 21px; + text-align: left; + color: ${({ theme }) => theme.palette.bullet.white}; +`; + +const DisabledDate = styled(Typography)` + font-size: 14px; + font-weight: 400; + line-height: 21px; + text-align: left; + color: ${({ theme }) => theme.palette.background.separator}; +`; + +export interface ReminderProps extends WidthProps { + date: string; + disabled: boolean; +} + +export const Reminder: FC = ({ + date, + disabled, + ...restProps +}) => { + const [isHover, setIsHover] = useState(false); + const [isSelected, setisSelected] = useState(false); + const theme = useTheme(); + + return !disabled ? ( + setIsHover(true)} + onClick={() => setisSelected(!isSelected)} + onMouseLeave={() => setIsHover(false)} + $isSelected={isSelected} + $isHover={isHover} + {...restProps} + > + + + {date} + + + ) : ( + + + + {date} + + + + + + ); +}; diff --git a/packages/ui/src/components/molecules/index.ts b/packages/ui/src/components/molecules/index.ts index fff2262e4..58f592d34 100644 --- a/packages/ui/src/components/molecules/index.ts +++ b/packages/ui/src/components/molecules/index.ts @@ -40,6 +40,7 @@ export * from './Topbar'; export * from './UpdateBar'; export * from './WallectConnect'; export * from './WalletDialogMainContainer'; +export * from './Reminder'; export * from './Accordion'; export * from './SimpleJsonView'; export * from './OneInMany'; diff --git a/packages/ui/src/stories/molecules/Reminder.stories.tsx b/packages/ui/src/stories/molecules/Reminder.stories.tsx new file mode 100644 index 000000000..f34a19425 --- /dev/null +++ b/packages/ui/src/stories/molecules/Reminder.stories.tsx @@ -0,0 +1,26 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { Reminder } from '../../components'; + +const meta: Meta = { + component: Reminder, + tags: ['autodocs'], +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + date: '3 Months', + disabled: false, + }, +}; + +export const Disabled: Story = { + args: { + date: '3 Months', + disabled: true, + }, +}; diff --git a/packages/ui/src/themes/color.styled.ts b/packages/ui/src/themes/color.styled.ts index b86a6212b..be99a362f 100644 --- a/packages/ui/src/themes/color.styled.ts +++ b/packages/ui/src/themes/color.styled.ts @@ -57,6 +57,9 @@ export const colors = { divider: '#333130', dialog: '#2B2420', }, + boxShadow: { + selected: '#1B1813', + }, background: { progressBar: '#1F1915', input: '#27221D', @@ -87,6 +90,8 @@ export const colors = { filterItem: `#1F1C19`, calendar: '#342F2C', calendarHeader: '#211C18', + cardDisabled: '#282522', + cardSelected: '#2A2827', }, border: { popup: '#2C2520', @@ -113,7 +118,7 @@ export const colors = { subMenuLeft: '#534B44', topbar: '#342C26', card: '#534A44', - cardSelected: `#e0bb74`, + selected: '#e0bb74', }, shadow: { dropdown: '#0f0d0b', diff --git a/packages/ui/src/themes/theme.styled.ts b/packages/ui/src/themes/theme.styled.ts index dce68958d..ea2100991 100644 --- a/packages/ui/src/themes/theme.styled.ts +++ b/packages/ui/src/themes/theme.styled.ts @@ -58,10 +58,13 @@ export const theme = { }, shadow: { dropdown: colors.shadow.dropdown, + selected: colors.boxShadow.selected, + }, + gradients: { + cardSelected: colors.gradients.cardSelected, + cardHover: colors.gradients.cardHover, + title: colors.gradients.title, }, - cardSelected: colors.gradients.cardSelected, - cardHover: colors.gradients.cardHover, - title: colors.gradients.title, background: { progress: colors.background.progressBar, input: colors.background.input, @@ -102,6 +105,8 @@ export const theme = { popup: colors.border.popup, danger: colors.background.danger, error: colors.background.error, + cardSelected: colors.background.cardSelected, + cardDisabled: colors.background.cardDisabled, }, border: { popup: colors.border.popup, @@ -129,7 +134,7 @@ export const theme = { subMenuLeft: colors.border.subMenuLeft, topbar: colors.border.topbar, card: colors.border.card, - cardSelected: colors.border.cardSelected, + selected: colors.border.selected, }, contrastThreshold: 3, tonalOffset: 0.2, From 8e523a67f784efd3b5bea53517820312753fdc31 Mon Sep 17 00:00:00 2001 From: Championrunner <27343592+Ishaan28malik@users.noreply.github.com> Date: Sat, 20 Jul 2024 16:25:11 +0530 Subject: [PATCH 04/96] feat: ui added for many in many inheritance component (#476) * feat: ui added manyinmany component for inheritance * feat: pr review changes * feat: pr review changes * feat: pr review changes * feat: animation * feat: fixes * feat: used variables * feat: fixes * feat: ui fixes * feat: animation and pr review changes * feat: pr review changes * feat: pr review changes * feat: pr review changes on svg nd state management * feat: ui fixes and updates pr review changes build fix * feat: icon names and fixes * refactor: many in many component --------- Co-authored-by: Md Irshad Ansari --- packages/ui/icons/mim-hover-wallet-icon.svg | 35 ++++ packages/ui/package.json | 3 +- .../ui/src/assets/icons/MimDefaultWallet.tsx | 59 ++++++ packages/ui/src/assets/icons/index.ts | 1 + packages/ui/src/assets/images/common/index.ts | 4 + .../ui/src/assets/images/common/mim-bg.svg | 12 ++ .../src/assets/images/common/mim-hover-bg.svg | 32 ++++ .../src/components/molecules/ManyInMany.tsx | 181 ++++++++++++++++++ packages/ui/src/components/molecules/index.ts | 1 + .../stories/molecules/ManyInMany.stories.tsx | 26 +++ 10 files changed, 353 insertions(+), 1 deletion(-) create mode 100644 packages/ui/icons/mim-hover-wallet-icon.svg create mode 100644 packages/ui/src/assets/icons/MimDefaultWallet.tsx create mode 100644 packages/ui/src/assets/images/common/mim-bg.svg create mode 100644 packages/ui/src/assets/images/common/mim-hover-bg.svg create mode 100644 packages/ui/src/components/molecules/ManyInMany.tsx create mode 100644 packages/ui/src/stories/molecules/ManyInMany.stories.tsx diff --git a/packages/ui/icons/mim-hover-wallet-icon.svg b/packages/ui/icons/mim-hover-wallet-icon.svg new file mode 100644 index 000000000..2e71e7126 --- /dev/null +++ b/packages/ui/icons/mim-hover-wallet-icon.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/ui/package.json b/packages/ui/package.json index 38cb48820..36e82e902 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -16,8 +16,9 @@ "build:esm": "tsc -p tsconfig.json && copyfiles -u 1 src/**/*.{png,jpg,svg,jpeg,gif,webm} dist/esm", "build:cjs": "tsc -p tsconfig_cjs.json && copyfiles -u 1 src/**/*.{png,jpg,svg,jpeg,gif,webm} dist/cjs", "build:icons": "svgr icons --out-dir ./src/assets/icons/generated --no-svgo", - "prebuild": "pnpm build:icons", + "prebuild": "pnpm clean:icons && pnpm build:icons", "clean": "rimraf dist", + "clean:icons": "rimraf ./src/assets/icons/generated/*", "test": "jest", "pre-commit": "lint-staged", "storybook": "storybook dev -p 6006", diff --git a/packages/ui/src/assets/icons/MimDefaultWallet.tsx b/packages/ui/src/assets/icons/MimDefaultWallet.tsx new file mode 100644 index 000000000..86143fd4c --- /dev/null +++ b/packages/ui/src/assets/icons/MimDefaultWallet.tsx @@ -0,0 +1,59 @@ +import React from 'react'; + +import { SvgStyle as Svg, SvgProps } from './Svg'; + +export const MimDefaultWalletIcon: React.FC = ({ + stroke, + fill, + ...props +}) => ( + + + + + + + + + + + + + +); diff --git a/packages/ui/src/assets/icons/index.ts b/packages/ui/src/assets/icons/index.ts index e104c4c70..0f436e752 100644 --- a/packages/ui/src/assets/icons/index.ts +++ b/packages/ui/src/assets/icons/index.ts @@ -1,2 +1,3 @@ export * from './generated'; export * from './Svg'; +export * from './MimDefaultWallet'; diff --git a/packages/ui/src/assets/images/common/index.ts b/packages/ui/src/assets/images/common/index.ts index 5eff1cdc6..57e135669 100644 --- a/packages/ui/src/assets/images/common/index.ts +++ b/packages/ui/src/assets/images/common/index.ts @@ -45,6 +45,8 @@ import loaderIcon from './loader.png'; import cysyncLogoBig from './logo-big.svg'; import cysyncLogoSmall from './logo-small.png'; import LogoOutlinedAsideImage from './LogoOutlineAside.png'; +import manyInManyBgImage from './mim-bg.svg'; +import manyInManyHoverBgImage from './mim-hover-bg.svg'; import oneInMany1Default from './oneInmany1Default.svg'; import oneInMany1Hover from './oneInmany1Hover.svg'; import oneInMany2Default from './oneInMany2Default.svg'; @@ -154,4 +156,6 @@ export { oneInMany1Hover, oneInMany2Hover, oneInMany2Default, + manyInManyBgImage, + manyInManyHoverBgImage, }; diff --git a/packages/ui/src/assets/images/common/mim-bg.svg b/packages/ui/src/assets/images/common/mim-bg.svg new file mode 100644 index 000000000..c32849cd6 --- /dev/null +++ b/packages/ui/src/assets/images/common/mim-bg.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/packages/ui/src/assets/images/common/mim-hover-bg.svg b/packages/ui/src/assets/images/common/mim-hover-bg.svg new file mode 100644 index 000000000..80bb5f00c --- /dev/null +++ b/packages/ui/src/assets/images/common/mim-hover-bg.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + diff --git a/packages/ui/src/components/molecules/ManyInMany.tsx b/packages/ui/src/components/molecules/ManyInMany.tsx new file mode 100644 index 000000000..8cac95fff --- /dev/null +++ b/packages/ui/src/components/molecules/ManyInMany.tsx @@ -0,0 +1,181 @@ +import React, { FC, useState } from 'react'; +import styled from 'styled-components'; + +import { + InformationIcon, + manyInManyBgImage, + manyInManyHoverBgImage, + MimDefaultWalletIcon, + MimHoverWalletIcon, +} from '../../assets'; +import { useTheme } from '../../themes'; +import { Flex } from '../atoms'; +import { WidthProps, width } from '../utils'; + +const getBoxShadow = (params: { + $isSelected: boolean; + theme: any; + disabled: boolean; +}) => { + if (params.disabled) return 'none'; + if (params.$isSelected) + return `0px 0px 12px 4px ${params.theme.palette.shadow.selected} inset`; + return `0px 0px 12px 4px ${params.theme.palette.shadow.selected}`; +}; + +const getBackground = (params: { + $isSelected: boolean; + theme: any; + disabled: boolean; + isHovered?: boolean; +}) => { + if (params.$isSelected) return params.theme.palette.background.cardSelected; + if (params.disabled) return params.theme.palette.background.cardDisabled; + if (params.isHovered) return params.theme.palette.gradients.cardHover; + return params.theme.palette.background.cardSelected; +}; + +const StyledMimDefaultWalletIcon = styled(MimDefaultWalletIcon)``; +const StyledMimHoverWalletIcon = styled(MimHoverWalletIcon)``; + +const StyledDateLabel = styled.div<{ $isSelected: boolean }>` + font-family: Poppins; + font-size: 14px; + font-weight: 400; + line-height: 21px; + text-align: center; + z-index: 1; + margin-top: 16px; + transition: font-size 0.5s ease; +`; + +const StyledContainer = styled.div< + { $isSelected: boolean; disabled: boolean } & WidthProps +>` + position: relative; + overflow: hidden; + width: 276px; + height: 128px; + padding: 24px 16px; + color: ${({ theme, disabled }) => + disabled ? theme.palette.border.separator : theme.palette.bullet.white}; + cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')}; + border-radius: 8px; + border: 1px solid + ${({ $isSelected, theme, disabled }) => + $isSelected && !disabled ? theme.palette.border.selected : 'transparent'}; + box-shadow: ${getBoxShadow}; + background: ${getBackground}; + ${width} + + &::after { + content: ''; + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + z-index: 0; + background-image: ${({ $isSelected, disabled }) => + !disabled && !$isSelected ? `url(${manyInManyBgImage})` : 'none'}; + background-position: bottom center; + background-repeat: no-repeat; + background-size: 280px; + transition: transform 0.5s ease-in-out; + transform-origin: bottom center; + } + + &:hover::after { + transform: ${({ $isSelected, disabled }) => + !disabled && !$isSelected + ? 'translateX(-30%) rotate(45deg) scale(1)' + : 'none'}; + background-position: bottom left 180%; + width: 167%; + height: 175%; + background-size: 496px; + background-image: ${({ $isSelected, disabled }) => + !disabled && !$isSelected ? `url(${manyInManyHoverBgImage})` : 'none'}; + } + + &:hover { + background: ${({ theme, $isSelected, disabled }) => + getBackground({ $isSelected, theme, disabled, isHovered: true })}; + } + + ${StyledMimDefaultWalletIcon} { + display: block; + } + + ${StyledMimHoverWalletIcon} { + display: none; + } + + &:hover ${StyledMimDefaultWalletIcon} { + display: ${({ disabled, $isSelected }) => + !disabled && !$isSelected ? 'none' : 'block'}; + } + + &:hover ${StyledMimHoverWalletIcon} { + display: ${({ disabled, $isSelected }) => + !disabled && !$isSelected ? 'block' : 'none'}; + } + + &:hover ${StyledDateLabel} { + font-size: ${({ $isSelected, disabled }) => + !$isSelected && !disabled ? '15px' : '14px'}; + } +`; + +const StyledRedInfoImage = styled.div` + position: absolute; + right: 8px; + top: 8px; +`; + +export interface ManyInManyProps extends WidthProps { + title: string; + disabled: boolean; +} + +export const ManyInMany: FC = ({ + title, + disabled, + ...restProps +}) => { + const [isSelected, setIsSelected] = useState(false); + const theme = useTheme(); + + return ( + !disabled && setIsSelected(!isSelected)} + $isSelected={isSelected} + disabled={disabled} + {...restProps} + > + {disabled && ( + + + + )} + + + + {title} + + + ); +}; diff --git a/packages/ui/src/components/molecules/index.ts b/packages/ui/src/components/molecules/index.ts index 58f592d34..7befff0a8 100644 --- a/packages/ui/src/components/molecules/index.ts +++ b/packages/ui/src/components/molecules/index.ts @@ -40,6 +40,7 @@ export * from './Topbar'; export * from './UpdateBar'; export * from './WallectConnect'; export * from './WalletDialogMainContainer'; +export * from './ManyInMany'; export * from './Reminder'; export * from './Accordion'; export * from './SimpleJsonView'; diff --git a/packages/ui/src/stories/molecules/ManyInMany.stories.tsx b/packages/ui/src/stories/molecules/ManyInMany.stories.tsx new file mode 100644 index 000000000..6eb2f7379 --- /dev/null +++ b/packages/ui/src/stories/molecules/ManyInMany.stories.tsx @@ -0,0 +1,26 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { ManyInMany } from '../../components'; + +const meta: Meta = { + component: ManyInMany, + tags: ['autodocs'], +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + title: 'DDDDDDDDDDDD', + disabled: false, + }, +}; + +export const Disabled: Story = { + args: { + title: 'DDDDDDDDDDDD', + disabled: true, + }, +}; From 91f772f020cd07f9a678a7e81592055b585d8228 Mon Sep 17 00:00:00 2001 From: Md Irshad Ansari Date: Sat, 20 Jul 2024 16:53:23 +0530 Subject: [PATCH 05/96] refactor: using typography in many in many component --- packages/ui/src/components/molecules/ManyInMany.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/ui/src/components/molecules/ManyInMany.tsx b/packages/ui/src/components/molecules/ManyInMany.tsx index 8cac95fff..46030dc7b 100644 --- a/packages/ui/src/components/molecules/ManyInMany.tsx +++ b/packages/ui/src/components/molecules/ManyInMany.tsx @@ -9,7 +9,7 @@ import { MimHoverWalletIcon, } from '../../assets'; import { useTheme } from '../../themes'; -import { Flex } from '../atoms'; +import { Flex, Typography } from '../atoms'; import { WidthProps, width } from '../utils'; const getBoxShadow = (params: { @@ -38,8 +38,7 @@ const getBackground = (params: { const StyledMimDefaultWalletIcon = styled(MimDefaultWalletIcon)``; const StyledMimHoverWalletIcon = styled(MimHoverWalletIcon)``; -const StyledDateLabel = styled.div<{ $isSelected: boolean }>` - font-family: Poppins; +const StyledDateLabel = styled(Typography)<{ $isSelected: boolean }>` font-size: 14px; font-weight: 400; line-height: 21px; From 83e1343e8e09b1b8312cdd64c5aa075bde43bb11 Mon Sep 17 00:00:00 2001 From: Irshad Ansari <40335357+irshadjsr21@users.noreply.github.com> Date: Sat, 20 Jul 2024 17:22:19 +0530 Subject: [PATCH 06/96] feat: added silver button variant (#504) --- packages/ui/src/components/atoms/Button.tsx | 9 +++- packages/ui/src/components/utils/Gradient.tsx | 49 +++++++++++++++++++ .../ui/src/stories/atoms/Button.stories.tsx | 7 +++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/packages/ui/src/components/atoms/Button.tsx b/packages/ui/src/components/atoms/Button.tsx index 60b44f080..f9cda5b86 100644 --- a/packages/ui/src/components/atoms/Button.tsx +++ b/packages/ui/src/components/atoms/Button.tsx @@ -5,18 +5,21 @@ import { Throbber } from './Throbber'; import { SvgProps } from '../../assets'; import { svgGradients } from '../GlobalStyles'; -import { UtilsProps, goldenGradient, utils } from '../utils'; +import { UtilsProps, goldenGradient, silverGradient, utils } from '../utils'; type ButtonVariant = | 'primary' | 'secondary' | 'secondaryLight' + | 'silver' | 'warning' | 'danger' | 'text' | 'icon' | 'none'; + type ButtonSize = 'lg' | 'md' | 'sm'; + export interface ButtonProps extends UtilsProps, React.ButtonHTMLAttributes { @@ -151,6 +154,10 @@ const buttonVariantCssMap: Record> = { border: none; padding: 0; `, + silver: css` + ${silverGradient('background')}; + border: 1px solid transparent; + `, }; const buttonStyle = css` diff --git a/packages/ui/src/components/utils/Gradient.tsx b/packages/ui/src/components/utils/Gradient.tsx index e9a93d619..4d1e11817 100644 --- a/packages/ui/src/components/utils/Gradient.tsx +++ b/packages/ui/src/components/utils/Gradient.tsx @@ -51,3 +51,52 @@ export const goldenGradient = (cssProperty: 'background' | 'color') => { return null; } }; + +export const silverGradient = (cssProperty: 'background' | 'color') => { + switch (cssProperty) { + case 'background': + return css` + @property --a { + syntax: ''; + inherits: false; + initial-value: 90deg; + } + transition: --a ${buttonAnimationData.duration} + ${buttonAnimationData.curve}; + ${cssProperty}: linear-gradient( + var(--a), + #A2ADB3 1.67%, + #F3F1F2 35.99%, + #BCC3C9 66.2%, + #DCDFE4 100% + ); + &:hover { + --a: 180deg; + } + `; + case 'color': + return css` + @property --a { + syntax: ''; + inherits: false; + initial-value: 90deg; + } + transition: --a ${buttonAnimationData.duration} + ${buttonAnimationData.curve}; + background: linear-gradient( + var(--a), + #a2adb3 1.67%, + #f3f1f2 35.99%, + #bcc3c9 66.2%, + #dcdfe4 100% + ); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + &:hover { + --a: 180deg; + } + `; + default: + return null; + } +}; diff --git a/packages/ui/src/stories/atoms/Button.stories.tsx b/packages/ui/src/stories/atoms/Button.stories.tsx index 4c06daa82..1ca153323 100644 --- a/packages/ui/src/stories/atoms/Button.stories.tsx +++ b/packages/ui/src/stories/atoms/Button.stories.tsx @@ -27,6 +27,13 @@ export const Secondary: Story = { }, }; +export const Silver: Story = { + args: { + ...Primary.args, + variant: 'silver', + }, +}; + export const Warning: Story = { args: { ...Primary.args, From 73ec11af040cab962b34cf8981f90d7237700443 Mon Sep 17 00:00:00 2001 From: "moayaan.eth" Date: Mon, 22 Jul 2024 17:36:48 +0530 Subject: [PATCH 07/96] feat: OTP input component ui (#489) * feat(inheritance): otp verification ui * chore: changeset for feature/inheritance-otpSection * feat(inheritance): otp verification ui PR fixes * feat(inheritance): ui fixes for otpSection * feat(inheritance): removed changeset * feat(inheritance): fix PR changes and removed eslint disable * feat(inheritance): removed pnpm-lock * feat(inheritance): fix PR review changes * chore: lint and pretty changes * refactor: otp input component ui --------- Co-authored-by: Md Irshad Ansari --- packages/ui/.storybook/main.ts | 1 - packages/ui/package.json | 4 +- .../ui/src/components/atoms/LangDisplay.tsx | 40 +- .../ui/src/components/molecules/Markdown.tsx | 9 +- .../ui/src/components/molecules/OTPInput.tsx | 195 +++++++++ packages/ui/src/components/molecules/index.ts | 1 + .../stories/molecules/OTPInput.stories.tsx | 86 ++++ packages/ui/src/themes/color.styled.ts | 1 + pnpm-lock.yaml | 378 +++++++++++++----- 9 files changed, 598 insertions(+), 117 deletions(-) create mode 100644 packages/ui/src/components/molecules/OTPInput.tsx create mode 100644 packages/ui/src/stories/molecules/OTPInput.stories.tsx diff --git a/packages/ui/.storybook/main.ts b/packages/ui/.storybook/main.ts index bce3d2746..422a5d126 100644 --- a/packages/ui/.storybook/main.ts +++ b/packages/ui/.storybook/main.ts @@ -15,7 +15,6 @@ const config: StorybookConfig = { getAbsolutePath('@storybook/addon-links'), getAbsolutePath('@storybook/addon-docs'), getAbsolutePath('@storybook/addon-essentials'), - getAbsolutePath('@storybook/addon-onboarding'), getAbsolutePath('@storybook/addon-interactions'), getAbsolutePath('@storybook/addon-styling'), ], diff --git a/packages/ui/package.json b/packages/ui/package.json index 36e82e902..6f9d57380 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -34,7 +34,6 @@ "@storybook/addon-essentials": "7.2.0", "@storybook/addon-interactions": "7.2.0", "@storybook/addon-links": "7.2.0", - "@storybook/addon-onboarding": "1.0.8", "@storybook/addon-styling": "^1.3.5", "@storybook/blocks": "7.2.0", "@storybook/jest": "^0.1.0", @@ -75,12 +74,13 @@ "chart.js": "^4.3.2", "date-fns": "^2.30.0", "focus-trap-react": "^10.2.3", - "marked-react": "^2.0.0", + "marked": "^13.0.2", "rc-tooltip": "^6.1.2", "react": "^18.2.0", "react-dom": "^18.2.0", "react-json-view": "^1.21.3", "react-multi-date-picker": "^4.1.2", + "react-otp-input": "^3.1.1", "react-tsparticles": "^2.10.1", "react-virtualized": "^9.22.5", "styled-components": "6.0.0-rc.1", diff --git a/packages/ui/src/components/atoms/LangDisplay.tsx b/packages/ui/src/components/atoms/LangDisplay.tsx index 2ffbbb4be..53ffdb6f8 100644 --- a/packages/ui/src/components/atoms/LangDisplay.tsx +++ b/packages/ui/src/components/atoms/LangDisplay.tsx @@ -1,27 +1,55 @@ +import { Marked } from 'marked'; import React from 'react'; export interface LangDisplayProps { text: string; variables?: object; $noPreWrap?: boolean; + $allowMarkdown?: boolean; } +const marked = new Marked({ + tokenizer: { + url: () => undefined, + }, + gfm: false, +}); + export const parseLangTemplate = (templateStr: string, variables = {}) => - templateStr.replace(/\${(.*?)}/g, (x, g) => (variables as any)[g] ?? ''); + templateStr.replace(/\${(.*?)}/g, (_, g) => (variables as any)[g] ?? ''); const BaseLangDisplay: React.FC = ({ text, variables, $noPreWrap, -}) => ( - - {parseLangTemplate(text, variables)} - -); + $allowMarkdown, +}) => { + let parsedText = parseLangTemplate(text, variables); + + if ($allowMarkdown) { + parsedText = marked.parseInline(parsedText) as string; + } + + return ( + + {$allowMarkdown ? undefined : parsedText} + + ); +}; BaseLangDisplay.defaultProps = { variables: undefined, $noPreWrap: undefined, + $allowMarkdown: undefined, }; export const LangDisplay = React.memo(BaseLangDisplay); diff --git a/packages/ui/src/components/molecules/Markdown.tsx b/packages/ui/src/components/molecules/Markdown.tsx index 4ac601477..017027a46 100644 --- a/packages/ui/src/components/molecules/Markdown.tsx +++ b/packages/ui/src/components/molecules/Markdown.tsx @@ -1,4 +1,4 @@ -import MarkedReact from 'marked-react'; +import { Marked } from 'marked'; import React from 'react'; import styled from 'styled-components'; @@ -33,12 +33,17 @@ const StyledContainer = styled.div` } `; +const marked = new Marked({ + async: false, + gfm: false, +}); + export interface MarkdownProps { children: string; } export const Markdown: React.FC = ({ children }) => ( - {children} +

); diff --git a/packages/ui/src/components/molecules/OTPInput.tsx b/packages/ui/src/components/molecules/OTPInput.tsx new file mode 100644 index 000000000..02fc9f74a --- /dev/null +++ b/packages/ui/src/components/molecules/OTPInput.tsx @@ -0,0 +1,195 @@ +import React from 'react'; +import OtpInput from 'react-otp-input'; +import styled from 'styled-components'; + +import { Button, Container, Flex, LangDisplay, Typography } from '../atoms'; + +export type OTPInputStatus = 'idle' | 'success' | 'error' | 'retryExceeded'; + +export interface OTPInputProps { + title: string; + status: OTPInputStatus; + otpLength: number; + isActionDisbaled?: boolean; + disabled?: boolean; + subText?: string; + errorSubText?: string; + infoText?: string[]; + textVariables?: Record; + value: string; + onChange: (value: string) => void; + onAction: () => void; +} + +const getBorderColor = (theme: any, status: OTPInputStatus) => { + const borderColorMap: Record = { + success: theme.palette.success.main, + error: theme.palette.text.error, + idle: theme.palette.border.popup, + retryExceeded: theme.palette.border.popup, + }; + + return borderColorMap[status]; +}; + +const OTPSingleInputContainer = styled.input<{ + $status: OTPInputStatus; +}>` + width: 56px !important; + height: 72px; + text-align: center; + font-size: 24px; + border: 1px solid ${({ theme, $status }) => getBorderColor(theme, $status)}; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + padding: 12px 0px; + + background-color: ${({ theme }) => + theme.palette.background.separatorSecondary}; + color: ${({ theme }) => theme.palette.text.white}; + + &:focus-within { + outline: none; + border-color: ${({ theme, $status }) => getBorderColor(theme, $status)}; + } + + -moz-appearance: textfield; + &::-webkit-inner-spin-button, + &::-webkit-outer-spin-button { + -webkit-appearance: none; + } +`; + +const ActionButton = styled(Button)` + color: ${({ theme }) => theme.palette.text.white}; + font-weight: 400; + + &:disabled { + color: ${({ theme }) => theme.palette.background.separator}; + } + + font-size: 14px; +`; + +export const OTPInput: React.FC = ({ + status, + title, + infoText, + textVariables, + errorSubText, + otpLength, + value, + onChange, + subText, + isActionDisbaled, + disabled, + onAction, +}) => { + const isRetryExceeded = status === 'retryExceeded'; + + return ( + + + {title} + + + {!isRetryExceeded && ( + <> + + } + renderInput={props => ( + + )} + /> + + + + + + {subText} + + + + + Resend OTP + + + + {infoText && infoText.length > 0 && ( + + {infoText.map(text => ( + + + + ))} + + )} + + )} + + {isRetryExceeded && errorSubText && ( + + + + + + )} + + ); +}; + +OTPInput.defaultProps = { + disabled: undefined, + isActionDisbaled: undefined, + textVariables: undefined, + subText: undefined, + infoText: undefined, + errorSubText: undefined, +}; diff --git a/packages/ui/src/components/molecules/index.ts b/packages/ui/src/components/molecules/index.ts index 7befff0a8..de144e378 100644 --- a/packages/ui/src/components/molecules/index.ts +++ b/packages/ui/src/components/molecules/index.ts @@ -44,4 +44,5 @@ export * from './ManyInMany'; export * from './Reminder'; export * from './Accordion'; export * from './SimpleJsonView'; +export * from './OTPInput'; export * from './OneInMany'; diff --git a/packages/ui/src/stories/molecules/OTPInput.stories.tsx b/packages/ui/src/stories/molecules/OTPInput.stories.tsx new file mode 100644 index 000000000..fbcc7942e --- /dev/null +++ b/packages/ui/src/stories/molecules/OTPInput.stories.tsx @@ -0,0 +1,86 @@ +/* eslint-disable no-template-curly-in-string */ +import type { Meta, StoryObj } from '@storybook/react'; +import React from 'react'; + +import { OTPInput } from '../../components/molecules'; + +const meta: Meta = { + component: OTPInput, + render: args => { + const [value, onChange] = React.useState(''); + + return ; + }, + tags: ['autodocs'], +}; + +export default meta; + +type Story = StoryObj; + +export const Idle: Story = { + args: { + title: 'Enter OTP', + status: 'idle', + otpLength: 6, + subText: '4 tries remaining 25..', + infoText: [ + 'An email has been sent to **${email}**', + 'Please check your email for the OTP.', + ], + textVariables: { + email: 'john@gmail.com', + }, + }, +}; + +export const Error: Story = { + args: { + title: 'Wrong OTP', + status: 'error', + otpLength: 6, + subText: '3 tries remaining 25..', + infoText: [ + 'An email has been sent to **${email}**', + 'Please check your email for the OTP.', + ], + textVariables: { + email: 'john@gmail.com', + }, + }, +}; + +export const Success: Story = { + args: { + title: 'Please wait while we redirect you...', + status: 'success', + otpLength: 6, + disabled: true, + subText: '3 tries remaining 25..', + infoText: [ + 'An email has been sent to **${email}**', + 'Please check your email for the OTP.', + ], + textVariables: { + email: 'john@gmail.com', + }, + }, +}; + +export const RetryExceeded: Story = { + args: { + title: 'You have 0 retries remaining', + status: 'retryExceeded', + errorSubText: 'Please exit the flow and restart', + otpLength: 6, + disabled: true, + subText: '3 tries remaining 25..', + infoText: [ + 'An email has been sent to **${email}**', + 'Please check your email for the OTP.', + ], + textVariables: { + email: 'john@gmail.com', + }, + }, +}; diff --git a/packages/ui/src/themes/color.styled.ts b/packages/ui/src/themes/color.styled.ts index be99a362f..260c2c861 100644 --- a/packages/ui/src/themes/color.styled.ts +++ b/packages/ui/src/themes/color.styled.ts @@ -56,6 +56,7 @@ export const colors = { normal: '#CCC4BE', divider: '#333130', dialog: '#2B2420', + separator: '#39322C', }, boxShadow: { selected: '#1B1813', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dfc4901b4..4b45ac51e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2060,9 +2060,9 @@ importers: focus-trap-react: specifier: ^10.2.3 version: 10.2.3(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0) - marked-react: - specifier: ^2.0.0 - version: 2.0.0(react@18.2.0) + marked: + specifier: ^13.0.2 + version: 13.0.2 rc-tooltip: specifier: ^6.1.2 version: 6.1.2(react-dom@18.2.0)(react@18.2.0) @@ -2078,6 +2078,9 @@ importers: react-multi-date-picker: specifier: ^4.1.2 version: 4.1.2(react-dom@18.2.0)(react@18.2.0) + react-otp-input: + specifier: ^3.1.1 + version: 3.1.1(react-dom@18.2.0)(react@18.2.0) react-tsparticles: specifier: ^2.10.1 version: 2.10.1(react@18.2.0) @@ -2118,9 +2121,6 @@ importers: '@storybook/addon-links': specifier: 7.2.0 version: 7.2.0(react-dom@18.2.0)(react@18.2.0) - '@storybook/addon-onboarding': - specifier: 1.0.8 - version: 1.0.8(react-dom@18.2.0)(react@18.2.0) '@storybook/addon-styling': specifier: ^1.3.5 version: 1.3.5(@types/react-dom@18.2.6)(@types/react@18.2.13)(less@4.2.0)(postcss@8.4.38)(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5)(webpack@5.92.1) @@ -2186,7 +2186,7 @@ importers: version: 3.0.0 jest: specifier: ^29.5.0 - version: 29.5.0(@types/node@18.15.11)(ts-node@10.9.1) + version: 29.5.0(@types/node@18.15.11) jest-environment-jsdom: specifier: ^29.5.0 version: 29.5.0 @@ -2476,7 +2476,7 @@ packages: '@babel/traverse': 7.24.7 '@babel/types': 7.24.7 convert-source-map: 1.9.0 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -2498,7 +2498,7 @@ packages: '@babel/traverse': 7.24.7 '@babel/types': 7.24.7 convert-source-map: 2.0.0 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -2621,7 +2621,7 @@ packages: '@babel/core': 7.22.5 '@babel/helper-compilation-targets': 7.24.7 '@babel/helper-plugin-utils': 7.24.7 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 lodash.debounce: 4.0.8 resolve: 1.22.8 transitivePeerDependencies: @@ -2636,7 +2636,7 @@ packages: '@babel/core': 7.24.7 '@babel/helper-compilation-targets': 7.24.7 '@babel/helper-plugin-utils': 7.24.7 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 lodash.debounce: 4.0.8 resolve: 1.22.8 transitivePeerDependencies: @@ -5007,7 +5007,7 @@ packages: '@babel/helper-split-export-declaration': 7.24.7 '@babel/parser': 7.24.7 '@babel/types': 7.24.7 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -5241,6 +5241,7 @@ packages: /@colors/colors@1.5.0: resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} + requiresBuild: true /@colors/colors@1.6.0: resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -6023,7 +6024,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 espree: 9.6.1 globals: 13.24.0 ignore: 5.3.1 @@ -6251,7 +6252,7 @@ packages: deprecated: Use @eslint/config-array instead dependencies: '@humanwhocodes/object-schema': 2.0.3 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -6451,6 +6452,49 @@ packages: slash: 3.0.0 dev: true + /@jest/core@29.7.0: + resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 16.18.36 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@16.18.36) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.7 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + dev: true + /@jest/core@29.7.0(ts-node@10.9.1): resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -9733,21 +9777,6 @@ packages: - '@types/react-dom' dev: true - /@storybook/addon-onboarding@1.0.8(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-3WwCocUuTibHWO89uzAbeONEn6x9NkmxA0mOU4rYPhEsMQ4NDNDD1GXcievZmpwt5VviFgw5FRx8+BxPn54YmQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - '@storybook/telemetry': 7.6.20 - react: 18.2.0 - react-confetti: 6.1.0(react@18.2.0) - react-dom: 18.2.0(react@18.2.0) - transitivePeerDependencies: - - encoding - - supports-color - dev: true - /@storybook/addon-outline@7.2.0(@types/react-dom@18.2.6)(@types/react@18.2.13)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-pP9Umngd8OsmVYAhRQqY4HGgTeQK63tZeNDOSdB5Jab4f6wDrdHUn6OMo0Hsfzw65g7mSQmEexnxbq1DrgKU1Q==} peerDependencies: @@ -10409,7 +10438,7 @@ packages: util: 0.12.5 util-deprecate: 1.0.2 watchpack: 2.4.1 - ws: 8.17.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) + ws: 8.17.1 transitivePeerDependencies: - bufferutil - encoding @@ -10469,22 +10498,6 @@ packages: - supports-color dev: false - /@storybook/csf-tools@7.6.20: - resolution: {integrity: sha512-rwcwzCsAYh/m/WYcxBiEtLpIW5OH1ingxNdF/rK9mtGWhJxXRDV8acPkFrF8rtFWIVKoOCXu5USJYmc3f2gdYQ==} - dependencies: - '@babel/generator': 7.24.7 - '@babel/parser': 7.24.7 - '@babel/traverse': 7.24.7 - '@babel/types': 7.24.7 - '@storybook/csf': 0.1.9 - '@storybook/types': 7.6.20 - fs-extra: 11.2.0 - recast: 0.23.9 - ts-dedent: 2.2.0 - transitivePeerDependencies: - - supports-color - dev: true - /@storybook/csf@0.0.1: resolution: {integrity: sha512-USTLkZze5gkel8MYCujSRBVIrUQ3YPBrLOx7GNk/0wttvVtlzWXAq9eLbQ4p/NicGxP+3T7KPEMVV//g+yubpw==} dependencies: @@ -10908,22 +10921,6 @@ packages: - supports-color dev: true - /@storybook/telemetry@7.6.20: - resolution: {integrity: sha512-dmAOCWmOscYN6aMbhCMmszQjoycg7tUPRVy2kTaWg6qX10wtMrvEtBV29W4eMvqdsoRj5kcvoNbzRdYcWBUOHQ==} - dependencies: - '@storybook/client-logger': 7.6.20 - '@storybook/core-common': 7.6.20 - '@storybook/csf-tools': 7.6.20 - chalk: 4.1.2 - detect-package-manager: 2.0.1 - fetch-retry: 5.0.6 - fs-extra: 11.2.0 - read-pkg-up: 7.0.1 - transitivePeerDependencies: - - encoding - - supports-color - dev: true - /@storybook/testing-library@0.2.0: resolution: {integrity: sha512-Ff6jNnrsosmDshgCf0Eb5Cz7IA34p/1Ps5N3Kp3598kfXpBSccSkQQvVFUXC3kIHw/isIXWPqntZuKqnWUz7Gw==} dependencies: @@ -11850,6 +11847,7 @@ packages: /@types/plist@3.0.5: resolution: {integrity: sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==} + requiresBuild: true dependencies: '@types/node': 16.18.36 xmlbuilder: 15.1.1 @@ -11951,6 +11949,7 @@ packages: /@types/verror@1.10.10: resolution: {integrity: sha512-l4MM0Jppn18hb9xmM6wwD1uTdShpf9Pn80aXTStnK1C94gtPvJcV2FrDmbOQUAQfJ1cKZHktkQUDwEqaAKXMMg==} + requiresBuild: true dev: true optional: true @@ -12134,7 +12133,7 @@ packages: dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.3 @@ -12768,6 +12767,7 @@ packages: /@xmldom/xmldom@0.8.10: resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==} engines: {node: '>=10.0.0'} + requiresBuild: true dev: true /@xtuc/ieee754@1.2.0: @@ -12941,7 +12941,7 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} dependencies: - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 transitivePeerDependencies: - supports-color dev: true @@ -13324,6 +13324,7 @@ packages: /assert-plus@1.0.0: resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} engines: {node: '>=0.8'} + requiresBuild: true dev: true optional: true @@ -14944,6 +14945,7 @@ packages: /core-util-is@1.0.2: resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} + requiresBuild: true dev: true optional: true @@ -15057,6 +15059,25 @@ packages: - ts-node dev: true + /create-jest@29.7.0(@types/node@18.15.11): + resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@18.15.11) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + /create-jest@29.7.0(@types/node@18.15.11)(ts-node@10.9.1): resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -15430,6 +15451,17 @@ packages: ms: 2.1.3 dev: true + /debug@4.3.5: + resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + /debug@4.3.5(supports-color@8.1.1): resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} engines: {node: '>=6.0'} @@ -15761,7 +15793,7 @@ packages: hasBin: true dependencies: address: 1.2.2 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 transitivePeerDependencies: - supports-color dev: true @@ -16426,7 +16458,7 @@ packages: peerDependencies: esbuild: '>=0.12 <1' dependencies: - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 esbuild: 0.18.20 transitivePeerDependencies: - supports-color @@ -16783,7 +16815,7 @@ packages: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -17121,6 +17153,7 @@ packages: /extsprintf@1.4.1: resolution: {integrity: sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==} engines: {'0': node >=0.6.0} + requiresBuild: true dev: true optional: true @@ -18326,7 +18359,7 @@ packages: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 transitivePeerDependencies: - supports-color dev: true @@ -18363,7 +18396,7 @@ packages: engines: {node: '>= 6.0.0'} dependencies: agent-base: 5.1.1 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 transitivePeerDependencies: - supports-color dev: true @@ -18373,7 +18406,7 @@ packages: engines: {node: '>= 6'} dependencies: agent-base: 6.0.2 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 transitivePeerDependencies: - supports-color dev: true @@ -18425,6 +18458,7 @@ packages: resolution: {integrity: sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==} engines: {node: ^8.11.2 || >=10} os: [darwin] + requiresBuild: true dependencies: cli-truncate: 2.1.0 node-addon-api: 1.7.2 @@ -19185,7 +19219,7 @@ packages: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} engines: {node: '>=10'} dependencies: - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: @@ -19322,6 +19356,34 @@ packages: - ts-node dev: true + /jest-cli@29.7.0(@types/node@18.15.11): + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@18.15.11) + exit: 0.1.2 + import-local: 3.1.0 + jest-config: 29.7.0(@types/node@18.15.11) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + /jest-cli@29.7.0(@types/node@18.15.11)(ts-node@10.9.1): resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -19378,6 +19440,46 @@ packages: - ts-node dev: true + /jest-config@29.7.0(@types/node@16.18.36): + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + dependencies: + '@babel/core': 7.22.5 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 16.18.36 + babel-jest: 29.7.0(@babel/core@7.22.5) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.7 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + dev: true + /jest-config@29.7.0(@types/node@16.18.36)(ts-node@10.9.1): resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -19413,7 +19515,47 @@ packages: pretty-format: 29.7.0 slash: 3.0.0 strip-json-comments: 3.1.1 - ts-node: 10.9.1(@types/node@18.15.11)(typescript@4.9.5) + ts-node: 10.9.1(@types/node@20.5.1)(typescript@4.9.5) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + dev: true + + /jest-config@29.7.0(@types/node@18.15.11): + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + dependencies: + '@babel/core': 7.22.5 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.15.11 + babel-jest: 29.7.0(@babel/core@7.22.5) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.7 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -19454,7 +19596,7 @@ packages: pretty-format: 29.7.0 slash: 3.0.0 strip-json-comments: 3.1.1 - ts-node: 10.9.1(@types/node@18.15.11)(typescript@4.9.5) + ts-node: 10.9.1(@types/node@20.5.1)(typescript@4.9.5) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -19831,7 +19973,7 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 16.18.36 + '@types/node': 18.15.11 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true @@ -19866,6 +20008,27 @@ packages: - ts-node dev: true + /jest@29.5.0(@types/node@18.15.11): + resolution: {integrity: sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.7.0 + '@jest/types': 29.6.3 + import-local: 3.1.0 + jest-cli: 29.7.0(@types/node@18.15.11) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + /jest@29.5.0(@types/node@18.15.11)(ts-node@10.9.1): resolution: {integrity: sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -20010,7 +20173,7 @@ packages: whatwg-encoding: 2.0.0 whatwg-mimetype: 3.0.0 whatwg-url: 11.0.0 - ws: 8.17.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) + ws: 8.17.1 xml-name-validator: 4.0.0 transitivePeerDependencies: - bufferutil @@ -20399,7 +20562,7 @@ packages: chalk: 5.2.0 cli-truncate: 3.1.0 commander: 10.0.1 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 execa: 7.2.0 lilconfig: 2.1.0 listr2: 5.0.8 @@ -20655,7 +20818,7 @@ packages: engines: {node: '>=8.0'} dependencies: date-format: 4.0.14 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 flatted: 3.3.1 rfdc: 1.4.1 streamroller: 3.1.5 @@ -20889,18 +21052,9 @@ packages: dependencies: react: 18.2.0 - /marked-react@2.0.0(react@18.2.0): - resolution: {integrity: sha512-Mp5HqfONf/RDqFtA+6xw2EjKkSbA8/xNPwyJ8ewLy/q3v21lRsPA7h+HUndVAW/yEIoebvcyzzSDpbjzL/xjZg==} - peerDependencies: - react: ^16.8.0 || >=17.0.0 - dependencies: - marked: 6.0.0 - react: 18.2.0 - dev: false - - /marked@6.0.0: - resolution: {integrity: sha512-7E3m/xIlymrFL5gWswIT4CheIE3fDeh51NV09M4x8iOc7NDYlyERcQMLAIHcSlrvwliwbPQ4OGD+MpPSYiQcqw==} - engines: {node: '>= 16'} + /marked@13.0.2: + resolution: {integrity: sha512-J6CPjP8pS5sgrRqxVRvkCIkZ6MFdRIjDkwUwgJ9nL2fbmM6qGQeB2C16hi8Cc9BOzj6xXzy0jyi0iPIfnMHYzA==} + engines: {node: '>= 18'} hasBin: true dev: false @@ -21530,6 +21684,7 @@ packages: /node-addon-api@1.7.2: resolution: {integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==} + requiresBuild: true dev: true optional: true @@ -23116,7 +23271,7 @@ packages: engines: {node: '>=8.16.0'} dependencies: '@types/mime-types': 2.1.4 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 extract-zip: 1.7.0 https-proxy-agent: 4.0.0 mime: 2.6.0 @@ -23344,16 +23499,6 @@ packages: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) - /react-confetti@6.1.0(react@18.2.0): - resolution: {integrity: sha512-7Ypx4vz0+g8ECVxr88W9zhcQpbeujJAVqL14ZnXJ3I23mOI9/oBVTQ3dkJhUmB0D6XOtCZEM6N0Gm9PMngkORw==} - engines: {node: '>=10.18'} - peerDependencies: - react: ^16.3.0 || ^17.0.1 || ^18.0.0 - dependencies: - react: 18.2.0 - tween-functions: 1.2.0 - dev: true - /react-date-object@2.1.8: resolution: {integrity: sha512-94zg/9r29v/VsKsJT0E5+tPg85IyMirkU/B7qBWlhT7RDSuf9x5dczfbQOsj9TbzSVUK/Vx/ZDboOBy9hA381w==} dev: false @@ -23480,6 +23625,16 @@ packages: react-element-popper: 2.1.6(react-dom@18.2.0)(react@18.2.0) dev: false + /react-otp-input@3.1.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-bjPavgJ0/Zmf/AYi4onj8FbH93IjeD+e8pWwxIJreDEWsU1ILR5fs8jEJmMGWSBe/yyvPP6X/W6Mk9UkOCkTPw==} + peerDependencies: + react: '>=16.8.6 || ^17.0.0 || ^18.0.0' + react-dom: '>=16.8.6 || ^17.0.0 || ^18.0.0' + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /react-player@2.14.1(react@18.2.0): resolution: {integrity: sha512-jILj7F9o+6NHzrJ1GqZIxfJgskvGmKeJ05FNhPvgiCpvMZFmFneKEkukywHcULDO2lqITm+zcEkLSq42mX0FbA==} peerDependencies: @@ -24932,7 +25087,7 @@ packages: engines: {node: '>=8.0'} dependencies: date-format: 4.0.14 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.5 fs-extra: 8.1.0 transitivePeerDependencies: - supports-color @@ -25690,7 +25845,7 @@ packages: bs-logger: 0.2.6 esbuild: 0.18.20 fast-json-stable-stringify: 2.1.0 - jest: 29.5.0(@types/node@18.15.11)(ts-node@10.9.1) + jest: 29.5.0(@types/node@18.15.11) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -26252,10 +26407,6 @@ packages: turbo-windows-arm64: 1.10.6 dev: true - /tween-functions@1.2.0: - resolution: {integrity: sha512-PZBtLYcCLtEcjL14Fzb1gSxPBeL7nWvGhO5ZFPGqziCcr8uvHp0NDmdjBchp6KHL+tExcg0m3NISmKxhU394dA==} - dev: true - /tweetnacl@1.0.3: resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} @@ -26915,6 +27066,7 @@ packages: /verror@1.10.1: resolution: {integrity: sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==} engines: {node: '>=0.6.0'} + requiresBuild: true dependencies: assert-plus: 1.0.0 core-util-is: 1.0.2 @@ -27611,6 +27763,19 @@ packages: utf-8-validate: optional: true + /ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: true + /ws@8.17.1(bufferutil@4.0.8)(utf-8-validate@5.0.10): resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} engines: {node: '>=10.0.0'} @@ -27664,6 +27829,7 @@ packages: /xmlbuilder@15.1.1: resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==} engines: {node: '>=8.0'} + requiresBuild: true dev: true /xmlchars@2.2.0: From 4ebe8a1c6b276d023c13ffb45150afd2c8bb43b1 Mon Sep 17 00:00:00 2001 From: Md Irshad Ansari Date: Mon, 22 Jul 2024 22:42:45 +0530 Subject: [PATCH 08/96] refactor: otp input component props --- .../ui/src/components/molecules/OTPInput.tsx | 21 ++++++++++++------- .../stories/molecules/OTPInput.stories.tsx | 16 ++++++++++++++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/packages/ui/src/components/molecules/OTPInput.tsx b/packages/ui/src/components/molecules/OTPInput.tsx index 02fc9f74a..69487b793 100644 --- a/packages/ui/src/components/molecules/OTPInput.tsx +++ b/packages/ui/src/components/molecules/OTPInput.tsx @@ -10,6 +10,7 @@ export interface OTPInputProps { title: string; status: OTPInputStatus; otpLength: number; + actionText?: string; isActionDisbaled?: boolean; disabled?: boolean; subText?: string; @@ -84,6 +85,7 @@ export const OTPInput: React.FC = ({ onChange, subText, isActionDisbaled, + actionText, disabled, onAction, }) => { @@ -139,14 +141,16 @@ export const OTPInput: React.FC = ({ - - Resend OTP - + {actionText && ( + + {actionText} + + )} {infoText && infoText.length > 0 && ( @@ -192,4 +196,5 @@ OTPInput.defaultProps = { subText: undefined, infoText: undefined, errorSubText: undefined, + actionText: undefined, }; diff --git a/packages/ui/src/stories/molecules/OTPInput.stories.tsx b/packages/ui/src/stories/molecules/OTPInput.stories.tsx index fbcc7942e..e9981291a 100644 --- a/packages/ui/src/stories/molecules/OTPInput.stories.tsx +++ b/packages/ui/src/stories/molecules/OTPInput.stories.tsx @@ -23,6 +23,7 @@ export const Idle: Story = { title: 'Enter OTP', status: 'idle', otpLength: 6, + actionText: 'Resend OTP', subText: '4 tries remaining 25..', infoText: [ 'An email has been sent to **${email}**', @@ -31,6 +32,9 @@ export const Idle: Story = { textVariables: { email: 'john@gmail.com', }, + onAction: () => { + console.log('Action'); + }, }, }; @@ -39,6 +43,7 @@ export const Error: Story = { title: 'Wrong OTP', status: 'error', otpLength: 6, + actionText: 'Resend OTP', subText: '3 tries remaining 25..', infoText: [ 'An email has been sent to **${email}**', @@ -47,6 +52,9 @@ export const Error: Story = { textVariables: { email: 'john@gmail.com', }, + onAction: () => { + console.log('Action'); + }, }, }; @@ -55,6 +63,7 @@ export const Success: Story = { title: 'Please wait while we redirect you...', status: 'success', otpLength: 6, + actionText: 'Resend OTP', disabled: true, subText: '3 tries remaining 25..', infoText: [ @@ -64,6 +73,9 @@ export const Success: Story = { textVariables: { email: 'john@gmail.com', }, + onAction: () => { + console.log('Action'); + }, }, }; @@ -73,6 +85,7 @@ export const RetryExceeded: Story = { status: 'retryExceeded', errorSubText: 'Please exit the flow and restart', otpLength: 6, + actionText: 'Resend OTP', disabled: true, subText: '3 tries remaining 25..', infoText: [ @@ -82,5 +95,8 @@ export const RetryExceeded: Story = { textVariables: { email: 'john@gmail.com', }, + onAction: () => { + console.log('Action'); + }, }, }; From 3dc16063957c4cf50f659fc7201fc3457a9c1167 Mon Sep 17 00:00:00 2001 From: "moayaan.eth" Date: Mon, 22 Jul 2024 22:43:27 +0530 Subject: [PATCH 09/96] feat: inheritance nominee message component ui (#499) * feat(inheritance): added ui for nominee message * chore: linting changes * feat(inheritance): nominee messages PR fix * feat(inheritance): fix PR review changes * feat(inheritance): fix PR review changes * chore: lint and pretty changes * refactor: nominee message component ui --------- Co-authored-by: Md Irshad Ansari --- packages/ui/icons/encrypted-message-icon.svg | 12 +++++ packages/ui/icons/message-icon.svg | 14 ++++++ .../src/components/atoms/NomineeMessage.tsx | 49 +++++++++++++++++++ packages/ui/src/components/atoms/index.ts | 1 + .../ui/src/components/utils/bgColor.styled.ts | 7 +++ .../stories/atoms/NomineeMessage.stories.tsx | 32 ++++++++++++ packages/ui/src/themes/color.styled.ts | 1 + packages/ui/src/themes/theme.styled.ts | 1 + 8 files changed, 117 insertions(+) create mode 100644 packages/ui/icons/encrypted-message-icon.svg create mode 100644 packages/ui/icons/message-icon.svg create mode 100644 packages/ui/src/components/atoms/NomineeMessage.tsx create mode 100644 packages/ui/src/stories/atoms/NomineeMessage.stories.tsx diff --git a/packages/ui/icons/encrypted-message-icon.svg b/packages/ui/icons/encrypted-message-icon.svg new file mode 100644 index 000000000..2de8e6ef6 --- /dev/null +++ b/packages/ui/icons/encrypted-message-icon.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/packages/ui/icons/message-icon.svg b/packages/ui/icons/message-icon.svg new file mode 100644 index 000000000..393ba4c22 --- /dev/null +++ b/packages/ui/icons/message-icon.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/packages/ui/src/components/atoms/NomineeMessage.tsx b/packages/ui/src/components/atoms/NomineeMessage.tsx new file mode 100644 index 000000000..c563896e9 --- /dev/null +++ b/packages/ui/src/components/atoms/NomineeMessage.tsx @@ -0,0 +1,49 @@ +import React, { FC } from 'react'; +import styled from 'styled-components'; + +import { Button } from './Button'; +import { Flex } from './Flex'; +import { Typography } from './Typography'; + +import { WidthProps, width } from '../utils'; + +export interface NomineeMessageProps extends WidthProps { + text: string; + icon: React.ReactNode; + actionText: string; + onAction: () => void; +} + +const StyledNomineeMessage = styled.div` + display: flex; + width: 624px; + padding: 8px 16px; + justify-content: space-between; + align-items: center; + background: ${({ theme }) => theme.palette.background.slate}; + border-radius: 8px; + + ${width} +`; + +export const NomineeMessage: FC = ({ + text, + icon, + onAction, + actionText, + ...props +}) => ( + + + {icon} + + {text} + + + + +); diff --git a/packages/ui/src/components/atoms/index.ts b/packages/ui/src/components/atoms/index.ts index 1636f66d9..3ca5b64b0 100644 --- a/packages/ui/src/components/atoms/index.ts +++ b/packages/ui/src/components/atoms/index.ts @@ -33,3 +33,4 @@ export * from './MainAppStyles'; export * from './Tooltip'; export * from './Video'; export * from './AdvanceTextDiaplay'; +export * from './NomineeMessage'; diff --git a/packages/ui/src/components/utils/bgColor.styled.ts b/packages/ui/src/components/utils/bgColor.styled.ts index 0ad0764e3..ab6a16f3f 100644 --- a/packages/ui/src/components/utils/bgColor.styled.ts +++ b/packages/ui/src/components/utils/bgColor.styled.ts @@ -23,6 +23,7 @@ export type BgColor = | 'warning' | 'calendar' | 'container' + | 'slate' | 'error'; export interface BgColorProps { $bgColor?: BgColor; @@ -65,11 +66,17 @@ export const bgColor = css` css` background: ${({ theme }) => theme.palette.background.separatorSecondary}; `} + ${props => + props.$bgColor === 'slate' && + css` + background: ${({ theme }) => theme.palette.background.slate}; + `} ${props => props.$bgColor === 'muted' && css` background: ${({ theme }) => theme.palette.text.muted}; `} + ${props => props.$bgColor === 'golden' && css` diff --git a/packages/ui/src/stories/atoms/NomineeMessage.stories.tsx b/packages/ui/src/stories/atoms/NomineeMessage.stories.tsx new file mode 100644 index 000000000..4922f2cb6 --- /dev/null +++ b/packages/ui/src/stories/atoms/NomineeMessage.stories.tsx @@ -0,0 +1,32 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import React from 'react'; + +import { EncryptedMessageIcon, MessageIcon } from '../../assets'; +import { NomineeMessage } from '../../components'; + +const meta: Meta = { + component: NomineeMessage, + tags: ['autodocs'], +}; + +export default meta; + +type Story = StoryObj; + +export const EncryptedMessage: Story = { + args: { + text: 'Encrypted Message', + icon: , + actionText: 'Edit', + onAction: () => console.log('edit clicked'), + }, +}; + +export const ExecutorMessage: Story = { + args: { + text: 'Executor Message', + icon: , + actionText: 'Edit', + onAction: () => console.log('edit clicked'), + }, +}; diff --git a/packages/ui/src/themes/color.styled.ts b/packages/ui/src/themes/color.styled.ts index 260c2c861..f813437b3 100644 --- a/packages/ui/src/themes/color.styled.ts +++ b/packages/ui/src/themes/color.styled.ts @@ -91,6 +91,7 @@ export const colors = { filterItem: `#1F1C19`, calendar: '#342F2C', calendarHeader: '#211C18', + slate: '#312d2a', cardDisabled: '#282522', cardSelected: '#2A2827', }, diff --git a/packages/ui/src/themes/theme.styled.ts b/packages/ui/src/themes/theme.styled.ts index ea2100991..f9847f960 100644 --- a/packages/ui/src/themes/theme.styled.ts +++ b/packages/ui/src/themes/theme.styled.ts @@ -105,6 +105,7 @@ export const theme = { popup: colors.border.popup, danger: colors.background.danger, error: colors.background.error, + slate: colors.background.slate, cardSelected: colors.background.cardSelected, cardDisabled: colors.background.cardDisabled, }, From 2befa9047fb81315536f01d7d13e78b1fbc8916a Mon Sep 17 00:00:00 2001 From: Irshad Ansari <40335357+irshadjsr21@users.noreply.github.com> Date: Wed, 24 Jul 2024 18:43:28 +0530 Subject: [PATCH 10/96] feat: added inheritance sync plans dialog ui (#508) --- .../src/i18n/lang/ar-AE.json | 31 +- .../src/i18n/lang/de-DE.json | 31 +- .../src/i18n/lang/en.json | 31 +- .../src/i18n/lang/id-ID.json | 31 +- .../src/i18n/lang/zh-CN.json | 31 +- .../cysync-core-constants/src/i18n/types.ts | 30 ++ .../cysync-core/src/actions/dialog/index.ts | 3 + .../SyncPlans/Dialogs/EnterEmail.tsx | 112 +++++++ .../SyncPlans/Dialogs/VerifyEmail.tsx | 119 +++++++ .../Inheritance/SyncPlans/Dialogs/index.tsx | 2 + .../dialogs/Inheritance/SyncPlans/Layout.tsx | 73 +++++ .../Inheritance/SyncPlans/context/index.tsx | 299 ++++++++++++++++++ .../dialogs/Inheritance/SyncPlans/index.tsx | 34 ++ .../src/dialogs/Inheritance/index.ts | 1 + packages/cysync-core/src/dialogs/index.tsx | 2 + packages/cysync-core/src/hooks/index.ts | 1 + .../cysync-core/src/hooks/useCountdown.tsx | 35 ++ packages/cysync-core/src/services/index.ts | 1 + .../src/services/inheritance/index.ts | 1 + .../src/services/inheritance/schema.ts | 39 +++ .../src/services/inheritance/syncPlans.ts | 65 ++++ .../cysync-core/src/services/utils/index.ts | 29 ++ .../cysync-core/src/store/dialog/index.ts | 3 + .../cysync-core/src/store/dialog/types.ts | 5 + .../ui/src/components/atoms/Container.tsx | 28 +- .../ui/src/components/atoms/Input/Input.tsx | 22 +- .../ui/src/components/molecules/OTPInput.tsx | 23 +- pnpm-lock.yaml | 269 ++-------------- 28 files changed, 1089 insertions(+), 262 deletions(-) create mode 100644 packages/cysync-core/src/dialogs/Inheritance/SyncPlans/Dialogs/EnterEmail.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/SyncPlans/Dialogs/VerifyEmail.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/SyncPlans/Dialogs/index.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/SyncPlans/Layout.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/SyncPlans/context/index.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/SyncPlans/index.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/index.ts create mode 100644 packages/cysync-core/src/hooks/useCountdown.tsx create mode 100644 packages/cysync-core/src/services/inheritance/index.ts create mode 100644 packages/cysync-core/src/services/inheritance/schema.ts create mode 100644 packages/cysync-core/src/services/inheritance/syncPlans.ts create mode 100644 packages/cysync-core/src/services/utils/index.ts diff --git a/packages/cysync-core-constants/src/i18n/lang/ar-AE.json b/packages/cysync-core-constants/src/i18n/lang/ar-AE.json index 294dcc8dc..c360e51ca 100644 --- a/packages/cysync-core-constants/src/i18n/lang/ar-AE.json +++ b/packages/cysync-core-constants/src/i18n/lang/ar-AE.json @@ -3,6 +3,10 @@ "help": "مساعدة", "back": "عودة", "allWallets": "جميع Wallets", + "labels": { + "email": "Email", + "required": "Required" + }, "buttons": { "addWallet": "إضافة Wallets", "addAccount": "إضافة حساب", @@ -40,7 +44,8 @@ "showQRCode": "إظهار رمز الاستجابة السريعة", "editAccount": "تحرير الحساب", "submit": "إرسال", - "showMore": "عرض المزيد" + "showMore": "عرض المزيد", + "resendOTP": "Resend OTP" }, "tooltips": { "downloadCsv": "تصدير كملف CSV" @@ -1454,6 +1459,16 @@ "info": "هذا هو ${derivationSchemeName} xpub. عند استيراده إلى محفظة تابعة لجهة خارجية، اختر نفس وضع الاستنتاج", "advanced": "متقدم" } + }, + "inheritanceSyncPlans": { + "enterEmail": { + "title": "Please enter your primary email associated with this plan", + "subTitle": "An OTP will be sent to this email", + "button": "Verify" + }, + "verifyEmail": { + "title": "OTP verification" + } } }, "toggle": { @@ -1556,5 +1571,19 @@ } } } + }, + "otp": { + "title": "Enter OTP", + "wrongOtpTitle": "Wrong OTP", + "successRedirectTitle": "Please wait while we redirect you...", + "triesRemaining": "${retries} tries remaining ${timeout}..", + "infoTexts": [ + "An email has been sent to **${email}**", + "Please enter the code received on the email" + ], + "noRetries": { + "title": "You have 0 retries remaining", + "subTitle": "Please exit the flow and restart" + } } } diff --git a/packages/cysync-core-constants/src/i18n/lang/de-DE.json b/packages/cysync-core-constants/src/i18n/lang/de-DE.json index 451c0c245..ee3aad450 100644 --- a/packages/cysync-core-constants/src/i18n/lang/de-DE.json +++ b/packages/cysync-core-constants/src/i18n/lang/de-DE.json @@ -3,6 +3,10 @@ "help": "Help", "back": "Zurück", "allWallets": "Alle Wallets", + "labels": { + "email": "Email", + "required": "Required" + }, "buttons": { "addWallet": "Wallet hinzufügen", "addAccount": "Konto hinzufügen", @@ -40,7 +44,8 @@ "showQRCode": "QR-Code anzeigen", "editAccount": "Konto bearbeiten", "submit": "Absenden", - "showMore": "Mehr anzeigen" + "showMore": "Mehr anzeigen", + "resendOTP": "Resend OTP" }, "tooltips": { "downloadCsv": "Als CSV exportieren" @@ -1459,6 +1464,16 @@ "info": "Dies ist ein ${derivationSchemeName} xpub. Wenn Sie es in eine Drittanbieter-Wallet importieren, wählen Sie denselben Derivationsmodus", "advanced": "Erweitert" } + }, + "inheritanceSyncPlans": { + "enterEmail": { + "title": "Please enter your primary email associated with this plan", + "subTitle": "An OTP will be sent to this email", + "button": "Verify" + }, + "verifyEmail": { + "title": "OTP verification" + } } }, "toggle": { @@ -1561,5 +1576,19 @@ } } } + }, + "otp": { + "title": "Enter OTP", + "wrongOtpTitle": "Wrong OTP", + "successRedirectTitle": "Please wait while we redirect you...", + "triesRemaining": "${retries} tries remaining ${timeout}..", + "infoTexts": [ + "An email has been sent to **${email}**", + "Please enter the code received on the email" + ], + "noRetries": { + "title": "You have 0 retries remaining", + "subTitle": "Please exit the flow and restart" + } } } diff --git a/packages/cysync-core-constants/src/i18n/lang/en.json b/packages/cysync-core-constants/src/i18n/lang/en.json index d352b3ded..9274f9c81 100644 --- a/packages/cysync-core-constants/src/i18n/lang/en.json +++ b/packages/cysync-core-constants/src/i18n/lang/en.json @@ -3,6 +3,10 @@ "help": "Help", "back": "Back", "allWallets": "All Wallets", + "labels": { + "email": "Email", + "required": "Required" + }, "buttons": { "addWallet": "Add Wallet", "addAccount": "Add Account", @@ -40,7 +44,8 @@ "showQRCode": "Show QR Code", "editAccount": "Edit Account", "submit": "Submit", - "showMore": "Show more" + "showMore": "Show more", + "resendOTP": "Resend OTP" }, "tooltips": { "downloadCsv": "Export as CSV" @@ -1459,6 +1464,16 @@ "info": "This is a ${derivationSchemeName} xpub. When importing it into a third-party wallet, choose the same derivation mode", "advanced": "Advanced" } + }, + "inheritanceSyncPlans": { + "enterEmail": { + "title": "Please enter your primary email associated with this plan", + "subTitle": "An OTP will be sent to this email", + "button": "Verify" + }, + "verifyEmail": { + "title": "OTP verification" + } } }, "toggle": { @@ -1561,5 +1576,19 @@ } } } + }, + "otp": { + "title": "Enter OTP", + "wrongOtpTitle": "Wrong OTP", + "successRedirectTitle": "Please wait while we redirect you...", + "triesRemaining": "${retries} tries remaining ${timeout}..", + "infoTexts": [ + "An email has been sent to **${email}**", + "Please enter the code received on the email" + ], + "noRetries": { + "title": "You have 0 retries remaining", + "subTitle": "Please exit the flow and restart" + } } } diff --git a/packages/cysync-core-constants/src/i18n/lang/id-ID.json b/packages/cysync-core-constants/src/i18n/lang/id-ID.json index ef39f6073..d5ec3ae0b 100644 --- a/packages/cysync-core-constants/src/i18n/lang/id-ID.json +++ b/packages/cysync-core-constants/src/i18n/lang/id-ID.json @@ -3,6 +3,10 @@ "help": "Help", "back": "Kembali", "allWallets": "Semua Wallets", + "labels": { + "email": "Email", + "required": "Required" + }, "buttons": { "addWallet": "Tambah Dompet", "addAccount": "Tambah Akun", @@ -40,7 +44,8 @@ "showQRCode": "Tampilkan Kode QR", "editAccount": "Sunting Akun", "submit": "Kirim", - "showMore": "Tampilkan lebih banyak" + "showMore": "Tampilkan lebih banyak", + "resendOTP": "Resend OTP" }, "tooltips": { "downloadCsv": "Ekspor sebagai CSV" @@ -1459,6 +1464,16 @@ "info": "Ini adalah ${derivationSchemeName} xpub. Saat mengimpor ke dalam dompet pihak ketiga, pilih mode derivasi yang sama", "advanced": "Lanjutan" } + }, + "inheritanceSyncPlans": { + "enterEmail": { + "title": "Please enter your primary email associated with this plan", + "subTitle": "An OTP will be sent to this email", + "button": "Verify" + }, + "verifyEmail": { + "title": "OTP verification" + } } }, "toggle": { @@ -1561,5 +1576,19 @@ } } } + }, + "otp": { + "title": "Enter OTP", + "wrongOtpTitle": "Wrong OTP", + "successRedirectTitle": "Please wait while we redirect you...", + "triesRemaining": "${retries} tries remaining ${timeout}..", + "infoTexts": [ + "An email has been sent to **${email}**", + "Please enter the code received on the email" + ], + "noRetries": { + "title": "You have 0 retries remaining", + "subTitle": "Please exit the flow and restart" + } } } diff --git a/packages/cysync-core-constants/src/i18n/lang/zh-CN.json b/packages/cysync-core-constants/src/i18n/lang/zh-CN.json index cf0a1fb5a..417c511c5 100644 --- a/packages/cysync-core-constants/src/i18n/lang/zh-CN.json +++ b/packages/cysync-core-constants/src/i18n/lang/zh-CN.json @@ -3,6 +3,10 @@ "help": "Help", "back": "返回", "allWallets": "所有 Wallets", + "labels": { + "email": "Email", + "required": "Required" + }, "buttons": { "addWallet": "添加 Wallet", "addAccount": "添加账户", @@ -40,7 +44,8 @@ "showQRCode": "显示二维码", "editAccount": "编辑账户", "submit": "提交", - "showMore": "显示更多" + "showMore": "显示更多", + "resendOTP": "Resend OTP" }, "tooltips": { "downloadCsv": "导出为CSV" @@ -1434,6 +1439,16 @@ "info": "这是一个 ${derivationSchemeName} xpub。将其导入第三方钱包时,请选择相同的派生模式", "advanced": "高级" } + }, + "inheritanceSyncPlans": { + "enterEmail": { + "title": "Please enter your primary email associated with this plan", + "subTitle": "An OTP will be sent to this email", + "button": "Verify" + }, + "verifyEmail": { + "title": "OTP verification" + } } }, "toggle": { @@ -1536,5 +1551,19 @@ } } } + }, + "otp": { + "title": "Enter OTP", + "wrongOtpTitle": "Wrong OTP", + "successRedirectTitle": "Please wait while we redirect you...", + "triesRemaining": "${retries} tries remaining ${timeout}..", + "infoTexts": [ + "An email has been sent to **${email}**", + "Please enter the code received on the email" + ], + "noRetries": { + "title": "You have 0 retries remaining", + "subTitle": "Please exit the flow and restart" + } } } diff --git a/packages/cysync-core-constants/src/i18n/types.ts b/packages/cysync-core-constants/src/i18n/types.ts index ee1841cf1..1c04ca7a2 100644 --- a/packages/cysync-core-constants/src/i18n/types.ts +++ b/packages/cysync-core-constants/src/i18n/types.ts @@ -5,6 +5,7 @@ export interface LanguageStrings { help: string; back: string; allWallets: string; + labels: LangLabels; buttons: LangButtons; tooltips: LangTooltips; deviceAuthentication: LangDeviceAuthentication; @@ -35,6 +36,12 @@ export interface LanguageStrings { toggle: LangToggle; snackbar: LangSnackbar; settings: LangSettings; + otp: LangOTP; +} + +interface LangLabels { + email: string; + required: string; } interface LangButtons { @@ -75,6 +82,7 @@ interface LangButtons { editAccount: string; submit: string; showMore: string; + resendOTP: string; } interface LangTooltips { @@ -731,6 +739,16 @@ interface LangDialogs { advanced: string; }; }; + inheritanceSyncPlans: { + enterEmail: { + title: string; + subTitle: string; + button: string; + }; + verifyEmail: { + title: string; + }; + }; } interface LangToggle { @@ -785,3 +803,15 @@ interface LangSettings { }; }; } + +interface LangOTP { + title: string; + wrongOtpTitle: string; + successRedirectTitle: string; + triesRemaining: string; + infoTexts: string[]; + noRetries: { + title: string; + subTitle: string; + }; +} diff --git a/packages/cysync-core/src/actions/dialog/index.ts b/packages/cysync-core/src/actions/dialog/index.ts index de730d0f1..edea72d6c 100644 --- a/packages/cysync-core/src/actions/dialog/index.ts +++ b/packages/cysync-core/src/actions/dialog/index.ts @@ -85,3 +85,6 @@ export const openBetaNotificationDialog = () => export const openEditAccountDialog = (props?: EditAccountDialogProps) => openDialog({ name: 'editAccount', data: props }); + +export const openInheritanceSyncPlansDialog = () => + openDialog({ name: 'inheritanceSyncPlans', data: undefined }); diff --git a/packages/cysync-core/src/dialogs/Inheritance/SyncPlans/Dialogs/EnterEmail.tsx b/packages/cysync-core/src/dialogs/Inheritance/SyncPlans/Dialogs/EnterEmail.tsx new file mode 100644 index 000000000..6418bf65d --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/SyncPlans/Dialogs/EnterEmail.tsx @@ -0,0 +1,112 @@ +import { Flex, Input, LangDisplay, Typography } from '@cypherock/cysync-ui'; +import React, { useState } from 'react'; + +import { useStateWithRef } from '~/hooks'; +import { selectLanguage, useAppSelector } from '~/store'; +import { validateEmail } from '~/utils'; + +import { useInheritanceSyncPlansDialog } from '../context'; +import { InheritanceSyncPlansLayout } from '../Layout'; + +export const EnterEmail: React.FC = () => { + const lang = useAppSelector(selectLanguage); + const { + onClose, + onEnterEmail, + email: existingEmail, + isSendingEmail, + sendingEmailError, + resetServerErrors, + } = useInheritanceSyncPlansDialog(); + const strings = lang.strings.dialogs.inheritanceSyncPlans; + + const [email, setEmail] = useStateWithRef(existingEmail); + const [validationError, setValidationError] = useState( + undefined, + ); + + const validateInputEmail = (newEmail: string) => { + if (newEmail.length === 0) { + setValidationError(undefined); + return; + } + + const validation = validateEmail(newEmail, lang); + + if (!validation.success) { + setValidationError(validation.error.issues[0].message); + return; + } + + setValidationError(undefined); + }; + + const onEmailChange = (val: string) => { + setEmail(val); + validateInputEmail(val); + + if (sendingEmailError) { + resetServerErrors(); + } + }; + + const onSubmit = (e?: React.FormEvent) => { + if (e) { + e.preventDefault(); + } + + if (!validationError) { + onEnterEmail(email); + } + }; + + const formId = 'inheritance-sync-plans-enter-email'; + const displayError = validationError ?? sendingEmailError; + + return ( + +

+ + + {strings.enterEmail.title} + + + {strings.enterEmail.subTitle} + + + {displayError && ( + + + + )} + +
+ + ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/SyncPlans/Dialogs/VerifyEmail.tsx b/packages/cysync-core/src/dialogs/Inheritance/SyncPlans/Dialogs/VerifyEmail.tsx new file mode 100644 index 000000000..238f270e7 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/SyncPlans/Dialogs/VerifyEmail.tsx @@ -0,0 +1,119 @@ +import { + Flex, + OTPInput, + OTPInputStatus, + Typography, +} from '@cypherock/cysync-ui'; +import React, { useEffect, useMemo, useState } from 'react'; + +import { useCountdown } from '~/hooks'; +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritanceSyncPlansDialog } from '../context'; +import { InheritanceSyncPlansLayout } from '../Layout'; + +export const VerifyEmail: React.FC = () => { + const lang = useAppSelector(selectLanguage); + const { + onClose, + email, + onPrevious, + otpLength, + verifyEmail, + isVerifyingEmail, + isResendingOtp, + onResendOtp, + retriesRemaining, + otpExpireTime, + wrongOtpError, + } = useInheritanceSyncPlansDialog(); + + const strings = lang.strings.dialogs.inheritanceSyncPlans; + + const [otp, setOtp] = useState(''); + const { seconds: expireSeconds } = useCountdown( + otpExpireTime ? new Date(otpExpireTime).getTime() : new Date().getTime(), + ); + + const onOtpChange = (val: string) => { + setOtp(val); + + if (val.length === otpLength) { + verifyEmail(otp); + } + }; + + const onResend = () => { + onResendOtp(); + }; + + const textVariables = useMemo( + () => ({ + email, + retries: retriesRemaining.toString(), + timeout: expireSeconds.toString(), + }), + [email, retriesRemaining, expireSeconds], + ); + + const getStatus = (): OTPInputStatus => { + if (retriesRemaining <= 0) { + return 'retryExceeded'; + } + + if (wrongOtpError) { + return 'error'; + } + + return 'idle'; + }; + + const getTitle = () => { + if (retriesRemaining <= 0) { + return lang.strings.otp.noRetries.title; + } + + if (wrongOtpError) { + return lang.strings.otp.wrongOtpTitle; + } + + return lang.strings.otp.title; + }; + + useEffect(() => { + if (!isVerifyingEmail) { + setOtp(''); + } + }, [isVerifyingEmail]); + + return ( + + + + {strings.verifyEmail.title} + + + + + ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/SyncPlans/Dialogs/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/SyncPlans/Dialogs/index.tsx new file mode 100644 index 000000000..cba24ca18 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/SyncPlans/Dialogs/index.tsx @@ -0,0 +1,2 @@ +export * from './EnterEmail'; +export * from './VerifyEmail'; diff --git a/packages/cysync-core/src/dialogs/Inheritance/SyncPlans/Layout.tsx b/packages/cysync-core/src/dialogs/Inheritance/SyncPlans/Layout.tsx new file mode 100644 index 000000000..744aa52d8 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/SyncPlans/Layout.tsx @@ -0,0 +1,73 @@ +import { + DialogBox, + DialogBoxHeader, + LangDisplay, + CloseButton, + ScrollableContainer, + DialogBoxBody, + DialogBoxFooter, + Button, + ButtonProps, +} from '@cypherock/cysync-ui'; +import React from 'react'; + +export interface IInheritanceSyncPlansLayoutProps { + children: React.ReactNode; + onClose: () => void; + actionButtonText?: string; + onActionButtonClick?: () => void; + isActionButtonDisabled?: boolean; + isActionButtonLoading?: boolean; + actionButtonVariant?: ButtonProps['variant']; + formId?: string; +} + +export const InheritanceSyncPlansLayout: React.FC< + IInheritanceSyncPlansLayoutProps +> = ({ + children, + onClose, + actionButtonText, + onActionButtonClick, + isActionButtonDisabled, + isActionButtonLoading, + actionButtonVariant, + formId, +}) => ( + + + + + + + {children} + + + {actionButtonText && onActionButtonClick && ( + + + + )} + +); + +InheritanceSyncPlansLayout.defaultProps = { + actionButtonText: undefined, + onActionButtonClick: undefined, + isActionButtonDisabled: undefined, + actionButtonVariant: undefined, + isActionButtonLoading: undefined, + formId: undefined, +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/SyncPlans/context/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/SyncPlans/context/index.tsx new file mode 100644 index 000000000..b4900fdb2 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/SyncPlans/context/index.tsx @@ -0,0 +1,299 @@ +import React, { + Context, + FC, + ReactNode, + createContext, + useCallback, + useContext, + useMemo, + useState, +} from 'react'; + +import { ITabs, useTabsAndDialogs } from '~/hooks'; +import { + inheritanceSyncPlansService, + InheritanceSyncPlansStartResponse, +} from '~/services'; +import { + closeDialog, + selectLanguage, + useAppDispatch, + useAppSelector, +} from '~/store'; + +import { EnterEmail, VerifyEmail } from '../Dialogs'; + +export interface InheritanceSyncPlansDialogContextInterface { + tabs: ITabs; + onNext: (tab?: number, dialog?: number) => void; + goTo: (tab: number, dialog?: number) => void; + onPrevious: () => void; + currentTab: number; + currentDialog: number; + isDeviceRequired: boolean; + onClose: () => void; + email: string; + onEnterEmail: (email: string) => void; + isSendingEmail: boolean; + startResponse?: InheritanceSyncPlansStartResponse; + unhandledError?: any; + sendingEmailError?: string; + onRetry: () => void; + resetServerErrors: () => void; + otpLength: number; + verifyEmail: (otp: string) => void; + isVerifyingEmail: boolean; + verifyingEmailError?: string; + onResendOtp: () => void; + isResendingOtp: boolean; + resendOtpError?: string; + retriesRemaining: number; + otpExpireTime?: string; + wrongOtpError?: boolean; +} + +export const InheritanceSyncPlansDialogContext: Context = + createContext( + {} as InheritanceSyncPlansDialogContextInterface, + ); + +export interface InheritanceSyncPlansDialogContextProviderProps { + children: ReactNode; +} + +export const InheritanceSyncPlansDialogProvider: FC< + InheritanceSyncPlansDialogContextProviderProps +> = ({ children }) => { + const dispatch = useAppDispatch(); + const lang = useAppSelector(selectLanguage); + + const deviceRequiredDialogsMap: Record = {}; + const tabs: ITabs = [ + { + name: 'Enter Email', + dialogs: [], + }, + { + name: 'Verify Email', + dialogs: [], + }, + ]; + + const { + onNext, + onPrevious, + goTo, + currentTab, + currentDialog, + isDeviceRequired, + } = useTabsAndDialogs({ + deviceRequiredDialogsMap, + tabs, + dialogName: 'editAccount', + }); + + const onClose = () => { + dispatch(closeDialog('inheritanceSyncPlans')); + }; + + const [email, setEmail] = useState(''); + const [startResponse, setStartResponse] = useState< + InheritanceSyncPlansStartResponse | undefined + >(undefined); + const [isSendingEmail, setIsSendingEmail] = useState(false); + const [unhandledError, setUnhandledError] = useState(); + const [sendingEmailError, setSendingEmailError] = useState< + string | undefined + >(); + const [otpLength, setOtpLength] = useState(6); + const [isVerifyingEmail, setIsVerifyingEmail] = useState(false); + const [verifyingEmailError, setVerifyingEmailError] = useState< + string | undefined + >(); + const [wrongOtpError, setWrongOtpError] = useState(false); + const [isResendingOtp, setIsResendingOtp] = useState(false); + const [resendOtpError, setResendOtpError] = useState(); + const [retriesRemaining, setRetriesRemaining] = useState(3); + const [otpExpireTime, setOtpExpireTime] = useState(''); + + const sendEmail = useCallback(async (params: { email: string }) => { + setIsSendingEmail(true); + setSendingEmailError(undefined); + try { + const response = await inheritanceSyncPlansService.start(params); + if (response.result) { + setOtpExpireTime(response.result.otpExpiry); + setRetriesRemaining(response.result.retriesRemaining); + setOtpLength(response.result.otpLength); + setStartResponse(response.result); + goTo(1); + } else { + setSendingEmailError(response.error ?? lang.strings.errors.default); + } + } catch (error) { + setUnhandledError(error); + } finally { + setIsSendingEmail(false); + } + }, []); + + const onEnterEmail = useCallback( + (val: string) => { + setEmail(val); + sendEmail({ email: val }); + }, + [sendEmail], + ); + + const onRetry = useCallback(() => { + setIsSendingEmail(false); + setSendingEmailError(undefined); + setUnhandledError(undefined); + setStartResponse(undefined); + goTo(0); + }, []); + + const resetServerErrors = useCallback(() => { + setSendingEmailError(undefined); + }, []); + + const syncPlans = () => { + // TODO: Implement this function + }; + + const verifyEmail = useCallback( + async (otp: string) => { + if (!startResponse) { + return; + } + + setIsVerifyingEmail(true); + setVerifyingEmailError(undefined); + try { + const response = await inheritanceSyncPlansService.verify({ + requestId: startResponse.requestId, + otp, + }); + if (response.result) { + if (response.result.otpExpiry) { + setOtpExpireTime(response.result.otpExpiry); + } + + if (response.result.retriesRemaining !== undefined) { + setRetriesRemaining(response.result.retriesRemaining); + } + + if (response.result.isSuccess && response.result.plans) { + syncPlans(); + onClose(); + } + + if (!response.result.isSuccess) { + setWrongOtpError(true); + } + } else { + setVerifyingEmailError(response.error ?? lang.strings.errors.default); + } + } catch (error) { + setUnhandledError(error); + } finally { + setIsVerifyingEmail(false); + } + }, + [startResponse], + ); + + const onResendOtp = useCallback(async () => { + if (!startResponse) { + return; + } + + setIsResendingOtp(true); + setResendOtpError(undefined); + + try { + const response = await inheritanceSyncPlansService.resendOTP({ + requestId: startResponse.requestId, + }); + + if (response.result) { + setOtpExpireTime(response.result.otpExpiry); + setRetriesRemaining(response.result.retriesRemaining); + } else { + setResendOtpError(response.error ?? lang.strings.errors.default); + } + } catch (error) { + setUnhandledError(error); + } finally { + setIsResendingOtp(false); + } + }, [startResponse]); + + const ctx = useMemo( + () => ({ + onNext, + onPrevious, + tabs, + onClose, + goTo, + currentTab, + currentDialog, + isDeviceRequired, + email, + onEnterEmail, + isSendingEmail, + startResponse, + unhandledError, + sendingEmailError, + onRetry, + resetServerErrors, + otpLength, + verifyEmail, + isVerifyingEmail, + verifyingEmailError, + onResendOtp, + isResendingOtp, + resendOtpError, + retriesRemaining, + otpExpireTime, + wrongOtpError, + }), + [ + onNext, + onPrevious, + tabs, + onClose, + goTo, + currentTab, + currentDialog, + isDeviceRequired, + email, + onEnterEmail, + isSendingEmail, + startResponse, + unhandledError, + sendingEmailError, + onRetry, + resetServerErrors, + otpLength, + verifyEmail, + isVerifyingEmail, + verifyingEmailError, + onResendOtp, + isResendingOtp, + resendOtpError, + otpExpireTime, + wrongOtpError, + ], + ); + + return ( + + {children} + + ); +}; + +export function useInheritanceSyncPlansDialog(): InheritanceSyncPlansDialogContextInterface { + return useContext(InheritanceSyncPlansDialogContext); +} diff --git a/packages/cysync-core/src/dialogs/Inheritance/SyncPlans/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/SyncPlans/index.tsx new file mode 100644 index 000000000..0b90f1b79 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/SyncPlans/index.tsx @@ -0,0 +1,34 @@ +import { BlurOverlay } from '@cypherock/cysync-ui'; +import React, { FC } from 'react'; + +import { ErrorHandlerDialog } from '~/components'; + +import { + InheritanceSyncPlansDialogProvider, + useInheritanceSyncPlansDialog, +} from './context'; + +const InheritanceSyncPlans: FC = () => { + const { tabs, currentTab, currentDialog, unhandledError, onClose, onRetry } = + useInheritanceSyncPlansDialog(); + + return ( + + + {tabs[currentTab]?.dialogs[currentDialog]} + + + ); +}; + +export const InheritanceSyncPlansDialog = () => ( + + + +); diff --git a/packages/cysync-core/src/dialogs/Inheritance/index.ts b/packages/cysync-core/src/dialogs/Inheritance/index.ts new file mode 100644 index 000000000..a8dfa8029 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/index.ts @@ -0,0 +1 @@ +export * from './SyncPlans'; diff --git a/packages/cysync-core/src/dialogs/index.tsx b/packages/cysync-core/src/dialogs/index.tsx index 2b40cc3ff..89dd6e3ba 100644 --- a/packages/cysync-core/src/dialogs/index.tsx +++ b/packages/cysync-core/src/dialogs/index.tsx @@ -19,6 +19,7 @@ import { EditAccountDialog } from './EditAccountDialog'; import { ErrorDialog } from './ErrorDialog'; import { GuidedFlow } from './GuidedFlow'; import { HistoryDialog } from './HistoryDialog'; +import { InheritanceSyncPlansDialog } from './Inheritance'; import { ChangePasswordDialog, RemovePasswordDialog, @@ -57,4 +58,5 @@ export const dialogs: Record = { contactSupportDialog: ContactSupportDialog, deleteAccount: DeleteAccountDialog, betaNotificationDialog: BetaNotificationDialog, + inheritanceSyncPlans: InheritanceSyncPlansDialog, }; diff --git a/packages/cysync-core/src/hooks/index.ts b/packages/cysync-core/src/hooks/index.ts index a5fc13141..bae855c63 100644 --- a/packages/cysync-core/src/hooks/index.ts +++ b/packages/cysync-core/src/hooks/index.ts @@ -20,3 +20,4 @@ export * from './useGraph'; export * from './useAssetDropdown'; export * from './useSubAccounts'; export * from './useWalletSync'; +export * from './useCountdown'; diff --git a/packages/cysync-core/src/hooks/useCountdown.tsx b/packages/cysync-core/src/hooks/useCountdown.tsx new file mode 100644 index 000000000..621efffdd --- /dev/null +++ b/packages/cysync-core/src/hooks/useCountdown.tsx @@ -0,0 +1,35 @@ +import { useEffect, useRef, useState } from 'react'; + +export const useCountdown = (time: number) => { + const timeRef = useRef(time); + + useEffect(() => { + timeRef.current = time; + }, [time]); + + const [state, setState] = useState({ + seconds: 0, + }); + + useEffect(() => { + const interval = setInterval(() => updateCountdown(), 1000); + + return () => clearInterval(interval); + }, []); + + const updateCountdown = () => { + const countdownDate = timeRef.current; + + if (countdownDate) { + const currentTime = new Date().getTime(); + + const distanceToDate = countdownDate - currentTime; + + const seconds = Math.floor(distanceToDate / 1000); + + setState({ seconds }); + } + }; + + return state; +}; diff --git a/packages/cysync-core/src/services/index.ts b/packages/cysync-core/src/services/index.ts index 9c002d79c..86d727e3a 100644 --- a/packages/cysync-core/src/services/index.ts +++ b/packages/cysync-core/src/services/index.ts @@ -1 +1,2 @@ export * from './feedbackService'; +export * from './inheritance'; diff --git a/packages/cysync-core/src/services/inheritance/index.ts b/packages/cysync-core/src/services/inheritance/index.ts new file mode 100644 index 000000000..27ef23d8f --- /dev/null +++ b/packages/cysync-core/src/services/inheritance/index.ts @@ -0,0 +1 @@ +export * from './syncPlans'; diff --git a/packages/cysync-core/src/services/inheritance/schema.ts b/packages/cysync-core/src/services/inheritance/schema.ts new file mode 100644 index 000000000..cb0e5e5d3 --- /dev/null +++ b/packages/cysync-core/src/services/inheritance/schema.ts @@ -0,0 +1,39 @@ +import { z } from 'zod'; + +export const startResultSchema = z.object({ + requestId: z.string(), + maskedEmail: z.string(), + retriesRemaining: z.number(), + otpExpiry: z.string().datetime(), + otpLength: z.number(), +}); + +export const resendResultSchema = z.object({ + requestId: z.string(), + maskedEmail: z.string(), + retriesRemaining: z.number(), + otpExpiry: z.string().datetime(), +}); + +export const verifyResultSchema = z.object({ + plans: z + .array( + z.object({ + __id: z.string(), + }), + ) + .optional(), + isSuccess: z.boolean(), + retriesRemaining: z.number().optional(), + otpExpiry: z.string().datetime().optional(), +}); + +export type InheritanceSyncPlansStartResponse = z.infer< + typeof startResultSchema +>; +export type InheritanceSyncPlansResendResponse = z.infer< + typeof resendResultSchema +>; +export type InheritanceSyncPlansVerifyResponse = z.infer< + typeof verifyResultSchema +>; diff --git a/packages/cysync-core/src/services/inheritance/syncPlans.ts b/packages/cysync-core/src/services/inheritance/syncPlans.ts new file mode 100644 index 000000000..20e12e391 --- /dev/null +++ b/packages/cysync-core/src/services/inheritance/syncPlans.ts @@ -0,0 +1,65 @@ +import axios from 'axios'; + +import { config } from '~/config'; + +import { + startResultSchema, + resendResultSchema, + verifyResultSchema, + InheritanceSyncPlansStartResponse, + InheritanceSyncPlansResendResponse, + InheritanceSyncPlansVerifyResponse, +} from './schema'; + +import { runAndHandleServerErrors } from '../utils'; + +export { + type InheritanceSyncPlansResendResponse, + type InheritanceSyncPlansVerifyResponse, + type InheritanceSyncPlansStartResponse, +} from './schema'; + +const start = async (params: { email: string }) => + runAndHandleServerErrors(async () => { + const response = await axios.post(`${config.API_CYPHEROCK}/sync-plans`, { + ...params, + }); + + const result = startResultSchema.parse(response.data); + + return result; + }); + +const resendOTP = async (params: { requestId: string }) => + runAndHandleServerErrors(async () => { + const response = await axios.post( + `${config.API_CYPHEROCK}/sync-plans/resend`, + { + ...params, + }, + ); + + const result = resendResultSchema.parse(response.data); + + return result; + }); + +const verify = async (params: { requestId: string; otp: string }) => + runAndHandleServerErrors(async () => { + const response = await axios.post( + `${config.API_CYPHEROCK}/sync-plans/verify`, + { + ...params, + }, + ); + + const result = verifyResultSchema.parse(response.data); + + return result; + }); + +export const inheritanceSyncPlansService = { + start, + resendOTP, + verify, +}; diff --git a/packages/cysync-core/src/services/utils/index.ts b/packages/cysync-core/src/services/utils/index.ts new file mode 100644 index 000000000..f6fb600be --- /dev/null +++ b/packages/cysync-core/src/services/utils/index.ts @@ -0,0 +1,29 @@ +import { AxiosError } from 'axios'; + +export interface WithServerError { + error?: string; + result?: T; +} + +export async function runAndHandleServerErrors( + callback: () => Promise, +): Promise> { + try { + const result = await callback(); + return { + result, + }; + } catch (error) { + if ((error as any)?.isAxiosError) { + const e = error as AxiosError; + + if ((e.response?.data as any).cysyncError) { + return { + error: (e.response?.data as any).cysyncError, + }; + } + } + + throw error; + } +} diff --git a/packages/cysync-core/src/store/dialog/index.ts b/packages/cysync-core/src/store/dialog/index.ts index 15fa7c327..27f5c3297 100644 --- a/packages/cysync-core/src/store/dialog/index.ts +++ b/packages/cysync-core/src/store/dialog/index.ts @@ -86,6 +86,9 @@ const initialState: IDialogState = { betaNotificationDialog: { isOpen: false, }, + inheritanceSyncPlans: { + isOpen: false, + }, }; export const dialogSlice = createSlice({ diff --git a/packages/cysync-core/src/store/dialog/types.ts b/packages/cysync-core/src/store/dialog/types.ts index 94a784fbe..1465be553 100644 --- a/packages/cysync-core/src/store/dialog/types.ts +++ b/packages/cysync-core/src/store/dialog/types.ts @@ -132,6 +132,11 @@ export interface IDialogState { isOpen: boolean; data?: undefined; }; + + inheritanceSyncPlans: { + isOpen: boolean; + data?: undefined; + }; } export const GuidedFlowMap = { diff --git a/packages/ui/src/components/atoms/Container.tsx b/packages/ui/src/components/atoms/Container.tsx index 35879d761..800834000 100644 --- a/packages/ui/src/components/atoms/Container.tsx +++ b/packages/ui/src/components/atoms/Container.tsx @@ -26,9 +26,10 @@ export interface ContainerProps children?: ReactNode; size?: 'lg'; $noFlex?: boolean; + $variant?: 'div' | 'span' | 'p'; } -const ContainerStyle = styled.div` +const containerStyles = css` ${props => !props.$noFlex && css` @@ -44,14 +45,33 @@ const ContainerStyle = styled.div` ${height} `; -export const Container: FC = ({ children, ...props }) => ( - {children} -); +const ContainerStyleDiv = styled.div` + ${containerStyles} +`; +const ContainerStyleP = styled.div` + ${containerStyles} +`; +const ContainerStyleSpan = styled.div` + ${containerStyles} +`; + +const variantMap = { + div: ContainerStyleDiv, + p: ContainerStyleP, + span: ContainerStyleSpan, +}; + +export const Container: FC = ({ children, ...props }) => { + const ContainerStyle = variantMap[props.$variant ?? 'div']; + + return {children}; +}; Container.defaultProps = { children: null, size: 'lg', $noFlex: false, + $variant: 'div', }; export const FlexGapContainer = styled(Container)` diff --git a/packages/ui/src/components/atoms/Input/Input.tsx b/packages/ui/src/components/atoms/Input/Input.tsx index a31849e60..9503fe7c9 100644 --- a/packages/ui/src/components/atoms/Input/Input.tsx +++ b/packages/ui/src/components/atoms/Input/Input.tsx @@ -5,6 +5,7 @@ import { InputLabel } from './InputLabel'; import { UtilsProps } from '../../utils'; import { Button } from '../Button'; +import { Container } from '../Container'; import { Flex } from '../Flex'; import { LangDisplay } from '../LangDisplay'; import { Typography } from '../Typography'; @@ -14,6 +15,8 @@ export interface InputProps { placeholder?: string; name: string; label?: string; + rightLabel?: string; + showRequiredStar?: boolean; onChange?: (val: string) => void; onBlur?: (val: string) => void; value?: string; @@ -108,6 +111,8 @@ export const Input: FC }> = type, name, label = undefined, + showRequiredStar = undefined, + rightLabel = undefined, onChange = undefined, onBlur = undefined, value = undefined, @@ -139,8 +144,19 @@ export const Input: FC }> = {...(utilProps ?? {})} > {label && ( - - + + + + + {rightLabel && } + {showRequiredStar && ( + + {' '} + * + + )} + + )} }> = Input.defaultProps = { label: undefined, + rightLabel: undefined, + showRequiredStar: undefined, placeholder: undefined, postfixText: undefined, onChange: undefined, diff --git a/packages/ui/src/components/molecules/OTPInput.tsx b/packages/ui/src/components/molecules/OTPInput.tsx index 69487b793..5facaa5a9 100644 --- a/packages/ui/src/components/molecules/OTPInput.tsx +++ b/packages/ui/src/components/molecules/OTPInput.tsx @@ -3,10 +3,11 @@ import OtpInput from 'react-otp-input'; import styled from 'styled-components'; import { Button, Container, Flex, LangDisplay, Typography } from '../atoms'; +import { UtilsProps } from '../utils'; export type OTPInputStatus = 'idle' | 'success' | 'error' | 'retryExceeded'; -export interface OTPInputProps { +export interface OTPInputProps extends UtilsProps { title: string; status: OTPInputStatus; otpLength: number; @@ -88,6 +89,7 @@ export const OTPInput: React.FC = ({ actionText, disabled, onAction, + ...rest }) => { const isRetryExceeded = status === 'retryExceeded'; @@ -98,6 +100,7 @@ export const OTPInput: React.FC = ({ align="center" justify="center" $alignSelf="stretch" + {...rest} > = ({ - - {subText} - + {subText && ( + + + + )} {actionText && ( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4b45ac51e..c18895875 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2186,7 +2186,7 @@ importers: version: 3.0.0 jest: specifier: ^29.5.0 - version: 29.5.0(@types/node@18.15.11) + version: 29.5.0(@types/node@18.15.11)(ts-node@10.9.1) jest-environment-jsdom: specifier: ^29.5.0 version: 29.5.0 @@ -2476,7 +2476,7 @@ packages: '@babel/traverse': 7.24.7 '@babel/types': 7.24.7 convert-source-map: 1.9.0 - debug: 4.3.5 + debug: 4.3.5(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -2498,7 +2498,7 @@ packages: '@babel/traverse': 7.24.7 '@babel/types': 7.24.7 convert-source-map: 2.0.0 - debug: 4.3.5 + debug: 4.3.5(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -2621,7 +2621,7 @@ packages: '@babel/core': 7.22.5 '@babel/helper-compilation-targets': 7.24.7 '@babel/helper-plugin-utils': 7.24.7 - debug: 4.3.5 + debug: 4.3.5(supports-color@8.1.1) lodash.debounce: 4.0.8 resolve: 1.22.8 transitivePeerDependencies: @@ -2636,7 +2636,7 @@ packages: '@babel/core': 7.24.7 '@babel/helper-compilation-targets': 7.24.7 '@babel/helper-plugin-utils': 7.24.7 - debug: 4.3.5 + debug: 4.3.5(supports-color@8.1.1) lodash.debounce: 4.0.8 resolve: 1.22.8 transitivePeerDependencies: @@ -5007,7 +5007,7 @@ packages: '@babel/helper-split-export-declaration': 7.24.7 '@babel/parser': 7.24.7 '@babel/types': 7.24.7 - debug: 4.3.5 + debug: 4.3.5(supports-color@8.1.1) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -6024,7 +6024,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 - debug: 4.3.5 + debug: 4.3.5(supports-color@8.1.1) espree: 9.6.1 globals: 13.24.0 ignore: 5.3.1 @@ -6252,7 +6252,7 @@ packages: deprecated: Use @eslint/config-array instead dependencies: '@humanwhocodes/object-schema': 2.0.3 - debug: 4.3.5 + debug: 4.3.5(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -6452,49 +6452,6 @@ packages: slash: 3.0.0 dev: true - /@jest/core@29.7.0: - resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/console': 29.7.0 - '@jest/reporters': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 16.18.36 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.9.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@16.18.36) - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - jest-watcher: 29.7.0 - micromatch: 4.0.7 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - ts-node - dev: true - /@jest/core@29.7.0(ts-node@10.9.1): resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -10438,7 +10395,7 @@ packages: util: 0.12.5 util-deprecate: 1.0.2 watchpack: 2.4.1 - ws: 8.17.1 + ws: 8.17.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - encoding @@ -12133,7 +12090,7 @@ packages: dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - debug: 4.3.5 + debug: 4.3.5(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.3 @@ -12941,7 +12898,7 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} dependencies: - debug: 4.3.5 + debug: 4.3.5(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: true @@ -15059,25 +15016,6 @@ packages: - ts-node dev: true - /create-jest@29.7.0(@types/node@18.15.11): - resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@18.15.11) - jest-util: 29.7.0 - prompts: 2.4.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - dev: true - /create-jest@29.7.0(@types/node@18.15.11)(ts-node@10.9.1): resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -15451,17 +15389,6 @@ packages: ms: 2.1.3 dev: true - /debug@4.3.5: - resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.2 - /debug@4.3.5(supports-color@8.1.1): resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} engines: {node: '>=6.0'} @@ -15793,7 +15720,7 @@ packages: hasBin: true dependencies: address: 1.2.2 - debug: 4.3.5 + debug: 4.3.5(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: true @@ -16458,7 +16385,7 @@ packages: peerDependencies: esbuild: '>=0.12 <1' dependencies: - debug: 4.3.5 + debug: 4.3.5(supports-color@8.1.1) esbuild: 0.18.20 transitivePeerDependencies: - supports-color @@ -16815,7 +16742,7 @@ packages: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.5 + debug: 4.3.5(supports-color@8.1.1) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -18359,7 +18286,7 @@ packages: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.3.5 + debug: 4.3.5(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: true @@ -18396,7 +18323,7 @@ packages: engines: {node: '>= 6.0.0'} dependencies: agent-base: 5.1.1 - debug: 4.3.5 + debug: 4.3.5(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: true @@ -18406,7 +18333,7 @@ packages: engines: {node: '>= 6'} dependencies: agent-base: 6.0.2 - debug: 4.3.5 + debug: 4.3.5(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: true @@ -19219,7 +19146,7 @@ packages: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} engines: {node: '>=10'} dependencies: - debug: 4.3.5 + debug: 4.3.5(supports-color@8.1.1) istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: @@ -19356,34 +19283,6 @@ packages: - ts-node dev: true - /jest-cli@29.7.0(@types/node@18.15.11): - resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/core': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0(@types/node@18.15.11) - exit: 0.1.2 - import-local: 3.1.0 - jest-config: 29.7.0(@types/node@18.15.11) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - dev: true - /jest-cli@29.7.0(@types/node@18.15.11)(ts-node@10.9.1): resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -19440,46 +19339,6 @@ packages: - ts-node dev: true - /jest-config@29.7.0(@types/node@16.18.36): - resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@types/node': '*' - ts-node: '>=9.0.0' - peerDependenciesMeta: - '@types/node': - optional: true - ts-node: - optional: true - dependencies: - '@babel/core': 7.22.5 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 16.18.36 - babel-jest: 29.7.0(@babel/core@7.22.5) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.7 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - dev: true - /jest-config@29.7.0(@types/node@16.18.36)(ts-node@10.9.1): resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -19515,47 +19374,7 @@ packages: pretty-format: 29.7.0 slash: 3.0.0 strip-json-comments: 3.1.1 - ts-node: 10.9.1(@types/node@20.5.1)(typescript@4.9.5) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - dev: true - - /jest-config@29.7.0(@types/node@18.15.11): - resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@types/node': '*' - ts-node: '>=9.0.0' - peerDependenciesMeta: - '@types/node': - optional: true - ts-node: - optional: true - dependencies: - '@babel/core': 7.22.5 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 18.15.11 - babel-jest: 29.7.0(@babel/core@7.22.5) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.7 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 + ts-node: 10.9.1(@types/node@18.15.11)(typescript@4.9.5) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -19596,7 +19415,7 @@ packages: pretty-format: 29.7.0 slash: 3.0.0 strip-json-comments: 3.1.1 - ts-node: 10.9.1(@types/node@20.5.1)(typescript@4.9.5) + ts-node: 10.9.1(@types/node@18.15.11)(typescript@4.9.5) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -19973,7 +19792,7 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 18.15.11 + '@types/node': 16.18.36 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true @@ -20008,27 +19827,6 @@ packages: - ts-node dev: true - /jest@29.5.0(@types/node@18.15.11): - resolution: {integrity: sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - dependencies: - '@jest/core': 29.7.0 - '@jest/types': 29.6.3 - import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@18.15.11) - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - dev: true - /jest@29.5.0(@types/node@18.15.11)(ts-node@10.9.1): resolution: {integrity: sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -20173,7 +19971,7 @@ packages: whatwg-encoding: 2.0.0 whatwg-mimetype: 3.0.0 whatwg-url: 11.0.0 - ws: 8.17.1 + ws: 8.17.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) xml-name-validator: 4.0.0 transitivePeerDependencies: - bufferutil @@ -20562,7 +20360,7 @@ packages: chalk: 5.2.0 cli-truncate: 3.1.0 commander: 10.0.1 - debug: 4.3.5 + debug: 4.3.5(supports-color@8.1.1) execa: 7.2.0 lilconfig: 2.1.0 listr2: 5.0.8 @@ -20818,7 +20616,7 @@ packages: engines: {node: '>=8.0'} dependencies: date-format: 4.0.14 - debug: 4.3.5 + debug: 4.3.5(supports-color@8.1.1) flatted: 3.3.1 rfdc: 1.4.1 streamroller: 3.1.5 @@ -23271,7 +23069,7 @@ packages: engines: {node: '>=8.16.0'} dependencies: '@types/mime-types': 2.1.4 - debug: 4.3.5 + debug: 4.3.5(supports-color@8.1.1) extract-zip: 1.7.0 https-proxy-agent: 4.0.0 mime: 2.6.0 @@ -25087,7 +24885,7 @@ packages: engines: {node: '>=8.0'} dependencies: date-format: 4.0.14 - debug: 4.3.5 + debug: 4.3.5(supports-color@8.1.1) fs-extra: 8.1.0 transitivePeerDependencies: - supports-color @@ -25845,7 +25643,7 @@ packages: bs-logger: 0.2.6 esbuild: 0.18.20 fast-json-stable-stringify: 2.1.0 - jest: 29.5.0(@types/node@18.15.11) + jest: 29.5.0(@types/node@18.15.11)(ts-node@10.9.1) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -27763,19 +27561,6 @@ packages: utf-8-validate: optional: true - /ws@8.17.1: - resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - dev: true - /ws@8.17.1(bufferutil@4.0.8)(utf-8-validate@5.0.10): resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} engines: {node: '>=10.0.0'} From 9b4a2b360910c1c5650f3c886d240623f71f9428 Mon Sep 17 00:00:00 2001 From: "moayaan.eth" Date: Thu, 25 Jul 2024 18:41:34 +0530 Subject: [PATCH 11/96] feat: coupon code input UI (#507) * feat(inheritance): coupon code UI * feat(inheritance): fix PR review changes * feat(inheritance): fix PR review changes * feat(inheritance): fix PR review changes --- packages/ui/icons/delete-icon.svg | 10 ++ .../src/components/molecules/CouponInput.tsx | 128 ++++++++++++++++++ packages/ui/src/components/molecules/index.ts | 1 + .../ui/src/components/utils/bgColor.styled.ts | 6 + .../ui/src/components/utils/border.styled.ts | 3 +- .../stories/molecules/CouponInput.stories.tsx | 60 ++++++++ packages/ui/src/themes/color.styled.ts | 2 + packages/ui/src/themes/theme.styled.ts | 2 + 8 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 packages/ui/icons/delete-icon.svg create mode 100644 packages/ui/src/components/molecules/CouponInput.tsx create mode 100644 packages/ui/src/stories/molecules/CouponInput.stories.tsx diff --git a/packages/ui/icons/delete-icon.svg b/packages/ui/icons/delete-icon.svg new file mode 100644 index 000000000..90ad35f11 --- /dev/null +++ b/packages/ui/icons/delete-icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/ui/src/components/molecules/CouponInput.tsx b/packages/ui/src/components/molecules/CouponInput.tsx new file mode 100644 index 000000000..8c159f4ed --- /dev/null +++ b/packages/ui/src/components/molecules/CouponInput.tsx @@ -0,0 +1,128 @@ +import React from 'react'; +import styled, { useTheme } from 'styled-components'; + +import { DeleteIcon } from '../../assets'; +import { Button, Flex, Typography } from '../atoms'; +import { width, WidthProps } from '../utils'; + +const Container = styled.div` + display: flex; + width: 320px; + padding: 0px; + justify-content: space-between; + align-items: center; + height: 45px; + ${width} +`; + +const InputField = styled.input` + display: flex; + padding: 12px 16px; + font-size: 14px; + align-items: center; + height:45px; + gap: 16px + border-color: transparent; + flex: 1 0 0; + background: ${({ theme }) => theme.palette.background.separatorSecondary}; + color: ${({ theme }) => theme.palette.text.white}; + + outline: none; + + &::placeholder { + color: ${({ theme }) => theme.palette.text.muted}; + } + + ::-webkit-input-placeholder { + color: ${({ theme }) => theme.palette.text.muted}; + } + border-radius: 8px 0px 0px 8px; + + border: 1px solid ${({ theme }) => theme.palette.border.separator}; + border-right: none; +`; + +const ApplyButton = styled.button` + border-radius: 0px 8px 8px 0px; + height: 45px; + color: ${({ theme }) => theme.palette.text.muted}; + background: ${({ theme }) => theme.palette.background.slateDark}; + padding: 12px 16px; + cursor: pointer; + border: none; + border: 1px solid ${({ theme }) => theme.palette.border.separator}; + border-color: transparent; +`; + +export interface CouponInputProps extends WidthProps { + isApplied: boolean; + value: string; + onApply: () => void; + onDelete: () => void; + onChange: (value: string) => void; + appliedText: string; + applyButtonText: string; + placeholderText: string; +} + +export const CouponInput: React.FC = ({ + isApplied, + value, + onApply, + onDelete, + onChange, + appliedText, + placeholderText, + applyButtonText, + ...restProps +}) => ( + + {!isApplied ? ( + <> + onChange(event.target.value)} + /> + {applyButtonText} + + ) : ( + + + + {appliedText} + + + {value} + + + + + + + ); +}; + +OTPInputDialog.defaultProps = { + wrongOtpError: undefined, + otpExpireTime: undefined, +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/components/index.ts b/packages/cysync-core/src/dialogs/Inheritance/components/index.ts new file mode 100644 index 000000000..392c2abe2 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/components/index.ts @@ -0,0 +1 @@ +export * from './OTPInputDialog'; diff --git a/packages/cysync-core/src/dialogs/Inheritance/index.ts b/packages/cysync-core/src/dialogs/Inheritance/index.ts index a8dfa8029..f9d784108 100644 --- a/packages/cysync-core/src/dialogs/Inheritance/index.ts +++ b/packages/cysync-core/src/dialogs/Inheritance/index.ts @@ -1 +1,2 @@ export * from './SyncPlans'; +export * from './PlanLogin'; diff --git a/packages/cysync-core/src/dialogs/index.tsx b/packages/cysync-core/src/dialogs/index.tsx index 89dd6e3ba..34194ec7d 100644 --- a/packages/cysync-core/src/dialogs/index.tsx +++ b/packages/cysync-core/src/dialogs/index.tsx @@ -19,7 +19,10 @@ import { EditAccountDialog } from './EditAccountDialog'; import { ErrorDialog } from './ErrorDialog'; import { GuidedFlow } from './GuidedFlow'; import { HistoryDialog } from './HistoryDialog'; -import { InheritanceSyncPlansDialog } from './Inheritance'; +import { + InheritancePlanLoginDialog, + InheritanceSyncPlansDialog, +} from './Inheritance'; import { ChangePasswordDialog, RemovePasswordDialog, @@ -59,4 +62,5 @@ export const dialogs: Record = { deleteAccount: DeleteAccountDialog, betaNotificationDialog: BetaNotificationDialog, inheritanceSyncPlans: InheritanceSyncPlansDialog, + inheritancePlanLogin: InheritancePlanLoginDialog, }; diff --git a/packages/cysync-core/src/hooks/useTabsAndDialogs.ts b/packages/cysync-core/src/hooks/useTabsAndDialogs.ts index eb0b98789..e9f5b56b5 100644 --- a/packages/cysync-core/src/hooks/useTabsAndDialogs.ts +++ b/packages/cysync-core/src/hooks/useTabsAndDialogs.ts @@ -28,7 +28,6 @@ export function useTabsAndDialogs({ }: IUseTabsAndDialogs) { const [currentTab, setCurrentTab] = useState(defaultTab); const [currentDialog, setCurrentDialog] = useState(defaultDialog); - const [isDeviceRequired, setIsDeviceRequired] = useState(false); const checkIfDeviceRequiredInDialog = useCallback( (tab: number, dialog: number) => { @@ -40,6 +39,10 @@ export function useTabsAndDialogs({ [deviceRequiredDialogsMap], ); + const [isDeviceRequired, setIsDeviceRequired] = useState( + checkIfDeviceRequiredInDialog(defaultTab, defaultDialog), + ); + const goTo = useCallback( (tab: number, dialog = 0) => { const _isDeviceRequired = checkIfDeviceRequiredInDialog(tab, dialog); diff --git a/packages/cysync-core/src/services/inheritance/index.ts b/packages/cysync-core/src/services/inheritance/index.ts index 27ef23d8f..aa0f08962 100644 --- a/packages/cysync-core/src/services/inheritance/index.ts +++ b/packages/cysync-core/src/services/inheritance/index.ts @@ -1 +1,2 @@ export * from './syncPlans'; +export * from './login'; diff --git a/packages/cysync-core/src/services/inheritance/login/index.ts b/packages/cysync-core/src/services/inheritance/login/index.ts new file mode 100644 index 000000000..cb2fad0c5 --- /dev/null +++ b/packages/cysync-core/src/services/inheritance/login/index.ts @@ -0,0 +1,44 @@ +import axios from 'axios'; + +import { config } from '~/config'; + +import { + resendResultSchema, + verifyResultSchema, + InheritanceLoginResendResponse, + InheritanceLoginVerifyResponse, +} from './schema'; + +import { runAndHandleServerErrors } from '../../utils'; + +export { + type InheritanceLoginResendResponse, + type InheritanceLoginVerifyResponse, +} from './schema'; + +const resendOTP = async (params: { requestId: string }) => + runAndHandleServerErrors(async () => { + const response = await axios.post(`${config.API_CYPHEROCK}/login/resend`, { + ...params, + }); + + const result = resendResultSchema.parse(response.data); + + return result; + }); + +const verify = async (params: { requestId: string; otp: string }) => + runAndHandleServerErrors(async () => { + const response = await axios.post(`${config.API_CYPHEROCK}/logn/verify`, { + ...params, + }); + + const result = verifyResultSchema.parse(response.data); + + return result; + }); + +export const inheritanceLoginService = { + resendOTP, + verify, +}; diff --git a/packages/cysync-core/src/services/inheritance/login/schema.ts b/packages/cysync-core/src/services/inheritance/login/schema.ts new file mode 100644 index 000000000..d14afcd4f --- /dev/null +++ b/packages/cysync-core/src/services/inheritance/login/schema.ts @@ -0,0 +1,17 @@ +import { z } from 'zod'; + +export const resendResultSchema = z.object({ + requestId: z.string(), + maskedEmail: z.string(), + retriesRemaining: z.number(), + otpExpiry: z.string().datetime(), +}); + +export const verifyResultSchema = z.object({ + isSuccess: z.boolean(), + retriesRemaining: z.number().optional(), + otpExpiry: z.string().datetime().optional(), +}); + +export type InheritanceLoginResendResponse = z.infer; +export type InheritanceLoginVerifyResponse = z.infer; diff --git a/packages/cysync-core/src/services/inheritance/syncPlans.ts b/packages/cysync-core/src/services/inheritance/syncPlans/index.ts similarity index 96% rename from packages/cysync-core/src/services/inheritance/syncPlans.ts rename to packages/cysync-core/src/services/inheritance/syncPlans/index.ts index 20e12e391..be1f3b10d 100644 --- a/packages/cysync-core/src/services/inheritance/syncPlans.ts +++ b/packages/cysync-core/src/services/inheritance/syncPlans/index.ts @@ -11,7 +11,7 @@ import { InheritanceSyncPlansVerifyResponse, } from './schema'; -import { runAndHandleServerErrors } from '../utils'; +import { runAndHandleServerErrors } from '../../utils'; export { type InheritanceSyncPlansResendResponse, diff --git a/packages/cysync-core/src/services/inheritance/schema.ts b/packages/cysync-core/src/services/inheritance/syncPlans/schema.ts similarity index 100% rename from packages/cysync-core/src/services/inheritance/schema.ts rename to packages/cysync-core/src/services/inheritance/syncPlans/schema.ts diff --git a/packages/cysync-core/src/store/dialog/index.ts b/packages/cysync-core/src/store/dialog/index.ts index 27f5c3297..dce735568 100644 --- a/packages/cysync-core/src/store/dialog/index.ts +++ b/packages/cysync-core/src/store/dialog/index.ts @@ -89,6 +89,9 @@ const initialState: IDialogState = { inheritanceSyncPlans: { isOpen: false, }, + inheritancePlanLogin: { + isOpen: false, + }, }; export const dialogSlice = createSlice({ diff --git a/packages/cysync-core/src/store/dialog/types.ts b/packages/cysync-core/src/store/dialog/types.ts index 1465be553..3e64c4683 100644 --- a/packages/cysync-core/src/store/dialog/types.ts +++ b/packages/cysync-core/src/store/dialog/types.ts @@ -137,6 +137,11 @@ export interface IDialogState { isOpen: boolean; data?: undefined; }; + + inheritancePlanLogin: { + isOpen: boolean; + data?: undefined; + }; } export const GuidedFlowMap = { From 1002199328908882035dc133e38d6c40c1a546cf Mon Sep 17 00:00:00 2001 From: Irshad Ansari <40335357+irshadjsr21@users.noreply.github.com> Date: Thu, 1 Aug 2024 12:31:53 +0530 Subject: [PATCH 13/96] feat: inheritance silver plan purchase ui (#513) * feat: added inheritance silver plan purchase ui till select wallets * feat: inheritance silver plan purchase ui without checkout --- .../cysync-core-constants/src/constants.ts | 3 + .../src/i18n/lang/ar-AE.json | 79 ++++- .../src/i18n/lang/de-DE.json | 79 ++++- .../src/i18n/lang/en.json | 79 ++++- .../src/i18n/lang/id-ID.json | 79 ++++- .../src/i18n/lang/zh-CN.json | 79 ++++- .../cysync-core-constants/src/i18n/types.ts | 77 +++++ .../cysync-core/src/actions/dialog/index.ts | 3 + .../Dialogs/Email/UserDetails.tsx | 134 +++++++++ .../Dialogs/Email/VerifyOTP.tsx | 62 ++++ .../SilverPlanPurchase/Dialogs/Email/index.ts | 2 + .../Dialogs/Encryption/Device.tsx | 89 ++++++ .../Dialogs/Encryption/Loader.tsx | 47 +++ .../Dialogs/Encryption/Success.tsx | 23 ++ .../Dialogs/Encryption/index.tsx | 3 + .../SilverPlanPurchase/Dialogs/Ensure.tsx | 64 ++++ .../Dialogs/Instructions.tsx | 56 ++++ .../Dialogs/SelectWallet.tsx | 71 +++++ .../SilverPlanPurchase/Dialogs/Terms.tsx | 61 ++++ .../SilverPlanPurchase/Dialogs/WalletAuth.tsx | 94 ++++++ .../SilverPlanPurchase/Dialogs/index.tsx | 7 + .../Inheritance/SilverPlanPurchase/Layout.tsx | 31 ++ .../SilverPlanPurchase/context/index.tsx | 224 ++++++++++++++ .../Inheritance/SilverPlanPurchase/index.tsx | 108 +++++++ .../Inheritance/components/OTPInputDialog.tsx | 284 ++++++++++-------- .../src/dialogs/Inheritance/index.ts | 1 + packages/cysync-core/src/dialogs/index.tsx | 2 + .../src/pages/OnBoarding/Terms.tsx | 44 +-- .../cysync-core/src/store/dialog/index.ts | 3 + .../cysync-core/src/store/dialog/types.ts | 5 + packages/ui/src/components/GlobalStyles.tsx | 58 ++++ .../ui/src/components/atoms/ExternalLink.tsx | 49 +++ .../ui/src/components/atoms/Input/Input.tsx | 7 +- .../src/components/atoms/Input/InputLabel.tsx | 2 +- packages/ui/src/components/atoms/index.ts | 1 + .../src/components/molecules/ManyInMany.tsx | 30 +- .../ui/src/components/utils/flex.styled.ts | 15 + .../ui/src/components/utils/font.styled.ts | 9 + .../stories/atoms/ExternalLink.stories.tsx | 20 ++ 39 files changed, 1902 insertions(+), 182 deletions(-) create mode 100644 packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Email/UserDetails.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Email/VerifyOTP.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Email/index.ts create mode 100644 packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Encryption/Device.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Encryption/Loader.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Encryption/Success.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Encryption/index.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Ensure.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Instructions.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/SelectWallet.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Terms.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/WalletAuth.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/index.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Layout.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/context/index.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/index.tsx create mode 100644 packages/ui/src/components/atoms/ExternalLink.tsx create mode 100644 packages/ui/src/stories/atoms/ExternalLink.stories.tsx diff --git a/packages/cysync-core-constants/src/constants.ts b/packages/cysync-core-constants/src/constants.ts index 24c382072..5b3db9b23 100644 --- a/packages/cysync-core-constants/src/constants.ts +++ b/packages/cysync-core-constants/src/constants.ts @@ -7,4 +7,7 @@ export const constants = { termsOfUseLink: 'https://www.cypherock.com/terms', tutorialLink: 'https://youtube.com/playlist?list=PL0db5IfQ4iyo7GxBtFVBxxvanAngjdjLn&feature=shared', + inheritance: { + silverPlanPurchaseTutorialLink: 'https://youtu.be/bRmNxrOf3VM', + }, }; diff --git a/packages/cysync-core-constants/src/i18n/lang/ar-AE.json b/packages/cysync-core-constants/src/i18n/lang/ar-AE.json index 859685482..e1e84b1ae 100644 --- a/packages/cysync-core-constants/src/i18n/lang/ar-AE.json +++ b/packages/cysync-core-constants/src/i18n/lang/ar-AE.json @@ -45,7 +45,8 @@ "editAccount": "تحرير الحساب", "submit": "إرسال", "showMore": "عرض المزيد", - "resendOTP": "Resend OTP" + "resendOTP": "Resend OTP", + "next": "Next" }, "tooltips": { "downloadCsv": "تصدير كملف CSV" @@ -1600,5 +1601,81 @@ "title": "You have 0 retries remaining", "subTitle": "Please exit the flow and restart" } + }, + "inheritance": { + "termsOfService": { + "title": "Terms of Service", + "privacyPolicy": "Privacy Policy", + "checkBoxLabel": "I have read and agree to Terms of use and Privacy policy" + } + }, + "inheritanceSilverPlanPurchase": { + "title": "Silver Plan Setup", + "ensure": { + "title": "Ensure the following before you continue", + "instructions": [ + "Make sure you have 15-30 minutes to set it up", + "You are present in a safe environment", + "You need to have one device and one card handy to set it up" + ] + }, + "instructions": { + "heading": "Instructions", + "title": "Watch the whole video before you proceed", + "subTitle": "The video will help you take the right decisions" + }, + "selectWallet": { + "heading": "Select Wallet", + "title": "Select the wallet you want to cover in this plan", + "subTitle": "Your plan will be linked with the selected wallet" + }, + "walletAuth": { + "heading": "Wallet Authentication", + "title": "Follow instructions on the X1 Vault to authenticate the selected wallet", + "actions": { + "tapCard": "Tap any one card" + }, + "footer": "Authenticating the wallet, " + }, + "email": { + "heading": "Email", + "userDetails": { + "title": "User Details", + "subTitle": "All the fields are mandatory", + "form": { + "name": "Name", + "email": "Primary Email", + "alternateEmail": "Alternate Email" + }, + "buttons": { + "sendOTP": "Send OTP" + } + }, + "primaryEmailOTP": { + "title": "OTP Verification for primary email" + }, + "alternateEmailOTP": { + "title": "OTP Verification for alternate email" + } + }, + "encryption": { + "heading": "Encryption", + "device": { + "title": "Follow instructions on the X1 Vault to encrypt the wallet PIN", + "actions": { + "tapCard": "Enter PIN and tap any one card" + }, + "messageBox": { + "warning": "The PIN will be encrypted by the X1 card" + } + }, + "loading": { + "title": "Please wait while we encrypt the data", + "subTitle": "Do not disconnect or close the app" + }, + "success": { + "title": "PIN recovery message successfully encrypted" + } + } } } diff --git a/packages/cysync-core-constants/src/i18n/lang/de-DE.json b/packages/cysync-core-constants/src/i18n/lang/de-DE.json index 4167c7149..efd0f9e60 100644 --- a/packages/cysync-core-constants/src/i18n/lang/de-DE.json +++ b/packages/cysync-core-constants/src/i18n/lang/de-DE.json @@ -45,7 +45,8 @@ "editAccount": "Konto bearbeiten", "submit": "Absenden", "showMore": "Mehr anzeigen", - "resendOTP": "Resend OTP" + "resendOTP": "Resend OTP", + "next": "Next" }, "tooltips": { "downloadCsv": "Als CSV exportieren" @@ -1605,5 +1606,81 @@ "title": "You have 0 retries remaining", "subTitle": "Please exit the flow and restart" } + }, + "inheritance": { + "termsOfService": { + "title": "Terms of Service", + "privacyPolicy": "Privacy Policy", + "checkBoxLabel": "I have read and agree to Terms of use and Privacy policy" + } + }, + "inheritanceSilverPlanPurchase": { + "title": "Silver Plan Setup", + "ensure": { + "title": "Ensure the following before you continue", + "instructions": [ + "Make sure you have 15-30 minutes to set it up", + "You are present in a safe environment", + "You need to have one device and one card handy to set it up" + ] + }, + "instructions": { + "heading": "Instructions", + "title": "Watch the whole video before you proceed", + "subTitle": "The video will help you take the right decisions" + }, + "selectWallet": { + "heading": "Select Wallet", + "title": "Select the wallet you want to cover in this plan", + "subTitle": "Your plan will be linked with the selected wallet" + }, + "walletAuth": { + "heading": "Wallet Authentication", + "title": "Follow instructions on the X1 Vault to authenticate the selected wallet", + "actions": { + "tapCard": "Tap any one card" + }, + "footer": "Authenticating the wallet, " + }, + "email": { + "heading": "Email", + "userDetails": { + "title": "User Details", + "subTitle": "All the fields are mandatory", + "form": { + "name": "Name", + "email": "Primary Email", + "alternateEmail": "Alternate Email" + }, + "buttons": { + "sendOTP": "Send OTP" + } + }, + "primaryEmailOTP": { + "title": "OTP Verification for primary email" + }, + "alternateEmailOTP": { + "title": "OTP Verification for alternate email" + } + }, + "encryption": { + "heading": "Encryption", + "device": { + "title": "Follow instructions on the X1 Vault to encrypt the wallet PIN", + "actions": { + "tapCard": "Enter PIN and tap any one card" + }, + "messageBox": { + "warning": "The PIN will be encrypted by the X1 card" + } + }, + "loading": { + "title": "Please wait while we encrypt the data", + "subTitle": "Do not disconnect or close the app" + }, + "success": { + "title": "PIN recovery message successfully encrypted" + } + } } } diff --git a/packages/cysync-core-constants/src/i18n/lang/en.json b/packages/cysync-core-constants/src/i18n/lang/en.json index f2e1f22c3..d87f8c5af 100644 --- a/packages/cysync-core-constants/src/i18n/lang/en.json +++ b/packages/cysync-core-constants/src/i18n/lang/en.json @@ -45,7 +45,8 @@ "editAccount": "Edit Account", "submit": "Submit", "showMore": "Show more", - "resendOTP": "Resend OTP" + "resendOTP": "Resend OTP", + "next": "Next" }, "tooltips": { "downloadCsv": "Export as CSV" @@ -1605,5 +1606,81 @@ "title": "You have 0 retries remaining", "subTitle": "Please exit the flow and restart" } + }, + "inheritance": { + "termsOfService": { + "title": "Terms of Service", + "privacyPolicy": "Privacy Policy", + "checkBoxLabel": "I have read and agree to Terms of use and Privacy policy" + } + }, + "inheritanceSilverPlanPurchase": { + "title": "Silver Plan Setup", + "ensure": { + "title": "Ensure the following before you continue", + "instructions": [ + "Make sure you have 15-30 minutes to set it up", + "You are present in a safe environment", + "You need to have one device and one card handy to set it up" + ] + }, + "instructions": { + "heading": "Instructions", + "title": "Watch the whole video before you proceed", + "subTitle": "The video will help you take the right decisions" + }, + "selectWallet": { + "heading": "Select Wallet", + "title": "Select the wallet you want to cover in this plan", + "subTitle": "Your plan will be linked with the selected wallet" + }, + "walletAuth": { + "heading": "Wallet Authentication", + "title": "Follow instructions on the X1 Vault to authenticate the selected wallet", + "actions": { + "tapCard": "Tap any one card" + }, + "footer": "Authenticating the wallet, " + }, + "email": { + "heading": "Email", + "userDetails": { + "title": "User Details", + "subTitle": "All the fields are mandatory", + "form": { + "name": "Name", + "email": "Primary Email", + "alternateEmail": "Alternate Email" + }, + "buttons": { + "sendOTP": "Send OTP" + } + }, + "primaryEmailOTP": { + "title": "OTP Verification for primary email" + }, + "alternateEmailOTP": { + "title": "OTP Verification for alternate email" + } + }, + "encryption": { + "heading": "Encryption", + "device": { + "title": "Follow instructions on the X1 Vault to encrypt the wallet PIN", + "actions": { + "tapCard": "Enter PIN and tap any one card" + }, + "messageBox": { + "warning": "The PIN will be encrypted by the X1 card" + } + }, + "loading": { + "title": "Please wait while we encrypt the data", + "subTitle": "Do not disconnect or close the app" + }, + "success": { + "title": "PIN recovery message successfully encrypted" + } + } } } diff --git a/packages/cysync-core-constants/src/i18n/lang/id-ID.json b/packages/cysync-core-constants/src/i18n/lang/id-ID.json index 64db6122d..70dce8796 100644 --- a/packages/cysync-core-constants/src/i18n/lang/id-ID.json +++ b/packages/cysync-core-constants/src/i18n/lang/id-ID.json @@ -45,7 +45,8 @@ "editAccount": "Sunting Akun", "submit": "Kirim", "showMore": "Tampilkan lebih banyak", - "resendOTP": "Resend OTP" + "resendOTP": "Resend OTP", + "next": "Next" }, "tooltips": { "downloadCsv": "Ekspor sebagai CSV" @@ -1605,5 +1606,81 @@ "title": "You have 0 retries remaining", "subTitle": "Please exit the flow and restart" } + }, + "inheritance": { + "termsOfService": { + "title": "Terms of Service", + "privacyPolicy": "Privacy Policy", + "checkBoxLabel": "I have read and agree to Terms of use and Privacy policy" + } + }, + "inheritanceSilverPlanPurchase": { + "title": "Silver Plan Setup", + "ensure": { + "title": "Ensure the following before you continue", + "instructions": [ + "Make sure you have 15-30 minutes to set it up", + "You are present in a safe environment", + "You need to have one device and one card handy to set it up" + ] + }, + "instructions": { + "heading": "Instructions", + "title": "Watch the whole video before you proceed", + "subTitle": "The video will help you take the right decisions" + }, + "selectWallet": { + "heading": "Select Wallet", + "title": "Select the wallet you want to cover in this plan", + "subTitle": "Your plan will be linked with the selected wallet" + }, + "walletAuth": { + "heading": "Wallet Authentication", + "title": "Follow instructions on the X1 Vault to authenticate the selected wallet", + "actions": { + "tapCard": "Tap any one card" + }, + "footer": "Authenticating the wallet, " + }, + "email": { + "heading": "Email", + "userDetails": { + "title": "User Details", + "subTitle": "All the fields are mandatory", + "form": { + "name": "Name", + "email": "Primary Email", + "alternateEmail": "Alternate Email" + }, + "buttons": { + "sendOTP": "Send OTP" + } + }, + "primaryEmailOTP": { + "title": "OTP Verification for primary email" + }, + "alternateEmailOTP": { + "title": "OTP Verification for alternate email" + } + }, + "encryption": { + "heading": "Encryption", + "device": { + "title": "Follow instructions on the X1 Vault to encrypt the wallet PIN", + "actions": { + "tapCard": "Enter PIN and tap any one card" + }, + "messageBox": { + "warning": "The PIN will be encrypted by the X1 card" + } + }, + "loading": { + "title": "Please wait while we encrypt the data", + "subTitle": "Do not disconnect or close the app" + }, + "success": { + "title": "PIN recovery message successfully encrypted" + } + } } } diff --git a/packages/cysync-core-constants/src/i18n/lang/zh-CN.json b/packages/cysync-core-constants/src/i18n/lang/zh-CN.json index 1c7d47b29..4d90b37cf 100644 --- a/packages/cysync-core-constants/src/i18n/lang/zh-CN.json +++ b/packages/cysync-core-constants/src/i18n/lang/zh-CN.json @@ -45,7 +45,8 @@ "editAccount": "编辑账户", "submit": "提交", "showMore": "显示更多", - "resendOTP": "Resend OTP" + "resendOTP": "Resend OTP", + "next": "Next" }, "tooltips": { "downloadCsv": "导出为CSV" @@ -1580,5 +1581,81 @@ "title": "You have 0 retries remaining", "subTitle": "Please exit the flow and restart" } + }, + "inheritance": { + "termsOfService": { + "title": "Terms of Service", + "privacyPolicy": "Privacy Policy", + "checkBoxLabel": "I have read and agree to Terms of use and Privacy policy" + } + }, + "inheritanceSilverPlanPurchase": { + "title": "Silver Plan Setup", + "ensure": { + "title": "Ensure the following before you continue", + "instructions": [ + "Make sure you have 15-30 minutes to set it up", + "You are present in a safe environment", + "You need to have one device and one card handy to set it up" + ] + }, + "instructions": { + "heading": "Instructions", + "title": "Watch the whole video before you proceed", + "subTitle": "The video will help you take the right decisions" + }, + "selectWallet": { + "heading": "Select Wallet", + "title": "Select the wallet you want to cover in this plan", + "subTitle": "Your plan will be linked with the selected wallet" + }, + "walletAuth": { + "heading": "Wallet Authentication", + "title": "Follow instructions on the X1 Vault to authenticate the selected wallet", + "actions": { + "tapCard": "Tap any one card" + }, + "footer": "Authenticating the wallet, " + }, + "email": { + "heading": "Email", + "userDetails": { + "title": "User Details", + "subTitle": "All the fields are mandatory", + "form": { + "name": "Name", + "email": "Primary Email", + "alternateEmail": "Alternate Email" + }, + "buttons": { + "sendOTP": "Send OTP" + } + }, + "primaryEmailOTP": { + "title": "OTP Verification for primary email" + }, + "alternateEmailOTP": { + "title": "OTP Verification for alternate email" + } + }, + "encryption": { + "heading": "Encryption", + "device": { + "title": "Follow instructions on the X1 Vault to encrypt the wallet PIN", + "actions": { + "tapCard": "Enter PIN and tap any one card" + }, + "messageBox": { + "warning": "The PIN will be encrypted by the X1 card" + } + }, + "loading": { + "title": "Please wait while we encrypt the data", + "subTitle": "Do not disconnect or close the app" + }, + "success": { + "title": "PIN recovery message successfully encrypted" + } + } } } diff --git a/packages/cysync-core-constants/src/i18n/types.ts b/packages/cysync-core-constants/src/i18n/types.ts index a404aafd5..a3fb3e2fe 100644 --- a/packages/cysync-core-constants/src/i18n/types.ts +++ b/packages/cysync-core-constants/src/i18n/types.ts @@ -37,6 +37,8 @@ export interface LanguageStrings { snackbar: LangSnackbar; settings: LangSettings; otp: LangOTP; + inheritance: LangInheritance; + inheritanceSilverPlanPurchase: LangInheritanceSilverPlanPurchase; } interface LangLabels { @@ -83,6 +85,7 @@ interface LangButtons { submit: string; showMore: string; resendOTP: string; + next: string; } interface LangTooltips { @@ -830,3 +833,77 @@ interface LangOTP { subTitle: string; }; } + +interface LangInheritance { + termsOfService: { + title: string; + privacyPolicy: string; + checkBoxLabel: string; + }; +} + +interface LangInheritanceSilverPlanPurchase { + title: string; + ensure: { + title: string; + instructions: string[]; + }; + instructions: { + heading: string; + title: string; + subTitle: string; + }; + selectWallet: { + heading: string; + title: string; + subTitle: string; + }; + walletAuth: { + heading: string; + title: string; + actions: { + tapCard: string; + }; + footer: string; + }; + email: { + heading: string; + userDetails: { + title: string; + subTitle: string; + form: { + name: string; + email: string; + alternateEmail: string; + }; + buttons: { + sendOTP: string; + }; + }; + primaryEmailOTP: { + title: string; + }; + alternateEmailOTP: { + title: string; + }; + }; + encryption: { + heading: string; + device: { + title: string; + actions: { + tapCard: string; + }; + messageBox: { + warning: string; + }; + }; + loading: { + title: string; + subTitle: string; + }; + success: { + title: string; + }; + }; +} diff --git a/packages/cysync-core/src/actions/dialog/index.ts b/packages/cysync-core/src/actions/dialog/index.ts index 2c14dfaf0..627cc7997 100644 --- a/packages/cysync-core/src/actions/dialog/index.ts +++ b/packages/cysync-core/src/actions/dialog/index.ts @@ -91,3 +91,6 @@ export const openInheritanceSyncPlansDialog = () => export const openInheritancePlanLoginDialog = () => openDialog({ name: 'inheritancePlanLogin', data: undefined }); + +export const openInheritanceSilverPlanPurchaseDialog = () => + openDialog({ name: 'inheritanceSilverPlanPurchase', data: undefined }); diff --git a/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Email/UserDetails.tsx b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Email/UserDetails.tsx new file mode 100644 index 000000000..cceb53c8e --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Email/UserDetails.tsx @@ -0,0 +1,134 @@ +import { + Button, + Container, + Input, + LangDisplay, + Typography, +} from '@cypherock/cysync-ui'; +import React, { useState } from 'react'; + +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritanceSilverPlanPurchaseDialog } from '../../context'; +import { Layout } from '../../Layout'; + +export const UserDetails = () => { + const lang = useAppSelector(selectLanguage); + + const strings = lang.strings.inheritanceSilverPlanPurchase.email.userDetails; + + const [name, setName] = useState(''); + const [email, setEmail] = useState(''); + const [alternateEmail, setAlternateEmail] = useState(''); + + const { onUserDetailsSubmit, onPrevious, isSubmittingUserDetails } = + useInheritanceSilverPlanPurchaseDialog(); + + const formId = 'inheritance-silver-plan-user-details'; + + const onSubmit = (e: React.FormEvent) => { + e.preventDefault(); + + if (isSubmittingUserDetails) return; + + onUserDetailsSubmit({ + name, + email, + alternateEmail, + }); + }; + + return ( + + + + + } + > +
+ + + + + + + + + + + + + + + +
+
+ ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Email/VerifyOTP.tsx b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Email/VerifyOTP.tsx new file mode 100644 index 000000000..3ab4f64e5 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Email/VerifyOTP.tsx @@ -0,0 +1,62 @@ +import React, { useMemo, useRef, useState } from 'react'; + +import { + OTPInputDialog, + OTPInputDialogRef, +} from '~/dialogs/Inheritance/components'; +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritanceSilverPlanPurchaseDialog } from '../../context'; + +export const VerifyOTP: React.FC = () => { + const lang = useAppSelector(selectLanguage); + const strings = lang.strings.inheritanceSilverPlanPurchase.email; + + const { onClose, onPrevious, userDetails, onNext } = + useInheritanceSilverPlanPurchaseDialog(); + + const [email, setEmail] = useState(userDetails?.email ?? ''); + const [title, setTitle] = useState(strings.primaryEmailOTP.title); + + const otpRef = useRef(null); + + const onVerify = () => { + // DUMMY FUNCTION + if (email === userDetails?.alternateEmail) { + onNext(); + return; + } + + setEmail(userDetails?.alternateEmail ?? ''); + setTitle(strings.alternateEmailOTP.title); + otpRef.current?.reset(); + }; + + const onResend = () => { + // DUMMY FUNCTION + }; + + const otpExpireTime = useMemo( + () => new Date(Date.now() + 60 * 1000).toISOString(), + [email], + ); + const otpLength = 6; + const retriesRemaining = 3; + + return ( + + ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Email/index.ts b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Email/index.ts new file mode 100644 index 000000000..549cfb854 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Email/index.ts @@ -0,0 +1,2 @@ +export * from './UserDetails'; +export * from './VerifyOTP'; diff --git a/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Encryption/Device.tsx b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Encryption/Device.tsx new file mode 100644 index 000000000..b2a925614 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Encryption/Device.tsx @@ -0,0 +1,89 @@ +import { SignTransactionDeviceEvent } from '@cypherock/coin-support-interfaces'; +import { + ArrowRightIcon, + Check, + Container, + LeanBox, + LeanBoxContainer, + LeanBoxProps, + MessageBox, + Throbber, + Typography, +} from '@cypherock/cysync-ui'; +import React, { useEffect } from 'react'; + +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritanceSilverPlanPurchaseDialog } from '../../context'; +import { Layout } from '../../Layout'; + +const checkIconComponent = ; +const throbberComponent = ; +const rightArrowIcon = ; + +export const DeviceEncryption = () => { + const lang = useAppSelector(selectLanguage); + + const strings = lang.strings.inheritanceSilverPlanPurchase.encryption.device; + + const { onNext } = useInheritanceSilverPlanPurchaseDialog(); + + const deviceEvents: Record = { + 0: true, + }; + + const getDeviceEventIcon = ( + loadingEvent: SignTransactionDeviceEvent, + completedEvent: SignTransactionDeviceEvent, + ) => { + if (deviceEvents[completedEvent]) return checkIconComponent; + if (deviceEvents[loadingEvent]) return throbberComponent; + + return undefined; + }; + + const actionsList = React.useMemo(() => { + const actions: LeanBoxProps[] = [ + { + id: '1', + text: strings.actions.tapCard, + leftImage: rightArrowIcon, + rightImage: getDeviceEventIcon(0, 1), + }, + ]; + + return actions; + }, []); + + useEffect(() => { + const timeout = setTimeout(() => { + onNext(); + }, 2000); + + return () => clearTimeout(timeout); + }, []); + + return ( + + + + {strings.title} + + + {actionsList.map(data => ( + + ))} + + + + + ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Encryption/Loader.tsx b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Encryption/Loader.tsx new file mode 100644 index 000000000..8ff5b7c52 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Encryption/Loader.tsx @@ -0,0 +1,47 @@ +import { + DialogBox, + Flex, + Image, + loaderGrayIcon, + Typography, +} from '@cypherock/cysync-ui'; +import React, { useEffect } from 'react'; + +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritanceSilverPlanPurchaseDialog } from '../../context'; + +export const EncryptionLoader: React.FC = () => { + const lang = useAppSelector(selectLanguage); + const { onNext } = useInheritanceSilverPlanPurchaseDialog(); + + const strings = lang.strings.inheritanceSilverPlanPurchase.encryption.loading; + + useEffect(() => { + const timeout = setTimeout(() => { + onNext(); + }, 2000); + + return () => clearTimeout(timeout); + }, []); + + return ( + + Loader icon + + + {strings.title} + + + {strings.subTitle} + + + + ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Encryption/Success.tsx b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Encryption/Success.tsx new file mode 100644 index 000000000..37f5b621b --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Encryption/Success.tsx @@ -0,0 +1,23 @@ +import { SuccessDialog } from '@cypherock/cysync-ui'; +import React from 'react'; + +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritanceSilverPlanPurchaseDialog } from '../../context'; + +export const EncryptionSuccess: React.FC = () => { + const { onClose } = useInheritanceSilverPlanPurchaseDialog(); + + const lang = useAppSelector(selectLanguage); + + return ( + + ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Encryption/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Encryption/index.tsx new file mode 100644 index 000000000..95ba8a4ea --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Encryption/index.tsx @@ -0,0 +1,3 @@ +export * from './Device'; +export * from './Loader'; +export * from './Success'; diff --git a/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Ensure.tsx b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Ensure.tsx new file mode 100644 index 000000000..d087c0546 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Ensure.tsx @@ -0,0 +1,64 @@ +import { + Button, + Container, + LangDisplay, + Typography, + ListContainer, + ListItem, +} from '@cypherock/cysync-ui'; +import React from 'react'; + +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritanceSilverPlanPurchaseDialog } from '../context'; +import { Layout } from '../Layout'; + +export const Ensure = () => { + const lang = useAppSelector(selectLanguage); + + const strings = lang.strings.inheritanceSilverPlanPurchase; + + const { onNext, onPrevious } = useInheritanceSilverPlanPurchaseDialog(); + + return ( + + + + + } + > + + + + + + {strings.ensure.instructions.map((listItem, index) => ( + + + + + + ))} + + + + ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Instructions.tsx b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Instructions.tsx new file mode 100644 index 000000000..bc23a93be --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Instructions.tsx @@ -0,0 +1,56 @@ +import { constants } from '@cypherock/cysync-core-constants'; +import { + Button, + Container, + LangDisplay, + Typography, +} from '@cypherock/cysync-ui'; +import React from 'react'; +import ReactPlayer from 'react-player/youtube'; + +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritanceSilverPlanPurchaseDialog } from '../context'; +import { Layout } from '../Layout'; + +export const Instructions = () => { + const lang = useAppSelector(selectLanguage); + + const strings = lang.strings.inheritanceSilverPlanPurchase; + + const { onNext, onPrevious } = useInheritanceSilverPlanPurchaseDialog(); + + return ( + + + + + } + > + + + + + + + + + + + ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/SelectWallet.tsx b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/SelectWallet.tsx new file mode 100644 index 000000000..248b8a943 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/SelectWallet.tsx @@ -0,0 +1,71 @@ +import { + Button, + Container, + LangDisplay, + ManyInMany, + ScrollableContainer, + Typography, +} from '@cypherock/cysync-ui'; +import React from 'react'; + +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritanceSilverPlanPurchaseDialog } from '../context'; +import { Layout } from '../Layout'; + +export const SelectWallet = () => { + const lang = useAppSelector(selectLanguage); + + const strings = lang.strings.inheritanceSilverPlanPurchase; + + const { onNext, onPrevious, allWallets, selectedWallet, setSelectedWallet } = + useInheritanceSilverPlanPurchaseDialog(); + + return ( + + + + + } + > + + + + + + + + + + + {allWallets.map(wallet => ( + setSelectedWallet(wallet)} + $width={340} + $height={128} + /> + ))} + + + + ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Terms.tsx b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Terms.tsx new file mode 100644 index 000000000..58221e8ec --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Terms.tsx @@ -0,0 +1,61 @@ +import { + Button, + CheckBox, + Container, + Flex, + LangDisplay, + Typography, + ExternalLink, +} from '@cypherock/cysync-ui'; +import React from 'react'; + +import { constants } from '~/constants'; +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritanceSilverPlanPurchaseDialog } from '../context'; +import { Layout } from '../Layout'; + +export const Terms = () => { + const [isChecked, setIsChecked] = React.useState(false); + const lang = useAppSelector(selectLanguage); + + const strings = lang.strings.inheritance.termsOfService; + + const { onNext } = useInheritanceSilverPlanPurchaseDialog(); + + const toNextPage = async () => { + if (isChecked) onNext(); + }; + + return ( + toNextPage()} + > + + + } + > + + + + + + + + + setIsChecked(!isChecked)} + id="privacy_policy_accepted" + label={strings.checkBoxLabel} + /> + + ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/WalletAuth.tsx b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/WalletAuth.tsx new file mode 100644 index 000000000..fedc2f6a1 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/WalletAuth.tsx @@ -0,0 +1,94 @@ +import { SignTransactionDeviceEvent } from '@cypherock/coin-support-interfaces'; +import { + ArrowRightIcon, + Check, + Container, + LangDisplay, + LeanBox, + LeanBoxContainer, + LeanBoxProps, + Throbber, + Typography, +} from '@cypherock/cysync-ui'; +import React, { useEffect } from 'react'; + +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritanceSilverPlanPurchaseDialog } from '../context'; +import { Layout } from '../Layout'; + +const checkIconComponent = ; +const throbberComponent = ; +const rightArrowIcon = ; + +export const WalletAuth = () => { + const lang = useAppSelector(selectLanguage); + + const strings = lang.strings.inheritanceSilverPlanPurchase; + + const { onNext, selectedWallet } = useInheritanceSilverPlanPurchaseDialog(); + + const deviceEvents: Record = { + 0: true, + }; + + const getDeviceEventIcon = ( + loadingEvent: SignTransactionDeviceEvent, + completedEvent: SignTransactionDeviceEvent, + ) => { + if (deviceEvents[completedEvent]) return checkIconComponent; + if (deviceEvents[loadingEvent]) return throbberComponent; + + return undefined; + }; + + const actionsList = React.useMemo(() => { + const actions: LeanBoxProps[] = [ + { + id: '1', + text: strings.walletAuth.actions.tapCard, + leftImage: rightArrowIcon, + rightImage: getDeviceEventIcon(0, 1), + }, + ]; + + return actions; + }, []); + + useEffect(() => { + const timeout = setTimeout(() => { + onNext(); + }, 2000); + + return () => clearTimeout(timeout); + }, []); + + return ( + + + + {strings.walletAuth.title} + + + {actionsList.map(data => ( + + ))} + + + + + {selectedWallet?.name} + + + + + ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/index.tsx new file mode 100644 index 000000000..c4a2a4a71 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/index.tsx @@ -0,0 +1,7 @@ +export * from './Terms'; +export * from './Ensure'; +export * from './Instructions'; +export * from './SelectWallet'; +export * from './WalletAuth'; +export * from './Email'; +export * from './Encryption'; diff --git a/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Layout.tsx b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Layout.tsx new file mode 100644 index 000000000..eb50005af --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Layout.tsx @@ -0,0 +1,31 @@ +import { + DialogBox, + DialogBoxBody, + DialogBoxFooter, +} from '@cypherock/cysync-ui'; +import React from 'react'; + +export interface LayoutProps { + children: React.ReactNode; + footerComponent?: React.ReactNode; +} + +export const Layout: React.FC = ({ + children, + footerComponent, +}) => ( + + + {children} + + {footerComponent && ( + + {footerComponent} + + )} + +); + +Layout.defaultProps = { + footerComponent: undefined, +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/context/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/context/index.tsx new file mode 100644 index 000000000..9414fac8d --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/context/index.tsx @@ -0,0 +1,224 @@ +import { sleep } from '@cypherock/cysync-utils'; +import { IWallet } from '@cypherock/db-interfaces'; +import React, { + Context, + FC, + ReactNode, + createContext, + useCallback, + useContext, + useMemo, + useState, +} from 'react'; + +import { ITabs, useTabsAndDialogs } from '~/hooks'; +import { + closeDialog, + selectLanguage, + useAppDispatch, + useAppSelector, +} from '~/store'; + +import { + DeviceEncryption, + EncryptionLoader, + EncryptionSuccess, + Ensure, + Instructions, + SelectWallet, + Terms, + UserDetails, + VerifyOTP, + WalletAuth, +} from '../Dialogs'; + +export interface IWalletWithDeleted extends IWallet { + isDeleted?: boolean; +} + +export interface IUserDetails { + name: string; + email: string; + alternateEmail: string; +} + +export interface InheritanceSilverPlanPurchaseDialogContextInterface { + tabs: ITabs; + onNext: (tab?: number, dialog?: number) => void; + goTo: (tab: number, dialog?: number) => void; + onPrevious: () => void; + currentTab: number; + currentDialog: number; + isDeviceRequired: boolean; + onClose: () => void; + allWallets: IWalletWithDeleted[]; + selectedWallet?: IWalletWithDeleted; + setSelectedWallet: (wallet: IWalletWithDeleted) => void; + onUserDetailsSubmit: (params: IUserDetails) => void; + isSubmittingUserDetails: boolean; + userDetails?: IUserDetails; + unhandledError?: any; + onRetry: () => void; +} + +export const InheritanceSilverPlanPurchaseDialogContext: Context = + createContext( + {} as InheritanceSilverPlanPurchaseDialogContextInterface, + ); + +export interface InheritanceSilverPlanPurchaseDialogContextProviderProps { + children: ReactNode; +} + +export const InheritanceSilverPlanPurchaseDialogProvider: FC< + InheritanceSilverPlanPurchaseDialogContextProviderProps +> = ({ children }) => { + const dispatch = useAppDispatch(); + const lang = useAppSelector(selectLanguage); + + const deviceRequiredDialogsMap: Record = + useMemo( + () => ({ + 3: [0], + }), + [], + ); + + const tabs: ITabs = useMemo( + () => [ + { + name: lang.strings.inheritance.termsOfService.title, + dialogs: [, ], + }, + { + name: lang.strings.inheritanceSilverPlanPurchase.instructions.heading, + dialogs: [], + }, + { + name: lang.strings.inheritanceSilverPlanPurchase.selectWallet.heading, + dialogs: [], + }, + { + name: lang.strings.inheritanceSilverPlanPurchase.walletAuth.heading, + dialogs: [], + }, + { + name: lang.strings.inheritanceSilverPlanPurchase.email.heading, + dialogs: [ + , + , + ], + }, + { + name: lang.strings.inheritanceSilverPlanPurchase.encryption.heading, + dialogs: [ + , + , + , + ], + }, + ], + [], + ); + + const { + onNext, + onPrevious, + goTo, + currentTab, + currentDialog, + isDeviceRequired, + } = useTabsAndDialogs({ + deviceRequiredDialogsMap, + tabs, + dialogName: 'inheritanceSilverPlanPurchase', + }); + + const onClose = () => { + dispatch(closeDialog('inheritanceSilverPlanPurchase')); + }; + + const wallets = useAppSelector(state => state.wallet.wallets); + const deletedWallets = useAppSelector(state => state.wallet.deletedWallets); + + const allWallets = useMemo(() => { + const deletedWalletIds = deletedWallets.map(e => e.__id); + + return [ + ...wallets.map(e => ({ + ...e, + isDeleted: deletedWalletIds.includes(e.__id), + })), + ]; + }, [wallets, deletedWallets]); + + const [userDetails, setUserDetails] = useState(); + const [selectedWallet, setSelectedWallet] = useState(); + const [isSubmittingUserDetails, setIsSubmittingUserDetails] = useState(false); + const [unhandledError, setUnhandledError] = useState(); + + const onUserDetailsSubmit = useCallback(async (params: IUserDetails) => { + setIsSubmittingUserDetails(true); + setUserDetails(params); + await sleep(2000); + setIsSubmittingUserDetails(false); + goTo(4, 1); + }, []); + + const onRetry = useCallback(() => { + setUserDetails(undefined); + setSelectedWallet(undefined); + setUnhandledError(undefined); + setIsSubmittingUserDetails(false); + goTo(0, 0); + }, []); + + const ctx = useMemo( + () => ({ + onNext, + onPrevious, + tabs, + onClose, + goTo, + currentTab, + currentDialog, + isDeviceRequired, + allWallets, + selectedWallet, + setSelectedWallet, + onUserDetailsSubmit, + isSubmittingUserDetails, + userDetails, + unhandledError, + onRetry, + }), + [ + onNext, + onPrevious, + tabs, + onClose, + goTo, + currentTab, + currentDialog, + isDeviceRequired, + allWallets, + selectedWallet, + setSelectedWallet, + onUserDetailsSubmit, + isSubmittingUserDetails, + userDetails, + unhandledError, + onRetry, + ], + ); + + return ( + + {children} + + ); +}; + +export function useInheritanceSilverPlanPurchaseDialog(): InheritanceSilverPlanPurchaseDialogContextInterface { + return useContext(InheritanceSilverPlanPurchaseDialogContext); +} diff --git a/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/index.tsx new file mode 100644 index 000000000..2991b96ca --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/index.tsx @@ -0,0 +1,108 @@ +import { + BlurOverlay, + CloseButton, + DialogBox, + DialogBoxBackgroundBar, + DialogBoxBody, + MilestoneAside, + WalletDialogMainContainer, +} from '@cypherock/cysync-ui'; +import React, { FC } from 'react'; + +import { + ErrorHandlerDialog, + WithConnectedDevice, + WithConnectedDeviceProps, +} from '~/components'; +import { useAppSelector, selectLanguage } from '~/store'; + +import { + InheritanceSilverPlanPurchaseDialogProvider, + useInheritanceSilverPlanPurchaseDialog, +} from './context'; + +const DeviceConnectionWrapper: React.FC<{ + isDeviceRequired: boolean; + children: React.ReactNode; + withConnectedProps?: WithConnectedDeviceProps; +}> = ({ isDeviceRequired, withConnectedProps, children }) => { + if (isDeviceRequired) + return ( + + {children} + + ); + // eslint-disable-next-line react/jsx-no-useless-fragment + return <>{children}; +}; + +DeviceConnectionWrapper.defaultProps = { + withConnectedProps: {}, +}; + +const InheritanceSilverPlanPurchase: FC = () => { + const { + tabs, + currentTab, + currentDialog, + unhandledError, + onClose, + onRetry, + isDeviceRequired, + } = useInheritanceSilverPlanPurchaseDialog(); + + const lang = useAppSelector(selectLanguage); + + return ( + + + <> + !t.dontShowOnMilestone) + .map(t => t.name)} + activeTab={currentTab} + heading={lang.strings.inheritanceSilverPlanPurchase.title} + /> + + + + + {tabs[currentTab]?.dialogs[currentDialog]} + + + + onClose()} />} + position="top" + useLightPadding + /> + + + + + ); +}; + +export const InheritanceSilverPlanPurchaseDialog = () => ( + + + +); diff --git a/packages/cysync-core/src/dialogs/Inheritance/components/OTPInputDialog.tsx b/packages/cysync-core/src/dialogs/Inheritance/components/OTPInputDialog.tsx index f4bed4139..8597e1f91 100644 --- a/packages/cysync-core/src/dialogs/Inheritance/components/OTPInputDialog.tsx +++ b/packages/cysync-core/src/dialogs/Inheritance/components/OTPInputDialog.tsx @@ -12,11 +12,22 @@ import { ScrollableContainer, Typography, } from '@cypherock/cysync-ui'; -import React, { useEffect, useMemo, useState } from 'react'; +import React, { + ForwardedRef, + forwardRef, + useEffect, + useImperativeHandle, + useMemo, + useState, +} from 'react'; import { useCountdown } from '~/hooks'; import { selectLanguage, useAppSelector } from '~/store'; +export interface OTPInputDialogRef { + reset: () => void; +} + export interface OTPInputDialogProps { onClose: () => void; emails: string | string[]; @@ -29,133 +40,160 @@ export interface OTPInputDialogProps { retriesRemaining: number; otpExpireTime?: string; wrongOtpError?: boolean; + title?: string; } -export const OTPInputDialog: React.FC = ({ - onClose, - emails, - onBack, - otpLength, - onVerify, - isVerifyingEmail, - isResendingOtp, - onResendOtp, - retriesRemaining, - otpExpireTime, - wrongOtpError, -}) => { - const lang = useAppSelector(selectLanguage); - - const strings = lang.strings.dialogs.inheritanceSyncPlans; - - const [otp, setOtp] = useState(''); - const { seconds: expireSeconds } = useCountdown( - otpExpireTime ? new Date(otpExpireTime).getTime() : new Date().getTime(), - ); - - const onOtpChange = (val: string) => { - setOtp(val); - - if (val.length === otpLength) { - onVerify(otp); - } - }; - - const onResend = () => { - onResendOtp(); - }; - - const textVariables = useMemo( - () => ({ - email: Array.isArray(emails) ? emails.join('&') : emails, - retries: retriesRemaining.toString(), - timeout: expireSeconds.toString(), - }), - [emails, retriesRemaining, expireSeconds], - ); - - const getStatus = (): OTPInputStatus => { - if (retriesRemaining <= 0) { - return 'retryExceeded'; - } - - if (wrongOtpError) { - return 'error'; - } - - return 'idle'; - }; - - const getTitle = () => { - if (retriesRemaining <= 0) { - return lang.strings.otp.noRetries.title; - } - - if (wrongOtpError) { - return lang.strings.otp.wrongOtpTitle; - } - - return lang.strings.otp.title; - }; - - useEffect(() => { - if (!isVerifyingEmail) { - setOtp(''); - } - }, [isVerifyingEmail]); - - return ( - - - - - - - } +> = forwardRef( + ( + { + onClose, + emails, + onBack, + otpLength, + onVerify, + isVerifyingEmail, + isResendingOtp, + onResendOtp, + retriesRemaining, + otpExpireTime, + wrongOtpError, + title, + }, + ref, + ) => { + const lang = useAppSelector(selectLanguage); + + const strings = lang.strings.dialogs.inheritanceSyncPlans; + + const [otp, setOtp] = useState(''); + const { seconds: expireSeconds } = useCountdown( + otpExpireTime ? new Date(otpExpireTime).getTime() : new Date().getTime(), + ); + + const onOtpChange = (val: string) => { + setOtp(val); + + if (val.length === otpLength) { + onVerify(otp); + } + }; + + const onResend = () => { + onResendOtp(); + }; + + const textVariables = useMemo( + () => ({ + email: Array.isArray(emails) ? emails.join('&') : emails, + retries: retriesRemaining.toString(), + timeout: expireSeconds.toString(), + }), + [emails, retriesRemaining, expireSeconds], + ); + + const getStatus = (): OTPInputStatus => { + if (retriesRemaining <= 0) { + return 'retryExceeded'; + } + + if (wrongOtpError) { + return 'error'; + } + + return 'idle'; + }; + + const getTitle = () => { + if (retriesRemaining <= 0) { + return lang.strings.otp.noRetries.title; + } + + if (wrongOtpError) { + return lang.strings.otp.wrongOtpTitle; + } + + return lang.strings.otp.title; + }; + + useEffect(() => { + if (!isVerifyingEmail) { + setOtp(''); + } + }, [isVerifyingEmail]); + + useImperativeHandle( + ref, + () => ({ + reset: () => { + setOtp(''); + }, + }), + [], + ); + + return ( + + + + + + + + + {title ?? strings.verifyEmail.title} + + + + + + + - - - ); -}; + + + + + ); + }, +); + +OTPInputDialog.displayName = 'OTPInputDialog'; OTPInputDialog.defaultProps = { wrongOtpError: undefined, otpExpireTime: undefined, + title: undefined, }; diff --git a/packages/cysync-core/src/dialogs/Inheritance/index.ts b/packages/cysync-core/src/dialogs/Inheritance/index.ts index f9d784108..2a46ee8eb 100644 --- a/packages/cysync-core/src/dialogs/Inheritance/index.ts +++ b/packages/cysync-core/src/dialogs/Inheritance/index.ts @@ -1,2 +1,3 @@ export * from './SyncPlans'; export * from './PlanLogin'; +export * from './SilverPlanPurchase'; diff --git a/packages/cysync-core/src/dialogs/index.tsx b/packages/cysync-core/src/dialogs/index.tsx index 34194ec7d..dcbcef190 100644 --- a/packages/cysync-core/src/dialogs/index.tsx +++ b/packages/cysync-core/src/dialogs/index.tsx @@ -22,6 +22,7 @@ import { HistoryDialog } from './HistoryDialog'; import { InheritancePlanLoginDialog, InheritanceSyncPlansDialog, + InheritanceSilverPlanPurchaseDialog, } from './Inheritance'; import { ChangePasswordDialog, @@ -63,4 +64,5 @@ export const dialogs: Record = { betaNotificationDialog: BetaNotificationDialog, inheritanceSyncPlans: InheritanceSyncPlansDialog, inheritancePlanLogin: InheritancePlanLoginDialog, + inheritanceSilverPlanPurchase: InheritanceSilverPlanPurchaseDialog, }; diff --git a/packages/cysync-core/src/pages/OnBoarding/Terms.tsx b/packages/cysync-core/src/pages/OnBoarding/Terms.tsx index 3da7d074e..067ae11bc 100644 --- a/packages/cysync-core/src/pages/OnBoarding/Terms.tsx +++ b/packages/cysync-core/src/pages/OnBoarding/Terms.tsx @@ -1,5 +1,4 @@ import { - Bullet, Button, CheckBox, Container, @@ -7,12 +6,11 @@ import { DialogBoxBody, DialogBoxFooter, Flex, - Image, LangDisplay, LogoOutlinedAsideImage, - openExternalLink, TermsOfUseGraphics, Typography, + ExternalLink, } from '@cypherock/cysync-ui'; import React, { Dispatch, @@ -30,42 +28,6 @@ import { keyValueStore } from '~/utils'; import { OnboardingPageLayout } from './OnboardingPageLayout'; -const ExternalLinkItem: React.FC<{ - text: string; - href: string; -}> = ({ text, href }) => ( - - - - - - - - - - termsLink - - - -); - const TermsDialogBox: FC<{ isChecked: boolean; setIsChecked: Dispatch>; @@ -105,11 +67,11 @@ const TermsDialogBox: FC<{
- - diff --git a/packages/cysync-core/src/store/dialog/index.ts b/packages/cysync-core/src/store/dialog/index.ts index dce735568..311418b81 100644 --- a/packages/cysync-core/src/store/dialog/index.ts +++ b/packages/cysync-core/src/store/dialog/index.ts @@ -92,6 +92,9 @@ const initialState: IDialogState = { inheritancePlanLogin: { isOpen: false, }, + inheritanceSilverPlanPurchase: { + isOpen: false, + }, }; export const dialogSlice = createSlice({ diff --git a/packages/cysync-core/src/store/dialog/types.ts b/packages/cysync-core/src/store/dialog/types.ts index 3e64c4683..a77061c6e 100644 --- a/packages/cysync-core/src/store/dialog/types.ts +++ b/packages/cysync-core/src/store/dialog/types.ts @@ -142,6 +142,11 @@ export interface IDialogState { isOpen: boolean; data?: undefined; }; + + inheritanceSilverPlanPurchase: { + isOpen: boolean; + data?: undefined; + }; } export const GuidedFlowMap = { diff --git a/packages/ui/src/components/GlobalStyles.tsx b/packages/ui/src/components/GlobalStyles.tsx index 2ff85c816..9a9a68564 100644 --- a/packages/ui/src/components/GlobalStyles.tsx +++ b/packages/ui/src/components/GlobalStyles.tsx @@ -59,6 +59,64 @@ export const GlobalStyles: React.FC = () => ( + + {/* For Many In Many Component */} + + + + + + + + + + + + + + + + + + + + + + + ); diff --git a/packages/ui/src/components/atoms/ExternalLink.tsx b/packages/ui/src/components/atoms/ExternalLink.tsx new file mode 100644 index 000000000..c555e1874 --- /dev/null +++ b/packages/ui/src/components/atoms/ExternalLink.tsx @@ -0,0 +1,49 @@ +import React from 'react'; + +import { Bullet } from './Bullet'; +import { Container } from './Container'; +import { Flex } from './Flex'; +import { Image } from './Image'; +import { LangDisplay } from './LangDisplay'; +import { Typography } from './Typography'; + +import { openExternalLink } from '../../assets'; +import { UtilsProps } from '../utils'; + +export interface ExternalLinkProps extends UtilsProps { + text: string; + href: string; +} + +export const ExternalLink: React.FC = ({ + text, + href, + ...restProps +}) => ( + + + + + + + + + + openLink + + + +); diff --git a/packages/ui/src/components/atoms/Input/Input.tsx b/packages/ui/src/components/atoms/Input/Input.tsx index 9503fe7c9..fc51fa0ad 100644 --- a/packages/ui/src/components/atoms/Input/Input.tsx +++ b/packages/ui/src/components/atoms/Input/Input.tsx @@ -1,7 +1,7 @@ import React, { FC, ForwardedRef } from 'react'; import styled from 'styled-components'; -import { InputLabel } from './InputLabel'; +import { InputLabel, InputLabelProps } from './InputLabel'; import { UtilsProps } from '../../utils'; import { Button } from '../Button'; @@ -37,6 +37,7 @@ export interface InputProps { required?: boolean; utilProps?: UtilsProps; autoFocus?: boolean; + inputLabelProps?: InputLabelProps; } const InputStyle = styled.input<{ @@ -133,6 +134,7 @@ export const Input: FC }> = $noBorder = false, utilProps, autoFocus = false, + inputLabelProps, }: InputProps, ref: ForwardedRef, ) => ( @@ -144,7 +146,7 @@ export const Input: FC }> = {...(utilProps ?? {})} > {label && ( - + @@ -241,6 +243,7 @@ Input.defaultProps = { required: false, utilProps: undefined, autoFocus: false, + inputLabelProps: undefined, }; Input.displayName = 'Input'; diff --git a/packages/ui/src/components/atoms/Input/InputLabel.tsx b/packages/ui/src/components/atoms/Input/InputLabel.tsx index 991b69500..6300545f3 100644 --- a/packages/ui/src/components/atoms/Input/InputLabel.tsx +++ b/packages/ui/src/components/atoms/Input/InputLabel.tsx @@ -13,7 +13,7 @@ import { } from '../../utils'; import { goldenGradient } from '../../utils/Gradient'; -interface InputLabelProps +export interface InputLabelProps extends LabelHTMLAttributes, SpacingProps, BorderProps, diff --git a/packages/ui/src/components/atoms/index.ts b/packages/ui/src/components/atoms/index.ts index 3ca5b64b0..8b9cf420e 100644 --- a/packages/ui/src/components/atoms/index.ts +++ b/packages/ui/src/components/atoms/index.ts @@ -34,3 +34,4 @@ export * from './Tooltip'; export * from './Video'; export * from './AdvanceTextDiaplay'; export * from './NomineeMessage'; +export * from './ExternalLink'; diff --git a/packages/ui/src/components/molecules/ManyInMany.tsx b/packages/ui/src/components/molecules/ManyInMany.tsx index 46030dc7b..7af0d5a7e 100644 --- a/packages/ui/src/components/molecules/ManyInMany.tsx +++ b/packages/ui/src/components/molecules/ManyInMany.tsx @@ -1,4 +1,4 @@ -import React, { FC, useState } from 'react'; +import React, { FC } from 'react'; import styled from 'styled-components'; import { @@ -10,7 +10,7 @@ import { } from '../../assets'; import { useTheme } from '../../themes'; import { Flex, Typography } from '../atoms'; -import { WidthProps, width } from '../utils'; +import { UtilsProps, WidthProps, utils } from '../utils'; const getBoxShadow = (params: { $isSelected: boolean; @@ -65,7 +65,7 @@ const StyledContainer = styled.div< $isSelected && !disabled ? theme.palette.border.selected : 'transparent'}; box-shadow: ${getBoxShadow}; background: ${getBackground}; - ${width} + ${utils} &::after { content: ''; @@ -132,24 +132,27 @@ const StyledRedInfoImage = styled.div` top: 8px; `; -export interface ManyInManyProps extends WidthProps { +export interface ManyInManyProps extends UtilsProps { title: string; - disabled: boolean; + disabled?: boolean; + isSelected?: boolean; + onClick: () => void; } export const ManyInMany: FC = ({ title, disabled, + isSelected, + onClick, ...restProps }) => { - const [isSelected, setIsSelected] = useState(false); const theme = useTheme(); return ( !disabled && setIsSelected(!isSelected)} - $isSelected={isSelected} - disabled={disabled} + onClick={() => !disabled && onClick()} + $isSelected={Boolean(isSelected)} + disabled={Boolean(disabled)} {...restProps} > {disabled && ( @@ -173,8 +176,15 @@ export const ManyInMany: FC = ({ stroke={disabled ? theme.palette.background.separator : undefined} /> - {title} + + {title} +
); }; + +ManyInMany.defaultProps = { + isSelected: undefined, + disabled: undefined, +}; diff --git a/packages/ui/src/components/utils/flex.styled.ts b/packages/ui/src/components/utils/flex.styled.ts index eced9027c..46c486089 100644 --- a/packages/ui/src/components/utils/flex.styled.ts +++ b/packages/ui/src/components/utils/flex.styled.ts @@ -27,6 +27,13 @@ type AlignSelfType = | 'initial' | 'inherit'; +export type FlexWrapType = + | 'nowrap' + | 'wrap' + | 'wrap-reverse' + | 'initial' + | 'inherit'; + const alignSelfObj: Record = { start: 'flex-start', end: 'flex-end', @@ -44,6 +51,7 @@ export interface FlexProps { shrink?: MediaQuery; $alignSelf?: MediaQuery; $flex?: MediaQuery; + $flexWrap?: MediaQuery; } const flexProperty = css` @@ -105,6 +113,12 @@ const alignSelf = css` )} `; +const flexWrap = css` + ${props => + props.$flexWrap && + generateCss(['flex-wrap'], (item: string) => item, props.$flexWrap)} +`; + export const flex = css` ${justifyContent} ${align} @@ -114,4 +128,5 @@ export const flex = css` ${shrink} ${alignSelf} ${flexProperty} + ${flexWrap} `; diff --git a/packages/ui/src/components/utils/font.styled.ts b/packages/ui/src/components/utils/font.styled.ts index 558443749..56b40e581 100644 --- a/packages/ui/src/components/utils/font.styled.ts +++ b/packages/ui/src/components/utils/font.styled.ts @@ -27,6 +27,7 @@ export interface FontProps { $fontSize?: MediaQuery; $fontFamily?: MediaQuery; $wordBreak?: MediaQuery; + $letterSpacing?: string | number; } export const fontWeightObj: Record = { @@ -69,4 +70,12 @@ export const font = css` ${props => props.$wordBreak && generateCss(['word-break'], val => val, props.$wordBreak)}; + + ${props => + props.$letterSpacing !== undefined && + css` + letter-spacing: ${typeof props.$letterSpacing === 'number' + ? `${props.$letterSpacing}em` + : props.$letterSpacing}; + `} `; diff --git a/packages/ui/src/stories/atoms/ExternalLink.stories.tsx b/packages/ui/src/stories/atoms/ExternalLink.stories.tsx new file mode 100644 index 000000000..a8e68f90f --- /dev/null +++ b/packages/ui/src/stories/atoms/ExternalLink.stories.tsx @@ -0,0 +1,20 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { ExternalLink } from '../../components'; + +const meta: Meta = { + component: ExternalLink, + tags: ['autodocs'], +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + href: 'https://www.cypherock.com', + text: 'Terms and Conditions', + $width: 400, + }, +}; From 083a99a3e3c2bbba260ac4fdfe0a02a893226359 Mon Sep 17 00:00:00 2001 From: "moayaan.eth" Date: Thu, 1 Aug 2024 18:27:57 +0530 Subject: [PATCH 14/96] feat: inheritance plan card UI (#506) Co-authored-by: AryanCypherock --- packages/ui/icons/cross-icon.svg | 3 + packages/ui/package.json | 2 +- .../ui/src/components/molecules/PlanCard.tsx | 184 ++++++++++++++++++ packages/ui/src/components/molecules/index.ts | 1 + .../ui/src/components/utils/bgColor.styled.ts | 6 + .../ui/src/components/utils/font.styled.ts | 10 + .../stories/molecules/PlanCard.stories.tsx | 51 +++++ packages/ui/src/themes/color.styled.ts | 6 + packages/ui/src/themes/theme.styled.ts | 4 + 9 files changed, 266 insertions(+), 1 deletion(-) create mode 100644 packages/ui/icons/cross-icon.svg create mode 100644 packages/ui/src/components/molecules/PlanCard.tsx create mode 100644 packages/ui/src/stories/molecules/PlanCard.stories.tsx diff --git a/packages/ui/icons/cross-icon.svg b/packages/ui/icons/cross-icon.svg new file mode 100644 index 000000000..a74f2194b --- /dev/null +++ b/packages/ui/icons/cross-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/package.json b/packages/ui/package.json index ae2535cab..a5b6fdf56 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -18,7 +18,7 @@ "build:icons": "svgr icons --out-dir ./src/assets/icons/generated --no-svgo", "prebuild": "pnpm clean:icons && pnpm build:icons", "clean": "rimraf dist", - "clean:icons": "rimraf ./src/assets/icons/generated/*", + "clean:icons": "rimraf ./src/assets/icons/generated", "test": "jest", "pre-commit": "lint-staged", "storybook": "storybook dev -p 6006", diff --git a/packages/ui/src/components/molecules/PlanCard.tsx b/packages/ui/src/components/molecules/PlanCard.tsx new file mode 100644 index 000000000..90ba4e510 --- /dev/null +++ b/packages/ui/src/components/molecules/PlanCard.tsx @@ -0,0 +1,184 @@ +import React from 'react'; +import styled from 'styled-components'; + +import { Check, CrossIcon } from '../../assets'; +import { useTheme } from '../../themes'; +import { Button, Flex, Typography } from '../atoms'; +import { WidthProps, width } from '../utils'; + +export type PlanCardType = 'silver' | 'gold'; + +export interface PlanCardProps extends WidthProps { + type: PlanCardType; + heading: string; + tagline: string; + description: string; + price: string; + duration: string; + buttonText: string; + popularTagText?: string; + features: { text: string; available: boolean }[]; + onClick: () => void; +} + +const PlanContainer = styled.div<{ type: PlanCardType } & WidthProps>` + position: relative; + display: flex; + width: 400px; + padding: 40px 24px; + flex-direction: column; + align-items: flex-start; + gap: 32px; + border-radius: 16px; + background: ${({ type, theme }) => + type === 'silver' + ? theme.palette.background.slateLight + : theme.palette.background.plan}; + box-shadow: 4px 4px 30px 0px ${({ theme }) => theme.palette.border.darkSlate}; + border: ${({ type, theme }) => + type === 'gold' && `1px solid ${theme.palette.background.golden}`}; + ${width} +`; +const PopularTag = styled.div` + position: absolute; + top: 0px; + right: 32px; + background: ${({ theme }) => theme.palette.golden}; + z-index: 1; + border-radius: 0px 0px 6px 6px; + padding: 8px 15px 8px 17px; + display: flex; + justify-content: center; + align-items: center; +`; + +export const PlanCard: React.FC = ({ + type, + heading, + tagline, + description, + price, + features, + duration, + buttonText, + popularTagText, + onClick, + ...restProps +}) => { + const theme = useTheme(); + return ( + + {popularTagText && ( + + + {popularTagText} + + + )} + + + {heading} + + + {tagline} + + + + + {price} + + + + {duration} + + + + + {description} + + + + {features.map((feature, index) => ( + + {feature.available ? ( + + ) : ( + + )} + + {feature.text} + + + ))} + + + + ); +}; +PlanCard.defaultProps = { + popularTagText: undefined, +}; diff --git a/packages/ui/src/components/molecules/index.ts b/packages/ui/src/components/molecules/index.ts index 288068fb5..2a2e8e738 100644 --- a/packages/ui/src/components/molecules/index.ts +++ b/packages/ui/src/components/molecules/index.ts @@ -47,3 +47,4 @@ export * from './SimpleJsonView'; export * from './OTPInput'; export * from './OneInMany'; export * from './CouponInput'; +export * from './PlanCard'; diff --git a/packages/ui/src/components/utils/bgColor.styled.ts b/packages/ui/src/components/utils/bgColor.styled.ts index 231bb9d04..3e7c70fa0 100644 --- a/packages/ui/src/components/utils/bgColor.styled.ts +++ b/packages/ui/src/components/utils/bgColor.styled.ts @@ -25,6 +25,7 @@ export type BgColor = | 'container' | 'slate' | 'slateDark' + | 'headlineLight' | 'error'; export interface BgColorProps { $bgColor?: BgColor; @@ -68,6 +69,11 @@ export const bgColor = css` background: ${({ theme }) => theme.palette.background.separatorSecondary}; `} ${props => + props.$bgColor === 'headlineLight' && + css` + background: ${({ theme }) => theme.palette.background.headlineLight}; + `} + ${props => props.$bgColor === 'slate' && css` background: ${({ theme }) => theme.palette.background.slate}; diff --git a/packages/ui/src/components/utils/font.styled.ts b/packages/ui/src/components/utils/font.styled.ts index 56b40e581..ca46697a7 100644 --- a/packages/ui/src/components/utils/font.styled.ts +++ b/packages/ui/src/components/utils/font.styled.ts @@ -28,6 +28,7 @@ export interface FontProps { $fontFamily?: MediaQuery; $wordBreak?: MediaQuery; $letterSpacing?: string | number; + $lineHeight?: MediaQuery; } export const fontWeightObj: Record = { @@ -78,4 +79,13 @@ export const font = css` ? `${props.$letterSpacing}em` : props.$letterSpacing}; `} + + ${props => + props.$lineHeight && + generateCss( + ['line-height'], + (item: number | string) => + typeof item === 'number' ? `${item}px` : item, + props.$lineHeight, + )} `; diff --git a/packages/ui/src/stories/molecules/PlanCard.stories.tsx b/packages/ui/src/stories/molecules/PlanCard.stories.tsx new file mode 100644 index 000000000..a5bab824b --- /dev/null +++ b/packages/ui/src/stories/molecules/PlanCard.stories.tsx @@ -0,0 +1,51 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { PlanCard } from '../../components'; + +const meta: Meta = { + component: PlanCard, + tags: ['autodocs'], +}; + +export default meta; + +type Story = StoryObj; + +export const SilverPlan: Story = { + args: { + type: 'silver', + heading: 'Silver', + tagline: 'PIN Recovery', + description: + 'Secure your wallet PIN with us and easily recover it if forgotten, ensuring continuous access to your crypto assets.', + price: '$25', + features: [ + { text: 'No KYC required', available: true }, + { text: 'Multi-Chain', available: true }, + { text: 'PIN Recovery', available: true }, + { text: 'Estate Recovery', available: false }, + ], + duration: '/Wallet/Year', + buttonText: 'SELECT', + }, +}; + +export const GoldPlan: Story = { + args: { + type: 'gold', + heading: 'Gold', + tagline: 'Non-Custodial Estate Recovery', + description: + 'Ensure your crypto assets are inherited by loved ones effortlessly, bypassing complex legal restrictions for smooth transfers.', + price: '$100', + features: [ + { text: 'No KYC required', available: true }, + { text: 'Multi-Chain', available: true }, + { text: 'PIN Recovery', available: true }, + { text: 'Estate Recovery', available: true }, + ], + duration: '/Wallet/Year', + buttonText: 'SELECT', + popularTagText: 'MOST POPULAR', + }, +}; diff --git a/packages/ui/src/themes/color.styled.ts b/packages/ui/src/themes/color.styled.ts index c6c3f9ac3..48d1bee2e 100644 --- a/packages/ui/src/themes/color.styled.ts +++ b/packages/ui/src/themes/color.styled.ts @@ -22,6 +22,7 @@ export const colors = { cardHover: 'linear-gradient(105deg, rgba(96, 58, 23, 0.20) 0%, rgba(0, 0, 0, 0.00) 60.65%), #332F2D', title: `linear-gradient(90deg,#e9b873 0.19%,#fedd8f 37.17%,#b78d51 100.19%)`, + plan: 'linear-gradient(90deg, rgba(224, 187, 117, 0.10) 0%, rgba(39, 35, 32, 0.00) 100%), #272320', }, info: { main: '#F1AE4A', @@ -57,6 +58,8 @@ export const colors = { divider: '#333130', dialog: '#2B2420', separator: '#39322C', + greenStroke: '#00FF75', + redStroke: '#FF0202', }, boxShadow: { selected: '#1B1813', @@ -95,8 +98,11 @@ export const colors = { cardDisabled: '#282522', cardSelected: '#2A2827', slateDark: '#423F3C', + slateLight: '#302C29', + headlineLight: '#655F53', }, border: { + darkSlate: '#030303', popup: '#2C2520', list: '#2C2824', input: '#3C3937', diff --git a/packages/ui/src/themes/theme.styled.ts b/packages/ui/src/themes/theme.styled.ts index f8632d934..45e94373a 100644 --- a/packages/ui/src/themes/theme.styled.ts +++ b/packages/ui/src/themes/theme.styled.ts @@ -80,6 +80,7 @@ export const theme = { disabled: colors.disabled.background, bar: colors.background.bar, gold: colors.gradients.golden, + plan: colors.gradients.plan, golden: colors.background.gold, success: colors.success.main, successSecondary: colors.success.secondary, @@ -99,6 +100,7 @@ export const theme = { message: colors.background.message, breadcrumbSeparator: colors.background.breadcrumbSeparator, batchTransactionBody: colors.background.batchTransactionBody, + slateLight: colors.background.slateLight, filterItem: colors.background.filterItem, calendar: colors.background.calendar, calendarHeader: colors.background.calendarHeader, @@ -109,6 +111,7 @@ export const theme = { cardSelected: colors.background.cardSelected, cardDisabled: colors.background.cardDisabled, slateDark: colors.background.slateDark, + headlineLight: colors.background.headlineLight, }, border: { popup: colors.border.popup, @@ -122,6 +125,7 @@ export const theme = { danger: colors.border.danger, bar: colors.border.bar, white: colors.border.white, + darkSlate: colors.border.darkSlate, table: { title: colors.border.table.title, row: colors.border.table.row, From 66af3e0c48ad5621bcde4e436d637686276f9fca Mon Sep 17 00:00:00 2001 From: AryanCypherock Date: Thu, 1 Aug 2024 18:28:16 +0530 Subject: [PATCH 15/96] feat: inheritance edit executor message dailog ui (#510) --- .../src/i18n/lang/ar-AE.json | 26 +++++ .../src/i18n/lang/de-DE.json | 26 +++++ .../src/i18n/lang/en.json | 26 +++++ .../src/i18n/lang/id-ID.json | 26 +++++ .../src/i18n/lang/zh-CN.json | 26 +++++ .../cysync-core-constants/src/i18n/types.ts | 23 ++++ .../cysync-core/src/actions/dialog/index.ts | 3 + .../src/components/LoaderDialog.tsx | 50 +++++++-- .../Dialogs/EditMessage.tsx | 90 +++++++++++++++ .../EditExecutorMessage/Dialogs/FetchData.tsx | 11 ++ .../EditExecutorMessage/Dialogs/Success.tsx | 25 +++++ .../EditExecutorMessage/Dialogs/index.tsx | 3 + .../EditExecutorMessage/context/index.tsx | 106 ++++++++++++++++++ .../Inheritance/EditExecutorMessage/index.tsx | 33 ++++++ .../src/dialogs/Inheritance/index.ts | 1 + packages/cysync-core/src/dialogs/index.tsx | 2 + .../cysync-core/src/store/dialog/index.ts | 3 + .../cysync-core/src/store/dialog/types.ts | 5 + .../molecules/Dialog/SuccessDialog.tsx | 49 ++++++-- 19 files changed, 516 insertions(+), 18 deletions(-) create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditExecutorMessage/Dialogs/EditMessage.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditExecutorMessage/Dialogs/FetchData.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditExecutorMessage/Dialogs/Success.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditExecutorMessage/Dialogs/index.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditExecutorMessage/context/index.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditExecutorMessage/index.tsx diff --git a/packages/cysync-core-constants/src/i18n/lang/ar-AE.json b/packages/cysync-core-constants/src/i18n/lang/ar-AE.json index e1e84b1ae..9735748c2 100644 --- a/packages/cysync-core-constants/src/i18n/lang/ar-AE.json +++ b/packages/cysync-core-constants/src/i18n/lang/ar-AE.json @@ -1485,6 +1485,32 @@ "title": "Please wait while we fetch the plan data", "subTitle": "Do not disconnect or close the app" } + }, + "inheritanceEditExecutorMessage": { + "fetchData": { + "title": "Please wait while we fetch the data", + "subTitle": "Do not disconnect or close the app" + }, + "editMessage": { + "title": "Executor Message", + "form": { + "messageField": { + "label": "Message", + "placeholder": "Executor Message" + } + }, + "buttons": { + "exit": "Exit without saving", + "save": "Save changes" + }, + "loading": { + "title": "Please wait while we update the data", + "subtitle": "Do not disconnect or close the app" + } + }, + "success": { + "title": "Executor message successfully encrypted" + } } }, "toggle": { diff --git a/packages/cysync-core-constants/src/i18n/lang/de-DE.json b/packages/cysync-core-constants/src/i18n/lang/de-DE.json index efd0f9e60..412154de4 100644 --- a/packages/cysync-core-constants/src/i18n/lang/de-DE.json +++ b/packages/cysync-core-constants/src/i18n/lang/de-DE.json @@ -1490,6 +1490,32 @@ "title": "Please wait while we fetch the plan data", "subTitle": "Do not disconnect or close the app" } + }, + "inheritanceEditExecutorMessage": { + "fetchData": { + "title": "Please wait while we fetch the data", + "subTitle": "Do not disconnect or close the app" + }, + "editMessage": { + "title": "Executor Message", + "form": { + "messageField": { + "label": "Message", + "placeholder": "Executor Message" + } + }, + "buttons": { + "exit": "Exit without saving", + "save": "Save changes" + }, + "loading": { + "title": "Please wait while we update the data", + "subtitle": "Do not disconnect or close the app" + } + }, + "success": { + "title": "Executor message successfully encrypted" + } } }, "toggle": { diff --git a/packages/cysync-core-constants/src/i18n/lang/en.json b/packages/cysync-core-constants/src/i18n/lang/en.json index d87f8c5af..4df0abba5 100644 --- a/packages/cysync-core-constants/src/i18n/lang/en.json +++ b/packages/cysync-core-constants/src/i18n/lang/en.json @@ -1490,6 +1490,32 @@ "title": "Please wait while we fetch the plan data", "subTitle": "Do not disconnect or close the app" } + }, + "inheritanceEditExecutorMessage": { + "fetchData": { + "title": "Please wait while we fetch the data", + "subTitle": "Do not disconnect or close the app" + }, + "editMessage": { + "title": "Executor Message", + "form": { + "messageField": { + "label": "Message", + "placeholder": "Executor Message" + } + }, + "buttons": { + "exit": "Exit without saving", + "save": "Save changes" + }, + "loading": { + "title": "Please wait while we update the data", + "subtitle": "Do not disconnect or close the app" + } + }, + "success": { + "title": "Executor message successfully encrypted" + } } }, "toggle": { diff --git a/packages/cysync-core-constants/src/i18n/lang/id-ID.json b/packages/cysync-core-constants/src/i18n/lang/id-ID.json index 70dce8796..df3a83d6a 100644 --- a/packages/cysync-core-constants/src/i18n/lang/id-ID.json +++ b/packages/cysync-core-constants/src/i18n/lang/id-ID.json @@ -1490,6 +1490,32 @@ "title": "Please wait while we fetch the plan data", "subTitle": "Do not disconnect or close the app" } + }, + "inheritanceEditExecutorMessage": { + "fetchData": { + "title": "Please wait while we fetch the data", + "subTitle": "Do not disconnect or close the app" + }, + "editMessage": { + "title": "Executor Message", + "form": { + "messageField": { + "label": "Message", + "placeholder": "Executor Message" + } + }, + "buttons": { + "exit": "Exit without saving", + "save": "Save changes" + }, + "loading": { + "title": "Please wait while we update the data", + "subtitle": "Do not disconnect or close the app" + } + }, + "success": { + "title": "Executor message successfully encrypted" + } } }, "toggle": { diff --git a/packages/cysync-core-constants/src/i18n/lang/zh-CN.json b/packages/cysync-core-constants/src/i18n/lang/zh-CN.json index 4d90b37cf..8ac068c0c 100644 --- a/packages/cysync-core-constants/src/i18n/lang/zh-CN.json +++ b/packages/cysync-core-constants/src/i18n/lang/zh-CN.json @@ -1465,6 +1465,32 @@ "title": "Please wait while we fetch the plan data", "subTitle": "Do not disconnect or close the app" } + }, + "inheritanceEditExecutorMessage": { + "fetchData": { + "title": "Please wait while we fetch the data", + "subTitle": "Do not disconnect or close the app" + }, + "editMessage": { + "title": "Executor Message", + "form": { + "messageField": { + "label": "Message", + "placeholder": "Executor Message" + } + }, + "buttons": { + "exit": "Exit without saving", + "save": "Save changes" + }, + "loading": { + "title": "Please wait while we update the data", + "subtitle": "Do not disconnect or close the app" + } + }, + "success": { + "title": "Executor message successfully encrypted" + } } }, "toggle": { diff --git a/packages/cysync-core-constants/src/i18n/types.ts b/packages/cysync-core-constants/src/i18n/types.ts index a3fb3e2fe..ed2758ce1 100644 --- a/packages/cysync-core-constants/src/i18n/types.ts +++ b/packages/cysync-core-constants/src/i18n/types.ts @@ -767,6 +767,29 @@ interface LangDialogs { subTitle: string; }; }; + inheritanceEditExecutorMessage: { + fetchData: { + title: string; + subTitle: string; + }; + editMessage: { + title: string; + form: { + messageField: { label: string; placeholder: string }; + }; + buttons: { + exit: string; + save: string; + }; + loading: { + title: string; + subtitle: string; + }; + }; + success: { + title: string; + }; + }; } interface LangToggle { diff --git a/packages/cysync-core/src/actions/dialog/index.ts b/packages/cysync-core/src/actions/dialog/index.ts index 627cc7997..0cc565e87 100644 --- a/packages/cysync-core/src/actions/dialog/index.ts +++ b/packages/cysync-core/src/actions/dialog/index.ts @@ -94,3 +94,6 @@ export const openInheritancePlanLoginDialog = () => export const openInheritanceSilverPlanPurchaseDialog = () => openDialog({ name: 'inheritanceSilverPlanPurchase', data: undefined }); + +export const openInheritanceEditExecutorMessageDialog = () => + openDialog({ name: 'inheritanceEditExecutorMessage', data: undefined }); diff --git a/packages/cysync-core/src/components/LoaderDialog.tsx b/packages/cysync-core/src/components/LoaderDialog.tsx index ca99ba83d..6ee1c8bfe 100644 --- a/packages/cysync-core/src/components/LoaderDialog.tsx +++ b/packages/cysync-core/src/components/LoaderDialog.tsx @@ -1,14 +1,46 @@ -import { DialogBox, Image, loaderGrayIcon } from '@cypherock/cysync-ui'; +import { + Container, + DialogBox, + DialogBoxBody, + Image, + LangDisplay, + loaderGrayIcon, + Typography, +} from '@cypherock/cysync-ui'; import React from 'react'; -export const LoaderDialog: React.FC = () => ( +export interface LoaderProps { + title?: string; + subtext?: string; +} + +export const LoaderDialog: React.FC = ({ title, subtext }) => ( - Loader icon + + Loader icon + {title && ( + + + + + {subtext && ( + + + + )} + + )} + ); + +LoaderDialog.defaultProps = { + title: undefined, + subtext: undefined, +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditExecutorMessage/Dialogs/EditMessage.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditExecutorMessage/Dialogs/EditMessage.tsx new file mode 100644 index 000000000..8f9daff4b --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditExecutorMessage/Dialogs/EditMessage.tsx @@ -0,0 +1,90 @@ +import { + Button, + CloseButton, + DialogBox, + DialogBoxBody, + DialogBoxFooter, + DialogBoxHeader, + Flex, + InputLabel, + LangDisplay, + ScrollableContainer, + TextAreaInput, + Typography, +} from '@cypherock/cysync-ui'; +import React, { useState } from 'react'; + +import { LoaderDialog } from '~/components'; +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritanceEditExecutorMessageDialog } from '../context'; + +export const EditMessage = () => { + const lang = useAppSelector(selectLanguage); + const [isLoading, setIsLoading] = useState(false); + const [message, setMessage] = useState(''); + const { onClose } = useInheritanceEditExecutorMessageDialog(); + const strings = + lang.strings.dialogs.inheritanceEditExecutorMessage.editMessage; + const { form } = strings; + + if (isLoading) { + return ( + + ); + } + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditExecutorMessage/Dialogs/FetchData.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditExecutorMessage/Dialogs/FetchData.tsx new file mode 100644 index 000000000..9eab5fb83 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditExecutorMessage/Dialogs/FetchData.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { LoaderDialog } from '~/components'; +import { selectLanguage, useAppSelector } from '~/store'; + +export const FetchData = () => { + const lang = useAppSelector(selectLanguage); + const strings = lang.strings.dialogs.inheritanceEditExecutorMessage.fetchData; + + return ; +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditExecutorMessage/Dialogs/Success.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditExecutorMessage/Dialogs/Success.tsx new file mode 100644 index 000000000..9a3828988 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditExecutorMessage/Dialogs/Success.tsx @@ -0,0 +1,25 @@ +import { SuccessDialog } from '@cypherock/cysync-ui'; +import React from 'react'; + +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritanceEditExecutorMessageDialog } from '../context'; + +export const Success = () => { + const lang = useAppSelector(selectLanguage); + const { onClose } = useInheritanceEditExecutorMessageDialog(); + const { strings } = lang; + + return ( + + ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditExecutorMessage/Dialogs/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditExecutorMessage/Dialogs/index.tsx new file mode 100644 index 000000000..4ee466b93 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditExecutorMessage/Dialogs/index.tsx @@ -0,0 +1,3 @@ +export * from './EditMessage'; +export * from './FetchData'; +export * from './Success'; diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditExecutorMessage/context/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditExecutorMessage/context/index.tsx new file mode 100644 index 000000000..3b05a4ce5 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditExecutorMessage/context/index.tsx @@ -0,0 +1,106 @@ +import React, { + Context, + FC, + ReactNode, + createContext, + useContext, + useMemo, +} from 'react'; + +import { ITabs, useTabsAndDialogs } from '~/hooks'; +import { closeDialog, useAppDispatch } from '~/store'; + +import { FetchData, EditMessage, Success } from '../Dialogs'; + +export interface InheritanceEditExecutorMessageDialogContextInterface { + tabs: ITabs; + onNext: (tab?: number, dialog?: number) => void; + goTo: (tab: number, dialog?: number) => void; + onPrevious: () => void; + onClose: () => void; + currentTab: number; + currentDialog: number; + isDeviceRequired: boolean; + unhandledError?: any; +} + +export const InheritanceEditExecutorMessageDialogContext: Context = + createContext( + {} as InheritanceEditExecutorMessageDialogContextInterface, + ); + +export interface InheritanceEditExecutorMessageDialogContextProviderProps { + children: ReactNode; +} + +export const InheritanceEditExecutorMessageDialogProvider: FC< + InheritanceEditExecutorMessageDialogContextProviderProps +> = ({ children }) => { + const dispatch = useAppDispatch(); + + const deviceRequiredDialogsMap: Record = {}; + const tabs: ITabs = [ + { + name: 'Fetch Data', + dialogs: [], + }, + { + name: 'Edit Message', + dialogs: [], + }, + { + name: 'Success Message', + dialogs: [], + }, + ]; + + const { + onNext, + onPrevious, + goTo, + currentTab, + currentDialog, + isDeviceRequired, + } = useTabsAndDialogs({ + deviceRequiredDialogsMap, + tabs, + dialogName: 'inheritanceEditExecutorMessage', + }); + + const onClose = () => { + dispatch(closeDialog('inheritanceEditExecutorMessage')); + }; + + const ctx = useMemo( + () => ({ + onNext, + onPrevious, + tabs, + onClose, + goTo, + currentTab, + currentDialog, + isDeviceRequired, + }), + [ + onNext, + onPrevious, + tabs, + onClose, + goTo, + currentTab, + currentDialog, + isDeviceRequired, + ], + ); + + return ( + + {children} + + ); +}; + +export function useInheritanceEditExecutorMessageDialog(): InheritanceEditExecutorMessageDialogContextInterface { + return useContext(InheritanceEditExecutorMessageDialogContext); +} diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditExecutorMessage/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditExecutorMessage/index.tsx new file mode 100644 index 000000000..5739290e3 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditExecutorMessage/index.tsx @@ -0,0 +1,33 @@ +import { BlurOverlay } from '@cypherock/cysync-ui'; +import React, { FC } from 'react'; + +import { ErrorHandlerDialog } from '~/components'; + +import { + InheritanceEditExecutorMessageDialogProvider, + useInheritanceEditExecutorMessageDialog, +} from './context'; + +const InheritanceEditExecutorMessage: FC = () => { + const { tabs, currentTab, currentDialog, unhandledError, onClose } = + useInheritanceEditExecutorMessageDialog(); + + return ( + + + {tabs[currentTab]?.dialogs[currentDialog]} + + + ); +}; + +export const InheritanceEditExecutorMessageDialog = () => ( + + + +); diff --git a/packages/cysync-core/src/dialogs/Inheritance/index.ts b/packages/cysync-core/src/dialogs/Inheritance/index.ts index 2a46ee8eb..97c0aaacb 100644 --- a/packages/cysync-core/src/dialogs/Inheritance/index.ts +++ b/packages/cysync-core/src/dialogs/Inheritance/index.ts @@ -1,3 +1,4 @@ export * from './SyncPlans'; export * from './PlanLogin'; export * from './SilverPlanPurchase'; +export * from './EditExecutorMessage'; diff --git a/packages/cysync-core/src/dialogs/index.tsx b/packages/cysync-core/src/dialogs/index.tsx index dcbcef190..ff16064d3 100644 --- a/packages/cysync-core/src/dialogs/index.tsx +++ b/packages/cysync-core/src/dialogs/index.tsx @@ -20,6 +20,7 @@ import { ErrorDialog } from './ErrorDialog'; import { GuidedFlow } from './GuidedFlow'; import { HistoryDialog } from './HistoryDialog'; import { + InheritanceEditExecutorMessageDialog, InheritancePlanLoginDialog, InheritanceSyncPlansDialog, InheritanceSilverPlanPurchaseDialog, @@ -65,4 +66,5 @@ export const dialogs: Record = { inheritanceSyncPlans: InheritanceSyncPlansDialog, inheritancePlanLogin: InheritancePlanLoginDialog, inheritanceSilverPlanPurchase: InheritanceSilverPlanPurchaseDialog, + inheritanceEditExecutorMessage: InheritanceEditExecutorMessageDialog, }; diff --git a/packages/cysync-core/src/store/dialog/index.ts b/packages/cysync-core/src/store/dialog/index.ts index 311418b81..8fbc0a323 100644 --- a/packages/cysync-core/src/store/dialog/index.ts +++ b/packages/cysync-core/src/store/dialog/index.ts @@ -95,6 +95,9 @@ const initialState: IDialogState = { inheritanceSilverPlanPurchase: { isOpen: false, }, + inheritanceEditExecutorMessage: { + isOpen: false, + }, }; export const dialogSlice = createSlice({ diff --git a/packages/cysync-core/src/store/dialog/types.ts b/packages/cysync-core/src/store/dialog/types.ts index a77061c6e..b4aabde10 100644 --- a/packages/cysync-core/src/store/dialog/types.ts +++ b/packages/cysync-core/src/store/dialog/types.ts @@ -147,6 +147,11 @@ export interface IDialogState { isOpen: boolean; data?: undefined; }; + + inheritanceEditExecutorMessage: { + isOpen: boolean; + data?: undefined; + }; } export const GuidedFlowMap = { diff --git a/packages/ui/src/components/molecules/Dialog/SuccessDialog.tsx b/packages/ui/src/components/molecules/Dialog/SuccessDialog.tsx index fdfd4c180..13a0cf8de 100644 --- a/packages/ui/src/components/molecules/Dialog/SuccessDialog.tsx +++ b/packages/ui/src/components/molecules/Dialog/SuccessDialog.tsx @@ -8,8 +8,17 @@ import { } from './DialogBox'; import { successIcon } from '../../../assets/images'; -import { Typography, LangDisplay, Image, Container, Button } from '../../atoms'; +import { + Typography, + LangDisplay, + Image, + Container, + Button, + TypographyProps, + CloseButton, +} from '../../atoms'; import { AlertBox } from '../AlertBox'; +import { SpacingProps } from '../../utils'; export interface SuccessDialogProps { title: string; @@ -22,6 +31,10 @@ export interface SuccessDialogProps { handleSecButtonClick?: () => void; onClose?: () => void; dontCloseOnEscape?: boolean; + width?: number; + headerType?: TypographyProps['variant']; + bodyBottomPadding?: SpacingProps['pb']; + showCloseBtn?: boolean; } export const SuccessDialog: React.FC = ({ @@ -35,23 +48,37 @@ export const SuccessDialog: React.FC = ({ handleSecButtonClick, onClose, dontCloseOnEscape, + width, + headerType, + bodyBottomPadding, + showCloseBtn, }) => ( - {headerText && ( - - - - + {(headerText || showCloseBtn) && ( + + {headerText && ( + + + + )} + + {onClose && showCloseBtn && ( + + )} )} - + Success Icon - + {subtext && ( @@ -88,4 +115,8 @@ SuccessDialog.defaultProps = { headerText: undefined, onClose: undefined, dontCloseOnEscape: undefined, + width: 500, + headerType: 'h4', + bodyBottomPadding: undefined, + showCloseBtn: false, }; From 28dfd0d6d7a6dafcfbcf8d334c0b34c90b50a7bb Mon Sep 17 00:00:00 2001 From: AryanCypherock Date: Sat, 3 Aug 2024 11:38:16 +0530 Subject: [PATCH 16/96] Feat/inheritance edit reminder time (#512) * feat: text strings and types for executor message dialog added * feat: dailog structure created * feat: executor message dialog ui created * refactor: export refactored * feat: added edit reminder time dialog * refactor: changed function names and types * fix: layout sizing and spacing issues * fix: open dialog function added * fix: layout sizing and spacing fixes and refactored lang string * fix: spacing and layout in success dialog * refactor: changed success dialog layout * refactor: fixed liniting and removed console logs * refactor: linting and exports refactored * fix: added props in reminder component and lang string updated --- .../src/i18n/lang/ar-AE.json | 28 ++++ .../src/i18n/lang/de-DE.json | 28 ++++ .../src/i18n/lang/en.json | 28 ++++ .../src/i18n/lang/id-ID.json | 28 ++++ .../src/i18n/lang/zh-CN.json | 28 ++++ .../cysync-core-constants/src/i18n/types.ts | 24 ++++ .../cysync-core/src/actions/dialog/index.ts | 3 + .../EditReminderTime/Dialogs/FetchData.tsx | 18 +++ .../Dialogs/ReminderSetup.tsx | 130 ++++++++++++++++++ .../EditReminderTime/Dialogs/Success.tsx | 25 ++++ .../EditReminderTime/Dialogs/index.tsx | 3 + .../EditReminderTime/context/index.tsx | 120 ++++++++++++++++ .../Inheritance/EditReminderTime/index.tsx | 33 +++++ .../src/dialogs/Inheritance/index.ts | 1 + packages/cysync-core/src/dialogs/index.tsx | 2 + .../cysync-core/src/store/dialog/index.ts | 3 + .../cysync-core/src/store/dialog/types.ts | 4 + .../ui/src/components/molecules/Reminder.tsx | 7 +- 18 files changed, 511 insertions(+), 2 deletions(-) create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditReminderTime/Dialogs/FetchData.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditReminderTime/Dialogs/ReminderSetup.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditReminderTime/Dialogs/Success.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditReminderTime/Dialogs/index.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditReminderTime/context/index.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditReminderTime/index.tsx diff --git a/packages/cysync-core-constants/src/i18n/lang/ar-AE.json b/packages/cysync-core-constants/src/i18n/lang/ar-AE.json index 9735748c2..7d3048aff 100644 --- a/packages/cysync-core-constants/src/i18n/lang/ar-AE.json +++ b/packages/cysync-core-constants/src/i18n/lang/ar-AE.json @@ -1511,6 +1511,34 @@ "success": { "title": "Executor message successfully encrypted" } + }, + "inheritanceEditReminderTime": { + "fetchData": { + "title": "Please wait while we fetch the data", + "subTitle": "Do not disconnect or close the app" + }, + "reminderSetup": { + "title": "Reminder setup", + "reminderInfo": { + "subtitle": "What is reminder time?", + "subtext": "Reminder Time is the interval at which you'll receive a confirmation email from Cypherock to verify your continued well-being. If no response is received within two months of the initial email, the inheritance process will commence and your nominee will receive the recovery instruction email from Cypherock" + }, + "form": { + "reminderField": { + "label": "Set reminder time interval", + "month": "Month", + "months": "Months" + } + }, + "currentReminder": "The current reminder is set at ${month}", + "loading": { + "title": "Please wait while we update the data", + "subtitle": "Do not disconnect or close the app" + } + }, + "success": { + "title": "Reminders duration updated successfully" + } } }, "toggle": { diff --git a/packages/cysync-core-constants/src/i18n/lang/de-DE.json b/packages/cysync-core-constants/src/i18n/lang/de-DE.json index 412154de4..59cae0bdc 100644 --- a/packages/cysync-core-constants/src/i18n/lang/de-DE.json +++ b/packages/cysync-core-constants/src/i18n/lang/de-DE.json @@ -1516,6 +1516,34 @@ "success": { "title": "Executor message successfully encrypted" } + }, + "inheritanceEditReminderTime": { + "fetchData": { + "title": "Please wait while we fetch the data", + "subTitle": "Do not disconnect or close the app" + }, + "reminderSetup": { + "title": "Reminder setup", + "reminderInfo": { + "subtitle": "What is reminder time?", + "subtext": "Reminder Time is the interval at which you'll receive a confirmation email from Cypherock to verify your continued well-being. If no response is received within two months of the initial email, the inheritance process will commence and your nominee will receive the recovery instruction email from Cypherock" + }, + "form": { + "reminderField": { + "label": "Set reminder time interval", + "month": "Month", + "months": "Months" + } + }, + "currentReminder": "The current reminder is set at ${month}", + "loading": { + "title": "Please wait while we update the data", + "subtitle": "Do not disconnect or close the app" + } + }, + "success": { + "title": "Reminders duration updated successfully" + } } }, "toggle": { diff --git a/packages/cysync-core-constants/src/i18n/lang/en.json b/packages/cysync-core-constants/src/i18n/lang/en.json index 4df0abba5..4471aa73d 100644 --- a/packages/cysync-core-constants/src/i18n/lang/en.json +++ b/packages/cysync-core-constants/src/i18n/lang/en.json @@ -1516,6 +1516,34 @@ "success": { "title": "Executor message successfully encrypted" } + }, + "inheritanceEditReminderTime": { + "fetchData": { + "title": "Please wait while we fetch the data", + "subTitle": "Do not disconnect or close the app" + }, + "reminderSetup": { + "title": "Reminder setup", + "reminderInfo": { + "subtitle": "What is reminder time?", + "subtext": "Reminder Time is the interval at which you'll receive a confirmation email from Cypherock to verify your continued well-being. If no response is received within two months of the initial email, the inheritance process will commence and your nominee will receive the recovery instruction email from Cypherock" + }, + "form": { + "reminderField": { + "label": "Set reminder time interval", + "month": "Month", + "months": "Months" + } + }, + "currentReminder": "The current reminder is set at ${month}", + "loading": { + "title": "Please wait while we update the data", + "subtitle": "Do not disconnect or close the app" + } + }, + "success": { + "title": "Reminders duration updated successfully" + } } }, "toggle": { diff --git a/packages/cysync-core-constants/src/i18n/lang/id-ID.json b/packages/cysync-core-constants/src/i18n/lang/id-ID.json index df3a83d6a..3ac4a6b52 100644 --- a/packages/cysync-core-constants/src/i18n/lang/id-ID.json +++ b/packages/cysync-core-constants/src/i18n/lang/id-ID.json @@ -1516,6 +1516,34 @@ "success": { "title": "Executor message successfully encrypted" } + }, + "inheritanceEditReminderTime": { + "fetchData": { + "title": "Please wait while we fetch the data", + "subTitle": "Do not disconnect or close the app" + }, + "reminderSetup": { + "title": "Reminder setup", + "reminderInfo": { + "subtitle": "What is reminder time?", + "subtext": "Reminder Time is the interval at which you'll receive a confirmation email from Cypherock to verify your continued well-being. If no response is received within two months of the initial email, the inheritance process will commence and your nominee will receive the recovery instruction email from Cypherock" + }, + "form": { + "reminderField": { + "label": "Set reminder time interval", + "month": "Month", + "months": "Months" + } + }, + "currentReminder": "The current reminder is set at ${month}", + "loading": { + "title": "Please wait while we update the data", + "subtitle": "Do not disconnect or close the app" + } + }, + "success": { + "title": "Reminders duration updated successfully" + } } }, "toggle": { diff --git a/packages/cysync-core-constants/src/i18n/lang/zh-CN.json b/packages/cysync-core-constants/src/i18n/lang/zh-CN.json index 8ac068c0c..d076a3671 100644 --- a/packages/cysync-core-constants/src/i18n/lang/zh-CN.json +++ b/packages/cysync-core-constants/src/i18n/lang/zh-CN.json @@ -1491,6 +1491,34 @@ "success": { "title": "Executor message successfully encrypted" } + }, + "inheritanceEditReminderTime": { + "fetchData": { + "title": "Please wait while we fetch the data", + "subTitle": "Do not disconnect or close the app" + }, + "reminderSetup": { + "title": "Reminder setup", + "reminderInfo": { + "subtitle": "What is reminder time?", + "subtext": "Reminder Time is the interval at which you'll receive a confirmation email from Cypherock to verify your continued well-being. If no response is received within two months of the initial email, the inheritance process will commence and your nominee will receive the recovery instruction email from Cypherock" + }, + "form": { + "reminderField": { + "label": "Set reminder time interval", + "month": "Month", + "months": "Months" + } + }, + "currentReminder": "The current reminder is set at ${month}", + "loading": { + "title": "Please wait while we update the data", + "subtitle": "Do not disconnect or close the app" + } + }, + "success": { + "title": "Reminders duration updated successfully" + } } }, "toggle": { diff --git a/packages/cysync-core-constants/src/i18n/types.ts b/packages/cysync-core-constants/src/i18n/types.ts index ed2758ce1..efdd2bb6d 100644 --- a/packages/cysync-core-constants/src/i18n/types.ts +++ b/packages/cysync-core-constants/src/i18n/types.ts @@ -790,6 +790,30 @@ interface LangDialogs { title: string; }; }; + inheritanceEditReminderTime: { + fetchData: { + title: string; + subTitle: string; + }; + reminderSetup: { + title: string; + reminderInfo: { + subtitle: string; + subtext: string; + }; + form: { + reminderField: { label: string; month: string; months: string }; + }; + currentReminder: string; + loading: { + title: string; + subtitle: string; + }; + }; + success: { + title: string; + }; + }; } interface LangToggle { diff --git a/packages/cysync-core/src/actions/dialog/index.ts b/packages/cysync-core/src/actions/dialog/index.ts index 0cc565e87..cdd5ee4bd 100644 --- a/packages/cysync-core/src/actions/dialog/index.ts +++ b/packages/cysync-core/src/actions/dialog/index.ts @@ -97,3 +97,6 @@ export const openInheritanceSilverPlanPurchaseDialog = () => export const openInheritanceEditExecutorMessageDialog = () => openDialog({ name: 'inheritanceEditExecutorMessage', data: undefined }); + +export const openInheritanceEditReminderTimeDialog = () => + openDialog({ name: 'inheritanceEditReminderTime', data: undefined }); diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditReminderTime/Dialogs/FetchData.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditReminderTime/Dialogs/FetchData.tsx new file mode 100644 index 000000000..db08d19c1 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditReminderTime/Dialogs/FetchData.tsx @@ -0,0 +1,18 @@ +import React, { useEffect } from 'react'; + +import { LoaderDialog } from '~/components'; +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritanceEditReminderTimeDialog } from '../context'; + +export const FetchData = () => { + const lang = useAppSelector(selectLanguage); + const strings = lang.strings.dialogs.inheritanceEditReminderTime.fetchData; + const { fetchData } = useInheritanceEditReminderTimeDialog(); + + useEffect(() => { + fetchData(); + }, []); + + return ; +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditReminderTime/Dialogs/ReminderSetup.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditReminderTime/Dialogs/ReminderSetup.tsx new file mode 100644 index 000000000..7a4bb541e --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditReminderTime/Dialogs/ReminderSetup.tsx @@ -0,0 +1,130 @@ +import { + Accordion, + Button, + CloseButton, + DialogBox, + DialogBoxBody, + DialogBoxFooter, + DialogBoxHeader, + Flex, + InputLabel, + LangDisplay, + MessageBox, + Reminder, + ScrollableContainer, + Typography, +} from '@cypherock/cysync-ui'; +import React, { useEffect, useState } from 'react'; + +import { LoaderDialog } from '~/components'; +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritanceEditReminderTimeDialog } from '../context'; + +export const ReminderSetup = () => { + const lang = useAppSelector(selectLanguage); + const [loading, setLoading] = useState(false); + const [currentReminder, setCurrentReminder] = useState(1); + const [newReminder, setNewReminder] = useState(1); + const { onClose, updateData } = useInheritanceEditReminderTimeDialog(); + const strings = + lang.strings.dialogs.inheritanceEditReminderTime.reminderSetup; + const { form } = strings; + + useEffect(() => { + setCurrentReminder(1); + }, []); + + if (loading) { + return ( + + ); + } + + return ( + + + + + + + + + + + + + + + + + + + setNewReminder(1)} + /> + setNewReminder(3)} + /> + setNewReminder(6)} + /> + + + + + + + + + + + + + ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditReminderTime/Dialogs/Success.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditReminderTime/Dialogs/Success.tsx new file mode 100644 index 000000000..44dcdfb3f --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditReminderTime/Dialogs/Success.tsx @@ -0,0 +1,25 @@ +import { SuccessDialog } from '@cypherock/cysync-ui'; +import React from 'react'; + +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritanceEditReminderTimeDialog } from '../context'; + +export const Success = () => { + const lang = useAppSelector(selectLanguage); + const { onClose } = useInheritanceEditReminderTimeDialog(); + const strings = lang.strings.dialogs.inheritanceEditReminderTime.success; + + return ( + + ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditReminderTime/Dialogs/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditReminderTime/Dialogs/index.tsx new file mode 100644 index 000000000..79ed52f73 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditReminderTime/Dialogs/index.tsx @@ -0,0 +1,3 @@ +export * from './FetchData'; +export * from './Success'; +export * from './ReminderSetup'; diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditReminderTime/context/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditReminderTime/context/index.tsx new file mode 100644 index 000000000..c78a4da03 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditReminderTime/context/index.tsx @@ -0,0 +1,120 @@ +import React, { + Context, + FC, + ReactNode, + createContext, + useContext, + useMemo, +} from 'react'; + +import { ITabs, useTabsAndDialogs } from '~/hooks'; +import { closeDialog, useAppDispatch } from '~/store'; + +import { FetchData, ReminderSetup, Success } from '../Dialogs'; + +export interface InheritanceEditReminderTimeDialogContextInterface { + tabs: ITabs; + onNext: (tab?: number, dialog?: number) => void; + goTo: (tab: number, dialog?: number) => void; + onPrevious: () => void; + onClose: () => void; + currentTab: number; + currentDialog: number; + isDeviceRequired: boolean; + unhandledError?: any; + fetchData: () => void; + updateData: () => void; +} + +export const InheritanceEditReminderTimeDialogContext: Context = + createContext( + {} as InheritanceEditReminderTimeDialogContextInterface, + ); + +export interface InheritanceEditReminderTimeDialogContextProviderProps { + children: ReactNode; +} + +export const InheritanceEditReminderTimeDialogProvider: FC< + InheritanceEditReminderTimeDialogContextProviderProps +> = ({ children }) => { + const dispatch = useAppDispatch(); + + const deviceRequiredDialogsMap: Record = {}; + const tabs: ITabs = [ + { + name: 'Fetch Data', + dialogs: [], + }, + { + name: 'Reminder Setup', + dialogs: [], + }, + { + name: 'Success Message', + dialogs: [], + }, + ]; + + const { + onNext, + onPrevious, + goTo, + currentTab, + currentDialog, + isDeviceRequired, + } = useTabsAndDialogs({ + deviceRequiredDialogsMap, + tabs, + dialogName: 'inheritanceEditReminderTime', + }); + + const onClose = () => { + dispatch(closeDialog('inheritanceEditReminderTime')); + }; + + const fetchData = () => { + // TODO: Implement this function + }; + + const updateData = () => { + // TODO: Implement this function + }; + + const ctx = useMemo( + () => ({ + onNext, + onPrevious, + tabs, + onClose, + goTo, + currentTab, + currentDialog, + isDeviceRequired, + fetchData, + updateData, + }), + [ + onNext, + onPrevious, + tabs, + onClose, + goTo, + currentTab, + currentDialog, + isDeviceRequired, + fetchData, + updateData, + ], + ); + + return ( + + {children} + + ); +}; + +export function useInheritanceEditReminderTimeDialog(): InheritanceEditReminderTimeDialogContextInterface { + return useContext(InheritanceEditReminderTimeDialogContext); +} diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditReminderTime/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditReminderTime/index.tsx new file mode 100644 index 000000000..c7e558f88 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditReminderTime/index.tsx @@ -0,0 +1,33 @@ +import { BlurOverlay } from '@cypherock/cysync-ui'; +import React, { FC } from 'react'; + +import { ErrorHandlerDialog } from '~/components'; + +import { + InheritanceEditReminderTimeDialogProvider, + useInheritanceEditReminderTimeDialog, +} from './context'; + +const InheritanceEditReminderTime: FC = () => { + const { tabs, currentTab, currentDialog, unhandledError, onClose } = + useInheritanceEditReminderTimeDialog(); + + return ( + + + {tabs[currentTab]?.dialogs[currentDialog]} + + + ); +}; + +export const InheritanceEditReminderTimeDialog = () => ( + + + +); diff --git a/packages/cysync-core/src/dialogs/Inheritance/index.ts b/packages/cysync-core/src/dialogs/Inheritance/index.ts index 97c0aaacb..ec184dbf0 100644 --- a/packages/cysync-core/src/dialogs/Inheritance/index.ts +++ b/packages/cysync-core/src/dialogs/Inheritance/index.ts @@ -2,3 +2,4 @@ export * from './SyncPlans'; export * from './PlanLogin'; export * from './SilverPlanPurchase'; export * from './EditExecutorMessage'; +export * from './EditReminderTime'; diff --git a/packages/cysync-core/src/dialogs/index.tsx b/packages/cysync-core/src/dialogs/index.tsx index ff16064d3..43057dc2b 100644 --- a/packages/cysync-core/src/dialogs/index.tsx +++ b/packages/cysync-core/src/dialogs/index.tsx @@ -21,6 +21,7 @@ import { GuidedFlow } from './GuidedFlow'; import { HistoryDialog } from './HistoryDialog'; import { InheritanceEditExecutorMessageDialog, + InheritanceEditReminderTimeDialog, InheritancePlanLoginDialog, InheritanceSyncPlansDialog, InheritanceSilverPlanPurchaseDialog, @@ -67,4 +68,5 @@ export const dialogs: Record = { inheritancePlanLogin: InheritancePlanLoginDialog, inheritanceSilverPlanPurchase: InheritanceSilverPlanPurchaseDialog, inheritanceEditExecutorMessage: InheritanceEditExecutorMessageDialog, + inheritanceEditReminderTime: InheritanceEditReminderTimeDialog, }; diff --git a/packages/cysync-core/src/store/dialog/index.ts b/packages/cysync-core/src/store/dialog/index.ts index 8fbc0a323..39faea431 100644 --- a/packages/cysync-core/src/store/dialog/index.ts +++ b/packages/cysync-core/src/store/dialog/index.ts @@ -98,6 +98,9 @@ const initialState: IDialogState = { inheritanceEditExecutorMessage: { isOpen: false, }, + inheritanceEditReminderTime: { + isOpen: false, + }, }; export const dialogSlice = createSlice({ diff --git a/packages/cysync-core/src/store/dialog/types.ts b/packages/cysync-core/src/store/dialog/types.ts index b4aabde10..acc6ebc70 100644 --- a/packages/cysync-core/src/store/dialog/types.ts +++ b/packages/cysync-core/src/store/dialog/types.ts @@ -152,6 +152,10 @@ export interface IDialogState { isOpen: boolean; data?: undefined; }; + inheritanceEditReminderTime: { + isOpen: boolean; + data?: undefined; + }; } export const GuidedFlowMap = { diff --git a/packages/ui/src/components/molecules/Reminder.tsx b/packages/ui/src/components/molecules/Reminder.tsx index a496d7626..15f14fa57 100644 --- a/packages/ui/src/components/molecules/Reminder.tsx +++ b/packages/ui/src/components/molecules/Reminder.tsx @@ -137,21 +137,24 @@ const DisabledDate = styled(Typography)` export interface ReminderProps extends WidthProps { date: string; disabled: boolean; + isSelected: boolean; + onClick: () => void; } export const Reminder: FC = ({ date, disabled, + onClick, + isSelected, ...restProps }) => { const [isHover, setIsHover] = useState(false); - const [isSelected, setisSelected] = useState(false); const theme = useTheme(); return !disabled ? ( setIsHover(true)} - onClick={() => setisSelected(!isSelected)} + onClick={onClick} onMouseLeave={() => setIsHover(false)} $isSelected={isSelected} $isHover={isHover} From 9437d100d1deaf5ff199b9e05f7dcc7289187c71 Mon Sep 17 00:00:00 2001 From: Championrunner <27343592+Ishaan28malik@users.noreply.github.com> Date: Thu, 8 Aug 2024 11:51:00 +0530 Subject: [PATCH 17/96] ui added for dashboardWallet inheritance component (#475) * feat: inheritance youtube play component * feat: css inline removed * feat: ui added for dashboardwallet inheritance * feat: small fix * feat: pr review changes * feat: fixes * feat: used variables * feat: ui fixes and pr review changes * feat: images fixed * feat: new designs added * feat: background image update * feat: uptes and pr review changes * feat: fixes * feat: lock file update * feat: build fix * feat: pr review changes code splitting and animation fixes * feat: bug fix * feat: bug fix * feat: icon update * fix: layout and animation fixes * fix: fixed animation and timings in setup cover state and added lang string * fix: updated ui according to the design changes --------- Co-authored-by: AryanCypherock --- .../src/i18n/lang/ar-AE.json | 17 + .../src/i18n/lang/de-DE.json | 17 + .../src/i18n/lang/en.json | 17 + .../src/i18n/lang/id-ID.json | 17 + .../src/i18n/lang/zh-CN.json | 17 + .../cysync-core-constants/src/i18n/types.ts | 19 + packages/ui/icons/alertIcon.svg | 13 + packages/ui/icons/plus-icon.svg | 11 + packages/ui/package.json | 1 + .../common/dash-wallet-default-bg-icon.svg | 3 + .../common/dash-wallet-hover-bg-icon.svg | 3 + .../common/expire-hover-wallet-icon.svg | 3 + .../images/common/gold-hover-wallet-icon.svg | 3 + packages/ui/src/assets/images/common/index.ts | 16 + .../images/common/wallet-default-expired.svg | 3 + .../images/common/wallet-default-pending.svg | 3 + .../common/wallet-hover-expired-icon.svg | 3 + .../images/common/wallet-silver-hover-bg.svg | 3 + .../DashbboardWallet.styled.ts | 333 ++++++++++++++++++ .../DashboardWallet/DashboardWallet.tsx | 260 ++++++++++++++ .../molecules/DashboardWallet/index.ts | 1 + .../molecules/DashboardWallet/utils.ts | 193 ++++++++++ packages/ui/src/components/molecules/index.ts | 1 + .../molecules/DashboardWallet.stories.tsx | 91 +++++ packages/ui/src/themes/color.styled.ts | 26 ++ packages/ui/src/themes/theme.styled.ts | 17 + pnpm-lock.yaml | 223 +++++++++++- 27 files changed, 1301 insertions(+), 13 deletions(-) create mode 100644 packages/ui/icons/alertIcon.svg create mode 100644 packages/ui/icons/plus-icon.svg create mode 100644 packages/ui/src/assets/images/common/dash-wallet-default-bg-icon.svg create mode 100644 packages/ui/src/assets/images/common/dash-wallet-hover-bg-icon.svg create mode 100644 packages/ui/src/assets/images/common/expire-hover-wallet-icon.svg create mode 100644 packages/ui/src/assets/images/common/gold-hover-wallet-icon.svg create mode 100644 packages/ui/src/assets/images/common/wallet-default-expired.svg create mode 100644 packages/ui/src/assets/images/common/wallet-default-pending.svg create mode 100644 packages/ui/src/assets/images/common/wallet-hover-expired-icon.svg create mode 100644 packages/ui/src/assets/images/common/wallet-silver-hover-bg.svg create mode 100644 packages/ui/src/components/molecules/DashboardWallet/DashbboardWallet.styled.ts create mode 100644 packages/ui/src/components/molecules/DashboardWallet/DashboardWallet.tsx create mode 100644 packages/ui/src/components/molecules/DashboardWallet/index.ts create mode 100644 packages/ui/src/components/molecules/DashboardWallet/utils.ts create mode 100644 packages/ui/src/stories/molecules/DashboardWallet.stories.tsx diff --git a/packages/cysync-core-constants/src/i18n/lang/ar-AE.json b/packages/cysync-core-constants/src/i18n/lang/ar-AE.json index 7d3048aff..c54021627 100644 --- a/packages/cysync-core-constants/src/i18n/lang/ar-AE.json +++ b/packages/cysync-core-constants/src/i18n/lang/ar-AE.json @@ -48,6 +48,23 @@ "resendOTP": "Resend OTP", "next": "Next" }, + "dashboard": { + "wallet": { + "renewNow": "Renew Now", + "buyNow": "Buy Now", + "created": "Created", + "expiredOn": "Expired on", + "expiresIn": "Expires in", + "expiry": "Expiry", + "expiring": "Expiring", + "expired": "Expired", + "pendingTime": "05", + "silver": "Silver", + "gold": "Gold", + "hours": "Hours", + "setupCover": "Setup Cypherock Cover" + } + }, "tooltips": { "downloadCsv": "تصدير كملف CSV" }, diff --git a/packages/cysync-core-constants/src/i18n/lang/de-DE.json b/packages/cysync-core-constants/src/i18n/lang/de-DE.json index 59cae0bdc..bcc1bf2c8 100644 --- a/packages/cysync-core-constants/src/i18n/lang/de-DE.json +++ b/packages/cysync-core-constants/src/i18n/lang/de-DE.json @@ -48,6 +48,23 @@ "resendOTP": "Resend OTP", "next": "Next" }, + "dashboard": { + "wallet": { + "renewNow": "Renew Now", + "buyNow": "Buy Now", + "created": "Created", + "expiredOn": "Expired on", + "expiresIn": "Expires in", + "expiry": "Expiry", + "expiring": "Expiring", + "expired": "Expired", + "pendingTime": "05", + "silver": "Silver", + "gold": "Gold", + "hours": "Hours", + "setupCover": "Setup Cypherock Cover" + } + }, "tooltips": { "downloadCsv": "Als CSV exportieren" }, diff --git a/packages/cysync-core-constants/src/i18n/lang/en.json b/packages/cysync-core-constants/src/i18n/lang/en.json index 4471aa73d..2a0075820 100644 --- a/packages/cysync-core-constants/src/i18n/lang/en.json +++ b/packages/cysync-core-constants/src/i18n/lang/en.json @@ -48,6 +48,23 @@ "resendOTP": "Resend OTP", "next": "Next" }, + "dashboard": { + "wallet": { + "renewNow": "Renew Now", + "buyNow": "Buy Now", + "created": "Created", + "expiredOn": "Expired on", + "expiresIn": "Expires in", + "expiry": "Expiry", + "expiring": "Expiring", + "expired": "Expired", + "pendingTime": "05", + "silver": "Silver", + "gold": "Gold", + "hours": "Hours", + "setupCover": "Setup Cypherock Cover" + } + }, "tooltips": { "downloadCsv": "Export as CSV" }, diff --git a/packages/cysync-core-constants/src/i18n/lang/id-ID.json b/packages/cysync-core-constants/src/i18n/lang/id-ID.json index 3ac4a6b52..aede73498 100644 --- a/packages/cysync-core-constants/src/i18n/lang/id-ID.json +++ b/packages/cysync-core-constants/src/i18n/lang/id-ID.json @@ -48,6 +48,23 @@ "resendOTP": "Resend OTP", "next": "Next" }, + "dashboard": { + "wallet": { + "renewNow": "Renew Now", + "buyNow": "Buy Now", + "created": "Created", + "expiredOn": "Expired on", + "expiresIn": "Expires in", + "expiry": "Expiry", + "expiring": "Expiring", + "expired": "Expired", + "pendingTime": "05", + "silver": "Silver", + "gold": "Gold", + "hours": "Hours", + "setupCover": "Setup Cypherock Cover" + } + }, "tooltips": { "downloadCsv": "Ekspor sebagai CSV" }, diff --git a/packages/cysync-core-constants/src/i18n/lang/zh-CN.json b/packages/cysync-core-constants/src/i18n/lang/zh-CN.json index d076a3671..4a358ba2f 100644 --- a/packages/cysync-core-constants/src/i18n/lang/zh-CN.json +++ b/packages/cysync-core-constants/src/i18n/lang/zh-CN.json @@ -48,6 +48,23 @@ "resendOTP": "Resend OTP", "next": "Next" }, + "dashboard": { + "wallet": { + "renewNow": "Renew Now", + "buyNow": "Buy Now", + "created": "Created", + "expiredOn": "Expired on", + "expiresIn": "Expires in", + "expiry": "Expiry", + "expiring": "Expiring", + "expired": "Expired", + "pendingTime": "05", + "silver": "Silver", + "gold": "Gold", + "hours": "Hours", + "setupCover": "Setup Cypherock Cover" + } + }, "tooltips": { "downloadCsv": "导出为CSV" }, diff --git a/packages/cysync-core-constants/src/i18n/types.ts b/packages/cysync-core-constants/src/i18n/types.ts index efdd2bb6d..da9e9868d 100644 --- a/packages/cysync-core-constants/src/i18n/types.ts +++ b/packages/cysync-core-constants/src/i18n/types.ts @@ -7,6 +7,7 @@ export interface LanguageStrings { allWallets: string; labels: LangLabels; buttons: LangButtons; + dashboard: LangDashboard; tooltips: LangTooltips; deviceAuthentication: LangDeviceAuthentication; lockscreen: LangLockscreen; @@ -88,6 +89,24 @@ interface LangButtons { next: string; } +interface LangDashboard { + wallet: { + renewNow: string; + buyNow: string; + created: string; + expiredOn: string; + expiresIn: string; + expiry: string; + expiring: string; + expired: string; + pendingTime: string; + silver: string; + gold: string; + hours: string; + setupCover: string; + }; +} + interface LangTooltips { downloadCsv: string; } diff --git a/packages/ui/icons/alertIcon.svg b/packages/ui/icons/alertIcon.svg new file mode 100644 index 000000000..9a6ef908d --- /dev/null +++ b/packages/ui/icons/alertIcon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/packages/ui/icons/plus-icon.svg b/packages/ui/icons/plus-icon.svg new file mode 100644 index 000000000..68be19b34 --- /dev/null +++ b/packages/ui/icons/plus-icon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packages/ui/package.json b/packages/ui/package.json index a5b6fdf56..146b5d013 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -77,6 +77,7 @@ "marked": "^13.0.2", "rc-tooltip": "^6.1.2", "react": "^18.2.0", + "react-circular-progressbar": "^2.1.0", "react-dom": "^18.2.0", "react-json-view": "^1.21.3", "react-multi-date-picker": "^4.1.2", diff --git a/packages/ui/src/assets/images/common/dash-wallet-default-bg-icon.svg b/packages/ui/src/assets/images/common/dash-wallet-default-bg-icon.svg new file mode 100644 index 000000000..386c143ed --- /dev/null +++ b/packages/ui/src/assets/images/common/dash-wallet-default-bg-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/src/assets/images/common/dash-wallet-hover-bg-icon.svg b/packages/ui/src/assets/images/common/dash-wallet-hover-bg-icon.svg new file mode 100644 index 000000000..816f61e2c --- /dev/null +++ b/packages/ui/src/assets/images/common/dash-wallet-hover-bg-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/src/assets/images/common/expire-hover-wallet-icon.svg b/packages/ui/src/assets/images/common/expire-hover-wallet-icon.svg new file mode 100644 index 000000000..386c143ed --- /dev/null +++ b/packages/ui/src/assets/images/common/expire-hover-wallet-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/src/assets/images/common/gold-hover-wallet-icon.svg b/packages/ui/src/assets/images/common/gold-hover-wallet-icon.svg new file mode 100644 index 000000000..ccdf57069 --- /dev/null +++ b/packages/ui/src/assets/images/common/gold-hover-wallet-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/src/assets/images/common/index.ts b/packages/ui/src/assets/images/common/index.ts index 57e135669..a3b676c21 100644 --- a/packages/ui/src/assets/images/common/index.ts +++ b/packages/ui/src/assets/images/common/index.ts @@ -21,6 +21,8 @@ import copyIcon from './copy.png'; import createWalletGraphics from './create-wallet-graphics.png'; import cypherockRedIcon from './cypherock-red.svg'; import cysyncLockedLogo from './cysync-locked.svg'; +import dashWalletDefaultBgIcon from './dash-wallet-default-bg-icon.svg'; +import dashWalletHoverBgIcon from './dash-wallet-hover-bg-icon.svg'; import deviceAuthAsideImage from './DeviceAuthAside.png'; import { DeviceUpdateIcon } from './DeviceUpdateIcon'; import disconnectedIcon from './disconnected.svg'; @@ -29,7 +31,9 @@ import emailIconOutlined from './email.svg'; import errorIcon from './error.svg'; import ethereumIcon from './ethereum-icon.svg'; import etheriumBlueIcon from './etherium-blue.svg'; +import expireHoverWalletIcon from './expire-hover-wallet-icon.svg'; import { FailIcon } from './FailIcon'; +import goldHoverWalletIcon from './gold-hover-wallet-icon.svg'; import goldSendIcon from './gold-send.svg'; import goldFail from './gold_fail.svg'; import greenTick from './green-tick.svg'; @@ -74,11 +78,18 @@ import triangleGreyIcon from './triangleGrey.svg'; import UniSwapLogo from './UniSwapLogo.svg'; import usbIcon from './usb.svg'; import verifyCoinIcon from './verify-coin.svg'; +import WalletDefaultExpiredIcon from './wallet-default-expired.svg'; +import WalletDefaultPendingIcon from './wallet-default-pending.svg'; import walletErrorIcon from './wallet-error.svg'; +import WalletHoverExpiredIcon from './wallet-hover-expired-icon.svg'; +import WalletHoverSilverBgIcon from './wallet-silver-hover-bg.svg'; import walletIcon from './wallet.svg'; import warningIcon from './warning.svg'; export { + WalletDefaultExpiredIcon, + WalletDefaultPendingIcon, + WalletHoverExpiredIcon, addWalletIcon, walletIcon, tetherIcon, @@ -151,6 +162,11 @@ export { UniSwapLogo, createWalletGraphics, recoverWalletFromSeedphraseGraphics, + goldHoverWalletIcon, + WalletHoverSilverBgIcon, + dashWalletDefaultBgIcon, + expireHoverWalletIcon, + dashWalletHoverBgIcon, bgClockIcon, oneInMany1Default, oneInMany1Hover, diff --git a/packages/ui/src/assets/images/common/wallet-default-expired.svg b/packages/ui/src/assets/images/common/wallet-default-expired.svg new file mode 100644 index 000000000..585965841 --- /dev/null +++ b/packages/ui/src/assets/images/common/wallet-default-expired.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/src/assets/images/common/wallet-default-pending.svg b/packages/ui/src/assets/images/common/wallet-default-pending.svg new file mode 100644 index 000000000..64161b471 --- /dev/null +++ b/packages/ui/src/assets/images/common/wallet-default-pending.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/src/assets/images/common/wallet-hover-expired-icon.svg b/packages/ui/src/assets/images/common/wallet-hover-expired-icon.svg new file mode 100644 index 000000000..73cd58846 --- /dev/null +++ b/packages/ui/src/assets/images/common/wallet-hover-expired-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/src/assets/images/common/wallet-silver-hover-bg.svg b/packages/ui/src/assets/images/common/wallet-silver-hover-bg.svg new file mode 100644 index 000000000..aa3d29db5 --- /dev/null +++ b/packages/ui/src/assets/images/common/wallet-silver-hover-bg.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/src/components/molecules/DashboardWallet/DashbboardWallet.styled.ts b/packages/ui/src/components/molecules/DashboardWallet/DashbboardWallet.styled.ts new file mode 100644 index 000000000..825a29883 --- /dev/null +++ b/packages/ui/src/components/molecules/DashboardWallet/DashbboardWallet.styled.ts @@ -0,0 +1,333 @@ +import styled from 'styled-components'; + +import { + AlertIcon, + ClockIcon, + PlusIcon, + dashWalletDefaultBgIcon, + dashWalletHoverBgIcon, +} from '../../../assets'; +import { WidthProps, width } from '../../utils'; + +export const SetupCoverContainer = styled.div` + position: relative; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + height: 100%; + transform: translateY(10px); +`; + +export const SetupCoverPlusImage = styled(PlusIcon)<{ isHover: boolean }>` + transition: transform 0.3s ease-in-out, margin-bottom 0.3s ease-in-out, + opacity 0.3s ease-in-out; + width: 34px; + height: 34px; + transform: ${({ isHover }) => + isHover ? 'rotate(-90deg) scale(2)' : 'rotate(0) scale(1)'}; + margin-bottom: ${({ isHover }) => (isHover ? '-10px' : '16px')}; + opacity: ${({ isHover }) => (isHover ? '1' : '0.4')}; +`; + +export const SetupCoverText = styled.div<{ isHover: boolean }>` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + font-family: 'Poppins'; + font-size: 12px; + color: white; + transition: transform 0.3s ease-in-out; + transform: ${({ isHover }) => + isHover ? 'translateY(500%) translateX(10%)' : 'translateY(0)'}; +`; + +export const ProgressbarWrapper = styled.div` + transition: all 0.9s ease-in-out; +`; + +export const Container = styled.div< + { isHover: boolean; isNone: boolean; backgroundImage: string } & WidthProps +>` + width: 200px; + height: 176px; + display: flex; + ${({ isNone }) => + isNone ? 'justify-content: center' : 'justify-content:normal'}; + align-items: center; + flex-direction: column; + font-family: 'Poppins'; + font-size: 14px; + font-weight: 500; + text-align: center; + color: white; + background: ${({ isHover, isNone, backgroundImage }) => + isNone + ? `url(${isHover ? dashWalletDefaultBgIcon : dashWalletHoverBgIcon})` + : `url(${backgroundImage})`}; + cursor: pointer; + position: relative; + overflow: hidden; + ${width} +`; + +export const StyledExpiredPlanIcon = styled(AlertIcon)` + position: absolute; + top: 24px; + right: 10px; + height: 14px; + width: 14px; +`; + +export const StyledExpiredClockIcon = styled(ClockIcon)` + position: absolute; + top: 24px; + right: 10px; + height: 14px; + width: 14px; + fill: #2a2827; +`; + +export const Flex = styled.div<{ isHover: boolean }>` + display: flex; + width: 45%; + height: unset; + padding: 8px 15px; + fill: #2a2827; +`; + +export const Type = styled.div<{ type: string; isHover: boolean }>` + font-family: 'Poppins'; + font-size: 12px; + font-weight: 700; + line-height: 18px; + text-align: left; + text-transform: uppercase; + background: ${({ type, theme }) => + type === 'silver' + ? theme.palette.background.secondary + : theme.palette.text.gold}; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + position: relative; + right: 95%; + opacity: 0; + transition: opacity 0.3s ease-in-out; + ${({ isHover }) => + isHover && + ` + opacity: 1; + `} +`; + +export const Expiring = styled.div<{ + isHover: boolean; + disableTransform: boolean; +}>` + font-family: 'Poppins'; + font-size: 12px; + font-weight: 500; + line-height: 18px; + text-align: left; + color: ${({ theme }) => theme.palette.error.main}; + position: relative; + right: 70%; + transform: ${({ isHover, disableTransform }) => { + if (disableTransform) return 'none'; + return isHover ? 'translateX(0)' : 'translateX(40%)'; + }}; + opacity: ${({ isHover }) => (isHover ? 1 : 0)}; + transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out; +`; + +export const TimerContainer = styled.div<{ isHover: boolean }>` + display: flex; + justify-content: center; + width: 90px; + height: 90px; + align-items: center; + margin-top: 2px; +`; + +export const TimerText = styled.div<{ isHover: boolean; theme: any }>` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + font-family: 'Poppins'; + font-size: 14px; + color: white; + text-align: center; + position: absolute; + border-radius: 40px; + height: 78px; + width: 78px; + fill: ${({ theme }) => theme.palette.background.timer.main}; + box-shadow: inset 0 0 2px 1px + ${({ theme }) => theme.palette.boxShadow.timer.main}; +`; + +export const TimerHead = styled.div<{ + isHover: boolean; + isExpired: boolean; + paymentPending: boolean; +}>` + font-family: 'Poppins'; + font-size: 10px; + font-weight: ${({ isHover, isExpired, paymentPending }) => { + if (isExpired || isHover || paymentPending) { + return '700'; + } + return '400'; + }}; + line-height: 16.5px; + text-align: left; + color: ${({ isExpired, isHover, theme, paymentPending }) => { + if (isExpired) { + return theme.palette.warn.main; + } + if (isHover || paymentPending) { + return 'white'; + } + return theme.palette.muted.main; + }}; +`; + +export const getColor = ( + isExpiring: boolean, + isHover: boolean, + isExpired: boolean, + paymentPending: boolean, + theme: any, +) => { + if (isExpiring && isHover) { + return theme.palette.muted.main; + } + if (isExpiring) { + return theme.palette.warn.main; + } + if (isHover) { + return theme.palette.muted.main; + } + if (isExpired || paymentPending) { + return theme.palette.muted.main; + } + return 'white'; +}; + +export const getFontWeight = ( + isHover: boolean, + isExpired: boolean, + paymentPending: boolean, +) => (isHover || isExpired || paymentPending ? '400' : '700'); + +export const TimerSubtitle = styled.div<{ + isHover: boolean; + isExpiring: boolean; + isExpired: boolean; + paymentPending: boolean; +}>` + font-family: 'Poppins'; + font-size: 10px; + font-weight: ${({ isHover, isExpired, paymentPending }) => + getFontWeight(isHover, isExpired, paymentPending)}; + line-height: 18px; + text-align: left; + text-transform: uppercase; + color: ${({ isExpiring, isHover, isExpired, paymentPending, theme }) => + getColor(isExpiring, isHover, isExpired, paymentPending, theme)}; +`; + +export const getWalletNameColor = ( + isHover: boolean, + isExpiring: boolean, + isExpired: boolean, + paymentPending: boolean, + theme: any, +) => { + if (isHover && (isExpiring || isExpired || paymentPending)) { + return theme.palette.background.golden; + } + return theme.palette.muted.main; +}; + +export const WalletNameContainer = styled.div<{ + isHover: boolean; + isExpiring: boolean; + isExpired: boolean; + paymentPending: boolean; +}>` + position: relative; + width: 100%; + height: 21px; + overflow: hidden; + font-family: 'Poppins'; + font-size: 14px; + font-weight: 600; + line-height: 21px; + text-align: center; + margin-top: 8px; + color: ${({ isHover, isExpiring, isExpired, paymentPending, theme }) => + getWalletNameColor(isHover, isExpiring, isExpired, paymentPending, theme)}; +`; + +export const WalletNameText = styled.div<{ + isHover: boolean; + disableAnimation?: boolean; +}>` + position: absolute; + width: 100%; + transition: ${({ disableAnimation }) => + disableAnimation + ? 'none' + : 'transform 0.3s ease-in-out, opacity 0.3s ease-in-out'}; + transform: ${({ isHover, disableAnimation }) => + isHover && !disableAnimation ? 'translateX(0)' : 'translateX(15%)'}; + opacity: ${({ isHover, disableAnimation }) => + isHover && !disableAnimation ? 1 : 0}; +`; + +export const WalletNameHoverText = styled(WalletNameText)<{ + isHover: boolean; + disableAnimation?: boolean; +}>` + transform: ${({ isHover, disableAnimation }) => + isHover && !disableAnimation ? 'translateX(0)' : 'translateX(-15%)'}; + opacity: ${({ isHover, disableAnimation }) => + isHover && !disableAnimation ? 1 : 0}; +`; + +export const TransitionTextWrapper = styled.div` + position: relative; + display: flex; + justify-content: center; + align-items: center; + height: 100%; +`; + +export const TransitionText = styled.div<{ isHover: boolean }>` + position: absolute; + transition: opacity 0.3s ease-in-out, transform 0.3s ease-in-out; + opacity: ${props => (props.isHover ? 1 : 0)}; + transform: ${props => + props.isHover + ? 'translateY(0px) translateX(0px)' + : 'translateY(1px) translateX(-1px)'}; + width: max-content; + text-align: center; + margin-top: 20px; +`; + +export const TransitionTextSubtitle = styled.div<{ isHover: boolean }>` + position: absolute; + transition: opacity 0.3s ease-in-out, transform 0.3s ease-in-out; + opacity: ${props => (props.isHover ? 1 : 0)}; + transform: ${props => + props.isHover + ? 'translateY(0px) translateX(0px)' + : 'translateY(1px) translateX(-1px)'}; + width: max-content; + text-align: center; + margin-bottom: 20px; +`; diff --git a/packages/ui/src/components/molecules/DashboardWallet/DashboardWallet.tsx b/packages/ui/src/components/molecules/DashboardWallet/DashboardWallet.tsx new file mode 100644 index 000000000..bf0a3148b --- /dev/null +++ b/packages/ui/src/components/molecules/DashboardWallet/DashboardWallet.tsx @@ -0,0 +1,260 @@ +import React, { FC, useState, useEffect } from 'react'; +import { CircularProgressbar, buildStyles } from 'react-circular-progressbar'; + +import 'react-circular-progressbar/dist/styles.css'; +import { DefaultTheme } from 'styled-components/dist/types'; + +import { + SetupCoverPlusImage, + ProgressbarWrapper, + Container, + StyledExpiredPlanIcon, + StyledExpiredClockIcon, + Flex, + Type, + Expiring, + TimerContainer, + TimerText, + TimerHead, + TimerSubtitle, + WalletNameContainer, + WalletNameText, + WalletNameHoverText, + TransitionTextWrapper, + TransitionText, + TransitionTextSubtitle, + SetupCoverContainer, + SetupCoverText, +} from './DashbboardWallet.styled'; +import { + calculateHoverText, + getBackgroundImage, + getExpiringText, + getPathColor, + getTimerHeadText, + getTypeText, + updateHoverOnly, + updateProgressAndHover, +} from './utils'; + +import { useTheme } from '../../../themes'; +import { WidthProps } from '../../utils'; +import { format } from 'date-fns'; + +export interface DashboardWalletProps extends WidthProps { + isNone: boolean; + type: 'silver' | 'gold'; + isExpiring: boolean; + isExpired: boolean; + paymentPending: boolean; + name: string; + lang: { + dashboard: { + wallet: { + renewNow: string; + buyNow: string; + created: string; + expiredOn: string; + expiresIn: string; + expiry: string; + expiring: string; + expired: string; + pendingTime: string; + silver: string; + gold: string; + hours: string; + setupCover: string; + }; + }; + }; + startDate: string; + expiryDate: string; + status: 'Active' | 'Inactive' | 'Pending'; +} + +export const DashboardWallet: FC = ({ + isNone, + type, + isExpiring, + isExpired, + paymentPending, + name, + lang, + startDate, + expiryDate, + status, + ...restProps +}) => { + const [isHover, setIsHover] = useState(false); + const [progress, setProgress] = useState({ value: 0, rotation: 0 }); + const theme = useTheme() as DefaultTheme; + const hoverText = calculateHoverText( + isExpiring, + isExpired, + paymentPending, + name, + lang, + ); + const disableAnimation = name === hoverText; + const [hoverProgress, setHoverProgress] = useState({ value: 0, rotation: 0 }); + + useEffect(() => { + updateProgressAndHover( + startDate, + expiryDate, + status, + isExpired, + paymentPending, + setProgress, + setHoverProgress, + ); + }, [startDate, expiryDate, status, isExpired, paymentPending]); + + useEffect(() => { + updateHoverOnly( + startDate, + expiryDate, + status, + setProgress, + setHoverProgress, + ); + }, [startDate, expiryDate, status]); + + return ( + setIsHover(true)} + onMouseLeave={() => setIsHover(false)} + onClick={() => setIsHover(true)} + role="button" + tabIndex={0} + onKeyDown={e => { + if (e.key === 'Enter' || e.key === ' ') setIsHover(true); + }} + {...restProps} + > + {isNone && ( + + + + {lang.dashboard.wallet.setupCover} + + + )} + {!isNone && ( + <> + + + {getTypeText(type, lang)} + + + {getExpiringText(isExpiring, isExpired, lang)} + + + + + + + + + + + {getTimerHeadText(isHover, isExpired, paymentPending, lang)} + + + + + {getTimerHeadText(isHover, isExpired, paymentPending, lang)} + + + + + + + {format(new Date(startDate), 'dd MMM yyyy')} + + + + + {format(new Date(expiryDate), 'dd MMM yyyy')} + + + + + + {isExpired && } + {paymentPending && ( + + )} + + {disableAnimation && name} + + {name} + + + {hoverText} + + + + )} + + ); +}; diff --git a/packages/ui/src/components/molecules/DashboardWallet/index.ts b/packages/ui/src/components/molecules/DashboardWallet/index.ts new file mode 100644 index 000000000..6525cf356 --- /dev/null +++ b/packages/ui/src/components/molecules/DashboardWallet/index.ts @@ -0,0 +1 @@ +export * from './DashboardWallet'; diff --git a/packages/ui/src/components/molecules/DashboardWallet/utils.ts b/packages/ui/src/components/molecules/DashboardWallet/utils.ts new file mode 100644 index 000000000..d2a4b1168 --- /dev/null +++ b/packages/ui/src/components/molecules/DashboardWallet/utils.ts @@ -0,0 +1,193 @@ +import { DefaultTheme } from 'styled-components/dist/types'; + +import { + WalletHoverExpiredIcon, + WalletHoverSilverBgIcon, + expireHoverWalletIcon, + goldHoverWalletIcon, + WalletDefaultExpiredIcon, + WalletDefaultPendingIcon, + dashWalletDefaultBgIcon, +} from '../../../assets'; + +export const calculateProgress = ( + startDate: string, + expiryDate: string, + status: 'Active' | 'Inactive' | 'Pending', +) => { + const start = new Date(startDate); + const expiry = new Date(expiryDate); + const now = new Date(); + + const totalDuration = expiry.getTime() - start.getTime(); + const elapsedDuration = now.getTime() - start.getTime(); + + let value = (elapsedDuration / totalDuration) * 100; + let rotation = 1 - elapsedDuration / totalDuration; + + if (status === 'Pending') { + value = 0; + rotation = 0; + } + + return { + value: value > 100 ? 100 : value, + rotation: rotation > 1 ? 1 : rotation, + }; +}; + +export const getCurrentTime = () => { + const date = new Date(); + return date.toLocaleTimeString([], { + hourCycle: 'h23', + hour: '2-digit', + minute: '2-digit', + }); +}; + +export const getCurrentFullTime = () => { + const date = new Date(); + return date.toLocaleTimeString([], { + hourCycle: 'h23', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + }); +}; + +export const getBackgroundImage = ( + isHover: boolean, + isExpired: boolean, + type: string, + isExpiring: boolean, + paymentPending: boolean, +) => { + if (isHover) { + if (isExpired) return WalletHoverExpiredIcon; + if (type === 'silver') return WalletHoverSilverBgIcon; + if (isExpiring) return expireHoverWalletIcon; + return goldHoverWalletIcon; + } + if (paymentPending) return WalletDefaultPendingIcon; + if (isExpired) return WalletDefaultExpiredIcon; + return dashWalletDefaultBgIcon; +}; + +export const getTypeText = (type: string, lang: any) => + type === 'silver' ? lang.dashboard.wallet.silver : lang.dashboard.wallet.gold; + +export const getExpiringText = ( + isExpiring: boolean, + isExpired: boolean, + lang: any, +): string => { + if (isExpiring) { + return lang.dashboard.wallet.expiring; + } + if (isExpired) { + return lang.dashboard.wallet.expired; + } + return ''; +}; + +export const getPathColor = ( + isHover: boolean, + isExpiring: boolean, + isExpired: boolean, + paymentPending: boolean, + theme: DefaultTheme, + type: string, +) => { + if (isHover && (isExpiring || isExpired)) { + return theme.palette.warn.main; + } + if (type === 'silver' && !paymentPending) { + return theme.palette.background.silver; + } + return theme.palette.background.golden; +}; + +export const calculateDiffDays = (expiryDate: string) => { + const diffTime = Math.abs( + new Date(expiryDate).getTime() - new Date().getTime(), + ); + const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); + return diffDays; +}; + +export const getTimerHeadText = ( + isHover: boolean, + isExpired: boolean, + paymentPending: boolean, + lang: any, +) => { + if (isHover || paymentPending) { + return lang.dashboard.wallet.created; + } + if (isExpired) { + return lang.dashboard.wallet.expiredOn; + } + return lang.dashboard.wallet.expiry; +}; + +export const calculateHoverText = ( + isExpiring: boolean, + isExpired: boolean, + paymentPending: boolean, + name: string, + lang: { dashboard: any }, +) => { + if (isExpiring || isExpired) { + return lang.dashboard.wallet.renewNow; + } + if (paymentPending) { + return lang.dashboard.wallet.buyNow; + } + return name; +}; + +export const updateProgressAndHover = ( + startDate: string, + expiryDate: string, + status: 'Active' | 'Inactive' | 'Pending', + isExpired: any, + paymentPending: any, + setProgress: (arg0: { value: number; rotation: number }) => void, + setHoverProgress: (arg0: { value: number; rotation: number }) => void, +) => { + let value = 0; + let rotation = 0; + let hoverValue = 100; + + if (isExpired) { + value = 0; + hoverValue = 100; + rotation = 0; + } else if (paymentPending) { + value = 0; + hoverValue = 0; + rotation = 0; + } else { + const progressData = calculateProgress(startDate, expiryDate, status); + value = progressData.value; + rotation = progressData.rotation; + hoverValue = 100 - value; + } + + setProgress({ value, rotation }); + setHoverProgress({ value: hoverValue, rotation }); +}; + +export const updateHoverOnly = ( + startDate: string, + expiryDate: string, + status: 'Active' | 'Inactive' | 'Pending', + setProgress: (arg0: { value: number; rotation: number }) => void, + setHoverProgress: (arg0: { value: number; rotation: number }) => void, +) => { + const { value, rotation } = calculateProgress(startDate, expiryDate, status); + const hoverValue = 100 - value; + const hoverRotation = 0; + setProgress({ value, rotation }); + setHoverProgress({ value: hoverValue, rotation: hoverRotation }); +}; diff --git a/packages/ui/src/components/molecules/index.ts b/packages/ui/src/components/molecules/index.ts index 2a2e8e738..f53128c53 100644 --- a/packages/ui/src/components/molecules/index.ts +++ b/packages/ui/src/components/molecules/index.ts @@ -40,6 +40,7 @@ export * from './Topbar'; export * from './UpdateBar'; export * from './WallectConnect'; export * from './WalletDialogMainContainer'; +export * from './DashboardWallet'; export * from './ManyInMany'; export * from './Reminder'; export * from './Accordion'; diff --git a/packages/ui/src/stories/molecules/DashboardWallet.stories.tsx b/packages/ui/src/stories/molecules/DashboardWallet.stories.tsx new file mode 100644 index 000000000..2df60a330 --- /dev/null +++ b/packages/ui/src/stories/molecules/DashboardWallet.stories.tsx @@ -0,0 +1,91 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { DashboardWallet } from '../../components'; + +const meta: Meta = { + component: DashboardWallet, + tags: ['autodocs'], +}; + +export default meta; + +type Story = StoryObj; + +const lang = { + dashboard: { + wallet: { + renewNow: 'Renew Now', + buyNow: 'Buy Now', + created: 'Created', + expiredOn: 'Expired on', + expiresIn: 'Expires in', + expiry: 'Expiry', + expiring: 'Expiring', + expired: 'Expired', + pendingTime: '05', + silver: 'Silver', + gold: 'Gold', + hours: 'Hours', + setupCover: 'Setup Cypherock Cover', + }, + }, +}; + +export const Default: Story = { + args: { + isNone: false, + type: 'silver', + isExpiring: false, + isExpired: false, + paymentPending: false, + name: 'My Default Wallet', + lang, + startDate: '2024-01-21', + expiryDate: '2024-09-21', + status: 'Active', + }, +}; + +export const SetupCover: Story = { + args: { + ...Default.args, + isNone: true, + }, +}; + +export const SilverPlan: Story = { + args: { + ...Default.args, + type: 'silver', + }, +}; + +export const GoldPlan: Story = { + args: { + ...Default.args, + type: 'gold', + name: 'My Gold Wallet', + }, +}; + +export const ExpiringPlan: Story = { + args: { + ...Default.args, + isExpiring: true, + }, +}; + +export const ExpiredPlan: Story = { + args: { + ...Default.args, + isExpired: true, + expiryDate: '2023-01-21', + }, +}; + +export const PendingPayment: Story = { + args: { + ...Default.args, + paymentPending: true, + }, +}; diff --git a/packages/ui/src/themes/color.styled.ts b/packages/ui/src/themes/color.styled.ts index 48d1bee2e..02ff7df88 100644 --- a/packages/ui/src/themes/color.styled.ts +++ b/packages/ui/src/themes/color.styled.ts @@ -17,6 +17,20 @@ export const colors = { silver: 'linear-gradient(180deg, #A2ADB3 -2.08%, #F3F1F2 34.27%, #BCC3C9 66.28%, #DCDFE4 102.08%)', stripe: 'linear-gradient(90deg, #211C18 1.69%, #242018 100%)', + conicGradient: { + default: + 'conic-gradient(from 0deg, transparent,90deg, transparent, 90deg, #E9B873 ,180deg, #FEDD8F, 270deg, #B78D51, 360deg ,transparent, 360deg, transparent)', + secondary: + 'conic-gradient(from 0deg, #A2ADB3 ,33deg, #F3F1F2, 67deg, #BCC3C9, 101deg, #DCDFE4,135deg ,transparent, 135deg, transparent)', + expirig: + 'conic-gradient(from 0deg, #FF624C ,270deg, #FF624C,270deg ,transparent, 270deg, transparent)', + golden: + 'conic-gradient(from 0deg, #E9B873 ,30deg, #FEDD8F, 60deg, #B78D51, 90deg ,transparent, 90deg, transparent)', + silver: + 'conic-gradient(from 0deg, transparent,135deg, transparent, 135deg, #A2ADB3 ,191deg, #F3F1F2, 247deg, #BCC3C9, 304deg, #DCDFE4, 360deg ,transparent, 360deg, transparent)', + notExpiring: + 'conic-gradient(from 0deg, transparent, 270deg, transparent, 270deg, #E9B873 ,300deg, #FEDD8F, 330deg, #B78D51, 360deg ,transparent, 360deg, transparent)', + }, cardSelected: 'linear-gradient(285deg, rgba(96, 58, 23, 0.20) 0%, rgba(0, 0, 0, 0.00) 60.65%), #2A2827', cardHover: @@ -63,6 +77,10 @@ export const colors = { }, boxShadow: { selected: '#1B1813', + timer: { + main: '#2e2523', + text: '#242322', + }, }, background: { progressBar: '#1F1915', @@ -94,6 +112,14 @@ export const colors = { filterItem: `#1F1C19`, calendar: '#342F2C', calendarHeader: '#211C18', + timer: { + main: '#3e3a38', + default: '#261f17', + silver: '#26221e', + expiring: '#271a15', + secondary: '#2a2827', + }, + silver: '#a2adb3', slate: '#312d2a', cardDisabled: '#282522', cardSelected: '#2A2827', diff --git a/packages/ui/src/themes/theme.styled.ts b/packages/ui/src/themes/theme.styled.ts index 45e94373a..72fa8d237 100644 --- a/packages/ui/src/themes/theme.styled.ts +++ b/packages/ui/src/themes/theme.styled.ts @@ -46,6 +46,9 @@ export const theme = { muted: { main: colors.text.muted, }, + error: { + main: colors.text.error, + }, golden: colors.gradients.golden, silver: colors.gradients.silver, highlight: colors.gradients.highlight, @@ -107,6 +110,20 @@ export const theme = { popup: colors.border.popup, danger: colors.background.danger, error: colors.background.error, + silver: colors.background.silver, + timer: { + main: colors.background.timer.main, + default: colors.background.timer.default, + silver: colors.background.timer.silver, + expiring: colors.background.timer.expiring, + secondary: colors.background.timer.secondary, + }, + }, + boxShadow: { + timer: { + main: colors.boxShadow.timer.main, + text: colors.boxShadow.timer.text, + }, slate: colors.background.slate, cardSelected: colors.background.cardSelected, cardDisabled: colors.background.cardDisabled, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c18895875..9c91f171a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.1' +lockfileVersion: '6.0' settings: autoInstallPeers: true @@ -381,7 +381,7 @@ importers: version: 4.9.5 vite: specifier: ^4.3.9 - version: 4.3.9(@types/node@18.15.11)(less@4.2.0) + version: 4.3.9(@types/node@20.5.1) vite-plugin-electron: specifier: ^0.12.0 version: 0.12.0 @@ -1577,7 +1577,7 @@ importers: version: 8.43.0 eslint-import-resolver-typescript: specifier: ^3.5.5 - version: 3.5.5(@typescript-eslint/parser@5.59.11)(eslint-plugin-import@2.27.5)(eslint@8.43.0) + version: 3.5.5(eslint-plugin-import@2.27.5)(eslint@8.43.0) ethers: specifier: ^6.7.0 version: 6.7.0 @@ -1756,7 +1756,7 @@ importers: version: 8.43.0 eslint-import-resolver-typescript: specifier: ^3.5.5 - version: 3.5.5(@typescript-eslint/parser@5.59.11)(eslint-plugin-import@2.27.5)(eslint@8.43.0) + version: 3.5.5(eslint-plugin-import@2.27.5)(eslint@8.43.0) jest: specifier: ^29.5.0 version: 29.5.0(@types/node@18.15.11)(ts-node@10.9.1) @@ -2069,6 +2069,9 @@ importers: react: specifier: ^18.2.0 version: 18.2.0 + react-circular-progressbar: + specifier: ^2.1.0 + version: 2.1.0(react@18.2.0) react-dom: specifier: ^18.2.0 version: 18.2.0(react@18.2.0) @@ -5241,7 +5244,6 @@ packages: /@colors/colors@1.5.0: resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} - requiresBuild: true /@colors/colors@1.6.0: resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} @@ -12188,7 +12190,7 @@ packages: '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.22.5) '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.22.5) react-refresh: 0.14.2 - vite: 4.3.9(@types/node@18.15.11)(less@4.2.0) + vite: 4.3.9(@types/node@20.5.1) transitivePeerDependencies: - supports-color dev: true @@ -13436,7 +13438,7 @@ packages: '@babel/core': 7.22.5 find-cache-dir: 3.3.2 schema-utils: 4.2.0 - webpack: 5.92.1(esbuild@0.18.20) + webpack: 5.92.1 dev: true /babel-plugin-istanbul@6.1.1: @@ -13743,6 +13745,7 @@ packages: /boolean@3.2.0: resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==} + requiresBuild: true dev: true optional: true @@ -15155,7 +15158,7 @@ packages: postcss-modules-values: 4.0.0(postcss@8.4.38) postcss-value-parser: 4.2.0 semver: 7.5.3 - webpack: 5.92.1(esbuild@0.18.20) + webpack: 5.92.1 dev: true /css-select@4.3.0: @@ -15704,6 +15707,7 @@ packages: /detect-node@2.1.0: resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + requiresBuild: true dev: true optional: true @@ -16560,6 +16564,30 @@ packages: - supports-color dev: true + /eslint-import-resolver-typescript@3.5.5(eslint-plugin-import@2.27.5)(eslint@8.43.0): + resolution: {integrity: sha512-TdJqPHs2lW5J9Zpe17DZNQuDnox4xo2o+0tE7Pggain9Rbc19ik8kFtXdxZ250FVx2kF4vlt2RSf4qlUpG7bhw==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + dependencies: + debug: 4.3.5(supports-color@8.1.1) + enhanced-resolve: 5.17.0 + eslint: 8.43.0 + eslint-module-utils: 2.8.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5)(eslint@8.43.0) + eslint-plugin-import: 2.27.5(eslint-import-resolver-typescript@3.5.5)(eslint@8.43.0) + get-tsconfig: 4.7.5 + globby: 13.2.2 + is-core-module: 2.14.0 + is-glob: 4.0.3 + synckit: 0.8.8 + transitivePeerDependencies: + - '@typescript-eslint/parser' + - eslint-import-resolver-node + - eslint-import-resolver-webpack + - supports-color + dev: true + /eslint-module-utils@2.8.1(@typescript-eslint/parser@5.59.11)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5)(eslint@8.43.0): resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} engines: {node: '>=4'} @@ -16590,6 +16618,35 @@ packages: - supports-color dev: true + /eslint-module-utils@2.8.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5)(eslint@8.43.0): + resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + dependencies: + debug: 3.2.7 + eslint: 8.43.0 + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.5.5(eslint-plugin-import@2.27.5)(eslint@8.43.0) + transitivePeerDependencies: + - supports-color + dev: true + /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.59.11)(eslint-import-resolver-typescript@3.5.5)(eslint@8.43.0): resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} engines: {node: '>=4'} @@ -16623,6 +16680,38 @@ packages: - supports-color dev: true + /eslint-plugin-import@2.27.5(eslint-import-resolver-typescript@3.5.5)(eslint@8.43.0): + resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + dependencies: + array-includes: 3.1.8 + array.prototype.flat: 1.3.2 + array.prototype.flatmap: 1.3.2 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 8.43.0 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.8.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5)(eslint@8.43.0) + has: 1.0.4 + is-core-module: 2.14.0 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.values: 1.2.0 + resolve: 1.22.8 + semver: 6.3.1 + tsconfig-paths: 3.15.0 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: true + /eslint-plugin-jsx-a11y@6.7.1(eslint@8.43.0): resolution: {integrity: sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==} engines: {node: '>=4.0'} @@ -20859,6 +20948,7 @@ packages: /matcher@3.0.0: resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==} engines: {node: '>=10'} + requiresBuild: true dependencies: escape-string-regexp: 4.0.0 dev: true @@ -21544,6 +21634,7 @@ packages: /node-gyp-build-optional-packages@5.2.2: resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} hasBin: true + requiresBuild: true dependencies: detect-libc: 2.0.3 dev: true @@ -21614,7 +21705,7 @@ packages: webpack: ^5.0.0 dependencies: loader-utils: 2.0.4 - webpack: 5.92.1(esbuild@0.18.20) + webpack: 5.92.1 dev: true /node-preload@0.2.1: @@ -22389,9 +22480,6 @@ packages: resolution: {integrity: sha512-2GTVocFkwblV/TIg9AmT7TI2fO4xdWkyN8aFUEVtiVNWt96GTR3FgQyHFValfCbcj1k9Xf962Ws2hYXYUr9k1Q==} engines: {node: '>= 12.0.0'} hasBin: true - peerDependenciesMeta: - '@parcel/core': - optional: true dependencies: '@parcel/config-default': 2.9.3(@parcel/core@2.9.3)(typescript@4.9.5) '@parcel/core': 2.9.3 @@ -23010,6 +23098,7 @@ packages: /prr@1.0.1: resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + requiresBuild: true dev: true optional: true @@ -23288,6 +23377,14 @@ packages: pure-color: 1.3.0 dev: false + /react-circular-progressbar@2.1.0(react@18.2.0): + resolution: {integrity: sha512-xp4THTrod4aLpGy68FX/k1Q3nzrfHUjUe5v6FsdwXBl3YVMwgeXYQKDrku7n/D6qsJA9CuunarAboC2xCiKs1g==} + peerDependencies: + react: ^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + /react-colorful@5.6.1(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==} peerDependencies: @@ -24080,6 +24177,7 @@ packages: /roarr@2.15.4: resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==} engines: {node: '>=8.0'} + requiresBuild: true dependencies: boolean: 3.2.0 detect-node: 2.1.0 @@ -24253,6 +24351,7 @@ packages: /semver-compare@1.0.0: resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} + requiresBuild: true dev: true optional: true @@ -24314,6 +24413,7 @@ packages: /serialize-error@7.0.1: resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} engines: {node: '>=10'} + requiresBuild: true dependencies: type-fest: 0.13.1 dev: true @@ -25110,7 +25210,7 @@ packages: peerDependencies: webpack: ^5.0.0 dependencies: - webpack: 5.92.1(esbuild@0.18.20) + webpack: 5.92.1 dev: true /styled-components@6.0.0-rc.1(react-dom@18.2.0)(react@18.2.0): @@ -25393,6 +25493,30 @@ packages: webpack: 5.92.1(esbuild@0.18.20) dev: true + /terser-webpack-plugin@5.3.10(webpack@5.92.1): + resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + jest-worker: 27.5.1 + schema-utils: 3.3.0 + serialize-javascript: 6.0.2 + terser: 5.31.1 + webpack: 5.92.1 + dev: true + /terser@4.8.1: resolution: {integrity: sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==} engines: {node: '>=6.0.0'} @@ -26941,6 +27065,39 @@ packages: fsevents: 2.3.3 dev: true + /vite@4.3.9(@types/node@20.5.1): + resolution: {integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 20.5.1 + esbuild: 0.17.19 + postcss: 8.4.38 + rollup: 3.29.4 + optionalDependencies: + fsevents: 2.3.3 + dev: true + /vm-browserify@1.1.2: resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} dev: true @@ -27287,6 +27444,46 @@ packages: /webpack-virtual-modules@0.6.2: resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + /webpack@5.92.1: + resolution: {integrity: sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.5 + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/wasm-edit': 1.12.1 + '@webassemblyjs/wasm-parser': 1.12.1 + acorn: 8.12.0 + acorn-import-attributes: 1.9.5(acorn@8.12.0) + browserslist: 4.23.1 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.17.0 + es-module-lexer: 1.5.4 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 3.3.0 + tapable: 2.2.1 + terser-webpack-plugin: 5.3.10(webpack@5.92.1) + watchpack: 2.4.1 + webpack-sources: 3.2.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + dev: true + /webpack@5.92.1(esbuild@0.18.20): resolution: {integrity: sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA==} engines: {node: '>=10.13.0'} From 599405909f696d22b73dfcac5db09b4a75857007 Mon Sep 17 00:00:00 2001 From: AryanCypherock Date: Thu, 8 Aug 2024 12:07:01 +0530 Subject: [PATCH 18/96] feat/inheritance payment promo (#515) * feat: payment promo ui created * feat: added invalid state ui in the coupon input element * fix: fixed error message overflow issue * refactor: used maps instead of if else conditions --- packages/ui/icons/open-external-link-icon.svg | 7 + packages/ui/icons/shopping-cart.svg | 3 + .../ui/src/components/atoms/ExternalLink.tsx | 102 ++++++++---- .../src/components/molecules/CouponInput.tsx | 22 ++- .../ui/src/components/molecules/Payment.tsx | 145 ++++++++++++++++++ packages/ui/src/components/molecules/index.ts | 1 + .../ui/src/components/utils/bgColor.styled.ts | 9 +- .../stories/molecules/CouponInput.stories.tsx | 11 +- .../src/stories/molecules/Payment.stories.tsx | 65 ++++++++ 9 files changed, 326 insertions(+), 39 deletions(-) create mode 100644 packages/ui/icons/open-external-link-icon.svg create mode 100644 packages/ui/icons/shopping-cart.svg create mode 100644 packages/ui/src/components/molecules/Payment.tsx create mode 100644 packages/ui/src/stories/molecules/Payment.stories.tsx diff --git a/packages/ui/icons/open-external-link-icon.svg b/packages/ui/icons/open-external-link-icon.svg new file mode 100644 index 000000000..5082a4875 --- /dev/null +++ b/packages/ui/icons/open-external-link-icon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packages/ui/icons/shopping-cart.svg b/packages/ui/icons/shopping-cart.svg new file mode 100644 index 000000000..20262fcc6 --- /dev/null +++ b/packages/ui/icons/shopping-cart.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/src/components/atoms/ExternalLink.tsx b/packages/ui/src/components/atoms/ExternalLink.tsx index c555e1874..f6d1e4ee6 100644 --- a/packages/ui/src/components/atoms/ExternalLink.tsx +++ b/packages/ui/src/components/atoms/ExternalLink.tsx @@ -3,47 +3,87 @@ import React from 'react'; import { Bullet } from './Bullet'; import { Container } from './Container'; import { Flex } from './Flex'; -import { Image } from './Image'; import { LangDisplay } from './LangDisplay'; -import { Typography } from './Typography'; +import { Typography, TypographyColor } from './Typography'; -import { openExternalLink } from '../../assets'; -import { UtilsProps } from '../utils'; +import { OpenExternalLinkIcon } from '../../assets'; +import { BgColor, UtilsProps } from '../utils'; +import { useTheme } from '../../themes'; + +export type ExternalLinkVariantTypes = 'disabled' | 'golden'; export interface ExternalLinkProps extends UtilsProps { + type?: ExternalLinkVariantTypes; + icon?: React.ReactNode; text: string; href: string; } +const bgColorMap: Record = { + golden: 'golden', + disabled: 'slate', +}; + +const textColorMap: Record = { + golden: 'black', + disabled: 'disabled', +}; + export const ExternalLink: React.FC = ({ + type, + icon, text, href, ...restProps -}) => ( - - - - - - - +}) => { + const theme = useTheme(); + const fillColorMap: Record = { + golden: theme.palette.text.black, + disabled: theme.palette.text.disabled, + default: theme.palette.text.white, + }; + + let bgColor: BgColor = 'input'; + let textColor: TypographyColor = 'heading'; + let fillColor = fillColorMap.default; + + if (type) { + bgColor = bgColorMap[type] ?? 'input'; + textColor = textColorMap[type] ?? 'heading'; + fillColor = fillColorMap[type]; + } + + return ( + + + + {icon ?? } + + + + + + + - - openLink - - - -); + + ); +}; + +ExternalLink.defaultProps = { + type: undefined, + icon: undefined, +}; diff --git a/packages/ui/src/components/molecules/CouponInput.tsx b/packages/ui/src/components/molecules/CouponInput.tsx index 8c159f4ed..6426a2cd0 100644 --- a/packages/ui/src/components/molecules/CouponInput.tsx +++ b/packages/ui/src/components/molecules/CouponInput.tsx @@ -15,7 +15,7 @@ const Container = styled.div` ${width} `; -const InputField = styled.input` +const InputField = styled.input<{ isInvalid: boolean }>` display: flex; padding: 12px 16px; font-size: 14px; @@ -38,11 +38,14 @@ const InputField = styled.input` } border-radius: 8px 0px 0px 8px; - border: 1px solid ${({ theme }) => theme.palette.border.separator}; + border: 1px solid ${({ theme, isInvalid }) => + isInvalid + ? theme.palette.background.danger + : theme.palette.border.separator}; border-right: none; `; -const ApplyButton = styled.button` +const ApplyButton = styled.button<{ isInvalid: boolean }>` border-radius: 0px 8px 8px 0px; height: 45px; color: ${({ theme }) => theme.palette.text.muted}; @@ -50,12 +53,15 @@ const ApplyButton = styled.button` padding: 12px 16px; cursor: pointer; border: none; - border: 1px solid ${({ theme }) => theme.palette.border.separator}; - border-color: transparent; + border: 1px solid + ${({ theme, isInvalid }) => + isInvalid ? theme.palette.background.danger : 'transparent'}; + border-left: none; `; export interface CouponInputProps extends WidthProps { isApplied: boolean; + isInvalid: boolean; value: string; onApply: () => void; onDelete: () => void; @@ -67,6 +73,7 @@ export interface CouponInputProps extends WidthProps { export const CouponInput: React.FC = ({ isApplied, + isInvalid, value, onApply, onDelete, @@ -85,8 +92,11 @@ export const CouponInput: React.FC = ({ type="text" name="coupon" onChange={event => onChange(event.target.value)} + isInvalid={isInvalid} /> - {applyButtonText} + + {applyButtonText} + ) : ( void; + onDelete: () => void; +} + +export const Payment: FC = ({ + lang, + applied, + year, + amount, + isError, + error, + onApply, + onDelete, +}) => { + const theme = useTheme(); + const [coupon, setCoupon] = useState(''); + + return ( + + + } + /> + + + + {lang.payment.form.promoField.label} + + + + {applied && ( + + + + {lang.payment.noOfYear} + + {`${year} ${lang.payment.year}`} + + + + + {lang.payment.total} + + + {amount} + + + + )} + {isError && ( + + + + error + + + {lang.payment.error.errorHeading} + + + + {error} + + + + + + )} + + + ); +}; diff --git a/packages/ui/src/components/molecules/index.ts b/packages/ui/src/components/molecules/index.ts index f53128c53..d3123db77 100644 --- a/packages/ui/src/components/molecules/index.ts +++ b/packages/ui/src/components/molecules/index.ts @@ -49,3 +49,4 @@ export * from './OTPInput'; export * from './OneInMany'; export * from './CouponInput'; export * from './PlanCard'; +export * from './Payment'; diff --git a/packages/ui/src/components/utils/bgColor.styled.ts b/packages/ui/src/components/utils/bgColor.styled.ts index 3e7c70fa0..3b1b242b4 100644 --- a/packages/ui/src/components/utils/bgColor.styled.ts +++ b/packages/ui/src/components/utils/bgColor.styled.ts @@ -26,7 +26,9 @@ export type BgColor = | 'slate' | 'slateDark' | 'headlineLight' - | 'error'; + | 'error' + | 'disabled'; + export interface BgColorProps { $bgColor?: BgColor; } @@ -156,4 +158,9 @@ ${props => css` background: ${({ theme }) => theme.palette.background.error}; `} + ${props => + props.$bgColor === 'error' && + css` + background: ${({ theme }) => theme.palette.background.disabled}; + `} `; diff --git a/packages/ui/src/stories/molecules/CouponInput.stories.tsx b/packages/ui/src/stories/molecules/CouponInput.stories.tsx index 68a45b1bc..3efe3aa51 100644 --- a/packages/ui/src/stories/molecules/CouponInput.stories.tsx +++ b/packages/ui/src/stories/molecules/CouponInput.stories.tsx @@ -27,7 +27,7 @@ const meta: Meta = { return ( = { + component: Payment, + tags: ['autodocs'], +}; + +export default meta; + +type Story = StoryObj; + +const lang = { + payment: { + heading: 'Buy plan on the Cypherock website', + form: { + promoField: { + label: 'Promo code / coupon', + placeholder: 'Enter code', + }, + }, + couponInput: { + appliedButtonText: 'Coupon applied:', + applyButtonText: 'Apply', + }, + noOfYear: 'Number of years', + total: 'Total', + year: 'year', + error: { + errorHeading: 'Invalid Coupon Code', + }, + }, +}; + +export const Default: Story = { + args: { + lang, + applied: false, + isError: false, + onApply: () => { + // Dummy functions + }, + onDelete: () => { + // Dummy functions + }, + }, +}; + +export const Applied: Story = { + args: { + ...Default.args, + applied: true, + year: 2, + amount: '$100', + }, +}; + +export const Error: Story = { + args: { + ...Default.args, + isError: true, + error: 'This coupon is invalid!This coupon is invalid!', + }, +}; From aa087a32ee9807138c2793311961405a9f6af6fa Mon Sep 17 00:00:00 2001 From: AryanCypherock Date: Fri, 9 Aug 2024 11:47:12 +0530 Subject: [PATCH 19/96] Feat/inheritance edit user details dialog (#517) * feat: edit user details dialog added * refactor: moved repeated strings to a common destination --- .../src/i18n/lang/ar-AE.json | 45 ++++-- .../src/i18n/lang/de-DE.json | 45 ++++-- .../src/i18n/lang/en.json | 45 ++++-- .../src/i18n/lang/id-ID.json | 45 ++++-- .../src/i18n/lang/zh-CN.json | 45 ++++-- .../cysync-core-constants/src/i18n/types.ts | 45 ++++-- .../cysync-core/src/actions/dialog/index.ts | 3 + .../EditUserDetails/Dialogs/EditDetails.tsx | 133 ++++++++++++++++++ .../EditUserDetails/Dialogs/Success.tsx | 25 ++++ .../EditUserDetails/Dialogs/VerifyOTP.tsx | 82 +++++++++++ .../EditUserDetails/Dialogs/index.tsx | 3 + .../EditUserDetails/context/index.tsx | 129 +++++++++++++++++ .../Inheritance/EditUserDetails/index.tsx | 31 ++++ .../Dialogs/Email/UserDetails.tsx | 10 +- .../Dialogs/Email/VerifyOTP.tsx | 2 +- .../src/dialogs/Inheritance/index.ts | 1 + packages/cysync-core/src/dialogs/index.tsx | 2 + .../cysync-core/src/store/dialog/index.ts | 3 + .../cysync-core/src/store/dialog/types.ts | 6 + 19 files changed, 629 insertions(+), 71 deletions(-) create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/Dialogs/EditDetails.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/Dialogs/Success.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/Dialogs/VerifyOTP.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/Dialogs/index.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/context/index.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/index.tsx diff --git a/packages/cysync-core-constants/src/i18n/lang/ar-AE.json b/packages/cysync-core-constants/src/i18n/lang/ar-AE.json index c54021627..8b16ff2cf 100644 --- a/packages/cysync-core-constants/src/i18n/lang/ar-AE.json +++ b/packages/cysync-core-constants/src/i18n/lang/ar-AE.json @@ -1556,6 +1556,23 @@ "success": { "title": "Reminders duration updated successfully" } + }, + "inheritanceEditUserDetails": { + "editDetails": { + "title": "Edit Details", + "buttons": { + "verifyEmail": "Verify Email" + } + }, + "verifyOtp": { + "loading": { + "title": "Please wait while we update the data", + "subtext": "Do not disconnect or close the app" + } + }, + "success": { + "title": "Owner details successfully updated" + } } }, "toggle": { @@ -1678,6 +1695,23 @@ "title": "Terms of Service", "privacyPolicy": "Privacy Policy", "checkBoxLabel": "I have read and agree to Terms of use and Privacy policy" + }, + "dialog": { + "userDetails": { + "form": { + "name": "Name", + "email": "Primary Email", + "alternateEmail": "Alternate Email" + } + }, + "verifyOTP": { + "primaryEmailOTP": { + "title": "OTP Verification for primary email" + }, + "alternateEmailOTP": { + "title": "OTP Verification for alternate email" + } + } } }, "inheritanceSilverPlanPurchase": { @@ -1713,20 +1747,9 @@ "userDetails": { "title": "User Details", "subTitle": "All the fields are mandatory", - "form": { - "name": "Name", - "email": "Primary Email", - "alternateEmail": "Alternate Email" - }, "buttons": { "sendOTP": "Send OTP" } - }, - "primaryEmailOTP": { - "title": "OTP Verification for primary email" - }, - "alternateEmailOTP": { - "title": "OTP Verification for alternate email" } }, "encryption": { diff --git a/packages/cysync-core-constants/src/i18n/lang/de-DE.json b/packages/cysync-core-constants/src/i18n/lang/de-DE.json index bcc1bf2c8..f5f476bd7 100644 --- a/packages/cysync-core-constants/src/i18n/lang/de-DE.json +++ b/packages/cysync-core-constants/src/i18n/lang/de-DE.json @@ -1561,6 +1561,23 @@ "success": { "title": "Reminders duration updated successfully" } + }, + "inheritanceEditUserDetails": { + "editDetails": { + "title": "Edit Details", + "buttons": { + "verifyEmail": "Verify Email" + } + }, + "verifyOtp": { + "loading": { + "title": "Please wait while we update the data", + "subtext": "Do not disconnect or close the app" + } + }, + "success": { + "title": "Owner details successfully updated" + } } }, "toggle": { @@ -1683,6 +1700,23 @@ "title": "Terms of Service", "privacyPolicy": "Privacy Policy", "checkBoxLabel": "I have read and agree to Terms of use and Privacy policy" + }, + "dialog": { + "userDetails": { + "form": { + "name": "Name", + "email": "Primary Email", + "alternateEmail": "Alternate Email" + } + }, + "verifyOTP": { + "primaryEmailOTP": { + "title": "OTP Verification for primary email" + }, + "alternateEmailOTP": { + "title": "OTP Verification for alternate email" + } + } } }, "inheritanceSilverPlanPurchase": { @@ -1718,20 +1752,9 @@ "userDetails": { "title": "User Details", "subTitle": "All the fields are mandatory", - "form": { - "name": "Name", - "email": "Primary Email", - "alternateEmail": "Alternate Email" - }, "buttons": { "sendOTP": "Send OTP" } - }, - "primaryEmailOTP": { - "title": "OTP Verification for primary email" - }, - "alternateEmailOTP": { - "title": "OTP Verification for alternate email" } }, "encryption": { diff --git a/packages/cysync-core-constants/src/i18n/lang/en.json b/packages/cysync-core-constants/src/i18n/lang/en.json index 2a0075820..57966c550 100644 --- a/packages/cysync-core-constants/src/i18n/lang/en.json +++ b/packages/cysync-core-constants/src/i18n/lang/en.json @@ -1561,6 +1561,23 @@ "success": { "title": "Reminders duration updated successfully" } + }, + "inheritanceEditUserDetails": { + "editDetails": { + "title": "Edit Details", + "buttons": { + "verifyEmail": "Verify Email" + } + }, + "verifyOtp": { + "loading": { + "title": "Please wait while we update the data", + "subtext": "Do not disconnect or close the app" + } + }, + "success": { + "title": "Owner details successfully updated" + } } }, "toggle": { @@ -1683,6 +1700,23 @@ "title": "Terms of Service", "privacyPolicy": "Privacy Policy", "checkBoxLabel": "I have read and agree to Terms of use and Privacy policy" + }, + "dialog": { + "userDetails": { + "form": { + "name": "Name", + "email": "Primary Email", + "alternateEmail": "Alternate Email" + } + }, + "verifyOTP": { + "primaryEmailOTP": { + "title": "OTP Verification for primary email" + }, + "alternateEmailOTP": { + "title": "OTP Verification for alternate email" + } + } } }, "inheritanceSilverPlanPurchase": { @@ -1718,20 +1752,9 @@ "userDetails": { "title": "User Details", "subTitle": "All the fields are mandatory", - "form": { - "name": "Name", - "email": "Primary Email", - "alternateEmail": "Alternate Email" - }, "buttons": { "sendOTP": "Send OTP" } - }, - "primaryEmailOTP": { - "title": "OTP Verification for primary email" - }, - "alternateEmailOTP": { - "title": "OTP Verification for alternate email" } }, "encryption": { diff --git a/packages/cysync-core-constants/src/i18n/lang/id-ID.json b/packages/cysync-core-constants/src/i18n/lang/id-ID.json index aede73498..aebe0aedc 100644 --- a/packages/cysync-core-constants/src/i18n/lang/id-ID.json +++ b/packages/cysync-core-constants/src/i18n/lang/id-ID.json @@ -1561,6 +1561,23 @@ "success": { "title": "Reminders duration updated successfully" } + }, + "inheritanceEditUserDetails": { + "editDetails": { + "title": "Edit Details", + "buttons": { + "verifyEmail": "Verify Email" + } + }, + "verifyOtp": { + "loading": { + "title": "Please wait while we update the data", + "subtext": "Do not disconnect or close the app" + } + }, + "success": { + "title": "Owner details successfully updated" + } } }, "toggle": { @@ -1683,6 +1700,23 @@ "title": "Terms of Service", "privacyPolicy": "Privacy Policy", "checkBoxLabel": "I have read and agree to Terms of use and Privacy policy" + }, + "dialog": { + "userDetails": { + "form": { + "name": "Name", + "email": "Primary Email", + "alternateEmail": "Alternate Email" + } + }, + "verifyOTP": { + "primaryEmailOTP": { + "title": "OTP Verification for primary email" + }, + "alternateEmailOTP": { + "title": "OTP Verification for alternate email" + } + } } }, "inheritanceSilverPlanPurchase": { @@ -1718,20 +1752,9 @@ "userDetails": { "title": "User Details", "subTitle": "All the fields are mandatory", - "form": { - "name": "Name", - "email": "Primary Email", - "alternateEmail": "Alternate Email" - }, "buttons": { "sendOTP": "Send OTP" } - }, - "primaryEmailOTP": { - "title": "OTP Verification for primary email" - }, - "alternateEmailOTP": { - "title": "OTP Verification for alternate email" } }, "encryption": { diff --git a/packages/cysync-core-constants/src/i18n/lang/zh-CN.json b/packages/cysync-core-constants/src/i18n/lang/zh-CN.json index 4a358ba2f..57de5462a 100644 --- a/packages/cysync-core-constants/src/i18n/lang/zh-CN.json +++ b/packages/cysync-core-constants/src/i18n/lang/zh-CN.json @@ -1536,6 +1536,23 @@ "success": { "title": "Reminders duration updated successfully" } + }, + "inheritanceEditUserDetails": { + "editDetails": { + "title": "Edit Details", + "buttons": { + "verifyEmail": "Verify Email" + } + }, + "verifyOtp": { + "loading": { + "title": "Please wait while we update the data", + "subtext": "Do not disconnect or close the app" + } + }, + "success": { + "title": "Owner details successfully updated" + } } }, "toggle": { @@ -1658,6 +1675,23 @@ "title": "Terms of Service", "privacyPolicy": "Privacy Policy", "checkBoxLabel": "I have read and agree to Terms of use and Privacy policy" + }, + "dialog": { + "userDetails": { + "form": { + "name": "Name", + "email": "Primary Email", + "alternateEmail": "Alternate Email" + } + }, + "verifyOTP": { + "primaryEmailOTP": { + "title": "OTP Verification for primary email" + }, + "alternateEmailOTP": { + "title": "OTP Verification for alternate email" + } + } } }, "inheritanceSilverPlanPurchase": { @@ -1693,20 +1727,9 @@ "userDetails": { "title": "User Details", "subTitle": "All the fields are mandatory", - "form": { - "name": "Name", - "email": "Primary Email", - "alternateEmail": "Alternate Email" - }, "buttons": { "sendOTP": "Send OTP" } - }, - "primaryEmailOTP": { - "title": "OTP Verification for primary email" - }, - "alternateEmailOTP": { - "title": "OTP Verification for alternate email" } }, "encryption": { diff --git a/packages/cysync-core-constants/src/i18n/types.ts b/packages/cysync-core-constants/src/i18n/types.ts index da9e9868d..39c29fbec 100644 --- a/packages/cysync-core-constants/src/i18n/types.ts +++ b/packages/cysync-core-constants/src/i18n/types.ts @@ -833,6 +833,23 @@ interface LangDialogs { title: string; }; }; + inheritanceEditUserDetails: { + editDetails: { + title: string; + buttons: { + verifyEmail: string; + }; + }; + verifyOtp: { + loading: { + title: string; + subtext: string; + }; + }; + success: { + title: string; + }; + }; } interface LangToggle { @@ -906,6 +923,23 @@ interface LangInheritance { privacyPolicy: string; checkBoxLabel: string; }; + dialog: { + userDetails: { + form: { + name: string; + email: string; + alternateEmail: string; + }; + }; + verifyOTP: { + primaryEmailOTP: { + title: string; + }; + alternateEmailOTP: { + title: string; + }; + }; + }; } interface LangInheritanceSilverPlanPurchase { @@ -937,21 +971,10 @@ interface LangInheritanceSilverPlanPurchase { userDetails: { title: string; subTitle: string; - form: { - name: string; - email: string; - alternateEmail: string; - }; buttons: { sendOTP: string; }; }; - primaryEmailOTP: { - title: string; - }; - alternateEmailOTP: { - title: string; - }; }; encryption: { heading: string; diff --git a/packages/cysync-core/src/actions/dialog/index.ts b/packages/cysync-core/src/actions/dialog/index.ts index cdd5ee4bd..e5540312b 100644 --- a/packages/cysync-core/src/actions/dialog/index.ts +++ b/packages/cysync-core/src/actions/dialog/index.ts @@ -100,3 +100,6 @@ export const openInheritanceEditExecutorMessageDialog = () => export const openInheritanceEditReminderTimeDialog = () => openDialog({ name: 'inheritanceEditReminderTime', data: undefined }); + +export const openEditUserDetailsDialog = () => + openDialog({ name: 'inheritanceEditUserDetails', data: undefined }); diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/Dialogs/EditDetails.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/Dialogs/EditDetails.tsx new file mode 100644 index 000000000..f972f8fa1 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/Dialogs/EditDetails.tsx @@ -0,0 +1,133 @@ +import React, { useState } from 'react'; + +import { useInheritanceEditUserDetailsDialog } from '../context'; +import { + Button, + CloseButton, + Container, + DialogBox, + DialogBoxBody, + DialogBoxFooter, + DialogBoxHeader, + Input, + LangDisplay, + ScrollableContainer, + Typography, +} from '@cypherock/cysync-ui'; +import { selectLanguage } from '~/store/lang'; +import { useAppSelector } from '~/store'; + +export const EditDetails = () => { + const lang = useAppSelector(selectLanguage); + const { onClose, onUserDetailsSubmit, isSubmittingUserDetails } = + useInheritanceEditUserDetailsDialog(); + const [name, setName] = useState(''); + const [email, setEmail] = useState(''); + const [alternateEmail, setAlternateEmail] = useState(''); + + const strings = lang.strings.dialogs.inheritanceEditUserDetails.editDetails; + const { form } = lang.strings.inheritance.dialog.userDetails; + + const formId = 'inheritance-edit-user-details'; + + const onSubmit = (e: React.FormEvent) => { + e.preventDefault(); + + if (isSubmittingUserDetails) return; + + onUserDetailsSubmit({ + name, + email, + alternateEmail, + }); + }; + + return ( + + + + + + +
+ + + + + + + + + + + + +
+
+
+ + + + +
+ ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/Dialogs/Success.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/Dialogs/Success.tsx new file mode 100644 index 000000000..c5abd1d34 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/Dialogs/Success.tsx @@ -0,0 +1,25 @@ +import { SuccessDialog } from '@cypherock/cysync-ui'; +import React from 'react'; + +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritanceEditUserDetailsDialog } from '../context'; + +export const Success = () => { + const lang = useAppSelector(selectLanguage); + const { onClose } = useInheritanceEditUserDetailsDialog(); + const strings = lang.strings.dialogs.inheritanceEditUserDetails.success; + + return ( + + ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/Dialogs/VerifyOTP.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/Dialogs/VerifyOTP.tsx new file mode 100644 index 000000000..1804c4af7 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/Dialogs/VerifyOTP.tsx @@ -0,0 +1,82 @@ +import React, { useMemo, useRef, useState } from 'react'; + +import { + OTPInputDialog, + OTPInputDialogRef, +} from '~/dialogs/Inheritance/components'; +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritanceEditUserDetailsDialog } from '../context'; +import { LoaderDialog } from '~/components'; +import { sleep } from '@cypherock/sdk-utils'; + +export const VerifyOTP: React.FC = () => { + const lang = useAppSelector(selectLanguage); + const strings = lang.strings.inheritance.dialog.verifyOTP; + + const { onClose, onPrevious, userDetails, onNext } = + useInheritanceEditUserDetailsDialog(); + + const [email, setEmail] = useState(userDetails?.email ?? ''); + const [title, setTitle] = useState(strings.primaryEmailOTP.title); + const [loading, setLoading] = useState(false); + + const otpRef = useRef(null); + + const onVerify = async () => { + // DUMMY FUNCTION + if (email === userDetails?.alternateEmail) { + setLoading(true); + await sleep(2000); + onNext(); + return; + } + + setEmail(userDetails?.alternateEmail ?? ''); + setTitle(strings.alternateEmailOTP.title); + otpRef.current?.reset(); + }; + + const onResend = () => { + // DUMMY FUNCTION + }; + + const otpExpireTime = useMemo( + () => new Date(Date.now() + 60 * 1000).toISOString(), + [email], + ); + const otpLength = 6; + const retriesRemaining = 3; + + if (loading) { + return ( + + ); + } + + return ( + + ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/Dialogs/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/Dialogs/index.tsx new file mode 100644 index 000000000..f8b647c4b --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/Dialogs/index.tsx @@ -0,0 +1,3 @@ +export * from './EditDetails'; +export * from './VerifyOTP'; +export * from './Success'; diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/context/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/context/index.tsx new file mode 100644 index 000000000..bdf6f29df --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/context/index.tsx @@ -0,0 +1,129 @@ +import React, { + Context, + FC, + ReactNode, + createContext, + useCallback, + useContext, + useMemo, + useState, +} from 'react'; + +import { ITabs, useTabsAndDialogs } from '~/hooks'; +import { closeDialog, useAppDispatch } from '~/store'; + +import { EditDetails, Success, VerifyOTP } from '../Dialogs'; +import { sleep } from '@cypherock/sdk-utils'; + +export interface IUserDetails { + name: string; + email: string; + alternateEmail: string; +} + +export interface InheritanceEditUserDetailsDialogContextInterface { + tabs: ITabs; + onNext: (tab?: number, dialog?: number) => void; + goTo: (tab: number, dialog?: number) => void; + onPrevious: () => void; + onClose: () => void; + currentTab: number; + currentDialog: number; + isDeviceRequired: boolean; + unhandledError?: any; + userDetails?: IUserDetails; + onUserDetailsSubmit: (params: IUserDetails) => void; + isSubmittingUserDetails: boolean; +} + +export const InheritanceEditUserDetailsDialogContext: Context = + createContext( + {} as InheritanceEditUserDetailsDialogContextInterface, + ); + +export interface InheritanceEditUserDetailsDialogContextProviderProps { + children: ReactNode; +} + +export const InheritanceEditUserDetailsDialogProvider: FC< + InheritanceEditUserDetailsDialogContextProviderProps +> = ({ children }) => { + const dispatch = useAppDispatch(); + + const [isSubmittingUserDetails, setIsSubmittingUserDetails] = useState(false); + const [userDetails, setUserDetails] = useState(); + + const deviceRequiredDialogsMap: Record = {}; + const tabs: ITabs = [ + { + name: 'Edit Details', + dialogs: [], + }, + { name: 'Verify OTP', dialogs: [] }, + { name: 'Success', dialogs: [] }, + ]; + + const { + onNext, + onPrevious, + goTo, + currentTab, + currentDialog, + isDeviceRequired, + } = useTabsAndDialogs({ + deviceRequiredDialogsMap, + tabs, + dialogName: 'inheritanceEditUserDetails', + }); + + const onClose = () => { + dispatch(closeDialog('inheritanceEditUserDetails')); + }; + + const onUserDetailsSubmit = useCallback(async (params: IUserDetails) => { + setIsSubmittingUserDetails(true); + setUserDetails(params); + await sleep(2000); + setIsSubmittingUserDetails(false); + goTo(1); + }, []); + + const ctx = useMemo( + () => ({ + onNext, + onPrevious, + tabs, + onClose, + goTo, + currentTab, + currentDialog, + isDeviceRequired, + userDetails, + onUserDetailsSubmit, + isSubmittingUserDetails, + }), + [ + onNext, + onPrevious, + tabs, + onClose, + goTo, + currentTab, + currentDialog, + isDeviceRequired, + userDetails, + onUserDetailsSubmit, + isSubmittingUserDetails, + ], + ); + + return ( + + {children} + + ); +}; + +export function useInheritanceEditUserDetailsDialog(): InheritanceEditUserDetailsDialogContextInterface { + return useContext(InheritanceEditUserDetailsDialogContext); +} diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/index.tsx new file mode 100644 index 000000000..3274c685c --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/index.tsx @@ -0,0 +1,31 @@ +import React, { FC } from 'react'; +import { BlurOverlay } from '@cypherock/cysync-ui'; +import { ErrorHandlerDialog } from '~/components'; +import { + InheritanceEditUserDetailsDialogProvider, + useInheritanceEditUserDetailsDialog, +} from './context'; + +const InheritanceEditUserDetails: FC = () => { + const { tabs, currentTab, currentDialog, unhandledError, onClose } = + useInheritanceEditUserDetailsDialog(); + + return ( + + + {tabs[currentTab]?.dialogs[currentDialog]} + + + ); +}; + +export const InheritanceEditUserDetailsDialog = () => ( + + + +); diff --git a/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Email/UserDetails.tsx b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Email/UserDetails.tsx index cceb53c8e..662314f67 100644 --- a/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Email/UserDetails.tsx +++ b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Email/UserDetails.tsx @@ -15,7 +15,9 @@ import { Layout } from '../../Layout'; export const UserDetails = () => { const lang = useAppSelector(selectLanguage); - const strings = lang.strings.inheritanceSilverPlanPurchase.email.userDetails; + const strings = lang.strings.inheritance.dialog.userDetails; + const silverPlanStrings = + lang.strings.inheritanceSilverPlanPurchase.email.userDetails; const [name, setName] = useState(''); const [email, setEmail] = useState(''); @@ -56,7 +58,7 @@ export const UserDetails = () => { disabled={isSubmittingUserDetails} isLoading={isSubmittingUserDetails} > - + } @@ -70,10 +72,10 @@ export const UserDetails = () => { $fontSize={20} mb="4px" > - + - + diff --git a/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Email/VerifyOTP.tsx b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Email/VerifyOTP.tsx index 3ab4f64e5..b8b06d914 100644 --- a/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Email/VerifyOTP.tsx +++ b/packages/cysync-core/src/dialogs/Inheritance/SilverPlanPurchase/Dialogs/Email/VerifyOTP.tsx @@ -10,7 +10,7 @@ import { useInheritanceSilverPlanPurchaseDialog } from '../../context'; export const VerifyOTP: React.FC = () => { const lang = useAppSelector(selectLanguage); - const strings = lang.strings.inheritanceSilverPlanPurchase.email; + const strings = lang.strings.inheritance.dialog.verifyOTP; const { onClose, onPrevious, userDetails, onNext } = useInheritanceSilverPlanPurchaseDialog(); diff --git a/packages/cysync-core/src/dialogs/Inheritance/index.ts b/packages/cysync-core/src/dialogs/Inheritance/index.ts index ec184dbf0..036aae895 100644 --- a/packages/cysync-core/src/dialogs/Inheritance/index.ts +++ b/packages/cysync-core/src/dialogs/Inheritance/index.ts @@ -3,3 +3,4 @@ export * from './PlanLogin'; export * from './SilverPlanPurchase'; export * from './EditExecutorMessage'; export * from './EditReminderTime'; +export * from './EditUserDetails'; diff --git a/packages/cysync-core/src/dialogs/index.tsx b/packages/cysync-core/src/dialogs/index.tsx index 43057dc2b..8507bc2a9 100644 --- a/packages/cysync-core/src/dialogs/index.tsx +++ b/packages/cysync-core/src/dialogs/index.tsx @@ -22,6 +22,7 @@ import { HistoryDialog } from './HistoryDialog'; import { InheritanceEditExecutorMessageDialog, InheritanceEditReminderTimeDialog, + InheritanceEditUserDetailsDialog, InheritancePlanLoginDialog, InheritanceSyncPlansDialog, InheritanceSilverPlanPurchaseDialog, @@ -69,4 +70,5 @@ export const dialogs: Record = { inheritanceSilverPlanPurchase: InheritanceSilverPlanPurchaseDialog, inheritanceEditExecutorMessage: InheritanceEditExecutorMessageDialog, inheritanceEditReminderTime: InheritanceEditReminderTimeDialog, + inheritanceEditUserDetails: InheritanceEditUserDetailsDialog, }; diff --git a/packages/cysync-core/src/store/dialog/index.ts b/packages/cysync-core/src/store/dialog/index.ts index 39faea431..4b133a6cb 100644 --- a/packages/cysync-core/src/store/dialog/index.ts +++ b/packages/cysync-core/src/store/dialog/index.ts @@ -101,6 +101,9 @@ const initialState: IDialogState = { inheritanceEditReminderTime: { isOpen: false, }, + inheritanceEditUserDetails: { + isOpen: false, + }, }; export const dialogSlice = createSlice({ diff --git a/packages/cysync-core/src/store/dialog/types.ts b/packages/cysync-core/src/store/dialog/types.ts index acc6ebc70..3134723b2 100644 --- a/packages/cysync-core/src/store/dialog/types.ts +++ b/packages/cysync-core/src/store/dialog/types.ts @@ -152,10 +152,16 @@ export interface IDialogState { isOpen: boolean; data?: undefined; }; + inheritanceEditReminderTime: { isOpen: boolean; data?: undefined; }; + + inheritanceEditUserDetails: { + isOpen: boolean; + data?: undefined; + }; } export const GuidedFlowMap = { From eae0ee6bb058842252f08441ff418cbccbe70577 Mon Sep 17 00:00:00 2001 From: AryanCypherock Date: Sat, 10 Aug 2024 15:47:42 +0530 Subject: [PATCH 20/96] feat: inheritance page layout implemented PRF-7402 (#518) * feat: inheritance page layout implemented * fix: passing action button text as a prop --- .../src/i18n/lang/ar-AE.json | 9 +++ .../src/i18n/lang/de-DE.json | 9 +++ .../src/i18n/lang/en.json | 9 +++ .../src/i18n/lang/id-ID.json | 9 +++ .../src/i18n/lang/zh-CN.json | 9 +++ .../cysync-core-constants/src/i18n/types.ts | 9 +++ .../src/components/InheritancePageLayout.tsx | 73 +++++++++++++++++++ .../cysync-core/src/components/SideBar.tsx | 7 ++ packages/cysync-core/src/components/index.ts | 1 + .../cysync-core/src/constants/routes/index.ts | 4 + packages/cysync-core/src/context/sidebar.tsx | 1 + .../src/pages/MainApp/Inheritance/index.tsx | 18 +++++ .../cysync-core/src/pages/MainApp/index.ts | 1 + packages/desktop-ui/src/Router.tsx | 2 + packages/ui/icons/arrow-back-golden-icon.svg | 10 +++ packages/ui/icons/cypherock-cover-icon.svg | 49 +++++++++++++ 16 files changed, 220 insertions(+) create mode 100644 packages/cysync-core/src/components/InheritancePageLayout.tsx create mode 100644 packages/cysync-core/src/pages/MainApp/Inheritance/index.tsx create mode 100644 packages/ui/icons/arrow-back-golden-icon.svg create mode 100644 packages/ui/icons/cypherock-cover-icon.svg diff --git a/packages/cysync-core-constants/src/i18n/lang/ar-AE.json b/packages/cysync-core-constants/src/i18n/lang/ar-AE.json index 8b16ff2cf..c3752d821 100644 --- a/packages/cysync-core-constants/src/i18n/lang/ar-AE.json +++ b/packages/cysync-core-constants/src/i18n/lang/ar-AE.json @@ -629,6 +629,7 @@ "receiveCrypto": "استقبال العملات المشفرة", "history": "السجل", "walletConnect": "Wallet Connect", + "cypherockCover": "Cypherock Cover", "tutorial": "تعليمات", "settings": "الإعدادات", "help": "مساعدة", @@ -1691,6 +1692,14 @@ } }, "inheritance": { + "title": "Cypherock Cover", + "choosePlan": { + "title": "Choose a plan" + }, + "buttons": { + "syncPlans": "Sync Plans", + "recoverPin": "Recover Pin" + }, "termsOfService": { "title": "Terms of Service", "privacyPolicy": "Privacy Policy", diff --git a/packages/cysync-core-constants/src/i18n/lang/de-DE.json b/packages/cysync-core-constants/src/i18n/lang/de-DE.json index f5f476bd7..91522dea9 100644 --- a/packages/cysync-core-constants/src/i18n/lang/de-DE.json +++ b/packages/cysync-core-constants/src/i18n/lang/de-DE.json @@ -631,6 +631,7 @@ "receiveCrypto": "Krypto empfangen", "history": "Verlauf", "walletConnect": "Wallet Connect", + "cypherockCover": "Cypherock Cover", "tutorial": "Anleitung", "settings": "Einstellungen", "help": "Help", @@ -1696,6 +1697,14 @@ } }, "inheritance": { + "title": "Cypherock Cover", + "choosePlan": { + "title": "Choose a plan" + }, + "buttons": { + "syncPlans": "Sync Plans", + "recoverPin": "Recover Pin" + }, "termsOfService": { "title": "Terms of Service", "privacyPolicy": "Privacy Policy", diff --git a/packages/cysync-core-constants/src/i18n/lang/en.json b/packages/cysync-core-constants/src/i18n/lang/en.json index 57966c550..70436d943 100644 --- a/packages/cysync-core-constants/src/i18n/lang/en.json +++ b/packages/cysync-core-constants/src/i18n/lang/en.json @@ -631,6 +631,7 @@ "receiveCrypto": "Receive Crypto", "history": "History", "walletConnect": "Wallet Connect", + "cypherockCover": "Cypherock Cover", "tutorial": "Tutorial", "settings": "Settings", "help": "Help", @@ -1696,6 +1697,14 @@ } }, "inheritance": { + "title": "Cypherock Cover", + "choosePlan": { + "title": "Choose a plan" + }, + "buttons": { + "syncPlans": "Sync Plans", + "recoverPin": "Recover Pin" + }, "termsOfService": { "title": "Terms of Service", "privacyPolicy": "Privacy Policy", diff --git a/packages/cysync-core-constants/src/i18n/lang/id-ID.json b/packages/cysync-core-constants/src/i18n/lang/id-ID.json index aebe0aedc..5662fab9b 100644 --- a/packages/cysync-core-constants/src/i18n/lang/id-ID.json +++ b/packages/cysync-core-constants/src/i18n/lang/id-ID.json @@ -631,6 +631,7 @@ "receiveCrypto": "Terima Crypto", "history": "Riwayat", "walletConnect": "Wallet Connect", + "cypherockCover": "Cypherock Cover", "tutorial": "Tutorial", "settings": "Pengaturan", "help": "Help", @@ -1696,6 +1697,14 @@ } }, "inheritance": { + "title": "Cypherock Cover", + "choosePlan": { + "title": "Choose a plan" + }, + "buttons": { + "syncPlans": "Sync Plans", + "recoverPin": "Recover Pin" + }, "termsOfService": { "title": "Terms of Service", "privacyPolicy": "Privacy Policy", diff --git a/packages/cysync-core-constants/src/i18n/lang/zh-CN.json b/packages/cysync-core-constants/src/i18n/lang/zh-CN.json index 57de5462a..f345d8abb 100644 --- a/packages/cysync-core-constants/src/i18n/lang/zh-CN.json +++ b/packages/cysync-core-constants/src/i18n/lang/zh-CN.json @@ -629,6 +629,7 @@ "receiveCrypto": "接收加密货币", "history": "历史", "walletConnect": "Wallet Connect", + "cypherockCover": "Cypherock Cover", "tutorial": "教程", "settings": "设置", "help": "Help", @@ -1671,6 +1672,14 @@ } }, "inheritance": { + "title": "Cypherock Cover", + "choosePlan": { + "title": "Choose a plan" + }, + "buttons": { + "syncPlans": "Sync Plans", + "recoverPin": "Recover Pin" + }, "termsOfService": { "title": "Terms of Service", "privacyPolicy": "Privacy Policy", diff --git a/packages/cysync-core-constants/src/i18n/types.ts b/packages/cysync-core-constants/src/i18n/types.ts index 39c29fbec..5355642d1 100644 --- a/packages/cysync-core-constants/src/i18n/types.ts +++ b/packages/cysync-core-constants/src/i18n/types.ts @@ -503,6 +503,7 @@ interface LangSidebar { receiveCrypto: string; history: string; walletConnect: string; + cypherockCover: string; tutorial: string; settings: string; help: string; @@ -918,6 +919,14 @@ interface LangOTP { } interface LangInheritance { + title: string; + choosePlan: { + title: string; + }; + buttons: { + syncPlans: string; + recoverPin: string; + }; termsOfService: { title: string; privacyPolicy: string; diff --git a/packages/cysync-core/src/components/InheritancePageLayout.tsx b/packages/cysync-core/src/components/InheritancePageLayout.tsx new file mode 100644 index 000000000..0e1957a36 --- /dev/null +++ b/packages/cysync-core/src/components/InheritancePageLayout.tsx @@ -0,0 +1,73 @@ +import { + ArrowBackGoldenIcon, + Breadcrumb, + BreadcrumbItem, + Button, + Container, + Flex, + Typography, +} from '@cypherock/cysync-ui'; +import React, { FC, ReactNode } from 'react'; + +interface InheritancePageLayoutProps { + children?: ReactNode; + headingOnly?: boolean; + breadcrumbs?: BreadcrumbItem[]; + onClick?: () => void; + lang: { + choosePlan: { + title: string; + }; + }; + actionButtonText?: string; +} + +export const InheritancePageLayout: FC = ({ + children, + breadcrumbs, + headingOnly, + lang, + onClick, + actionButtonText, +}) => ( + + + {breadcrumbs ? ( + + ) : ( + + )} + + + {children} + + +); + +InheritancePageLayout.defaultProps = { + children: undefined, + headingOnly: undefined, + breadcrumbs: undefined, + onClick: undefined, + actionButtonText: undefined, +}; diff --git a/packages/cysync-core/src/components/SideBar.tsx b/packages/cysync-core/src/components/SideBar.tsx index 336dc0ddb..ced648d21 100644 --- a/packages/cysync-core/src/components/SideBar.tsx +++ b/packages/cysync-core/src/components/SideBar.tsx @@ -3,6 +3,7 @@ import { ArrowReceivedIcon, ArrowSentIcon, Button, + CypherockCoverIcon, DropDownItemProps, Flex, FloatingMenu, @@ -165,6 +166,12 @@ const SideBarComponent: FC<{ collapseWallets?: boolean }> = () => { dispatch(openWalletConnectDialog()); }} /> + navigate('inheritance')} + />
{ + const lang = useAppSelector(selectLanguage); + + return ( + + + + ); +}; diff --git a/packages/cysync-core/src/pages/MainApp/index.ts b/packages/cysync-core/src/pages/MainApp/index.ts index 8fad3948b..b4d5c12e2 100644 --- a/packages/cysync-core/src/pages/MainApp/index.ts +++ b/packages/cysync-core/src/pages/MainApp/index.ts @@ -5,3 +5,4 @@ export * from './Settings'; export * from './Help'; export * from './Asset'; export * from './Account'; +export * from './Inheritance'; diff --git a/packages/desktop-ui/src/Router.tsx b/packages/desktop-ui/src/Router.tsx index a09609701..2a1ee1d35 100644 --- a/packages/desktop-ui/src/Router.tsx +++ b/packages/desktop-ui/src/Router.tsx @@ -23,6 +23,7 @@ import { AppUpdate, AssetPage, AccountPage, + Inheritance, } from '@cypherock/cysync-core'; import React, { memo, ReactNode } from 'react'; import { HashRouter as Router, Route, Routes } from 'react-router-dom'; @@ -44,6 +45,7 @@ const components: Record = { 'onboarding-device-update': , portfolio: , wallet: , + inheritance: , asset: , account: , history: , diff --git a/packages/ui/icons/arrow-back-golden-icon.svg b/packages/ui/icons/arrow-back-golden-icon.svg new file mode 100644 index 000000000..f927c6a22 --- /dev/null +++ b/packages/ui/icons/arrow-back-golden-icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/ui/icons/cypherock-cover-icon.svg b/packages/ui/icons/cypherock-cover-icon.svg new file mode 100644 index 000000000..805f439c1 --- /dev/null +++ b/packages/ui/icons/cypherock-cover-icon.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 0bfd3a1ed91b74fe8abadb3646b5f42fcb4f7dea Mon Sep 17 00:00:00 2001 From: AryanCypherock Date: Wed, 21 Aug 2024 12:33:35 +0530 Subject: [PATCH 21/96] feat: added lang strings for edit encrypted message --- .../src/i18n/lang/en.json | 70 +++++++++++++++++++ .../cysync-core-constants/src/i18n/types.ts | 70 +++++++++++++++++++ 2 files changed, 140 insertions(+) diff --git a/packages/cysync-core-constants/src/i18n/lang/en.json b/packages/cysync-core-constants/src/i18n/lang/en.json index 70436d943..ea6e3c104 100644 --- a/packages/cysync-core-constants/src/i18n/lang/en.json +++ b/packages/cysync-core-constants/src/i18n/lang/en.json @@ -1579,6 +1579,76 @@ "success": { "title": "Owner details successfully updated" } + }, + "inheritanceEditEncryptedMessage": { + "confirmation": { + "title": "Are you sure you want to edit the encrypted nominee message?", + "subTitle": "You will need the device and at least one card to encrypt the message again after editing" + }, + "syncing": { + "title": "Please wait while we fetch the data", + "subTitle": "Do not disconnect or close the app" + }, + "decryption": { + "wallet": { + "title": "Follow instructions on the X1 Vault", + "actions": { + "tapCard": "Tap any card" + }, + "messageBox": { + "warning": "The message will be decrypted by the X1 card" + }, + "decryption": { + "title": "Please wait while we decrypt the data", + "subTitle": "Do not disconnect or close the app" + } + } + }, + "editMessage": { + "title": "Nominee message", + "subTitle": "The nominee messages will be end-to-end encrypted", + "form": { + "cardLocationField": { + "label": "Card location", + "placeholder": "Enter your card's location" + }, + "personalMessageField": { + "label": "Personal message", + "placeholder": "Enter your personal message here" + } + }, + "messageBox": { + "warning": "Make sure you describe the card's location clearly for the nominee" + }, + "confirmMessage": { + "title": "Follow instructions on the X1 Vault to verify the nominee message", + "subTitle": "You can encrypt the message in the next step", + "actions": { + "confirmOnDevice": "Confirm on device", + "verifyLocation": "Verify Card location in your device" + }, + "messageBox": { + "warning": "Read through the whole text and verify that the message shown in the device is same as you entered in cySync" + } + }, + "encryption": { + "title": "Follow instructions on the X1 Vault to encrypt the nominee message", + "subTitle": "Lift your card after 2 beep sounds", + "actions": { + "enterPinAndTap": "Enter PIN and tap any one card" + }, + "messageBox": { + "warning": "The nominee message will be encrypted by the Card" + }, + "syncing": { + "title": "Please wait while we save the encrypted data on the server", + "subTitle": "Do not disconnect or close the app" + } + }, + "success": { + "title": "Nominee message successfully updated" + } + } } }, "toggle": { diff --git a/packages/cysync-core-constants/src/i18n/types.ts b/packages/cysync-core-constants/src/i18n/types.ts index 5355642d1..9043af8c5 100644 --- a/packages/cysync-core-constants/src/i18n/types.ts +++ b/packages/cysync-core-constants/src/i18n/types.ts @@ -851,6 +851,76 @@ interface LangDialogs { title: string; }; }; + inheritanceEditEncryptedMessage: { + confirmation: { + title: string; + subTitle: string; + }; + syncing: { + title: string; + subTitle: string; + }; + decryption: { + wallet: { + title: string; + actions: { + tapCard: string; + }; + messageBox: { + warning: string; + }; + decryption: { + title: string; + subTitle: string; + }; + }; + }; + editMessage: { + title: string; + subTitle: string; + form: { + cardLocationField: { + label: string; + placeholder: string; + }; + personalMessageField: { + label: string; + placeholder: string; + }; + }; + messageBox: { + warning: string; + }; + confirmMessage: { + title: string; + subTitle: string; + actions: { + confirmOnDevice: string; + verifyLocation: string; + }; + messageBox: { + warning: string; + }; + }; + encryption: { + title: string; + subTitle: string; + actions: { + enterPinAndTap: string; + }; + messageBox: { + warning: string; + }; + syncing: { + title: string; + subTitle: string; + }; + }; + success: { + title: string; + }; + }; + }; } interface LangToggle { From 62f9b4c7f1ccd36161e8f79566beeba1d3b8780a Mon Sep 17 00:00:00 2001 From: AryanCypherock Date: Wed, 21 Aug 2024 16:40:48 +0530 Subject: [PATCH 22/96] feat: edit encrypted message dialog implemented --- .../src/i18n/lang/en.json | 56 +++++---- .../cysync-core-constants/src/i18n/types.ts | 52 ++++---- .../cysync-core/src/actions/dialog/index.ts | 3 + .../Dialogs/ConfirmMessage.tsx | 96 ++++++++++++++ .../Dialogs/Confirmation.tsx | 49 ++++++++ .../Dialogs/Decryption.tsx | 108 ++++++++++++++++ .../Dialogs/EditMessage.tsx | 72 +++++++++++ .../Dialogs/Encryption.tsx | 114 +++++++++++++++++ .../Dialogs/FetchData.tsx | 18 +++ .../EditEncryptedMessage/Dialogs/Success.tsx | 25 ++++ .../EditEncryptedMessage/Dialogs/index.tsx | 7 ++ .../EditEncryptedMessage/Layout.tsx | 45 +++++++ .../EditEncryptedMessage/context/index.tsx | 118 ++++++++++++++++++ .../EditEncryptedMessage/index.tsx | 33 +++++ packages/cysync-core/src/dialogs/index.tsx | 2 + .../cysync-core/src/store/dialog/index.ts | 3 + .../cysync-core/src/store/dialog/types.ts | 5 + packages/ui/src/assets/images/common/index.ts | 2 + .../assets/images/common/verify-on-device.svg | 37 ++++++ 19 files changed, 795 insertions(+), 50 deletions(-) create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/ConfirmMessage.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/Confirmation.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/Decryption.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/EditMessage.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/Encryption.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/FetchData.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/Success.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/index.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Layout.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/context/index.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/index.tsx create mode 100644 packages/ui/src/assets/images/common/verify-on-device.svg diff --git a/packages/cysync-core-constants/src/i18n/lang/en.json b/packages/cysync-core-constants/src/i18n/lang/en.json index ea6e3c104..f0246b764 100644 --- a/packages/cysync-core-constants/src/i18n/lang/en.json +++ b/packages/cysync-core-constants/src/i18n/lang/en.json @@ -46,7 +46,11 @@ "submit": "Submit", "showMore": "Show more", "resendOTP": "Resend OTP", - "next": "Next" + "next": "Next", + "yes": "Yes", + "no": "No", + "exitWithoutSaving": "Exit without saving", + "saveChanges": "Save changes" }, "dashboard": { "wallet": { @@ -1619,35 +1623,35 @@ }, "messageBox": { "warning": "Make sure you describe the card's location clearly for the nominee" + } + }, + "confirmMessage": { + "title": "Follow instructions on the X1 Vault to verify the nominee message", + "subTitle": "You can encrypt the message in the next step", + "actions": { + "confirmOnDevice": "Confirm on device", + "verifyLocation": "Verify Card location in your device" }, - "confirmMessage": { - "title": "Follow instructions on the X1 Vault to verify the nominee message", - "subTitle": "You can encrypt the message in the next step", - "actions": { - "confirmOnDevice": "Confirm on device", - "verifyLocation": "Verify Card location in your device" - }, - "messageBox": { - "warning": "Read through the whole text and verify that the message shown in the device is same as you entered in cySync" - } + "messageBox": { + "danger": "Read through the whole text and verify that the message shown in the device is same as you entered in cySync" + } + }, + "encryption": { + "title": "Follow instructions on the X1 Vault to encrypt the nominee message", + "subTitle": "Lift your card after 2 beep sounds", + "actions": { + "enterPinAndTap": "Enter PIN and tap any one card" }, - "encryption": { - "title": "Follow instructions on the X1 Vault to encrypt the nominee message", - "subTitle": "Lift your card after 2 beep sounds", - "actions": { - "enterPinAndTap": "Enter PIN and tap any one card" - }, - "messageBox": { - "warning": "The nominee message will be encrypted by the Card" - }, - "syncing": { - "title": "Please wait while we save the encrypted data on the server", - "subTitle": "Do not disconnect or close the app" - } + "messageBox": { + "warning": "The nominee message will be encrypted by the Card" }, - "success": { - "title": "Nominee message successfully updated" + "syncing": { + "title": "Please wait while we save the encrypted data on the server", + "subTitle": "Do not disconnect or close the app" } + }, + "success": { + "title": "Nominee message successfully updated" } } }, diff --git a/packages/cysync-core-constants/src/i18n/types.ts b/packages/cysync-core-constants/src/i18n/types.ts index 9043af8c5..9ee8102d2 100644 --- a/packages/cysync-core-constants/src/i18n/types.ts +++ b/packages/cysync-core-constants/src/i18n/types.ts @@ -87,6 +87,10 @@ interface LangButtons { showMore: string; resendOTP: string; next: string; + yes: string; + no: string; + exitWithoutSaving: string; + saveChanges: string; } interface LangDashboard { @@ -891,35 +895,35 @@ interface LangDialogs { messageBox: { warning: string; }; - confirmMessage: { - title: string; - subTitle: string; - actions: { - confirmOnDevice: string; - verifyLocation: string; - }; - messageBox: { - warning: string; - }; + }; + confirmMessage: { + title: string; + subTitle: string; + actions: { + confirmOnDevice: string; + verifyLocation: string; }; - encryption: { - title: string; - subTitle: string; - actions: { - enterPinAndTap: string; - }; - messageBox: { - warning: string; - }; - syncing: { - title: string; - subTitle: string; - }; + messageBox: { + danger: string; }; - success: { + }; + encryption: { + title: string; + subTitle: string; + actions: { + enterPinAndTap: string; + }; + messageBox: { + warning: string; + }; + syncing: { title: string; + subTitle: string; }; }; + success: { + title: string; + }; }; } diff --git a/packages/cysync-core/src/actions/dialog/index.ts b/packages/cysync-core/src/actions/dialog/index.ts index e5540312b..845b55d98 100644 --- a/packages/cysync-core/src/actions/dialog/index.ts +++ b/packages/cysync-core/src/actions/dialog/index.ts @@ -103,3 +103,6 @@ export const openInheritanceEditReminderTimeDialog = () => export const openEditUserDetailsDialog = () => openDialog({ name: 'inheritanceEditUserDetails', data: undefined }); + +export const openEditEncryptedMessageDialog = () => + openDialog({ name: 'inheritanceEditEncryptedMessage', data: undefined }); diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/ConfirmMessage.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/ConfirmMessage.tsx new file mode 100644 index 000000000..10eaf8dab --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/ConfirmMessage.tsx @@ -0,0 +1,96 @@ +import { SignTransactionDeviceEvent } from '@cypherock/coin-support-interfaces'; +import { + ArrowRightIcon, + Check, + Image, + LeanBox, + LeanBoxContainer, + LeanBoxProps, + MessageBox, + Throbber, + Typography, + verifyOnDevice, +} from '@cypherock/cysync-ui'; +import React, { useEffect } from 'react'; + +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritanceEditEncryptedMessageDialog } from '../context'; +import { Layout } from '../Layout'; + +const checkIconComponent = ; +const throbberComponent = ; +const rightArrowIcon = ; + +export const ConfirmMessage = () => { + const lang = useAppSelector(selectLanguage); + + const strings = + lang.strings.dialogs.inheritanceEditEncryptedMessage.confirmMessage; + + const { onNext } = useInheritanceEditEncryptedMessageDialog(); + + const deviceEvents: Record = { + 0: true, + }; + + const getDeviceEventIcon = ( + loadingEvent: SignTransactionDeviceEvent, + completedEvent: SignTransactionDeviceEvent, + ) => { + if (deviceEvents[completedEvent]) return checkIconComponent; + if (deviceEvents[loadingEvent]) return throbberComponent; + + return undefined; + }; + + const actionsList = React.useMemo(() => { + const actions: LeanBoxProps[] = [ + { + id: '1', + text: strings.actions.confirmOnDevice, + leftImage: rightArrowIcon, + rightImage: getDeviceEventIcon(0, 1), + }, + { + id: '2', + text: strings.actions.verifyLocation, + leftImage: rightArrowIcon, + rightImage: getDeviceEventIcon(0, 1), + }, + ]; + + return actions; + }, []); + + useEffect(() => { + const timeout = setTimeout(() => { + onNext(); + }, 2000); + + return () => clearTimeout(timeout); + }, []); + + return ( + + verify on device + + {strings.title} + + + {actionsList.map(data => ( + + ))} + + + + ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/Confirmation.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/Confirmation.tsx new file mode 100644 index 000000000..a8d7c35e9 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/Confirmation.tsx @@ -0,0 +1,49 @@ +import React from 'react'; +import { selectLanguage, useAppSelector } from '~/store'; +import { Layout } from '../Layout'; +import { + Button, + Container, + LangDisplay, + Typography, +} from '@cypherock/cysync-ui'; +import { useInheritanceEditEncryptedMessageDialog } from '../context'; + +export const Confirmation = () => { + const lang = useAppSelector(selectLanguage); + const strings = lang.strings.dialogs.inheritanceEditEncryptedMessage; + const { onNext, onClose } = useInheritanceEditEncryptedMessageDialog(); + + return ( + + + + + } + > + + + + + + + + + + ); +}; + +export default Confirmation; diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/Decryption.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/Decryption.tsx new file mode 100644 index 000000000..2c2ccfa01 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/Decryption.tsx @@ -0,0 +1,108 @@ +import { SignTransactionDeviceEvent } from '@cypherock/coin-support-interfaces'; +import { + ArrowRightIcon, + Check, + LeanBox, + LeanBoxContainer, + LeanBoxProps, + MessageBox, + tapAnyCardDeviceAnimation2DVideo, + Throbber, + Typography, +} from '@cypherock/cysync-ui'; +import React, { useEffect } from 'react'; + +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritanceEditEncryptedMessageDialog } from '../context'; +import { Layout } from '../Layout'; +import { LoaderDialog } from '~/components'; +import ReactPlayer from 'react-player'; + +const checkIconComponent = ; +const throbberComponent = ; +const rightArrowIcon = ; + +export const Decryption = () => { + const lang = useAppSelector(selectLanguage); + + const strings = + lang.strings.dialogs.inheritanceEditEncryptedMessage.decryption.wallet; + + const { onNext } = useInheritanceEditEncryptedMessageDialog(); + const loading = false; + + const deviceEvents: Record = { + 0: true, + }; + + const getDeviceEventIcon = ( + loadingEvent: SignTransactionDeviceEvent, + completedEvent: SignTransactionDeviceEvent, + ) => { + if (deviceEvents[completedEvent]) return checkIconComponent; + if (deviceEvents[loadingEvent]) return throbberComponent; + + return undefined; + }; + + const actionsList = React.useMemo(() => { + const actions: LeanBoxProps[] = [ + { + id: '1', + text: strings.actions.tapCard, + leftImage: rightArrowIcon, + rightImage: getDeviceEventIcon(0, 1), + }, + ]; + + return actions; + }, []); + + useEffect(() => { + const timeout = setTimeout(() => { + onNext(); + }, 5000); + + return () => clearTimeout(timeout); + }, []); + + if (loading) { + return ( + + ); + } + + return ( + + + + {strings.title} + + + {actionsList.map(data => ( + + ))} + + + + ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/EditMessage.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/EditMessage.tsx new file mode 100644 index 000000000..6fea6433b --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/EditMessage.tsx @@ -0,0 +1,72 @@ +import React from 'react'; +import { Layout } from '../Layout'; +import { selectLanguage, useAppSelector } from '~/store'; +import { + Button, + Container, + InputLabel, + LangDisplay, + MessageBox, + TextAreaInput, + Typography, +} from '@cypherock/cysync-ui'; +import { useInheritanceEditEncryptedMessageDialog } from '../context'; + +export const EditMessage = () => { + const lang = useAppSelector(selectLanguage); + const strings = + lang.strings.dialogs.inheritanceEditEncryptedMessage.editMessage; + const { onClose, onNext } = useInheritanceEditEncryptedMessageDialog(); + + return ( + + + + + } + > + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export default EditMessage; diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/Encryption.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/Encryption.tsx new file mode 100644 index 000000000..235820fbb --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/Encryption.tsx @@ -0,0 +1,114 @@ +import { SignTransactionDeviceEvent } from '@cypherock/coin-support-interfaces'; +import { + ArrowRightIcon, + Check, + Container, + LeanBox, + LeanBoxContainer, + LeanBoxProps, + MessageBox, + tapAnyCardDeviceAnimation2DVideo, + Throbber, + Typography, +} from '@cypherock/cysync-ui'; +import React, { useEffect } from 'react'; + +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritanceEditEncryptedMessageDialog } from '../context'; +import { Layout } from '../Layout'; +import { LoaderDialog } from '~/components'; +import ReactPlayer from 'react-player'; + +const checkIconComponent = ; +const throbberComponent = ; +const rightArrowIcon = ; + +export const Encryption = () => { + const lang = useAppSelector(selectLanguage); + + const strings = + lang.strings.dialogs.inheritanceEditEncryptedMessage.encryption; + + const { onNext } = useInheritanceEditEncryptedMessageDialog(); + const loading = false; + + const deviceEvents: Record = { + 0: true, + }; + + const getDeviceEventIcon = ( + loadingEvent: SignTransactionDeviceEvent, + completedEvent: SignTransactionDeviceEvent, + ) => { + if (deviceEvents[completedEvent]) return checkIconComponent; + if (deviceEvents[loadingEvent]) return throbberComponent; + + return undefined; + }; + + const actionsList = React.useMemo(() => { + const actions: LeanBoxProps[] = [ + { + id: '1', + text: strings.actions.enterPinAndTap, + leftImage: rightArrowIcon, + rightImage: getDeviceEventIcon(0, 1), + }, + ]; + + return actions; + }, []); + + useEffect(() => { + const timeout = setTimeout(() => { + onNext(); + }, 5000); + + return () => clearTimeout(timeout); + }, []); + + if (loading) { + return ( + + ); + } + + return ( + + + + + {strings.title} + + + {strings.subTitle} + + + + {actionsList.map(data => ( + + ))} + + + + ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/FetchData.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/FetchData.tsx new file mode 100644 index 000000000..712d4bd9e --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/FetchData.tsx @@ -0,0 +1,18 @@ +import React, { useEffect } from 'react'; + +import { LoaderDialog } from '~/components'; +import { selectLanguage, useAppSelector } from '~/store'; +import { useInheritanceEditEncryptedMessageDialog } from '../context'; + +export const FetchData = () => { + const lang = useAppSelector(selectLanguage); + const strings = lang.strings.dialogs.inheritanceEditEncryptedMessage.syncing; + + const { onNext } = useInheritanceEditEncryptedMessageDialog(); + + useEffect(() => { + setTimeout(() => onNext(), 2000); + }, []); + + return ; +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/Success.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/Success.tsx new file mode 100644 index 000000000..c06bbb981 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/Success.tsx @@ -0,0 +1,25 @@ +import { SuccessDialog } from '@cypherock/cysync-ui'; +import React from 'react'; + +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritanceEditEncryptedMessageDialog } from '../context'; + +export const Success = () => { + const lang = useAppSelector(selectLanguage); + const { onClose } = useInheritanceEditEncryptedMessageDialog(); + const strings = lang.strings.dialogs.inheritanceEditEncryptedMessage.success; + + return ( + + ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/index.tsx new file mode 100644 index 000000000..9ce96d8c3 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/index.tsx @@ -0,0 +1,7 @@ +export * from './Confirmation'; +export * from './FetchData'; +export * from './Decryption'; +export * from './EditMessage'; +export * from './ConfirmMessage'; +export * from './Encryption'; +export * from './Success'; diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Layout.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Layout.tsx new file mode 100644 index 000000000..02cebe893 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Layout.tsx @@ -0,0 +1,45 @@ +import { + CloseButton, + DialogBox, + DialogBoxBody, + DialogBoxFooter, + DialogBoxHeader, + WidthProps, +} from '@cypherock/cysync-ui'; +import React from 'react'; + +export interface LayoutProps { + width?: WidthProps['width']; + children: React.ReactNode; + footerComponent?: React.ReactNode; + onClose?: () => void; +} + +export const Layout: React.FC = ({ + width, + children, + footerComponent, + onClose, +}) => ( + + {onClose && ( + + + + )} + + {children} + + {footerComponent && ( + + {footerComponent} + + )} + +); + +Layout.defaultProps = { + footerComponent: undefined, + width: undefined, + onClose: undefined, +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/context/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/context/index.tsx new file mode 100644 index 000000000..f25324144 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/context/index.tsx @@ -0,0 +1,118 @@ +import React, { + Context, + FC, + ReactNode, + createContext, + useContext, + useMemo, +} from 'react'; + +import { ITabs, useTabsAndDialogs } from '~/hooks'; +import { closeDialog, useAppDispatch } from '~/store'; + +import { + Confirmation, + EditMessage, + ConfirmMessage, + Decryption, + Encryption, + FetchData, + Success, +} from '../Dialogs'; + +export interface InheritanceEditEncryptedMessageDialogContextInterface { + tabs: ITabs; + onNext: (tab?: number, dialog?: number) => void; + goTo: (tab: number, dialog?: number) => void; + onPrevious: () => void; + onClose: () => void; + currentTab: number; + currentDialog: number; + isDeviceRequired: boolean; + unhandledError?: any; +} + +export const InheritanceEditEncryptedMessageDialogContext: Context = + createContext( + {} as InheritanceEditEncryptedMessageDialogContextInterface, + ); + +export interface InheritanceEditEncryptedMessageDialogContextProviderProps { + children: ReactNode; +} + +export const InheritanceEditEncryptedMessageDialogProvider: FC< + InheritanceEditEncryptedMessageDialogContextProviderProps +> = ({ children }) => { + const dispatch = useAppDispatch(); + + const deviceRequiredDialogsMap: Record = {}; + const tabs: ITabs = [ + { name: 'Confirmation', dialogs: [] }, + { name: 'Fetch Data', dialogs: [] }, + { name: 'Decryption', dialogs: [] }, + { name: 'Edit Message', dialogs: [] }, + { + name: 'Confirm Message', + dialogs: [], + }, + { + name: 'Encryption', + dialogs: [], + }, + { + name: 'Success', + dialogs: [], + }, + ]; + + const { + onNext, + onPrevious, + goTo, + currentTab, + currentDialog, + isDeviceRequired, + } = useTabsAndDialogs({ + deviceRequiredDialogsMap, + tabs, + dialogName: 'inheritanceEditEncryptedMessage', + }); + + const onClose = () => { + dispatch(closeDialog('inheritanceEditEncryptedMessage')); + }; + + const ctx = useMemo( + () => ({ + onNext, + onPrevious, + tabs, + onClose, + goTo, + currentTab, + currentDialog, + isDeviceRequired, + }), + [ + onNext, + onPrevious, + tabs, + onClose, + goTo, + currentTab, + currentDialog, + isDeviceRequired, + ], + ); + + return ( + + {children} + + ); +}; + +export function useInheritanceEditEncryptedMessageDialog(): InheritanceEditEncryptedMessageDialogContextInterface { + return useContext(InheritanceEditEncryptedMessageDialogContext); +} diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/index.tsx new file mode 100644 index 000000000..26655977b --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/index.tsx @@ -0,0 +1,33 @@ +import { BlurOverlay } from '@cypherock/cysync-ui'; +import React, { FC } from 'react'; + +import { ErrorHandlerDialog } from '~/components'; + +import { + InheritanceEditEncryptedMessageDialogProvider, + useInheritanceEditEncryptedMessageDialog, +} from './context'; + +const InheritanceEditEncryptedMessage: FC = () => { + const { tabs, currentTab, currentDialog, unhandledError, onClose } = + useInheritanceEditEncryptedMessageDialog(); + + return ( + + + {tabs[currentTab]?.dialogs[currentDialog]} + + + ); +}; + +export const InheritanceEditEncryptedMessageDialog = () => ( + + + +); diff --git a/packages/cysync-core/src/dialogs/index.tsx b/packages/cysync-core/src/dialogs/index.tsx index 8507bc2a9..51e274fe0 100644 --- a/packages/cysync-core/src/dialogs/index.tsx +++ b/packages/cysync-core/src/dialogs/index.tsx @@ -38,6 +38,7 @@ import { SignMessageDialog } from './SignMessage'; import { WalletActionsDialogBox } from './WalletActions'; import { WalletConnectDialog } from './WalletConnect'; import { WalletSyncError } from './WalletSyncError'; +import { InheritanceEditEncryptedMessageDialog } from './Inheritance/EditEncryptedMessage'; export const dialogs: Record = { walletSyncError: WalletSyncError, @@ -71,4 +72,5 @@ export const dialogs: Record = { inheritanceEditExecutorMessage: InheritanceEditExecutorMessageDialog, inheritanceEditReminderTime: InheritanceEditReminderTimeDialog, inheritanceEditUserDetails: InheritanceEditUserDetailsDialog, + inheritanceEditEncryptedMessage: InheritanceEditEncryptedMessageDialog, }; diff --git a/packages/cysync-core/src/store/dialog/index.ts b/packages/cysync-core/src/store/dialog/index.ts index 4b133a6cb..8fcea8683 100644 --- a/packages/cysync-core/src/store/dialog/index.ts +++ b/packages/cysync-core/src/store/dialog/index.ts @@ -104,6 +104,9 @@ const initialState: IDialogState = { inheritanceEditUserDetails: { isOpen: false, }, + inheritanceEditEncryptedMessage: { + isOpen: false, + }, }; export const dialogSlice = createSlice({ diff --git a/packages/cysync-core/src/store/dialog/types.ts b/packages/cysync-core/src/store/dialog/types.ts index 3134723b2..63b3012d2 100644 --- a/packages/cysync-core/src/store/dialog/types.ts +++ b/packages/cysync-core/src/store/dialog/types.ts @@ -162,6 +162,11 @@ export interface IDialogState { isOpen: boolean; data?: undefined; }; + + inheritanceEditEncryptedMessage: { + isOpen: boolean; + data?: undefined; + }; } export const GuidedFlowMap = { diff --git a/packages/ui/src/assets/images/common/index.ts b/packages/ui/src/assets/images/common/index.ts index a3b676c21..69b736b0a 100644 --- a/packages/ui/src/assets/images/common/index.ts +++ b/packages/ui/src/assets/images/common/index.ts @@ -85,6 +85,7 @@ import WalletHoverExpiredIcon from './wallet-hover-expired-icon.svg'; import WalletHoverSilverBgIcon from './wallet-silver-hover-bg.svg'; import walletIcon from './wallet.svg'; import warningIcon from './warning.svg'; +import verifyOnDevice from './verify-on-device.svg'; export { WalletDefaultExpiredIcon, @@ -174,4 +175,5 @@ export { oneInMany2Default, manyInManyBgImage, manyInManyHoverBgImage, + verifyOnDevice, }; diff --git a/packages/ui/src/assets/images/common/verify-on-device.svg b/packages/ui/src/assets/images/common/verify-on-device.svg new file mode 100644 index 000000000..724d9085f --- /dev/null +++ b/packages/ui/src/assets/images/common/verify-on-device.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From d383971a5e058f2e76234a332c4b877eb3a70847 Mon Sep 17 00:00:00 2001 From: AryanCypherock Date: Wed, 21 Aug 2024 18:08:35 +0530 Subject: [PATCH 23/96] chore: updated lang strings across all languages --- .../src/i18n/lang/ar-AE.json | 76 ++++++++++++++++++- .../src/i18n/lang/de-DE.json | 76 ++++++++++++++++++- .../src/i18n/lang/id-ID.json | 76 ++++++++++++++++++- .../src/i18n/lang/zh-CN.json | 76 ++++++++++++++++++- 4 files changed, 300 insertions(+), 4 deletions(-) diff --git a/packages/cysync-core-constants/src/i18n/lang/ar-AE.json b/packages/cysync-core-constants/src/i18n/lang/ar-AE.json index c3752d821..cab757f68 100644 --- a/packages/cysync-core-constants/src/i18n/lang/ar-AE.json +++ b/packages/cysync-core-constants/src/i18n/lang/ar-AE.json @@ -46,7 +46,11 @@ "submit": "إرسال", "showMore": "عرض المزيد", "resendOTP": "Resend OTP", - "next": "Next" + "next": "Next", + "yes": "Yes", + "no": "No", + "exitWithoutSaving": "Exit without saving", + "saveChanges": "Save changes" }, "dashboard": { "wallet": { @@ -1574,6 +1578,76 @@ "success": { "title": "Owner details successfully updated" } + }, + "inheritanceEditEncryptedMessage": { + "confirmation": { + "title": "Are you sure you want to edit the encrypted nominee message?", + "subTitle": "You will need the device and at least one card to encrypt the message again after editing" + }, + "syncing": { + "title": "Please wait while we fetch the data", + "subTitle": "Do not disconnect or close the app" + }, + "decryption": { + "wallet": { + "title": "Follow instructions on the X1 Vault", + "actions": { + "tapCard": "Tap any card" + }, + "messageBox": { + "warning": "The message will be decrypted by the X1 card" + }, + "decryption": { + "title": "Please wait while we decrypt the data", + "subTitle": "Do not disconnect or close the app" + } + } + }, + "editMessage": { + "title": "Nominee message", + "subTitle": "The nominee messages will be end-to-end encrypted", + "form": { + "cardLocationField": { + "label": "Card location", + "placeholder": "Enter your card's location" + }, + "personalMessageField": { + "label": "Personal message", + "placeholder": "Enter your personal message here" + } + }, + "messageBox": { + "warning": "Make sure you describe the card's location clearly for the nominee" + } + }, + "confirmMessage": { + "title": "Follow instructions on the X1 Vault to verify the nominee message", + "subTitle": "You can encrypt the message in the next step", + "actions": { + "confirmOnDevice": "Confirm on device", + "verifyLocation": "Verify Card location in your device" + }, + "messageBox": { + "danger": "Read through the whole text and verify that the message shown in the device is same as you entered in cySync" + } + }, + "encryption": { + "title": "Follow instructions on the X1 Vault to encrypt the nominee message", + "subTitle": "Lift your card after 2 beep sounds", + "actions": { + "enterPinAndTap": "Enter PIN and tap any one card" + }, + "messageBox": { + "warning": "The nominee message will be encrypted by the Card" + }, + "syncing": { + "title": "Please wait while we save the encrypted data on the server", + "subTitle": "Do not disconnect or close the app" + } + }, + "success": { + "title": "Nominee message successfully updated" + } } }, "toggle": { diff --git a/packages/cysync-core-constants/src/i18n/lang/de-DE.json b/packages/cysync-core-constants/src/i18n/lang/de-DE.json index 91522dea9..f7eb623ae 100644 --- a/packages/cysync-core-constants/src/i18n/lang/de-DE.json +++ b/packages/cysync-core-constants/src/i18n/lang/de-DE.json @@ -46,7 +46,11 @@ "submit": "Absenden", "showMore": "Mehr anzeigen", "resendOTP": "Resend OTP", - "next": "Next" + "next": "Next", + "yes": "Yes", + "no": "No", + "exitWithoutSaving": "Exit without saving", + "saveChanges": "Save changes" }, "dashboard": { "wallet": { @@ -1579,6 +1583,76 @@ "success": { "title": "Owner details successfully updated" } + }, + "inheritanceEditEncryptedMessage": { + "confirmation": { + "title": "Are you sure you want to edit the encrypted nominee message?", + "subTitle": "You will need the device and at least one card to encrypt the message again after editing" + }, + "syncing": { + "title": "Please wait while we fetch the data", + "subTitle": "Do not disconnect or close the app" + }, + "decryption": { + "wallet": { + "title": "Follow instructions on the X1 Vault", + "actions": { + "tapCard": "Tap any card" + }, + "messageBox": { + "warning": "The message will be decrypted by the X1 card" + }, + "decryption": { + "title": "Please wait while we decrypt the data", + "subTitle": "Do not disconnect or close the app" + } + } + }, + "editMessage": { + "title": "Nominee message", + "subTitle": "The nominee messages will be end-to-end encrypted", + "form": { + "cardLocationField": { + "label": "Card location", + "placeholder": "Enter your card's location" + }, + "personalMessageField": { + "label": "Personal message", + "placeholder": "Enter your personal message here" + } + }, + "messageBox": { + "warning": "Make sure you describe the card's location clearly for the nominee" + } + }, + "confirmMessage": { + "title": "Follow instructions on the X1 Vault to verify the nominee message", + "subTitle": "You can encrypt the message in the next step", + "actions": { + "confirmOnDevice": "Confirm on device", + "verifyLocation": "Verify Card location in your device" + }, + "messageBox": { + "danger": "Read through the whole text and verify that the message shown in the device is same as you entered in cySync" + } + }, + "encryption": { + "title": "Follow instructions on the X1 Vault to encrypt the nominee message", + "subTitle": "Lift your card after 2 beep sounds", + "actions": { + "enterPinAndTap": "Enter PIN and tap any one card" + }, + "messageBox": { + "warning": "The nominee message will be encrypted by the Card" + }, + "syncing": { + "title": "Please wait while we save the encrypted data on the server", + "subTitle": "Do not disconnect or close the app" + } + }, + "success": { + "title": "Nominee message successfully updated" + } } }, "toggle": { diff --git a/packages/cysync-core-constants/src/i18n/lang/id-ID.json b/packages/cysync-core-constants/src/i18n/lang/id-ID.json index 5662fab9b..bf4e891e3 100644 --- a/packages/cysync-core-constants/src/i18n/lang/id-ID.json +++ b/packages/cysync-core-constants/src/i18n/lang/id-ID.json @@ -46,7 +46,11 @@ "submit": "Kirim", "showMore": "Tampilkan lebih banyak", "resendOTP": "Resend OTP", - "next": "Next" + "next": "Next", + "yes": "Yes", + "no": "No", + "exitWithoutSaving": "Exit without saving", + "saveChanges": "Save changes" }, "dashboard": { "wallet": { @@ -1579,6 +1583,76 @@ "success": { "title": "Owner details successfully updated" } + }, + "inheritanceEditEncryptedMessage": { + "confirmation": { + "title": "Are you sure you want to edit the encrypted nominee message?", + "subTitle": "You will need the device and at least one card to encrypt the message again after editing" + }, + "syncing": { + "title": "Please wait while we fetch the data", + "subTitle": "Do not disconnect or close the app" + }, + "decryption": { + "wallet": { + "title": "Follow instructions on the X1 Vault", + "actions": { + "tapCard": "Tap any card" + }, + "messageBox": { + "warning": "The message will be decrypted by the X1 card" + }, + "decryption": { + "title": "Please wait while we decrypt the data", + "subTitle": "Do not disconnect or close the app" + } + } + }, + "editMessage": { + "title": "Nominee message", + "subTitle": "The nominee messages will be end-to-end encrypted", + "form": { + "cardLocationField": { + "label": "Card location", + "placeholder": "Enter your card's location" + }, + "personalMessageField": { + "label": "Personal message", + "placeholder": "Enter your personal message here" + } + }, + "messageBox": { + "warning": "Make sure you describe the card's location clearly for the nominee" + } + }, + "confirmMessage": { + "title": "Follow instructions on the X1 Vault to verify the nominee message", + "subTitle": "You can encrypt the message in the next step", + "actions": { + "confirmOnDevice": "Confirm on device", + "verifyLocation": "Verify Card location in your device" + }, + "messageBox": { + "danger": "Read through the whole text and verify that the message shown in the device is same as you entered in cySync" + } + }, + "encryption": { + "title": "Follow instructions on the X1 Vault to encrypt the nominee message", + "subTitle": "Lift your card after 2 beep sounds", + "actions": { + "enterPinAndTap": "Enter PIN and tap any one card" + }, + "messageBox": { + "warning": "The nominee message will be encrypted by the Card" + }, + "syncing": { + "title": "Please wait while we save the encrypted data on the server", + "subTitle": "Do not disconnect or close the app" + } + }, + "success": { + "title": "Nominee message successfully updated" + } } }, "toggle": { diff --git a/packages/cysync-core-constants/src/i18n/lang/zh-CN.json b/packages/cysync-core-constants/src/i18n/lang/zh-CN.json index f345d8abb..1f6c5010c 100644 --- a/packages/cysync-core-constants/src/i18n/lang/zh-CN.json +++ b/packages/cysync-core-constants/src/i18n/lang/zh-CN.json @@ -46,7 +46,11 @@ "submit": "提交", "showMore": "显示更多", "resendOTP": "Resend OTP", - "next": "Next" + "next": "Next", + "yes": "Yes", + "no": "No", + "exitWithoutSaving": "Exit without saving", + "saveChanges": "Save changes" }, "dashboard": { "wallet": { @@ -1554,6 +1558,76 @@ "success": { "title": "Owner details successfully updated" } + }, + "inheritanceEditEncryptedMessage": { + "confirmation": { + "title": "Are you sure you want to edit the encrypted nominee message?", + "subTitle": "You will need the device and at least one card to encrypt the message again after editing" + }, + "syncing": { + "title": "Please wait while we fetch the data", + "subTitle": "Do not disconnect or close the app" + }, + "decryption": { + "wallet": { + "title": "Follow instructions on the X1 Vault", + "actions": { + "tapCard": "Tap any card" + }, + "messageBox": { + "warning": "The message will be decrypted by the X1 card" + }, + "decryption": { + "title": "Please wait while we decrypt the data", + "subTitle": "Do not disconnect or close the app" + } + } + }, + "editMessage": { + "title": "Nominee message", + "subTitle": "The nominee messages will be end-to-end encrypted", + "form": { + "cardLocationField": { + "label": "Card location", + "placeholder": "Enter your card's location" + }, + "personalMessageField": { + "label": "Personal message", + "placeholder": "Enter your personal message here" + } + }, + "messageBox": { + "warning": "Make sure you describe the card's location clearly for the nominee" + } + }, + "confirmMessage": { + "title": "Follow instructions on the X1 Vault to verify the nominee message", + "subTitle": "You can encrypt the message in the next step", + "actions": { + "confirmOnDevice": "Confirm on device", + "verifyLocation": "Verify Card location in your device" + }, + "messageBox": { + "danger": "Read through the whole text and verify that the message shown in the device is same as you entered in cySync" + } + }, + "encryption": { + "title": "Follow instructions on the X1 Vault to encrypt the nominee message", + "subTitle": "Lift your card after 2 beep sounds", + "actions": { + "enterPinAndTap": "Enter PIN and tap any one card" + }, + "messageBox": { + "warning": "The nominee message will be encrypted by the Card" + }, + "syncing": { + "title": "Please wait while we save the encrypted data on the server", + "subTitle": "Do not disconnect or close the app" + } + }, + "success": { + "title": "Nominee message successfully updated" + } } }, "toggle": { From 5b44a6a8d61c1158764879355fcdf8a99918809b Mon Sep 17 00:00:00 2001 From: AryanCypherock Date: Thu, 22 Aug 2024 11:43:29 +0530 Subject: [PATCH 24/96] Feat/inheritance pin recovery dialog (#520) * feat: inheritance recovery pin dialog added * feat: sidebar and device required dialog added * fix: dialog state changed * chore: review changes * fix: revert error dialog --- .../src/i18n/lang/ar-AE.json | 33 +++++ .../src/i18n/lang/de-DE.json | 33 +++++ .../src/i18n/lang/en.json | 33 +++++ .../src/i18n/lang/id-ID.json | 33 +++++ .../src/i18n/lang/zh-CN.json | 33 +++++ .../cysync-core-constants/src/i18n/types.ts | 33 +++++ .../cysync-core/src/actions/dialog/index.ts | 3 + .../EditUserDetails/Dialogs/EditDetails.tsx | 9 +- .../EditUserDetails/Dialogs/VerifyOTP.tsx | 4 +- .../EditUserDetails/context/index.tsx | 2 +- .../Inheritance/EditUserDetails/index.tsx | 4 +- .../PinRecovery/Dialogs/DecryptPin.tsx | 110 ++++++++++++++++ .../PinRecovery/Dialogs/FetchData.tsx | 22 ++++ .../PinRecovery/Dialogs/Success.tsx | 25 ++++ .../PinRecovery/Dialogs/ViewPin.tsx | 70 ++++++++++ .../Inheritance/PinRecovery/Dialogs/index.tsx | 4 + .../Inheritance/PinRecovery/Layout.tsx | 31 +++++ .../Inheritance/PinRecovery/context/index.tsx | 123 ++++++++++++++++++ .../dialogs/Inheritance/PinRecovery/index.tsx | 97 ++++++++++++++ packages/cysync-core/src/dialogs/index.tsx | 2 + .../src/pages/MainApp/Inheritance/index.tsx | 4 +- .../cysync-core/src/store/dialog/index.ts | 3 + .../cysync-core/src/store/dialog/types.ts | 5 + packages/ui/icons/account-icon.svg | 3 + .../ui/src/components/atoms/ExternalLink.tsx | 2 +- .../DashboardWallet/DashboardWallet.tsx | 1 + .../molecules/Dialog/SuccessDialog.tsx | 2 +- .../src/components/molecules/MessageBox.tsx | 9 +- .../ui/src/components/molecules/Payment.tsx | 9 +- .../ui/src/components/molecules/PlanCard.tsx | 4 +- .../ui/src/components/utils/bgColor.styled.ts | 10 +- 31 files changed, 734 insertions(+), 22 deletions(-) create mode 100644 packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/DecryptPin.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/FetchData.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/Success.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/ViewPin.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/index.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Layout.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/PinRecovery/context/index.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/PinRecovery/index.tsx create mode 100644 packages/ui/icons/account-icon.svg diff --git a/packages/cysync-core-constants/src/i18n/lang/ar-AE.json b/packages/cysync-core-constants/src/i18n/lang/ar-AE.json index c3752d821..c9e7e0ae6 100644 --- a/packages/cysync-core-constants/src/i18n/lang/ar-AE.json +++ b/packages/cysync-core-constants/src/i18n/lang/ar-AE.json @@ -1574,6 +1574,39 @@ "success": { "title": "Owner details successfully updated" } + }, + "inheritancePinRecovery": { + "title": "Pin Recovery", + "fetch": { + "name": "Syncing", + "title": "Please wait while we fetch the encrypted PIN", + "subTitle": "Do no disconnect or close the app" + }, + "decryptPin": { + "name": "Decryption", + "title": "Follow instructions on the X1 Vault", + "actions": { + "tapCard": "Tap a valid card" + }, + "messageBox": { + "warning": "The PIN will be decrypted by the card" + }, + "error": { + "title": "PIN Decryption Failed", + "subTitle": "PIN decryption was unsuccessful. Wallet ABC was not found in the card" + } + }, + "viewPin": { + "name": "View pin", + "title": "Follow instructions on the X1 Vault", + "actions": { + "viewDevice": "View the PIN on your device and confirm" + } + }, + "success": { + "name": "Confirmation", + "title": "Congratulations, Pin recovery is complete" + } } }, "toggle": { diff --git a/packages/cysync-core-constants/src/i18n/lang/de-DE.json b/packages/cysync-core-constants/src/i18n/lang/de-DE.json index 91522dea9..71f4bbd44 100644 --- a/packages/cysync-core-constants/src/i18n/lang/de-DE.json +++ b/packages/cysync-core-constants/src/i18n/lang/de-DE.json @@ -1579,6 +1579,39 @@ "success": { "title": "Owner details successfully updated" } + }, + "inheritancePinRecovery": { + "title": "Pin Recovery", + "fetch": { + "name": "Syncing", + "title": "Please wait while we fetch the encrypted PIN", + "subTitle": "Do no disconnect or close the app" + }, + "decryptPin": { + "name": "Decryption", + "title": "Follow instructions on the X1 Vault", + "actions": { + "tapCard": "Tap a valid card" + }, + "messageBox": { + "warning": "The PIN will be decrypted by the card" + }, + "error": { + "title": "PIN Decryption Failed", + "subTitle": "PIN decryption was unsuccessful. Wallet ABC was not found in the card" + } + }, + "viewPin": { + "name": "View pin", + "title": "Follow instructions on the X1 Vault", + "actions": { + "viewDevice": "View the PIN on your device and confirm" + } + }, + "success": { + "name": "Confirmation", + "title": "Congratulations, Pin recovery is complete" + } } }, "toggle": { diff --git a/packages/cysync-core-constants/src/i18n/lang/en.json b/packages/cysync-core-constants/src/i18n/lang/en.json index 70436d943..23ecc2883 100644 --- a/packages/cysync-core-constants/src/i18n/lang/en.json +++ b/packages/cysync-core-constants/src/i18n/lang/en.json @@ -1579,6 +1579,39 @@ "success": { "title": "Owner details successfully updated" } + }, + "inheritancePinRecovery": { + "title": "Pin Recovery", + "fetch": { + "name": "Syncing", + "title": "Please wait while we fetch the encrypted PIN", + "subTitle": "Do no disconnect or close the app" + }, + "decryptPin": { + "name": "Decryption", + "title": "Follow instructions on the X1 Vault", + "actions": { + "tapCard": "Tap a valid card" + }, + "messageBox": { + "warning": "The PIN will be decrypted by the card" + }, + "error": { + "title": "PIN Decryption Failed", + "subTitle": "PIN decryption was unsuccessful. Wallet ABC was not found in the card" + } + }, + "viewPin": { + "name": "View pin", + "title": "Follow instructions on the X1 Vault", + "actions": { + "viewDevice": "View the PIN on your device and confirm" + } + }, + "success": { + "name": "Confirmation", + "title": "Congratulations, Pin recovery is complete" + } } }, "toggle": { diff --git a/packages/cysync-core-constants/src/i18n/lang/id-ID.json b/packages/cysync-core-constants/src/i18n/lang/id-ID.json index 5662fab9b..5591d32bc 100644 --- a/packages/cysync-core-constants/src/i18n/lang/id-ID.json +++ b/packages/cysync-core-constants/src/i18n/lang/id-ID.json @@ -1579,6 +1579,39 @@ "success": { "title": "Owner details successfully updated" } + }, + "inheritancePinRecovery": { + "title": "Pin Recovery", + "fetch": { + "name": "Syncing", + "title": "Please wait while we fetch the encrypted PIN", + "subTitle": "Do no disconnect or close the app" + }, + "decryptPin": { + "name": "Decryption", + "title": "Follow instructions on the X1 Vault", + "actions": { + "tapCard": "Tap a valid card" + }, + "messageBox": { + "warning": "The PIN will be decrypted by the card" + }, + "error": { + "title": "PIN Decryption Failed", + "subTitle": "PIN decryption was unsuccessful. Wallet ABC was not found in the card" + } + }, + "viewPin": { + "name": "View pin", + "title": "Follow instructions on the X1 Vault", + "actions": { + "viewDevice": "View the PIN on your device and confirm" + } + }, + "success": { + "name": "Confirmation", + "title": "Congratulations, Pin recovery is complete" + } } }, "toggle": { diff --git a/packages/cysync-core-constants/src/i18n/lang/zh-CN.json b/packages/cysync-core-constants/src/i18n/lang/zh-CN.json index f345d8abb..f13b376b5 100644 --- a/packages/cysync-core-constants/src/i18n/lang/zh-CN.json +++ b/packages/cysync-core-constants/src/i18n/lang/zh-CN.json @@ -1554,6 +1554,39 @@ "success": { "title": "Owner details successfully updated" } + }, + "inheritancePinRecovery": { + "title": "Pin Recovery", + "fetch": { + "name": "Syncing", + "title": "Please wait while we fetch the encrypted PIN", + "subTitle": "Do no disconnect or close the app" + }, + "decryptPin": { + "name": "Decryption", + "title": "Follow instructions on the X1 Vault", + "actions": { + "tapCard": "Tap a valid card" + }, + "messageBox": { + "warning": "The PIN will be decrypted by the card" + }, + "error": { + "title": "PIN Decryption Failed", + "subTitle": "PIN decryption was unsuccessful. Wallet ABC was not found in the card" + } + }, + "viewPin": { + "name": "View pin", + "title": "Follow instructions on the X1 Vault", + "actions": { + "viewDevice": "View the PIN on your device and confirm" + } + }, + "success": { + "name": "Confirmation", + "title": "Congratulations, Pin recovery is complete" + } } }, "toggle": { diff --git a/packages/cysync-core-constants/src/i18n/types.ts b/packages/cysync-core-constants/src/i18n/types.ts index 5355642d1..c5eaa9a75 100644 --- a/packages/cysync-core-constants/src/i18n/types.ts +++ b/packages/cysync-core-constants/src/i18n/types.ts @@ -851,6 +851,39 @@ interface LangDialogs { title: string; }; }; + inheritancePinRecovery: { + title: string; + fetch: { + name: string; + title: string; + subTitle: string; + }; + decryptPin: { + name: string; + title: string; + actions: { + tapCard: string; + }; + messageBox: { + warning: string; + }; + error: { + title: string; + subTitle: string; + }; + }; + viewPin: { + name: string; + title: string; + actions: { + viewDevice: string; + }; + }; + success: { + name: string; + title: string; + }; + }; } interface LangToggle { diff --git a/packages/cysync-core/src/actions/dialog/index.ts b/packages/cysync-core/src/actions/dialog/index.ts index e5540312b..b4d4ac243 100644 --- a/packages/cysync-core/src/actions/dialog/index.ts +++ b/packages/cysync-core/src/actions/dialog/index.ts @@ -103,3 +103,6 @@ export const openInheritanceEditReminderTimeDialog = () => export const openEditUserDetailsDialog = () => openDialog({ name: 'inheritanceEditUserDetails', data: undefined }); + +export const openInheritancePinRecoveryDialog = () => + openDialog({ name: 'inheritancePinRecovery', data: undefined }); diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/Dialogs/EditDetails.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/Dialogs/EditDetails.tsx index f972f8fa1..d3f269a8f 100644 --- a/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/Dialogs/EditDetails.tsx +++ b/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/Dialogs/EditDetails.tsx @@ -1,6 +1,3 @@ -import React, { useState } from 'react'; - -import { useInheritanceEditUserDetailsDialog } from '../context'; import { Button, CloseButton, @@ -14,8 +11,12 @@ import { ScrollableContainer, Typography, } from '@cypherock/cysync-ui'; -import { selectLanguage } from '~/store/lang'; +import React, { useState } from 'react'; + import { useAppSelector } from '~/store'; +import { selectLanguage } from '~/store/lang'; + +import { useInheritanceEditUserDetailsDialog } from '../context'; export const EditDetails = () => { const lang = useAppSelector(selectLanguage); diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/Dialogs/VerifyOTP.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/Dialogs/VerifyOTP.tsx index 1804c4af7..e70f44e5f 100644 --- a/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/Dialogs/VerifyOTP.tsx +++ b/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/Dialogs/VerifyOTP.tsx @@ -1,5 +1,7 @@ +import { sleep } from '@cypherock/sdk-utils'; import React, { useMemo, useRef, useState } from 'react'; +import { LoaderDialog } from '~/components'; import { OTPInputDialog, OTPInputDialogRef, @@ -7,8 +9,6 @@ import { import { selectLanguage, useAppSelector } from '~/store'; import { useInheritanceEditUserDetailsDialog } from '../context'; -import { LoaderDialog } from '~/components'; -import { sleep } from '@cypherock/sdk-utils'; export const VerifyOTP: React.FC = () => { const lang = useAppSelector(selectLanguage); diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/context/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/context/index.tsx index bdf6f29df..802c546b9 100644 --- a/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/context/index.tsx +++ b/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/context/index.tsx @@ -1,3 +1,4 @@ +import { sleep } from '@cypherock/sdk-utils'; import React, { Context, FC, @@ -13,7 +14,6 @@ import { ITabs, useTabsAndDialogs } from '~/hooks'; import { closeDialog, useAppDispatch } from '~/store'; import { EditDetails, Success, VerifyOTP } from '../Dialogs'; -import { sleep } from '@cypherock/sdk-utils'; export interface IUserDetails { name: string; diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/index.tsx index 3274c685c..a2cde9a0e 100644 --- a/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/index.tsx +++ b/packages/cysync-core/src/dialogs/Inheritance/EditUserDetails/index.tsx @@ -1,6 +1,8 @@ -import React, { FC } from 'react'; import { BlurOverlay } from '@cypherock/cysync-ui'; +import React, { FC } from 'react'; + import { ErrorHandlerDialog } from '~/components'; + import { InheritanceEditUserDetailsDialogProvider, useInheritanceEditUserDetailsDialog, diff --git a/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/DecryptPin.tsx b/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/DecryptPin.tsx new file mode 100644 index 000000000..71d9eb180 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/DecryptPin.tsx @@ -0,0 +1,110 @@ +import { SignTransactionDeviceEvent } from '@cypherock/coin-support-interfaces'; +import { + ArrowRightIcon, + Check, + Container, + ErrorDialog, + LeanBox, + LeanBoxContainer, + LeanBoxProps, + MessageBox, + Throbber, + Typography, +} from '@cypherock/cysync-ui'; +import React, { useEffect } from 'react'; + +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritancePinRecoveryDialog } from '../context'; +import { Layout } from '../Layout'; + +const checkIconComponent = ; +const throbberComponent = ; +const rightArrowIcon = ; + +export const DecryptPin = () => { + const lang = useAppSelector(selectLanguage); + + const strings = lang.strings.dialogs.inheritancePinRecovery; + + const { onNext } = useInheritancePinRecoveryDialog(); + const error = false; + + const deviceEvents: Record = { + 0: true, + }; + + const getDeviceEventIcon = ( + loadingEvent: SignTransactionDeviceEvent, + completedEvent: SignTransactionDeviceEvent, + ) => { + if (deviceEvents[completedEvent]) return checkIconComponent; + if (deviceEvents[loadingEvent]) return throbberComponent; + + return undefined; + }; + + const actionsList = React.useMemo(() => { + const actions: LeanBoxProps[] = [ + { + id: '1', + text: strings.decryptPin.actions.tapCard, + leftImage: rightArrowIcon, + rightImage: getDeviceEventIcon(0, 1), + }, + ]; + + return actions; + }, []); + + useEffect(() => { + const timeout = setTimeout(() => { + onNext(); + }, 2000); + return () => clearTimeout(timeout); + }, []); + + if (error) { + return ( + { + onNext(); + }} + onSecondaryClick={() => { + // TODO: implement this function + }} + /> + ); + } + + return ( + + + + {strings.decryptPin.title} + + + {actionsList.map(data => ( + + ))} + + + + + ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/FetchData.tsx b/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/FetchData.tsx new file mode 100644 index 000000000..605e763fa --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/FetchData.tsx @@ -0,0 +1,22 @@ +import React, { useEffect } from 'react'; + +import { LoaderDialog } from '~/components'; +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritancePinRecoveryDialog } from '../context'; + +export const FetchData = () => { + const lang = useAppSelector(selectLanguage); + const strings = lang.strings.dialogs.inheritancePinRecovery.fetch; + const { onNext } = useInheritancePinRecoveryDialog(); + + useEffect(() => { + const timeout = setTimeout(() => { + onNext(); + }, 2000); + + return () => clearTimeout(timeout); + }, []); + + return ; +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/Success.tsx b/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/Success.tsx new file mode 100644 index 000000000..4f0cf5ddb --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/Success.tsx @@ -0,0 +1,25 @@ +import { ConfettiBlast, SuccessDialog } from '@cypherock/cysync-ui'; +import React from 'react'; + +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritancePinRecoveryDialog } from '../context'; + +export const SuccessPinRecovery = () => { + const lang = useAppSelector(selectLanguage); + const { onClose } = useInheritancePinRecoveryDialog(); + + const strings = lang.strings.dialogs.inheritancePinRecovery.success; + + return ( + <> + + + + ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/ViewPin.tsx b/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/ViewPin.tsx new file mode 100644 index 000000000..9d4beab58 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/ViewPin.tsx @@ -0,0 +1,70 @@ +import { + ArrowRightIcon, + Container, + LeanBox, + LeanBoxContainer, + LeanBoxProps, + Throbber, + Typography, +} from '@cypherock/cysync-ui'; +import React, { useEffect } from 'react'; + +import { selectLanguage, useAppSelector } from '~/store'; + +import { useInheritancePinRecoveryDialog } from '../context'; +import { Layout } from '../Layout'; + +const throbberComponent = ; +const rightArrowIcon = ; + +export const ViewPin = () => { + const lang = useAppSelector(selectLanguage); + + const { onNext } = useInheritancePinRecoveryDialog(); + + const strings = lang.strings.dialogs.inheritancePinRecovery.viewPin; + + const actionsList = React.useMemo(() => { + const actions: LeanBoxProps[] = [ + { + id: '1', + text: strings.actions.viewDevice, + leftImage: rightArrowIcon, + rightImage: throbberComponent, + }, + ]; + + return actions; + }, []); + + useEffect(() => { + const timeout = setTimeout(() => { + onNext(); + }, 2000); + + return () => clearTimeout(timeout); + }, []); + + return ( + + + + {strings.title} + + + {actionsList.map(data => ( + + ))} + + + + ); +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/index.tsx new file mode 100644 index 000000000..d88a84a56 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/index.tsx @@ -0,0 +1,4 @@ +export * from './DecryptPin'; +export * from './Success'; +export * from './ViewPin'; +export * from './FetchData'; diff --git a/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Layout.tsx b/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Layout.tsx new file mode 100644 index 000000000..eb50005af --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Layout.tsx @@ -0,0 +1,31 @@ +import { + DialogBox, + DialogBoxBody, + DialogBoxFooter, +} from '@cypherock/cysync-ui'; +import React from 'react'; + +export interface LayoutProps { + children: React.ReactNode; + footerComponent?: React.ReactNode; +} + +export const Layout: React.FC = ({ + children, + footerComponent, +}) => ( + + + {children} + + {footerComponent && ( + + {footerComponent} + + )} + +); + +Layout.defaultProps = { + footerComponent: undefined, +}; diff --git a/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/context/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/context/index.tsx new file mode 100644 index 000000000..f07398b2f --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/context/index.tsx @@ -0,0 +1,123 @@ +import React, { + Context, + FC, + ReactNode, + createContext, + useContext, + useMemo, +} from 'react'; + +import { ITabs, useTabsAndDialogs } from '~/hooks'; +import { + closeDialog, + selectLanguage, + useAppDispatch, + useAppSelector, +} from '~/store'; + +import { ViewPin, FetchData, SuccessPinRecovery, DecryptPin } from '../Dialogs'; + +export interface InheritancePinRecoveryDialogContextInterface { + tabs: ITabs; + onNext: (tab?: number, dialog?: number) => void; + goTo: (tab: number, dialog?: number) => void; + onPrevious: () => void; + onClose: () => void; + currentTab: number; + currentDialog: number; + isDeviceRequired: boolean; + unhandledError?: any; +} + +export const InheritancePinRecoveryDialogContext: Context = + createContext( + {} as InheritancePinRecoveryDialogContextInterface, + ); + +export interface InheritancePinRecoveryDialogContextProviderProps { + children: ReactNode; +} + +export const InheritancePinRecoveryDialogProvider: FC< + InheritancePinRecoveryDialogContextProviderProps +> = ({ children }) => { + const lang = useAppSelector(selectLanguage); + const dispatch = useAppDispatch(); + + const deviceRequiredDialogsMap: Record = + useMemo( + () => ({ + 0: [0], + }), + [], + ); + + const tabs: ITabs = [ + { + name: lang.strings.dialogs.inheritancePinRecovery.fetch.name, + dialogs: [], + }, + { + name: lang.strings.dialogs.inheritancePinRecovery.decryptPin.name, + dialogs: [], + }, + { + name: lang.strings.dialogs.inheritancePinRecovery.viewPin.name, + dialogs: [], + }, + { + name: lang.strings.dialogs.inheritancePinRecovery.success.name, + dialogs: [], + }, + ]; + + const { + onNext, + onPrevious, + goTo, + currentTab, + currentDialog, + isDeviceRequired, + } = useTabsAndDialogs({ + deviceRequiredDialogsMap, + tabs, + dialogName: 'inheritancePinRecovery', + }); + + const onClose = () => { + dispatch(closeDialog('inheritancePinRecovery')); + }; + + const ctx = useMemo( + () => ({ + onNext, + onPrevious, + tabs, + onClose, + goTo, + currentTab, + currentDialog, + isDeviceRequired, + }), + [ + onNext, + onPrevious, + tabs, + onClose, + goTo, + currentTab, + currentDialog, + isDeviceRequired, + ], + ); + + return ( + + {children} + + ); +}; + +export function useInheritancePinRecoveryDialog(): InheritancePinRecoveryDialogContextInterface { + return useContext(InheritancePinRecoveryDialogContext); +} diff --git a/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/index.tsx new file mode 100644 index 000000000..9c8d38832 --- /dev/null +++ b/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/index.tsx @@ -0,0 +1,97 @@ +import { + BlurOverlay, + DialogBox, + DialogBoxBody, + MilestoneAside, +} from '@cypherock/cysync-ui'; +import React, { FC } from 'react'; + +import { + ErrorHandlerDialog, + WithConnectedDevice, + WithConnectedDeviceProps, +} from '~/components'; + +import { + InheritancePinRecoveryDialogProvider, + useInheritancePinRecoveryDialog, +} from './context'; +import { selectLanguage, useAppSelector } from '~/store'; + +const DeviceConnectionWrapper: React.FC<{ + isDeviceRequired: boolean; + children: React.ReactNode; + withConnectedProps?: WithConnectedDeviceProps; +}> = ({ isDeviceRequired, withConnectedProps, children }) => { + if (isDeviceRequired) + return ( + + {children} + + ); + // eslint-disable-next-line react/jsx-no-useless-fragment + return <>{children}; +}; + +DeviceConnectionWrapper.defaultProps = { + withConnectedProps: {}, +}; + +const InheritancePinRecovery: FC = () => { + const lang = useAppSelector(selectLanguage); + const { + tabs, + currentTab, + currentDialog, + unhandledError, + onClose, + isDeviceRequired, + } = useInheritancePinRecoveryDialog(); + + return ( + + + <> + !t.dontShowOnMilestone) + .map(t => t.name)} + activeTab={currentTab} + heading={lang.strings.dialogs.inheritancePinRecovery.title} + /> + + + + {tabs[currentTab]?.dialogs[currentDialog]} + + + + + + + ); +}; + +export const InheritancePinRecoveryDialog = () => ( + + + +); diff --git a/packages/cysync-core/src/dialogs/index.tsx b/packages/cysync-core/src/dialogs/index.tsx index 8507bc2a9..dcd22a412 100644 --- a/packages/cysync-core/src/dialogs/index.tsx +++ b/packages/cysync-core/src/dialogs/index.tsx @@ -27,6 +27,7 @@ import { InheritanceSyncPlansDialog, InheritanceSilverPlanPurchaseDialog, } from './Inheritance'; +import { InheritancePinRecoveryDialog } from './Inheritance/PinRecovery'; import { ChangePasswordDialog, RemovePasswordDialog, @@ -71,4 +72,5 @@ export const dialogs: Record = { inheritanceEditExecutorMessage: InheritanceEditExecutorMessageDialog, inheritanceEditReminderTime: InheritanceEditReminderTimeDialog, inheritanceEditUserDetails: InheritanceEditUserDetailsDialog, + inheritancePinRecovery: InheritancePinRecoveryDialog, }; diff --git a/packages/cysync-core/src/pages/MainApp/Inheritance/index.tsx b/packages/cysync-core/src/pages/MainApp/Inheritance/index.tsx index 224791a44..4d37e36f6 100644 --- a/packages/cysync-core/src/pages/MainApp/Inheritance/index.tsx +++ b/packages/cysync-core/src/pages/MainApp/Inheritance/index.tsx @@ -1,8 +1,10 @@ import React from 'react'; + import { InheritancePageLayout } from '~/components'; -import { MainAppLayout } from '../Layout'; import { selectLanguage, useAppSelector } from '~/store'; +import { MainAppLayout } from '../Layout'; + export const Inheritance = () => { const lang = useAppSelector(selectLanguage); diff --git a/packages/cysync-core/src/store/dialog/index.ts b/packages/cysync-core/src/store/dialog/index.ts index 4b133a6cb..42dccc74a 100644 --- a/packages/cysync-core/src/store/dialog/index.ts +++ b/packages/cysync-core/src/store/dialog/index.ts @@ -104,6 +104,9 @@ const initialState: IDialogState = { inheritanceEditUserDetails: { isOpen: false, }, + inheritancePinRecovery: { + isOpen: false, + }, }; export const dialogSlice = createSlice({ diff --git a/packages/cysync-core/src/store/dialog/types.ts b/packages/cysync-core/src/store/dialog/types.ts index 3134723b2..f411cace2 100644 --- a/packages/cysync-core/src/store/dialog/types.ts +++ b/packages/cysync-core/src/store/dialog/types.ts @@ -162,6 +162,11 @@ export interface IDialogState { isOpen: boolean; data?: undefined; }; + + inheritancePinRecovery: { + isOpen: boolean; + data?: undefined; + }; } export const GuidedFlowMap = { diff --git a/packages/ui/icons/account-icon.svg b/packages/ui/icons/account-icon.svg new file mode 100644 index 000000000..10958b670 --- /dev/null +++ b/packages/ui/icons/account-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/src/components/atoms/ExternalLink.tsx b/packages/ui/src/components/atoms/ExternalLink.tsx index f6d1e4ee6..cb5c14e8d 100644 --- a/packages/ui/src/components/atoms/ExternalLink.tsx +++ b/packages/ui/src/components/atoms/ExternalLink.tsx @@ -7,8 +7,8 @@ import { LangDisplay } from './LangDisplay'; import { Typography, TypographyColor } from './Typography'; import { OpenExternalLinkIcon } from '../../assets'; -import { BgColor, UtilsProps } from '../utils'; import { useTheme } from '../../themes'; +import { BgColor, UtilsProps } from '../utils'; export type ExternalLinkVariantTypes = 'disabled' | 'golden'; diff --git a/packages/ui/src/components/molecules/DashboardWallet/DashboardWallet.tsx b/packages/ui/src/components/molecules/DashboardWallet/DashboardWallet.tsx index bf0a3148b..fde018202 100644 --- a/packages/ui/src/components/molecules/DashboardWallet/DashboardWallet.tsx +++ b/packages/ui/src/components/molecules/DashboardWallet/DashboardWallet.tsx @@ -39,6 +39,7 @@ import { import { useTheme } from '../../../themes'; import { WidthProps } from '../../utils'; + import { format } from 'date-fns'; export interface DashboardWalletProps extends WidthProps { diff --git a/packages/ui/src/components/molecules/Dialog/SuccessDialog.tsx b/packages/ui/src/components/molecules/Dialog/SuccessDialog.tsx index 13a0cf8de..1ab8fd774 100644 --- a/packages/ui/src/components/molecules/Dialog/SuccessDialog.tsx +++ b/packages/ui/src/components/molecules/Dialog/SuccessDialog.tsx @@ -17,8 +17,8 @@ import { TypographyProps, CloseButton, } from '../../atoms'; -import { AlertBox } from '../AlertBox'; import { SpacingProps } from '../../utils'; +import { AlertBox } from '../AlertBox'; export interface SuccessDialogProps { title: string; diff --git a/packages/ui/src/components/molecules/MessageBox.tsx b/packages/ui/src/components/molecules/MessageBox.tsx index 404ed5a0d..eec252b71 100644 --- a/packages/ui/src/components/molecules/MessageBox.tsx +++ b/packages/ui/src/components/molecules/MessageBox.tsx @@ -27,6 +27,7 @@ export const MessageBox: FC<{ altText?: string; textColor?: TypographyColor; type: MessageBoxType; + icon?: React.ReactNode; rightImage?: React.ReactNode; variables?: any; showIcon?: boolean; @@ -39,6 +40,7 @@ export const MessageBox: FC<{ rightImage, variables, showIcon, + icon, showQuestionmark, }) => { const theme = useTheme(); @@ -61,9 +63,9 @@ export const MessageBox: FC<{ $alignSelf="stretch" > {showIcon && ( -
- -
+ + {icon ?? } + )} @@ -91,4 +93,5 @@ MessageBox.defaultProps = { variables: undefined, showIcon: true, showQuestionmark: false, + icon: undefined, }; diff --git a/packages/ui/src/components/molecules/Payment.tsx b/packages/ui/src/components/molecules/Payment.tsx index 0cd74d49c..4d4f79103 100644 --- a/packages/ui/src/components/molecules/Payment.tsx +++ b/packages/ui/src/components/molecules/Payment.tsx @@ -1,4 +1,9 @@ import React, { FC, useState } from 'react'; + +import { CouponInput } from './CouponInput'; + +import { errorIcon, ShoppingCart } from '../../assets'; +import { useTheme } from '../../themes'; import { Container, Divider, @@ -8,10 +13,6 @@ import { Typography, } from '../atoms'; -import { CouponInput } from './CouponInput'; -import { useTheme } from '../../themes'; -import { errorIcon, ShoppingCart } from '../../assets'; - export interface PaymentProps { lang: { payment: { diff --git a/packages/ui/src/components/molecules/PlanCard.tsx b/packages/ui/src/components/molecules/PlanCard.tsx index 90ba4e510..b73f81982 100644 --- a/packages/ui/src/components/molecules/PlanCard.tsx +++ b/packages/ui/src/components/molecules/PlanCard.tsx @@ -135,14 +135,14 @@ export const PlanCard: React.FC = ({ {feature.available ? ( ) : ( background: ${({ theme }) => theme.palette.background.error}; `} ${props => - props.$bgColor === 'error' && + props.$bgColor === 'disabled' && css` background: ${({ theme }) => theme.palette.background.disabled}; `} + ${props => + props.$bgColor === 'message' && + css` + background: ${({ theme }) => theme.palette.background.message}; + `} `; From cc744bdd37345a1ff7cb30a3b229be049cc1a682 Mon Sep 17 00:00:00 2001 From: Championrunner <27343592+Ishaan28malik@users.noreply.github.com> Date: Fri, 23 Aug 2024 19:42:13 +0530 Subject: [PATCH 25/96] feat: summary component UI (#514) * feat: summary component UI * refactor: changed summary into summary card component - Rewrote nominee message to make it atomic - Refactored summary component into summary card component * refactor: remove obsolete comments * chore: prettier fix * chore: review changes * refactor: generalize the summary card * fix: fix build --------- Co-authored-by: TejasvOnly Co-authored-by: Tejasv Sharma <37572049+TejasvOnly@users.noreply.github.com> --- packages/ui/icons/email-icon-small.svg | 3 + packages/ui/icons/user-icon.svg | 3 + .../src/components/atoms/NomineeMessage.tsx | 76 ++++--- .../src/components/molecules/DetailsCard.tsx | 137 ++++++++++++ .../molecules/Prefabs/EditButton.tsx | 20 ++ .../src/components/molecules/Prefabs/index.ts | 1 + packages/ui/src/components/molecules/index.ts | 1 + .../stories/atoms/NomineeMessage.stories.tsx | 18 +- .../stories/molecules/DetailsCard.stories.tsx | 195 ++++++++++++++++++ packages/ui/src/themes/color.styled.ts | 4 + packages/ui/src/themes/theme.styled.ts | 3 + 11 files changed, 426 insertions(+), 35 deletions(-) create mode 100644 packages/ui/icons/email-icon-small.svg create mode 100644 packages/ui/icons/user-icon.svg create mode 100644 packages/ui/src/components/molecules/DetailsCard.tsx create mode 100644 packages/ui/src/components/molecules/Prefabs/EditButton.tsx create mode 100644 packages/ui/src/stories/molecules/DetailsCard.stories.tsx diff --git a/packages/ui/icons/email-icon-small.svg b/packages/ui/icons/email-icon-small.svg new file mode 100644 index 000000000..5f1971e79 --- /dev/null +++ b/packages/ui/icons/email-icon-small.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/icons/user-icon.svg b/packages/ui/icons/user-icon.svg new file mode 100644 index 000000000..00997a301 --- /dev/null +++ b/packages/ui/icons/user-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/src/components/atoms/NomineeMessage.tsx b/packages/ui/src/components/atoms/NomineeMessage.tsx index c563896e9..3509b02e1 100644 --- a/packages/ui/src/components/atoms/NomineeMessage.tsx +++ b/packages/ui/src/components/atoms/NomineeMessage.tsx @@ -1,49 +1,73 @@ import React, { FC } from 'react'; import styled from 'styled-components'; -import { Button } from './Button'; import { Flex } from './Flex'; -import { Typography } from './Typography'; +import { Typography, TypographyColor } from './Typography'; -import { WidthProps, width } from '../utils'; +import { WidthProps } from '../utils'; + +export type NomineeMessageVariant = 'danger'; export interface NomineeMessageProps extends WidthProps { - text: string; - icon: React.ReactNode; - actionText: string; - onAction: () => void; + label: string; + value?: string; + leading?: React.ReactNode; + trailing?: React.ReactNode; + variant?: NomineeMessageVariant; + withBackground?: boolean; + isHeader?: boolean; } -const StyledNomineeMessage = styled.div` +const StyledNomineeMessage = styled.div<{ $withBackground: boolean }>` display: flex; - width: 624px; + min-width: 624px; padding: 8px 16px; justify-content: space-between; align-items: center; - background: ${({ theme }) => theme.palette.background.slate}; + background: ${({ $withBackground, theme }) => + $withBackground ? theme.palette.background.slate : 'none'}; border-radius: 8px; - - ${width} `; +const variantColorMap: Record = { + danger: 'error', +}; + export const NomineeMessage: FC = ({ - text, - icon, - onAction, - actionText, - ...props + label, + value, + leading, + trailing, + variant, + withBackground, + isHeader: withHeaderFont, }) => ( - - - {icon} - - {text} + + + {leading} + + {label} - + {trailing} + ); + +NomineeMessage.defaultProps = { + value: undefined, + leading: undefined, + trailing: undefined, + variant: undefined, + withBackground: undefined, + isHeader: undefined, +}; diff --git a/packages/ui/src/components/molecules/DetailsCard.tsx b/packages/ui/src/components/molecules/DetailsCard.tsx new file mode 100644 index 000000000..1ada9e0b3 --- /dev/null +++ b/packages/ui/src/components/molecules/DetailsCard.tsx @@ -0,0 +1,137 @@ +import React, { createElement, FC, ReactNode } from 'react'; +import styled, { useTheme } from 'styled-components'; + +import { SvgProps } from '../../assets'; +import { Flex, NomineeMessage, Divider, Typography } from '../atoms'; +import { utils, UtilsProps } from '../utils'; + +export type BackgroundType = 'gold' | 'silver' | 'default'; + +export interface DetailField { + icon: FC; + label: string; + value?: string; + trailing?: ReactNode; + isDanger?: boolean; +} + +export interface DetailsCardProps extends UtilsProps { + headerText: string; + headerLeading?: ReactNode; + headerTrailing?: ReactNode; + fields?: DetailField[]; + footer?: DetailField; + text?: string; + $backgroundType?: BackgroundType; +} + +const backgroundMap: Record = { + gold: 'goldenhint', + silver: 'silverhint', + default: 'separatorSecondary', +}; + +const StyledBackground = styled.div>` + display: flex; + flex-direction: column; + background: ${({ theme, $backgroundType }) => + theme.palette.background[ + backgroundMap[$backgroundType ?? 'default'] ?? backgroundMap.default + ]}; + border-radius: 8px; + padding: 8px 8px; + ${utils}; +`; + +interface FieldDisplayProps extends DetailField { + $withBackground?: boolean; +} + +const FieldDisplay: FC = ({ + label, + icon, + value, + isDanger, + trailing, + $withBackground, +}) => { + const theme = useTheme(); + return ( + + ); +}; + +FieldDisplay.defaultProps = { + value: undefined, + trailing: undefined, + isDanger: undefined, + $withBackground: undefined, +}; + +export const DetailsCard: FC = ({ + headerLeading, + headerText, + headerTrailing, + fields, + footer, + text, + ...rest +}) => { + const theme = useTheme(); + + return ( + + + {(fields || footer || text) && ( + + + {fields && ( + + {fields.map(field => ( + + ))} + + )} + {footer && ( + + + + )} + {text && ( + + {text} + + )} + + )} + + ); +}; + +DetailsCard.defaultProps = { + headerLeading: undefined, + headerTrailing: undefined, + fields: undefined, + footer: undefined, + text: undefined, + $backgroundType: undefined, +}; diff --git a/packages/ui/src/components/molecules/Prefabs/EditButton.tsx b/packages/ui/src/components/molecules/Prefabs/EditButton.tsx new file mode 100644 index 000000000..8d09c0f39 --- /dev/null +++ b/packages/ui/src/components/molecules/Prefabs/EditButton.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +import { Button, Typography } from '../../atoms'; + +export interface EditButtonProps { + text: string; + onClick?: () => void; +} + +export const EditButton: React.FC = ({ text, onClick }) => ( + +); + +EditButton.defaultProps = { + onClick: undefined, +}; diff --git a/packages/ui/src/components/molecules/Prefabs/index.ts b/packages/ui/src/components/molecules/Prefabs/index.ts index ef84059db..7cba2bd4b 100644 --- a/packages/ui/src/components/molecules/Prefabs/index.ts +++ b/packages/ui/src/components/molecules/Prefabs/index.ts @@ -2,3 +2,4 @@ export * from './BackButton'; export * from './EmailDisplay'; export * from './HelpButton'; export * from './QuestionMarkButton'; +export * from './EditButton'; diff --git a/packages/ui/src/components/molecules/index.ts b/packages/ui/src/components/molecules/index.ts index d3123db77..e900c22c1 100644 --- a/packages/ui/src/components/molecules/index.ts +++ b/packages/ui/src/components/molecules/index.ts @@ -49,4 +49,5 @@ export * from './OTPInput'; export * from './OneInMany'; export * from './CouponInput'; export * from './PlanCard'; +export * from './DetailsCard'; export * from './Payment'; diff --git a/packages/ui/src/stories/atoms/NomineeMessage.stories.tsx b/packages/ui/src/stories/atoms/NomineeMessage.stories.tsx index 4922f2cb6..5301c6d87 100644 --- a/packages/ui/src/stories/atoms/NomineeMessage.stories.tsx +++ b/packages/ui/src/stories/atoms/NomineeMessage.stories.tsx @@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import React from 'react'; import { EncryptedMessageIcon, MessageIcon } from '../../assets'; -import { NomineeMessage } from '../../components'; +import { EditButton, NomineeMessage } from '../../components'; const meta: Meta = { component: NomineeMessage, @@ -15,18 +15,18 @@ type Story = StoryObj; export const EncryptedMessage: Story = { args: { - text: 'Encrypted Message', - icon: , - actionText: 'Edit', - onAction: () => console.log('edit clicked'), + label: 'Encrypted Message', + leading: , + trailing: , + withBackground: true, }, }; export const ExecutorMessage: Story = { args: { - text: 'Executor Message', - icon: , - actionText: 'Edit', - onAction: () => console.log('edit clicked'), + label: 'Executor Message', + leading: , + trailing: , + withBackground: true, }, }; diff --git a/packages/ui/src/stories/molecules/DetailsCard.stories.tsx b/packages/ui/src/stories/molecules/DetailsCard.stories.tsx new file mode 100644 index 000000000..be365938b --- /dev/null +++ b/packages/ui/src/stories/molecules/DetailsCard.stories.tsx @@ -0,0 +1,195 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import React from 'react'; +import { + ClockIcon, + EmailIconSmall, + EncryptedMessageIcon, + UserIcon, + WalletIcon, +} from '../../assets'; +import { + DetailsCard, + EditButton, + svgGradients, + Typography, +} from '../../components'; + +const meta: Meta = { + component: DetailsCard, + tags: ['autodocs'], + parameters: { actions: { argTypesRegex: null } }, +}; + +export default meta; + +type Story = StoryObj; + +const editButton = ( + alert('Edit Clicked')} /> +); + +const goldWalletIcon = ; + +export const Default: Story = { + args: { + headerLeading: goldWalletIcon, + headerText: 'MyFunnyWallet', + headerTrailing: editButton, + $backgroundType: 'gold', + }, +}; + +export const OwnerDetails: Story = { + args: { + headerText: 'Owner Details', + headerTrailing: editButton, + fields: [ + { + label: 'User Name', + icon: UserIcon, + value: 'Alfred Bellows', + }, + { + label: 'Primary Email', + icon: EmailIconSmall, + value: 'doc.bellows@yahoo.com', + }, + { + label: 'Secondary Email', + icon: EmailIconSmall, + value: 'alfred@psych.com', + }, + { + label: 'Reminder Period', + icon: ClockIcon, + value: 'Every 3 Months', + trailing: editButton, + }, + ], + }, +}; + +export const WalletDetail: Story = { + args: { + headerLeading: goldWalletIcon, + headerText: 'MyNoNameWallet', + $backgroundType: 'gold', + headerTrailing: ( + + GOLD + + ), + fields: [ + { + label: 'Created on', + icon: ClockIcon, + value: '01 July 2024', + }, + { + label: 'Expiring on', + icon: ClockIcon, + value: '30 June 2024', + isDanger: true, + }, + { + label: 'Reminder Period', + icon: ClockIcon, + value: 'Every 3 Months', + trailing: editButton, + }, + ], + }, +}; + +export const WalletDetailSilver: Story = { + args: { + headerLeading: goldWalletIcon, + headerText: 'MyNoNameWallet', + $backgroundType: 'silver', + headerTrailing: ( + + SILVER + + ), + fields: [ + { + label: 'Created on', + icon: ClockIcon, + value: '01 July 2024', + }, + { + label: 'Expiring on', + icon: ClockIcon, + value: '30 June 2024', + isDanger: true, + }, + { + label: 'Reminder Period', + icon: ClockIcon, + value: 'Every 3 Months', + trailing: editButton, + }, + ], + }, +}; + +export const Nominee: Story = { + args: { + headerText: 'Nominee #1', + headerTrailing: editButton, + fields: [ + { + label: 'Nominee Name', + icon: UserIcon, + value: 'Alfred Bellows', + }, + { + label: 'Primary Email', + icon: EmailIconSmall, + value: 'doc.bellows@yahoo.com', + }, + { + label: 'Secondary Email', + icon: EmailIconSmall, + value: 'alfred@psych.com', + }, + ], + }, +}; + +export const NomineeWithEncryptedMessage: Story = { + args: { + headerText: 'Nominee #1', + headerTrailing: editButton, + fields: [ + { + label: 'Nominee Name', + icon: UserIcon, + value: 'Alfred Bellows', + }, + { + label: 'Primary Email', + icon: EmailIconSmall, + value: 'doc.bellows@yahoo.com', + }, + { + label: 'Secondary Email', + icon: EmailIconSmall, + value: 'alfred@psych.com', + }, + ], + footer: { + label: 'Encrypted Message', + icon: EncryptedMessageIcon, + trailing: editButton, + }, + }, +}; + +export const CardLocation: Story = { + args: { + headerText: 'Card Location', + headerTrailing: editButton, + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam eget dapibus est. Mauris varius sapien a diam elementum posuere. Maecenas aliquam nec justo a dictum. Aliquam eu condimentum mi, eu vulputate ipsum. Proin vel semper nisl. Donec ultricies consectetur dapibus. Donec suscipit, mi sed tristique feugiat, urna ipsum viverra risus, vitae commodo tortor est interdum ligula. Fusce tellus mi, malesuada tristique mauris a, pulvinar varius metus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec in nulla sit amet ex cursus dictum. Nam felis odio, egestas sed porttitor eu, consequat eget dolor. Phasellus luctus, arcu non auctor euismod, lectus quam tempus lacus, in sollicitudin elit risus ut ex.', + }, +}; diff --git a/packages/ui/src/themes/color.styled.ts b/packages/ui/src/themes/color.styled.ts index 02ff7df88..36de0b8c5 100644 --- a/packages/ui/src/themes/color.styled.ts +++ b/packages/ui/src/themes/color.styled.ts @@ -17,6 +17,10 @@ export const colors = { silver: 'linear-gradient(180deg, #A2ADB3 -2.08%, #F3F1F2 34.27%, #BCC3C9 66.28%, #DCDFE4 102.08%)', stripe: 'linear-gradient(90deg, #211C18 1.69%, #242018 100%)', + goldenhint: + 'linear-gradient(263deg, rgba(139, 100, 41, 0.14) 5.24%, rgba(38, 34, 31, 0.00) 55.22%), #272320', + silverhint: + 'linear-gradient(263deg, rgba(194, 194, 194, 0.14) 5.24%, rgba(38, 34, 31, 0.00) 55.22%), #272320', conicGradient: { default: 'conic-gradient(from 0deg, transparent,90deg, transparent, 90deg, #E9B873 ,180deg, #FEDD8F, 270deg, #B78D51, 360deg ,transparent, 360deg, transparent)', diff --git a/packages/ui/src/themes/theme.styled.ts b/packages/ui/src/themes/theme.styled.ts index 72fa8d237..556173246 100644 --- a/packages/ui/src/themes/theme.styled.ts +++ b/packages/ui/src/themes/theme.styled.ts @@ -83,6 +83,8 @@ export const theme = { disabled: colors.disabled.background, bar: colors.background.bar, gold: colors.gradients.golden, + goldenhint: colors.gradients.goldenhint, + silverhint: colors.gradients.silverhint, plan: colors.gradients.plan, golden: colors.background.gold, success: colors.success.main, @@ -104,6 +106,7 @@ export const theme = { breadcrumbSeparator: colors.background.breadcrumbSeparator, batchTransactionBody: colors.background.batchTransactionBody, slateLight: colors.background.slateLight, + slate: colors.background.slate, filterItem: colors.background.filterItem, calendar: colors.background.calendar, calendarHeader: colors.background.calendarHeader, From 9c1cf9bdaed6679db439c9a28ffed399b4f39dcc Mon Sep 17 00:00:00 2001 From: AryanCypherock Date: Fri, 30 Aug 2024 13:07:14 +0530 Subject: [PATCH 26/96] chore: updated review changes --- packages/cysync-core/src/actions/dialog/index.ts | 4 ++-- .../EditEncryptedMessage/Dialogs/Decryption.tsx | 13 ++++++------- .../EditEncryptedMessage/Dialogs/Encryption.tsx | 13 ++++++------- .../cysync-core/src/dialogs/Inheritance/index.ts | 1 + packages/cysync-core/src/dialogs/index.tsx | 2 +- 5 files changed, 16 insertions(+), 17 deletions(-) diff --git a/packages/cysync-core/src/actions/dialog/index.ts b/packages/cysync-core/src/actions/dialog/index.ts index 845b55d98..5c8671ae5 100644 --- a/packages/cysync-core/src/actions/dialog/index.ts +++ b/packages/cysync-core/src/actions/dialog/index.ts @@ -101,8 +101,8 @@ export const openInheritanceEditExecutorMessageDialog = () => export const openInheritanceEditReminderTimeDialog = () => openDialog({ name: 'inheritanceEditReminderTime', data: undefined }); -export const openEditUserDetailsDialog = () => +export const openInheritanceEditUserDetailsDialog = () => openDialog({ name: 'inheritanceEditUserDetails', data: undefined }); -export const openEditEncryptedMessageDialog = () => +export const openInheritanceEditEncryptedMessageDialog = () => openDialog({ name: 'inheritanceEditEncryptedMessage', data: undefined }); diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/Decryption.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/Decryption.tsx index 2c2ccfa01..f05731b69 100644 --- a/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/Decryption.tsx +++ b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/Decryption.tsx @@ -9,6 +9,7 @@ import { tapAnyCardDeviceAnimation2DVideo, Throbber, Typography, + Video, } from '@cypherock/cysync-ui'; import React, { useEffect } from 'react'; @@ -17,7 +18,6 @@ import { selectLanguage, useAppSelector } from '~/store'; import { useInheritanceEditEncryptedMessageDialog } from '../context'; import { Layout } from '../Layout'; import { LoaderDialog } from '~/components'; -import ReactPlayer from 'react-player'; const checkIconComponent = ; const throbberComponent = ; @@ -78,13 +78,12 @@ export const Decryption = () => { return ( - {strings.title} diff --git a/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/Encryption.tsx b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/Encryption.tsx index 235820fbb..40d5ee234 100644 --- a/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/Encryption.tsx +++ b/packages/cysync-core/src/dialogs/Inheritance/EditEncryptedMessage/Dialogs/Encryption.tsx @@ -10,6 +10,7 @@ import { tapAnyCardDeviceAnimation2DVideo, Throbber, Typography, + Video, } from '@cypherock/cysync-ui'; import React, { useEffect } from 'react'; @@ -18,7 +19,6 @@ import { selectLanguage, useAppSelector } from '~/store'; import { useInheritanceEditEncryptedMessageDialog } from '../context'; import { Layout } from '../Layout'; import { LoaderDialog } from '~/components'; -import ReactPlayer from 'react-player'; const checkIconComponent = ; const throbberComponent = ; @@ -79,13 +79,12 @@ export const Encryption = () => { return ( - diff --git a/packages/cysync-core/src/dialogs/Inheritance/index.ts b/packages/cysync-core/src/dialogs/Inheritance/index.ts index 036aae895..c5d72fc91 100644 --- a/packages/cysync-core/src/dialogs/Inheritance/index.ts +++ b/packages/cysync-core/src/dialogs/Inheritance/index.ts @@ -4,3 +4,4 @@ export * from './SilverPlanPurchase'; export * from './EditExecutorMessage'; export * from './EditReminderTime'; export * from './EditUserDetails'; +export * from './EditEncryptedMessage'; diff --git a/packages/cysync-core/src/dialogs/index.tsx b/packages/cysync-core/src/dialogs/index.tsx index 51e274fe0..73c0211c5 100644 --- a/packages/cysync-core/src/dialogs/index.tsx +++ b/packages/cysync-core/src/dialogs/index.tsx @@ -26,6 +26,7 @@ import { InheritancePlanLoginDialog, InheritanceSyncPlansDialog, InheritanceSilverPlanPurchaseDialog, + InheritanceEditEncryptedMessageDialog, } from './Inheritance'; import { ChangePasswordDialog, @@ -38,7 +39,6 @@ import { SignMessageDialog } from './SignMessage'; import { WalletActionsDialogBox } from './WalletActions'; import { WalletConnectDialog } from './WalletConnect'; import { WalletSyncError } from './WalletSyncError'; -import { InheritanceEditEncryptedMessageDialog } from './Inheritance/EditEncryptedMessage'; export const dialogs: Record = { walletSyncError: WalletSyncError, From c41c3b6ed8621b75b21154f728f362e9e344add4 Mon Sep 17 00:00:00 2001 From: AryanCypherock Date: Fri, 30 Aug 2024 17:35:17 +0530 Subject: [PATCH 27/96] feat: inheritance home page and choose plan ui (#529) * feat: inheritance page layout implemented * fix: passing action button text as a prop * feat: inheritance homepage implementation * fix: added sidebar chip and fixed lang string errors * fix: updated the page layout and added new pages * fix: icon fixed * chore: updated lang strings across all languages * fix: removed duplicate identifier from lang strings * feat: choose plan screen added * chore: review changes and lang strings updated * fix: removed action button from setup page --- .../src/i18n/lang/ar-AE.json | 55 ++++++++- .../src/i18n/lang/de-DE.json | 55 ++++++++- .../src/i18n/lang/en.json | 55 ++++++++- .../src/i18n/lang/id-ID.json | 55 ++++++++- .../src/i18n/lang/zh-CN.json | 55 ++++++++- .../cysync-core-constants/src/i18n/types.ts | 46 +++++++- .../src/components/InheritancePageLayout.tsx | 60 +++------- .../cysync-core/src/components/SideBar.tsx | 13 ++- .../dialogs/Inheritance/PinRecovery/index.tsx | 2 +- .../pages/MainApp/Inheritance/ChoosePlan.tsx | 73 ++++++++++++ .../pages/MainApp/Inheritance/Homepage.tsx | 104 ++++++++++++++++++ .../pages/MainApp/Inheritance/SetupPage.tsx | 99 +++++++++++++++++ .../src/pages/MainApp/Inheritance/index.tsx | 17 ++- packages/ui/icons/cypherock-cover-icon.svg | 96 ++++++++-------- .../images/common/cypherock-cover-icon.svg | 47 ++++++++ packages/ui/src/assets/images/common/index.ts | 6 + .../common/question-mark-ellipse-icon.svg | 21 ++++ .../ui/src/assets/images/common/sync-icon.svg | 10 ++ packages/ui/src/components/atoms/Chip.tsx | 16 ++- .../ui/src/components/molecules/PlanCard.tsx | 35 +----- .../stories/molecules/DetailsCard.stories.tsx | 1 + .../stories/molecules/PlanCard.stories.tsx | 6 - 22 files changed, 759 insertions(+), 168 deletions(-) create mode 100644 packages/cysync-core/src/pages/MainApp/Inheritance/ChoosePlan.tsx create mode 100644 packages/cysync-core/src/pages/MainApp/Inheritance/Homepage.tsx create mode 100644 packages/cysync-core/src/pages/MainApp/Inheritance/SetupPage.tsx create mode 100644 packages/ui/src/assets/images/common/cypherock-cover-icon.svg create mode 100644 packages/ui/src/assets/images/common/question-mark-ellipse-icon.svg create mode 100644 packages/ui/src/assets/images/common/sync-icon.svg diff --git a/packages/cysync-core-constants/src/i18n/lang/ar-AE.json b/packages/cysync-core-constants/src/i18n/lang/ar-AE.json index c9e7e0ae6..8d017c081 100644 --- a/packages/cysync-core-constants/src/i18n/lang/ar-AE.json +++ b/packages/cysync-core-constants/src/i18n/lang/ar-AE.json @@ -46,7 +46,10 @@ "submit": "إرسال", "showMore": "عرض المزيد", "resendOTP": "Resend OTP", - "next": "Next" + "next": "Next", + "setup": "Setup", + "sync": "Sync", + "learnMore": "Learn More" }, "dashboard": { "wallet": { @@ -633,6 +636,7 @@ "tutorial": "تعليمات", "settings": "الإعدادات", "help": "مساعدة", + "new": "New", "tooltip": { "walletDeleted": "${walletName} تم حذفه من Cypherock X1 Vault" } @@ -1727,11 +1731,54 @@ "inheritance": { "title": "Cypherock Cover", "choosePlan": { - "title": "Choose a plan" + "title": "Choose a plan", + "plans": { + "silver": { + "heading": "Silver", + "description": "Secure your wallet PIN with us and easily recover it if forgotten, ensuring continuous access to your crypto assets." + }, + "gold": { + "heading": "Gold", + "description": "Ensure your crypto assets are inherited by loved ones effortlessly, bypassing complex legal restrictions for smooth transfers." + }, + "buttonText": "SELECT", + "popularTagText": "MOST POPULAR", + "features": [ + "No KYC required", + "Multi-Chain", + "PIN Recovery", + "Estate Recovery" + ] + } + }, + "homePage": { + "headers": { + "owner": { + "title": "Owner", + "subtitle": "You have setup Cypherock cover for these wallets" + }, + "nominee": { + "title": "Nominee", + "subtitle": "Somebody made you a nominee of their assets" + } + }, + "setup": { + "setupCover": { + "title": "Setup Cypherock Cover", + "subTitle": "Protect your crypto assets and ensure smooth transfer to your beneficiaries in unforeseen circumstances." + }, + "syncFromMail": { + "title": "Sync from Email", + "subTitle": "If you're already part of a Cypherock Cover plan, either as the plan owner or a beneficiary." + }, + "learnMore": { + "title": "What is Cypherock Cover?", + "subTitle": "Learn how Cypherock Cover secures your crypto assets and ensures their seamless transfer to your loved ones." + } + } }, "buttons": { - "syncPlans": "Sync Plans", - "recoverPin": "Recover Pin" + "syncFromEmail": "Sync from Email" }, "termsOfService": { "title": "Terms of Service", diff --git a/packages/cysync-core-constants/src/i18n/lang/de-DE.json b/packages/cysync-core-constants/src/i18n/lang/de-DE.json index 71f4bbd44..1d6ed29ef 100644 --- a/packages/cysync-core-constants/src/i18n/lang/de-DE.json +++ b/packages/cysync-core-constants/src/i18n/lang/de-DE.json @@ -46,7 +46,10 @@ "submit": "Absenden", "showMore": "Mehr anzeigen", "resendOTP": "Resend OTP", - "next": "Next" + "next": "Next", + "setup": "Setup", + "sync": "Sync", + "learnMore": "Learn More" }, "dashboard": { "wallet": { @@ -635,6 +638,7 @@ "tutorial": "Anleitung", "settings": "Einstellungen", "help": "Help", + "new": "New", "tooltip": { "walletDeleted": "${walletName} wurde aus deinem Cypherock X1 Vault gelöscht" } @@ -1732,11 +1736,54 @@ "inheritance": { "title": "Cypherock Cover", "choosePlan": { - "title": "Choose a plan" + "title": "Choose a plan", + "plans": { + "silver": { + "heading": "Silver", + "description": "Secure your wallet PIN with us and easily recover it if forgotten, ensuring continuous access to your crypto assets." + }, + "gold": { + "heading": "Gold", + "description": "Ensure your crypto assets are inherited by loved ones effortlessly, bypassing complex legal restrictions for smooth transfers." + }, + "buttonText": "SELECT", + "popularTagText": "MOST POPULAR", + "features": [ + "No KYC required", + "Multi-Chain", + "PIN Recovery", + "Estate Recovery" + ] + } + }, + "homePage": { + "headers": { + "owner": { + "title": "Owner", + "subtitle": "You have setup Cypherock cover for these wallets" + }, + "nominee": { + "title": "Nominee", + "subtitle": "Somebody made you a nominee of their assets" + } + }, + "setup": { + "setupCover": { + "title": "Setup Cypherock Cover", + "subTitle": "Protect your crypto assets and ensure smooth transfer to your beneficiaries in unforeseen circumstances." + }, + "syncFromMail": { + "title": "Sync from Email", + "subTitle": "If you're already part of a Cypherock Cover plan, either as the plan owner or a beneficiary." + }, + "learnMore": { + "title": "What is Cypherock Cover?", + "subTitle": "Learn how Cypherock Cover secures your crypto assets and ensures their seamless transfer to your loved ones." + } + } }, "buttons": { - "syncPlans": "Sync Plans", - "recoverPin": "Recover Pin" + "syncFromEmail": "Sync from Email" }, "termsOfService": { "title": "Terms of Service", diff --git a/packages/cysync-core-constants/src/i18n/lang/en.json b/packages/cysync-core-constants/src/i18n/lang/en.json index 23ecc2883..98bf5b799 100644 --- a/packages/cysync-core-constants/src/i18n/lang/en.json +++ b/packages/cysync-core-constants/src/i18n/lang/en.json @@ -46,7 +46,10 @@ "submit": "Submit", "showMore": "Show more", "resendOTP": "Resend OTP", - "next": "Next" + "next": "Next", + "setup": "Setup", + "sync": "Sync", + "learnMore": "Learn More" }, "dashboard": { "wallet": { @@ -635,6 +638,7 @@ "tutorial": "Tutorial", "settings": "Settings", "help": "Help", + "new": "New", "tooltip": { "walletDeleted": "${walletName} has been deleted from your Cypherock X1 Vault" } @@ -1732,11 +1736,54 @@ "inheritance": { "title": "Cypherock Cover", "choosePlan": { - "title": "Choose a plan" + "title": "Choose a plan", + "plans": { + "silver": { + "heading": "Silver", + "description": "Secure your wallet PIN with us and easily recover it if forgotten, ensuring continuous access to your crypto assets." + }, + "gold": { + "heading": "Gold", + "description": "Ensure your crypto assets are inherited by loved ones effortlessly, bypassing complex legal restrictions for smooth transfers." + }, + "buttonText": "SELECT", + "popularTagText": "MOST POPULAR", + "features": [ + "No KYC required", + "Multi-Chain", + "PIN Recovery", + "Estate Recovery" + ] + } + }, + "homePage": { + "headers": { + "owner": { + "title": "Owner", + "subtitle": "You have setup Cypherock cover for these wallets" + }, + "nominee": { + "title": "Nominee", + "subtitle": "Somebody made you a nominee of their assets" + } + }, + "setup": { + "setupCover": { + "title": "Setup Cypherock Cover", + "subTitle": "Protect your crypto assets and ensure smooth transfer to your beneficiaries in unforeseen circumstances." + }, + "syncFromMail": { + "title": "Sync from Email", + "subTitle": "If you're already part of a Cypherock Cover plan, either as the plan owner or a beneficiary." + }, + "learnMore": { + "title": "What is Cypherock Cover?", + "subTitle": "Learn how Cypherock Cover secures your crypto assets and ensures their seamless transfer to your loved ones." + } + } }, "buttons": { - "syncPlans": "Sync Plans", - "recoverPin": "Recover Pin" + "syncFromEmail": "Sync from Email" }, "termsOfService": { "title": "Terms of Service", diff --git a/packages/cysync-core-constants/src/i18n/lang/id-ID.json b/packages/cysync-core-constants/src/i18n/lang/id-ID.json index 5591d32bc..9d0350c93 100644 --- a/packages/cysync-core-constants/src/i18n/lang/id-ID.json +++ b/packages/cysync-core-constants/src/i18n/lang/id-ID.json @@ -46,7 +46,10 @@ "submit": "Kirim", "showMore": "Tampilkan lebih banyak", "resendOTP": "Resend OTP", - "next": "Next" + "next": "Next", + "setup": "Setup", + "sync": "Sync", + "learnMore": "Learn More" }, "dashboard": { "wallet": { @@ -635,6 +638,7 @@ "tutorial": "Tutorial", "settings": "Pengaturan", "help": "Help", + "new": "New", "tooltip": { "walletDeleted": "${walletName} telah dihapus dari Cypherock X1 Vault Anda" } @@ -1732,11 +1736,54 @@ "inheritance": { "title": "Cypherock Cover", "choosePlan": { - "title": "Choose a plan" + "title": "Choose a plan", + "plans": { + "silver": { + "heading": "Silver", + "description": "Secure your wallet PIN with us and easily recover it if forgotten, ensuring continuous access to your crypto assets." + }, + "gold": { + "heading": "Gold", + "description": "Ensure your crypto assets are inherited by loved ones effortlessly, bypassing complex legal restrictions for smooth transfers." + }, + "buttonText": "SELECT", + "popularTagText": "MOST POPULAR", + "features": [ + "No KYC required", + "Multi-Chain", + "PIN Recovery", + "Estate Recovery" + ] + } + }, + "homePage": { + "headers": { + "owner": { + "title": "Owner", + "subtitle": "You have setup Cypherock cover for these wallets" + }, + "nominee": { + "title": "Nominee", + "subtitle": "Somebody made you a nominee of their assets" + } + }, + "setup": { + "setupCover": { + "title": "Setup Cypherock Cover", + "subTitle": "Protect your crypto assets and ensure smooth transfer to your beneficiaries in unforeseen circumstances." + }, + "syncFromMail": { + "title": "Sync from Email", + "subTitle": "If you're already part of a Cypherock Cover plan, either as the plan owner or a beneficiary." + }, + "learnMore": { + "title": "What is Cypherock Cover?", + "subTitle": "Learn how Cypherock Cover secures your crypto assets and ensures their seamless transfer to your loved ones." + } + } }, "buttons": { - "syncPlans": "Sync Plans", - "recoverPin": "Recover Pin" + "syncFromEmail": "Sync from Email" }, "termsOfService": { "title": "Terms of Service", diff --git a/packages/cysync-core-constants/src/i18n/lang/zh-CN.json b/packages/cysync-core-constants/src/i18n/lang/zh-CN.json index f13b376b5..4de46792c 100644 --- a/packages/cysync-core-constants/src/i18n/lang/zh-CN.json +++ b/packages/cysync-core-constants/src/i18n/lang/zh-CN.json @@ -46,7 +46,10 @@ "submit": "提交", "showMore": "显示更多", "resendOTP": "Resend OTP", - "next": "Next" + "next": "Next", + "setup": "Setup", + "sync": "Sync", + "learnMore": "Learn More" }, "dashboard": { "wallet": { @@ -633,6 +636,7 @@ "tutorial": "教程", "settings": "设置", "help": "Help", + "new": "New", "tooltip": { "walletDeleted": "${walletName} 已从您的 Cypherock X1 Vault 中删除" } @@ -1707,11 +1711,54 @@ "inheritance": { "title": "Cypherock Cover", "choosePlan": { - "title": "Choose a plan" + "title": "Choose a plan", + "plans": { + "silver": { + "heading": "Silver", + "description": "Secure your wallet PIN with us and easily recover it if forgotten, ensuring continuous access to your crypto assets." + }, + "gold": { + "heading": "Gold", + "description": "Ensure your crypto assets are inherited by loved ones effortlessly, bypassing complex legal restrictions for smooth transfers." + }, + "buttonText": "SELECT", + "popularTagText": "MOST POPULAR", + "features": [ + "No KYC required", + "Multi-Chain", + "PIN Recovery", + "Estate Recovery" + ] + } + }, + "homePage": { + "headers": { + "owner": { + "title": "Owner", + "subtitle": "You have setup Cypherock cover for these wallets" + }, + "nominee": { + "title": "Nominee", + "subtitle": "Somebody made you a nominee of their assets" + } + }, + "setup": { + "setupCover": { + "title": "Setup Cypherock Cover", + "subTitle": "Protect your crypto assets and ensure smooth transfer to your beneficiaries in unforeseen circumstances." + }, + "syncFromMail": { + "title": "Sync from Email", + "subTitle": "If you're already part of a Cypherock Cover plan, either as the plan owner or a beneficiary." + }, + "learnMore": { + "title": "What is Cypherock Cover?", + "subTitle": "Learn how Cypherock Cover secures your crypto assets and ensures their seamless transfer to your loved ones." + } + } }, "buttons": { - "syncPlans": "Sync Plans", - "recoverPin": "Recover Pin" + "syncFromEmail": "Sync from Email" }, "termsOfService": { "title": "Terms of Service", diff --git a/packages/cysync-core-constants/src/i18n/types.ts b/packages/cysync-core-constants/src/i18n/types.ts index c5eaa9a75..64754cfdc 100644 --- a/packages/cysync-core-constants/src/i18n/types.ts +++ b/packages/cysync-core-constants/src/i18n/types.ts @@ -87,6 +87,9 @@ interface LangButtons { showMore: string; resendOTP: string; next: string; + setup: string; + sync: string; + learnMore: string; } interface LangDashboard { @@ -507,6 +510,7 @@ interface LangSidebar { tutorial: string; settings: string; help: string; + new: string; tooltip: { walletDeleted: string }; } @@ -955,10 +959,48 @@ interface LangInheritance { title: string; choosePlan: { title: string; + plans: { + silver: { + heading: string; + description: string; + }; + gold: { + heading: string; + description: string; + }; + buttonText: string; + popularTagText: string; + features: string[]; + }; + }; + homePage: { + headers: { + owner: { + title: string; + subtitle: string; + }; + nominee: { + title: string; + subtitle: string; + }; + }; + setup: { + setupCover: { + title: string; + subTitle: string; + }; + syncFromMail: { + title: string; + subTitle: string; + }; + learnMore: { + title: string; + subTitle: string; + }; + }; }; buttons: { - syncPlans: string; - recoverPin: string; + syncFromEmail: string; }; termsOfService: { title: string; diff --git a/packages/cysync-core/src/components/InheritancePageLayout.tsx b/packages/cysync-core/src/components/InheritancePageLayout.tsx index 0e1957a36..57d6ddf01 100644 --- a/packages/cysync-core/src/components/InheritancePageLayout.tsx +++ b/packages/cysync-core/src/components/InheritancePageLayout.tsx @@ -1,33 +1,15 @@ -import { - ArrowBackGoldenIcon, - Breadcrumb, - BreadcrumbItem, - Button, - Container, - Flex, - Typography, -} from '@cypherock/cysync-ui'; +import { Button, Container, Flex, LangDisplay } from '@cypherock/cysync-ui'; import React, { FC, ReactNode } from 'react'; interface InheritancePageLayoutProps { children?: ReactNode; - headingOnly?: boolean; - breadcrumbs?: BreadcrumbItem[]; - onClick?: () => void; - lang: { - choosePlan: { - title: string; - }; - }; actionButtonText?: string; + onActionButtonClick?: () => void; } export const InheritancePageLayout: FC = ({ children, - breadcrumbs, - headingOnly, - lang, - onClick, + onActionButtonClick, actionButtonText, }) => ( = ({ m="20" $borderRadius={24} direction="column" - justify="space-between" shadow="popup" > - - {breadcrumbs ? ( - - ) : ( - - )} - - + {onActionButtonClick && actionButtonText && ( + + + + )} + {children} - + ); InheritancePageLayout.defaultProps = { children: undefined, - headingOnly: undefined, - breadcrumbs: undefined, - onClick: undefined, + onActionButtonClick: undefined, actionButtonText: undefined, }; diff --git a/packages/cysync-core/src/components/SideBar.tsx b/packages/cysync-core/src/components/SideBar.tsx index ced648d21..53f63236d 100644 --- a/packages/cysync-core/src/components/SideBar.tsx +++ b/packages/cysync-core/src/components/SideBar.tsx @@ -3,6 +3,7 @@ import { ArrowReceivedIcon, ArrowSentIcon, Button, + Chip, CypherockCoverIcon, DropDownItemProps, Flex, @@ -16,6 +17,7 @@ import { SupportIcon, Synchronizing, TutorialIcon, + Typography, WalletConnectWhiteIcon, WalletIcon, WalletInfoIcon, @@ -169,7 +171,16 @@ const SideBarComponent: FC<{ collapseWallets?: boolean }> = () => { + + {strings.new} + + + } onClick={() => navigate('inheritance')} /> diff --git a/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/index.tsx b/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/index.tsx index 9c8d38832..d496cc418 100644 --- a/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/index.tsx +++ b/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/index.tsx @@ -11,12 +11,12 @@ import { WithConnectedDevice, WithConnectedDeviceProps, } from '~/components'; +import { selectLanguage, useAppSelector } from '~/store'; import { InheritancePinRecoveryDialogProvider, useInheritancePinRecoveryDialog, } from './context'; -import { selectLanguage, useAppSelector } from '~/store'; const DeviceConnectionWrapper: React.FC<{ isDeviceRequired: boolean; diff --git a/packages/cysync-core/src/pages/MainApp/Inheritance/ChoosePlan.tsx b/packages/cysync-core/src/pages/MainApp/Inheritance/ChoosePlan.tsx new file mode 100644 index 000000000..acee3bb8f --- /dev/null +++ b/packages/cysync-core/src/pages/MainApp/Inheritance/ChoosePlan.tsx @@ -0,0 +1,73 @@ +import { + Typography, + Container, + Flex, + PlanCard, + Button, + ArrowBackGoldenIcon, +} from '@cypherock/cysync-ui'; +import React, { FC } from 'react'; + +import { InheritancePageLayout } from '~/components'; +import { selectLanguage, useAppSelector } from '~/store'; + +export const ChoosePlan: FC = () => { + const lang = useAppSelector(selectLanguage); + const strings = lang.strings.inheritance.choosePlan; + + const SilverPlanFeatures = strings.plans.features.map((cur, i) => ({ + text: cur, + available: i < 3, + })); + + const GoldPlanFeatures = strings.plans.features.map(cur => ({ + text: cur, + available: true, + })); + + return ( + + + + + +
+ + + Sync Icon + + + {lang.strings.inheritance.homePage.setup.syncFromMail.title} + + + { + lang.strings.inheritance.homePage.setup.syncFromMail + .subTitle + } + + + + + + + + + + + + question mark + + + {lang.strings.inheritance.homePage.setup.learnMore.title} + + + {lang.strings.inheritance.homePage.setup.learnMore.subTitle} + + + + + + + + + + ); +}; diff --git a/packages/cysync-core/src/pages/MainApp/Inheritance/index.tsx b/packages/cysync-core/src/pages/MainApp/Inheritance/index.tsx index 4d37e36f6..8b16be432 100644 --- a/packages/cysync-core/src/pages/MainApp/Inheritance/index.tsx +++ b/packages/cysync-core/src/pages/MainApp/Inheritance/index.tsx @@ -1,20 +1,25 @@ import React from 'react'; -import { InheritancePageLayout } from '~/components'; import { selectLanguage, useAppSelector } from '~/store'; +import { ChoosePlan } from './ChoosePlan'; +import { Homepage } from './Homepage'; +import { SetupPage } from './SetupPage'; + import { MainAppLayout } from '../Layout'; +const renderMap = { + setup: , + home: , + choosePlan: , +}; + export const Inheritance = () => { const lang = useAppSelector(selectLanguage); return ( - + {renderMap.choosePlan} ); }; diff --git a/packages/ui/icons/cypherock-cover-icon.svg b/packages/ui/icons/cypherock-cover-icon.svg index 805f439c1..343761f22 100644 --- a/packages/ui/icons/cypherock-cover-icon.svg +++ b/packages/ui/icons/cypherock-cover-icon.svg @@ -1,49 +1,47 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/ui/src/assets/images/common/cypherock-cover-icon.svg b/packages/ui/src/assets/images/common/cypherock-cover-icon.svg new file mode 100644 index 000000000..1c8e1c753 --- /dev/null +++ b/packages/ui/src/assets/images/common/cypherock-cover-icon.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/ui/src/assets/images/common/index.ts b/packages/ui/src/assets/images/common/index.ts index a3b676c21..156a8de71 100644 --- a/packages/ui/src/assets/images/common/index.ts +++ b/packages/ui/src/assets/images/common/index.ts @@ -59,6 +59,7 @@ import openExternalLink from './open-external-link.svg'; import qrImage from './qr.png'; import qrcodeIcon from './qrcode.svg'; import questionMarkGoldIcon from './question-gold.svg'; +import questionMarkEllipseIcon from './question-mark-ellipse-icon.svg'; import questionMarkIcon from './question-mark.svg'; import recoverWalletFromSeedphraseGraphics from './recover-wallet-from-seedphrase-graphics.png'; import recoverWalletIcon from './recover-wallet.png'; @@ -72,6 +73,7 @@ import sliderThumbIcon from './slider-thumb.svg'; import solanaIcon from './solana.svg'; import spinnerGoldIcon from './spinner-gold.svg'; import successIcon from './success.svg'; +import syncIcon from './sync-icon.svg'; import tetherIcon from './tether.svg'; import triangleInverseIcon from './triangle-inverse-icon.svg'; import triangleGreyIcon from './triangleGrey.svg'; @@ -85,6 +87,7 @@ import WalletHoverExpiredIcon from './wallet-hover-expired-icon.svg'; import WalletHoverSilverBgIcon from './wallet-silver-hover-bg.svg'; import walletIcon from './wallet.svg'; import warningIcon from './warning.svg'; +import cypherockCoverIcon from './cypherock-cover-icon.svg'; export { WalletDefaultExpiredIcon, @@ -174,4 +177,7 @@ export { oneInMany2Default, manyInManyBgImage, manyInManyHoverBgImage, + questionMarkEllipseIcon, + syncIcon, + cypherockCoverIcon, }; diff --git a/packages/ui/src/assets/images/common/question-mark-ellipse-icon.svg b/packages/ui/src/assets/images/common/question-mark-ellipse-icon.svg new file mode 100644 index 000000000..9e3f2261a --- /dev/null +++ b/packages/ui/src/assets/images/common/question-mark-ellipse-icon.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/ui/src/assets/images/common/sync-icon.svg b/packages/ui/src/assets/images/common/sync-icon.svg new file mode 100644 index 000000000..0404793be --- /dev/null +++ b/packages/ui/src/assets/images/common/sync-icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/ui/src/components/atoms/Chip.tsx b/packages/ui/src/components/atoms/Chip.tsx index 4f5397f02..cfe0923af 100644 --- a/packages/ui/src/components/atoms/Chip.tsx +++ b/packages/ui/src/components/atoms/Chip.tsx @@ -1,13 +1,20 @@ import React, { FC } from 'react'; import styled from 'styled-components'; -import { FontProps, SpacingProps, font, spacing } from '../utils'; +import { + FontProps, + SpacingProps, + font, + silverGradient, + spacing, +} from '../utils'; interface ChipProps extends SpacingProps, FontProps { children: React.ReactNode; + $gradient?: boolean; } -const ChipStyle = styled.div` +const ChipStyle = styled.div` display: flex; padding: 1px 6px; gap: var(--0-px, 0px); @@ -16,8 +23,13 @@ const ChipStyle = styled.div` border: 1px solid ${({ theme }) => theme.palette.border.muted}; ${spacing} ${font} + ${({ $gradient }) => ($gradient ? silverGradient('background') : '')} `; export const Chip: FC = ({ children, ...props }) => ( {children} ); + +Chip.defaultProps = { + $gradient: undefined, +}; diff --git a/packages/ui/src/components/molecules/PlanCard.tsx b/packages/ui/src/components/molecules/PlanCard.tsx index b73f81982..75d0f0308 100644 --- a/packages/ui/src/components/molecules/PlanCard.tsx +++ b/packages/ui/src/components/molecules/PlanCard.tsx @@ -11,10 +11,7 @@ export type PlanCardType = 'silver' | 'gold'; export interface PlanCardProps extends WidthProps { type: PlanCardType; heading: string; - tagline: string; description: string; - price: string; - duration: string; buttonText: string; popularTagText?: string; features: { text: string; available: boolean }[]; @@ -55,11 +52,8 @@ const PopularTag = styled.div` export const PlanCard: React.FC = ({ type, heading, - tagline, description, - price, features, - duration, buttonText, popularTagText, onClick, @@ -92,34 +86,7 @@ export const PlanCard: React.FC = ({ > {heading} - - {tagline} - - - - {price} - - - - {duration} - - - = ({ > {description} - + {features.map((feature, index) => ( diff --git a/packages/ui/src/stories/molecules/DetailsCard.stories.tsx b/packages/ui/src/stories/molecules/DetailsCard.stories.tsx index be365938b..3b3ecf553 100644 --- a/packages/ui/src/stories/molecules/DetailsCard.stories.tsx +++ b/packages/ui/src/stories/molecules/DetailsCard.stories.tsx @@ -1,5 +1,6 @@ import type { Meta, StoryObj } from '@storybook/react'; import React from 'react'; + import { ClockIcon, EmailIconSmall, diff --git a/packages/ui/src/stories/molecules/PlanCard.stories.tsx b/packages/ui/src/stories/molecules/PlanCard.stories.tsx index a5bab824b..ec43b9270 100644 --- a/packages/ui/src/stories/molecules/PlanCard.stories.tsx +++ b/packages/ui/src/stories/molecules/PlanCard.stories.tsx @@ -15,17 +15,14 @@ export const SilverPlan: Story = { args: { type: 'silver', heading: 'Silver', - tagline: 'PIN Recovery', description: 'Secure your wallet PIN with us and easily recover it if forgotten, ensuring continuous access to your crypto assets.', - price: '$25', features: [ { text: 'No KYC required', available: true }, { text: 'Multi-Chain', available: true }, { text: 'PIN Recovery', available: true }, { text: 'Estate Recovery', available: false }, ], - duration: '/Wallet/Year', buttonText: 'SELECT', }, }; @@ -34,17 +31,14 @@ export const GoldPlan: Story = { args: { type: 'gold', heading: 'Gold', - tagline: 'Non-Custodial Estate Recovery', description: 'Ensure your crypto assets are inherited by loved ones effortlessly, bypassing complex legal restrictions for smooth transfers.', - price: '$100', features: [ { text: 'No KYC required', available: true }, { text: 'Multi-Chain', available: true }, { text: 'PIN Recovery', available: true }, { text: 'Estate Recovery', available: true }, ], - duration: '/Wallet/Year', buttonText: 'SELECT', popularTagText: 'MOST POPULAR', }, From 93589cdc49efa5ee99f5eb953ec7191e08b30059 Mon Sep 17 00:00:00 2001 From: Irshad Ansari <40335357+irshadjsr21@users.noreply.github.com> Date: Fri, 30 Aug 2024 19:07:49 +0530 Subject: [PATCH 28/96] feat: implemented inheritance pages (#530) --- .../cysync-core-constants/src/constants.ts | 1 + .../src/components/InheritancePageLayout.tsx | 39 --------------- packages/cysync-core/src/components/index.ts | 1 - .../cysync-core/src/constants/routes/index.ts | 11 +++-- .../src/constants/routes/inheritance.ts | 13 +++++ packages/cysync-core/src/context/sidebar.tsx | 10 +++- .../pages/MainApp/Inheritance/ChoosePlan.tsx | 29 +++++++---- .../{Homepage.tsx => Home/PlanList.tsx} | 19 ++++--- .../{SetupPage.tsx => Home/Setup.tsx} | 42 +++++++++++++--- .../pages/MainApp/Inheritance/Home/index.tsx | 12 +++++ .../src/pages/MainApp/Inheritance/Layout.tsx | 49 +++++++++++++++++++ .../src/pages/MainApp/Inheritance/index.tsx | 27 +--------- packages/desktop-ui/src/Router.tsx | 6 ++- packages/ui/src/assets/images/common/index.ts | 2 +- 14 files changed, 164 insertions(+), 97 deletions(-) delete mode 100644 packages/cysync-core/src/components/InheritancePageLayout.tsx create mode 100644 packages/cysync-core/src/constants/routes/inheritance.ts rename packages/cysync-core/src/pages/MainApp/Inheritance/{Homepage.tsx => Home/PlanList.tsx} (87%) rename packages/cysync-core/src/pages/MainApp/Inheritance/{SetupPage.tsx => Home/Setup.tsx} (73%) create mode 100644 packages/cysync-core/src/pages/MainApp/Inheritance/Home/index.tsx create mode 100644 packages/cysync-core/src/pages/MainApp/Inheritance/Layout.tsx diff --git a/packages/cysync-core-constants/src/constants.ts b/packages/cysync-core-constants/src/constants.ts index 5b3db9b23..28d65b115 100644 --- a/packages/cysync-core-constants/src/constants.ts +++ b/packages/cysync-core-constants/src/constants.ts @@ -9,5 +9,6 @@ export const constants = { 'https://youtube.com/playlist?list=PL0db5IfQ4iyo7GxBtFVBxxvanAngjdjLn&feature=shared', inheritance: { silverPlanPurchaseTutorialLink: 'https://youtu.be/bRmNxrOf3VM', + learnMore: 'https://www.cypherock.com', }, }; diff --git a/packages/cysync-core/src/components/InheritancePageLayout.tsx b/packages/cysync-core/src/components/InheritancePageLayout.tsx deleted file mode 100644 index 57d6ddf01..000000000 --- a/packages/cysync-core/src/components/InheritancePageLayout.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { Button, Container, Flex, LangDisplay } from '@cypherock/cysync-ui'; -import React, { FC, ReactNode } from 'react'; - -interface InheritancePageLayoutProps { - children?: ReactNode; - actionButtonText?: string; - onActionButtonClick?: () => void; -} - -export const InheritancePageLayout: FC = ({ - children, - onActionButtonClick, - actionButtonText, -}) => ( - - {onActionButtonClick && actionButtonText && ( - - - - )} - - {children} - - -); - -InheritancePageLayout.defaultProps = { - children: undefined, - onActionButtonClick: undefined, - actionButtonText: undefined, -}; diff --git a/packages/cysync-core/src/components/index.ts b/packages/cysync-core/src/components/index.ts index c12c4dddf..a0b5c4b84 100644 --- a/packages/cysync-core/src/components/index.ts +++ b/packages/cysync-core/src/components/index.ts @@ -19,4 +19,3 @@ export * from './DeviceAuthenticating'; export * from './BetaNotificationBar'; export * from './LangDropdown'; export * from './DeleteAccount'; -export * from './InheritancePageLayout'; diff --git a/packages/cysync-core/src/constants/routes/index.ts b/packages/cysync-core/src/constants/routes/index.ts index dd165271e..692f80715 100644 --- a/packages/cysync-core/src/constants/routes/index.ts +++ b/packages/cysync-core/src/constants/routes/index.ts @@ -1,3 +1,4 @@ +import { inheritance, InheritanceRouteName } from './inheritance'; import { OnboardingRouteName, onboarding } from './onboarding'; const rootRoutes = { @@ -13,10 +14,6 @@ const rootRoutes = { name: 'wallet', path: '/wallet', }, - inheritance: { - name: 'inheritance', - path: '/inheritance', - }, asset: { name: 'asset', path: '/asset', @@ -37,12 +34,16 @@ const rootRoutes = { export const routes = { onboarding, + inheritance, ...rootRoutes, }; type RootRouteName = (typeof rootRoutes)[keyof typeof rootRoutes]['name']; -export type RouteName = RootRouteName | OnboardingRouteName; +export type RouteName = + | RootRouteName + | OnboardingRouteName + | InheritanceRouteName; export interface IRoute { name: RouteName; diff --git a/packages/cysync-core/src/constants/routes/inheritance.ts b/packages/cysync-core/src/constants/routes/inheritance.ts new file mode 100644 index 000000000..18421c660 --- /dev/null +++ b/packages/cysync-core/src/constants/routes/inheritance.ts @@ -0,0 +1,13 @@ +export const inheritance = { + home: { + name: 'inheritance', + path: '/inheritance', + }, + choosePlan: { + name: 'inheritance-choose-plan', + path: '/inheritance/plans', + }, +} as const; + +export type InheritanceRouteName = + (typeof inheritance)[keyof typeof inheritance]['name']; diff --git a/packages/cysync-core/src/context/sidebar.tsx b/packages/cysync-core/src/context/sidebar.tsx index 6d6fa6158..5a5d5a5ce 100644 --- a/packages/cysync-core/src/context/sidebar.tsx +++ b/packages/cysync-core/src/context/sidebar.tsx @@ -83,6 +83,10 @@ export const SidebarProvider: React.FC = ({ dispatch(openContactSupportDialog()); return; } + if (page === 'inheritance') { + navigateTo(routes[page].home.path); + return; + } navigateTo(routes[page].path); }; @@ -94,9 +98,13 @@ export const SidebarProvider: React.FC = ({ const getState = (page: Page): State => { if (page === 'help') return State.normal; if (page === 'tutorial') return State.normal; - if (location.pathname === routes[page].path) return State.selected; + const path = + page === 'inheritance' ? routes[page].home.path : routes[page].path; + + if (location.pathname.startsWith(path)) return State.selected; return State.normal; }; + const getWalletState = (id: string | undefined): State => { if ( (id && diff --git a/packages/cysync-core/src/pages/MainApp/Inheritance/ChoosePlan.tsx b/packages/cysync-core/src/pages/MainApp/Inheritance/ChoosePlan.tsx index acee3bb8f..fc3c1aea2 100644 --- a/packages/cysync-core/src/pages/MainApp/Inheritance/ChoosePlan.tsx +++ b/packages/cysync-core/src/pages/MainApp/Inheritance/ChoosePlan.tsx @@ -6,14 +6,21 @@ import { Button, ArrowBackGoldenIcon, } from '@cypherock/cysync-ui'; -import React, { FC } from 'react'; +import React, { FC, useCallback } from 'react'; +import { useDispatch } from 'react-redux'; -import { InheritancePageLayout } from '~/components'; +import { openInheritanceSilverPlanPurchaseDialog } from '~/actions'; +import { routes } from '~/constants'; +import { useNavigateTo } from '~/hooks'; import { selectLanguage, useAppSelector } from '~/store'; -export const ChoosePlan: FC = () => { +import { InheritancePageLayout } from './Layout'; + +export const InheritanceChoosePlan: FC = () => { const lang = useAppSelector(selectLanguage); const strings = lang.strings.inheritance.choosePlan; + const navigateTo = useNavigateTo(); + const dispatch = useDispatch(); const SilverPlanFeatures = strings.plans.features.map((cur, i) => ({ text: cur, @@ -25,6 +32,14 @@ export const ChoosePlan: FC = () => { available: true, })); + const onBack = useCallback(() => { + navigateTo(routes.inheritance.home.path); + }, [navigateTo]); + + const openSilverPlanSetup = useCallback(() => { + dispatch(openInheritanceSilverPlanPurchaseDialog()); + }, [dispatch]); + return ( @@ -32,9 +47,7 @@ export const ChoosePlan: FC = () => { + @@ -65,7 +89,9 @@ export const SetupPage: FC = () => { - + @@ -86,7 +112,7 @@ export const SetupPage: FC = () => { {lang.strings.inheritance.homePage.setup.learnMore.subTitle} - @@ -94,6 +120,6 @@ export const SetupPage: FC = () => { - + ); }; diff --git a/packages/cysync-core/src/pages/MainApp/Inheritance/Home/index.tsx b/packages/cysync-core/src/pages/MainApp/Inheritance/Home/index.tsx new file mode 100644 index 000000000..5f6333543 --- /dev/null +++ b/packages/cysync-core/src/pages/MainApp/Inheritance/Home/index.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +import { InheritancePlanList } from './PlanList'; +import { InheritanceSetup } from './Setup'; + +export const InheritanceHome = () => { + if (true) { + return ; + } + + return ; +}; diff --git a/packages/cysync-core/src/pages/MainApp/Inheritance/Layout.tsx b/packages/cysync-core/src/pages/MainApp/Inheritance/Layout.tsx new file mode 100644 index 000000000..1b8017863 --- /dev/null +++ b/packages/cysync-core/src/pages/MainApp/Inheritance/Layout.tsx @@ -0,0 +1,49 @@ +import { Button, Container, Flex, LangDisplay } from '@cypherock/cysync-ui'; +import React, { FC, ReactNode } from 'react'; + +import { useAppSelector, selectLanguage } from '~/store'; + +import { MainAppLayout } from '../Layout'; + +interface InheritancePageLayoutProps { + children?: ReactNode; + actionButtonText?: string; + onActionButtonClick?: () => void; +} + +export const InheritancePageLayout: FC = ({ + children, + onActionButtonClick, + actionButtonText, +}) => { + const lang = useAppSelector(selectLanguage); + + return ( + + + {onActionButtonClick && actionButtonText && ( + + + + )} + + {children} + + + + ); +}; + +InheritancePageLayout.defaultProps = { + children: undefined, + onActionButtonClick: undefined, + actionButtonText: undefined, +}; diff --git a/packages/cysync-core/src/pages/MainApp/Inheritance/index.tsx b/packages/cysync-core/src/pages/MainApp/Inheritance/index.tsx index 8b16be432..35217bcd1 100644 --- a/packages/cysync-core/src/pages/MainApp/Inheritance/index.tsx +++ b/packages/cysync-core/src/pages/MainApp/Inheritance/index.tsx @@ -1,25 +1,2 @@ -import React from 'react'; - -import { selectLanguage, useAppSelector } from '~/store'; - -import { ChoosePlan } from './ChoosePlan'; -import { Homepage } from './Homepage'; -import { SetupPage } from './SetupPage'; - -import { MainAppLayout } from '../Layout'; - -const renderMap = { - setup: , - home: , - choosePlan: , -}; - -export const Inheritance = () => { - const lang = useAppSelector(selectLanguage); - - return ( - - {renderMap.choosePlan} - - ); -}; +export * from './ChoosePlan'; +export * from './Home'; diff --git a/packages/desktop-ui/src/Router.tsx b/packages/desktop-ui/src/Router.tsx index 2a1ee1d35..96c0d8ceb 100644 --- a/packages/desktop-ui/src/Router.tsx +++ b/packages/desktop-ui/src/Router.tsx @@ -23,7 +23,8 @@ import { AppUpdate, AssetPage, AccountPage, - Inheritance, + InheritanceHome, + InheritanceChoosePlan, } from '@cypherock/cysync-core'; import React, { memo, ReactNode } from 'react'; import { HashRouter as Router, Route, Routes } from 'react-router-dom'; @@ -45,11 +46,12 @@ const components: Record = { 'onboarding-device-update': , portfolio: , wallet: , - inheritance: , asset: , account: , history: , settings: , + inheritance: , + 'inheritance-choose-plan': , }; export type InternalRoute = Record; diff --git a/packages/ui/src/assets/images/common/index.ts b/packages/ui/src/assets/images/common/index.ts index 156a8de71..c3616595c 100644 --- a/packages/ui/src/assets/images/common/index.ts +++ b/packages/ui/src/assets/images/common/index.ts @@ -19,6 +19,7 @@ import confirmIcon from './confirm.svg'; import goldCopyIcon from './copy-gold.svg'; import copyIcon from './copy.png'; import createWalletGraphics from './create-wallet-graphics.png'; +import cypherockCoverIcon from './cypherock-cover-icon.svg'; import cypherockRedIcon from './cypherock-red.svg'; import cysyncLockedLogo from './cysync-locked.svg'; import dashWalletDefaultBgIcon from './dash-wallet-default-bg-icon.svg'; @@ -87,7 +88,6 @@ import WalletHoverExpiredIcon from './wallet-hover-expired-icon.svg'; import WalletHoverSilverBgIcon from './wallet-silver-hover-bg.svg'; import walletIcon from './wallet.svg'; import warningIcon from './warning.svg'; -import cypherockCoverIcon from './cypherock-cover-icon.svg'; export { WalletDefaultExpiredIcon, From 631286cc25aa43112ff3cd935972f03e33fb0e88 Mon Sep 17 00:00:00 2001 From: Irshad Ansari <40335357+irshadjsr21@users.noreply.github.com> Date: Sat, 31 Aug 2024 18:37:25 +0530 Subject: [PATCH 29/96] feat: implemented inheritance plan db (#533) --- apps/desktop/src/main/ipc/db.ts | 1 + apps/desktop/src/preload/index.ts | 1 + packages/core-services/src/db/version.ts | 1 + .../src/bgTask/dbListener/helper.ts | 17 ++ .../pages/MainApp/Inheritance/ChoosePlan.tsx | 9 +- .../MainApp/Inheritance/Home/PlanList.tsx | 141 ++++++------ .../pages/MainApp/Inheritance/Home/index.tsx | 6 +- packages/cysync-core/src/store/index.ts | 1 + .../src/store/inheritancePlan/index.ts | 34 +++ .../src/store/inheritancePlan/types.ts | 6 + packages/cysync-core/src/store/store.ts | 5 + packages/database/src/database.ts | 13 ++ packages/database/src/entity/index.ts | 1 + .../database/src/entity/inheritancePlan.ts | 17 ++ packages/db-interfaces/src/entities/index.ts | 1 + .../src/entities/inheritancePlan.ts | 20 ++ packages/db-interfaces/src/index.ts | 2 + .../DashbboardWallet.styled.ts | 150 ++++++------- .../DashboardWallet/DashboardWallet.tsx | 137 ++++++------ .../molecules/DashboardWallet/utils.ts | 131 ++++------- .../molecules/DashboardWallet.stories.tsx | 11 +- pnpm-lock.yaml | 203 ++---------------- 22 files changed, 417 insertions(+), 491 deletions(-) create mode 100644 packages/cysync-core/src/store/inheritancePlan/index.ts create mode 100644 packages/cysync-core/src/store/inheritancePlan/types.ts create mode 100644 packages/database/src/entity/inheritancePlan.ts create mode 100644 packages/db-interfaces/src/entities/inheritancePlan.ts diff --git a/apps/desktop/src/main/ipc/db.ts b/apps/desktop/src/main/ipc/db.ts index 2e8d52fad..a48ed9247 100644 --- a/apps/desktop/src/main/ipc/db.ts +++ b/apps/desktop/src/main/ipc/db.ts @@ -15,6 +15,7 @@ const collectionNameList: (keyof IDatabase)[] = [ 'priceInfo', 'transactionNotificationRead', 'transactionNotificationClick', + 'inheritancePlan', ]; export const setupDbListeners = async (webContents: WebContents) => { diff --git a/apps/desktop/src/preload/index.ts b/apps/desktop/src/preload/index.ts index 99e39cbba..cf69f56b4 100644 --- a/apps/desktop/src/preload/index.ts +++ b/apps/desktop/src/preload/index.ts @@ -133,6 +133,7 @@ const electronAPI = { 'priceInfo', 'transactionNotificationRead', 'transactionNotificationClick', + 'inheritancePlan', ]; const eventNames = ['change']; diff --git a/packages/core-services/src/db/version.ts b/packages/core-services/src/db/version.ts index a60dfddb9..8a5015df0 100644 --- a/packages/core-services/src/db/version.ts +++ b/packages/core-services/src/db/version.ts @@ -10,4 +10,5 @@ export const setDBVersions = (database: IDatabase) => { database.transactionNotificationClick.setVersion(0); database.transactionNotificationRead.setVersion(0); database.migration.setVersion(0); + database.inheritancePlan.setVersion(0); }; diff --git a/packages/cysync-core/src/bgTask/dbListener/helper.ts b/packages/cysync-core/src/bgTask/dbListener/helper.ts index 35dfd8ba7..73b784fcd 100644 --- a/packages/cysync-core/src/bgTask/dbListener/helper.ts +++ b/packages/cysync-core/src/bgTask/dbListener/helper.ts @@ -4,6 +4,7 @@ import { syncAccounts } from '~/actions'; import { setAccounts, setDevices, + setInheritancePlans, setLanguage, setPriceHistories, setPriceInfos, @@ -93,6 +94,16 @@ const syncTransactionsDb = createFuncWithErrorHandler( }, ); +const syncInheritancePlanDb = createFuncWithErrorHandler( + 'syncInheritancePlanDb', + async () => { + const db = getDB(); + + const plans = await db.inheritancePlan.getAll(); + store.dispatch(setInheritancePlans(plans)); + }, +); + export const syncAllDb = async (isFirst: boolean) => { await syncAccountsDb(isFirst); await syncWalletsDb(); @@ -100,6 +111,7 @@ export const syncAllDb = async (isFirst: boolean) => { await syncPriceInfosDb(); await syncPriceHistoriesDb(); await syncTransactionsDb(); + await syncInheritancePlanDb(); store.dispatch(setLanguage((await keyValueStore.appLanguage.get()) as any)); }; @@ -119,6 +131,10 @@ export const addListeners = () => { throttleDbFunction(syncPriceHistoriesDb), ); db.transaction.addListener('change', throttleDbFunction(syncTransactionsDb)); + db.inheritancePlan.addListener( + 'change', + throttleDbFunction(syncInheritancePlanDb), + ); }; export const removeListeners = () => { @@ -130,4 +146,5 @@ export const removeListeners = () => { db.priceInfo.removeAllListener(); db.priceHistory.removeAllListener(); db.transaction.removeAllListener(); + db.inheritancePlan.removeAllListener(); }; diff --git a/packages/cysync-core/src/pages/MainApp/Inheritance/ChoosePlan.tsx b/packages/cysync-core/src/pages/MainApp/Inheritance/ChoosePlan.tsx index fc3c1aea2..3442a0f3b 100644 --- a/packages/cysync-core/src/pages/MainApp/Inheritance/ChoosePlan.tsx +++ b/packages/cysync-core/src/pages/MainApp/Inheritance/ChoosePlan.tsx @@ -8,10 +8,9 @@ import { } from '@cypherock/cysync-ui'; import React, { FC, useCallback } from 'react'; import { useDispatch } from 'react-redux'; +import { useNavigate } from 'react-router-dom'; import { openInheritanceSilverPlanPurchaseDialog } from '~/actions'; -import { routes } from '~/constants'; -import { useNavigateTo } from '~/hooks'; import { selectLanguage, useAppSelector } from '~/store'; import { InheritancePageLayout } from './Layout'; @@ -19,7 +18,7 @@ import { InheritancePageLayout } from './Layout'; export const InheritanceChoosePlan: FC = () => { const lang = useAppSelector(selectLanguage); const strings = lang.strings.inheritance.choosePlan; - const navigateTo = useNavigateTo(); + const navigate = useNavigate(); const dispatch = useDispatch(); const SilverPlanFeatures = strings.plans.features.map((cur, i) => ({ @@ -33,8 +32,8 @@ export const InheritanceChoosePlan: FC = () => { })); const onBack = useCallback(() => { - navigateTo(routes.inheritance.home.path); - }, [navigateTo]); + navigate(-1); + }, [navigate]); const openSilverPlanSetup = useCallback(() => { dispatch(openInheritanceSilverPlanPurchaseDialog()); diff --git a/packages/cysync-core/src/pages/MainApp/Inheritance/Home/PlanList.tsx b/packages/cysync-core/src/pages/MainApp/Inheritance/Home/PlanList.tsx index 1d2a7e3cf..7e37d27f1 100644 --- a/packages/cysync-core/src/pages/MainApp/Inheritance/Home/PlanList.tsx +++ b/packages/cysync-core/src/pages/MainApp/Inheritance/Home/PlanList.tsx @@ -4,22 +4,77 @@ import { Flex, Typography, } from '@cypherock/cysync-ui'; -import React, { FC, useCallback } from 'react'; +import { IInheritancePlan } from '@cypherock/db-interfaces'; +import React, { FC, useCallback, useMemo } from 'react'; import { useDispatch } from 'react-redux'; import { openInheritanceSyncPlansDialog } from '~/actions'; -import { selectLanguage, useAppSelector } from '~/store'; +import { routes } from '~/constants'; +import { useNavigateTo } from '~/hooks'; +import { + selectInheritancePlans, + selectLanguage, + useAppSelector, +} from '~/store'; import { InheritancePageLayout } from '../Layout'; export const InheritancePlanList: FC = () => { const lang = useAppSelector(selectLanguage); const dispatch = useDispatch(); + const { inheritancePlans } = useAppSelector(selectInheritancePlans); + const navigateTo = useNavigateTo(); const openSyncPlans = useCallback(() => { dispatch(openInheritanceSyncPlansDialog()); }, [dispatch]); + const toSetup = useCallback(() => { + navigateTo(routes.inheritance.choosePlan.path); + }, [navigateTo]); + + const ownerPlans = useMemo( + () => inheritancePlans.filter(p => !p.isNominee), + [inheritancePlans], + ); + const nomineePlans = useMemo( + () => inheritancePlans.filter(p => p.isNominee), + [inheritancePlans], + ); + + const hasNomineePlans = nomineePlans.length > 0; + + const getPlanCardComponent = (plan: IInheritancePlan) => { + const currentDate = Date.now(); + + const isExpiring = Boolean( + plan.purchasedAt && + plan.expireAt && + plan.expireAt < currentDate + 1 * 30 * 24 * 60 * 60 * 1000, + ); + + const isExpired = Boolean( + plan.purchasedAt && plan.expireAt && plan.expireAt < currentDate, + ); + + const isPaymentPending = !(plan.purchasedAt && plan.expireAt); + + return ( + + ); + }; + return ( { type="silver" isExpiring={false} isExpired={false} - paymentPending={false} + isPaymentPending={false} name="" lang={lang.strings} - startDate="" - expiryDate="" - status="Active" + startDate={0} + expiryDate={0} + onClick={toSetup} /> - {Array(8) - .fill(0) - .map(i => ( - - ))} - - - - - - - {lang.strings.inheritance.homePage.headers.nominee.title} - - - {lang.strings.inheritance.homePage.headers.nominee.subtitle} - - - - {Array(2) - .fill(0) - .map(i => ( - - ))} + {ownerPlans.map(getPlanCardComponent)} + {hasNomineePlans && ( + <> + + + + + {lang.strings.inheritance.homePage.headers.nominee.title} + + + {lang.strings.inheritance.homePage.headers.nominee.subtitle} + + + + {nomineePlans.map(getPlanCardComponent)} + + + + )} ); diff --git a/packages/cysync-core/src/pages/MainApp/Inheritance/Home/index.tsx b/packages/cysync-core/src/pages/MainApp/Inheritance/Home/index.tsx index 5f6333543..56fc3d75e 100644 --- a/packages/cysync-core/src/pages/MainApp/Inheritance/Home/index.tsx +++ b/packages/cysync-core/src/pages/MainApp/Inheritance/Home/index.tsx @@ -1,10 +1,14 @@ import React from 'react'; +import { selectInheritancePlans, useAppSelector } from '~/store'; + import { InheritancePlanList } from './PlanList'; import { InheritanceSetup } from './Setup'; export const InheritanceHome = () => { - if (true) { + const { inheritancePlans } = useAppSelector(selectInheritancePlans); + + if (inheritancePlans.length <= 0) { return ; } diff --git a/packages/cysync-core/src/store/index.ts b/packages/cysync-core/src/store/index.ts index 210d7cd9a..be4e0a945 100644 --- a/packages/cysync-core/src/store/index.ts +++ b/packages/cysync-core/src/store/index.ts @@ -12,3 +12,4 @@ export * from './accountSync'; export * from './snackBar'; export * from './notification'; export * from './network'; +export * from './inheritancePlan'; diff --git a/packages/cysync-core/src/store/inheritancePlan/index.ts b/packages/cysync-core/src/store/inheritancePlan/index.ts new file mode 100644 index 000000000..a44fe7f4f --- /dev/null +++ b/packages/cysync-core/src/store/inheritancePlan/index.ts @@ -0,0 +1,34 @@ +import { IInheritancePlan } from '@cypherock/db-interfaces'; +import { createSlice, PayloadAction } from '@reduxjs/toolkit'; + +import { IInheritancePlanState } from './types'; + +import { RootState } from '../store'; + +export * from './types'; + +const initialState: IInheritancePlanState = { + isLoaded: false, + inheritancePlans: [], +} as IInheritancePlanState; + +export const inheritancePlanSlice = createSlice({ + name: 'inheritancePlans', + initialState, + reducers: { + setInheritancePlans: ( + state, + payload: PayloadAction, + ) => { + state.inheritancePlans = payload.payload; + state.isLoaded = true; + }, + }, +}); + +export const { setInheritancePlans } = inheritancePlanSlice.actions; + +export const selectInheritancePlans = (state: RootState) => + state.inheritancePlan; + +export default inheritancePlanSlice.reducer; diff --git a/packages/cysync-core/src/store/inheritancePlan/types.ts b/packages/cysync-core/src/store/inheritancePlan/types.ts new file mode 100644 index 000000000..2473e2135 --- /dev/null +++ b/packages/cysync-core/src/store/inheritancePlan/types.ts @@ -0,0 +1,6 @@ +import { IInheritancePlan } from '@cypherock/db-interfaces'; + +export interface IInheritancePlanState { + isLoaded: boolean; + inheritancePlans: IInheritancePlan[]; +} diff --git a/packages/cysync-core/src/store/store.ts b/packages/cysync-core/src/store/store.ts index 497ae4fe9..d314ae8b8 100644 --- a/packages/cysync-core/src/store/store.ts +++ b/packages/cysync-core/src/store/store.ts @@ -16,6 +16,9 @@ import accountSyncReducer, { IAccountSyncState } from './accountSync'; import deviceReducer, { IDeviceState } from './device'; import dialogReducer, { IDialogState } from './dialog'; import discreetModeReducer, { IDiscreetModeState } from './discreetMode'; +import inheritancePlanReducer, { + IInheritancePlanState, +} from './inheritancePlan'; import langReducers, { ILangState } from './lang'; import networkReducer, { INetworkState } from './network'; import notificationReducer, { INotificationState } from './notification'; @@ -39,6 +42,7 @@ export interface RootState { snackBar: ISnackBarState; notification: INotificationState; network: INetworkState; + inheritancePlan: IInheritancePlanState; } export const store = configureStore({ @@ -56,6 +60,7 @@ export const store = configureStore({ snackBar: snackBarReducer, notification: notificationReducer, network: networkReducer, + inheritancePlan: inheritancePlanReducer, }, }); diff --git a/packages/database/src/database.ts b/packages/database/src/database.ts index 135d4fc33..8791352a2 100644 --- a/packages/database/src/database.ts +++ b/packages/database/src/database.ts @@ -21,12 +21,15 @@ import { ITransactionNotificationReadRepository, IMigrationRepository, IMigration, + IInheritancePlan, + IInheritancePlanRepository, } from '@cypherock/db-interfaces'; import { EncryptedDB } from './encryptedDb'; import { Account, Device, + InheritancePlan, Migration, PriceHistory, PriceInfo, @@ -62,6 +65,8 @@ export class Database implements IDatabase { migration: IMigrationRepository; + inheritancePlan: IInheritancePlanRepository; + constructor(params: { database: EncryptedDB; device: IDeviceRepository; @@ -73,6 +78,7 @@ export class Database implements IDatabase { transactionNotificationClick: ITransactionNotificationClickRepository; transactionNotificationRead: ITransactionNotificationReadRepository; migration: IMigrationRepository; + inheritancePlan: IInheritancePlanRepository; }) { this.database = params.database; @@ -85,6 +91,7 @@ export class Database implements IDatabase { this.transactionNotificationClick = params.transactionNotificationClick; this.transactionNotificationRead = params.transactionNotificationRead; this.migration = params.migration; + this.inheritancePlan = params.inheritancePlan; } public static async create(dirPath: string) { @@ -137,6 +144,11 @@ export class Database implements IDatabase { Migration.name, Migration.schema, ); + const inheritancePlan = await Repository.create( + database, + InheritancePlan.name, + InheritancePlan.schema, + ); return new Database({ database, @@ -149,6 +161,7 @@ export class Database implements IDatabase { transactionNotificationClick, transactionNotificationRead, migration, + inheritancePlan, }); } diff --git a/packages/database/src/entity/index.ts b/packages/database/src/entity/index.ts index 89db5c3d9..7c53aba06 100644 --- a/packages/database/src/entity/index.ts +++ b/packages/database/src/entity/index.ts @@ -8,3 +8,4 @@ export * from './types'; export * from './transactionNotificationClick'; export * from './transactionNotificationRead'; export * from './migration'; +export * from './inheritancePlan'; diff --git a/packages/database/src/entity/inheritancePlan.ts b/packages/database/src/entity/inheritancePlan.ts new file mode 100644 index 000000000..5603b8f45 --- /dev/null +++ b/packages/database/src/entity/inheritancePlan.ts @@ -0,0 +1,17 @@ +import { IInheritancePlan } from '@cypherock/db-interfaces'; + +import { BaseFields, ITableDetails } from './types'; + +export const InheritancePlan: ITableDetails< + Omit +> = { + name: 'inheritancePlan', + schema: { + walletId: { type: 'string' }, + walletName: { type: 'string' }, + type: { type: 'string' }, + isNominee: { type: 'boolean' }, + purchasedAt: { type: 'number', isOptional: true }, + expireAt: { type: 'number', isOptional: true }, + }, +}; diff --git a/packages/db-interfaces/src/entities/index.ts b/packages/db-interfaces/src/entities/index.ts index 9d3c87509..1e0c80d7c 100644 --- a/packages/db-interfaces/src/entities/index.ts +++ b/packages/db-interfaces/src/entities/index.ts @@ -8,3 +8,4 @@ export * from './wallet'; export * from './transactionNotificationRead'; export * from './transactionNotificationClick'; export * from './migrations'; +export * from './inheritancePlan'; diff --git a/packages/db-interfaces/src/entities/inheritancePlan.ts b/packages/db-interfaces/src/entities/inheritancePlan.ts new file mode 100644 index 000000000..1eaeb0e38 --- /dev/null +++ b/packages/db-interfaces/src/entities/inheritancePlan.ts @@ -0,0 +1,20 @@ +import type { IEntity, IRepository } from './base'; + +export const InheritancePlanTypeMap = { + gold: 'gold', + silver: 'silver', +} as const; + +export type InheritancePlanType = + (typeof InheritancePlanTypeMap)[keyof typeof InheritancePlanTypeMap]; + +export interface IInheritancePlan extends IEntity { + walletId: string; + walletName: string; + type: InheritancePlanType; + isNominee: boolean; + purchasedAt?: number; + expireAt?: number; +} + +export type IInheritancePlanRepository = IRepository; diff --git a/packages/db-interfaces/src/index.ts b/packages/db-interfaces/src/index.ts index 0d94db808..34b16562a 100644 --- a/packages/db-interfaces/src/index.ts +++ b/packages/db-interfaces/src/index.ts @@ -11,6 +11,7 @@ import type { ITransactionNotificationClickRepository, ITransactionNotificationReadRepository, IMigrationRepository, + IInheritancePlanRepository, } from './entities'; export interface IDatabase { @@ -26,6 +27,7 @@ export interface IDatabase { priceHistory: IPriceHistoryRepository; priceInfo: IPriceInfoRepository; migration: IMigrationRepository; + inheritancePlan: IInheritancePlanRepository; changeEncryptionKey(encryptionKey?: string): Promise; createOrFetchRepository( name: string, diff --git a/packages/ui/src/components/molecules/DashboardWallet/DashbboardWallet.styled.ts b/packages/ui/src/components/molecules/DashboardWallet/DashbboardWallet.styled.ts index 825a29883..302ea0590 100644 --- a/packages/ui/src/components/molecules/DashboardWallet/DashbboardWallet.styled.ts +++ b/packages/ui/src/components/molecules/DashboardWallet/DashbboardWallet.styled.ts @@ -19,18 +19,18 @@ export const SetupCoverContainer = styled.div` transform: translateY(10px); `; -export const SetupCoverPlusImage = styled(PlusIcon)<{ isHover: boolean }>` +export const SetupCoverPlusImage = styled(PlusIcon)<{ $isHover: boolean }>` transition: transform 0.3s ease-in-out, margin-bottom 0.3s ease-in-out, opacity 0.3s ease-in-out; width: 34px; height: 34px; - transform: ${({ isHover }) => - isHover ? 'rotate(-90deg) scale(2)' : 'rotate(0) scale(1)'}; - margin-bottom: ${({ isHover }) => (isHover ? '-10px' : '16px')}; - opacity: ${({ isHover }) => (isHover ? '1' : '0.4')}; + transform: ${({ $isHover }) => + $isHover ? 'rotate(-90deg) scale(2)' : 'rotate(0) scale(1)'}; + margin-bottom: ${({ $isHover }) => ($isHover ? '-10px' : '16px')}; + opacity: ${({ $isHover }) => ($isHover ? '1' : '0.4')}; `; -export const SetupCoverText = styled.div<{ isHover: boolean }>` +export const SetupCoverText = styled.div<{ $isHover: boolean }>` display: flex; flex-direction: column; justify-content: center; @@ -39,8 +39,8 @@ export const SetupCoverText = styled.div<{ isHover: boolean }>` font-size: 12px; color: white; transition: transform 0.3s ease-in-out; - transform: ${({ isHover }) => - isHover ? 'translateY(500%) translateX(10%)' : 'translateY(0)'}; + transform: ${({ $isHover }) => + $isHover ? 'translateY(500%) translateX(10%)' : 'translateY(0)'}; `; export const ProgressbarWrapper = styled.div` @@ -48,13 +48,13 @@ export const ProgressbarWrapper = styled.div` `; export const Container = styled.div< - { isHover: boolean; isNone: boolean; backgroundImage: string } & WidthProps + { $isHover: boolean; $isNone: boolean; $backgroundImage: string } & WidthProps >` width: 200px; height: 176px; display: flex; - ${({ isNone }) => - isNone ? 'justify-content: center' : 'justify-content:normal'}; + ${({ $isNone }) => + $isNone ? 'justify-content: center' : 'justify-content:normal'}; align-items: center; flex-direction: column; font-family: 'Poppins'; @@ -62,10 +62,10 @@ export const Container = styled.div< font-weight: 500; text-align: center; color: white; - background: ${({ isHover, isNone, backgroundImage }) => - isNone - ? `url(${isHover ? dashWalletDefaultBgIcon : dashWalletHoverBgIcon})` - : `url(${backgroundImage})`}; + background: ${({ $isHover, $isNone, $backgroundImage }) => + $isNone + ? `url(${$isHover ? dashWalletDefaultBgIcon : dashWalletHoverBgIcon})` + : `url(${$backgroundImage})`}; cursor: pointer; position: relative; overflow: hidden; @@ -89,7 +89,7 @@ export const StyledExpiredClockIcon = styled(ClockIcon)` fill: #2a2827; `; -export const Flex = styled.div<{ isHover: boolean }>` +export const Flex = styled.div<{ $isHover: boolean }>` display: flex; width: 45%; height: unset; @@ -97,7 +97,7 @@ export const Flex = styled.div<{ isHover: boolean }>` fill: #2a2827; `; -export const Type = styled.div<{ type: string; isHover: boolean }>` +export const Type = styled.div<{ type: string; $isHover: boolean }>` font-family: 'Poppins'; font-size: 12px; font-weight: 700; @@ -114,16 +114,16 @@ export const Type = styled.div<{ type: string; isHover: boolean }>` right: 95%; opacity: 0; transition: opacity 0.3s ease-in-out; - ${({ isHover }) => - isHover && + ${({ $isHover }) => + $isHover && ` opacity: 1; `} `; export const Expiring = styled.div<{ - isHover: boolean; - disableTransform: boolean; + $isHover: boolean; + $disableTransform: boolean; }>` font-family: 'Poppins'; font-size: 12px; @@ -133,15 +133,15 @@ export const Expiring = styled.div<{ color: ${({ theme }) => theme.palette.error.main}; position: relative; right: 70%; - transform: ${({ isHover, disableTransform }) => { - if (disableTransform) return 'none'; - return isHover ? 'translateX(0)' : 'translateX(40%)'; + transform: ${({ $isHover, $disableTransform }) => { + if ($disableTransform) return 'none'; + return $isHover ? 'translateX(0)' : 'translateX(40%)'; }}; - opacity: ${({ isHover }) => (isHover ? 1 : 0)}; + opacity: ${({ $isHover }) => ($isHover ? 1 : 0)}; transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out; `; -export const TimerContainer = styled.div<{ isHover: boolean }>` +export const TimerContainer = styled.div<{ $isHover: boolean }>` display: flex; justify-content: center; width: 90px; @@ -150,7 +150,7 @@ export const TimerContainer = styled.div<{ isHover: boolean }>` margin-top: 2px; `; -export const TimerText = styled.div<{ isHover: boolean; theme: any }>` +export const TimerText = styled.div<{ $isHover: boolean; theme: any }>` display: flex; flex-direction: column; justify-content: center; @@ -169,25 +169,25 @@ export const TimerText = styled.div<{ isHover: boolean; theme: any }>` `; export const TimerHead = styled.div<{ - isHover: boolean; - isExpired: boolean; - paymentPending: boolean; + $isHover: boolean; + $isExpired: boolean; + $isPaymentPending: boolean; }>` font-family: 'Poppins'; font-size: 10px; - font-weight: ${({ isHover, isExpired, paymentPending }) => { - if (isExpired || isHover || paymentPending) { + font-weight: ${({ $isHover, $isExpired, $isPaymentPending }) => { + if ($isExpired || $isHover || $isPaymentPending) { return '700'; } return '400'; }}; line-height: 16.5px; text-align: left; - color: ${({ isExpired, isHover, theme, paymentPending }) => { - if (isExpired) { + color: ${({ $isExpired, $isHover, theme, $isPaymentPending }) => { + if ($isExpired) { return theme.palette.warn.main; } - if (isHover || paymentPending) { + if ($isHover || $isPaymentPending) { return 'white'; } return theme.palette.muted.main; @@ -198,7 +198,7 @@ export const getColor = ( isExpiring: boolean, isHover: boolean, isExpired: boolean, - paymentPending: boolean, + isPaymentPending: boolean, theme: any, ) => { if (isExpiring && isHover) { @@ -210,7 +210,7 @@ export const getColor = ( if (isHover) { return theme.palette.muted.main; } - if (isExpired || paymentPending) { + if (isExpired || isPaymentPending) { return theme.palette.muted.main; } return 'white'; @@ -219,44 +219,44 @@ export const getColor = ( export const getFontWeight = ( isHover: boolean, isExpired: boolean, - paymentPending: boolean, -) => (isHover || isExpired || paymentPending ? '400' : '700'); + isPaymentPending: boolean, +) => (isHover || isExpired || isPaymentPending ? '400' : '700'); export const TimerSubtitle = styled.div<{ - isHover: boolean; - isExpiring: boolean; - isExpired: boolean; - paymentPending: boolean; + $isHover: boolean; + $isExpiring: boolean; + $isExpired: boolean; + $isPaymentPending: boolean; }>` font-family: 'Poppins'; font-size: 10px; - font-weight: ${({ isHover, isExpired, paymentPending }) => - getFontWeight(isHover, isExpired, paymentPending)}; + font-weight: ${({ $isHover, $isExpired, $isPaymentPending }) => + getFontWeight($isHover, $isExpired, $isPaymentPending)}; line-height: 18px; text-align: left; text-transform: uppercase; - color: ${({ isExpiring, isHover, isExpired, paymentPending, theme }) => - getColor(isExpiring, isHover, isExpired, paymentPending, theme)}; + color: ${({ $isExpiring, $isHover, $isExpired, $isPaymentPending, theme }) => + getColor($isExpiring, $isHover, $isExpired, $isPaymentPending, theme)}; `; export const getWalletNameColor = ( isHover: boolean, isExpiring: boolean, isExpired: boolean, - paymentPending: boolean, + isPaymentPending: boolean, theme: any, ) => { - if (isHover && (isExpiring || isExpired || paymentPending)) { + if (isHover && (isExpiring || isExpired || isPaymentPending)) { return theme.palette.background.golden; } return theme.palette.muted.main; }; export const WalletNameContainer = styled.div<{ - isHover: boolean; - isExpiring: boolean; - isExpired: boolean; - paymentPending: boolean; + $isHover: boolean; + $isExpiring: boolean; + $isExpired: boolean; + $isPaymentPending: boolean; }>` position: relative; width: 100%; @@ -268,34 +268,40 @@ export const WalletNameContainer = styled.div<{ line-height: 21px; text-align: center; margin-top: 8px; - color: ${({ isHover, isExpiring, isExpired, paymentPending, theme }) => - getWalletNameColor(isHover, isExpiring, isExpired, paymentPending, theme)}; + color: ${({ $isHover, $isExpiring, $isExpired, $isPaymentPending, theme }) => + getWalletNameColor( + $isHover, + $isExpiring, + $isExpired, + $isPaymentPending, + theme, + )}; `; export const WalletNameText = styled.div<{ - isHover: boolean; - disableAnimation?: boolean; + $isHover: boolean; + $disableAnimation?: boolean; }>` position: absolute; width: 100%; - transition: ${({ disableAnimation }) => + transition: ${({ $disableAnimation: disableAnimation }) => disableAnimation ? 'none' : 'transform 0.3s ease-in-out, opacity 0.3s ease-in-out'}; - transform: ${({ isHover, disableAnimation }) => + transform: ${({ $isHover: isHover, $disableAnimation: disableAnimation }) => isHover && !disableAnimation ? 'translateX(0)' : 'translateX(15%)'}; - opacity: ${({ isHover, disableAnimation }) => + opacity: ${({ $isHover: isHover, $disableAnimation: disableAnimation }) => isHover && !disableAnimation ? 1 : 0}; `; export const WalletNameHoverText = styled(WalletNameText)<{ - isHover: boolean; - disableAnimation?: boolean; + $isHover: boolean; + $disableAnimation?: boolean; }>` - transform: ${({ isHover, disableAnimation }) => - isHover && !disableAnimation ? 'translateX(0)' : 'translateX(-15%)'}; - opacity: ${({ isHover, disableAnimation }) => - isHover && !disableAnimation ? 1 : 0}; + transform: ${({ $isHover, $disableAnimation }) => + $isHover && !$disableAnimation ? 'translateX(0)' : 'translateX(-15%)'}; + opacity: ${({ $isHover, $disableAnimation }) => + $isHover && !$disableAnimation ? 1 : 0}; `; export const TransitionTextWrapper = styled.div` @@ -306,12 +312,12 @@ export const TransitionTextWrapper = styled.div` height: 100%; `; -export const TransitionText = styled.div<{ isHover: boolean }>` +export const TransitionText = styled.div<{ $isHover: boolean }>` position: absolute; transition: opacity 0.3s ease-in-out, transform 0.3s ease-in-out; - opacity: ${props => (props.isHover ? 1 : 0)}; + opacity: ${props => (props.$isHover ? 1 : 0)}; transform: ${props => - props.isHover + props.$isHover ? 'translateY(0px) translateX(0px)' : 'translateY(1px) translateX(-1px)'}; width: max-content; @@ -319,12 +325,12 @@ export const TransitionText = styled.div<{ isHover: boolean }>` margin-top: 20px; `; -export const TransitionTextSubtitle = styled.div<{ isHover: boolean }>` +export const TransitionTextSubtitle = styled.div<{ $isHover: boolean }>` position: absolute; transition: opacity 0.3s ease-in-out, transform 0.3s ease-in-out; - opacity: ${props => (props.isHover ? 1 : 0)}; + opacity: ${props => (props.$isHover ? 1 : 0)}; transform: ${props => - props.isHover + props.$isHover ? 'translateY(0px) translateX(0px)' : 'translateY(1px) translateX(-1px)'}; width: max-content; diff --git a/packages/ui/src/components/molecules/DashboardWallet/DashboardWallet.tsx b/packages/ui/src/components/molecules/DashboardWallet/DashboardWallet.tsx index fde018202..551d89b1d 100644 --- a/packages/ui/src/components/molecules/DashboardWallet/DashboardWallet.tsx +++ b/packages/ui/src/components/molecules/DashboardWallet/DashboardWallet.tsx @@ -2,7 +2,6 @@ import React, { FC, useState, useEffect } from 'react'; import { CircularProgressbar, buildStyles } from 'react-circular-progressbar'; import 'react-circular-progressbar/dist/styles.css'; -import { DefaultTheme } from 'styled-components/dist/types'; import { SetupCoverPlusImage, @@ -33,7 +32,6 @@ import { getPathColor, getTimerHeadText, getTypeText, - updateHoverOnly, updateProgressAndHover, } from './utils'; @@ -47,7 +45,7 @@ export interface DashboardWalletProps extends WidthProps { type: 'silver' | 'gold'; isExpiring: boolean; isExpired: boolean; - paymentPending: boolean; + isPaymentPending: boolean; name: string; lang: { dashboard: { @@ -68,9 +66,9 @@ export interface DashboardWalletProps extends WidthProps { }; }; }; - startDate: string; - expiryDate: string; - status: 'Active' | 'Inactive' | 'Pending'; + startDate: number; + expiryDate: number; + onClick?: () => void; } export const DashboardWallet: FC = ({ @@ -78,21 +76,21 @@ export const DashboardWallet: FC = ({ type, isExpiring, isExpired, - paymentPending, + isPaymentPending, name, lang, startDate, expiryDate, - status, + onClick, ...restProps }) => { const [isHover, setIsHover] = useState(false); const [progress, setProgress] = useState({ value: 0, rotation: 0 }); - const theme = useTheme() as DefaultTheme; + const theme = useTheme(); const hoverText = calculateHoverText( isExpiring, isExpired, - paymentPending, + isPaymentPending, name, lang, ); @@ -100,41 +98,30 @@ export const DashboardWallet: FC = ({ const [hoverProgress, setHoverProgress] = useState({ value: 0, rotation: 0 }); useEffect(() => { - updateProgressAndHover( + updateProgressAndHover({ startDate, expiryDate, - status, isExpired, - paymentPending, + isPaymentPending, setProgress, setHoverProgress, - ); - }, [startDate, expiryDate, status, isExpired, paymentPending]); - - useEffect(() => { - updateHoverOnly( - startDate, - expiryDate, - status, - setProgress, - setHoverProgress, - ); - }, [startDate, expiryDate, status]); + }); + }, [startDate, expiryDate, isExpired, isPaymentPending]); return ( setIsHover(true)} onMouseLeave={() => setIsHover(false)} - onClick={() => setIsHover(true)} + onClick={onClick} role="button" tabIndex={0} onKeyDown={e => { @@ -144,23 +131,23 @@ export const DashboardWallet: FC = ({ > {isNone && ( - - + + {lang.dashboard.wallet.setupCover} )} {!isNone && ( <> - - + + {getTypeText(type, lang)} - + {getExpiringText(isExpiring, isExpired, lang)} - + = ({ isHover, isExpired, isExpiring, - paymentPending, + isPaymentPending, theme, type, ), @@ -179,50 +166,60 @@ export const DashboardWallet: FC = ({ ? hoverProgress.rotation : progress.rotation, pathTransition: - isExpired || paymentPending + isExpired || isPaymentPending ? 'none' : 'all 0.4s ease-in-out 0s', })} /> - + - + - {getTimerHeadText(isHover, isExpired, paymentPending, lang)} + {getTimerHeadText( + isHover, + isExpired, + isPaymentPending, + lang, + )} - + - {getTimerHeadText(isHover, isExpired, paymentPending, lang)} + {getTimerHeadText( + isHover, + isExpired, + isPaymentPending, + lang, + )} - + {format(new Date(startDate), 'dd MMM yyyy')} - + {format(new Date(expiryDate), 'dd MMM yyyy')} @@ -231,25 +228,25 @@ export const DashboardWallet: FC = ({ {isExpired && } - {paymentPending && ( + {isPaymentPending && ( )} {disableAnimation && name} {name} {hoverText} @@ -259,3 +256,7 @@ export const DashboardWallet: FC = ({ ); }; + +DashboardWallet.defaultProps = { + onClick: undefined, +}; diff --git a/packages/ui/src/components/molecules/DashboardWallet/utils.ts b/packages/ui/src/components/molecules/DashboardWallet/utils.ts index d2a4b1168..c673439fe 100644 --- a/packages/ui/src/components/molecules/DashboardWallet/utils.ts +++ b/packages/ui/src/components/molecules/DashboardWallet/utils.ts @@ -1,5 +1,3 @@ -import { DefaultTheme } from 'styled-components/dist/types'; - import { WalletHoverExpiredIcon, WalletHoverSilverBgIcon, @@ -9,51 +7,7 @@ import { WalletDefaultPendingIcon, dashWalletDefaultBgIcon, } from '../../../assets'; - -export const calculateProgress = ( - startDate: string, - expiryDate: string, - status: 'Active' | 'Inactive' | 'Pending', -) => { - const start = new Date(startDate); - const expiry = new Date(expiryDate); - const now = new Date(); - - const totalDuration = expiry.getTime() - start.getTime(); - const elapsedDuration = now.getTime() - start.getTime(); - - let value = (elapsedDuration / totalDuration) * 100; - let rotation = 1 - elapsedDuration / totalDuration; - - if (status === 'Pending') { - value = 0; - rotation = 0; - } - - return { - value: value > 100 ? 100 : value, - rotation: rotation > 1 ? 1 : rotation, - }; -}; - -export const getCurrentTime = () => { - const date = new Date(); - return date.toLocaleTimeString([], { - hourCycle: 'h23', - hour: '2-digit', - minute: '2-digit', - }); -}; - -export const getCurrentFullTime = () => { - const date = new Date(); - return date.toLocaleTimeString([], { - hourCycle: 'h23', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - }); -}; +import { ThemeType } from '../../../themes'; export const getBackgroundImage = ( isHover: boolean, @@ -95,7 +49,7 @@ export const getPathColor = ( isExpiring: boolean, isExpired: boolean, paymentPending: boolean, - theme: DefaultTheme, + theme: ThemeType, type: string, ) => { if (isHover && (isExpiring || isExpired)) { @@ -107,14 +61,6 @@ export const getPathColor = ( return theme.palette.background.golden; }; -export const calculateDiffDays = (expiryDate: string) => { - const diffTime = Math.abs( - new Date(expiryDate).getTime() - new Date().getTime(), - ); - const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); - return diffDays; -}; - export const getTimerHeadText = ( isHover: boolean, isExpired: boolean, @@ -146,48 +92,59 @@ export const calculateHoverText = ( return name; }; -export const updateProgressAndHover = ( - startDate: string, - expiryDate: string, - status: 'Active' | 'Inactive' | 'Pending', - isExpired: any, - paymentPending: any, - setProgress: (arg0: { value: number; rotation: number }) => void, - setHoverProgress: (arg0: { value: number; rotation: number }) => void, -) => { +export const updateProgressAndHover = (params: { + startDate: number; + expiryDate: number; + isExpired: boolean; + isPaymentPending: boolean; + setProgress: (params: { value: number; rotation: number }) => void; + setHoverProgress: (params: { value: number; rotation: number }) => void; +}) => { + const { + startDate, + expiryDate, + isExpired, + isPaymentPending, + setProgress, + setHoverProgress, + } = params; let value = 0; let rotation = 0; - let hoverValue = 100; + let hoverValue = 0; + let hoverRotation = 0; if (isExpired) { value = 0; - hoverValue = 100; rotation = 0; - } else if (paymentPending) { + + hoverValue = 100; + hoverRotation = 0; + } else if (isPaymentPending) { value = 0; - hoverValue = 0; rotation = 0; + + hoverValue = 0; + hoverRotation = 0; } else { - const progressData = calculateProgress(startDate, expiryDate, status); - value = progressData.value; - rotation = progressData.rotation; - hoverValue = 100 - value; - } + const now = Date.now(); - setProgress({ value, rotation }); - setHoverProgress({ value: hoverValue, rotation }); -}; + const totalDuration = expiryDate - startDate; + const elapsedDuration = now - startDate; + const timeRemaining = expiryDate - now; + + hoverValue = (elapsedDuration / totalDuration) * 100; + hoverRotation = 0; + + value = (timeRemaining / totalDuration) * 100; + rotation = hoverValue / 100; + + value = Math.min(100, value); + hoverValue = Math.min(100, hoverValue); + + rotation = Math.min(1, rotation); + hoverRotation = Math.min(1, hoverRotation); + } -export const updateHoverOnly = ( - startDate: string, - expiryDate: string, - status: 'Active' | 'Inactive' | 'Pending', - setProgress: (arg0: { value: number; rotation: number }) => void, - setHoverProgress: (arg0: { value: number; rotation: number }) => void, -) => { - const { value, rotation } = calculateProgress(startDate, expiryDate, status); - const hoverValue = 100 - value; - const hoverRotation = 0; setProgress({ value, rotation }); setHoverProgress({ value: hoverValue, rotation: hoverRotation }); }; diff --git a/packages/ui/src/stories/molecules/DashboardWallet.stories.tsx b/packages/ui/src/stories/molecules/DashboardWallet.stories.tsx index 2df60a330..a82495fdd 100644 --- a/packages/ui/src/stories/molecules/DashboardWallet.stories.tsx +++ b/packages/ui/src/stories/molecules/DashboardWallet.stories.tsx @@ -37,12 +37,11 @@ export const Default: Story = { type: 'silver', isExpiring: false, isExpired: false, - paymentPending: false, + isPaymentPending: false, name: 'My Default Wallet', lang, - startDate: '2024-01-21', - expiryDate: '2024-09-21', - status: 'Active', + startDate: new Date('2024-01-21').getTime(), + expiryDate: new Date('2024-09-21').getTime(), }, }; @@ -79,13 +78,13 @@ export const ExpiredPlan: Story = { args: { ...Default.args, isExpired: true, - expiryDate: '2023-01-21', + expiryDate: new Date('2023-01-21').getTime(), }, }; export const PendingPayment: Story = { args: { ...Default.args, - paymentPending: true, + isPaymentPending: true, }, }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9c91f171a..27b7aac0b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.0' +lockfileVersion: '6.1' settings: autoInstallPeers: true @@ -381,7 +381,7 @@ importers: version: 4.9.5 vite: specifier: ^4.3.9 - version: 4.3.9(@types/node@20.5.1) + version: 4.3.9(@types/node@18.15.11)(less@4.2.0) vite-plugin-electron: specifier: ^0.12.0 version: 0.12.0 @@ -1577,7 +1577,7 @@ importers: version: 8.43.0 eslint-import-resolver-typescript: specifier: ^3.5.5 - version: 3.5.5(eslint-plugin-import@2.27.5)(eslint@8.43.0) + version: 3.5.5(@typescript-eslint/parser@5.59.11)(eslint-plugin-import@2.27.5)(eslint@8.43.0) ethers: specifier: ^6.7.0 version: 6.7.0 @@ -1756,7 +1756,7 @@ importers: version: 8.43.0 eslint-import-resolver-typescript: specifier: ^3.5.5 - version: 3.5.5(eslint-plugin-import@2.27.5)(eslint@8.43.0) + version: 3.5.5(@typescript-eslint/parser@5.59.11)(eslint-plugin-import@2.27.5)(eslint@8.43.0) jest: specifier: ^29.5.0 version: 29.5.0(@types/node@18.15.11)(ts-node@10.9.1) @@ -12190,7 +12190,7 @@ packages: '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.22.5) '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.22.5) react-refresh: 0.14.2 - vite: 4.3.9(@types/node@20.5.1) + vite: 4.3.9(@types/node@18.15.11)(less@4.2.0) transitivePeerDependencies: - supports-color dev: true @@ -13438,7 +13438,7 @@ packages: '@babel/core': 7.22.5 find-cache-dir: 3.3.2 schema-utils: 4.2.0 - webpack: 5.92.1 + webpack: 5.92.1(esbuild@0.18.20) dev: true /babel-plugin-istanbul@6.1.1: @@ -15158,7 +15158,7 @@ packages: postcss-modules-values: 4.0.0(postcss@8.4.38) postcss-value-parser: 4.2.0 semver: 7.5.3 - webpack: 5.92.1 + webpack: 5.92.1(esbuild@0.18.20) dev: true /css-select@4.3.0: @@ -16564,30 +16564,6 @@ packages: - supports-color dev: true - /eslint-import-resolver-typescript@3.5.5(eslint-plugin-import@2.27.5)(eslint@8.43.0): - resolution: {integrity: sha512-TdJqPHs2lW5J9Zpe17DZNQuDnox4xo2o+0tE7Pggain9Rbc19ik8kFtXdxZ250FVx2kF4vlt2RSf4qlUpG7bhw==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - eslint: '*' - eslint-plugin-import: '*' - dependencies: - debug: 4.3.5(supports-color@8.1.1) - enhanced-resolve: 5.17.0 - eslint: 8.43.0 - eslint-module-utils: 2.8.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5)(eslint@8.43.0) - eslint-plugin-import: 2.27.5(eslint-import-resolver-typescript@3.5.5)(eslint@8.43.0) - get-tsconfig: 4.7.5 - globby: 13.2.2 - is-core-module: 2.14.0 - is-glob: 4.0.3 - synckit: 0.8.8 - transitivePeerDependencies: - - '@typescript-eslint/parser' - - eslint-import-resolver-node - - eslint-import-resolver-webpack - - supports-color - dev: true - /eslint-module-utils@2.8.1(@typescript-eslint/parser@5.59.11)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5)(eslint@8.43.0): resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} engines: {node: '>=4'} @@ -16618,35 +16594,6 @@ packages: - supports-color dev: true - /eslint-module-utils@2.8.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5)(eslint@8.43.0): - resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true - dependencies: - debug: 3.2.7 - eslint: 8.43.0 - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.5.5(eslint-plugin-import@2.27.5)(eslint@8.43.0) - transitivePeerDependencies: - - supports-color - dev: true - /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.59.11)(eslint-import-resolver-typescript@3.5.5)(eslint@8.43.0): resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} engines: {node: '>=4'} @@ -16680,38 +16627,6 @@ packages: - supports-color dev: true - /eslint-plugin-import@2.27.5(eslint-import-resolver-typescript@3.5.5)(eslint@8.43.0): - resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - dependencies: - array-includes: 3.1.8 - array.prototype.flat: 1.3.2 - array.prototype.flatmap: 1.3.2 - debug: 3.2.7 - doctrine: 2.1.0 - eslint: 8.43.0 - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5)(eslint@8.43.0) - has: 1.0.4 - is-core-module: 2.14.0 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.values: 1.2.0 - resolve: 1.22.8 - semver: 6.3.1 - tsconfig-paths: 3.15.0 - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - dev: true - /eslint-plugin-jsx-a11y@6.7.1(eslint@8.43.0): resolution: {integrity: sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==} engines: {node: '>=4.0'} @@ -21705,7 +21620,7 @@ packages: webpack: ^5.0.0 dependencies: loader-utils: 2.0.4 - webpack: 5.92.1 + webpack: 5.92.1(esbuild@0.18.20) dev: true /node-preload@0.2.1: @@ -22480,6 +22395,9 @@ packages: resolution: {integrity: sha512-2GTVocFkwblV/TIg9AmT7TI2fO4xdWkyN8aFUEVtiVNWt96GTR3FgQyHFValfCbcj1k9Xf962Ws2hYXYUr9k1Q==} engines: {node: '>= 12.0.0'} hasBin: true + peerDependenciesMeta: + '@parcel/core': + optional: true dependencies: '@parcel/config-default': 2.9.3(@parcel/core@2.9.3)(typescript@4.9.5) '@parcel/core': 2.9.3 @@ -25210,7 +25128,7 @@ packages: peerDependencies: webpack: ^5.0.0 dependencies: - webpack: 5.92.1 + webpack: 5.92.1(esbuild@0.18.20) dev: true /styled-components@6.0.0-rc.1(react-dom@18.2.0)(react@18.2.0): @@ -25493,30 +25411,6 @@ packages: webpack: 5.92.1(esbuild@0.18.20) dev: true - /terser-webpack-plugin@5.3.10(webpack@5.92.1): - resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} - engines: {node: '>= 10.13.0'} - peerDependencies: - '@swc/core': '*' - esbuild: '*' - uglify-js: '*' - webpack: ^5.1.0 - peerDependenciesMeta: - '@swc/core': - optional: true - esbuild: - optional: true - uglify-js: - optional: true - dependencies: - '@jridgewell/trace-mapping': 0.3.25 - jest-worker: 27.5.1 - schema-utils: 3.3.0 - serialize-javascript: 6.0.2 - terser: 5.31.1 - webpack: 5.92.1 - dev: true - /terser@4.8.1: resolution: {integrity: sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==} engines: {node: '>=6.0.0'} @@ -27065,39 +26959,6 @@ packages: fsevents: 2.3.3 dev: true - /vite@4.3.9(@types/node@20.5.1): - resolution: {integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==} - engines: {node: ^14.18.0 || >=16.0.0} - hasBin: true - peerDependencies: - '@types/node': '>= 14' - less: '*' - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - dependencies: - '@types/node': 20.5.1 - esbuild: 0.17.19 - postcss: 8.4.38 - rollup: 3.29.4 - optionalDependencies: - fsevents: 2.3.3 - dev: true - /vm-browserify@1.1.2: resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} dev: true @@ -27444,46 +27305,6 @@ packages: /webpack-virtual-modules@0.6.2: resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} - /webpack@5.92.1: - resolution: {integrity: sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA==} - engines: {node: '>=10.13.0'} - hasBin: true - peerDependencies: - webpack-cli: '*' - peerDependenciesMeta: - webpack-cli: - optional: true - dependencies: - '@types/eslint-scope': 3.7.7 - '@types/estree': 1.0.5 - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/wasm-edit': 1.12.1 - '@webassemblyjs/wasm-parser': 1.12.1 - acorn: 8.12.0 - acorn-import-attributes: 1.9.5(acorn@8.12.0) - browserslist: 4.23.1 - chrome-trace-event: 1.0.4 - enhanced-resolve: 5.17.0 - es-module-lexer: 1.5.4 - eslint-scope: 5.1.1 - events: 3.3.0 - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - json-parse-even-better-errors: 2.3.1 - loader-runner: 4.3.0 - mime-types: 2.1.35 - neo-async: 2.6.2 - schema-utils: 3.3.0 - tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(webpack@5.92.1) - watchpack: 2.4.1 - webpack-sources: 3.2.3 - transitivePeerDependencies: - - '@swc/core' - - esbuild - - uglify-js - dev: true - /webpack@5.92.1(esbuild@0.18.20): resolution: {integrity: sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA==} engines: {node: '>=10.13.0'} From f025bfb9ee1a41bd41bc7e497d86c026e8d59c59 Mon Sep 17 00:00:00 2001 From: AryanCypherock Date: Sat, 31 Aug 2024 19:08:05 +0530 Subject: [PATCH 30/96] fix: inheritance UI many in many (#523) * fix: animation fix in many in many * fix: added animated gradient * fix: added colors for card in theme --- .../src/assets/images/common/mim-hover-bg.svg | 4 ++-- .../src/components/molecules/ManyInMany.tsx | 22 +++++++++---------- packages/ui/src/themes/color.styled.ts | 2 ++ packages/ui/src/themes/theme.styled.ts | 3 +++ 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/packages/ui/src/assets/images/common/mim-hover-bg.svg b/packages/ui/src/assets/images/common/mim-hover-bg.svg index 80bb5f00c..737e172bb 100644 --- a/packages/ui/src/assets/images/common/mim-hover-bg.svg +++ b/packages/ui/src/assets/images/common/mim-hover-bg.svg @@ -14,13 +14,13 @@ diff --git a/packages/ui/src/components/molecules/ManyInMany.tsx b/packages/ui/src/components/molecules/ManyInMany.tsx index 7af0d5a7e..b6aef8af2 100644 --- a/packages/ui/src/components/molecules/ManyInMany.tsx +++ b/packages/ui/src/components/molecules/ManyInMany.tsx @@ -32,7 +32,7 @@ const getBackground = (params: { if (params.$isSelected) return params.theme.palette.background.cardSelected; if (params.disabled) return params.theme.palette.background.cardDisabled; if (params.isHovered) return params.theme.palette.gradients.cardHover; - return params.theme.palette.background.cardSelected; + return params.theme.palette.gradients.cardDefault; }; const StyledMimDefaultWalletIcon = styled(MimDefaultWalletIcon)``; @@ -53,7 +53,7 @@ const StyledContainer = styled.div< >` position: relative; overflow: hidden; - width: 276px; + width: 340px; height: 128px; padding: 24px 16px; color: ${({ theme, disabled }) => @@ -65,6 +65,8 @@ const StyledContainer = styled.div< $isSelected && !disabled ? theme.palette.border.selected : 'transparent'}; box-shadow: ${getBoxShadow}; background: ${getBackground}; + transition: font-weight 0.3s ease-in-out, background 0.3s ease-in-out; + ${utils} &::after { @@ -79,20 +81,18 @@ const StyledContainer = styled.div< !disabled && !$isSelected ? `url(${manyInManyBgImage})` : 'none'}; background-position: bottom center; background-repeat: no-repeat; - background-size: 280px; - transition: transform 0.5s ease-in-out; + background-size: contain; + transition: transform 0.3s ease-in-out; transform-origin: bottom center; } &:hover::after { transform: ${({ $isSelected, disabled }) => !disabled && !$isSelected - ? 'translateX(-30%) rotate(45deg) scale(1)' + ? 'translateX(-40%) translateY(15%) rotate(30deg) scaleY(1.8) scaleX(.8)' : 'none'}; - background-position: bottom left 180%; - width: 167%; - height: 175%; - background-size: 496px; + background-position: bottom left; + background-size: contain; background-image: ${({ $isSelected, disabled }) => !disabled && !$isSelected ? `url(${manyInManyHoverBgImage})` : 'none'}; } @@ -121,8 +121,8 @@ const StyledContainer = styled.div< } &:hover ${StyledDateLabel} { - font-size: ${({ $isSelected, disabled }) => - !$isSelected && !disabled ? '15px' : '14px'}; + font-weight: ${({ $isSelected, disabled }) => + !$isSelected && !disabled ? '500' : '400'}; } `; diff --git a/packages/ui/src/themes/color.styled.ts b/packages/ui/src/themes/color.styled.ts index 36de0b8c5..f3ff1b3fc 100644 --- a/packages/ui/src/themes/color.styled.ts +++ b/packages/ui/src/themes/color.styled.ts @@ -35,6 +35,8 @@ export const colors = { notExpiring: 'conic-gradient(from 0deg, transparent, 270deg, transparent, 270deg, #E9B873 ,300deg, #FEDD8F, 330deg, #B78D51, 360deg ,transparent, 360deg, transparent)', }, + cardDefault: + 'linear-gradient(300deg, rgba(96, 58, 23, 0.20) 0%, rgba(0, 0, 0, 0.00) 57.81%, rgba(0, 0, 0, 0.00) 100%), #2A2827', cardSelected: 'linear-gradient(285deg, rgba(96, 58, 23, 0.20) 0%, rgba(0, 0, 0, 0.00) 60.65%), #2A2827', cardHover: diff --git a/packages/ui/src/themes/theme.styled.ts b/packages/ui/src/themes/theme.styled.ts index 556173246..8914cd7fb 100644 --- a/packages/ui/src/themes/theme.styled.ts +++ b/packages/ui/src/themes/theme.styled.ts @@ -64,6 +64,7 @@ export const theme = { selected: colors.boxShadow.selected, }, gradients: { + cardDefault: colors.gradients.cardDefault, cardSelected: colors.gradients.cardSelected, cardHover: colors.gradients.cardHover, title: colors.gradients.title, @@ -114,6 +115,8 @@ export const theme = { danger: colors.background.danger, error: colors.background.error, silver: colors.background.silver, + cardDisabled: colors.background.cardDisabled, + cardSelected: colors.background.cardSelected, timer: { main: colors.background.timer.main, default: colors.background.timer.default, From a796fc5e8c1b4a3f9488d938d18d0d2ec3287917 Mon Sep 17 00:00:00 2001 From: AryanCypherock Date: Mon, 2 Sep 2024 19:58:46 +0530 Subject: [PATCH 31/96] feat: inheritance pin recovery dialog (#526) * feat: inheritance recovery pin dialog added * feat: sidebar and device required dialog added * fix: dialog state changed * chore: review changes * fix: revert error dialog * feat: added images and animations in the dialogs * chore: review changes --- .../src/i18n/lang/ar-AE.json | 22 +++- .../src/i18n/lang/de-DE.json | 22 +++- .../src/i18n/lang/en.json | 24 +++- .../src/i18n/lang/id-ID.json | 22 +++- .../src/i18n/lang/zh-CN.json | 22 +++- .../cysync-core-constants/src/i18n/types.ts | 20 +++- .../PinRecovery/Dialogs/DecryptPin.tsx | 15 +++ .../PinRecovery/Dialogs/FetchData.tsx | 2 +- .../PinRecovery/Dialogs/VerifyOTP.tsx | 53 +++++++++ .../PinRecovery/Dialogs/ViewPin.tsx | 2 + .../PinRecovery/Dialogs/WalletAuth.tsx | 108 ++++++++++++++++++ .../Inheritance/PinRecovery/Dialogs/index.tsx | 2 + .../Inheritance/PinRecovery/context/index.tsx | 32 +++++- .../dialogs/Inheritance/PinRecovery/index.tsx | 82 +++++++------ 14 files changed, 364 insertions(+), 64 deletions(-) create mode 100644 packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/VerifyOTP.tsx create mode 100644 packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/WalletAuth.tsx diff --git a/packages/cysync-core-constants/src/i18n/lang/ar-AE.json b/packages/cysync-core-constants/src/i18n/lang/ar-AE.json index 8d017c081..578fc04e4 100644 --- a/packages/cysync-core-constants/src/i18n/lang/ar-AE.json +++ b/packages/cysync-core-constants/src/i18n/lang/ar-AE.json @@ -1581,16 +1581,30 @@ }, "inheritancePinRecovery": { "title": "Pin Recovery", - "fetch": { + "sync": { "name": "Syncing", - "title": "Please wait while we fetch the encrypted PIN", - "subTitle": "Do no disconnect or close the app" + "walletAuth": { + "title": "Follow instructions on the X1 Vault to authenticate the selected wallet", + "subTitle": "Authenticating the wallet ", + "actions": { + "confirmAuth": "Confirm wallet authentication on device", + "enterPinAndTapCard": "Enter Pin and tap any one card" + } + }, + "verifyOtp": { + "title": "OTP verification" + }, + "fetch": { + "title": "Please wait while we fetch the encrypted PIN", + "subTitle": "Do no disconnect or close the app" + } }, "decryptPin": { "name": "Decryption", "title": "Follow instructions on the X1 Vault", "actions": { - "tapCard": "Tap a valid card" + "confirmOnDevice": "Confirm decryption on device", + "tapCard": "Tap any one card" }, "messageBox": { "warning": "The PIN will be decrypted by the card" diff --git a/packages/cysync-core-constants/src/i18n/lang/de-DE.json b/packages/cysync-core-constants/src/i18n/lang/de-DE.json index 1d6ed29ef..2ac000bb4 100644 --- a/packages/cysync-core-constants/src/i18n/lang/de-DE.json +++ b/packages/cysync-core-constants/src/i18n/lang/de-DE.json @@ -1586,16 +1586,30 @@ }, "inheritancePinRecovery": { "title": "Pin Recovery", - "fetch": { + "sync": { "name": "Syncing", - "title": "Please wait while we fetch the encrypted PIN", - "subTitle": "Do no disconnect or close the app" + "walletAuth": { + "title": "Follow instructions on the X1 Vault to authenticate the selected wallet", + "subTitle": "Authenticating the wallet ", + "actions": { + "confirmAuth": "Confirm wallet authentication on device", + "enterPinAndTapCard": "Enter Pin and tap any one card" + } + }, + "verifyOtp": { + "title": "OTP verification" + }, + "fetch": { + "title": "Please wait while we fetch the encrypted PIN", + "subTitle": "Do no disconnect or close the app" + } }, "decryptPin": { "name": "Decryption", "title": "Follow instructions on the X1 Vault", "actions": { - "tapCard": "Tap a valid card" + "confirmOnDevice": "Confirm decryption on device", + "tapCard": "Tap any one card" }, "messageBox": { "warning": "The PIN will be decrypted by the card" diff --git a/packages/cysync-core-constants/src/i18n/lang/en.json b/packages/cysync-core-constants/src/i18n/lang/en.json index 98bf5b799..8415e91c1 100644 --- a/packages/cysync-core-constants/src/i18n/lang/en.json +++ b/packages/cysync-core-constants/src/i18n/lang/en.json @@ -1586,16 +1586,30 @@ }, "inheritancePinRecovery": { "title": "Pin Recovery", - "fetch": { + "sync": { "name": "Syncing", - "title": "Please wait while we fetch the encrypted PIN", - "subTitle": "Do no disconnect or close the app" + "walletAuth": { + "title": "Follow instructions on the X1 Vault to authenticate the selected wallet", + "subTitle": "Authenticating the wallet ", + "actions": { + "confirmAuth": "Confirm wallet authentication on device", + "enterPinAndTapCard": "Enter Pin and tap any one card" + } + }, + "verifyOtp": { + "title": "OTP verification" + }, + "fetch": { + "title": "Please wait while we fetch the encrypted PIN", + "subTitle": "Do no disconnect or close the app" + } }, "decryptPin": { "name": "Decryption", "title": "Follow instructions on the X1 Vault", "actions": { - "tapCard": "Tap a valid card" + "confirmOnDevice": "Confirm decryption on device", + "tapCard": "Tap any one card" }, "messageBox": { "warning": "The PIN will be decrypted by the card" @@ -1606,7 +1620,7 @@ } }, "viewPin": { - "name": "View pin", + "name": "View PIN", "title": "Follow instructions on the X1 Vault", "actions": { "viewDevice": "View the PIN on your device and confirm" diff --git a/packages/cysync-core-constants/src/i18n/lang/id-ID.json b/packages/cysync-core-constants/src/i18n/lang/id-ID.json index 9d0350c93..7abc20b7a 100644 --- a/packages/cysync-core-constants/src/i18n/lang/id-ID.json +++ b/packages/cysync-core-constants/src/i18n/lang/id-ID.json @@ -1586,16 +1586,30 @@ }, "inheritancePinRecovery": { "title": "Pin Recovery", - "fetch": { + "sync": { "name": "Syncing", - "title": "Please wait while we fetch the encrypted PIN", - "subTitle": "Do no disconnect or close the app" + "walletAuth": { + "title": "Follow instructions on the X1 Vault to authenticate the selected wallet", + "subTitle": "Authenticating the wallet ", + "actions": { + "confirmAuth": "Confirm wallet authentication on device", + "enterPinAndTapCard": "Enter Pin and tap any one card" + } + }, + "verifyOtp": { + "title": "OTP verification" + }, + "fetch": { + "title": "Please wait while we fetch the encrypted PIN", + "subTitle": "Do no disconnect or close the app" + } }, "decryptPin": { "name": "Decryption", "title": "Follow instructions on the X1 Vault", "actions": { - "tapCard": "Tap a valid card" + "confirmOnDevice": "Confirm decryption on device", + "tapCard": "Tap any one card" }, "messageBox": { "warning": "The PIN will be decrypted by the card" diff --git a/packages/cysync-core-constants/src/i18n/lang/zh-CN.json b/packages/cysync-core-constants/src/i18n/lang/zh-CN.json index 4de46792c..cc5fe0f24 100644 --- a/packages/cysync-core-constants/src/i18n/lang/zh-CN.json +++ b/packages/cysync-core-constants/src/i18n/lang/zh-CN.json @@ -1561,16 +1561,30 @@ }, "inheritancePinRecovery": { "title": "Pin Recovery", - "fetch": { + "sync": { "name": "Syncing", - "title": "Please wait while we fetch the encrypted PIN", - "subTitle": "Do no disconnect or close the app" + "walletAuth": { + "title": "Follow instructions on the X1 Vault to authenticate the selected wallet", + "subTitle": "Authenticating the wallet ", + "actions": { + "confirmAuth": "Confirm wallet authentication on device", + "enterPinAndTapCard": "Enter Pin and tap any one card" + } + }, + "verifyOtp": { + "title": "OTP verification" + }, + "fetch": { + "title": "Please wait while we fetch the encrypted PIN", + "subTitle": "Do no disconnect or close the app" + } }, "decryptPin": { "name": "Decryption", "title": "Follow instructions on the X1 Vault", "actions": { - "tapCard": "Tap a valid card" + "confirmOnDevice": "Confirm decryption on device", + "tapCard": "Tap any one card" }, "messageBox": { "warning": "The PIN will be decrypted by the card" diff --git a/packages/cysync-core-constants/src/i18n/types.ts b/packages/cysync-core-constants/src/i18n/types.ts index 64754cfdc..bd9a6af7e 100644 --- a/packages/cysync-core-constants/src/i18n/types.ts +++ b/packages/cysync-core-constants/src/i18n/types.ts @@ -857,15 +857,29 @@ interface LangDialogs { }; inheritancePinRecovery: { title: string; - fetch: { + sync: { name: string; - title: string; - subTitle: string; + walletAuth: { + title: string; + subTitle: string; + actions: { + confirmAuth: string; + enterPinAndTapCard: string; + }; + }; + verifyOtp: { + title: string; + }; + fetch: { + title: string; + subTitle: string; + }; }; decryptPin: { name: string; title: string; actions: { + confirmOnDevice: string; tapCard: string; }; messageBox: { diff --git a/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/DecryptPin.tsx b/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/DecryptPin.tsx index 71d9eb180..934c306e1 100644 --- a/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/DecryptPin.tsx +++ b/packages/cysync-core/src/dialogs/Inheritance/PinRecovery/Dialogs/DecryptPin.tsx @@ -8,8 +8,10 @@ import { LeanBoxContainer, LeanBoxProps, MessageBox, + tapAnyCardDeviceAnimation2DVideo, Throbber, Typography, + Video, } from '@cypherock/cysync-ui'; import React, { useEffect } from 'react'; @@ -48,6 +50,12 @@ export const DecryptPin = () => { const actions: LeanBoxProps[] = [ { id: '1', + text: strings.decryptPin.actions.confirmOnDevice, + leftImage: rightArrowIcon, + rightImage: getDeviceEventIcon(0, 1), + }, + { + id: '2', text: strings.decryptPin.actions.tapCard, leftImage: rightArrowIcon, rightImage: getDeviceEventIcon(0, 1), @@ -83,6 +91,13 @@ export const DecryptPin = () => { return ( +