From 3738bb0b02291542fc69b977aafbbb143c458b3f Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Sat, 1 May 2021 19:02:07 +0900 Subject: [PATCH 01/91] Polishing Sidebar --- .../organisms/settings/SettingsComponent.tsx | 48 ++++++++----------- .../organisms/settings/TabButton.tsx | 12 ++--- 2 files changed, 24 insertions(+), 36 deletions(-) diff --git a/src/cloud/components/organisms/settings/SettingsComponent.tsx b/src/cloud/components/organisms/settings/SettingsComponent.tsx index c2dfa58df9..ac34e0a9a8 100644 --- a/src/cloud/components/organisms/settings/SettingsComponent.tsx +++ b/src/cloud/components/organisms/settings/SettingsComponent.tsx @@ -11,16 +11,7 @@ import { import { baseIconStyle } from '../../../lib/styled/styleFunctions' import { useTranslation } from 'react-i18next' import Icon from '../../atoms/IconMdi' -import { - mdiClose, - mdiDomain, - mdiAccountGroup, - mdiProfessionalHexagon, - mdiThemeLightDark, - mdiFlash, - mdiKey, - mdiAccountCircleOutline, -} from '@mdi/js' +import { mdiClose, mdiDomain, mdiAccountCircleOutline } from '@mdi/js' import { StyledModals, StyledModalsBackground, @@ -43,6 +34,7 @@ import IntegrationsTab from './IntegrationsTab' import PreferencesTab from './PreferencesTab' import ApiTab from './ApiTab' import { PageStoreWithTeam } from '../../../interfaces/pageStore' +import IconMdi from '../../atoms/IconMdi' const SettingsComponent = () => { const { t } = useTranslation() @@ -140,22 +132,26 @@ const SettingsComponent = () => { - Account + + + Account + - Space + + + Space + {currentUserPermissions != null && ( <> { active={settingsTab === 'teamInfo'} tab='teamInfo' id='settings-teamInfoTab-btn' - prependIcon={mdiDomain} /> )} @@ -199,7 +191,6 @@ const SettingsComponent = () => { active={settingsTab === 'teamUpgrade'} tab='teamUpgrade' id='settings-teamUpgradeTab-btn' - prependIcon={mdiProfessionalHexagon} /> ) : ( { active={settingsTab === 'teamSubscription'} tab='teamSubscription' id='settings-teamBillingTab-btn' - prependIcon={mdiProfessionalHexagon} /> )} @@ -227,11 +217,10 @@ const SettingsComponent = () => { const TabNav = styled.nav` width: 250px; - padding: 0; + padding: ${({ theme }) => theme.space.xsmall}px; overflow: hidden auto; margin-right: 0; margin-bottom: 0; - padding: ${({ theme }) => theme.space.xsmall}px 0; hr { height: 1px; @@ -242,13 +231,16 @@ const TabNav = styled.nav` ` const Subtitle = styled.div` + display: flex; + align-items: center; margin: ${({ theme }) => theme.space.default}px - ${({ theme }) => theme.space.default}px - ${({ theme }) => theme.space.xsmall}px; - color: ${({ theme }) => theme.baseTextColor}; - font-size: ${({ theme }) => theme.fontSizes.large}px; - text-transform: uppercase; - font-weight: 500; + ${({ theme }) => theme.space.small}px ${({ theme }) => theme.space.xsmall}px; + color: ${({ theme }) => theme.subtleTextColor}; + font-size: ${({ theme }) => theme.fontSizes.default}px; + + svg { + margin-right: ${({ theme }) => theme.space.xxsmall}px; + } ` const TabContent = styled.div` diff --git a/src/cloud/components/organisms/settings/TabButton.tsx b/src/cloud/components/organisms/settings/TabButton.tsx index b31b148f43..914ed8a45d 100644 --- a/src/cloud/components/organisms/settings/TabButton.tsx +++ b/src/cloud/components/organisms/settings/TabButton.tsx @@ -2,17 +2,15 @@ import React, { useCallback } from 'react' import styled from '../../../lib/styled' import { SettingsTab, useSettings } from '../../../lib/stores/settings' import cc from 'classcat' -import IconMdi from '../../atoms/IconMdi' interface TabButtonProps { label: string active: boolean tab: SettingsTab id?: string - prependIcon: string } -const TabButton = ({ label, tab, active, id, prependIcon }: TabButtonProps) => { +const TabButton = ({ label, tab, active, id }: TabButtonProps) => { const { openSettingsTab } = useSettings() const onClickHandler = useCallback(() => { openSettingsTab(tab) @@ -24,9 +22,6 @@ const TabButton = ({ label, tab, active, id, prependIcon }: TabButtonProps) => { className={cc([active && 'active'])} id={id} > - - - {label} ) @@ -39,10 +34,11 @@ const StyledButton = styled.button` align-items: center; width: 100%; padding: ${({ theme }) => theme.space.xsmall}px - ${({ theme }) => theme.space.small}px; + ${({ theme }) => theme.space.medium}px; background-color: transparent; border: none; - color: ${({ theme }) => theme.subtleTextColor}; + border-radius: 4px; + color: ${({ theme }) => theme.baseTextColor}; cursor: pointer; .icon { From c95a1bf533215228f67003343763621bc62a80eb Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 3 May 2021 17:23:23 +0900 Subject: [PATCH 02/91] Created Settings Layout --- .../components/molecules/SettingsLayout.tsx | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 src/shared/components/molecules/SettingsLayout.tsx diff --git a/src/shared/components/molecules/SettingsLayout.tsx b/src/shared/components/molecules/SettingsLayout.tsx new file mode 100644 index 0000000000..9e5467e3eb --- /dev/null +++ b/src/shared/components/molecules/SettingsLayout.tsx @@ -0,0 +1,60 @@ +import React from 'react' +import styled from '../../lib/styled' +import GlobalStyle from '../atoms/GlobalStyle' + +interface SettingsLayoutProps { + sidebar: React.ReactNode + pageBody: React.ReactNode +} + +const SettingsLayout = ({ sidebar, pageBody }: SettingsLayoutProps) => ( + +
+
{sidebar}
+
+
{pageBody}
+
+ +
+) + +const zIndexModals = 8001 + +const Container = styled.div` + z-index: ${zIndexModals}; + display: flex; + align-items: center; + justify-content: center; + position: fixed; + left: 40px; + top: 0; + right: 0; + bottom: 0; + background-color: ${({ theme }) => theme.colors.background.primary}; + overflow: hidden; + + .settings__wrapper { + z-index: ${zIndexModals + 2}; + display: flex; + position: relative; + width: 100%; + height: 100%; + max-width: 100%; + max-height: 100%; + overflow: auto; + } + + .settings__divider { + width: 1px; + height: 100%; + background-color: ${({ theme }) => theme.colors.border.main}; + } + + .settings__content { + flex: 1 1 100%; + min-width: 0; + overflow: auto; + } +` + +export default SettingsLayout From 6c351bd5e46d43a83be1e0e9ce1f2da2a3b263b9 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 3 May 2021 17:43:42 +0900 Subject: [PATCH 03/91] Adding SettingsLayout --- .../organisms/settings/SettingsComponent.tsx | 193 +++++++----------- 1 file changed, 76 insertions(+), 117 deletions(-) diff --git a/src/cloud/components/organisms/settings/SettingsComponent.tsx b/src/cloud/components/organisms/settings/SettingsComponent.tsx index ac34e0a9a8..eddb1a96f0 100644 --- a/src/cloud/components/organisms/settings/SettingsComponent.tsx +++ b/src/cloud/components/organisms/settings/SettingsComponent.tsx @@ -8,16 +8,8 @@ import { useCapturingGlobalKeyDownHandler, isSingleKeyEventOutsideOfInput, } from '../../../lib/keyboard' -import { baseIconStyle } from '../../../lib/styled/styleFunctions' import { useTranslation } from 'react-i18next' -import Icon from '../../atoms/IconMdi' -import { mdiClose, mdiDomain, mdiAccountCircleOutline } from '@mdi/js' -import { - StyledModals, - StyledModalsBackground, - StyledModalsContainer, - StyledSideNavModal, -} from '../Modal/styled' +import { mdiDomain, mdiAccountCircleOutline } from '@mdi/js' import PersonalInfoTab from './PersonalInfoTab' import { usePage } from '../../../lib/stores/pageStore' import TeamInfoTab from './TeamInfoTab' @@ -35,6 +27,7 @@ import PreferencesTab from './PreferencesTab' import ApiTab from './ApiTab' import { PageStoreWithTeam } from '../../../interfaces/pageStore' import IconMdi from '../../atoms/IconMdi' +import SettingsLayout from '../../../../shared/components/molecules/SettingsLayout' const SettingsComponent = () => { const { t } = useTranslation() @@ -108,13 +101,6 @@ const SettingsComponent = () => { } }, [settingsTab, currentUserPermissions]) - const backgroundClickHandler = useMemo(() => { - return (event: MouseEvent) => { - event.preventDefault() - toggleClosed() - } - }, [toggleClosed]) - useEffect(() => { if (closed) { return @@ -127,91 +113,83 @@ const SettingsComponent = () => { } return ( - - - - - - - - Account - - - - - - Space - - {currentUserPermissions != null && ( + + + + Account + + + + + + Space + + {currentUserPermissions != null && ( + <> + + + + + + )} + {team != null && + currentUserPermissions != null && + currentUserPermissions.role === 'admin' && ( <> - - - - + {subscription == null || subscription.status === 'trialing' ? ( + + ) : ( + + )} + )} - {team != null && - currentUserPermissions != null && - currentUserPermissions.role === 'admin' && ( - <> - {subscription == null || - subscription.status === 'trialing' ? ( - - ) : ( - - )} - - - )} - - - - {content} - - - - - +
+ } + pageBody={{content}} + > ) } @@ -249,23 +227,4 @@ const TabContent = styled.div` position: relative; ` -const CloseButton = styled.button` - ${baseIconStyle} - position: absolute; - top: ${({ theme }) => theme.space.small}px; - right: ${({ theme }) => theme.space.small}px; - width: 40px; - height: 40px; - background-color: transparent; - border: none; - cursor: pointer; - font-size: ${({ theme }) => theme.fontSizes.xxlarge}px; -` - -const DividerBorder = styled.div` - width: 1px; - height: 100%; - background-color: ${({ theme }) => theme.baseBorderColor}; -` - export default SettingsComponent From 683fa94edcc0bc14a44f3ae038af6d6ae47faaeb Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 3 May 2021 18:41:57 +0900 Subject: [PATCH 04/91] Removed GlobalStyle --- src/shared/components/molecules/SettingsLayout.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/shared/components/molecules/SettingsLayout.tsx b/src/shared/components/molecules/SettingsLayout.tsx index 9e5467e3eb..611de74710 100644 --- a/src/shared/components/molecules/SettingsLayout.tsx +++ b/src/shared/components/molecules/SettingsLayout.tsx @@ -1,6 +1,5 @@ import React from 'react' import styled from '../../lib/styled' -import GlobalStyle from '../atoms/GlobalStyle' interface SettingsLayoutProps { sidebar: React.ReactNode @@ -14,7 +13,6 @@ const SettingsLayout = ({ sidebar, pageBody }: SettingsLayoutProps) => (
{pageBody}
- ) From 2ef400410394176516a9156a5b8085be56f83f64 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 3 May 2021 19:47:43 +0900 Subject: [PATCH 05/91] Removed Styled Components --- .../organisms/settings/SettingsComponent.tsx | 79 ++++++------------- 1 file changed, 22 insertions(+), 57 deletions(-) diff --git a/src/cloud/components/organisms/settings/SettingsComponent.tsx b/src/cloud/components/organisms/settings/SettingsComponent.tsx index eddb1a96f0..4bf7fc6cbf 100644 --- a/src/cloud/components/organisms/settings/SettingsComponent.tsx +++ b/src/cloud/components/organisms/settings/SettingsComponent.tsx @@ -1,7 +1,6 @@ import React, { useMemo, useEffect } from 'react' -import styled from '../../../lib/styled' import { useSettings } from '../../../lib/stores/settings' -import TabButton from './TabButton' +import SettingSidenavItem from './SettingSidenavItem' import { preventKeyboardEventPropagation, useUpDownNavigationListener, @@ -26,8 +25,10 @@ import IntegrationsTab from './IntegrationsTab' import PreferencesTab from './PreferencesTab' import ApiTab from './ApiTab' import { PageStoreWithTeam } from '../../../interfaces/pageStore' -import IconMdi from '../../atoms/IconMdi' -import SettingsLayout from '../../../../shared/components/molecules/SettingsLayout' +import SettingsLayout from '../../../../shared/components/organisms/Settings/molecles/SettingsLayout' +import SettingSidenavHeader from '../../../../shared/components/organisms/Settings/atoms/SettingSidenavHeader' +import SettingSidenav from '../../../../shared/components/organisms/Settings/atoms/SettingSidenav' +import SettingContent from '../../../../shared/components/organisms/Settings/atoms/SettingContent' const SettingsComponent = () => { const { t } = useTranslation() @@ -115,48 +116,46 @@ const SettingsComponent = () => { return ( - - - Account - - + + - - - - Space - + {currentUserPermissions != null && ( <> - - - - { currentUserPermissions.role === 'admin' && ( <> {subscription == null || subscription.status === 'trialing' ? ( - ) : ( - { )} - + } - pageBody={{content}} + content={{content}} > ) } -const TabNav = styled.nav` - width: 250px; - padding: ${({ theme }) => theme.space.xsmall}px; - overflow: hidden auto; - margin-right: 0; - margin-bottom: 0; - - hr { - height: 1px; - background-color: ${({ theme }) => theme.baseBorderColor}; - border: none; - margin: ${({ theme }) => theme.space.small}px 0; - } -` - -const Subtitle = styled.div` - display: flex; - align-items: center; - margin: ${({ theme }) => theme.space.default}px - ${({ theme }) => theme.space.small}px ${({ theme }) => theme.space.xsmall}px; - color: ${({ theme }) => theme.subtleTextColor}; - font-size: ${({ theme }) => theme.fontSizes.default}px; - - svg { - margin-right: ${({ theme }) => theme.space.xxsmall}px; - } -` - -const TabContent = styled.div` - flex: 1; - overflow: hidden auto; - position: relative; -` - export default SettingsComponent From 690b4fe87615f744a6391fd0bc8e115781caa486 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 3 May 2021 19:48:16 +0900 Subject: [PATCH 06/91] Added SettingSidenavItem --- .../organisms/settings/SettingSidenavItem.tsx | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/cloud/components/organisms/settings/SettingSidenavItem.tsx diff --git a/src/cloud/components/organisms/settings/SettingSidenavItem.tsx b/src/cloud/components/organisms/settings/SettingSidenavItem.tsx new file mode 100644 index 0000000000..e92845e45e --- /dev/null +++ b/src/cloud/components/organisms/settings/SettingSidenavItem.tsx @@ -0,0 +1,35 @@ +import React, { useCallback } from 'react' +import { SettingsTab, useSettings } from '../../../lib/stores/settings' +import cc from 'classcat' +import SettingTabButton from '../../../../shared/components/organisms/Settings/atoms/SettingTabButton' + +interface SettingSidenavItemProps { + label: string + active: boolean + tab: SettingsTab + id?: string +} + +const SettingSidenavItem = ({ + label, + tab, + active, + id, +}: SettingSidenavItemProps) => { + const { openSettingsTab } = useSettings() + const onClickHandler = useCallback(() => { + openSettingsTab(tab) + }, [openSettingsTab, tab]) + + return ( + + {label} + + ) +} + +export default SettingSidenavItem From cd312fda616b25c5f4fb2d4d850f64f65a359b21 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 3 May 2021 19:48:37 +0900 Subject: [PATCH 07/91] Removed TabButton --- .../organisms/settings/TabButton.tsx | 67 ------------------- 1 file changed, 67 deletions(-) delete mode 100644 src/cloud/components/organisms/settings/TabButton.tsx diff --git a/src/cloud/components/organisms/settings/TabButton.tsx b/src/cloud/components/organisms/settings/TabButton.tsx deleted file mode 100644 index 914ed8a45d..0000000000 --- a/src/cloud/components/organisms/settings/TabButton.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import React, { useCallback } from 'react' -import styled from '../../../lib/styled' -import { SettingsTab, useSettings } from '../../../lib/stores/settings' -import cc from 'classcat' - -interface TabButtonProps { - label: string - active: boolean - tab: SettingsTab - id?: string -} - -const TabButton = ({ label, tab, active, id }: TabButtonProps) => { - const { openSettingsTab } = useSettings() - const onClickHandler = useCallback(() => { - openSettingsTab(tab) - }, [openSettingsTab, tab]) - - return ( - - {label} - - ) -} - -export default TabButton - -const StyledButton = styled.button` - display: flex; - align-items: center; - width: 100%; - padding: ${({ theme }) => theme.space.xsmall}px - ${({ theme }) => theme.space.medium}px; - background-color: transparent; - border: none; - border-radius: 4px; - color: ${({ theme }) => theme.baseTextColor}; - cursor: pointer; - - .icon { - margin-left: ${({ theme }) => theme.space.small}px; - margin-right: ${({ theme }) => theme.space.xsmall}px; - - svg { - vertical-align: sub; - } - } - - .label { - font-size: ${({ theme }) => theme.fontSizes.small}px; - text-align: left; - } - - &.active, - &:hover, - &:focus { - color: ${({ theme }) => theme.emphasizedTextColor}; - } - - &.active { - background-color: ${({ theme }) => theme.emphasizedBackgroundColor}; - } -` From 545df890f4fb5b312e1d6d15f7d70080c669dbdb Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 3 May 2021 19:49:06 +0900 Subject: [PATCH 08/91] Moved SettingsLayout --- .../Settings/molecles}/SettingsLayout.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename src/shared/components/{molecules => organisms/Settings/molecles}/SettingsLayout.tsx (84%) diff --git a/src/shared/components/molecules/SettingsLayout.tsx b/src/shared/components/organisms/Settings/molecles/SettingsLayout.tsx similarity index 84% rename from src/shared/components/molecules/SettingsLayout.tsx rename to src/shared/components/organisms/Settings/molecles/SettingsLayout.tsx index 611de74710..779cb7a8a9 100644 --- a/src/shared/components/molecules/SettingsLayout.tsx +++ b/src/shared/components/organisms/Settings/molecles/SettingsLayout.tsx @@ -1,17 +1,17 @@ import React from 'react' -import styled from '../../lib/styled' +import styled from '../../../../lib/styled' interface SettingsLayoutProps { sidebar: React.ReactNode - pageBody: React.ReactNode + content: React.ReactNode } -const SettingsLayout = ({ sidebar, pageBody }: SettingsLayoutProps) => ( +const SettingsLayout = ({ sidebar, content }: SettingsLayoutProps) => (
{sidebar}
-
{pageBody}
+
{content}
) From c6ce86e10936b620be31bcd25289bb8a7de6fe05 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 3 May 2021 19:49:22 +0900 Subject: [PATCH 09/91] Removed Styled Component --- src/components/PreferencesModal/TabButton.tsx | 38 ++----------------- 1 file changed, 3 insertions(+), 35 deletions(-) diff --git a/src/components/PreferencesModal/TabButton.tsx b/src/components/PreferencesModal/TabButton.tsx index cbaa4c6022..d107aa48c0 100644 --- a/src/components/PreferencesModal/TabButton.tsx +++ b/src/components/PreferencesModal/TabButton.tsx @@ -1,6 +1,6 @@ import React, { useCallback } from 'react' -import styled from '../../lib/styled' import cc from 'classcat' +import SettingTabButton from '../../shared/components/organisms/Settings/atoms/SettingTabButton' interface TabButtonProps { label: string @@ -9,46 +9,14 @@ interface TabButtonProps { setTab: (tab: string) => void } -const StyledButton = styled.button` - width: 100%; - border-radius: 4px; - height: 30px; - background-color: ${({ theme }) => theme.navItemBackgroundColor}; - border: none; - cursor: pointer; - display: flex; - align-items: center; - margin-bottom: 5px; - - .label { - flex: 1; - color: ${({ theme }) => theme.navItemColor}; - text-align: left; - padding-left: 15px; - font-size: 14px; - } - &:hover { - background-color: ${({ theme }) => theme.navItemHoverBackgroundColor}; - } - &.active { - color: ${({ theme }) => theme.textColor}; - background-color: ${({ theme }) => theme.navItemActiveBackgroundColor}; - - .label { - color: ${({ theme }) => theme.textColor}; - color: ${({ theme }) => theme.navItemActiveColor}; - } - } -` - const TabButton = ({ label, tab, setTab, active }: TabButtonProps) => { const selectTab = useCallback(() => { setTab(tab) }, [tab, setTab]) return ( - +
{label}
-
+ ) } From 8810d4c6f7c1e32e51b2d37d6db03059e445cf30 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 3 May 2021 19:49:59 +0900 Subject: [PATCH 10/91] Use Same Font Family Set --- src/cloud/lib/styled/themes/shared.ts | 3 +-- src/components/GlobalStyle.tsx | 3 +-- src/lib/preview.ts | 3 +-- src/lib/styled/styleUtil.ts | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/cloud/lib/styled/themes/shared.ts b/src/cloud/lib/styled/themes/shared.ts index 8d6f1f3ff6..c502ca2d88 100644 --- a/src/cloud/lib/styled/themes/shared.ts +++ b/src/cloud/lib/styled/themes/shared.ts @@ -1,8 +1,7 @@ import { InitialTheme } from './types' export const sharedTheme: InitialTheme = { - fontFamily: `-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Fira sans', Roboto, Helvetica, - Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'`, + fontFamily: `Lato, -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif`, fontSizes: { xxxsmall: 8, xxsmall: 10, diff --git a/src/components/GlobalStyle.tsx b/src/components/GlobalStyle.tsx index ddeb0bac82..33735c04ee 100644 --- a/src/components/GlobalStyle.tsx +++ b/src/components/GlobalStyle.tsx @@ -7,8 +7,7 @@ export default createGlobalStyle` margin: 0; ${backgroundColor} ${textColor} - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Fira sans', Roboto, Helvetica, - Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; + font-family: Lato, -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif; font-size: 15px; font-weight: 400; } diff --git a/src/lib/preview.ts b/src/lib/preview.ts index 4457e74e4d..07b13867df 100644 --- a/src/lib/preview.ts +++ b/src/lib/preview.ts @@ -7,8 +7,7 @@ export const defaultPreviewStyle = ` -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; color: #24292e; -font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, - sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol; +font-family: Lato, -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 1.6; word-wrap: break-word; diff --git a/src/lib/styled/styleUtil.ts b/src/lib/styled/styleUtil.ts index fc7d6073ec..c809485915 100644 --- a/src/lib/styled/styleUtil.ts +++ b/src/lib/styled/styleUtil.ts @@ -10,8 +10,7 @@ export const getGlobalCss = (theme: BaseTheme) => ` margin: 10px; background-color: ${theme.backgroundColor}; color: ${theme.textColor}; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Fira sans', Roboto, Helvetica, - Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; + font-family: Lato, -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif; font-size: 15px; font-weight: 400; } From 7bb6ac8ffd32b98c311d4c414f26b1c3dd76ac2b Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 3 May 2021 19:50:17 +0900 Subject: [PATCH 11/91] Added SettingContent --- .../organisms/Settings/atoms/SettingContent.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/shared/components/organisms/Settings/atoms/SettingContent.tsx diff --git a/src/shared/components/organisms/Settings/atoms/SettingContent.tsx b/src/shared/components/organisms/Settings/atoms/SettingContent.tsx new file mode 100644 index 0000000000..c8be1f82b4 --- /dev/null +++ b/src/shared/components/organisms/Settings/atoms/SettingContent.tsx @@ -0,0 +1,9 @@ +import styled from '../../../../../lib/styled' + +const SettingContent = styled.div` + flex: 1; + overflow: hidden auto; + position: relative; +` + +export default SettingContent From f77f99cfef20d66c50d4c30b60090e47ec7227c3 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 3 May 2021 19:50:27 +0900 Subject: [PATCH 12/91] Added SettingSidenav --- .../organisms/Settings/atoms/SettingSidenav.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/shared/components/organisms/Settings/atoms/SettingSidenav.tsx diff --git a/src/shared/components/organisms/Settings/atoms/SettingSidenav.tsx b/src/shared/components/organisms/Settings/atoms/SettingSidenav.tsx new file mode 100644 index 0000000000..2105ef3f4b --- /dev/null +++ b/src/shared/components/organisms/Settings/atoms/SettingSidenav.tsx @@ -0,0 +1,11 @@ +import styled from '../../../../lib/styled' + +const SettingSidenav = styled.nav` + width: 250px; + margin-bottom: 0; + margin-right: 0; + padding: ${({ theme }) => theme.sizes.spaces.sm}px; + overflow: hidden auto; +` + +export default SettingSidenav From 41986660d26aeaeb75b81dedd223f731017b6867 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 3 May 2021 19:50:39 +0900 Subject: [PATCH 13/91] Added SettingSidenavHeader --- .../Settings/atoms/SettingSidenavHeader.tsx | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/shared/components/organisms/Settings/atoms/SettingSidenavHeader.tsx diff --git a/src/shared/components/organisms/Settings/atoms/SettingSidenavHeader.tsx b/src/shared/components/organisms/Settings/atoms/SettingSidenavHeader.tsx new file mode 100644 index 0000000000..363a8fdc3d --- /dev/null +++ b/src/shared/components/organisms/Settings/atoms/SettingSidenavHeader.tsx @@ -0,0 +1,35 @@ +import React from 'react' +import styled from '../../../../lib/styled' +import Icon, { IconSize } from '../../../atoms/Icon' + +interface SettingSidenavHeaderProps { + path: string + text: string + size: IconSize +} + +const SettingSidenavHeader = ({ + path, + text, + size, +}: SettingSidenavHeaderProps) => ( + + + {text} + +) + +const Container = styled.div` + display: flex; + align-items: center; + margin: ${({ theme }) => theme.sizes.spaces.md}px 0 + ${({ theme }) => theme.sizes.spaces.sm}px; + color: ${({ theme }) => theme.colors.text.subtle}; + font-size: ${({ theme }) => theme.sizes.fonts.df}px; + + svg { + margin-right: ${({ theme }) => theme.sizes.spaces.xsm}px; + } +` + +export default SettingSidenavHeader From 2b18f6e7d67c5e4487cf1b7a11a6779278daddc1 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 3 May 2021 19:50:50 +0900 Subject: [PATCH 14/91] Added SettingTabButton --- .../Settings/atoms/SettingTabButton.tsx | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/shared/components/organisms/Settings/atoms/SettingTabButton.tsx diff --git a/src/shared/components/organisms/Settings/atoms/SettingTabButton.tsx b/src/shared/components/organisms/Settings/atoms/SettingTabButton.tsx new file mode 100644 index 0000000000..e006ff8b4b --- /dev/null +++ b/src/shared/components/organisms/Settings/atoms/SettingTabButton.tsx @@ -0,0 +1,28 @@ +import styled from '../../../../lib/styled' + +const SettingTabButton = styled.button` + display: flex; + align-items: center; + width: 100%; + margin-bottom: ${({ theme }) => theme.sizes.spaces.xsm}px; + padding: ${({ theme }) => theme.sizes.spaces.xsm}px + ${({ theme }) => theme.sizes.spaces.md}px; + background-color: transparent; + border: none; + border-radius: 4px; + color: ${({ theme }) => theme.colors.text.primary}; + cursor: pointer; + font-size: ${({ theme }) => theme.sizes.fonts.df}px; + text-align: left; + + &:hover, + &:focus { + background-color: ${({ theme }) => theme.colors.background.tertiary}; + } + + &.active { + background-color: ${({ theme }) => theme.colors.background.quaternary}; + } +` + +export default SettingTabButton From 7dee8090dbaee2ad00938eab71137f5ee1e5c125 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 3 May 2021 20:04:25 +0900 Subject: [PATCH 15/91] Changed the border-radius --- .../components/organisms/Settings/atoms/SettingTabButton.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/components/organisms/Settings/atoms/SettingTabButton.tsx b/src/shared/components/organisms/Settings/atoms/SettingTabButton.tsx index e006ff8b4b..012406df87 100644 --- a/src/shared/components/organisms/Settings/atoms/SettingTabButton.tsx +++ b/src/shared/components/organisms/Settings/atoms/SettingTabButton.tsx @@ -9,7 +9,7 @@ const SettingTabButton = styled.button` ${({ theme }) => theme.sizes.spaces.md}px; background-color: transparent; border: none; - border-radius: 4px; + border-radius: 2px; color: ${({ theme }) => theme.colors.text.primary}; cursor: pointer; font-size: ${({ theme }) => theme.sizes.fonts.df}px; From 86b9c7d724351b66a0f15356b8a13d174810a520 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 3 May 2021 20:10:19 +0900 Subject: [PATCH 16/91] Updated Font Family --- src/cloud/components/atoms/MarkdownView/styles.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cloud/components/atoms/MarkdownView/styles.ts b/src/cloud/components/atoms/MarkdownView/styles.ts index 8adebf93fc..3bc089d315 100644 --- a/src/cloud/components/atoms/MarkdownView/styles.ts +++ b/src/cloud/components/atoms/MarkdownView/styles.ts @@ -114,8 +114,7 @@ export const defaultPreviewStyle = ({ theme }: StyledProps) => ` -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; line-height: 1.6; -font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, - sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol; +font-family: Lato, -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif; font-size: 15px; line-height: 1.6; word-wrap: break-word; From 95f9bc857593f0ff28be5c032dcecab6d8849833 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Sat, 8 May 2021 15:03:42 +0900 Subject: [PATCH 17/91] SettingSidenavItem -> SettingTabButton --- .../organisms/settings/SettingSidenavItem.tsx | 35 ------------------- .../organisms/settings/SettingsComponent.tsx | 27 +++++++++----- .../Settings/atoms/SettingTabButton.tsx | 32 ++++++++++++++++- 3 files changed, 49 insertions(+), 45 deletions(-) delete mode 100644 src/cloud/components/organisms/settings/SettingSidenavItem.tsx diff --git a/src/cloud/components/organisms/settings/SettingSidenavItem.tsx b/src/cloud/components/organisms/settings/SettingSidenavItem.tsx deleted file mode 100644 index e92845e45e..0000000000 --- a/src/cloud/components/organisms/settings/SettingSidenavItem.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import React, { useCallback } from 'react' -import { SettingsTab, useSettings } from '../../../lib/stores/settings' -import cc from 'classcat' -import SettingTabButton from '../../../../shared/components/organisms/Settings/atoms/SettingTabButton' - -interface SettingSidenavItemProps { - label: string - active: boolean - tab: SettingsTab - id?: string -} - -const SettingSidenavItem = ({ - label, - tab, - active, - id, -}: SettingSidenavItemProps) => { - const { openSettingsTab } = useSettings() - const onClickHandler = useCallback(() => { - openSettingsTab(tab) - }, [openSettingsTab, tab]) - - return ( - - {label} - - ) -} - -export default SettingSidenavItem diff --git a/src/cloud/components/organisms/settings/SettingsComponent.tsx b/src/cloud/components/organisms/settings/SettingsComponent.tsx index 4bf7fc6cbf..fdfa22046e 100644 --- a/src/cloud/components/organisms/settings/SettingsComponent.tsx +++ b/src/cloud/components/organisms/settings/SettingsComponent.tsx @@ -1,6 +1,5 @@ import React, { useMemo, useEffect } from 'react' import { useSettings } from '../../../lib/stores/settings' -import SettingSidenavItem from './SettingSidenavItem' import { preventKeyboardEventPropagation, useUpDownNavigationListener, @@ -29,12 +28,14 @@ import SettingsLayout from '../../../../shared/components/organisms/Settings/mol import SettingSidenavHeader from '../../../../shared/components/organisms/Settings/atoms/SettingSidenavHeader' import SettingSidenav from '../../../../shared/components/organisms/Settings/atoms/SettingSidenav' import SettingContent from '../../../../shared/components/organisms/Settings/atoms/SettingContent' +import SettingTabButton from '../../../../shared/components/organisms/Settings/atoms/SettingTabButton' const SettingsComponent = () => { const { t } = useTranslation() const { closed, toggleClosed, settingsTab } = useSettings() const contentSideRef = React.createRef() const menuRef = React.createRef() + const { openSettingsTab } = useSettings() const { team, subscription, currentUserPermissions } = usePage< PageStoreWithTeam >() @@ -122,44 +123,50 @@ const SettingsComponent = () => { text={'Account'} size={16} /> - openSettingsTab('personalInfo')} /> - openSettingsTab('preferences')} /> {currentUserPermissions != null && ( <> - openSettingsTab('teamInfo')} /> - openSettingsTab('teamMembers')} /> - openSettingsTab('integrations')} /> - openSettingsTab('api')} /> )} @@ -168,18 +175,20 @@ const SettingsComponent = () => { currentUserPermissions.role === 'admin' && ( <> {subscription == null || subscription.status === 'trialing' ? ( - openSettingsTab('teamUpgrade')} /> ) : ( - openSettingsTab('teamSubscription')} /> )} diff --git a/src/shared/components/organisms/Settings/atoms/SettingTabButton.tsx b/src/shared/components/organisms/Settings/atoms/SettingTabButton.tsx index 012406df87..b9eafcdbbf 100644 --- a/src/shared/components/organisms/Settings/atoms/SettingTabButton.tsx +++ b/src/shared/components/organisms/Settings/atoms/SettingTabButton.tsx @@ -1,6 +1,36 @@ +import React, { MouseEventHandler } from 'react' +import cc from 'classcat' import styled from '../../../../lib/styled' +import { SettingsTab } from '../../../../../cloud/lib/stores/settings' -const SettingTabButton = styled.button` +interface SettingTabButtonProps { + label: string + active: boolean + tab: SettingsTab + id?: string + onClick?: MouseEventHandler +} + +const SettingTabButton = ({ + label, + tab, + active, + id, + onClick, +}: SettingTabButtonProps) => { + return ( + + {label} + + ) +} + +const Container = styled.button` display: flex; align-items: center; width: 100%; From 214d14e558e4e998521b8b94d113911f3ad8475d Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Sat, 8 May 2021 15:13:07 +0900 Subject: [PATCH 18/91] SettingsLayout -> Settings --- .../components/organisms/settings/SettingsComponent.tsx | 6 +++--- .../Settings/{molecles/SettingsLayout.tsx => index.tsx} | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) rename src/shared/components/organisms/Settings/{molecles/SettingsLayout.tsx => index.tsx} (85%) diff --git a/src/cloud/components/organisms/settings/SettingsComponent.tsx b/src/cloud/components/organisms/settings/SettingsComponent.tsx index fdfa22046e..6a1e32a645 100644 --- a/src/cloud/components/organisms/settings/SettingsComponent.tsx +++ b/src/cloud/components/organisms/settings/SettingsComponent.tsx @@ -24,7 +24,7 @@ import IntegrationsTab from './IntegrationsTab' import PreferencesTab from './PreferencesTab' import ApiTab from './ApiTab' import { PageStoreWithTeam } from '../../../interfaces/pageStore' -import SettingsLayout from '../../../../shared/components/organisms/Settings/molecles/SettingsLayout' +import Settings from '../../../../shared/components/organisms/Settings' import SettingSidenavHeader from '../../../../shared/components/organisms/Settings/atoms/SettingSidenavHeader' import SettingSidenav from '../../../../shared/components/organisms/Settings/atoms/SettingSidenav' import SettingContent from '../../../../shared/components/organisms/Settings/atoms/SettingContent' @@ -115,7 +115,7 @@ const SettingsComponent = () => { } return ( - { } content={{content}} - > + > ) } diff --git a/src/shared/components/organisms/Settings/molecles/SettingsLayout.tsx b/src/shared/components/organisms/Settings/index.tsx similarity index 85% rename from src/shared/components/organisms/Settings/molecles/SettingsLayout.tsx rename to src/shared/components/organisms/Settings/index.tsx index 779cb7a8a9..cb44067480 100644 --- a/src/shared/components/organisms/Settings/molecles/SettingsLayout.tsx +++ b/src/shared/components/organisms/Settings/index.tsx @@ -1,12 +1,12 @@ import React from 'react' -import styled from '../../../../lib/styled' +import styled from '../../../lib/styled' -interface SettingsLayoutProps { +interface SettingsProps { sidebar: React.ReactNode content: React.ReactNode } -const SettingsLayout = ({ sidebar, content }: SettingsLayoutProps) => ( +const Settings = ({ sidebar, content }: SettingsProps) => (
{sidebar}
@@ -55,4 +55,4 @@ const Container = styled.div` } ` -export default SettingsLayout +export default Settings From 66a59eaeb71c2a9cb4623ae955d3683209a3ac10 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Sat, 8 May 2021 15:36:33 +0900 Subject: [PATCH 19/91] Removed TabButton --- .../PreferencesModal/PreferencesModal.tsx | 42 ++++++++++--------- src/components/PreferencesModal/TabButton.tsx | 23 ---------- .../Settings/atoms/SettingTabButton.tsx | 3 +- 3 files changed, 24 insertions(+), 44 deletions(-) delete mode 100644 src/components/PreferencesModal/TabButton.tsx diff --git a/src/components/PreferencesModal/PreferencesModal.tsx b/src/components/PreferencesModal/PreferencesModal.tsx index 4cb37e1a98..1856198f85 100644 --- a/src/components/PreferencesModal/PreferencesModal.tsx +++ b/src/components/PreferencesModal/PreferencesModal.tsx @@ -1,7 +1,6 @@ import React, { useMemo, useCallback } from 'react' import styled from '../../lib/styled' import { usePreferences } from '../../lib/preferences' -import TabButton from './TabButton' import { useGlobalKeyDownHandler } from '../../lib/keyboard' import GeneralTab from './GeneralTab' import EditorTab from './EditorTab' @@ -24,6 +23,7 @@ import StorageTab from './StorageTab' import MigrationPage from './MigrationTab' import { useMigrations } from '../../lib/migrate/store' import KeymapTab from './KeymapTab' +import SettingTabButton from '../../shared/components/organisms/Settings/atoms/SettingTabButton' const FullScreenContainer = styled.div` z-index: 7000; @@ -177,43 +177,47 @@ const PreferencesModal = () => { - openTab('about')} /> - openTab('keymap')} /> - openTab('general')} /> {currentStorage != null && ( - + openTab( + get(currentStorage.id) != null ? 'migration' : 'storage' + ) + } /> )} - openTab('editor')} /> - openTab('markdown')} /> {content} diff --git a/src/components/PreferencesModal/TabButton.tsx b/src/components/PreferencesModal/TabButton.tsx deleted file mode 100644 index d107aa48c0..0000000000 --- a/src/components/PreferencesModal/TabButton.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React, { useCallback } from 'react' -import cc from 'classcat' -import SettingTabButton from '../../shared/components/organisms/Settings/atoms/SettingTabButton' - -interface TabButtonProps { - label: string - tab: string - active: boolean - setTab: (tab: string) => void -} - -const TabButton = ({ label, tab, setTab, active }: TabButtonProps) => { - const selectTab = useCallback(() => { - setTab(tab) - }, [tab, setTab]) - return ( - -
{label}
-
- ) -} - -export default TabButton diff --git a/src/shared/components/organisms/Settings/atoms/SettingTabButton.tsx b/src/shared/components/organisms/Settings/atoms/SettingTabButton.tsx index b9eafcdbbf..167c0a142d 100644 --- a/src/shared/components/organisms/Settings/atoms/SettingTabButton.tsx +++ b/src/shared/components/organisms/Settings/atoms/SettingTabButton.tsx @@ -1,12 +1,11 @@ import React, { MouseEventHandler } from 'react' import cc from 'classcat' import styled from '../../../../lib/styled' -import { SettingsTab } from '../../../../../cloud/lib/stores/settings' interface SettingTabButtonProps { label: string active: boolean - tab: SettingsTab + tab: string id?: string onClick?: MouseEventHandler } From d91441efd1c3b7082080ba6af6be79cdd0a2199d Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Sat, 8 May 2021 18:01:01 +0900 Subject: [PATCH 20/91] Created SettingTeamSubLimit --- .../organisms/settings/TeamSubLimit.tsx | 85 ++----------------- .../Settings/atoms/SettingTeamSubLimit.tsx | 69 +++++++++++++++ 2 files changed, 75 insertions(+), 79 deletions(-) create mode 100644 src/shared/components/organisms/Settings/atoms/SettingTeamSubLimit.tsx diff --git a/src/cloud/components/organisms/settings/TeamSubLimit.tsx b/src/cloud/components/organisms/settings/TeamSubLimit.tsx index 6f87c066cd..7e0a8b288a 100644 --- a/src/cloud/components/organisms/settings/TeamSubLimit.tsx +++ b/src/cloud/components/organisms/settings/TeamSubLimit.tsx @@ -1,8 +1,8 @@ import React from 'react' -import styled from '../../../lib/styled' -import { usePage } from '../../../lib/stores/pageStore' import cc from 'classcat' +import { usePage } from '../../../lib/stores/pageStore' import { useSettings } from '../../../lib/stores/settings' +import SettingTeamSubLimit from '../../../../shared/components/organisms/Settings/atoms/SettingTeamSubLimit' const TeamSubLimit = () => { const { subscription, team, currentSubInfo } = usePage() @@ -18,7 +18,7 @@ const TeamSubLimit = () => { if (currentSubInfo.trialing) { return ( - + { You can upgrade at anytime during your trial.

-
+ ) } return ( - + {

)}
-
+ ) } -const StyledSidebarTeamSubLimit = styled.div` - width: 100%; - color: ${({ theme }) => theme.baseTextColor}; - font-size: ${({ theme }) => theme.fontSizes.xsmall}px; - - p { - color: ${({ theme }) => theme.subtleTextColor}; - margin: 10px 0px; - } - - .note-limit { - font-size: ${({ theme }) => theme.fontSizes.small}px; - } - - .progress-sm { - display: block; - width: 100%; - overflow: hidden; - height: 3px; - font-size: 0.75rem; - background-color: ${({ theme }) => theme.subtleBackgroundColor}; - border-radius: 0.25rem; - text-align: center; - position: relative; - } - - .progress-bar { - background-color: ${({ theme }) => theme.primaryBackgroundColor}; - max-width: 100%; - flex-direction: column; - justify-content: center; - text-align: center; - white-space: nowrap; - transition: width 0.6s ease; - height: 3px; - - &.over-limit { - background-color: ${({ theme }) => theme.dangerBackgroundColor}; - } - } - - .upgrade-link { - &:hover, - &:focus { - background-color: ${({ theme }) => theme.subtleBackgroundColor}; - } - cursor: pointer; - display: block; - margin: 10px 0 5px 0; - padding: 15px; - text-decoration: none; - } - - h6 { - font-size: ${({ theme }) => theme.fontSizes.default}px; - margin: 0; - color: ${({ theme }) => theme.primaryTextColor}; - } - - .progress-label { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - margin: auto; - } - - .text-danger { - color: ${({ theme }) => theme.dangerTextColor}; - } -` - export default TeamSubLimit diff --git a/src/shared/components/organisms/Settings/atoms/SettingTeamSubLimit.tsx b/src/shared/components/organisms/Settings/atoms/SettingTeamSubLimit.tsx new file mode 100644 index 0000000000..723df18239 --- /dev/null +++ b/src/shared/components/organisms/Settings/atoms/SettingTeamSubLimit.tsx @@ -0,0 +1,69 @@ +import styled from '../../../../lib/styled' + +const SettingTeamSubLimit = styled.nav` + width: 100%; + margin-top: ${({ theme }) => theme.sizes.spaces.l}px; + + h6 { + margin: 0; + color: ${({ theme }) => theme.colors.variants.primary.base}; + font-size: ${({ theme }) => theme.sizes.fonts.df}px; + } + + p { + margin: ${({ theme }) => theme.sizes.spaces.sm}px 0; + color: ${({ theme }) => theme.colors.text.subtle}; + font-size: ${({ theme }) => theme.sizes.fonts.sm}px; + } + + .upgrade-link { + display: block; + margin-top: ${({ theme }) => theme.sizes.spaces.sm}px; + margin-bottom: ${({ theme }) => theme.sizes.spaces.xsm}px; + padding: ${({ theme }) => theme.sizes.spaces.df}px; + cursor: pointer; + text-decoration: none; + + &:hover, + &:focus { + background-color: ${({ theme }) => theme.colors.background.tertiary}; + } + } + + .note-limit { + font-size: ${({ theme }) => theme.sizes.fonts.sm}px; + } + + .progress-sm { + display: block; + position: relative; + width: 100%; + height: 3px; + background-color: ${({ theme }) => theme.colors.background.quaternary}; + border-radius: 0.25rem; + font-size: 0.75rem; + overflow: hidden; + text-align: center; + } + + .progress-bar { + flex-direction: column; + justify-content: center; + height: 3px; + max-width: 100%; + background-color: ${({ theme }) => theme.colors.background.primary}; + text-align: center; + white-space: nowrap; + transition: width 0.6s ease; + + &.over-limit { + background-color: ${({ theme }) => theme.colors.variants.danger.base}; + } + } + + .text-danger { + color: ${({ theme }) => theme.colors.variants.danger.base}; + } +` + +export default SettingTeamSubLimit From 175f955d35c9c3ba5afcbc7c7f5947bc4c280cdc Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Sat, 8 May 2021 22:11:05 +0900 Subject: [PATCH 21/91] SettingContent -> SettingMain --- .../Settings/atoms/{SettingContent.tsx => SettingMain.tsx} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename src/shared/components/organisms/Settings/atoms/{SettingContent.tsx => SettingMain.tsx} (62%) diff --git a/src/shared/components/organisms/Settings/atoms/SettingContent.tsx b/src/shared/components/organisms/Settings/atoms/SettingMain.tsx similarity index 62% rename from src/shared/components/organisms/Settings/atoms/SettingContent.tsx rename to src/shared/components/organisms/Settings/atoms/SettingMain.tsx index c8be1f82b4..fd8ae7695d 100644 --- a/src/shared/components/organisms/Settings/atoms/SettingContent.tsx +++ b/src/shared/components/organisms/Settings/atoms/SettingMain.tsx @@ -1,9 +1,9 @@ import styled from '../../../../../lib/styled' -const SettingContent = styled.div` +const SettingMain = styled.div` flex: 1; - overflow: hidden auto; position: relative; + overflow: hidden auto; ` -export default SettingContent +export default SettingMain From f2a778c671855e1277456f85b9c2fa0d00b59765 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Sat, 8 May 2021 22:11:20 +0900 Subject: [PATCH 22/91] Added SettingTabContent --- .../Settings/atoms/SettingTabContent.tsx | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/shared/components/organisms/Settings/atoms/SettingTabContent.tsx diff --git a/src/shared/components/organisms/Settings/atoms/SettingTabContent.tsx b/src/shared/components/organisms/Settings/atoms/SettingTabContent.tsx new file mode 100644 index 0000000000..14d0954018 --- /dev/null +++ b/src/shared/components/organisms/Settings/atoms/SettingTabContent.tsx @@ -0,0 +1,53 @@ +import React from 'react' +import styled from '../../../../lib/styled' + +interface SettingTabContentProps { + header?: React.ReactNode + body: React.ReactNode +} + +const SettingTabContent = ({ header, body }: SettingTabContentProps) => ( + +
+
+
{header}
+
{body}
+
+
+
+) + +const Container = styled.div` + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + + .tab-content__scrollable { + flex: 1 1 auto; + width: 100%; + padding: ${({ theme }) => theme.sizes.spaces.xl}px + ${({ theme }) => theme.sizes.spaces.md}px; + overflow: hidden auto; + } + + .tab-content__container { + max-width: 700px; + margin: 0 auto; + } + + .tab-content__header { + margin-top: 0; + margin-bottom: ${({ theme }) => theme.sizes.spaces.xsm}px; + font-size: ${({ theme }) => theme.sizes.fonts.xl}px; + font-weight: 500; + } + + .tab-content__body { + section { + padding: ${({ theme }) => theme.sizes.spaces.xsm}px 0; + } + } +` + +export default SettingTabContent From a3478ad97bb3472b4d98907c894c8406a9402d34 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Sat, 8 May 2021 22:23:28 +0900 Subject: [PATCH 23/91] Apply SettingTabContent --- .../components/organisms/settings/ApiTab.tsx | 34 ++--- .../organisms/settings/AppFeedbackTab.tsx | 20 ++- .../components/organisms/settings/InfoTab.tsx | 28 ++-- .../organisms/settings/IntegrationsTab.tsx | 37 ++--- .../organisms/settings/MembersTab.tsx | 68 ++++------ .../organisms/settings/PersonalInfoTab.tsx | 39 +++--- .../organisms/settings/PreferencesTab.tsx | 16 +-- .../organisms/settings/SubscriptionTab.tsx | 128 ++++++++---------- .../organisms/settings/TeamInfoTab.tsx | 39 ++---- .../organisms/settings/UpgradeTab.tsx | 103 +++++++------- 10 files changed, 219 insertions(+), 293 deletions(-) diff --git a/src/cloud/components/organisms/settings/ApiTab.tsx b/src/cloud/components/organisms/settings/ApiTab.tsx index d363989741..1222678e48 100644 --- a/src/cloud/components/organisms/settings/ApiTab.tsx +++ b/src/cloud/components/organisms/settings/ApiTab.tsx @@ -1,15 +1,6 @@ import React, { useCallback, useMemo, useState } from 'react' import styled from '../../../lib/styled' -import { - Section, - TabHeader, - Column, - Container, - Scrollable, - SectionSubtleText, - SectionHeader2, - PrimaryAnchor, -} from './styled' +import { SectionSubtleText, SectionHeader2, PrimaryAnchor } from './styled' import CustomButton from '../../atoms/buttons/CustomButton' import Spinner from '../../atoms/CustomSpinner' import { useApiTokens, withApiTokens } from '../../../lib/stores/apiTokens' @@ -19,6 +10,7 @@ import { usePage } from '../../../lib/stores/pageStore' import Flexbox from '../../atoms/Flexbox' import Icon from '../../atoms/IconMdi' import { mdiOpenInNew } from '@mdi/js' +import SettingTabContent from '../../../../shared/components/organisms/Settings/atoms/SettingTabContent' const ApiTab = () => { const { team } = usePage() @@ -42,17 +34,17 @@ const ApiTab = () => { }, [apiTokenState, team]) return ( - - - -
- API + +
These tokens are available only to{' '} {team != null ? team.name : 'your team'}. -
-
+
+
@@ -103,10 +95,10 @@ const ApiTab = () => { })} )} -
- - - +
+ + } + > ) } diff --git a/src/cloud/components/organisms/settings/AppFeedbackTab.tsx b/src/cloud/components/organisms/settings/AppFeedbackTab.tsx index a0665295df..2b40c42d1c 100644 --- a/src/cloud/components/organisms/settings/AppFeedbackTab.tsx +++ b/src/cloud/components/organisms/settings/AppFeedbackTab.tsx @@ -1,22 +1,20 @@ import React from 'react' -import { Column, Container, Scrollable, Section, TabHeader } from './styled' import { useTranslation } from 'react-i18next' import AppFeedbackForm from '../../molecules/AppFeedbackForm' +import SettingTabContent from '../../../../shared/components/organisms/Settings/atoms/SettingTabContent' const AppFeedbackTab = () => { const { t } = useTranslation() return ( - - - - {t('settings.appFeedback')} -
- -
-
-
-
+ + + + } + > ) } diff --git a/src/cloud/components/organisms/settings/InfoTab.tsx b/src/cloud/components/organisms/settings/InfoTab.tsx index 9a91664163..d501425ba3 100644 --- a/src/cloud/components/organisms/settings/InfoTab.tsx +++ b/src/cloud/components/organisms/settings/InfoTab.tsx @@ -1,13 +1,8 @@ import React, { useCallback, useState, useMemo } from 'react' import { - Section, - TabHeader, SectionLabel, SectionInput, SectionProfilePic, - Column, - Container, - Scrollable, SectionFlexLeft, SectionSeparator, SectionDescription, @@ -27,6 +22,7 @@ import { dangerButtonStyle, } from '../../../lib/styled/styleFunctions' import { useToast } from '../../../../shared/lib/stores/toast' +import SettingTabContent from '../../../../shared/components/organisms/Settings/atoms/SettingTabContent' const InfoTab = () => { const { @@ -87,11 +83,11 @@ const InfoTab = () => { }, [currentUser]) return ( - - - - {t('settings.info')} -
+ +
{currentUser != null && ( <> Display Name @@ -123,9 +119,9 @@ const InfoTab = () => { {t('general.cancel')} -
+
-
+
{t('settings.account.delete')} @@ -142,10 +138,10 @@ const InfoTab = () => { {t('general.delete')} -
- - - +
+ + } + > ) } diff --git a/src/cloud/components/organisms/settings/IntegrationsTab.tsx b/src/cloud/components/organisms/settings/IntegrationsTab.tsx index b357b33e4e..cb0df7b9b2 100644 --- a/src/cloud/components/organisms/settings/IntegrationsTab.tsx +++ b/src/cloud/components/organisms/settings/IntegrationsTab.tsx @@ -1,14 +1,6 @@ import React, { useCallback, useMemo } from 'react' import styled from '../../../lib/styled' -import { - Section, - TabHeader, - Column, - Container, - Scrollable, - SectionSubtleText, - SectionHeader2, -} from './styled' +import { SectionSubtleText, SectionHeader2 } from './styled' import CustomButton from '../../atoms/buttons/CustomButton' import ServiceConnect from '../../atoms/ServiceConnect' import Spinner from '../../atoms/CustomSpinner' @@ -25,6 +17,7 @@ import Button from '../../atoms/Button' import { openNew } from '../../../lib/utils/platform' import { usePage } from '../../../lib/stores/pageStore' import { useModal } from '../../../../shared/lib/stores/modal' +import SettingTabContent from '../../../../shared/components/organisms/Settings/atoms/SettingTabContent' const IntegrationsTab = () => { const { openModal } = useModal() @@ -48,11 +41,11 @@ const IntegrationsTab = () => { }, []) return ( - - - -
- Integrations + +
Connect 3rd party content to your Boost Note for Teams documents. @@ -77,8 +70,8 @@ const IntegrationsTab = () => { -
-
+
+
Popular Integrations @@ -509,8 +502,8 @@ const IntegrationsTab = () => { -
-
+
+
External Entity @@ -592,10 +585,10 @@ const IntegrationsTab = () => {

-
- - - +
+ + } + > ) } diff --git a/src/cloud/components/organisms/settings/MembersTab.tsx b/src/cloud/components/organisms/settings/MembersTab.tsx index 84dbe48a5c..b2229b2264 100644 --- a/src/cloud/components/organisms/settings/MembersTab.tsx +++ b/src/cloud/components/organisms/settings/MembersTab.tsx @@ -1,10 +1,5 @@ import React, { useCallback, useState, useEffect, useRef } from 'react' import { - Section, - Column, - Container, - Scrollable, - TabHeader, SectionHeader2, StyledMembername, SectionSelect, @@ -53,6 +48,7 @@ import { getDocTitle } from '../../../lib/utils/patterns' import SettingsTeamForm from '../../molecules/SettingsTeamForm' import { guestsPerMember } from '../../../lib/subscription' import { useToast } from '../../../../shared/lib/stores/toast' +import SettingTabContent from '../../../../shared/components/organisms/Settings/atoms/SettingTabContent' const MembersTab = () => { const { t } = useTranslation() @@ -402,16 +398,14 @@ const MembersTab = () => { if (currentUserPermissions == null || team == null) { return ( - - - - {t('settings.teamMembers')} - - You don't own any permissions. - - - - + + You don't own any permissions. + + } + > ) } @@ -419,24 +413,22 @@ const MembersTab = () => { if (team.personal && showTeamPersonalForm) { return ( - - - - setShowTeamPersonalForm(false)} - teamConversion={true} - /> - - - + setShowTeamPersonalForm(false)} + teamConversion={true} + /> + } + > ) } return ( - - - +
@@ -66,7 +69,22 @@ const Container = styled.div` .tab-content__body { section { - padding: ${({ theme }) => theme.sizes.spaces.xsm}px 0; + margin: ${({ theme }) => theme.sizes.spaces.md}px 0; + } + } + + .tab-content__footer { + margin-top: ${({ theme }) => theme.sizes.spaces.md}px; + padding-top: ${({ theme }) => theme.sizes.spaces.md}px; + border-top: 1px solid ${({ theme }) => theme.colors.border.main}; + + h2 { + font-size: ${({ theme }) => theme.sizes.fonts.md}px; + font-weight: normal; + } + + p { + color: ${({ theme }) => theme.colors.text.subtle}; } } ` From 045bd4918282c20bb4937e9d9fef629a9e26f746 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Sun, 9 May 2021 14:37:59 +0900 Subject: [PATCH 30/91] Added More Padding to Buttons --- src/shared/components/atoms/Button.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/components/atoms/Button.tsx b/src/shared/components/atoms/Button.tsx index d3dddefbd5..d912e43fca 100644 --- a/src/shared/components/atoms/Button.tsx +++ b/src/shared/components/atoms/Button.tsx @@ -143,7 +143,7 @@ export const LoadingButton = ({ export default Button const StyledButton = styled.button` - padding: 0 10px; + padding: 0 ${({ theme }) => theme.sizes.spaces.md}px; border-radius: 2px; font-size: ${({ theme }) => theme.sizes.fonts.df}px; height: 32px; From 262caf6d7aa9944c3000ded3e6cfcb51ebd9668d Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Sun, 9 May 2021 14:38:12 +0900 Subject: [PATCH 31/91] Updated PersonalInfoTab --- .../organisms/settings/PersonalInfoTab.tsx | 133 ++++++++---------- src/cloud/lib/i18n/enUS.ts | 2 +- 2 files changed, 61 insertions(+), 74 deletions(-) diff --git a/src/cloud/components/organisms/settings/PersonalInfoTab.tsx b/src/cloud/components/organisms/settings/PersonalInfoTab.tsx index 24384208d2..bdf2fb4c42 100644 --- a/src/cloud/components/organisms/settings/PersonalInfoTab.tsx +++ b/src/cloud/components/organisms/settings/PersonalInfoTab.tsx @@ -1,21 +1,9 @@ import React, { useCallback, useState, useMemo } from 'react' -import { - SectionLabel, - SectionInput, - SectionProfilePic, - SectionFlexLeft, - SectionSeparator, - SectionDescription, - SectionSelect, - SectionHeader3, - SectionHeader2, -} from './styled' import { useTranslation } from 'react-i18next' import { useGlobalData } from '../../../lib/stores/globalData' import { saveUserInfo, updateUserIcon } from '../../../api/users' import { buildIconUrl } from '../../../api/files' import IconInput from '../../molecules/IconInput' -import CustomButton from '../../atoms/buttons/CustomButton' import { Spinner } from '../../atoms/Spinner' import { useSettings } from '../../../lib/stores/settings' import AccountLink from '../../atoms/Link/AccountLink' @@ -24,6 +12,9 @@ import { UserEmailNotificationType } from '../../../interfaces/db/userSettings' import { saveUserSettings } from '../../../api/users/settings' import { useToast } from '../../../../shared/lib/stores/toast' import SettingTabContent from '../../../../shared/components/organisms/Settings/atoms/SettingTabContent' +import SettingInput from '../../../../shared/components/organisms/Settings/atoms/SettingInput' +import SettingSelect from '../../../../shared/components/organisms/Settings/atoms/SettingSelect' +import Button from '../../../../shared/components/atoms/Button' const PersonalInfoTab = () => { const { @@ -119,37 +110,38 @@ const PersonalInfoTab = () => { return ( -
- {currentUser != null && ( - <> - Display Name - - - Icon - - - - )} + {currentUser != null && ( + <> +
+ +
+
+ +
+ + )} - {currentUser != null && ( - <> - {t('settings.notifications')} -
- - {t('settings.notificationsFrequency')} - - + {currentUser != null && ( +
+ - -
- - )} + + } + > +
+ )} - - - {updating ? ( - - ) : ( - t('general.update') - )} - - - {t('general.cancel')} - - -
-
- - {t('settings.account.delete')} - - - You may delete your account at any time, note that this is - unrecoverable.{' '} - - {t('general.delete')} - - +
} + footer={ + <> +

{t('settings.account.delete')}

+

+ You may delete your account at any time, note that this is + unrecoverable.{' '} + + {t('general.delete')} + +

+ + } >
) } diff --git a/src/cloud/lib/i18n/enUS.ts b/src/cloud/lib/i18n/enUS.ts index 0de2144b6f..99fd43894a 100644 --- a/src/cloud/lib/i18n/enUS.ts +++ b/src/cloud/lib/i18n/enUS.ts @@ -40,7 +40,7 @@ const enTranslation: TranslationSource = { 'settings.editorKeyMap': 'Editor Keymap', 'settings.light': 'Light', 'settings.dark': 'Dark', - 'settings.notificationsFrequency': 'Notification frequency', + 'settings.notificationsFrequency': 'Email updates', // Settings Community 'community.feature.requests': 'Feature Requests', From b91a2d88bf5fc3dcc755e59b9fa5c6213ba4ded7 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Sun, 9 May 2021 18:44:59 +0900 Subject: [PATCH 32/91] Removed Unused Files --- .../organisms/settings/AppFeedbackTab.tsx | 21 --- .../components/organisms/settings/InfoTab.tsx | 157 ------------------ .../organisms/settings/SettingsFooter.tsx | 44 ----- .../components/organisms/settings/TabLink.tsx | 67 -------- 4 files changed, 289 deletions(-) delete mode 100644 src/cloud/components/organisms/settings/AppFeedbackTab.tsx delete mode 100644 src/cloud/components/organisms/settings/InfoTab.tsx delete mode 100644 src/cloud/components/organisms/settings/SettingsFooter.tsx delete mode 100644 src/cloud/components/organisms/settings/TabLink.tsx diff --git a/src/cloud/components/organisms/settings/AppFeedbackTab.tsx b/src/cloud/components/organisms/settings/AppFeedbackTab.tsx deleted file mode 100644 index c72ba5cb87..0000000000 --- a/src/cloud/components/organisms/settings/AppFeedbackTab.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react' -import { useTranslation } from 'react-i18next' -import AppFeedbackForm from '../../molecules/AppFeedbackForm' -import SettingTabContent from '../../../../shared/components/organisms/Settings/atoms/SettingTabContent' - -const AppFeedbackTab = () => { - const { t } = useTranslation() - - return ( - - - - } - > - ) -} - -export default AppFeedbackTab diff --git a/src/cloud/components/organisms/settings/InfoTab.tsx b/src/cloud/components/organisms/settings/InfoTab.tsx deleted file mode 100644 index c08c956d96..0000000000 --- a/src/cloud/components/organisms/settings/InfoTab.tsx +++ /dev/null @@ -1,157 +0,0 @@ -import React, { useCallback, useState, useMemo } from 'react' -import { - SectionLabel, - SectionInput, - SectionProfilePic, - SectionFlexLeft, - SectionSeparator, - SectionDescription, -} from './styled' -import { useTranslation } from 'react-i18next' -import { useGlobalData } from '../../../lib/stores/globalData' -import { saveUserInfo, updateUserIcon } from '../../../api/users' -import { buildIconUrl } from '../../../api/files' -import IconInput from '../../molecules/IconInput' -import CustomButton from '../../atoms/buttons/CustomButton' -import { Spinner } from '../../atoms/Spinner' -import { useSettings } from '../../../lib/stores/settings' -import AccountLink from '../../atoms/Link/AccountLink' -import styled from '../../../lib/styled' -import { - baseButtonStyle, - dangerButtonStyle, -} from '../../../lib/styled/styleFunctions' -import { useToast } from '../../../../shared/lib/stores/toast' -import SettingTabContent from '../../../../shared/components/organisms/Settings/atoms/SettingTabContent' - -const InfoTab = () => { - const { - globalData: { currentUser }, - setPartialGlobalData, - } = useGlobalData() - - const { pushMessage } = useToast() - const [updating, setUpdating] = useState(false) - const [displayName, setDisplayName] = useState( - currentUser != null ? currentUser.displayName : '' - ) - const [iconFile, setIconFile] = useState(null) - const { t } = useTranslation() - const { closeSettingsTab } = useSettings() - - const onChangeHandler = useCallback( - (event: React.ChangeEvent) => { - setDisplayName(event.target.value) - }, - [setDisplayName] - ) - - const updateHandler = useCallback(async () => { - if (updating) { - return - } - setUpdating(true) - try { - await saveUserInfo({ displayName }) - const user = { ...currentUser!, displayName } - if (iconFile != null) { - const { icon } = await updateUserIcon(iconFile) - user.icon = icon - } - setPartialGlobalData({ currentUser: user }) - } catch (error) { - pushMessage({ - title: 'Error', - description: `Could not update your user information`, - }) - } - setUpdating(false) - }, [ - updating, - displayName, - currentUser, - iconFile, - setPartialGlobalData, - pushMessage, - ]) - - const iconUrl = useMemo(() => { - if (currentUser == null || currentUser.icon == null) { - return undefined - } - return buildIconUrl(currentUser.icon.location) - }, [currentUser]) - - return ( - -
- {currentUser != null && ( - <> - Display Name - - - Icon - - - - )} - - - {updating ? ( - - ) : ( - t('general.update') - )} - - - {t('general.cancel')} - - -
- -
- - - {t('settings.account.delete')} - - - You may delete your account at any time, note that this is - unrecoverable.{' '} - - - {t('general.delete')} - - -
- - } - >
- ) -} - -const StyledInfoTabDelete = styled.div` - .delete-link { - ${baseButtonStyle} - ${dangerButtonStyle} - display: inline-block; - text-decoration: none; - } -` - -export default InfoTab diff --git a/src/cloud/components/organisms/settings/SettingsFooter.tsx b/src/cloud/components/organisms/settings/SettingsFooter.tsx deleted file mode 100644 index 04bea9c2f0..0000000000 --- a/src/cloud/components/organisms/settings/SettingsFooter.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import React, { useCallback } from 'react' -import { SectionFooter } from './styled' -import { useSettings } from '../../../lib/stores/settings' -import { useTranslation } from 'react-i18next' -import CustomButton from '../../atoms/buttons/CustomButton' -import { Spinner } from '../../atoms/Spinner' - -interface SettingsFooterProps { - onUpdate: () => void - updating: boolean - disabled: boolean -} - -const SettingsFooter = ({ - onUpdate, - updating, - disabled, -}: SettingsFooterProps) => { - const { setClosed } = useSettings() - - const onCancelHandler = useCallback(() => { - setClosed(true) - }, [setClosed]) - - const { t } = useTranslation() - - return ( - - - {updating ? : t('general.update')} - - - {t('general.cancel')} - - - ) -} - -export default SettingsFooter diff --git a/src/cloud/components/organisms/settings/TabLink.tsx b/src/cloud/components/organisms/settings/TabLink.tsx deleted file mode 100644 index 8c9e515d13..0000000000 --- a/src/cloud/components/organisms/settings/TabLink.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import React from 'react' -import styled from '../../../lib/styled' -import Icon from '../../atoms/IconMdi' -import { mdiOpenInNew } from '@mdi/js' - -interface TabLinkProps { - label: string - href: string - id?: string - prependIcon: string -} - -const TabLink = ({ id, label, href, prependIcon }: TabLinkProps) => { - return ( - - - - -
{label}
- -
- ) -} - -export default TabLink - -const StyledLink = styled.a` - display: flex; - align-items: center; - width: 100%; - padding: ${({ theme }) => theme.space.xsmall}px - ${({ theme }) => theme.space.small}px; - background-color: transparent; - border: none; - color: ${({ theme }) => theme.subtleTextColor}; - cursor: pointer; - text-decoration: none !important; - - .icon { - margin-left: ${({ theme }) => theme.space.small}px; - margin-right: ${({ theme }) => theme.space.xsmall}px; - - svg { - vertical-align: sub; - } - } - - .label { - flex: 1; - font-size: ${({ theme }) => theme.fontSizes.small}px; - text-align: left; - } - - &.active, - &:hover, - &:focus { - color: ${({ theme }) => theme.emphasizedTextColor}; - } - - &.active { - background-color: ${({ theme }) => theme.emphasizedBackgroundColor}; - } - - &:focus { - outline: none; - } -` From 205e7b1d571e2f38bcbe316044fce14f723aa62c Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Sun, 9 May 2021 18:45:35 +0900 Subject: [PATCH 33/91] Updated Title --- src/cloud/lib/i18n/enUS.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cloud/lib/i18n/enUS.ts b/src/cloud/lib/i18n/enUS.ts index 99fd43894a..42b9b33c41 100644 --- a/src/cloud/lib/i18n/enUS.ts +++ b/src/cloud/lib/i18n/enUS.ts @@ -23,7 +23,7 @@ const enTranslation: TranslationSource = { 'settings.teamInfo': 'Settings', 'settings.title': 'Settings', 'settings.personalInfo': 'Settings', - 'settings.preferences': 'Theme and Preferences', + 'settings.preferences': 'Preferences', 'settings.teamMembers': 'Members', 'settings.teamUpgrade': 'Upgrade', 'settings.teamSubscription': 'Billing', From 9ff291c7b1fe752146e45533c6f1feb3f639a033 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Sun, 9 May 2021 18:46:01 +0900 Subject: [PATCH 34/91] Added Setting Divider --- .../organisms/Settings/atoms/SettingDivider.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/shared/components/organisms/Settings/atoms/SettingDivider.tsx diff --git a/src/shared/components/organisms/Settings/atoms/SettingDivider.tsx b/src/shared/components/organisms/Settings/atoms/SettingDivider.tsx new file mode 100644 index 0000000000..ddc5acf07a --- /dev/null +++ b/src/shared/components/organisms/Settings/atoms/SettingDivider.tsx @@ -0,0 +1,11 @@ +import styled from '../../../../lib/styled' + +const SettingDivider = styled.div` + margin-top: ${({ theme }) => theme.sizes.spaces.l}px; + margin-bottom: ${({ theme }) => theme.sizes.spaces.l}px; + width: 100%; + height: 1px; + background-color: ${({ theme }) => theme.colors.border.main}; +` + +export default SettingDivider From cb8fa37dec47b5925281f9b7ad6eeb85b6f12093 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Sun, 9 May 2021 18:46:15 +0900 Subject: [PATCH 35/91] Added SettingLink --- .../organisms/Settings/atoms/SettingLink.tsx | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/shared/components/organisms/Settings/atoms/SettingLink.tsx diff --git a/src/shared/components/organisms/Settings/atoms/SettingLink.tsx b/src/shared/components/organisms/Settings/atoms/SettingLink.tsx new file mode 100644 index 0000000000..489a2eae99 --- /dev/null +++ b/src/shared/components/organisms/Settings/atoms/SettingLink.tsx @@ -0,0 +1,13 @@ +import styled from '../../../../lib/styled' + +const SettingLink = styled.a` + color: ${({ theme }) => theme.colors.text.link}; + text-decoration: none; + + &:hover { + color: ${({ theme }) => theme.colors.text.link}; + text-decoration: underline; + } +` + +export default SettingLink From 9d048c9cd21bc168fb22b0f2dfcb1fcd04328979 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Sun, 9 May 2021 18:46:27 +0900 Subject: [PATCH 36/91] Added SettingTextarea --- .../Settings/atoms/SettingTextarea.tsx | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/shared/components/organisms/Settings/atoms/SettingTextarea.tsx diff --git a/src/shared/components/organisms/Settings/atoms/SettingTextarea.tsx b/src/shared/components/organisms/Settings/atoms/SettingTextarea.tsx new file mode 100644 index 0000000000..51c5c89f5c --- /dev/null +++ b/src/shared/components/organisms/Settings/atoms/SettingTextarea.tsx @@ -0,0 +1,25 @@ +import styled from '../../../../lib/styled' + +const SettingTextarea = styled.textarea` + flex-grow: 1; + flex-shrink: 1; + width: 100%; + height: 200px; + max-width: 400px; + padding: ${({ theme }) => theme.sizes.spaces.xsm}px + ${({ theme }) => theme.sizes.spaces.sm}px; + background-color: ${({ theme }) => theme.colors.background.primary}; + border: 1px solid ${({ theme }) => theme.colors.border.main}; + border-radius: 4px; + color: ${({ theme }) => theme.colors.text.primary}; + resize: none; + + &:focus { + border-color: ${({ theme }) => theme.colors.variants.primary.base}; + } + &::placeholder { + color: ${({ theme }) => theme.colors.text.subtle}; + } +` + +export default SettingTextarea From adc600aab8e8eec49f4bb490c59c8560f8d8f416 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Sun, 9 May 2021 18:46:50 +0900 Subject: [PATCH 37/91] Updated SettingTabContent --- .../Settings/atoms/SettingTabContent.tsx | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/shared/components/organisms/Settings/atoms/SettingTabContent.tsx b/src/shared/components/organisms/Settings/atoms/SettingTabContent.tsx index 41477a6f3f..097f74ffdf 100644 --- a/src/shared/components/organisms/Settings/atoms/SettingTabContent.tsx +++ b/src/shared/components/organisms/Settings/atoms/SettingTabContent.tsx @@ -49,8 +49,8 @@ const Container = styled.div` } .tab-content__header { - margin-bottom: ${({ theme }) => theme.sizes.spaces.md}px; - padding-bottom: ${({ theme }) => theme.sizes.spaces.md}px; + margin-bottom: ${({ theme }) => theme.sizes.spaces.l}px; + padding-bottom: ${({ theme }) => theme.sizes.spaces.l}px; border-bottom: 1px solid ${({ theme }) => theme.colors.border.main}; } @@ -67,25 +67,26 @@ const Container = styled.div` font-size: ${({ theme }) => theme.sizes.fonts.df}px; } - .tab-content__body { - section { - margin: ${({ theme }) => theme.sizes.spaces.md}px 0; - } - } - + .tab-content__body, .tab-content__footer { - margin-top: ${({ theme }) => theme.sizes.spaces.md}px; - padding-top: ${({ theme }) => theme.sizes.spaces.md}px; - border-top: 1px solid ${({ theme }) => theme.colors.border.main}; - h2 { font-size: ${({ theme }) => theme.sizes.fonts.md}px; font-weight: normal; } - p { + .text--subtle { color: ${({ theme }) => theme.colors.text.subtle}; } + + .text--small { + font-size: ${({ theme }) => theme.sizes.fonts.sm}px; + } + } + + .tab-content__body { + section { + margin: ${({ theme }) => theme.sizes.spaces.md}px 0; + } } ` From 635fb6e7efdfe63bb998c713de18dcb5cd17162c Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Sun, 9 May 2021 18:52:17 +0900 Subject: [PATCH 38/91] Removed Components from styled.ts --- .../molecules/AppFeedbackForm/index.tsx | 33 ++-- .../molecules/GuestInvitesSection.tsx | 12 +- .../molecules/OpenInviteSection.tsx | 14 +- .../components/molecules/SettingsTeamForm.tsx | 4 +- .../molecules/SubscriptionForm/index.tsx | 29 ++- .../molecules/TeamInvitesSection.tsx | 29 +-- .../components/molecules/TokenCreate.tsx | 19 +- .../Modal/contents/Doc/GuestsModal/index.tsx | 6 +- .../Subscription/SubscriptionManagement.tsx | 18 +- .../components/organisms/settings/ApiTab.tsx | 21 ++- .../organisms/settings/IntegrationsTab.tsx | 133 +++++++------- .../organisms/settings/MembersTab.tsx | 99 +++++----- .../organisms/settings/PersonalInfoTab.tsx | 4 +- .../organisms/settings/PreferencesTab.tsx | 1 + .../organisms/settings/SubscriptionTab.tsx | 5 +- .../organisms/settings/TeamInfoTab.tsx | 14 +- .../organisms/settings/UpgradeTab.tsx | 11 +- .../settings/UserPreferencesForm.tsx | 153 ++++++++++------ .../components/organisms/settings/styled.ts | 169 +----------------- 19 files changed, 321 insertions(+), 453 deletions(-) diff --git a/src/cloud/components/molecules/AppFeedbackForm/index.tsx b/src/cloud/components/molecules/AppFeedbackForm/index.tsx index b1f40c3a61..9a8a436871 100644 --- a/src/cloud/components/molecules/AppFeedbackForm/index.tsx +++ b/src/cloud/components/molecules/AppFeedbackForm/index.tsx @@ -3,16 +3,13 @@ import CustomButton from '../../atoms/buttons/CustomButton' import { Spinner } from '../../atoms/Spinner' import { StyledAppFeedbackForm } from './styled' import { SelectChangeEventHandler } from '../../../lib/utils/events' -import { - SectionSelect, - SectionHeader2, - SectionTextarea, -} from '../../organisms/settings/styled' import { registerAppFeedback } from '../../../api/users/appfeedback' import { AppFeedbackTypeOption } from '../../../interfaces/db/userAppFeedback' import ColoredBlock from '../../atoms/ColoredBlock' import { useEffectOnce } from 'react-use' import { useToast } from '../../../../shared/lib/stores/toast' +import SettingSelect from '../../../../shared/components/organisms/Settings/atoms/SettingSelect' +import SettingTextarea from '../../../../shared/components/organisms/Settings/atoms/SettingTextarea' const typeOptions: AppFeedbackTypeOption[] = ['Feature Request', 'Bug Report'] @@ -98,17 +95,23 @@ const AppFeedbackForm = () => { return ( - Type of feedback - - {typeOptions.map((val) => ( - - ))} - +

Type of feedback

+ + {typeOptions.map((val) => ( + + ))} + + } + > - Free form - Free form + { ) return ( -
+
- Invite with Email +

Invite with Email

{has('fetch') && }
- { ))} {error != null && } -
+
) } diff --git a/src/cloud/components/molecules/OpenInviteSection.tsx b/src/cloud/components/molecules/OpenInviteSection.tsx index 15e00b4b0c..166fe6a70f 100644 --- a/src/cloud/components/molecules/OpenInviteSection.tsx +++ b/src/cloud/components/molecules/OpenInviteSection.tsx @@ -1,9 +1,5 @@ import React, { useState, useCallback, useMemo } from 'react' -import { - Section, - SectionHeader2, - SectionRow, -} from '../organisms/settings/styled' +import { SectionRow } from '../organisms/settings/styled' import { useDialog, DialogIconTypes } from '../../../shared/lib/stores/dialog' import { usePage } from '../../lib/stores/pageStore' import { useEffectOnce } from 'react-use' @@ -172,11 +168,9 @@ const OpenInvitesSection = ({ userPermissions }: OpenInvitesSectionProps) => { } return ( -
+
- - Invite with an open Link - +

Invite with an open Link

{ )} -
+
) } diff --git a/src/cloud/components/molecules/SettingsTeamForm.tsx b/src/cloud/components/molecules/SettingsTeamForm.tsx index 5bc08d138c..2c60b270f5 100644 --- a/src/cloud/components/molecules/SettingsTeamForm.tsx +++ b/src/cloud/components/molecules/SettingsTeamForm.tsx @@ -13,13 +13,13 @@ import { inputStyle, inverseSecondaryButtonStyle, } from '../../lib/styled/styleFunctions' -import Button from '../atoms/Button' import Flexbox from '../atoms/Flexbox' import Icon from '../atoms/Icon' import { Spinner } from '../atoms/Spinner' import { useRouter } from '../../lib/router' import { getTeamURL } from '../../lib/utils/patterns' import { useToast } from '../../../shared/lib/stores/toast' +import Button from '../../../shared/components/atoms/Button' interface SettingsTeamFormProps { team: SerializedTeam @@ -144,7 +144,7 @@ const SettingsTeamForm = ({ {onCancel != null && ( )} - {sending ? : 'Subscribe'} - + ) diff --git a/src/cloud/components/molecules/TeamInvitesSection.tsx b/src/cloud/components/molecules/TeamInvitesSection.tsx index 377030962b..4fea03f301 100644 --- a/src/cloud/components/molecules/TeamInvitesSection.tsx +++ b/src/cloud/components/molecules/TeamInvitesSection.tsx @@ -1,13 +1,9 @@ import React, { useState, useCallback } from 'react' import { - Section, - SectionHeader2, SectionRow, - SectionInput, SectionList, SectionListItem, SectionInLineIcon, - SectionSelect, } from '../organisms/settings/styled' import { SerializedTeamInvite } from '../../interfaces/db/teamInvite' import { useDialog, DialogIconTypes } from '../../../shared/lib/stores/dialog' @@ -27,6 +23,8 @@ import CustomButton from '../atoms/buttons/CustomButton' import { SelectChangeEventHandler } from '../../lib/utils/events' import { SerializedUserTeamPermissions } from '../../interfaces/db/userTeamPermissions' import Flexbox from '../atoms/Flexbox' +import SettingSelect from '../../../shared/components/organisms/Settings/atoms/SettingSelect' +import SettingInput from '../../../shared/components/organisms/Settings/atoms/SettingInput' interface TeamInvitesSectionProps { userPermissions: SerializedUserTeamPermissions @@ -144,27 +142,30 @@ const TeamInvitesSection = ({ userPermissions }: TeamInvitesSectionProps) => { ) return ( -
+
- Invite with Email +

Invite with Email

{sending && }
- - - - - + options={ + <> + + + + } + > Send @@ -193,7 +194,7 @@ const TeamInvitesSection = ({ userPermissions }: TeamInvitesSectionProps) => { ))} {error != null && } -
+
) } diff --git a/src/cloud/components/molecules/TokenCreate.tsx b/src/cloud/components/molecules/TokenCreate.tsx index f27252826d..134b7488cf 100644 --- a/src/cloud/components/molecules/TokenCreate.tsx +++ b/src/cloud/components/molecules/TokenCreate.tsx @@ -1,13 +1,8 @@ import React, { useState, useCallback, ChangeEvent } from 'react' import CustomButton from '../atoms/buttons/CustomButton' -import { - Section, - SectionInput, - SectionLabel, -} from '../organisms/settings/styled' import Flexbox from '../atoms/Flexbox' import styled from '../../lib/styled' -import { SectionHeader2 } from '../organisms/Modal/contents/styled' +import SettingInput from '../../../shared/components/organisms/Settings/atoms/SettingInput' interface TokenCreateProps { onCreate: (name: string) => void @@ -22,16 +17,16 @@ const TokenCreate = ({ onCreate }: TokenCreateProps) => { return ( - Create a token -
- Name - Create a token +
+ ) => setName(e.target.value) } - /> -
+ > +
{name.length === 0 && ( Enter a name diff --git a/src/cloud/components/organisms/Modal/contents/Doc/GuestsModal/index.tsx b/src/cloud/components/organisms/Modal/contents/Doc/GuestsModal/index.tsx index fd1316c5a0..6d9c8fb4c8 100644 --- a/src/cloud/components/organisms/Modal/contents/Doc/GuestsModal/index.tsx +++ b/src/cloud/components/organisms/Modal/contents/Doc/GuestsModal/index.tsx @@ -9,8 +9,8 @@ import Icon from '../../../../../atoms/Icon' import { guestsPerMember } from '../../../../../../lib/subscription' import plur from 'plur' import GuestInvitesSection from '../../../../../molecules/GuestInvitesSection' -import { PrimaryAnchor } from '../../../../settings/styled' import { useModal } from '../../../../../../../shared/lib/stores/modal' +import SettingLink from '../../../../../../../shared/components/organisms/Settings/atoms/SettingLink' interface GuestsModalProps { docId: string @@ -64,13 +64,13 @@ const GuestsModal = ({ docId, teamId }: GuestsModalProps) => { permissions.length * guestsPerMember - guestsMap.size } remaining ${plur('seat', permissions.length)}. ` : 'No Remaining seats. '} - See how it works - +

diff --git a/src/cloud/components/organisms/Subscription/SubscriptionManagement.tsx b/src/cloud/components/organisms/Subscription/SubscriptionManagement.tsx index eef6520e1c..07eb900767 100644 --- a/src/cloud/components/organisms/Subscription/SubscriptionManagement.tsx +++ b/src/cloud/components/organisms/Subscription/SubscriptionManagement.tsx @@ -26,7 +26,7 @@ import { StyledTotal, StyledUpgradePlan, } from '../../molecules/SubscriptionForm' -import { SectionIntroduction, SectionParagraph } from '../settings/styled' +import { SectionIntroduction } from '../settings/styled' import PlanTables from './PlanTables' import Alert from '../../../../components/atoms/Alert' @@ -140,7 +140,7 @@ const SubscriptionManagement = ({ {subscription.plan === 'pro' ? ( - +
Pro @@ -165,9 +165,9 @@ const SubscriptionManagement = ({ : `¥${stripeProJpyPlanUnit * subscription.seats}`} - +
) : subscription.plan === 'standard' ? ( - +
Standard @@ -191,7 +191,7 @@ const SubscriptionManagement = ({ : `¥${stripeStandardJpyPlanUnit * subscription.seats}`} - +
) : null} {usingJpyPricing && ( @@ -337,7 +337,7 @@ const SubscriptionManagement = ({

- +
${stripeProPlanUnit} × {subscription.seats}{' '} @@ -348,7 +348,7 @@ const SubscriptionManagement = ({ ${subscription.seats * stripeProPlanUnit} - +
) : ( <> @@ -368,7 +368,7 @@ const SubscriptionManagement = ({

- +
${stripeStandardPlanUnit} × {subscription.seats}{' '} @@ -381,7 +381,7 @@ const SubscriptionManagement = ({ ${subscription.seats * stripeStandardPlanUnit} - +
)}
diff --git a/src/cloud/components/organisms/settings/ApiTab.tsx b/src/cloud/components/organisms/settings/ApiTab.tsx index b679a686f0..9d5ba08a9a 100644 --- a/src/cloud/components/organisms/settings/ApiTab.tsx +++ b/src/cloud/components/organisms/settings/ApiTab.tsx @@ -1,7 +1,5 @@ import React, { useCallback, useMemo, useState } from 'react' import styled from '../../../lib/styled' -import { SectionSubtleText, SectionHeader2, PrimaryAnchor } from './styled' -import CustomButton from '../../atoms/buttons/CustomButton' import Spinner from '../../atoms/CustomSpinner' import { useApiTokens, withApiTokens } from '../../../lib/stores/apiTokens' import TokenControl from '../../molecules/TokenControl' @@ -11,6 +9,8 @@ import Flexbox from '../../atoms/Flexbox' import Icon from '../../atoms/IconMdi' import { mdiOpenInNew } from '@mdi/js' import SettingTabContent from '../../../../shared/components/organisms/Settings/atoms/SettingTabContent' +import SettingLink from '../../../../shared/components/organisms/Settings/atoms/SettingLink' +import Button from '../../../../shared/components/atoms/Button' const ApiTab = () => { const { team } = usePage() @@ -36,37 +36,36 @@ const ApiTab = () => { return (
- +

These tokens are available only to{' '} {team != null ? team.name : 'your team'}. - +

- - Access Tokens - +

Access Tokens

See the{' '} - documentation for Boost Note for Teams API{' '} - +

- setTokenCreateMode(!tokenCreateMode)} disabled={apiTokenState.state === 'initialising'} > {tokenCreateMode ? 'Close' : 'Generate Token'} - +
{tokenCreateMode && ( diff --git a/src/cloud/components/organisms/settings/IntegrationsTab.tsx b/src/cloud/components/organisms/settings/IntegrationsTab.tsx index 4fed1d3842..fff4ff0312 100644 --- a/src/cloud/components/organisms/settings/IntegrationsTab.tsx +++ b/src/cloud/components/organisms/settings/IntegrationsTab.tsx @@ -1,7 +1,5 @@ import React, { useCallback, useMemo } from 'react' import styled from '../../../lib/styled' -import { SectionSubtleText, SectionHeader2 } from './styled' -import CustomButton from '../../atoms/buttons/CustomButton' import ServiceConnect from '../../atoms/ServiceConnect' import Spinner from '../../atoms/CustomSpinner' import { @@ -13,11 +11,11 @@ import { MixpanelActionTrackTypes } from '../../../interfaces/analytics/mixpanel import FeedbackModal from '../Modal/contents/FeedbackModal' import { githubOauthId, boostHubBaseUrl } from '../../../lib/consts' import { usingElectron } from '../../../lib/stores/electron' -import Button from '../../atoms/Button' import { openNew } from '../../../lib/utils/platform' import { usePage } from '../../../lib/stores/pageStore' import { useModal } from '../../../../shared/lib/stores/modal' import SettingTabContent from '../../../../shared/components/organisms/Settings/atoms/SettingTabContent' +import Button from '../../../../shared/components/atoms/Button' const IntegrationsTab = () => { const { openModal } = useModal() @@ -46,9 +44,9 @@ const IntegrationsTab = () => { body={ <>
- +

Connect 3rd party content to your Boost Note for Teams documents. - +

@@ -60,19 +58,19 @@ const IntegrationsTab = () => { target='_blank' rel='noreferrer noopener' > - onIntegrationLinkClick('global')} > See - +
- Popular Integrations +

Popular Integrations

@@ -90,13 +88,13 @@ const IntegrationsTab = () => { target='_blank' rel='noreferrer noopener' > - onIntegrationLinkClick('github')} > See - + @@ -115,13 +113,13 @@ const IntegrationsTab = () => { target='_blank' rel='noreferrer noopener' > - onIntegrationLinkClick('trello')} > See - + @@ -140,13 +138,13 @@ const IntegrationsTab = () => { target='_blank' rel='noreferrer noopener' > - onIntegrationLinkClick('slack')} > See - + @@ -165,13 +163,13 @@ const IntegrationsTab = () => { target='_blank' rel='noreferrer noopener' > - onIntegrationLinkClick('gmail')} > See - + @@ -193,13 +191,13 @@ const IntegrationsTab = () => { target='_blank' rel='noreferrer noopener' > - onIntegrationLinkClick('google-calendar')} > See - + @@ -221,13 +219,13 @@ const IntegrationsTab = () => { target='_blank' rel='noreferrer noopener' > - onIntegrationLinkClick('google-drive')} > See - + @@ -246,13 +244,13 @@ const IntegrationsTab = () => { target='_blank' rel='noreferrer noopener' > - onIntegrationLinkClick('jira')} > See - + @@ -271,13 +269,13 @@ const IntegrationsTab = () => { target='_blank' rel='noreferrer noopener' > - onIntegrationLinkClick('miro')} > See - + @@ -296,13 +294,13 @@ const IntegrationsTab = () => { target='_blank' rel='noreferrer noopener' > - onIntegrationLinkClick('dropbox')} > See - + @@ -321,13 +319,13 @@ const IntegrationsTab = () => { target='_blank' rel='noreferrer noopener' > - onIntegrationLinkClick('airtable')} > See - + @@ -346,13 +344,13 @@ const IntegrationsTab = () => { target='_blank' rel='noreferrer noopener' > - onIntegrationLinkClick('clickup')} > See - + @@ -375,13 +373,13 @@ const IntegrationsTab = () => { target='_blank' rel='noreferrer noopener' > - onIntegrationLinkClick('lambda')} > See - + @@ -400,13 +398,13 @@ const IntegrationsTab = () => { target='_blank' rel='noreferrer noopener' > - onIntegrationLinkClick('mailchimp')} > See - + @@ -425,13 +423,13 @@ const IntegrationsTab = () => { target='_blank' rel='noreferrer noopener' > - onIntegrationLinkClick('intercom')} > See - + @@ -451,13 +449,13 @@ const IntegrationsTab = () => { target='_blank' rel='noreferrer noopener' > - onIntegrationLinkClick('stripe')} > See - + @@ -473,13 +471,13 @@ const IntegrationsTab = () => { target='_blank' rel='noreferrer noopener' > - onIntegrationLinkClick('asana')} > See - + @@ -492,19 +490,19 @@ const IntegrationsTab = () => { target='_blank' rel='noreferrer noopener' > - onIntegrationLinkClick('zapier')} > See - +
- External Entity +

External Entity

@@ -528,12 +526,9 @@ const IntegrationsTab = () => {
{(connectionState.type === 'initialising' || connectionState.type === 'working') && ( - + )} {connectionState.type === 'initialised' && ( <> @@ -549,7 +544,7 @@ const IntegrationsTab = () => { ) : ( { ) ) : ( - Disable - + )} )} diff --git a/src/cloud/components/organisms/settings/MembersTab.tsx b/src/cloud/components/organisms/settings/MembersTab.tsx index 2c45836450..a807d53665 100644 --- a/src/cloud/components/organisms/settings/MembersTab.tsx +++ b/src/cloud/components/organisms/settings/MembersTab.tsx @@ -1,11 +1,4 @@ import React, { useCallback, useState, useEffect, useRef } from 'react' -import { - SectionHeader2, - StyledMembername, - SectionSelect, - SectionDescription, - PrimaryAnchor, -} from './styled' import { useTranslation } from 'react-i18next' import { usePage } from '../../../lib/stores/pageStore' import { @@ -37,7 +30,6 @@ import { mdiArrowRight, mdiCardTextOutline, mdiChevronDown } from '@mdi/js' import { deleteGuestDoc, getGuestsEmails } from '../../../api/guests' import { useSet } from 'react-use' import plur from 'plur' -import Button from '../../atoms/Button' import { MenuTypes, useContextMenu, @@ -49,6 +41,9 @@ import SettingsTeamForm from '../../molecules/SettingsTeamForm' import { guestsPerMember } from '../../../lib/subscription' import { useToast } from '../../../../shared/lib/stores/toast' import SettingTabContent from '../../../../shared/components/organisms/Settings/atoms/SettingTabContent' +import SettingSelect from '../../../../shared/components/organisms/Settings/atoms/SettingSelect' +import SettingLink from '../../../../shared/components/organisms/Settings/atoms/SettingLink' +import Button from '../../../../shared/components/atoms/Button' const MembersTab = () => { const { t } = useTranslation() @@ -376,7 +371,7 @@ const MembersTab = () => { {doc != null ? getDocTitle(doc, 'Untitled') : 'Untitled'} + + + } + description={'Manage who access to this space.'} body={ <> - - - - - {tab === 'member' ? ( team.personal ? (
- Current Members +

Current Members

{fetching.has('userEmails') && ( )} @@ -496,7 +493,7 @@ const MembersTab = () => {
- Current Members +

Current Members

{fetching.has('userEmails') && ( )} @@ -541,7 +538,7 @@ const MembersTab = () => { }} /> ) : ( - changePermissionsRole( @@ -560,10 +557,13 @@ const MembersTab = () => { !currentUserIsAdmin || targetPermissionsAreUsersOwn } - > - - - + options={ + <> + + + + } + > )} {(targetPermissionsAreUsersOwn || currentUserIsAdmin) && ( @@ -600,43 +600,43 @@ const MembersTab = () => { {subscription == null || subscription.status === 'inactive' ? ( <> - +

Upgrade to invite guests. Guests are people external to your team who you want to work with on specific documents. They can be invited to individual documents but not an entire workspace. {` `} - See how it works - - + +

- { openSettingsTab('teamUpgrade') }} > Start Free Trial - + ) : (
- Current Guests +

Current Guests

{fetching.has('guestEmails') && ( )}
- +

Guests are people external to your team who you want to work with on specific documents. They can be invited to individual documents but not an entire workspace. - +

{permissions.length > 0 ? `${ @@ -787,4 +787,23 @@ const StyledGuestInactiveText = styled.p` margin-bottom: ${({ theme }) => theme.space.default}px; ` +const StyledMembername = styled.div` + display: flex; + align-items: center; + flex: 1 1 auto; + + p { + margin: 0; + color: ${({ theme }) => theme.baseTextColor}; + padding-right: ${({ theme }) => theme.space.xsmall}px; + } + + span { + color: ${({ theme }) => theme.subtleTextColor}; + margin-left: ${({ theme }) => theme.space.xsmall}px; + font-size: ${({ theme }) => theme.fontSizes.small}px; + padding: 2px 5px; + } +` + export default MembersTab diff --git a/src/cloud/components/organisms/settings/PersonalInfoTab.tsx b/src/cloud/components/organisms/settings/PersonalInfoTab.tsx index bdf2fb4c42..1d1048f424 100644 --- a/src/cloud/components/organisms/settings/PersonalInfoTab.tsx +++ b/src/cloud/components/organisms/settings/PersonalInfoTab.tsx @@ -15,6 +15,7 @@ import SettingTabContent from '../../../../shared/components/organisms/Settings/ import SettingInput from '../../../../shared/components/organisms/Settings/atoms/SettingInput' import SettingSelect from '../../../../shared/components/organisms/Settings/atoms/SettingSelect' import Button from '../../../../shared/components/atoms/Button' +import SettingDivider from '../../../../shared/components/organisms/Settings/atoms/SettingDivider' const PersonalInfoTab = () => { const { @@ -183,8 +184,9 @@ const PersonalInfoTab = () => { } footer={ <> +

{t('settings.account.delete')}

-

+

You may delete your account at any time, note that this is unrecoverable.{' '} diff --git a/src/cloud/components/organisms/settings/PreferencesTab.tsx b/src/cloud/components/organisms/settings/PreferencesTab.tsx index 6214e43e7f..2c4c6757df 100644 --- a/src/cloud/components/organisms/settings/PreferencesTab.tsx +++ b/src/cloud/components/organisms/settings/PreferencesTab.tsx @@ -9,6 +9,7 @@ const PreferencesTab = () => { return ( } > ) diff --git a/src/cloud/components/organisms/settings/SubscriptionTab.tsx b/src/cloud/components/organisms/settings/SubscriptionTab.tsx index 21b5335666..b431ffa3fd 100644 --- a/src/cloud/components/organisms/settings/SubscriptionTab.tsx +++ b/src/cloud/components/organisms/settings/SubscriptionTab.tsx @@ -1,6 +1,5 @@ import React, { useCallback, useState, useEffect } from 'react' import { useTranslation } from 'react-i18next' -import { StyledSmallFont } from './styled' import { usePage } from '../../../lib/stores/pageStore' import { PageStoreWithTeam } from '../../../interfaces/pageStore' import { Elements } from '@stripe/react-stripe-js' @@ -82,7 +81,7 @@ const SubscriptionTab = () => { title={t('settings.teamSubscription')} body={

- +

{formtab == null ? ( { ) : null} )} - +

} > diff --git a/src/cloud/components/organisms/settings/TeamInfoTab.tsx b/src/cloud/components/organisms/settings/TeamInfoTab.tsx index bf85753816..70ee2670c4 100644 --- a/src/cloud/components/organisms/settings/TeamInfoTab.tsx +++ b/src/cloud/components/organisms/settings/TeamInfoTab.tsx @@ -1,5 +1,4 @@ import React, { useMemo } from 'react' -import { SectionDescription, SectionSeparator } from './styled' import { usePage } from '../../../lib/stores/pageStore' import { PageStoreWithTeam } from '../../../interfaces/pageStore' import { useTranslation } from 'react-i18next' @@ -7,6 +6,7 @@ import { useSettings } from '../../../lib/stores/settings' import TeamLink from '../../atoms/Link/TeamLink' import SettingsTeamForm from '../../molecules/SettingsTeamForm' import SettingTabContent from '../../../../shared/components/organisms/Settings/atoms/SettingTabContent' +import SettingDivider from '../../../../shared/components/organisms/Settings/atoms/SettingDivider' const TeamInfoTab = () => { const { team, currentUserPermissions } = usePage() @@ -25,11 +25,10 @@ const TeamInfoTab = () => { return ( <> - +
- Space Deletion - - +

Delete Space

+

Once you delete this space we will remove all associated data. There is no turning back.{' '} { > {t('general.delete')} - +

) @@ -52,12 +51,13 @@ const TeamInfoTab = () => { return ( - {adminContent}
} + footer={adminContent} > ) } diff --git a/src/cloud/components/organisms/settings/UpgradeTab.tsx b/src/cloud/components/organisms/settings/UpgradeTab.tsx index d72cbfdc7c..ef38f5faab 100644 --- a/src/cloud/components/organisms/settings/UpgradeTab.tsx +++ b/src/cloud/components/organisms/settings/UpgradeTab.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect, useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' -import { SectionRow, StyledSmallFont } from './styled' +import { SectionRow } from './styled' import { usePage } from '../../../lib/stores/pageStore' import { PageStoreWithTeam } from '../../../interfaces/pageStore' import { Elements } from '@stripe/react-stripe-js' @@ -77,6 +77,7 @@ const UpgradeTab = () => { return ( {showTrialPopup && ( @@ -86,7 +87,7 @@ const UpgradeTab = () => { /> )}
- +

{ . - +

} @@ -121,7 +122,7 @@ const UpgradeTab = () => { title={t('settings.teamUpgrade')} body={
- +

{currentUserPermissions.role !== 'admin' ? ( Only admins can access this content. @@ -144,7 +145,7 @@ const UpgradeTab = () => { ) )} - +

} >
diff --git a/src/cloud/components/organisms/settings/UserPreferencesForm.tsx b/src/cloud/components/organisms/settings/UserPreferencesForm.tsx index 5ad08d424e..33f4c1be46 100644 --- a/src/cloud/components/organisms/settings/UserPreferencesForm.tsx +++ b/src/cloud/components/organisms/settings/UserPreferencesForm.tsx @@ -1,5 +1,4 @@ import React, { useCallback } from 'react' -import { SectionSelect, SectionHeader3 } from './styled' import { useSettings, GeneralThemeOptions, @@ -14,6 +13,7 @@ import { useTranslation } from 'react-i18next' import { SelectChangeEventHandler } from '../../../lib/utils/events' import { trackEvent } from '../../../api/track' import { MixpanelActionTrackTypes } from '../../../interfaces/analytics/mixpanel' +import SettingSelect from '../../../../shared/components/organisms/Settings/atoms/SettingSelect' const UserPreferencesForm = () => { const { settings, setSettings } = useSettings() @@ -88,64 +88,101 @@ const UserPreferencesForm = () => { ) return ( -
- {t('settings.applicationTheme')} - - - - + <> +
+ + + + + } + > +
- {t('settings.editorTheme')} - - {codeMirrorEditorThemes.map((val) => ( - - ))} - - {t('settings.codeblockTheme')} - - {codeMirrorEditorThemes.map((val) => ( - - ))} - - {t('settings.editorKeyMap')} - - {codeMirrorKeyMap.map((val) => ( - - ))} - - Editor Indent Type - - - - - Editor Indent Size - - - - - -
+
+ + {codeMirrorEditorThemes.map((val) => ( + + ))} + + } + > +
+ +
+ + {codeMirrorEditorThemes.map((val) => ( + + ))} + + } + > +
+ +
+ + {codeMirrorKeyMap.map((val) => ( + + ))} + + } + > +
+ +
+ + + + + } + > +
+ +
+ + + + + + } + > +
+ ) } diff --git a/src/cloud/components/organisms/settings/styled.ts b/src/cloud/components/organisms/settings/styled.ts index 5e1d521fe8..d60d4fbb0c 100644 --- a/src/cloud/components/organisms/settings/styled.ts +++ b/src/cloud/components/organisms/settings/styled.ts @@ -1,91 +1,5 @@ import styled from '../../../lib/styled' -import { - selectStyle, - primaryButtonStyle, - secondaryButtonStyle, - inputStyle, - baseIconStyle, - baseButtonStyle, -} from '../../../lib/styled/styleFunctions' - -export const Section = styled.section` - padding: ${({ theme }) => theme.space.xsmall}px 0; -` - -export const SectionHeader2 = styled.h2` - margin: ${({ theme }) => theme.space.medium}px 0 - ${({ theme }) => theme.space.default}px; - font-size: ${({ theme }) => theme.fontSizes.default}px; - font-weight: 500; -` - -export const SectionHeader3 = styled.h3` - display: inline-block; - width: 40%; - margin: ${({ theme }) => theme.space.default}px 0; - font-size: ${({ theme }) => theme.fontSizes.small}px; - font-weight: 500; -` - -export const SectionLabel = styled.label` - display: inline-block; - width: 40%; - color: ${({ theme }) => theme.emphasizedTextColor}; - font-size: ${({ theme }) => theme.fontSizes.small}px; -` - -export const SectionParagraph = styled.div` - display: block; - color: ${({ theme }) => theme.emphasizedTextColor}; - font-size: ${({ theme }) => theme.fontSizes.default}px; -` - -export const SectionSubtleText = styled.p` - color: ${({ theme }) => theme.subtleTextColor}; -` - -export const PrimaryAnchor = styled.a` - color: ${({ theme }) => theme.primaryBackgroundColor}; - text-decoration: none; - &:hover { - color: ${({ theme }) => theme.darkerPrimaryBackgroundColor}; - text-decoration: underline; - } -` - -export const SectionSelect = styled.select` - ${selectStyle} - min-width: 200px; - width: 60%; - height: 40px; - padding: 0 ${({ theme }) => theme.space.small}px; - border-radius: 2px; - - option { - color: initial; - } -` - -export const SectionPrimaryButton = styled.button` - ${baseButtonStyle} - ${primaryButtonStyle} - vertical-align: middle; - align-items: center; - - svg.icon { - position: relative; - color: ${({ theme }) => theme.whiteTextColor}; - transform: none; - top: 0; - left: 0; - } -` - -export const SectionSecondaryButton = styled.button` - ${baseButtonStyle} - ${secondaryButtonStyle} - align-items: center; -` +import { baseIconStyle } from '../../../lib/styled/styleFunctions' export const SectionList = styled.ul` margin: 0; @@ -129,44 +43,6 @@ export const SectionInLineIcon = styled.span` ${baseIconStyle} ` -export const SectionInput = styled.input` - ${inputStyle} - flex-grow: 1; - flex-shrink: 1; - min-width: 200px; - width: 60%; - height: 40px; - padding: ${({ theme }) => theme.space.xsmall}px - ${({ theme }) => theme.space.small}px; - border-radius: 2px; -` - -export const SectionTextarea = styled.textarea` - ${inputStyle} - flex-grow: 1; - flex-shrink: 1; - min-width: 200px; - width: 100%; - height: 200px; - padding: ${({ theme }) => theme.space.xsmall}px - ${({ theme }) => theme.space.small}px; - border-radius: 2px; - resize: none; -` - -export const SectionProfilePic = styled.div` - margin-top: ${({ theme }) => theme.space.default}px; -` - -export const SectionFooter = styled.div` - flex-grow: 0; - flex-shrink: 0; - padding: ${({ theme }) => theme.space.small}px - ${({ theme }) => theme.space.default}px; - border-top: 1px solid ${({ theme }) => theme.baseBorderColor}; - text-align: center; -` - export const SectionIntroduction = styled.div` .setHeight { display: block; @@ -218,10 +94,6 @@ export const SectionFlexRow = styled.div` } ` -export const StyledSmallFont = styled.div` - font-size: ${({ theme }) => theme.fontSizes.small}px; -` - export const SectionFlexDualButtons = styled.div` display: flex; align-items: center; @@ -242,42 +114,3 @@ export const SectionFlexDualButtons = styled.div` } } ` - -export const SectionDescription = styled.small` - color: ${({ theme }) => theme.subtleTextColor}; - display: block; - margin-bottom: ${({ theme }) => theme.space.xsmall}px; - line-height: 1.6; -` - -export const SectionFlexLeft = styled.div` - display: flex; - justify-content: flex-start; - margin: ${({ theme }) => theme.space.medium}px 0; -` - -export const SectionSeparator = styled.div` - background-color: ${({ theme }) => theme.baseBorderColor}; - width: 100%; - margin: 120px 0 40px 0; - height: 1px; -` - -export const StyledMembername = styled.div` - display: flex; - align-items: center; - flex: 1 1 auto; - - p { - margin: 0; - color: ${({ theme }) => theme.baseTextColor}; - padding-right: ${({ theme }) => theme.space.xsmall}px; - } - - span { - color: ${({ theme }) => theme.subtleTextColor}; - margin-left: ${({ theme }) => theme.space.xsmall}px; - font-size: ${({ theme }) => theme.fontSizes.small}px; - padding: 2px 5px; - } -` From d51e7ab2a2048025fe585afcb9ebc06dd509f333 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Sun, 9 May 2021 18:52:26 +0900 Subject: [PATCH 39/91] Added placeholder --- src/shared/components/organisms/Settings/atoms/SettingInput.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/shared/components/organisms/Settings/atoms/SettingInput.tsx b/src/shared/components/organisms/Settings/atoms/SettingInput.tsx index 3ebb720239..1bcd2decf0 100644 --- a/src/shared/components/organisms/Settings/atoms/SettingInput.tsx +++ b/src/shared/components/organisms/Settings/atoms/SettingInput.tsx @@ -4,6 +4,7 @@ import styled from '../../../../lib/styled' interface SettingInputProps { label?: string value?: string + placeholder?: string onChange?: React.ChangeEventHandler } From 83cabf507272cf685ad910b2660751e73f1b902e Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Sun, 9 May 2021 18:52:54 +0900 Subject: [PATCH 40/91] Added value & style --- .../components/organisms/Settings/atoms/SettingSelect.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/shared/components/organisms/Settings/atoms/SettingSelect.tsx b/src/shared/components/organisms/Settings/atoms/SettingSelect.tsx index 408d7c87b7..7123519e7c 100644 --- a/src/shared/components/organisms/Settings/atoms/SettingSelect.tsx +++ b/src/shared/components/organisms/Settings/atoms/SettingSelect.tsx @@ -3,9 +3,10 @@ import styled from '../../../../lib/styled' interface SettingSelectProps { label?: string - value?: string + value?: string | number disabled?: boolean onChange?: (val: any) => void + style?: React.CSSProperties options: React.ReactNode } From ef43ab349c3445bdae20e187b8fa5576c4fdfe72 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Sun, 9 May 2021 19:21:22 +0900 Subject: [PATCH 41/91] Adjusted Modal z-index --- src/shared/components/organisms/Settings/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/components/organisms/Settings/index.tsx b/src/shared/components/organisms/Settings/index.tsx index cb44067480..bc6125bc76 100644 --- a/src/shared/components/organisms/Settings/index.tsx +++ b/src/shared/components/organisms/Settings/index.tsx @@ -16,7 +16,7 @@ const Settings = ({ sidebar, content }: SettingsProps) => ( ) -const zIndexModals = 8001 +const zIndexModals = 8000 const Container = styled.div` z-index: ${zIndexModals}; From f435c8e2de81a306b9dee0b457d052c772b17a20 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 10 May 2021 13:13:27 +0900 Subject: [PATCH 42/91] EmphasizedLink -> SettingLink --- src/cloud/components/atoms/Link/AccountLink.tsx | 6 +++--- .../components/atoms/Link/EmphasizedLink.tsx | 15 --------------- src/cloud/components/atoms/Link/TeamLink.tsx | 6 +++--- .../organisms/Settings/atoms/SettingLink.tsx | 16 ++++++++++------ 4 files changed, 16 insertions(+), 27 deletions(-) delete mode 100644 src/cloud/components/atoms/Link/EmphasizedLink.tsx diff --git a/src/cloud/components/atoms/Link/AccountLink.tsx b/src/cloud/components/atoms/Link/AccountLink.tsx index bf8858a018..94212d4282 100644 --- a/src/cloud/components/atoms/Link/AccountLink.tsx +++ b/src/cloud/components/atoms/Link/AccountLink.tsx @@ -1,6 +1,6 @@ import React, { FC } from 'react' import querystring from 'querystring' -import EmphasizedLink from './EmphasizedLink' +import SettingLink from '../../../../shared/components/organisms/Settings/atoms/SettingLink' export type AccountLinkIntent = 'delete' @@ -23,7 +23,7 @@ const AccountLink: FC = ({ beforeNavigate, }) => { return ( - = ({ draggable={draggable} > {children} - + ) } diff --git a/src/cloud/components/atoms/Link/EmphasizedLink.tsx b/src/cloud/components/atoms/Link/EmphasizedLink.tsx deleted file mode 100644 index 7f2c4ba992..0000000000 --- a/src/cloud/components/atoms/Link/EmphasizedLink.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import styled from '../../../lib/styled' -import Link from './Link' - -const EmphasizedLink = styled(Link)` - a { - color: ${({ theme }) => theme.primaryTextColor}; - - &:hover, - &:focus { - text-decoration: none; - } - } -` - -export default EmphasizedLink diff --git a/src/cloud/components/atoms/Link/TeamLink.tsx b/src/cloud/components/atoms/Link/TeamLink.tsx index 5703950545..1d326b0596 100644 --- a/src/cloud/components/atoms/Link/TeamLink.tsx +++ b/src/cloud/components/atoms/Link/TeamLink.tsx @@ -2,7 +2,7 @@ import React, { useCallback } from 'react' import querystring from 'querystring' import { SerializedTeam } from '../../../interfaces/db/team' import { useRouter } from '../../../lib/router' -import EmphasizedLink from './EmphasizedLink' +import SettingLink from '../../../../shared/components/organisms/Settings/atoms/SettingLink' export type TeamLinkIntent = | 'index' @@ -47,7 +47,7 @@ const TeamLink = ({ tabIndex = 0, }: TeamLinkProps) => { return ( - {children} - + ) } diff --git a/src/shared/components/organisms/Settings/atoms/SettingLink.tsx b/src/shared/components/organisms/Settings/atoms/SettingLink.tsx index 489a2eae99..f7533cc382 100644 --- a/src/shared/components/organisms/Settings/atoms/SettingLink.tsx +++ b/src/shared/components/organisms/Settings/atoms/SettingLink.tsx @@ -1,12 +1,16 @@ import styled from '../../../../lib/styled' +import Link from '../../../atoms/Link' -const SettingLink = styled.a` - color: ${({ theme }) => theme.colors.text.link}; - text-decoration: none; - - &:hover { +const SettingLink = styled(Link)` + a { color: ${({ theme }) => theme.colors.text.link}; - text-decoration: underline; + text-decoration: none; + + &:hover, + &:focus { + color: ${({ theme }) => theme.colors.text.link}; + text-decoration: underline; + } } ` From db32a659754b6af359f95d9350c4f72328cf23c2 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 10 May 2021 13:42:09 +0900 Subject: [PATCH 43/91] IconInput -> SettingIconInput --- src/cloud/components/molecules/IconInput.tsx | 94 -------------------- 1 file changed, 94 deletions(-) delete mode 100644 src/cloud/components/molecules/IconInput.tsx diff --git a/src/cloud/components/molecules/IconInput.tsx b/src/cloud/components/molecules/IconInput.tsx deleted file mode 100644 index 2c2fa58daf..0000000000 --- a/src/cloud/components/molecules/IconInput.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import React, { useState, useCallback } from 'react' -import styled from '../../lib/styled' -import { - secondaryButtonStyle, - baseButtonStyle, -} from '../../lib/styled/styleFunctions' - -interface IconInputProps { - onChange?: (file: File) => void - defaultUrl?: string - shape?: 'square' | 'circle' -} - -const IconInput = ({ - onChange, - defaultUrl, - shape = 'square', -}: IconInputProps) => { - const [fileUrl, setFileUrl] = useState(null) - - const changeHandler: React.ChangeEventHandler = useCallback( - (event) => { - if ( - event.target != null && - event.target.files != null && - event.target.files.length > 0 - ) { - const file = event.target.files[0] - setFileUrl(URL.createObjectURL(file)) - if (onChange != null) { - onChange(file) - } - } - }, - [onChange] - ) - - return ( - - - - - - Select Image... - - - - ) -} - -export default IconInput - -const StyledIcon = styled.div` - display: flex; - align-items: center; -` - -const StyledIconImage = styled.div` - width: 90px; - height: 90px; -` - -const StyledIconImageContent = styled.img<{ shape: string }>` - object-fit: cover; - width: 100%; - height: 100%; - background: ${({ theme }) => theme.secondaryBackgroundColor}; - border: 1px solid ${({ theme }) => theme.secondaryBorderColor}; - border-radius: ${(props: any) => (props.shape === 'circle' ? '100%' : '0')}; -` - -const StyledIconInputLabel = styled.label` - position: relative; - cursor: pointer; - margin-left: ${({ theme }) => theme.space.default}px; - - & > span { - display: block; - padding: 8px 16px; - border-radius: 5px; - ${baseButtonStyle} - ${secondaryButtonStyle} - } - - & > input[type='file'] { - position: absolute; - opacity: 0; - height: 0px; - width: 0px; - } -` From 105b2ffb9cfccc4dbbd62639d245dba5620f97c2 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 10 May 2021 13:42:32 +0900 Subject: [PATCH 44/91] IconInput -> SettingIconInput --- .../organisms/settings/PersonalInfoTab.tsx | 8 +- .../Settings/atoms/SettingIconInput.tsx | 100 ++++++++++++++++++ 2 files changed, 102 insertions(+), 6 deletions(-) create mode 100644 src/shared/components/organisms/Settings/atoms/SettingIconInput.tsx diff --git a/src/cloud/components/organisms/settings/PersonalInfoTab.tsx b/src/cloud/components/organisms/settings/PersonalInfoTab.tsx index 1d1048f424..4790229c4c 100644 --- a/src/cloud/components/organisms/settings/PersonalInfoTab.tsx +++ b/src/cloud/components/organisms/settings/PersonalInfoTab.tsx @@ -3,7 +3,6 @@ import { useTranslation } from 'react-i18next' import { useGlobalData } from '../../../lib/stores/globalData' import { saveUserInfo, updateUserIcon } from '../../../api/users' import { buildIconUrl } from '../../../api/files' -import IconInput from '../../molecules/IconInput' import { Spinner } from '../../atoms/Spinner' import { useSettings } from '../../../lib/stores/settings' import AccountLink from '../../atoms/Link/AccountLink' @@ -16,6 +15,7 @@ import SettingInput from '../../../../shared/components/organisms/Settings/atoms import SettingSelect from '../../../../shared/components/organisms/Settings/atoms/SettingSelect' import Button from '../../../../shared/components/atoms/Button' import SettingDivider from '../../../../shared/components/organisms/Settings/atoms/SettingDivider' +import SettingIconInput from '../../../../shared/components/organisms/Settings/atoms/SettingIconInput' const PersonalInfoTab = () => { const { @@ -118,11 +118,7 @@ const PersonalInfoTab = () => { {currentUser != null && ( <>
- +
void + defaultUrl?: string +} + +const SettingIconInput = ({ onChange, defaultUrl }: SettingIconInputProps) => { + const [fileUrl, setFileUrl] = useState(null) + + const changeHandler: React.ChangeEventHandler = useCallback( + (event) => { + if ( + event.target != null && + event.target.files != null && + event.target.files.length > 0 + ) { + const file = event.target.files[0] + setFileUrl(URL.createObjectURL(file)) + if (onChange != null) { + onChange(file) + } + } + }, + [onChange] + ) + + return ( + +
+ +
+ +
+ ) +} + +export default SettingIconInput + +const Container = styled.div` + display: flex; + align-items: center; + + .setting__icon-input__img { + width: 90px; + height: 90px; + } + + .setting__icon-input__img__content { + object-fit: cover; + width: 100%; + height: 100%; + background: ${({ theme }) => theme.colors.background.secondary}; + border: 1px solid ${({ theme }) => theme.colors.border.second}; + border-radius: 50%; + } + + .setting__icon-input__label { + position: relative; + cursor: pointer; + margin-left: ${({ theme }) => theme.sizes.spaces.df}px; + + & > span { + display: flex; + align-items: center; + height: 32px; + padding: 0 ${({ theme }) => theme.sizes.spaces.md}px; + background-color: ${({ theme }) => theme.colors.variants.secondary.base}; + border-radius: 4px; + color: ${({ theme }) => theme.colors.variants.secondary.text}; + font-size: ${({ theme }) => theme.sizes.fonts.df}px; + transition: 200ms background-color; + + &.focus { + filter: brightness(103%); + } + &:hover { + filter: brightness(106%); + } + &:active, + &.button__state--active { + filter: brightness(112%); + } + } + + & > input[type='file'] { + position: absolute; + opacity: 0; + height: 0px; + width: 0px; + } + } +` From fbf3bedb49bd545421543d7a65192cb365bc34e9 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 10 May 2021 13:43:00 +0900 Subject: [PATCH 45/91] Removed Unnecessary Styles --- src/shared/components/atoms/Button.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/shared/components/atoms/Button.tsx b/src/shared/components/atoms/Button.tsx index d912e43fca..2d497a758c 100644 --- a/src/shared/components/atoms/Button.tsx +++ b/src/shared/components/atoms/Button.tsx @@ -144,7 +144,6 @@ export default Button const StyledButton = styled.button` padding: 0 ${({ theme }) => theme.sizes.spaces.md}px; - border-radius: 2px; font-size: ${({ theme }) => theme.sizes.fonts.df}px; height: 32px; outline: none; @@ -157,7 +156,6 @@ const StyledButton = styled.button` display: inline-flex; align-items: center; justify-content: center; - font-family: Arial; box-sizing: border-box; transition: 200ms background-color; width: auto; From d805a9fcf59447cd1e87d5f4efeeaf84333eb642 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 10 May 2021 13:49:03 +0900 Subject: [PATCH 46/91] Removed {} --- src/cloud/components/organisms/settings/ApiTab.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cloud/components/organisms/settings/ApiTab.tsx b/src/cloud/components/organisms/settings/ApiTab.tsx index 9d5ba08a9a..a256b351d7 100644 --- a/src/cloud/components/organisms/settings/ApiTab.tsx +++ b/src/cloud/components/organisms/settings/ApiTab.tsx @@ -35,8 +35,8 @@ const ApiTab = () => { return (
From 97299560091c669a8234f6c9059de949d9fd707d Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 10 May 2021 13:49:23 +0900 Subject: [PATCH 47/91] Put subtle text to description --- src/cloud/components/organisms/settings/IntegrationsTab.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/cloud/components/organisms/settings/IntegrationsTab.tsx b/src/cloud/components/organisms/settings/IntegrationsTab.tsx index fff4ff0312..57162342d5 100644 --- a/src/cloud/components/organisms/settings/IntegrationsTab.tsx +++ b/src/cloud/components/organisms/settings/IntegrationsTab.tsx @@ -40,13 +40,11 @@ const IntegrationsTab = () => { return (
-

- Connect 3rd party content to your Boost Note for Teams documents. -

From 45074a33d91e3798f60e0d234191eb3a3e4e7965 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 10 May 2021 14:08:36 +0900 Subject: [PATCH 48/91] TokenCreate -> SettingTokenCreate --- .../components/molecules/TokenCreate.tsx | 51 ------------------- .../components/organisms/settings/ApiTab.tsx | 6 +-- .../Settings/atoms/SettingTokenCreate.tsx | 49 ++++++++++++++++++ 3 files changed, 52 insertions(+), 54 deletions(-) delete mode 100644 src/cloud/components/molecules/TokenCreate.tsx create mode 100644 src/shared/components/organisms/Settings/atoms/SettingTokenCreate.tsx diff --git a/src/cloud/components/molecules/TokenCreate.tsx b/src/cloud/components/molecules/TokenCreate.tsx deleted file mode 100644 index 134b7488cf..0000000000 --- a/src/cloud/components/molecules/TokenCreate.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import React, { useState, useCallback, ChangeEvent } from 'react' -import CustomButton from '../atoms/buttons/CustomButton' -import Flexbox from '../atoms/Flexbox' -import styled from '../../lib/styled' -import SettingInput from '../../../shared/components/organisms/Settings/atoms/SettingInput' - -interface TokenCreateProps { - onCreate: (name: string) => void -} - -const TokenCreate = ({ onCreate }: TokenCreateProps) => { - const [name, setName] = useState('') - - const create = useCallback(() => { - onCreate(name) - }, [name, onCreate]) - - return ( - -

Create a token

-
- ) => - setName(e.target.value) - } - > -
- - {name.length === 0 && ( - Enter a name - )} - - Create - - -
- ) -} - -export default TokenCreate - -const StyledTokenCreate = styled.div` - width: 100%; -` - -const StyledWarningText = styled.small` - margin-right: ${({ theme }) => theme.space.xsmall}px; - color: ${({ theme }) => theme.warningTextColor}; -` diff --git a/src/cloud/components/organisms/settings/ApiTab.tsx b/src/cloud/components/organisms/settings/ApiTab.tsx index a256b351d7..e9043217f6 100644 --- a/src/cloud/components/organisms/settings/ApiTab.tsx +++ b/src/cloud/components/organisms/settings/ApiTab.tsx @@ -3,14 +3,14 @@ import styled from '../../../lib/styled' import Spinner from '../../atoms/CustomSpinner' import { useApiTokens, withApiTokens } from '../../../lib/stores/apiTokens' import TokenControl from '../../molecules/TokenControl' -import TokenCreate from '../../molecules/TokenCreate' import { usePage } from '../../../lib/stores/pageStore' -import Flexbox from '../../atoms/Flexbox' import Icon from '../../atoms/IconMdi' import { mdiOpenInNew } from '@mdi/js' import SettingTabContent from '../../../../shared/components/organisms/Settings/atoms/SettingTabContent' import SettingLink from '../../../../shared/components/organisms/Settings/atoms/SettingLink' import Button from '../../../../shared/components/atoms/Button' +import SettingTokenCreate from '../../../../shared/components/organisms/Settings/atoms/SettingTokenCreate' +import Flexbox from '../../../../shared/components/atoms/Flexbox' const ApiTab = () => { const { team } = usePage() @@ -70,7 +70,7 @@ const ApiTab = () => { {tokenCreateMode && ( - + )} diff --git a/src/shared/components/organisms/Settings/atoms/SettingTokenCreate.tsx b/src/shared/components/organisms/Settings/atoms/SettingTokenCreate.tsx new file mode 100644 index 0000000000..9ae0a760d7 --- /dev/null +++ b/src/shared/components/organisms/Settings/atoms/SettingTokenCreate.tsx @@ -0,0 +1,49 @@ +import React, { useState, useCallback, ChangeEvent } from 'react' +import styled from '../../../../lib/styled' +import Button from '../../../atoms/Button' +import Flexbox from '../../../atoms/Flexbox' +import SettingInput from './SettingInput' + +interface SettingTokenCreateProps { + onCreate: (name: string) => void +} + +const SettingTokenCreate = ({ onCreate }: SettingTokenCreateProps) => { + const [name, setName] = useState('') + + const create = useCallback(() => { + onCreate(name) + }, [name, onCreate]) + + return ( + +

Create a token

+
+ ) => + setName(e.target.value) + } + > +
+ + {name.length === 0 &&

Enter a name

} + +
+
+ ) +} + +export default SettingTokenCreate + +const Container = styled.div` + width: 100%; + + .text--warning { + margin-right: ${({ theme }) => theme.sizes.spaces.sm}px; + color: ${({ theme }) => theme.colors.variants.warning.base}; + } +` From 6b8fc7897644d9582e73056d2ee2cb12a7bbbec1 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 10 May 2021 14:08:59 +0900 Subject: [PATCH 49/91] Changed to Shared Component Flexbox --- src/cloud/components/organisms/settings/MembersTab.tsx | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/cloud/components/organisms/settings/MembersTab.tsx b/src/cloud/components/organisms/settings/MembersTab.tsx index a807d53665..d43daf8686 100644 --- a/src/cloud/components/organisms/settings/MembersTab.tsx +++ b/src/cloud/components/organisms/settings/MembersTab.tsx @@ -21,7 +21,6 @@ import UserIcon from '../../atoms/UserIcon' import styled from '../../../lib/styled' import { arraysAreIdentical } from '../../../lib/utils/array' import { getUserEmailsFromPermissions } from '../../../api/teams/permissions/emails' -import Flexbox from '../../atoms/Flexbox' import { useRouter } from '../../../lib/router' import cc from 'classcat' import { useNav } from '../../../lib/stores/nav' @@ -44,6 +43,7 @@ import SettingTabContent from '../../../../shared/components/organisms/Settings/ import SettingSelect from '../../../../shared/components/organisms/Settings/atoms/SettingSelect' import SettingLink from '../../../../shared/components/organisms/Settings/atoms/SettingLink' import Button from '../../../../shared/components/atoms/Button' +import Flexbox from '../../../../shared/components/atoms/Flexbox' const MembersTab = () => { const { t } = useTranslation() @@ -354,11 +354,8 @@ const MembersTab = () => { return { type: MenuTypes.Component, component: ( - - + + {doc != null ? ( Date: Mon, 10 May 2021 14:32:43 +0900 Subject: [PATCH 50/91] Adjusted Token Section Styles --- src/cloud/components/molecules/TokenControl.tsx | 2 +- .../components/organisms/Settings/atoms/SettingTokenCreate.tsx | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cloud/components/molecules/TokenControl.tsx b/src/cloud/components/molecules/TokenControl.tsx index 0a1752b264..f9dea18bca 100644 --- a/src/cloud/components/molecules/TokenControl.tsx +++ b/src/cloud/components/molecules/TokenControl.tsx @@ -81,7 +81,7 @@ const TokenControl = ({ token, onUpdate, onDelete }: TokenControlProps) => { ) : ( <> -

{name}

+

{name}

setEdit(true)}> diff --git a/src/shared/components/organisms/Settings/atoms/SettingTokenCreate.tsx b/src/shared/components/organisms/Settings/atoms/SettingTokenCreate.tsx index 9ae0a760d7..3b702494b4 100644 --- a/src/shared/components/organisms/Settings/atoms/SettingTokenCreate.tsx +++ b/src/shared/components/organisms/Settings/atoms/SettingTokenCreate.tsx @@ -43,7 +43,8 @@ const Container = styled.div` width: 100%; .text--warning { - margin-right: ${({ theme }) => theme.sizes.spaces.sm}px; + margin-top: 0; + margin-right: ${({ theme }) => theme.sizes.spaces.df}px; color: ${({ theme }) => theme.colors.variants.warning.base}; } ` From c0cfb7b0e0c6b3971a46d0acde052c0dd81dac4b Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 10 May 2021 15:24:56 +0900 Subject: [PATCH 51/91] Added SettingTokenCreate --- .../Settings/molecules/SettingTokenCreate.tsx | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/shared/components/organisms/Settings/molecules/SettingTokenCreate.tsx diff --git a/src/shared/components/organisms/Settings/molecules/SettingTokenCreate.tsx b/src/shared/components/organisms/Settings/molecules/SettingTokenCreate.tsx new file mode 100644 index 0000000000..666b07f918 --- /dev/null +++ b/src/shared/components/organisms/Settings/molecules/SettingTokenCreate.tsx @@ -0,0 +1,50 @@ +import React, { useState, useCallback, ChangeEvent } from 'react' +import styled from '../../../../lib/styled' +import Button from '../../../atoms/Button' +import Flexbox from '../../../atoms/Flexbox' +import SettingInput from '../atoms/SettingInput' + +interface SettingTokenCreateProps { + onCreate: (name: string) => void +} + +const SettingTokenCreate = ({ onCreate }: SettingTokenCreateProps) => { + const [name, setName] = useState('') + + const create = useCallback(() => { + onCreate(name) + }, [name, onCreate]) + + return ( + +

Create a token

+
+ ) => + setName(e.target.value) + } + > +
+ + {name.length === 0 &&

Enter a name

} + +
+
+ ) +} + +export default SettingTokenCreate + +const Container = styled.div` + width: 100%; + + .text--warning { + margin-top: 0; + margin-right: ${({ theme }) => theme.sizes.spaces.df}px; + color: ${({ theme }) => theme.colors.variants.warning.base}; + } +` From 60d766164d6ca494101a585d430ed76ce56628a3 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 10 May 2021 15:25:32 +0900 Subject: [PATCH 52/91] Moved Components --- .../Settings/atoms/SettingTokenCreate.tsx | 50 ------------------- .../{atoms => molecules}/SettingIconInput.tsx | 41 ++------------- .../SettingSidenavHeader.tsx | 0 3 files changed, 3 insertions(+), 88 deletions(-) delete mode 100644 src/shared/components/organisms/Settings/atoms/SettingTokenCreate.tsx rename src/shared/components/organisms/Settings/{atoms => molecules}/SettingIconInput.tsx (62%) rename src/shared/components/organisms/Settings/{atoms => molecules}/SettingSidenavHeader.tsx (100%) diff --git a/src/shared/components/organisms/Settings/atoms/SettingTokenCreate.tsx b/src/shared/components/organisms/Settings/atoms/SettingTokenCreate.tsx deleted file mode 100644 index 3b702494b4..0000000000 --- a/src/shared/components/organisms/Settings/atoms/SettingTokenCreate.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React, { useState, useCallback, ChangeEvent } from 'react' -import styled from '../../../../lib/styled' -import Button from '../../../atoms/Button' -import Flexbox from '../../../atoms/Flexbox' -import SettingInput from './SettingInput' - -interface SettingTokenCreateProps { - onCreate: (name: string) => void -} - -const SettingTokenCreate = ({ onCreate }: SettingTokenCreateProps) => { - const [name, setName] = useState('') - - const create = useCallback(() => { - onCreate(name) - }, [name, onCreate]) - - return ( - -

Create a token

-
- ) => - setName(e.target.value) - } - > -
- - {name.length === 0 &&

Enter a name

} - -
-
- ) -} - -export default SettingTokenCreate - -const Container = styled.div` - width: 100%; - - .text--warning { - margin-top: 0; - margin-right: ${({ theme }) => theme.sizes.spaces.df}px; - color: ${({ theme }) => theme.colors.variants.warning.base}; - } -` diff --git a/src/shared/components/organisms/Settings/atoms/SettingIconInput.tsx b/src/shared/components/organisms/Settings/molecules/SettingIconInput.tsx similarity index 62% rename from src/shared/components/organisms/Settings/atoms/SettingIconInput.tsx rename to src/shared/components/organisms/Settings/molecules/SettingIconInput.tsx index 1a59ac50a8..cbf5f65798 100644 --- a/src/shared/components/organisms/Settings/atoms/SettingIconInput.tsx +++ b/src/shared/components/organisms/Settings/molecules/SettingIconInput.tsx @@ -1,5 +1,6 @@ import React, { useState, useCallback } from 'react' import styled from '../../../../lib/styled' +import SettingIconInputLabel from '../atoms/SettingIconInputLabel' interface SettingIconInputProps { onChange?: (file: File) => void @@ -34,10 +35,10 @@ const SettingIconInput = ({ onChange, defaultUrl }: SettingIconInputProps) => { src={fileUrl != null ? fileUrl : defaultUrl} />
- + ) } @@ -61,40 +62,4 @@ const Container = styled.div` border: 1px solid ${({ theme }) => theme.colors.border.second}; border-radius: 50%; } - - .setting__icon-input__label { - position: relative; - cursor: pointer; - margin-left: ${({ theme }) => theme.sizes.spaces.df}px; - - & > span { - display: flex; - align-items: center; - height: 32px; - padding: 0 ${({ theme }) => theme.sizes.spaces.md}px; - background-color: ${({ theme }) => theme.colors.variants.secondary.base}; - border-radius: 4px; - color: ${({ theme }) => theme.colors.variants.secondary.text}; - font-size: ${({ theme }) => theme.sizes.fonts.df}px; - transition: 200ms background-color; - - &.focus { - filter: brightness(103%); - } - &:hover { - filter: brightness(106%); - } - &:active, - &.button__state--active { - filter: brightness(112%); - } - } - - & > input[type='file'] { - position: absolute; - opacity: 0; - height: 0px; - width: 0px; - } - } ` diff --git a/src/shared/components/organisms/Settings/atoms/SettingSidenavHeader.tsx b/src/shared/components/organisms/Settings/molecules/SettingSidenavHeader.tsx similarity index 100% rename from src/shared/components/organisms/Settings/atoms/SettingSidenavHeader.tsx rename to src/shared/components/organisms/Settings/molecules/SettingSidenavHeader.tsx From 9ec79eb072b49ec84eb61b1012ba01e111696237 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 10 May 2021 15:25:57 +0900 Subject: [PATCH 53/91] Added SettingTabSelector --- .../organisms/settings/MembersTab.tsx | 31 +++---------------- .../Settings/atoms/SettingTabSelector.tsx | 28 +++++++++++++++++ 2 files changed, 32 insertions(+), 27 deletions(-) create mode 100644 src/shared/components/organisms/Settings/atoms/SettingTabSelector.tsx diff --git a/src/cloud/components/organisms/settings/MembersTab.tsx b/src/cloud/components/organisms/settings/MembersTab.tsx index d43daf8686..90e65328df 100644 --- a/src/cloud/components/organisms/settings/MembersTab.tsx +++ b/src/cloud/components/organisms/settings/MembersTab.tsx @@ -44,6 +44,7 @@ import SettingSelect from '../../../../shared/components/organisms/Settings/atom import SettingLink from '../../../../shared/components/organisms/Settings/atoms/SettingLink' import Button from '../../../../shared/components/atoms/Button' import Flexbox from '../../../../shared/components/atoms/Flexbox' +import SettingTabSelector from '../../../../shared/components/organisms/Settings/atoms/SettingTabSelector' const MembersTab = () => { const { t } = useTranslation() @@ -420,7 +421,7 @@ const MembersTab = () => { return ( + - + } description={'Manage who access to this space.'} body={ @@ -693,31 +694,6 @@ const MembersTab = () => { ) } -const TabSelector = styled.div` - display: flex; - button { - background: transparent; - margin-bottom: ${({ theme }) => theme.space.xxsmall}px; - font-size: ${({ theme }) => theme.fontSizes.medium}px; - color: ${({ theme }) => theme.subtleTextColor}; - border-bottom: 1px solid transparent; - cursor: pointer; - outline: none; - - &:hover { - color: ${({ theme }) => theme.emphasizedTextColor}; - } - - &.active { - color: ${({ theme }) => theme.emphasizedTextColor}; - border-color: ${({ theme }) => theme.emphasizedTextColor}; - } - } - button:first-of-type { - margin-right: ${({ theme }) => theme.space.medium}px; - } -` - const TopMargin = styled.div` margin-top: ${({ theme }) => theme.space.medium}px; ` @@ -782,6 +758,7 @@ const StyledMembersTable = styled.table` const StyledGuestInactiveText = styled.p` margin-top: ${({ theme }) => theme.space.medium}px; margin-bottom: ${({ theme }) => theme.space.default}px; + line-height: 1.6; ` const StyledMembername = styled.div` diff --git a/src/shared/components/organisms/Settings/atoms/SettingTabSelector.tsx b/src/shared/components/organisms/Settings/atoms/SettingTabSelector.tsx new file mode 100644 index 0000000000..5b2e57ea18 --- /dev/null +++ b/src/shared/components/organisms/Settings/atoms/SettingTabSelector.tsx @@ -0,0 +1,28 @@ +import styled from '../../../../lib/styled' + +const SettingTabSelector = styled.div` + display: flex; + + button { + padding: 0; + background: transparent; + color: ${({ theme }) => theme.colors.text.subtle}; + cursor: pointer; + font-size: ${({ theme }) => theme.sizes.fonts.l}px; + outline: none; + + &:hover { + color: ${({ theme }) => theme.colors.text.secondary}; + } + + &.active { + color: ${({ theme }) => theme.colors.text.primary}; + } + } + + button:first-of-type { + margin-right: ${({ theme }) => theme.sizes.spaces.xl}px; + } +` + +export default SettingTabSelector From 6edf82415b7579d794c3bb8a7f50063a20137b6e Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 10 May 2021 15:26:28 +0900 Subject: [PATCH 54/91] Added SettingIconInputLabel --- .../components/molecules/SettingsTeamForm.tsx | 47 +++++++------------ .../organisms/settings/PersonalInfoTab.tsx | 2 +- .../Settings/atoms/SettingIconInputLabel.tsx | 39 +++++++++++++++ 3 files changed, 58 insertions(+), 30 deletions(-) create mode 100644 src/shared/components/organisms/Settings/atoms/SettingIconInputLabel.tsx diff --git a/src/cloud/components/molecules/SettingsTeamForm.tsx b/src/cloud/components/molecules/SettingsTeamForm.tsx index 2c60b270f5..61a0b61116 100644 --- a/src/cloud/components/molecules/SettingsTeamForm.tsx +++ b/src/cloud/components/molecules/SettingsTeamForm.tsx @@ -8,11 +8,6 @@ import { useElectron } from '../../lib/stores/electron' import { useGlobalData } from '../../lib/stores/globalData' import { usePage } from '../../lib/stores/pageStore' import styled from '../../lib/styled' -import { - baseButtonStyle, - inputStyle, - inverseSecondaryButtonStyle, -} from '../../lib/styled/styleFunctions' import Flexbox from '../atoms/Flexbox' import Icon from '../atoms/Icon' import { Spinner } from '../atoms/Spinner' @@ -20,6 +15,8 @@ import { useRouter } from '../../lib/router' import { getTeamURL } from '../../lib/utils/patterns' import { useToast } from '../../../shared/lib/stores/toast' import Button from '../../../shared/components/atoms/Button' +import SettingInput from '../../../shared/components/organisms/Settings/atoms/SettingInput' +import SettingIconInputLabel from '../../../shared/components/organisms/Settings/atoms/SettingIconInputLabel' interface SettingsTeamFormProps { team: SerializedTeam @@ -165,22 +162,24 @@ const SettingsTeamForm = ({ ) : ( )} + + + {fileUrl == null ? 'Add a photo' : 'Change your photo'} + + +
- - - setName(ev.target.value)} @@ -190,7 +189,7 @@ const SettingsTeamForm = ({ - setDomain(ev.target.value)} @@ -234,12 +233,9 @@ const Container = styled.div` color: ${({ theme }) => theme.secondaryBorderColor}; } .profile__row { + display: flex; + align-items: center; margin-bottom: ${({ theme }) => theme.space.small}px; - text-align: center; - } - .profile__label { - ${baseButtonStyle} - ${inverseSecondaryButtonStyle} } #profile { display: none; @@ -271,13 +267,6 @@ const Container = styled.div` margin: 0; color: ${({ theme }) => theme.subtleTextColor}; } - input[type='text'] { - ${inputStyle}; - width: 100%; - height: 30px; - padding: ${({ theme }) => theme.space.xsmall}px - ${({ theme }) => theme.space.small}px; - } .description { font-size: ${({ theme }) => theme.fontSizes.xsmall}px; margin-top: ${({ theme }) => theme.space.xsmall}px; diff --git a/src/cloud/components/organisms/settings/PersonalInfoTab.tsx b/src/cloud/components/organisms/settings/PersonalInfoTab.tsx index 4790229c4c..d38a025067 100644 --- a/src/cloud/components/organisms/settings/PersonalInfoTab.tsx +++ b/src/cloud/components/organisms/settings/PersonalInfoTab.tsx @@ -15,7 +15,7 @@ import SettingInput from '../../../../shared/components/organisms/Settings/atoms import SettingSelect from '../../../../shared/components/organisms/Settings/atoms/SettingSelect' import Button from '../../../../shared/components/atoms/Button' import SettingDivider from '../../../../shared/components/organisms/Settings/atoms/SettingDivider' -import SettingIconInput from '../../../../shared/components/organisms/Settings/atoms/SettingIconInput' +import SettingIconInput from '../../../../shared/components/organisms/Settings/molecules/SettingIconInput' const PersonalInfoTab = () => { const { diff --git a/src/shared/components/organisms/Settings/atoms/SettingIconInputLabel.tsx b/src/shared/components/organisms/Settings/atoms/SettingIconInputLabel.tsx new file mode 100644 index 0000000000..23913cd809 --- /dev/null +++ b/src/shared/components/organisms/Settings/atoms/SettingIconInputLabel.tsx @@ -0,0 +1,39 @@ +import styled from '../../../../lib/styled' + +const SettingIconInputLabel = styled.label` + position: relative; + cursor: pointer; + margin-left: ${({ theme }) => theme.sizes.spaces.md}px; + + & > span { + display: flex; + align-items: center; + height: 32px; + padding: 0 ${({ theme }) => theme.sizes.spaces.md}px; + background-color: ${({ theme }) => theme.colors.variants.secondary.base}; + border-radius: 4px; + color: ${({ theme }) => theme.colors.variants.secondary.text}; + font-size: ${({ theme }) => theme.sizes.fonts.df}px; + transition: 200ms background-color; + + &.focus { + filter: brightness(103%); + } + &:hover { + filter: brightness(106%); + } + &:active, + &.button__state--active { + filter: brightness(112%); + } + } + + & > input[type='file'] { + position: absolute; + opacity: 0; + height: 0px; + width: 0px; + } +` + +export default SettingIconInputLabel From e57a1faa6990e74db58e6a549f0a11aae571d56e Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 10 May 2021 15:26:42 +0900 Subject: [PATCH 55/91] Added SettingTokenCreate --- src/cloud/components/organisms/settings/ApiTab.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cloud/components/organisms/settings/ApiTab.tsx b/src/cloud/components/organisms/settings/ApiTab.tsx index e9043217f6..a382d01b72 100644 --- a/src/cloud/components/organisms/settings/ApiTab.tsx +++ b/src/cloud/components/organisms/settings/ApiTab.tsx @@ -9,7 +9,7 @@ import { mdiOpenInNew } from '@mdi/js' import SettingTabContent from '../../../../shared/components/organisms/Settings/atoms/SettingTabContent' import SettingLink from '../../../../shared/components/organisms/Settings/atoms/SettingLink' import Button from '../../../../shared/components/atoms/Button' -import SettingTokenCreate from '../../../../shared/components/organisms/Settings/atoms/SettingTokenCreate' +import SettingTokenCreate from '../../../../shared/components/organisms/Settings/molecules/SettingTokenCreate' import Flexbox from '../../../../shared/components/atoms/Flexbox' const ApiTab = () => { From 31a6743fc4d1369cea6b6e7549b01816426fd039 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 10 May 2021 15:26:54 +0900 Subject: [PATCH 56/91] Fix Alignment --- src/shared/components/atoms/Button.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/shared/components/atoms/Button.tsx b/src/shared/components/atoms/Button.tsx index 2d497a758c..afa2106c93 100644 --- a/src/shared/components/atoms/Button.tsx +++ b/src/shared/components/atoms/Button.tsx @@ -164,6 +164,11 @@ const StyledButton = styled.button` margin-left: 5px; } + .button__label { + display: flex; + align-items: center; + } + .button__icon { flex: 0 0 auto; } From 1ab0b26f0851d3e5792e6cb8cbda8b54d2af601f Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 10 May 2021 15:27:10 +0900 Subject: [PATCH 57/91] Changed Import --- src/cloud/components/organisms/settings/SettingsComponent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cloud/components/organisms/settings/SettingsComponent.tsx b/src/cloud/components/organisms/settings/SettingsComponent.tsx index 54ef420f28..1780d153b7 100644 --- a/src/cloud/components/organisms/settings/SettingsComponent.tsx +++ b/src/cloud/components/organisms/settings/SettingsComponent.tsx @@ -25,7 +25,7 @@ import PreferencesTab from './PreferencesTab' import ApiTab from './ApiTab' import { PageStoreWithTeam } from '../../../interfaces/pageStore' import Settings from '../../../../shared/components/organisms/Settings' -import SettingSidenavHeader from '../../../../shared/components/organisms/Settings/atoms/SettingSidenavHeader' +import SettingSidenavHeader from '../../../../shared/components/organisms/Settings/molecules/SettingSidenavHeader' import SettingSidenav from '../../../../shared/components/organisms/Settings/atoms/SettingSidenav' import SettingMain from '../../../../shared/components/organisms/Settings/atoms/SettingMain' import SettingTabButton from '../../../../shared/components/organisms/Settings/atoms/SettingTabButton' From bd347c620e0de5559eb30b772add84a219eb61a5 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Mon, 10 May 2021 15:27:31 +0900 Subject: [PATCH 58/91] Changed Sizes fo Input & Select --- .../components/organisms/Settings/atoms/SettingInput.tsx | 4 +++- .../components/organisms/Settings/atoms/SettingSelect.tsx | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/shared/components/organisms/Settings/atoms/SettingInput.tsx b/src/shared/components/organisms/Settings/atoms/SettingInput.tsx index 1bcd2decf0..010ff48254 100644 --- a/src/shared/components/organisms/Settings/atoms/SettingInput.tsx +++ b/src/shared/components/organisms/Settings/atoms/SettingInput.tsx @@ -5,6 +5,7 @@ interface SettingInputProps { label?: string value?: string placeholder?: string + type?: string onChange?: React.ChangeEventHandler } @@ -28,7 +29,8 @@ const Container = styled.div` flex-shrink: 1; width: 100%; height: 40px; - max-width: 400px; + min-width: 300px; + max-width: 450px; padding: ${({ theme }) => theme.sizes.spaces.xsm}px ${({ theme }) => theme.sizes.spaces.sm}px; background-color: ${({ theme }) => theme.colors.background.primary}; diff --git a/src/shared/components/organisms/Settings/atoms/SettingSelect.tsx b/src/shared/components/organisms/Settings/atoms/SettingSelect.tsx index 7123519e7c..e95ab4d91e 100644 --- a/src/shared/components/organisms/Settings/atoms/SettingSelect.tsx +++ b/src/shared/components/organisms/Settings/atoms/SettingSelect.tsx @@ -36,7 +36,8 @@ const Container = styled.div` select { width: 100%; height: 40px; - max-width: 400px; + min-width: 300px; + max-width: 450px; padding: 0 ${({ theme }) => theme.sizes.spaces.sm}px; border-radius: 4px; background-color: ${({ theme }) => theme.colors.background.primary}; From a021c7eac1cd1df250e3430a834f9ea796ffd5d2 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Sat, 15 May 2021 12:33:00 +0900 Subject: [PATCH 59/91] Put Close Button Back --- .../organisms/settings/SettingsComponent.tsx | 16 +++++++-- .../Settings/atoms/SettingCloseButton.tsx | 33 +++++++++++++++++++ .../components/organisms/Settings/index.tsx | 2 +- 3 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 src/shared/components/organisms/Settings/atoms/SettingCloseButton.tsx diff --git a/src/cloud/components/organisms/settings/SettingsComponent.tsx b/src/cloud/components/organisms/settings/SettingsComponent.tsx index 1780d153b7..07b2a5c612 100644 --- a/src/cloud/components/organisms/settings/SettingsComponent.tsx +++ b/src/cloud/components/organisms/settings/SettingsComponent.tsx @@ -7,7 +7,7 @@ import { isSingleKeyEventOutsideOfInput, } from '../../../lib/keyboard' import { useTranslation } from 'react-i18next' -import { mdiDomain, mdiAccountCircleOutline } from '@mdi/js' +import { mdiDomain, mdiAccountCircleOutline, mdiClose } from '@mdi/js' import PersonalInfoTab from './PersonalInfoTab' import { usePage } from '../../../lib/stores/pageStore' import TeamInfoTab from './TeamInfoTab' @@ -29,6 +29,8 @@ import SettingSidenavHeader from '../../../../shared/components/organisms/Settin import SettingSidenav from '../../../../shared/components/organisms/Settings/atoms/SettingSidenav' import SettingMain from '../../../../shared/components/organisms/Settings/atoms/SettingMain' import SettingTabButton from '../../../../shared/components/organisms/Settings/atoms/SettingTabButton' +import Icon from '../../../../shared/components/atoms/Icon' +import SettingCloseButton from '../../../../shared/components/organisms/Settings/atoms/SettingCloseButton' const SettingsComponent = () => { const { t } = useTranslation() @@ -196,7 +198,17 @@ const SettingsComponent = () => { )} } - content={{content}} + content={ + + {content} + + + + + } > ) } diff --git a/src/shared/components/organisms/Settings/atoms/SettingCloseButton.tsx b/src/shared/components/organisms/Settings/atoms/SettingCloseButton.tsx new file mode 100644 index 0000000000..3926a47bd0 --- /dev/null +++ b/src/shared/components/organisms/Settings/atoms/SettingCloseButton.tsx @@ -0,0 +1,33 @@ +import styled from '../../../../lib/styled' + +const SettingCloseButton = styled.div` + position: absolute; + top: ${({ theme }) => theme.sizes.spaces.md}px; + right: ${({ theme }) => theme.sizes.spaces.md}px; + width: 26px; + height: 26px; + background-color: transparent; + border: none; + cursor: pointer; + color: ${({ theme }) => theme.colors.icon.default}; + transition: 200ms color; + + &:hover, + &:focus { + color: ${({ theme }) => theme.colors.icon.hover} !important; + } + + &:active { + color: ${({ theme }) => theme.colors.icon.active} !important; + } + + &:disabled { + &:hover, + &:focus, + &:active { + cursor: not-allowed; + } + } +` + +export default SettingCloseButton diff --git a/src/shared/components/organisms/Settings/index.tsx b/src/shared/components/organisms/Settings/index.tsx index bc6125bc76..a5e84fc7d3 100644 --- a/src/shared/components/organisms/Settings/index.tsx +++ b/src/shared/components/organisms/Settings/index.tsx @@ -24,7 +24,7 @@ const Container = styled.div` align-items: center; justify-content: center; position: fixed; - left: 40px; + left: 0; top: 0; right: 0; bottom: 0; From f777e51c27334e2fd54f1be068f96d34d2532e91 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Sat, 15 May 2021 14:47:27 +0900 Subject: [PATCH 60/91] Adjusted Pricing Table Styles --- .../organisms/Subscription/PlanTables.tsx | 65 ++++++++++--------- .../organisms/settings/UpgradeTab.tsx | 44 ++++++------- 2 files changed, 55 insertions(+), 54 deletions(-) diff --git a/src/cloud/components/organisms/Subscription/PlanTables.tsx b/src/cloud/components/organisms/Subscription/PlanTables.tsx index efa5e9741b..300bd46fac 100644 --- a/src/cloud/components/organisms/Subscription/PlanTables.tsx +++ b/src/cloud/components/organisms/Subscription/PlanTables.tsx @@ -12,8 +12,8 @@ import { revisionHistoryStandardDays, standardPlanStorageMb, } from '../../../lib/subscription' -import CustomButton from '../../atoms/buttons/CustomButton' import cc from 'classcat' +import Button from '../../../../shared/components/atoms/Button' interface PlanTablesProps { team: SerializedTeam @@ -67,7 +67,7 @@ const PlanTables = ({ onTrialCallback() }} > - Try it for free + 7 days free trial

) @@ -83,20 +83,25 @@ const PlanTables = ({
$0 +
+ per user +
+ per month +
{selectedPlan === 'free' ? ( - Current Plan - + ) : ( - + )} @@ -104,23 +109,24 @@ const PlanTables = ({
$3 -
per member per month
+
+ per user +
+ per month +
{selectedPlan === 'standard' ? ( - Current Plan - + ) : ( - + )} @@ -128,21 +134,25 @@ const PlanTables = ({
$8 -
per member per month
+
+ per user +
+ per month +
{selectedPlan === 'pro' ? ( - Current Plan - + ) : ( - + )} {freeTrialContent} @@ -351,12 +361,6 @@ const Container = styled.div` .upgrade-btn { width: 100%; margin: ${({ theme }) => theme.fontSizes.xsmall}px 0; - padding: 0px ${({ theme }) => theme.space.xxsmall}px !important; - display: flex; - align-items: center; - justify-content: center; - height: 40px; - line-height: inherit; } tr td { @@ -366,9 +370,8 @@ const Container = styled.div` text-align: left; min-height: 30px; - &:not(.first) { - padding: ${({ theme }) => theme.space.xsmall}px - ${({ theme }) => theme.space.small}px; + &:not(.first):not(.header) { + padding: ${({ theme }) => theme.space.xsmall}px; } &.first { diff --git a/src/cloud/components/organisms/settings/UpgradeTab.tsx b/src/cloud/components/organisms/settings/UpgradeTab.tsx index ef38f5faab..80f07ecf71 100644 --- a/src/cloud/components/organisms/settings/UpgradeTab.tsx +++ b/src/cloud/components/organisms/settings/UpgradeTab.tsx @@ -87,29 +87,27 @@ const UpgradeTab = () => { /> )}
-

- onUpgradeCallback('standard')} - onProCallback={() => onUpgradeCallback('pro')} - onTrialCallback={() => setShowTrialPopup(true)} - /> - - * For larger businesses or those in highly regulated - industries, please{' '} - - contact our sales department - - . - -

+ onUpgradeCallback('standard')} + onProCallback={() => onUpgradeCallback('pro')} + onTrialCallback={() => setShowTrialPopup(true)} + /> + + * For larger businesses or those in highly regulated industries, + please{' '} + + contact our sales department + + . +
} From b36270537266ff97ca210b4d941c87fe88501703 Mon Sep 17 00:00:00 2001 From: Elle Kasai Date: Sat, 15 May 2021 15:03:07 +0900 Subject: [PATCH 61/91] Replaced to SettingLink --- .../organisms/Subscription/PlanTables.tsx | 15 +++------------ .../organisms/settings/UpgradeTab.tsx | 6 +++--- .../organisms/Settings/atoms/SettingLink.tsx | 17 +++++++---------- 3 files changed, 13 insertions(+), 25 deletions(-) diff --git a/src/cloud/components/organisms/Subscription/PlanTables.tsx b/src/cloud/components/organisms/Subscription/PlanTables.tsx index 300bd46fac..a8a1d9c365 100644 --- a/src/cloud/components/organisms/Subscription/PlanTables.tsx +++ b/src/cloud/components/organisms/Subscription/PlanTables.tsx @@ -14,6 +14,7 @@ import { } from '../../../lib/subscription' import cc from 'classcat' import Button from '../../../../shared/components/atoms/Button' +import SettingLink from '../../../../shared/components/organisms/Settings/atoms/SettingLink' interface PlanTablesProps { team: SerializedTeam @@ -60,7 +61,7 @@ const PlanTables = ({ return (

- { e.preventDefault() @@ -68,7 +69,7 @@ const PlanTables = ({ }} > 7 days free trial - +

) }, [subscription, team, onTrialCallback]) @@ -299,16 +300,6 @@ const PlanTables = ({ ) } -const StyledTrialLink = styled.a` - text-decoration: underline; - font-size: ${({ theme }) => theme.fontSizes.default}px; - transition: 200ms color; - color: ${({ theme }) => theme.primaryTextColor}; - &:hover { - text-decoration: none; - } -` - const Container = styled.div` width: 100%; overflow: auto; diff --git a/src/cloud/components/organisms/settings/UpgradeTab.tsx b/src/cloud/components/organisms/settings/UpgradeTab.tsx index 80f07ecf71..cd25ec1bcd 100644 --- a/src/cloud/components/organisms/settings/UpgradeTab.tsx +++ b/src/cloud/components/organisms/settings/UpgradeTab.tsx @@ -11,11 +11,11 @@ import { useGlobalData } from '../../../lib/stores/globalData' import ColoredBlock from '../../atoms/ColoredBlock' import FreeTrialPopup from '../FreeTrialPopup' import { stripePublishableKey } from '../../../lib/consts' -import CustomLink from '../../atoms/Link/CustomLink' import PlanTables from '../Subscription/PlanTables' import { UpgradePlans } from '../../../lib/stripe' import styled from '../../../lib/styled' import SettingTabContent from '../../../../shared/components/organisms/Settings/atoms/SettingTabContent' +import SettingLink from '../../../../shared/components/organisms/Settings/atoms/SettingLink' const stripePromise = loadStripe(stripePublishableKey) @@ -98,14 +98,14 @@ const UpgradeTab = () => { * For larger businesses or those in highly regulated industries, please{' '} - contact our sales department - + .
diff --git a/src/shared/components/organisms/Settings/atoms/SettingLink.tsx b/src/shared/components/organisms/Settings/atoms/SettingLink.tsx index f7533cc382..9d1d6ca145 100644 --- a/src/shared/components/organisms/Settings/atoms/SettingLink.tsx +++ b/src/shared/components/organisms/Settings/atoms/SettingLink.tsx @@ -1,16 +1,13 @@ import styled from '../../../../lib/styled' -import Link from '../../../atoms/Link' -const SettingLink = styled(Link)` - a { - color: ${({ theme }) => theme.colors.text.link}; - text-decoration: none; +const SettingLink = styled.a` + color: ${({ theme }) => theme.colors.text.link}; + text-decoration: none; - &:hover, - &:focus { - color: ${({ theme }) => theme.colors.text.link}; - text-decoration: underline; - } + &:hover, + &:focus { + color: ${({ theme }) => theme.colors.text.link}; + text-decoration: underline; } ` From 2d955e7dbd56d0403ef112d86f8c48c8a920a75f Mon Sep 17 00:00:00 2001 From: davy-c Date: Tue, 18 May 2021 07:27:06 +0900 Subject: [PATCH 62/91] remove setting link --- src/cloud/components/Application.tsx | 2 + .../components/atoms/Link/AccountLink.tsx | 8 +- .../atoms/Link/{Link.tsx => CloudLink.tsx} | 22 +- src/cloud/components/atoms/Link/TeamLink.tsx | 9 +- .../Modal/contents/Doc/GuestsModal/index.tsx | 10 +- .../organisms/RightSideTopBar/BreadCrumbs.tsx | 404 ------------------ .../SideNavigator/SideNavigatorItem.tsx | 6 +- .../Sidebar/SidebarTeamPickerContext.tsx | 6 +- .../organisms/Subscription/PlanTables.tsx | 6 +- .../components/organisms/settings/ApiTab.tsx | 9 +- .../organisms/settings/MembersTab.tsx | 10 +- .../organisms/settings/UpgradeTab.tsx | 11 +- src/shared/components/atoms/Link.tsx | 3 + .../organisms/Settings/atoms/SettingLink.tsx | 14 - 14 files changed, 39 insertions(+), 481 deletions(-) rename src/cloud/components/atoms/Link/{Link.tsx => CloudLink.tsx} (77%) delete mode 100644 src/cloud/components/organisms/RightSideTopBar/BreadCrumbs.tsx delete mode 100644 src/shared/components/organisms/Settings/atoms/SettingLink.tsx diff --git a/src/cloud/components/Application.tsx b/src/cloud/components/Application.tsx index 1244c4c27b..a72680d6dd 100644 --- a/src/cloud/components/Application.tsx +++ b/src/cloud/components/Application.tsx @@ -219,6 +219,8 @@ const Application = ({ if (query.settings === 'upgrade') { openSettingsTab('teamUpgrade') } + + openSettingsTab('personalInfo') }) useEffect(() => { diff --git a/src/cloud/components/atoms/Link/AccountLink.tsx b/src/cloud/components/atoms/Link/AccountLink.tsx index 94212d4282..1897590d2a 100644 --- a/src/cloud/components/atoms/Link/AccountLink.tsx +++ b/src/cloud/components/atoms/Link/AccountLink.tsx @@ -1,6 +1,6 @@ import React, { FC } from 'react' import querystring from 'querystring' -import SettingLink from '../../../../shared/components/organisms/Settings/atoms/SettingLink' +import CloudLink from './CloudLink' export type AccountLinkIntent = 'delete' @@ -16,22 +16,20 @@ interface AccountLinkProps { const AccountLink: FC = ({ intent = 'delete', className, - style, children, query, draggable = false, beforeNavigate, }) => { return ( - {children} - + ) } diff --git a/src/cloud/components/atoms/Link/Link.tsx b/src/cloud/components/atoms/Link/CloudLink.tsx similarity index 77% rename from src/cloud/components/atoms/Link/Link.tsx rename to src/cloud/components/atoms/Link/CloudLink.tsx index d0a377b8b4..0e3f9f2274 100644 --- a/src/cloud/components/atoms/Link/Link.tsx +++ b/src/cloud/components/atoms/Link/CloudLink.tsx @@ -1,31 +1,24 @@ -import React, { - FC, - useCallback, - MouseEventHandler, - CSSProperties, - FocusEvent, -} from 'react' +import React, { FC, useCallback, MouseEventHandler, FocusEvent } from 'react' import { useRouter, Url } from '../../../lib/router' import { stringifyUrl } from '../../../lib/utils/string' +import Link from '../../../../shared/components/atoms/Link' -export interface LinkProps { +export interface CloudLinkProps { href: Url id?: string className?: string tabIndex?: number draggable?: boolean - style?: CSSProperties beforeNavigate?: () => void | Promise onFocus?: (event: FocusEvent) => void } -const Link: FC = ({ +const CloudLink: FC = ({ href, id, className, tabIndex, draggable, - style, beforeNavigate, onFocus, children, @@ -44,7 +37,7 @@ const Link: FC = ({ ) return ( - = ({ className={className} tabIndex={tabIndex} draggable={draggable} - style={style} > {children} - + ) } -export default Link +export default CloudLink diff --git a/src/cloud/components/atoms/Link/TeamLink.tsx b/src/cloud/components/atoms/Link/TeamLink.tsx index 1d326b0596..d92cac4944 100644 --- a/src/cloud/components/atoms/Link/TeamLink.tsx +++ b/src/cloud/components/atoms/Link/TeamLink.tsx @@ -2,7 +2,7 @@ import React, { useCallback } from 'react' import querystring from 'querystring' import { SerializedTeam } from '../../../interfaces/db/team' import { useRouter } from '../../../lib/router' -import SettingLink from '../../../../shared/components/organisms/Settings/atoms/SettingLink' +import CloudLink from './CloudLink' export type TeamLinkIntent = | 'index' @@ -25,7 +25,6 @@ interface TeamLinkProps { team: TeamIdProps intent?: TeamLinkIntent className?: string - style?: React.CSSProperties children?: React.ReactNode query?: any beforeNavigate?: () => void @@ -38,7 +37,6 @@ const TeamLink = ({ intent = 'index', team, className, - style, children, query, beforeNavigate, @@ -47,17 +45,16 @@ const TeamLink = ({ tabIndex = 0, }: TeamLinkProps) => { return ( - {children} - + ) } diff --git a/src/cloud/components/organisms/Modal/contents/Doc/GuestsModal/index.tsx b/src/cloud/components/organisms/Modal/contents/Doc/GuestsModal/index.tsx index 6d9c8fb4c8..cbf5409035 100644 --- a/src/cloud/components/organisms/Modal/contents/Doc/GuestsModal/index.tsx +++ b/src/cloud/components/organisms/Modal/contents/Doc/GuestsModal/index.tsx @@ -10,7 +10,7 @@ import { guestsPerMember } from '../../../../../../lib/subscription' import plur from 'plur' import GuestInvitesSection from '../../../../../molecules/GuestInvitesSection' import { useModal } from '../../../../../../../shared/lib/stores/modal' -import SettingLink from '../../../../../../../shared/components/organisms/Settings/atoms/SettingLink' +import { ExternalLink } from '../../../../../../../shared/components/atoms/Link' interface GuestsModalProps { docId: string @@ -64,13 +64,9 @@ const GuestsModal = ({ docId, teamId }: GuestsModalProps) => { permissions.length * guestsPerMember - guestsMap.size } remaining ${plur('seat', permissions.length)}. ` : 'No Remaining seats. '} - + See how it works - +

diff --git a/src/cloud/components/organisms/RightSideTopBar/BreadCrumbs.tsx b/src/cloud/components/organisms/RightSideTopBar/BreadCrumbs.tsx deleted file mode 100644 index 604cfa7244..0000000000 --- a/src/cloud/components/organisms/RightSideTopBar/BreadCrumbs.tsx +++ /dev/null @@ -1,404 +0,0 @@ -import React, { - useMemo, - useState, - useRef, - useCallback, - useEffect, - FocusEventHandler, - MouseEventHandler, -} from 'react' -import { useNav } from '../../../lib/stores/nav' -import { SerializedTeam } from '../../../interfaces/db/team' -import { SerializedFolderWithBookmark } from '../../../interfaces/db/folder' -import FolderLink from '../../atoms/Link/FolderLink' -import WorkspaceLink from '../../atoms/Link/WorkspaceLink' -import styled from '../../../lib/styled' -import TeamLink from '../../atoms/Link/TeamLink' -import ToolbarSlashSeparator from './TopBarSlashSeparator' -import Icon from '../../atoms/Icon' -import { - mdiFolderAccountOutline, - mdiDotsHorizontal, - mdiFolderOutline, - mdiSubdirectoryArrowRight, -} from '@mdi/js' -import { isChildNode } from '../../../lib/dom' -import Flexbox from '../../atoms/Flexbox' -import { useContextMenuKeydownHandler } from '../../../lib/keyboard' -import { useMediaQuery } from 'react-responsive' -import { usePage } from '../../../lib/stores/pageStore' - -interface BreadCrumbsProps { - team: SerializedTeam - minimize?: boolean - path?: string - addedNodes?: React.ReactNode -} - -type BreadCrumbs = { - folderLabel: string - folderPathname: string -}[] - -const BreadCrumbs = ({ team, path, addedNodes }: BreadCrumbsProps) => { - const { - currentPath: defaultPath, - currentWorkspaceId, - workspacesMap, - foldersMap, - } = useNav() - const [ - showingParentFolderListPopup, - setShowingParentFolderListPopup, - ] = useState(false) - const parentFolderListPopupRef = useRef(null) - const [contextOffset, setContextOffset] = useState(0) - const { currentUserPermissions } = usePage() - - useEffect(() => { - if (parentFolderListPopupRef.current != null) { - parentFolderListPopupRef.current.focus() - } - }, [showingParentFolderListPopup]) - - const showParentFolderListPopup: MouseEventHandler = useCallback( - (event) => { - setContextOffset(event.currentTarget.offsetLeft) - setShowingParentFolderListPopup(true) - }, - [] - ) - - const hideParentFolderListPopup: FocusEventHandler = useCallback( - (event) => { - if ( - parentFolderListPopupRef.current != null && - !isChildNode( - parentFolderListPopupRef.current, - event.relatedTarget as HTMLElement | null - ) - ) { - setShowingParentFolderListPopup(false) - } - }, - [] - ) - - const currentWorkspace = useMemo(() => { - if (currentWorkspaceId == null) { - return undefined - } - return workspacesMap.get(currentWorkspaceId) - }, [currentWorkspaceId, workspacesMap]) - - const foldersMapByPathnames = useMemo(() => { - const folderMap = new Map() - const folders = [...foldersMap.values()] - folders.forEach((folder) => { - if (folder.teamId === team.id) folderMap.set(folder.pathname, folder) - }) - return folderMap - }, [foldersMap, team]) - - const breadCrumbs = useMemo(() => { - const currentPath = path != null ? path : defaultPath - if (currentPath === '/') { - return [] - } - - const folders = currentPath.substring(1).split('/') - const thread = folders.map((folderName, index) => { - const folderPathname = `/${folders.slice(0, index + 1).join('/')}` - return { - folderLabel: folderName, - folderPathname, - } - }) - return thread as BreadCrumbs - }, [defaultPath, path]) - - const parentBreadCrumbs = new Set( - breadCrumbs - .slice(0, breadCrumbs.length - 1) - .map((breadcrumb) => breadcrumb.folderPathname) - ) - const directParentBreadCrumb = breadCrumbs[breadCrumbs.length - 1] - const directParentFolder = - directParentBreadCrumb != null - ? foldersMapByPathnames.get(directParentBreadCrumb.folderPathname) - : null - - useContextMenuKeydownHandler(parentFolderListPopupRef) - - const isTabletOrMobile = useMediaQuery({ maxWidth: 1080 }) - - if (currentUserPermissions == null) { - return ( - - - Shared - {addedNodes != null && } - - {addedNodes} - - ) - } - - return ( - - {showingParentFolderListPopup && ( - -
    - {isTabletOrMobile && currentWorkspace != null && ( -
  • - {currentWorkspace.default ? ( - - - {currentWorkspace.name} - - ) : ( - - - {currentWorkspace.name} - - )} -
  • - )} - - {breadCrumbs.map((elem, index) => { - const folder = foldersMapByPathnames.get(elem.folderPathname) - if ( - !isTabletOrMobile && - !parentBreadCrumbs.has(elem.folderPathname) - ) { - return null - } - - return ( -
  • - {folder != null ? ( - - {(index !== 0 || (index === 0 && isTabletOrMobile)) && ( - - )} - - {folder.name} - - ) : ( - {elem.folderLabel} - )} -
  • - ) - })} -
-
- )} - - {isTabletOrMobile ? ( - -
- -
- {addedNodes != null && } -
- ) : ( - - {currentWorkspace != null && ( - <> - {currentWorkspace.default ? ( - - - {currentWorkspace.name} - - ) : ( - - - {currentWorkspace.name} - - )} - - )} - {breadCrumbs.length > 1 && ( - <> - -
- -
- - )} - - {directParentBreadCrumb != null && ( - <> - {directParentFolder != null ? ( - - - {directParentFolder.name} - - ) : ( - {directParentBreadCrumb.folderLabel} - )} - - {addedNodes != null && } - - )} -
- )} - {addedNodes} -
- ) -} - -const StyledBreadCrumbs = styled.div` - display: flex; - flex: 1; - min-width: 0; - height: 100%; - align-items: center; - color: ${({ theme }) => theme.subtleTextColor}; - overflow-x: hidden; - - .padded { - padding-left: ${({ theme }) => theme.space.small}px; - } - - .bread-crumb-link { - display: flex; - align-items: center; - padding: 2px 4px; - border-radius: 3px; - white-space: nowrap; - background-color: transparent; - color: ${({ theme }) => theme.baseTextColor}; - cursor: pointer; - text-decoration: none !important; - - .label { - margin-left: 4px; - } - .hoverIcon { - margin-left: 4px; - opacity: 0; - } - - &:hover, - &:focus { - background-color: ${({ theme }) => theme.subtleBackgroundColor}; - .hoverIcon { - opacity: 1; - } - } - } -` - -const ParentFolderListPopup = styled.div` - position: absolute; - z-index: 1000; - max-height: 300px; - top: 100%; - background-color: ${({ theme }) => theme.baseBackgroundColor}; - border: 1px solid ${({ theme }) => theme.baseBorderColor}; - border-radius: 4px; - overflow-y: auto; - overflow-x: hidden; - & > ul { - padding: 0; - margin: 0; - list-style: none; - } - - .parentFolderItem { - padding-right: 5px; - background-color: transparent; - color: ${({ theme }) => theme.subtleTextColor}; - cursor: pointer; - text-decoration: none !important; - &:hover, - &:focus { - color: ${({ theme }) => theme.emphasizedTextColor}; - & > .hoverIcon { - opacity: 1; - } - } - - display: flex; - align-items: center; - & > .icon { - margin-right: 4px; - } - & > .hoverIcon { - margin-left: 4px; - opacity: 0; - } - } -` - -export default BreadCrumbs diff --git a/src/cloud/components/organisms/Sidebar/SideNavigator/SideNavigatorItem.tsx b/src/cloud/components/organisms/Sidebar/SideNavigator/SideNavigatorItem.tsx index 5a7be09c43..0149e72f92 100644 --- a/src/cloud/components/organisms/Sidebar/SideNavigator/SideNavigatorItem.tsx +++ b/src/cloud/components/organisms/Sidebar/SideNavigator/SideNavigatorItem.tsx @@ -9,7 +9,7 @@ import { } from './styled' import styled from '../../../../lib/styled' import IconMdi from '../../../atoms/IconMdi' -import Link from '../../../atoms/Link/Link' +import CloudLink from '../../../atoms/Link/CloudLink' import { Url } from '../../../../lib/router' type SideNavNextLink = { @@ -64,7 +64,7 @@ const SideNavigatorItem = ({ const labelContent = useMemo(() => { if (href != null) { return ( - setFocused(true)} id={`tree-${elementId}`} @@ -72,7 +72,7 @@ const SideNavigatorItem = ({ href={href} > {label} - + ) } diff --git a/src/cloud/components/organisms/Sidebar/SidebarTeamPickerContext.tsx b/src/cloud/components/organisms/Sidebar/SidebarTeamPickerContext.tsx index da9fb266f8..ee1a48b7c0 100644 --- a/src/cloud/components/organisms/Sidebar/SidebarTeamPickerContext.tsx +++ b/src/cloud/components/organisms/Sidebar/SidebarTeamPickerContext.tsx @@ -11,7 +11,7 @@ import { useTranslation } from 'react-i18next' import TeamLink from '../../atoms/Link/TeamLink' import cc from 'classcat' import { usePage } from '../../../lib/stores/pageStore' -import Link from '../../atoms/Link/Link' +import CloudLink from '../../atoms/Link/CloudLink' import { getHexFromUUID } from '../../../lib/utils/string' import { stringify } from 'querystring' import IconMdi from '../../atoms/IconMdi' @@ -95,7 +95,7 @@ const SidebarTeamPickerContext = ({ {invites.map((invite) => { const query = { t: invite.team.id, i: getHexFromUUID(invite.id) } return ( - - invited - + ) })} diff --git a/src/cloud/components/organisms/Subscription/PlanTables.tsx b/src/cloud/components/organisms/Subscription/PlanTables.tsx index a8a1d9c365..56bbb80b6b 100644 --- a/src/cloud/components/organisms/Subscription/PlanTables.tsx +++ b/src/cloud/components/organisms/Subscription/PlanTables.tsx @@ -14,7 +14,7 @@ import { } from '../../../lib/subscription' import cc from 'classcat' import Button from '../../../../shared/components/atoms/Button' -import SettingLink from '../../../../shared/components/organisms/Settings/atoms/SettingLink' +import Link from '../../../../shared/components/atoms/Link' interface PlanTablesProps { team: SerializedTeam @@ -61,7 +61,7 @@ const PlanTables = ({ return (

- { e.preventDefault() @@ -69,7 +69,7 @@ const PlanTables = ({ }} > 7 days free trial - +

) }, [subscription, team, onTrialCallback]) diff --git a/src/cloud/components/organisms/settings/ApiTab.tsx b/src/cloud/components/organisms/settings/ApiTab.tsx index a382d01b72..08b36da073 100644 --- a/src/cloud/components/organisms/settings/ApiTab.tsx +++ b/src/cloud/components/organisms/settings/ApiTab.tsx @@ -7,10 +7,10 @@ import { usePage } from '../../../lib/stores/pageStore' import Icon from '../../atoms/IconMdi' import { mdiOpenInNew } from '@mdi/js' import SettingTabContent from '../../../../shared/components/organisms/Settings/atoms/SettingTabContent' -import SettingLink from '../../../../shared/components/organisms/Settings/atoms/SettingLink' import Button from '../../../../shared/components/atoms/Button' import SettingTokenCreate from '../../../../shared/components/organisms/Settings/molecules/SettingTokenCreate' import Flexbox from '../../../../shared/components/atoms/Flexbox' +import { ExternalLink } from '../../../../shared/components/atoms/Link' const ApiTab = () => { const { team } = usePage() @@ -51,13 +51,10 @@ const ApiTab = () => {

Access Tokens

See the{' '} - + documentation for Boost Note for Teams API{' '} - +

diff --git a/src/shared/components/atoms/Link.tsx b/src/shared/components/atoms/Link.tsx index 8512c34299..e89e015413 100644 --- a/src/shared/components/atoms/Link.tsx +++ b/src/shared/components/atoms/Link.tsx @@ -10,6 +10,7 @@ interface HyperLinkProps { id?: string href: string className?: string + tabIndex?: number onContextMenu?: MouseEventHandler onFocus?: FocusEventHandler draggable?: boolean @@ -57,10 +58,12 @@ const Container = styled.a` cursor: pointer; &:hover { + color: ${({ theme }) => theme.colors.text.link}; text-decoration: underline; } &:focus { + color: ${({ theme }) => theme.colors.text.link}; opacity: 0.8; } ` diff --git a/src/shared/components/organisms/Settings/atoms/SettingLink.tsx b/src/shared/components/organisms/Settings/atoms/SettingLink.tsx deleted file mode 100644 index 9d1d6ca145..0000000000 --- a/src/shared/components/organisms/Settings/atoms/SettingLink.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import styled from '../../../../lib/styled' - -const SettingLink = styled.a` - color: ${({ theme }) => theme.colors.text.link}; - text-decoration: none; - - &:hover, - &:focus { - color: ${({ theme }) => theme.colors.text.link}; - text-decoration: underline; - } -` - -export default SettingLink From 1f6281d26251f7931c920ccbf6d697d568f98e7e Mon Sep 17 00:00:00 2001 From: davy-c Date: Tue, 18 May 2021 08:04:13 +0900 Subject: [PATCH 63/91] fix team invites section --- .../molecules/TeamInvitesSection.tsx | 79 +++++++++++-------- .../molecules/Form/atoms/FormRow.tsx | 0 .../molecules/Form/atoms/FormSelect.tsx | 8 +- .../components/molecules/Form/index.tsx | 28 +++++-- 4 files changed, 75 insertions(+), 40 deletions(-) create mode 100644 src/shared/components/molecules/Form/atoms/FormRow.tsx diff --git a/src/cloud/components/molecules/TeamInvitesSection.tsx b/src/cloud/components/molecules/TeamInvitesSection.tsx index 4fea03f301..640e227ffd 100644 --- a/src/cloud/components/molecules/TeamInvitesSection.tsx +++ b/src/cloud/components/molecules/TeamInvitesSection.tsx @@ -1,6 +1,5 @@ import React, { useState, useCallback } from 'react' import { - SectionRow, SectionList, SectionListItem, SectionInLineIcon, @@ -19,12 +18,10 @@ import IconMdi from '../atoms/IconMdi' import { mdiClose } from '@mdi/js' import { Spinner } from '../atoms/Spinner' import ErrorBlock from '../atoms/ErrorBlock' -import CustomButton from '../atoms/buttons/CustomButton' -import { SelectChangeEventHandler } from '../../lib/utils/events' import { SerializedUserTeamPermissions } from '../../interfaces/db/userTeamPermissions' import Flexbox from '../atoms/Flexbox' -import SettingSelect from '../../../shared/components/organisms/Settings/atoms/SettingSelect' -import SettingInput from '../../../shared/components/organisms/Settings/atoms/SettingInput' +import Form from '../../../shared/components/molecules/Form' +import { FormSelectOption } from '../../../shared/components/molecules/Form/atoms/FormSelect' interface TeamInvitesSectionProps { userPermissions: SerializedUserTeamPermissions @@ -130,13 +127,13 @@ const TeamInvitesSection = ({ userPermissions }: TeamInvitesSectionProps) => { [messageBox, team] ) - const selectRole: SelectChangeEventHandler = useCallback( - (event) => { + const selectRole = useCallback( + (option: FormSelectOption) => { if (userPermissions.role !== 'admin') { return } - setRole(event.target.value as TeamPermissionType) + setRole(option.value as TeamPermissionType) }, [setRole, userPermissions] ) @@ -147,30 +144,48 @@ const TeamInvitesSection = ({ userPermissions }: TeamInvitesSectionProps) => {

Invite with Email

{sending && } - - - - - - - - } - > - - Send - - - +
{role === 'admin' ? ( Admins can handle billing, remove or promote/demote members. diff --git a/src/shared/components/molecules/Form/atoms/FormRow.tsx b/src/shared/components/molecules/Form/atoms/FormRow.tsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/shared/components/molecules/Form/atoms/FormSelect.tsx b/src/shared/components/molecules/Form/atoms/FormSelect.tsx index c23c62d7ab..9fbbbc4fed 100644 --- a/src/shared/components/molecules/Form/atoms/FormSelect.tsx +++ b/src/shared/components/molecules/Form/atoms/FormSelect.tsx @@ -74,9 +74,15 @@ const Container = styled.div` width: 0; } + .form__select .form__select__control, + .form__select .form__select__indicator, + .form__select .form__select__indicators { + height: 32px; + min-height: 32px; + } + .form__select .form__select__control { width: 100%; - height: 40px; color: ${({ theme }) => theme.colors.text.subtle}; border: none; &.form__select__control--is-focused { diff --git a/src/shared/components/molecules/Form/index.tsx b/src/shared/components/molecules/Form/index.tsx index 100a7a0eaf..c472c3b472 100644 --- a/src/shared/components/molecules/Form/index.tsx +++ b/src/shared/components/molecules/Form/index.tsx @@ -2,20 +2,24 @@ import React, { useState } from 'react' import styled from '../../../lib/styled' import { LoadingButton, ButtonProps } from '../../atoms/Button' import FormInput, { FormInputProps } from './atoms/FormInput' -import { FormSelectProps } from './atoms/FormSelect' +import FormSelect, { FormSelectProps } from './atoms/FormSelect' import cc from 'classcat' import { AppComponent } from '../../../lib/types' -import { FormSelect } from '../../../../components/atoms/form' import FormEmoji, { FormEmojiProps } from './atoms/FormEmoji' interface FormProps { rows: FormRowProps[] onSubmit: React.FormEventHandler - submitButton?: ButtonProps & { label: React.ReactNode; spinning?: boolean } + submitButton?: LabelButtonProps onCancel?: () => void } +type LabelButtonProps = ButtonProps & { + label: React.ReactNode + spinning?: boolean +} + export type FormRowProps = { layout?: 'column' | 'split' title?: React.ReactNode @@ -27,6 +31,8 @@ export type FormRowProps = { } | { type: 'select'; props: FormSelectProps } | { type: 'emoji'; props: FormEmojiProps } + | { type: 'button'; props: LabelButtonProps } + | { type: 'node'; element: React.ReactNode } )[] } @@ -66,7 +72,13 @@ const Form: AppComponent = ({ ) : item.type === 'emoji' ? ( - ) : null} + ) : item.type === 'button' ? ( + + {item.props.label} + + ) : ( + item.element + )} ))} @@ -120,12 +132,14 @@ const Container = styled.form` align-items: stretch; } - .form__row__item--emoji { + .form__row__item--emoji, + .form__row__item--button { flex: 0 0 auto; } - .form__row__item:not(.form__row__item--emoji), - .form__row__item:not(.form__row__item--emoji) > * { + .form__row__item:not(.form__row__item--emoji):not(.form__row__item--button), + .form__row__item:not(.form__row__item--emoji):not(.form__row__item--button) + > * { flex: 1 1 auto; } From c79087c9dc9bf219369197ce0a4b34e496651ee5 Mon Sep 17 00:00:00 2001 From: davy-c Date: Tue, 18 May 2021 08:33:23 +0900 Subject: [PATCH 64/91] simple select --- .../molecules/TeamInvitesSection.tsx | 21 ++----- .../molecules/Form/atoms/FormSelect.tsx | 57 +++++++++++++++++-- .../components/molecules/Form/index.tsx | 9 ++- 3 files changed, 65 insertions(+), 22 deletions(-) diff --git a/src/cloud/components/molecules/TeamInvitesSection.tsx b/src/cloud/components/molecules/TeamInvitesSection.tsx index 640e227ffd..027ba7c37d 100644 --- a/src/cloud/components/molecules/TeamInvitesSection.tsx +++ b/src/cloud/components/molecules/TeamInvitesSection.tsx @@ -21,7 +21,6 @@ import ErrorBlock from '../atoms/ErrorBlock' import { SerializedUserTeamPermissions } from '../../interfaces/db/userTeamPermissions' import Flexbox from '../atoms/Flexbox' import Form from '../../../shared/components/molecules/Form' -import { FormSelectOption } from '../../../shared/components/molecules/Form/atoms/FormSelect' interface TeamInvitesSectionProps { userPermissions: SerializedUserTeamPermissions @@ -128,12 +127,12 @@ const TeamInvitesSection = ({ userPermissions }: TeamInvitesSectionProps) => { ) const selectRole = useCallback( - (option: FormSelectOption) => { + (option: string) => { if (userPermissions.role !== 'admin') { return } - setRole(option.value as TeamPermissionType) + setRole(option as TeamPermissionType) }, [setRole, userPermissions] ) @@ -158,21 +157,13 @@ const TeamInvitesSection = ({ userPermissions }: TeamInvitesSectionProps) => { }, }, { - type: 'select', + type: 'select--string', + props: { - value: { - label: role, - value: role, - }, + value: role, onChange: selectRole, isDisabled: userPermissions.role !== 'admin', - options: [ - { - label: 'admin', - value: 'admin', - }, - { label: 'member', value: 'member' }, - ], + options: ['admin', 'member'], }, }, { diff --git a/src/shared/components/molecules/Form/atoms/FormSelect.tsx b/src/shared/components/molecules/Form/atoms/FormSelect.tsx index 9fbbbc4fed..01c454777f 100644 --- a/src/shared/components/molecules/Form/atoms/FormSelect.tsx +++ b/src/shared/components/molecules/Form/atoms/FormSelect.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React, { useCallback, useMemo, useState } from 'react' import Select from 'react-select' import cc from 'classcat' import styled from '../../../../lib/styled' @@ -8,11 +8,8 @@ export interface FormSelectOption { value: string } -export interface FormSelectProps { +interface FormSelectCommonProps { id?: string - options: FormSelectOption[] - value?: FormSelectOption - onChange: (val: any) => void closeMenuOnSelect?: boolean className?: string isDisabled?: boolean @@ -24,6 +21,20 @@ export interface FormSelectProps { onMenuOpen?: () => void } +interface StandardFormSelectOptions { + value?: FormSelectOption + options: FormSelectOption[] + onChange: (val: any) => void +} + +interface SimpleFormSelectOptions { + value: string + options: string[] + onChange: (val: string) => void +} + +export type FormSelectProps = FormSelectCommonProps & StandardFormSelectOptions + const FormSelect = ({ id, options, @@ -38,7 +49,7 @@ const FormSelect = ({ name, filterOption, onMenuOpen, -}: FormSelectProps) => { +}: FormSelectProps & StandardFormSelectOptions) => { const [focused, setFocused] = useState(false) return ( @@ -69,6 +80,40 @@ const FormSelect = ({ ) } +export type SimpleFormSelectProps = FormSelectCommonProps & + SimpleFormSelectOptions +export const SimpleFormSelect = ({ + options, + value, + onChange, + ...props +}: SimpleFormSelectProps) => { + const convertedOptions = useMemo(() => { + return options.map((opt) => { + return { + label: opt, + value: opt, + } + }) + }, [options]) + + const onSelectChange = useCallback( + (val: FormSelectOption) => { + onChange(val.value) + }, + [onChange] + ) + + return ( + + ) +} + const Container = styled.div` .form__select .form__select__indicator-separator { width: 0; diff --git a/src/shared/components/molecules/Form/index.tsx b/src/shared/components/molecules/Form/index.tsx index c472c3b472..a1d1a3effe 100644 --- a/src/shared/components/molecules/Form/index.tsx +++ b/src/shared/components/molecules/Form/index.tsx @@ -2,7 +2,11 @@ import React, { useState } from 'react' import styled from '../../../lib/styled' import { LoadingButton, ButtonProps } from '../../atoms/Button' import FormInput, { FormInputProps } from './atoms/FormInput' -import FormSelect, { FormSelectProps } from './atoms/FormSelect' +import FormSelect, { + FormSelectProps, + SimpleFormSelect, + SimpleFormSelectProps, +} from './atoms/FormSelect' import cc from 'classcat' import { AppComponent } from '../../../lib/types' import FormEmoji, { FormEmojiProps } from './atoms/FormEmoji' @@ -30,6 +34,7 @@ export type FormRowProps = { props: FormInputProps & { ref?: React.Ref } } | { type: 'select'; props: FormSelectProps } + | { type: 'select--string'; props: SimpleFormSelectProps } | { type: 'emoji'; props: FormEmojiProps } | { type: 'button'; props: LabelButtonProps } | { type: 'node'; element: React.ReactNode } @@ -70,6 +75,8 @@ const Form: AppComponent = ({ ) : item.type === 'select' ? ( + ) : item.type === 'select--string' ? ( + ) : item.type === 'emoji' ? ( ) : item.type === 'button' ? ( From 45d0c4fb12be11ed53281d14d7fb4efa8d35867d Mon Sep 17 00:00:00 2001 From: davy-c Date: Tue, 18 May 2021 08:35:14 +0900 Subject: [PATCH 65/91] optional onSubmit --- src/shared/components/molecules/Form/index.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/shared/components/molecules/Form/index.tsx b/src/shared/components/molecules/Form/index.tsx index a1d1a3effe..35b16df6ef 100644 --- a/src/shared/components/molecules/Form/index.tsx +++ b/src/shared/components/molecules/Form/index.tsx @@ -13,8 +13,7 @@ import FormEmoji, { FormEmojiProps } from './atoms/FormEmoji' interface FormProps { rows: FormRowProps[] - onSubmit: React.FormEventHandler - + onSubmit?: React.FormEventHandler submitButton?: LabelButtonProps onCancel?: () => void } @@ -52,6 +51,10 @@ const Form: AppComponent = ({ return ( { + if (onSubmit == null) { + return + } + event.preventDefault() setSubmitState(true) new Promise(() => onSubmit(event)).finally(() => setSubmitState(false)) From 2ae12fb4f35feeebfbe0546dedcfa2c81cc6b9c3 Mon Sep 17 00:00:00 2001 From: davy-c Date: Tue, 18 May 2021 09:45:15 +0900 Subject: [PATCH 66/91] preferences, layout and overflow, typo --- .../molecules/TeamInvitesSection.tsx | 3 +- .../organisms/Subscription/PlanTables.tsx | 4 +- .../organisms/settings/SettingsComponent.tsx | 7 +- .../settings/UserPreferencesForm.tsx | 244 +++++++++--------- src/cloud/lib/hooks/useCloudUI.tsx | 2 +- .../molecules/Form/atoms/FormRow.tsx | 0 .../molecules/Form/atoms/FormSelect.tsx | 3 +- .../components/molecules/Form/index.tsx | 103 +------- .../molecules/Form/layouts/FormRow.tsx | 107 ++++++++ .../components/organisms/EmojiInputForm.tsx | 3 +- .../organisms/Settings/atoms/SettingMain.tsx | 9 - .../Settings/atoms/SettingTabContent.tsx | 40 ++- .../components/organisms/Settings/index.tsx | 7 + 13 files changed, 273 insertions(+), 259 deletions(-) delete mode 100644 src/shared/components/molecules/Form/atoms/FormRow.tsx create mode 100644 src/shared/components/molecules/Form/layouts/FormRow.tsx delete mode 100644 src/shared/components/organisms/Settings/atoms/SettingMain.tsx diff --git a/src/cloud/components/molecules/TeamInvitesSection.tsx b/src/cloud/components/molecules/TeamInvitesSection.tsx index 027ba7c37d..8baffcfdc3 100644 --- a/src/cloud/components/molecules/TeamInvitesSection.tsx +++ b/src/cloud/components/molecules/TeamInvitesSection.tsx @@ -158,7 +158,6 @@ const TeamInvitesSection = ({ userPermissions }: TeamInvitesSectionProps) => { }, { type: 'select--string', - props: { value: role, onChange: selectRole, @@ -176,7 +175,7 @@ const TeamInvitesSection = ({ userPermissions }: TeamInvitesSectionProps) => { ], }, ]} - > + /> {role === 'admin' ? ( Admins can handle billing, remove or promote/demote members. diff --git a/src/cloud/components/organisms/Subscription/PlanTables.tsx b/src/cloud/components/organisms/Subscription/PlanTables.tsx index 56bbb80b6b..fdec200a6d 100644 --- a/src/cloud/components/organisms/Subscription/PlanTables.tsx +++ b/src/cloud/components/organisms/Subscription/PlanTables.tsx @@ -49,8 +49,8 @@ const PlanTables = ({ const trialEndDate = new Date(subscription.currentPeriodEnd * 1000) return (

- In free trial ({' '} - {formatDistanceToNow(trialEndDate, { includeSeconds: false })} left ) + In free trial ( + {formatDistanceToNow(trialEndDate, { includeSeconds: false })} left)

) } diff --git a/src/cloud/components/organisms/settings/SettingsComponent.tsx b/src/cloud/components/organisms/settings/SettingsComponent.tsx index 07b2a5c612..c8f2a9218c 100644 --- a/src/cloud/components/organisms/settings/SettingsComponent.tsx +++ b/src/cloud/components/organisms/settings/SettingsComponent.tsx @@ -27,7 +27,6 @@ import { PageStoreWithTeam } from '../../../interfaces/pageStore' import Settings from '../../../../shared/components/organisms/Settings' import SettingSidenavHeader from '../../../../shared/components/organisms/Settings/molecules/SettingSidenavHeader' import SettingSidenav from '../../../../shared/components/organisms/Settings/atoms/SettingSidenav' -import SettingMain from '../../../../shared/components/organisms/Settings/atoms/SettingMain' import SettingTabButton from '../../../../shared/components/organisms/Settings/atoms/SettingTabButton' import Icon from '../../../../shared/components/atoms/Icon' import SettingCloseButton from '../../../../shared/components/organisms/Settings/atoms/SettingCloseButton' @@ -199,7 +198,7 @@ const SettingsComponent = () => { } content={ - +
{content} { > - +
} - > + /> ) } diff --git a/src/cloud/components/organisms/settings/UserPreferencesForm.tsx b/src/cloud/components/organisms/settings/UserPreferencesForm.tsx index 33f4c1be46..1ae521e317 100644 --- a/src/cloud/components/organisms/settings/UserPreferencesForm.tsx +++ b/src/cloud/components/organisms/settings/UserPreferencesForm.tsx @@ -10,76 +10,79 @@ import { GeneralEditorIndentSize, } from '../../../lib/stores/settings' import { useTranslation } from 'react-i18next' -import { SelectChangeEventHandler } from '../../../lib/utils/events' import { trackEvent } from '../../../api/track' import { MixpanelActionTrackTypes } from '../../../interfaces/analytics/mixpanel' -import SettingSelect from '../../../../shared/components/organisms/Settings/atoms/SettingSelect' +import Form from '../../../../shared/components/molecules/Form' +import FormSelect, { + FormSelectOption, + SimpleFormSelect, +} from '../../../../shared/components/molecules/Form/atoms/FormSelect' +import FormRow from '../../../../shared/components/molecules/Form/layouts/FormRow' const UserPreferencesForm = () => { const { settings, setSettings } = useSettings() const { t } = useTranslation() - const selectTheme: SelectChangeEventHandler = useCallback( - (event) => { + const selectTheme = useCallback( + (formOption: FormSelectOption) => { setSettings({ - 'general.theme': event.target.value as GeneralThemeOptions, + 'general.theme': formOption.value as GeneralThemeOptions, }) trackEvent(MixpanelActionTrackTypes.ThemeChangeApp, { - theme: event.target.value, + theme: formOption.value, }) }, [setSettings] ) - const selectEditorTheme: SelectChangeEventHandler = useCallback( - (event) => { + const selectEditorTheme = useCallback( + (value: string) => { setSettings({ - 'general.editorTheme': event.target.value as CodeMirrorEditorTheme, + 'general.editorTheme': value as CodeMirrorEditorTheme, }) trackEvent(MixpanelActionTrackTypes.ThemeChangeEditor, { - theme: event.target.value, + theme: value, }) }, [setSettings] ) - const selectCodeBlockTheme: SelectChangeEventHandler = useCallback( - (event) => { + const selectCodeBlockTheme = useCallback( + (value: string) => { setSettings({ - 'general.codeBlockTheme': event.target.value as CodeMirrorEditorTheme, + 'general.codeBlockTheme': value as CodeMirrorEditorTheme, }) trackEvent(MixpanelActionTrackTypes.ThemeChangeCodeblock, { - theme: event.target.value, + theme: value, }) }, [setSettings] ) - const selectEditorKeyMap: SelectChangeEventHandler = useCallback( - (event) => { + const selectEditorKeyMap = useCallback( + (value: string) => { setSettings({ - 'general.editorKeyMap': event.target.value as CodeMirrorKeyMap, + 'general.editorKeyMap': value as CodeMirrorKeyMap, }) }, [setSettings] ) - const selectIndentType: SelectChangeEventHandler = useCallback( - (event) => { + const selectIndentType = useCallback( + (value: string) => { setSettings({ - 'general.editorIndentType': event.target - .value as GeneralEditorIndentType, + 'general.editorIndentType': value as GeneralEditorIndentType, }) }, [setSettings] ) - const selectIndentSize: SelectChangeEventHandler = useCallback( - (event) => { + const selectIndentSize = useCallback( + (value: string) => { setSettings({ 'general.editorIndentSize': parseInt( - event.target.value, + value, 10 ) as GeneralEditorIndentSize, }) @@ -88,101 +91,104 @@ const UserPreferencesForm = () => { ) return ( - <> -
- - - - - } - > -
- -
- - {codeMirrorEditorThemes.map((val) => ( - - ))} - - } - > -
- -
- - {codeMirrorEditorThemes.map((val) => ( - - ))} - - } - > -
- -
- - {codeMirrorKeyMap.map((val) => ( - - ))} - - } - > -
- -
- - - - - } - > -
- -
- - - - - - } - > -
- +
+ ), + }, + ], + }, + { + title: t('settings.editorTheme'), + items: [ + { + type: 'select--string', + props: { + options: codeMirrorEditorThemes, + value: settings['general.editorTheme'], + onChange: selectEditorTheme, + }, + }, + ], + }, + { + title: t('settings.codeblockTheme'), + items: [ + { + type: 'select--string', + props: { + options: codeMirrorEditorThemes, + value: settings['general.codeBlockTheme'], + onChange: selectCodeBlockTheme, + }, + }, + ], + }, + { + title: t('settings.editorKeyMap'), + items: [ + { + type: 'select--string', + props: { + options: codeMirrorKeyMap, + value: settings['general.editorKeyMap'], + onChange: selectEditorKeyMap, + }, + }, + ], + }, + { + title: 'Editor Indent Type', + items: [ + { + type: 'node', + element: ( + + ), + }, + ], + }, + ]} + > + + ) } diff --git a/src/cloud/lib/hooks/useCloudUI.tsx b/src/cloud/lib/hooks/useCloudUI.tsx index 90b4394abc..b53b03b4ea 100644 --- a/src/cloud/lib/hooks/useCloudUI.tsx +++ b/src/cloud/lib/hooks/useCloudUI.tsx @@ -1,6 +1,6 @@ import { mdiFileDocumentOutline, mdiFolderOutline } from '@mdi/js' import React, { useCallback } from 'react' -import { FormRowProps } from '../../../shared/components/molecules/Form' +import { FormRowProps } from '../../../shared/components/molecules/Form/layouts/FormRow' import EmojiInputForm from '../../../shared/components/organisms/EmojiInputForm' import { DialogIconTypes, useDialog } from '../../../shared/lib/stores/dialog' import { useModal } from '../../../shared/lib/stores/modal' diff --git a/src/shared/components/molecules/Form/atoms/FormRow.tsx b/src/shared/components/molecules/Form/atoms/FormRow.tsx deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/shared/components/molecules/Form/atoms/FormSelect.tsx b/src/shared/components/molecules/Form/atoms/FormSelect.tsx index 01c454777f..ec0622ce6c 100644 --- a/src/shared/components/molecules/Form/atoms/FormSelect.tsx +++ b/src/shared/components/molecules/Form/atoms/FormSelect.tsx @@ -29,7 +29,7 @@ interface StandardFormSelectOptions { interface SimpleFormSelectOptions { value: string - options: string[] + options: readonly string[] onChange: (val: string) => void } @@ -178,6 +178,7 @@ const Container = styled.div` .form__select .form__select__menu { background-color: ${({ theme }) => theme.colors.background.primary}; border: 1px solid ${({ theme }) => theme.colors.border.main}; + overflow-y: visible !important; } .form__select .form__select__option { diff --git a/src/shared/components/molecules/Form/index.tsx b/src/shared/components/molecules/Form/index.tsx index 35b16df6ef..b14d4f65aa 100644 --- a/src/shared/components/molecules/Form/index.tsx +++ b/src/shared/components/molecules/Form/index.tsx @@ -1,45 +1,17 @@ import React, { useState } from 'react' import styled from '../../../lib/styled' -import { LoadingButton, ButtonProps } from '../../atoms/Button' -import FormInput, { FormInputProps } from './atoms/FormInput' -import FormSelect, { - FormSelectProps, - SimpleFormSelect, - SimpleFormSelectProps, -} from './atoms/FormSelect' +import { LoadingButton } from '../../atoms/Button' import cc from 'classcat' import { AppComponent } from '../../../lib/types' -import FormEmoji, { FormEmojiProps } from './atoms/FormEmoji' +import FormRow, { FormRowProps, FormRowButtonProps } from './layouts/FormRow' interface FormProps { rows: FormRowProps[] onSubmit?: React.FormEventHandler - submitButton?: LabelButtonProps + submitButton?: FormRowButtonProps onCancel?: () => void } -type LabelButtonProps = ButtonProps & { - label: React.ReactNode - spinning?: boolean -} - -export type FormRowProps = { - layout?: 'column' | 'split' - title?: React.ReactNode - description?: React.ReactNode - items?: ( - | { - type: 'input' - props: FormInputProps & { ref?: React.Ref } - } - | { type: 'select'; props: FormSelectProps } - | { type: 'select--string'; props: SimpleFormSelectProps } - | { type: 'emoji'; props: FormEmojiProps } - | { type: 'button'; props: LabelButtonProps } - | { type: 'node'; element: React.ReactNode } - )[] -} - const Form: AppComponent = ({ onSubmit, rows, @@ -62,42 +34,7 @@ const Form: AppComponent = ({ className={cc(['form', className])} > {rows.map((row, i) => { - return ( -
- {row.title != null && ( -
{row.title}
- )} - {row.items != null && ( -
- {row.items.map((item, k) => ( -
- {item.type === 'input' ? ( - - ) : item.type === 'select' ? ( - - ) : item.type === 'select--string' ? ( - - ) : item.type === 'emoji' ? ( - - ) : item.type === 'button' ? ( - - {item.props.label} - - ) : ( - item.element - )} -
- ))} -
- )} - {row.description != null && ( -
{row.description}
- )} -
- ) + return })} {children} {submitButton != null && ( @@ -121,39 +58,7 @@ export default Form const Container = styled.form` font-size: ${({ theme }) => theme.sizes.fonts.df}px; - .form__row__description { - color: ${({ theme }) => theme.colors.text.subtle}; - } - .form__row + .form__row { margin-top: ${({ theme }) => theme.sizes.spaces.df}px; } - - .form__row__items { - display: flex; - flex-direction: row; - flex-wrap: nowrap; - align-items: stretch; - justify-content: space-between; - } - - .form__row__item { - display: flex; - align-items: stretch; - } - - .form__row__item--emoji, - .form__row__item--button { - flex: 0 0 auto; - } - - .form__row__item:not(.form__row__item--emoji):not(.form__row__item--button), - .form__row__item:not(.form__row__item--emoji):not(.form__row__item--button) - > * { - flex: 1 1 auto; - } - - .form__row__item + .form__row__item { - margin-left: ${({ theme }) => theme.sizes.spaces.df}px; - } ` diff --git a/src/shared/components/molecules/Form/layouts/FormRow.tsx b/src/shared/components/molecules/Form/layouts/FormRow.tsx new file mode 100644 index 0000000000..6e2e616aaa --- /dev/null +++ b/src/shared/components/molecules/Form/layouts/FormRow.tsx @@ -0,0 +1,107 @@ +import React from 'react' +import styled from '../../../../lib/styled' +import { AppComponent } from '../../../../lib/types' +import { ButtonProps, LoadingButton } from '../../../atoms/Button' +import FormEmoji, { FormEmojiProps } from '../atoms/FormEmoji' +import FormInput, { FormInputProps } from '../atoms/FormInput' +import FormSelect, { + FormSelectProps, + SimpleFormSelect, + SimpleFormSelectProps, +} from '../atoms/FormSelect' +import cc from 'classcat' + +export type FormRowButtonProps = ButtonProps & { + label: React.ReactNode + spinning?: boolean +} + +export type FormRowProps = { + layout?: 'column' | 'split' + title?: React.ReactNode + description?: React.ReactNode + items?: ( + | { + type: 'input' + props: FormInputProps & { ref?: React.Ref } + } + | { type: 'select'; props: FormSelectProps } + | { type: 'select--string'; props: SimpleFormSelectProps } + | { type: 'emoji'; props: FormEmojiProps } + | { type: 'button'; props: FormRowButtonProps } + | { type: 'node'; element: React.ReactNode } + )[] +} + +const FormRow: AppComponent<{ row: FormRowProps }> = ({ row, className }) => { + return ( + + {row.title != null &&
{row.title}
} + {row.items != null && ( +
+ {row.items.map((item, k) => ( +
+ {item.type === 'input' ? ( + + ) : item.type === 'select' ? ( + + ) : item.type === 'select--string' ? ( + + ) : item.type === 'emoji' ? ( + + ) : item.type === 'button' ? ( + + {item.props.label} + + ) : ( + item.element + )} +
+ ))} +
+ )} + {row.description != null && ( +
{row.description}
+ )} +
+ ) +} + +const Container = styled.div` + .form__row__description { + color: ${({ theme }) => theme.colors.text.subtle}; + } + + .form__row__items { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + align-items: stretch; + justify-content: space-between; + } + + .form__row__item { + display: flex; + align-items: stretch; + } + + .form__row__item--emoji, + .form__row__item--button { + flex: 0 0 auto; + } + + .form__row__item:not(.form__row__item--emoji):not(.form__row__item--button), + .form__row__item:not(.form__row__item--emoji):not(.form__row__item--button) + > * { + flex: 1 1 auto; + } + + .form__row__item + .form__row__item { + margin-left: ${({ theme }) => theme.sizes.spaces.df}px; + } +` + +export default FormRow diff --git a/src/shared/components/organisms/EmojiInputForm.tsx b/src/shared/components/organisms/EmojiInputForm.tsx index 9f41bca52b..f28ddf49e0 100644 --- a/src/shared/components/organisms/EmojiInputForm.tsx +++ b/src/shared/components/organisms/EmojiInputForm.tsx @@ -1,7 +1,8 @@ import React, { useRef, useState } from 'react' import { useEffectOnce } from 'react-use' import { ButtonProps } from '../atoms/Button' -import Form, { FormRowProps } from '../molecules/Form' +import Form from '../molecules/Form' +import { FormRowProps } from '../molecules/Form/layouts/FormRow' interface EmojiInputFormProps { prevRows?: FormRowProps[] diff --git a/src/shared/components/organisms/Settings/atoms/SettingMain.tsx b/src/shared/components/organisms/Settings/atoms/SettingMain.tsx deleted file mode 100644 index fd8ae7695d..0000000000 --- a/src/shared/components/organisms/Settings/atoms/SettingMain.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import styled from '../../../../../lib/styled' - -const SettingMain = styled.div` - flex: 1; - position: relative; - overflow: hidden auto; -` - -export default SettingMain diff --git a/src/shared/components/organisms/Settings/atoms/SettingTabContent.tsx b/src/shared/components/organisms/Settings/atoms/SettingTabContent.tsx index 097f74ffdf..0509f41856 100644 --- a/src/shared/components/organisms/Settings/atoms/SettingTabContent.tsx +++ b/src/shared/components/organisms/Settings/atoms/SettingTabContent.tsx @@ -14,61 +14,59 @@ const SettingTabContent = ({ body, footer, }: SettingTabContentProps) => ( - -
-
-
-

{title}

-

{description}

+ +
+
+
+

{title}

+

+ {description} +

-
{body}
-
{footer}
+
{body}
+
{footer}
) const Container = styled.div` - display: flex; - flex-direction: column; + display: block; width: 100%; - height: 100%; padding-top: ${({ theme }) => theme.sizes.spaces.xl}px; - .tab-content__scrollable { - flex: 1 1 auto; + .setting__tab__content__scrollable { width: 100%; padding: ${({ theme }) => theme.sizes.spaces.xl}px ${({ theme }) => theme.sizes.spaces.md}px; - overflow: hidden auto; } - .tab-content__container { + .setting__tab__content__container { max-width: 700px; margin: 0 auto; } - .tab-content__header { + .setting__tab__content__header { margin-bottom: ${({ theme }) => theme.sizes.spaces.l}px; padding-bottom: ${({ theme }) => theme.sizes.spaces.l}px; border-bottom: 1px solid ${({ theme }) => theme.colors.border.main}; } - .tab-content__header__title { + .setting__tab__content__header__title { margin-top: 0; margin-bottom: ${({ theme }) => theme.sizes.spaces.xsm}px; font-size: ${({ theme }) => theme.sizes.fonts.l}px; font-weight: normal; } - .tab-content__header__description { + .setting__tab__content__header__description { margin-bottom: 0; color: ${({ theme }) => theme.colors.text.subtle}; font-size: ${({ theme }) => theme.sizes.fonts.df}px; } - .tab-content__body, - .tab-content__footer { + .setting__tab__content__body, + .setting__tab__content__footer { h2 { font-size: ${({ theme }) => theme.sizes.fonts.md}px; font-weight: normal; @@ -83,7 +81,7 @@ const Container = styled.div` } } - .tab-content__body { + .setting__tab__content__body { section { margin: ${({ theme }) => theme.sizes.spaces.md}px 0; } diff --git a/src/shared/components/organisms/Settings/index.tsx b/src/shared/components/organisms/Settings/index.tsx index a5e84fc7d3..d548d3a365 100644 --- a/src/shared/components/organisms/Settings/index.tsx +++ b/src/shared/components/organisms/Settings/index.tsx @@ -51,8 +51,15 @@ const Container = styled.div` .settings__content { flex: 1 1 100%; min-width: 0; + display: block; + height: 100vh; overflow: auto; } + + .settings__content__wrapper { + width: 100%; + height: auto; + } ` export default Settings From 720191a4393388f2b8ed4d832ce708321155f0f0 Mon Sep 17 00:00:00 2001 From: davy-c Date: Tue, 18 May 2021 11:17:03 +0900 Subject: [PATCH 67/91] fix personal info tab --- .../molecules/TeamInvitesSection.tsx | 1 + .../organisms/settings/MembersTab.tsx | 42 +++--- .../organisms/settings/PersonalInfoTab.tsx | 131 +++++++----------- .../organisms/settings/TeamInfoTab.tsx | 2 - src/shared/components/atoms/Button.tsx | 4 - .../molecules/Form/atoms/FormEmoji.tsx | 3 +- .../molecules/Form/atoms/FormImage.tsx | 115 +++++++++++++++ .../molecules/Form/atoms/FormInput.tsx | 3 +- .../molecules/Form/atoms/FormSelect.tsx | 31 +++-- .../components/molecules/Form/index.tsx | 8 +- .../molecules/Form/layouts/FormRow.tsx | 9 ++ .../Settings/atoms/SettingDivider.tsx | 11 -- .../Settings/atoms/SettingTabContent.tsx | 10 +- .../Settings/molecules/SettingIconInput.tsx | 65 --------- src/shared/lib/styled/styleFunctions.ts | 5 + 15 files changed, 240 insertions(+), 200 deletions(-) create mode 100644 src/shared/components/molecules/Form/atoms/FormImage.tsx delete mode 100644 src/shared/components/organisms/Settings/atoms/SettingDivider.tsx delete mode 100644 src/shared/components/organisms/Settings/molecules/SettingIconInput.tsx diff --git a/src/cloud/components/molecules/TeamInvitesSection.tsx b/src/cloud/components/molecules/TeamInvitesSection.tsx index 8baffcfdc3..2c4dd2a993 100644 --- a/src/cloud/components/molecules/TeamInvitesSection.tsx +++ b/src/cloud/components/molecules/TeamInvitesSection.tsx @@ -170,6 +170,7 @@ const TeamInvitesSection = ({ userPermissions }: TeamInvitesSectionProps) => { props: { type: 'submit', label: 'Send', + disabled: sending, }, }, ], diff --git a/src/cloud/components/organisms/settings/MembersTab.tsx b/src/cloud/components/organisms/settings/MembersTab.tsx index c98562feab..6d9afd245c 100644 --- a/src/cloud/components/organisms/settings/MembersTab.tsx +++ b/src/cloud/components/organisms/settings/MembersTab.tsx @@ -40,11 +40,11 @@ import SettingsTeamForm from '../../molecules/SettingsTeamForm' import { guestsPerMember } from '../../../lib/subscription' import { useToast } from '../../../../shared/lib/stores/toast' import SettingTabContent from '../../../../shared/components/organisms/Settings/atoms/SettingTabContent' -import SettingSelect from '../../../../shared/components/organisms/Settings/atoms/SettingSelect' import Button from '../../../../shared/components/atoms/Button' import Flexbox from '../../../../shared/components/atoms/Flexbox' import SettingTabSelector from '../../../../shared/components/organisms/Settings/atoms/SettingTabSelector' import { ExternalLink } from '../../../../shared/components/atoms/Link' +import { SimpleFormSelect } from '../../../../shared/components/molecules/Form/atoms/FormSelect' const MembersTab = () => { const { t } = useTranslation() @@ -219,12 +219,10 @@ const MembersTab = () => { const changePermissionsRole = useCallback( async ( - event: React.ChangeEvent, + targetedRole, userPermissions, targetedPermissions: SerializedUserTeamPermissions ) => { - event.preventDefault() - const targetedRole = event.target.value if ( team == null || userPermissions.role !== 'admin' || @@ -536,32 +534,22 @@ const MembersTab = () => { }} /> ) : ( - + onChange={(value: string) => changePermissionsRole( - e, + value, currentUserPermissions, permission ) } - style={{ - width: 'auto', - minWidth: 'initial', - height: 24, - marginRight: 16, - }} - disabled={ + isDisabled={ !currentUserIsAdmin || targetPermissionsAreUsersOwn } - options={ - <> - - - - } - > + options={['admin', 'member']} + /> )} {(targetPermissionsAreUsersOwn || currentUserIsAdmin) && ( @@ -737,11 +725,13 @@ const StyledMembersTable = styled.table` .user-action { position: relative; - select { - padding-left: 0; - padding-right: ${({ theme }) => theme.space.small}px; - background-color: transparent; - border: transparent; + + .form__select__wrapper { + flex: 1 0 auto; + } + + .form__select .form__select__control { + /* border: transparent; */ } } diff --git a/src/cloud/components/organisms/settings/PersonalInfoTab.tsx b/src/cloud/components/organisms/settings/PersonalInfoTab.tsx index d38a025067..e551455782 100644 --- a/src/cloud/components/organisms/settings/PersonalInfoTab.tsx +++ b/src/cloud/components/organisms/settings/PersonalInfoTab.tsx @@ -3,19 +3,13 @@ import { useTranslation } from 'react-i18next' import { useGlobalData } from '../../../lib/stores/globalData' import { saveUserInfo, updateUserIcon } from '../../../api/users' import { buildIconUrl } from '../../../api/files' -import { Spinner } from '../../atoms/Spinner' import { useSettings } from '../../../lib/stores/settings' import AccountLink from '../../atoms/Link/AccountLink' -import { SelectChangeEventHandler } from '../../../lib/utils/events' import { UserEmailNotificationType } from '../../../interfaces/db/userSettings' import { saveUserSettings } from '../../../api/users/settings' import { useToast } from '../../../../shared/lib/stores/toast' import SettingTabContent from '../../../../shared/components/organisms/Settings/atoms/SettingTabContent' -import SettingInput from '../../../../shared/components/organisms/Settings/atoms/SettingInput' -import SettingSelect from '../../../../shared/components/organisms/Settings/atoms/SettingSelect' -import Button from '../../../../shared/components/atoms/Button' -import SettingDivider from '../../../../shared/components/organisms/Settings/atoms/SettingDivider' -import SettingIconInput from '../../../../shared/components/organisms/Settings/molecules/SettingIconInput' +import Form from '../../../../shared/components/molecules/Form' const PersonalInfoTab = () => { const { @@ -92,19 +86,19 @@ const PersonalInfoTab = () => { return buildIconUrl(currentUser.icon.location) }, [currentUser]) - const selectCurrentEmailNotifications: SelectChangeEventHandler = useCallback( - (event) => { - let value: UserEmailNotificationType | 'never' - switch (event.target.value) { + const selectCurrentEmailNotifications = useCallback( + (value: string | 'never') => { + let targetedValue: UserEmailNotificationType | 'never' + switch (value) { case 'daily': case 'weekly': - value = event.target.value + targetedValue = value break case 'never': default: - value = 'never' + targetedValue = 'never' } - setCurrentEmailNotifications(value) + setCurrentEmailNotifications(targetedValue) }, [] ) @@ -114,73 +108,54 @@ const PersonalInfoTab = () => { title={t('settings.personalInfo')} description={'Manage your Boost Note profile.'} body={ - <> - {currentUser != null && ( - <> -
- -
-
- -
- - )} - - {currentUser != null && ( -
- - - - - - } - > -
- )} - -
- -
- + currentUser == null ? null : ( +
+ ) } footer={ <> -

{t('settings.account.delete')}

You may delete your account at any time, note that this is diff --git a/src/cloud/components/organisms/settings/TeamInfoTab.tsx b/src/cloud/components/organisms/settings/TeamInfoTab.tsx index 70ee2670c4..2a94962377 100644 --- a/src/cloud/components/organisms/settings/TeamInfoTab.tsx +++ b/src/cloud/components/organisms/settings/TeamInfoTab.tsx @@ -6,7 +6,6 @@ import { useSettings } from '../../../lib/stores/settings' import TeamLink from '../../atoms/Link/TeamLink' import SettingsTeamForm from '../../molecules/SettingsTeamForm' import SettingTabContent from '../../../../shared/components/organisms/Settings/atoms/SettingTabContent' -import SettingDivider from '../../../../shared/components/organisms/Settings/atoms/SettingDivider' const TeamInfoTab = () => { const { team, currentUserPermissions } = usePage() @@ -25,7 +24,6 @@ const TeamInfoTab = () => { return ( <> -

Delete Space

diff --git a/src/shared/components/atoms/Button.tsx b/src/shared/components/atoms/Button.tsx index afa2106c93..fcd04239bb 100644 --- a/src/shared/components/atoms/Button.tsx +++ b/src/shared/components/atoms/Button.tsx @@ -354,8 +354,4 @@ const StyledButton = styled.button` &:focus { box-shadow: 0 0 0 1px ${({ theme }) => theme.colors.variants.info.base}; } - - .button__spinner { - margin-top: 6px; - } ` diff --git a/src/shared/components/molecules/Form/atoms/FormEmoji.tsx b/src/shared/components/molecules/Form/atoms/FormEmoji.tsx index 9748d1cef5..d33f6f403e 100644 --- a/src/shared/components/molecules/Form/atoms/FormEmoji.tsx +++ b/src/shared/components/molecules/Form/atoms/FormEmoji.tsx @@ -9,6 +9,7 @@ import styled from '../../../../lib/styled' import { useEmojiPicker } from '../../../../../cloud/lib/stores/emoji' import Icon from '../../../atoms/Icon' import { Emoji } from 'emoji-mart' +import { formInputHeight } from '../../../../lib/styled/styleFunctions' export interface FormEmojiProps { emoji?: string @@ -95,7 +96,7 @@ const Container = styled.button` padding: 0 ${({ theme }) => theme.sizes.spaces.sm}px; border-radius: ${({ theme }) => theme.borders.radius}px; font-size: ${({ theme }) => theme.sizes.fonts.df}px; - height: 32px; + ${formInputHeight()} outline: none; background: none; border: 1px solid ${({ theme }) => theme.colors.border.main}; diff --git a/src/shared/components/molecules/Form/atoms/FormImage.tsx b/src/shared/components/molecules/Form/atoms/FormImage.tsx new file mode 100644 index 0000000000..43ddb6b0d0 --- /dev/null +++ b/src/shared/components/molecules/Form/atoms/FormImage.tsx @@ -0,0 +1,115 @@ +import React, { useCallback, useState } from 'react' +import styled from '../../../../lib/styled' +import { AppComponent } from '../../../../lib/types' +import cc from 'classcat' +import Icon from '@mdi/react' + +export interface FormImageProps { + onChange?: (file: File) => void + defaultUrl?: string + defaultIcon?: string + label?: string +} + +const FormImage: AppComponent = ({ + className, + defaultIcon, + defaultUrl, + label = 'Select Image...', + onChange, +}) => { + const [fileUrl, setFileUrl] = useState(null) + + const changeHandler: React.ChangeEventHandler = useCallback( + (event) => { + if ( + event.target != null && + event.target.files != null && + event.target.files.length > 0 + ) { + const file = event.target.files[0] + setFileUrl(URL.createObjectURL(file)) + if (onChange != null) { + onChange(file) + } + } + }, + [onChange] + ) + + return ( + +

+ {fileUrl == null && defaultUrl == null && defaultIcon != null ? ( + + ) : ( + + )} +
+ + + ) +} + +const Container = styled.div` + display: flex; + align-items: center; + + .form__image__wrapper { + width: 90px; + height: 90px; + } + + .form__image--img { + object-fit: cover; + width: 100%; + height: 100%; + background: ${({ theme }) => theme.colors.background.secondary}; + border: 1px solid ${({ theme }) => theme.colors.border.second}; + border-radius: 50%; + } + + .form__image__label { + position: relative; + cursor: pointer; + margin-left: ${({ theme }) => theme.sizes.spaces.md}px; + + & > span { + display: flex; + align-items: center; + height: 32px; + padding: 0 ${({ theme }) => theme.sizes.spaces.md}px; + background-color: ${({ theme }) => theme.colors.variants.secondary.base}; + border-radius: 4px; + color: ${({ theme }) => theme.colors.variants.secondary.text}; + font-size: ${({ theme }) => theme.sizes.fonts.df}px; + transition: 200ms background-color; + + &.focus { + filter: brightness(103%); + } + &:hover { + filter: brightness(106%); + } + &:active, + &.button__state--active { + filter: brightness(112%); + } + } + + & > input[type='file'] { + position: absolute; + opacity: 0; + height: 0px; + width: 0px; + } + } +` + +export default FormImage diff --git a/src/shared/components/molecules/Form/atoms/FormInput.tsx b/src/shared/components/molecules/Form/atoms/FormInput.tsx index 42aaab5018..c7457f426c 100644 --- a/src/shared/components/molecules/Form/atoms/FormInput.tsx +++ b/src/shared/components/molecules/Form/atoms/FormInput.tsx @@ -5,6 +5,7 @@ import React, { } from 'react' import cc from 'classcat' import styled from '../../../../lib/styled' +import { formInputHeight } from '../../../../lib/styled/styleFunctions' export interface FormInputProps { type?: 'text' | 'number' | 'email' | 'password' @@ -98,7 +99,7 @@ const StyledInput = styled.input` padding: 0 ${({ theme }) => theme.sizes.spaces.sm}px; border-radius: ${({ theme }) => theme.borders.radius}px; font-size: ${({ theme }) => theme.sizes.fonts.df}px; - height: 32px; + ${formInputHeight()} outline: none; background: none; border: 1px solid ${({ theme }) => theme.colors.border.main}; diff --git a/src/shared/components/molecules/Form/atoms/FormSelect.tsx b/src/shared/components/molecules/Form/atoms/FormSelect.tsx index ec0622ce6c..1eaad04da2 100644 --- a/src/shared/components/molecules/Form/atoms/FormSelect.tsx +++ b/src/shared/components/molecules/Form/atoms/FormSelect.tsx @@ -2,6 +2,8 @@ import React, { useCallback, useMemo, useState } from 'react' import Select from 'react-select' import cc from 'classcat' import styled from '../../../../lib/styled' +import { formInputHeight } from '../../../../lib/styled/styleFunctions' +import { capitalize } from 'lodash' export interface FormSelectOption { label: string | React.ReactNode @@ -52,7 +54,7 @@ const FormSelect = ({ }: FormSelectProps & StandardFormSelectOptions) => { const [focused, setFocused] = useState(false) return ( - + - - - ) -} - -export default SettingIconInput - -const Container = styled.div` - display: flex; - align-items: center; - - .setting__icon-input__img { - width: 90px; - height: 90px; - } - - .setting__icon-input__img__content { - object-fit: cover; - width: 100%; - height: 100%; - background: ${({ theme }) => theme.colors.background.secondary}; - border: 1px solid ${({ theme }) => theme.colors.border.second}; - border-radius: 50%; - } -` diff --git a/src/shared/lib/styled/styleFunctions.ts b/src/shared/lib/styled/styleFunctions.ts index 07d9cd7f55..a20f4a3d2d 100644 --- a/src/shared/lib/styled/styleFunctions.ts +++ b/src/shared/lib/styled/styleFunctions.ts @@ -115,6 +115,11 @@ export const hideScroll = () => ` Form ———————————–———————————–———————————–——–—— */ +export const formInputHeight = () => ` + height: 32px; + min-height: 32px; +` + /* ———————————–———————————–———————————–——–—— Table ———————————–———————————–———————————–——–—— */ From fe72e2c7ab88ec42d818b51b50fb46f011d21c8a Mon Sep 17 00:00:00 2001 From: davy-c Date: Tue, 18 May 2021 11:22:04 +0900 Subject: [PATCH 68/91] fix dom nesting --- .../components/organisms/settings/MembersTab.tsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/cloud/components/organisms/settings/MembersTab.tsx b/src/cloud/components/organisms/settings/MembersTab.tsx index 6d9afd245c..bbda4d0e60 100644 --- a/src/cloud/components/organisms/settings/MembersTab.tsx +++ b/src/cloud/components/organisms/settings/MembersTab.tsx @@ -455,7 +455,9 @@ const MembersTab = () => { - User + + User + @@ -496,8 +498,10 @@ const MembersTab = () => { - User - Access Level + + User + Access Level + {permissions.map((permission) => { @@ -628,8 +632,10 @@ const MembersTab = () => {

- User - Access Level + + User + Access Level + {[...guestsMap.values()].map((guest) => ( From ea209d4459d7f431c1221735e7badb7735e6a555 Mon Sep 17 00:00:00 2001 From: davy-c Date: Tue, 18 May 2021 11:47:13 +0900 Subject: [PATCH 69/91] textarea and feedback form --- .../molecules/AppFeedbackForm/index.tsx | 83 +++++------- .../molecules/AppFeedbackForm/styled.ts | 45 ------- .../Modal/contents/FeedbackModal.tsx | 8 +- .../molecules/Form/atoms/FormTextArea.tsx | 123 ++++++++++++++++++ .../molecules/Form/layouts/FormRow.tsx | 7 + .../components/organisms/Modal/index.tsx | 1 - .../Settings/atoms/SettingSelect.tsx | 57 -------- .../Settings/atoms/SettingTextarea.tsx | 25 ---- 8 files changed, 167 insertions(+), 182 deletions(-) delete mode 100644 src/cloud/components/molecules/AppFeedbackForm/styled.ts create mode 100644 src/shared/components/molecules/Form/atoms/FormTextArea.tsx delete mode 100644 src/shared/components/organisms/Settings/atoms/SettingSelect.tsx delete mode 100644 src/shared/components/organisms/Settings/atoms/SettingTextarea.tsx diff --git a/src/cloud/components/molecules/AppFeedbackForm/index.tsx b/src/cloud/components/molecules/AppFeedbackForm/index.tsx index 9a8a436871..9d683ed47f 100644 --- a/src/cloud/components/molecules/AppFeedbackForm/index.tsx +++ b/src/cloud/components/molecules/AppFeedbackForm/index.tsx @@ -1,15 +1,11 @@ import React, { FormEvent, useState, useCallback, useRef } from 'react' import CustomButton from '../../atoms/buttons/CustomButton' -import { Spinner } from '../../atoms/Spinner' -import { StyledAppFeedbackForm } from './styled' -import { SelectChangeEventHandler } from '../../../lib/utils/events' import { registerAppFeedback } from '../../../api/users/appfeedback' import { AppFeedbackTypeOption } from '../../../interfaces/db/userAppFeedback' import ColoredBlock from '../../atoms/ColoredBlock' import { useEffectOnce } from 'react-use' import { useToast } from '../../../../shared/lib/stores/toast' -import SettingSelect from '../../../../shared/components/organisms/Settings/atoms/SettingSelect' -import SettingTextarea from '../../../../shared/components/organisms/Settings/atoms/SettingTextarea' +import Form from '../../../../shared/components/molecules/Form' const typeOptions: AppFeedbackTypeOption[] = ['Feature Request', 'Bug Report'] @@ -57,13 +53,9 @@ const AppFeedbackForm = () => { ] ) - const feedbackTypeChangeHandler: SelectChangeEventHandler = useCallback( - async (event) => { - event.preventDefault() - setFeedbackType(event.target.value as AppFeedbackTypeOption) - }, - [] - ) + const feedbackTypeChangeHandler = useCallback(async (value: string) => { + setFeedbackType(value as AppFeedbackTypeOption) + }, []) const feedbackOnChangeEvent = useCallback( (event: React.ChangeEvent) => { @@ -94,41 +86,38 @@ const AppFeedbackForm = () => { } return ( - -

Type of feedback

- - {typeOptions.map((val) => ( - - ))} - - } - > - -

Free form

- - -
- - {sending ? : 'Send'} - -
-
- + ) } diff --git a/src/cloud/components/molecules/AppFeedbackForm/styled.ts b/src/cloud/components/molecules/AppFeedbackForm/styled.ts deleted file mode 100644 index 796c44fec7..0000000000 --- a/src/cloud/components/molecules/AppFeedbackForm/styled.ts +++ /dev/null @@ -1,45 +0,0 @@ -import styled from '../../../lib/styled' - -export const StyledAppFeedbackForm = styled.form` - margin: 0 auto; - color: ${({ theme }) => theme.baseTextColor}; - - .flex { - display: flex; - width: 100%; - align-items: center; - } - - .row { - display: block; - margin: ${({ theme }) => theme.space.medium}px 0 - ${({ theme }) => theme.space.small}px; - } - - .submit-feedback { - width: 100px; - border-radius: 2px; - - svg { - color: ${({ theme }) => theme.emphasizedTextColor} !important; - } - } - - svg.icon { - top: 0; - left: 0; - position: relative !important; - } - - .submit-row { - display: flex; - width: 100%; - justify-content: flex-start; - align-items: center; - margin-top: ${({ theme }) => theme.space.xlarge}px; - } - - .clear { - clear: both; - } -` diff --git a/src/cloud/components/organisms/Modal/contents/FeedbackModal.tsx b/src/cloud/components/organisms/Modal/contents/FeedbackModal.tsx index 253964188a..45d8da226f 100644 --- a/src/cloud/components/organisms/Modal/contents/FeedbackModal.tsx +++ b/src/cloud/components/organisms/Modal/contents/FeedbackModal.tsx @@ -1,14 +1,8 @@ import React from 'react' -import { ModalContainer } from './styled' import AppFeedbackForm from '../../../molecules/AppFeedbackForm' const FeedbackModal = () => { - return ( - -

Feedback

- -
- ) + return } export default FeedbackModal diff --git a/src/shared/components/molecules/Form/atoms/FormTextArea.tsx b/src/shared/components/molecules/Form/atoms/FormTextArea.tsx new file mode 100644 index 0000000000..2d7c9588a1 --- /dev/null +++ b/src/shared/components/molecules/Form/atoms/FormTextArea.tsx @@ -0,0 +1,123 @@ +import React, { + ChangeEventHandler, + MouseEventHandler, + FocusEventHandler, +} from 'react' +import cc from 'classcat' +import styled from '../../../../lib/styled' + +export interface FormTextareaProps { + type?: 'text' | 'number' | 'email' | 'password' + id?: string + className?: string + disabled?: boolean + placeholder?: string + title?: string + value?: string + defaultValue?: string + readOnly?: boolean + autoComplete?: 'on' | 'off' + onBlur?: FocusEventHandler + onChange?: ChangeEventHandler + onClick?: MouseEventHandler + onMouseUp?: MouseEventHandler + onMouseDown?: MouseEventHandler + onMouseMove?: MouseEventHandler + onMouseOver?: MouseEventHandler + onMouseOut?: MouseEventHandler + onMouseEnter?: MouseEventHandler + onMouseLeave?: MouseEventHandler + onDoubleClick?: MouseEventHandler + onContextMenu?: MouseEventHandler + onFocus?: FocusEventHandler +} + +const FormTextarea = React.forwardRef( + ( + { + value, + className, + type = 'text', + autoComplete = 'off', + id, + placeholder, + title, + defaultValue, + readOnly, + disabled, + onBlur, + onChange, + onClick, + onMouseUp, + onMouseDown, + onMouseMove, + onMouseOver, + onMouseOut, + onMouseEnter, + onMouseLeave, + onDoubleClick, + onContextMenu, + onFocus, + }, + ref + ) => { + return ( + + ) + } +) + +export default FormTextarea + +const StyledTextarea = styled.textarea` + padding: 0 ${({ theme }) => theme.sizes.spaces.sm}px; + border-radius: ${({ theme }) => theme.borders.radius}px; + font-size: ${({ theme }) => theme.sizes.fonts.df}px; + outline: none; + background: none; + border: 1px solid ${({ theme }) => theme.colors.border.main}; + color: ${({ theme }) => theme.colors.text.primary}; + height: 200px; + resize: none; + + &:focus { + border-color: ${({ theme }) => theme.colors.variants.primary.base}; + } + &::placeholder { + color: ${({ theme }) => theme.colors.text.subtle}; + } + + &:disabled { + cursor: not-allowed; + opacity: 0.5; + } + + &:focus { + border-color: ${({ theme }) => theme.colors.variants.info.base}; + } +` diff --git a/src/shared/components/molecules/Form/layouts/FormRow.tsx b/src/shared/components/molecules/Form/layouts/FormRow.tsx index 4b372d610f..54d5cba702 100644 --- a/src/shared/components/molecules/Form/layouts/FormRow.tsx +++ b/src/shared/components/molecules/Form/layouts/FormRow.tsx @@ -11,6 +11,7 @@ import FormSelect, { } from '../atoms/FormSelect' import cc from 'classcat' import FormImage, { FormImageProps } from '../atoms/FormImage' +import FormTextarea, { FormTextareaProps } from '../atoms/FormTextArea' export type FormRowButtonProps = ButtonProps & { label: React.ReactNode @@ -26,6 +27,10 @@ export type FormRowProps = { type: 'input' props: FormInputProps & { ref?: React.Ref } } + | { + type: 'textarea' + props: FormTextareaProps & { ref?: React.Ref } + } | { type: 'select'; props: FormSelectProps } | { type: 'select--string'; props: SimpleFormSelectProps } | { type: 'emoji'; props: FormEmojiProps } @@ -52,6 +57,8 @@ const FormRow: AppComponent<{ row: FormRowProps }> = ({ row, className }) => { ) : item.type === 'select--string' ? ( + ) : item.type === 'textarea' ? ( + ) : item.type === 'image' ? ( ) : item.type === 'emoji' ? ( diff --git a/src/shared/components/organisms/Modal/index.tsx b/src/shared/components/organisms/Modal/index.tsx index 2a4640d1fd..d874e280bf 100644 --- a/src/shared/components/organisms/Modal/index.tsx +++ b/src/shared/components/organisms/Modal/index.tsx @@ -45,7 +45,6 @@ const ModalItem = ({ const contentRef = useRef(null) const onScrollClickHandler: React.MouseEventHandler = useCallback( (event) => { - console.log('clicked') if ( contentRef.current != null && contentRef.current.contains(event.target as Node) diff --git a/src/shared/components/organisms/Settings/atoms/SettingSelect.tsx b/src/shared/components/organisms/Settings/atoms/SettingSelect.tsx deleted file mode 100644 index e95ab4d91e..0000000000 --- a/src/shared/components/organisms/Settings/atoms/SettingSelect.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React from 'react' -import styled from '../../../../lib/styled' - -interface SettingSelectProps { - label?: string - value?: string | number - disabled?: boolean - onChange?: (val: any) => void - style?: React.CSSProperties - options: React.ReactNode -} - -const SettingSelect = ({ - label, - value, - disabled, - onChange, - options, -}: SettingSelectProps) => ( - - - - -) - -const Container = styled.div` - label { - display: block; - margin-bottom: ${({ theme }) => theme.sizes.spaces.sm}px; - color: ${({ theme }) => theme.colors.text.secondary}; - font-size: ${({ theme }) => theme.sizes.fonts.df}px; - } - - select { - width: 100%; - height: 40px; - min-width: 300px; - max-width: 450px; - padding: 0 ${({ theme }) => theme.sizes.spaces.sm}px; - border-radius: 4px; - background-color: ${({ theme }) => theme.colors.background.primary}; - border: 1px solid ${({ theme }) => theme.colors.border.main}; - color: ${({ theme }) => theme.colors.text.primary}; - - &:focus { - border-color: ${({ theme }) => theme.colors.variants.primary.base}; - } - - option { - color: initial; - } - } -` - -export default SettingSelect diff --git a/src/shared/components/organisms/Settings/atoms/SettingTextarea.tsx b/src/shared/components/organisms/Settings/atoms/SettingTextarea.tsx deleted file mode 100644 index 51c5c89f5c..0000000000 --- a/src/shared/components/organisms/Settings/atoms/SettingTextarea.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import styled from '../../../../lib/styled' - -const SettingTextarea = styled.textarea` - flex-grow: 1; - flex-shrink: 1; - width: 100%; - height: 200px; - max-width: 400px; - padding: ${({ theme }) => theme.sizes.spaces.xsm}px - ${({ theme }) => theme.sizes.spaces.sm}px; - background-color: ${({ theme }) => theme.colors.background.primary}; - border: 1px solid ${({ theme }) => theme.colors.border.main}; - border-radius: 4px; - color: ${({ theme }) => theme.colors.text.primary}; - resize: none; - - &:focus { - border-color: ${({ theme }) => theme.colors.variants.primary.base}; - } - &::placeholder { - color: ${({ theme }) => theme.colors.text.subtle}; - } -` - -export default SettingTextarea From eddb425917b717294362928b677ea021f584a474 Mon Sep 17 00:00:00 2001 From: davy-c Date: Tue, 18 May 2021 11:48:15 +0900 Subject: [PATCH 70/91] rename --- src/cloud/components/organisms/settings/UserPreferencesForm.tsx | 2 +- src/cloud/lib/hooks/useCloudUI.tsx | 2 +- src/shared/components/molecules/Form/index.tsx | 2 +- .../molecules/Form/{layouts => templates}/FormRow.tsx | 0 src/shared/components/organisms/EmojiInputForm.tsx | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename src/shared/components/molecules/Form/{layouts => templates}/FormRow.tsx (100%) diff --git a/src/cloud/components/organisms/settings/UserPreferencesForm.tsx b/src/cloud/components/organisms/settings/UserPreferencesForm.tsx index 1ae521e317..21a34a76c2 100644 --- a/src/cloud/components/organisms/settings/UserPreferencesForm.tsx +++ b/src/cloud/components/organisms/settings/UserPreferencesForm.tsx @@ -17,7 +17,7 @@ import FormSelect, { FormSelectOption, SimpleFormSelect, } from '../../../../shared/components/molecules/Form/atoms/FormSelect' -import FormRow from '../../../../shared/components/molecules/Form/layouts/FormRow' +import FormRow from '../../../../shared/components/molecules/Form/templates/FormRow' const UserPreferencesForm = () => { const { settings, setSettings } = useSettings() diff --git a/src/cloud/lib/hooks/useCloudUI.tsx b/src/cloud/lib/hooks/useCloudUI.tsx index b53b03b4ea..e5b003692b 100644 --- a/src/cloud/lib/hooks/useCloudUI.tsx +++ b/src/cloud/lib/hooks/useCloudUI.tsx @@ -1,6 +1,6 @@ import { mdiFileDocumentOutline, mdiFolderOutline } from '@mdi/js' import React, { useCallback } from 'react' -import { FormRowProps } from '../../../shared/components/molecules/Form/layouts/FormRow' +import { FormRowProps } from '../../../shared/components/molecules/Form/templates/FormRow' import EmojiInputForm from '../../../shared/components/organisms/EmojiInputForm' import { DialogIconTypes, useDialog } from '../../../shared/lib/stores/dialog' import { useModal } from '../../../shared/lib/stores/modal' diff --git a/src/shared/components/molecules/Form/index.tsx b/src/shared/components/molecules/Form/index.tsx index ed5efd8c41..f6dcd32415 100644 --- a/src/shared/components/molecules/Form/index.tsx +++ b/src/shared/components/molecules/Form/index.tsx @@ -3,7 +3,7 @@ import styled from '../../../lib/styled' import { LoadingButton } from '../../atoms/Button' import cc from 'classcat' import { AppComponent } from '../../../lib/types' -import FormRow, { FormRowProps, FormRowButtonProps } from './layouts/FormRow' +import FormRow, { FormRowProps, FormRowButtonProps } from './templates/FormRow' interface FormProps { rows: FormRowProps[] diff --git a/src/shared/components/molecules/Form/layouts/FormRow.tsx b/src/shared/components/molecules/Form/templates/FormRow.tsx similarity index 100% rename from src/shared/components/molecules/Form/layouts/FormRow.tsx rename to src/shared/components/molecules/Form/templates/FormRow.tsx diff --git a/src/shared/components/organisms/EmojiInputForm.tsx b/src/shared/components/organisms/EmojiInputForm.tsx index f28ddf49e0..b1921008f4 100644 --- a/src/shared/components/organisms/EmojiInputForm.tsx +++ b/src/shared/components/organisms/EmojiInputForm.tsx @@ -2,7 +2,7 @@ import React, { useRef, useState } from 'react' import { useEffectOnce } from 'react-use' import { ButtonProps } from '../atoms/Button' import Form from '../molecules/Form' -import { FormRowProps } from '../molecules/Form/layouts/FormRow' +import { FormRowProps } from '../molecules/Form/templates/FormRow' interface EmojiInputFormProps { prevRows?: FormRowProps[] From a554f4c40335c6039c457a7cd7c8ab2361fabc3a Mon Sep 17 00:00:00 2001 From: davy-c Date: Tue, 18 May 2021 12:07:45 +0900 Subject: [PATCH 71/91] settings token --- src/cloud/components/Application.tsx | 2 +- .../components/organisms/settings/ApiTab.tsx | 48 +++++++++++++++++- .../molecules/Form/templates/FormRow.tsx | 20 +++++++- .../Settings/molecules/SettingTokenCreate.tsx | 50 ------------------- 4 files changed, 66 insertions(+), 54 deletions(-) delete mode 100644 src/shared/components/organisms/Settings/molecules/SettingTokenCreate.tsx diff --git a/src/cloud/components/Application.tsx b/src/cloud/components/Application.tsx index a72680d6dd..507d16caad 100644 --- a/src/cloud/components/Application.tsx +++ b/src/cloud/components/Application.tsx @@ -220,7 +220,7 @@ const Application = ({ openSettingsTab('teamUpgrade') } - openSettingsTab('personalInfo') + openSettingsTab('api') }) useEffect(() => { diff --git a/src/cloud/components/organisms/settings/ApiTab.tsx b/src/cloud/components/organisms/settings/ApiTab.tsx index 08b36da073..20a8023a2e 100644 --- a/src/cloud/components/organisms/settings/ApiTab.tsx +++ b/src/cloud/components/organisms/settings/ApiTab.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useMemo, useState } from 'react' +import React, { ChangeEvent, useCallback, useMemo, useState } from 'react' import styled from '../../../lib/styled' import Spinner from '../../atoms/CustomSpinner' import { useApiTokens, withApiTokens } from '../../../lib/stores/apiTokens' @@ -8,9 +8,9 @@ import Icon from '../../atoms/IconMdi' import { mdiOpenInNew } from '@mdi/js' import SettingTabContent from '../../../../shared/components/organisms/Settings/atoms/SettingTabContent' import Button from '../../../../shared/components/atoms/Button' -import SettingTokenCreate from '../../../../shared/components/organisms/Settings/molecules/SettingTokenCreate' import Flexbox from '../../../../shared/components/atoms/Flexbox' import { ExternalLink } from '../../../../shared/components/atoms/Link' +import Form from '../../../../shared/components/molecules/Form' const ApiTab = () => { const { team } = usePage() @@ -98,6 +98,46 @@ const ApiTab = () => { ) } +const SettingTokenCreate = ({ + onCreate, +}: { + onCreate: (val: string) => void +}) => { + const [name, setName] = useState('') + + const create = useCallback(() => { + onCreate(name) + }, [name, onCreate]) + + return ( +
+

Create a new token

+ ) => { + setName(e.target.value) + }, + }, + }, + ], + }, + ]} + /> +
+ ) +} + const StyledServiceList = styled.ul` background-color: ${({ theme }) => theme.baseBackgroundColor}; padding-left: 0; @@ -111,6 +151,10 @@ const StyledServiceListItem = styled.li` justify-content: space-between; padding: ${({ theme }) => theme.space.small}px; + .setting__token__form { + width: 100%; + } + + li { border-top: 1px solid ${({ theme }) => theme.baseBorderColor}; } diff --git a/src/shared/components/molecules/Form/templates/FormRow.tsx b/src/shared/components/molecules/Form/templates/FormRow.tsx index 54d5cba702..3e983bcd32 100644 --- a/src/shared/components/molecules/Form/templates/FormRow.tsx +++ b/src/shared/components/molecules/Form/templates/FormRow.tsx @@ -21,6 +21,7 @@ export type FormRowButtonProps = ButtonProps & { export type FormRowProps = { layout?: 'column' | 'split' title?: React.ReactNode + required?: boolean description?: React.ReactNode items?: ( | { @@ -42,7 +43,13 @@ export type FormRowProps = { const FormRow: AppComponent<{ row: FormRowProps }> = ({ row, className }) => { return ( - + {row.title != null &&
{row.title}
} {row.items != null && (
@@ -86,7 +93,18 @@ const Container = styled.div` color: ${({ theme }) => theme.colors.text.subtle}; } + &.form__row--required .form__row__title::after { + content: '*'; + color: ${({ theme }) => theme.colors.variants.danger.base}; + filter: brightness(160%); + position: absolute; + right: -5px; + top: -3px; + } + .form__row__title { + display: inline-block; + position: relative; filter: brightness(80%); margin-bottom: ${({ theme }) => theme.sizes.spaces.sm}px; } diff --git a/src/shared/components/organisms/Settings/molecules/SettingTokenCreate.tsx b/src/shared/components/organisms/Settings/molecules/SettingTokenCreate.tsx deleted file mode 100644 index 666b07f918..0000000000 --- a/src/shared/components/organisms/Settings/molecules/SettingTokenCreate.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React, { useState, useCallback, ChangeEvent } from 'react' -import styled from '../../../../lib/styled' -import Button from '../../../atoms/Button' -import Flexbox from '../../../atoms/Flexbox' -import SettingInput from '../atoms/SettingInput' - -interface SettingTokenCreateProps { - onCreate: (name: string) => void -} - -const SettingTokenCreate = ({ onCreate }: SettingTokenCreateProps) => { - const [name, setName] = useState('') - - const create = useCallback(() => { - onCreate(name) - }, [name, onCreate]) - - return ( - -

Create a token

-
- ) => - setName(e.target.value) - } - > -
- - {name.length === 0 &&

Enter a name

} - -
-
- ) -} - -export default SettingTokenCreate - -const Container = styled.div` - width: 100%; - - .text--warning { - margin-top: 0; - margin-right: ${({ theme }) => theme.sizes.spaces.df}px; - color: ${({ theme }) => theme.colors.variants.warning.base}; - } -` From 7443aa89690837600ab1a599eae547a4ef1d4426 Mon Sep 17 00:00:00 2001 From: davy-c Date: Tue, 18 May 2021 12:25:04 +0900 Subject: [PATCH 72/91] teamsublimit --- .../organisms/settings/TeamSubLimit.tsx | 76 +++++++++++++++++-- .../Settings/atoms/SettingTeamSubLimit.tsx | 69 ----------------- 2 files changed, 71 insertions(+), 74 deletions(-) delete mode 100644 src/shared/components/organisms/Settings/atoms/SettingTeamSubLimit.tsx diff --git a/src/cloud/components/organisms/settings/TeamSubLimit.tsx b/src/cloud/components/organisms/settings/TeamSubLimit.tsx index 7e0a8b288a..8ddaa5b167 100644 --- a/src/cloud/components/organisms/settings/TeamSubLimit.tsx +++ b/src/cloud/components/organisms/settings/TeamSubLimit.tsx @@ -2,7 +2,7 @@ import React from 'react' import cc from 'classcat' import { usePage } from '../../../lib/stores/pageStore' import { useSettings } from '../../../lib/stores/settings' -import SettingTeamSubLimit from '../../../../shared/components/organisms/Settings/atoms/SettingTeamSubLimit' +import styled from '../../../../shared/lib/styled' const TeamSubLimit = () => { const { subscription, team, currentSubInfo } = usePage() @@ -18,7 +18,7 @@ const TeamSubLimit = () => { if (currentSubInfo.trialing) { return ( - + { You can upgrade at anytime during your trial.

-
+ ) } return ( - + {

)}
-
+ ) } +const Container = styled.nav` + width: 100%; + margin-top: ${({ theme }) => theme.sizes.spaces.l}px; + + h6 { + margin: 0; + color: ${({ theme }) => theme.colors.variants.primary.base}; + font-size: ${({ theme }) => theme.sizes.fonts.df}px; + } + + p { + margin: ${({ theme }) => theme.sizes.spaces.sm}px 0; + color: ${({ theme }) => theme.colors.text.subtle}; + font-size: ${({ theme }) => theme.sizes.fonts.sm}px; + } + + .upgrade-link { + display: block; + margin-top: ${({ theme }) => theme.sizes.spaces.sm}px; + margin-bottom: ${({ theme }) => theme.sizes.spaces.xsm}px; + padding: ${({ theme }) => theme.sizes.spaces.df}px; + cursor: pointer; + text-decoration: none; + + &:hover, + &:focus { + background-color: ${({ theme }) => theme.colors.background.tertiary}; + } + } + + .note-limit { + font-size: ${({ theme }) => theme.sizes.fonts.sm}px; + } + + .progress-sm { + display: block; + position: relative; + width: 100%; + height: 3px; + background-color: ${({ theme }) => theme.colors.background.quaternary}; + border-radius: 0.25rem; + font-size: 0.75rem; + overflow: hidden; + text-align: center; + } + + .progress-bar { + flex-direction: column; + justify-content: center; + height: 3px; + max-width: 100%; + background-color: ${({ theme }) => theme.colors.background.primary}; + text-align: center; + white-space: nowrap; + transition: width 0.6s ease; + + &.over-limit { + background-color: ${({ theme }) => theme.colors.variants.danger.base}; + } + } + + .text-danger { + color: ${({ theme }) => theme.colors.variants.danger.base}; + } +` + export default TeamSubLimit diff --git a/src/shared/components/organisms/Settings/atoms/SettingTeamSubLimit.tsx b/src/shared/components/organisms/Settings/atoms/SettingTeamSubLimit.tsx deleted file mode 100644 index 723df18239..0000000000 --- a/src/shared/components/organisms/Settings/atoms/SettingTeamSubLimit.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import styled from '../../../../lib/styled' - -const SettingTeamSubLimit = styled.nav` - width: 100%; - margin-top: ${({ theme }) => theme.sizes.spaces.l}px; - - h6 { - margin: 0; - color: ${({ theme }) => theme.colors.variants.primary.base}; - font-size: ${({ theme }) => theme.sizes.fonts.df}px; - } - - p { - margin: ${({ theme }) => theme.sizes.spaces.sm}px 0; - color: ${({ theme }) => theme.colors.text.subtle}; - font-size: ${({ theme }) => theme.sizes.fonts.sm}px; - } - - .upgrade-link { - display: block; - margin-top: ${({ theme }) => theme.sizes.spaces.sm}px; - margin-bottom: ${({ theme }) => theme.sizes.spaces.xsm}px; - padding: ${({ theme }) => theme.sizes.spaces.df}px; - cursor: pointer; - text-decoration: none; - - &:hover, - &:focus { - background-color: ${({ theme }) => theme.colors.background.tertiary}; - } - } - - .note-limit { - font-size: ${({ theme }) => theme.sizes.fonts.sm}px; - } - - .progress-sm { - display: block; - position: relative; - width: 100%; - height: 3px; - background-color: ${({ theme }) => theme.colors.background.quaternary}; - border-radius: 0.25rem; - font-size: 0.75rem; - overflow: hidden; - text-align: center; - } - - .progress-bar { - flex-direction: column; - justify-content: center; - height: 3px; - max-width: 100%; - background-color: ${({ theme }) => theme.colors.background.primary}; - text-align: center; - white-space: nowrap; - transition: width 0.6s ease; - - &.over-limit { - background-color: ${({ theme }) => theme.colors.variants.danger.base}; - } - } - - .text-danger { - color: ${({ theme }) => theme.colors.variants.danger.base}; - } -` - -export default SettingTeamSubLimit From 6624a2d661b58ea61e4f671997bbab4f476103aa Mon Sep 17 00:00:00 2001 From: davy-c Date: Tue, 18 May 2021 12:29:06 +0900 Subject: [PATCH 73/91] single item --- src/cloud/components/Application.tsx | 2 +- .../components/molecules/Form/templates/FormRow.tsx | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/cloud/components/Application.tsx b/src/cloud/components/Application.tsx index 507d16caad..d9fd6bedb5 100644 --- a/src/cloud/components/Application.tsx +++ b/src/cloud/components/Application.tsx @@ -220,7 +220,7 @@ const Application = ({ openSettingsTab('teamUpgrade') } - openSettingsTab('api') + openSettingsTab('teamInfo') }) useEffect(() => { diff --git a/src/shared/components/molecules/Form/templates/FormRow.tsx b/src/shared/components/molecules/Form/templates/FormRow.tsx index 3e983bcd32..ca00e6c193 100644 --- a/src/shared/components/molecules/Form/templates/FormRow.tsx +++ b/src/shared/components/molecules/Form/templates/FormRow.tsx @@ -52,7 +52,12 @@ const FormRow: AppComponent<{ row: FormRowProps }> = ({ row, className }) => { > {row.title != null &&
{row.title}
} {row.items != null && ( -
+
{row.items.map((item, k) => (
Date: Tue, 18 May 2021 13:11:04 +0900 Subject: [PATCH 74/91] fix tab contnet --- .../components/molecules/SettingsTeamForm.tsx | 27 ++------------- .../organisms/settings/MembersTab.tsx | 23 ++++++++----- .../organisms/settings/SettingsComponent.tsx | 24 +++++++++----- .../organisms/settings/TeamInfoTab.tsx | 2 +- .../Settings/atoms/SettingCloseButton.tsx | 33 ------------------- .../Settings/atoms/SettingSidenav.tsx | 11 ------- .../Settings/atoms/SettingTabContent.tsx | 14 ++++++++ .../components/organisms/Settings/index.tsx | 6 ++++ 8 files changed, 54 insertions(+), 86 deletions(-) delete mode 100644 src/shared/components/organisms/Settings/atoms/SettingCloseButton.tsx delete mode 100644 src/shared/components/organisms/Settings/atoms/SettingSidenav.tsx diff --git a/src/cloud/components/molecules/SettingsTeamForm.tsx b/src/cloud/components/molecules/SettingsTeamForm.tsx index 61a0b61116..2c7ccda7a7 100644 --- a/src/cloud/components/molecules/SettingsTeamForm.tsx +++ b/src/cloud/components/molecules/SettingsTeamForm.tsx @@ -1,4 +1,4 @@ -import { mdiArrowLeft, mdiDomain } from '@mdi/js' +import { mdiDomain } from '@mdi/js' import React, { useCallback, useMemo, useState } from 'react' import slugify from 'slugify' import { buildIconUrl } from '../../api/files' @@ -21,16 +21,9 @@ import SettingIconInputLabel from '../../../shared/components/organisms/Settings interface SettingsTeamFormProps { team: SerializedTeam teamConversion?: boolean - header?: boolean - onCancel?: () => void } -const SettingsTeamForm = ({ - team, - teamConversion, - header = true, - onCancel, -}: SettingsTeamFormProps) => { +const SettingsTeamForm = ({ team, teamConversion }: SettingsTeamFormProps) => { const [name, setName] = useState(!teamConversion ? team.name : '') const [domain, setDomain] = useState( !teamConversion ? team.domain : '' @@ -137,22 +130,6 @@ const SettingsTeamForm = ({ return ( - {header && ( - - {onCancel != null && ( - - )} -
Create a team account in order to invite your teammates
-
- )} diff --git a/src/cloud/components/organisms/settings/MembersTab.tsx b/src/cloud/components/organisms/settings/MembersTab.tsx index bbda4d0e60..392f8aec91 100644 --- a/src/cloud/components/organisms/settings/MembersTab.tsx +++ b/src/cloud/components/organisms/settings/MembersTab.tsx @@ -25,7 +25,12 @@ import { useRouter } from '../../../lib/router' import cc from 'classcat' import { useNav } from '../../../lib/stores/nav' import Icon from '../../atoms/Icon' -import { mdiArrowRight, mdiCardTextOutline, mdiChevronDown } from '@mdi/js' +import { + mdiArrowLeft, + mdiArrowRight, + mdiCardTextOutline, + mdiChevronDown, +} from '@mdi/js' import { deleteGuestDoc, getGuestsEmails } from '../../../api/guests' import { useSet } from 'react-use' import plur from 'plur' @@ -405,13 +410,15 @@ const MembersTab = () => { if (team.personal && showTeamPersonalForm) { return ( setShowTeamPersonalForm(false)} - teamConversion={true} - /> - } + title='Create team space' + description='Create a team space in order to invite your teammates' + backLink={{ + variant: 'icon', + iconPath: mdiArrowLeft, + iconSize: 20, + onClick: () => setShowTeamPersonalForm(false), + }} + body={} > ) } diff --git a/src/cloud/components/organisms/settings/SettingsComponent.tsx b/src/cloud/components/organisms/settings/SettingsComponent.tsx index c8f2a9218c..f39b16c049 100644 --- a/src/cloud/components/organisms/settings/SettingsComponent.tsx +++ b/src/cloud/components/organisms/settings/SettingsComponent.tsx @@ -26,10 +26,9 @@ import ApiTab from './ApiTab' import { PageStoreWithTeam } from '../../../interfaces/pageStore' import Settings from '../../../../shared/components/organisms/Settings' import SettingSidenavHeader from '../../../../shared/components/organisms/Settings/molecules/SettingSidenavHeader' -import SettingSidenav from '../../../../shared/components/organisms/Settings/atoms/SettingSidenav' import SettingTabButton from '../../../../shared/components/organisms/Settings/atoms/SettingTabButton' -import Icon from '../../../../shared/components/atoms/Icon' -import SettingCloseButton from '../../../../shared/components/organisms/Settings/atoms/SettingCloseButton' +import Button from '../../../../shared/components/atoms/Button' +import styled from '../../../../shared/lib/styled' const SettingsComponent = () => { const { t } = useTranslation() @@ -118,7 +117,7 @@ const SettingsComponent = () => { return ( + { content={
{content} - - - + iconPath={mdiClose} + iconSize={26} + />
} /> ) } +const SettingSidenav = styled.nav` + width: 250px; + margin-bottom: 0; + margin-right: 0; + padding: ${({ theme }) => theme.sizes.spaces.sm}px; + overflow: hidden auto; +` + export default SettingsComponent diff --git a/src/cloud/components/organisms/settings/TeamInfoTab.tsx b/src/cloud/components/organisms/settings/TeamInfoTab.tsx index 2a94962377..e7aeb9f3d9 100644 --- a/src/cloud/components/organisms/settings/TeamInfoTab.tsx +++ b/src/cloud/components/organisms/settings/TeamInfoTab.tsx @@ -52,7 +52,7 @@ const TeamInfoTab = () => { description={'Manage your space settings.'} body={
- +
} footer={adminContent} diff --git a/src/shared/components/organisms/Settings/atoms/SettingCloseButton.tsx b/src/shared/components/organisms/Settings/atoms/SettingCloseButton.tsx deleted file mode 100644 index 3926a47bd0..0000000000 --- a/src/shared/components/organisms/Settings/atoms/SettingCloseButton.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import styled from '../../../../lib/styled' - -const SettingCloseButton = styled.div` - position: absolute; - top: ${({ theme }) => theme.sizes.spaces.md}px; - right: ${({ theme }) => theme.sizes.spaces.md}px; - width: 26px; - height: 26px; - background-color: transparent; - border: none; - cursor: pointer; - color: ${({ theme }) => theme.colors.icon.default}; - transition: 200ms color; - - &:hover, - &:focus { - color: ${({ theme }) => theme.colors.icon.hover} !important; - } - - &:active { - color: ${({ theme }) => theme.colors.icon.active} !important; - } - - &:disabled { - &:hover, - &:focus, - &:active { - cursor: not-allowed; - } - } -` - -export default SettingCloseButton diff --git a/src/shared/components/organisms/Settings/atoms/SettingSidenav.tsx b/src/shared/components/organisms/Settings/atoms/SettingSidenav.tsx deleted file mode 100644 index 2105ef3f4b..0000000000 --- a/src/shared/components/organisms/Settings/atoms/SettingSidenav.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import styled from '../../../../lib/styled' - -const SettingSidenav = styled.nav` - width: 250px; - margin-bottom: 0; - margin-right: 0; - padding: ${({ theme }) => theme.sizes.spaces.sm}px; - overflow: hidden auto; -` - -export default SettingSidenav diff --git a/src/shared/components/organisms/Settings/atoms/SettingTabContent.tsx b/src/shared/components/organisms/Settings/atoms/SettingTabContent.tsx index b51950e0c0..45b6a7c5eb 100644 --- a/src/shared/components/organisms/Settings/atoms/SettingTabContent.tsx +++ b/src/shared/components/organisms/Settings/atoms/SettingTabContent.tsx @@ -1,11 +1,13 @@ import React from 'react' import styled from '../../../../lib/styled' +import Button, { ButtonProps } from '../../../atoms/Button' interface SettingTabContentProps { title?: React.ReactNode description?: React.ReactNode body: React.ReactNode footer?: React.ReactNode + backLink?: ButtonProps } const SettingTabContent = ({ @@ -13,10 +15,16 @@ const SettingTabContent = ({ description, body, footer, + backLink, }: SettingTabContentProps) => (
+ {backLink != null && ( + + )}

{title}

@@ -36,6 +44,12 @@ const Container = styled.div` display: block; width: 100%; padding-top: ${({ theme }) => theme.sizes.spaces.xl}px; + position: relative; + + .settings__tab__content__backlink { + position: absolute; + top: ${({ theme }) => theme.sizes.spaces.md}px; + } .setting__tab__content__footer { margin-top: ${({ theme }) => theme.sizes.spaces.l}px; diff --git a/src/shared/components/organisms/Settings/index.tsx b/src/shared/components/organisms/Settings/index.tsx index d548d3a365..291b0cb8bd 100644 --- a/src/shared/components/organisms/Settings/index.tsx +++ b/src/shared/components/organisms/Settings/index.tsx @@ -60,6 +60,12 @@ const Container = styled.div` width: 100%; height: auto; } + + .settings__close-btn { + position: absolute; + top: ${({ theme }) => theme.sizes.spaces.md}px; + right: ${({ theme }) => theme.sizes.spaces.md}px; + } ` export default Settings From 70780e0ce67ce9430e4e4edec946e84c039d2367 Mon Sep 17 00:00:00 2001 From: davy-c Date: Tue, 18 May 2021 13:11:24 +0900 Subject: [PATCH 75/91] fix writing --- src/cloud/components/organisms/settings/MembersTab.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cloud/components/organisms/settings/MembersTab.tsx b/src/cloud/components/organisms/settings/MembersTab.tsx index 392f8aec91..1d8928e35f 100644 --- a/src/cloud/components/organisms/settings/MembersTab.tsx +++ b/src/cloud/components/organisms/settings/MembersTab.tsx @@ -419,7 +419,7 @@ const MembersTab = () => { onClick: () => setShowTeamPersonalForm(false), }} body={} - > + /> ) } From 079e50fc66213d80d6220c2498649f058819dcc2 Mon Sep 17 00:00:00 2001 From: davy-c Date: Tue, 18 May 2021 13:37:40 +0900 Subject: [PATCH 76/91] remove setting input --- src/cloud/components/Application.tsx | 2 - .../molecules/GuestInvitesSection.tsx | 51 ++-- .../components/molecules/SettingsTeamForm.tsx | 219 ++++++------------ src/shared/components/atoms/Icon.tsx | 2 +- .../molecules/Form/atoms/FormImage.tsx | 7 +- .../Settings/atoms/SettingIconInputLabel.tsx | 39 ---- .../organisms/Settings/atoms/SettingInput.tsx | 50 ---- 7 files changed, 110 insertions(+), 260 deletions(-) delete mode 100644 src/shared/components/organisms/Settings/atoms/SettingIconInputLabel.tsx delete mode 100644 src/shared/components/organisms/Settings/atoms/SettingInput.tsx diff --git a/src/cloud/components/Application.tsx b/src/cloud/components/Application.tsx index d9fd6bedb5..1244c4c27b 100644 --- a/src/cloud/components/Application.tsx +++ b/src/cloud/components/Application.tsx @@ -219,8 +219,6 @@ const Application = ({ if (query.settings === 'upgrade') { openSettingsTab('teamUpgrade') } - - openSettingsTab('teamInfo') }) useEffect(() => { diff --git a/src/cloud/components/molecules/GuestInvitesSection.tsx b/src/cloud/components/molecules/GuestInvitesSection.tsx index fe52ed7a22..30f7c3197b 100644 --- a/src/cloud/components/molecules/GuestInvitesSection.tsx +++ b/src/cloud/components/molecules/GuestInvitesSection.tsx @@ -1,14 +1,9 @@ import React, { useState, useCallback, useMemo } from 'react' -import { - SectionRow, - SectionList, - SectionListItem, -} from '../organisms/settings/styled' +import { SectionList, SectionListItem } from '../organisms/settings/styled' import { useDialog, DialogIconTypes } from '../../../shared/lib/stores/dialog' import { useEffectOnce, useSet } from 'react-use' import { Spinner } from '../atoms/Spinner' import ErrorBlock from '../atoms/ErrorBlock' -import CustomButton from '../atoms/buttons/CustomButton' import Flexbox from '../atoms/Flexbox' import { SerializedGuestInvite } from '../../interfaces/db/guest' import { @@ -20,7 +15,7 @@ import Button from '../atoms/Button' import UserIcon from '../atoms/UserIcon' import { deleteGuestDoc } from '../../api/guests' import { usePage } from '../../lib/stores/pageStore' -import SettingInput from '../../../shared/components/organisms/Settings/atoms/SettingInput' +import Form from '../../../shared/components/molecules/Form' interface GuestInvitesSectionProps { docId: string @@ -189,22 +184,32 @@ const GuestInvitesSection = ({ teamId, docId }: GuestInvitesSectionProps) => {

Invite with Email

{has('fetch') && } - - - - - Send - - - +
{pendingInvites.map((invite) => ( diff --git a/src/cloud/components/molecules/SettingsTeamForm.tsx b/src/cloud/components/molecules/SettingsTeamForm.tsx index 2c7ccda7a7..299558e31b 100644 --- a/src/cloud/components/molecules/SettingsTeamForm.tsx +++ b/src/cloud/components/molecules/SettingsTeamForm.tsx @@ -7,16 +7,12 @@ import { SerializedTeam } from '../../interfaces/db/team' import { useElectron } from '../../lib/stores/electron' import { useGlobalData } from '../../lib/stores/globalData' import { usePage } from '../../lib/stores/pageStore' -import styled from '../../lib/styled' -import Flexbox from '../atoms/Flexbox' -import Icon from '../atoms/Icon' -import { Spinner } from '../atoms/Spinner' import { useRouter } from '../../lib/router' import { getTeamURL } from '../../lib/utils/patterns' import { useToast } from '../../../shared/lib/stores/toast' -import Button from '../../../shared/components/atoms/Button' -import SettingInput from '../../../shared/components/organisms/Settings/atoms/SettingInput' -import SettingIconInputLabel from '../../../shared/components/organisms/Settings/atoms/SettingIconInputLabel' +import Form from '../../../shared/components/molecules/Form' +import FormRow from '../../../shared/components/molecules/Form/templates/FormRow' +import styled from '../../../shared/lib/styled' interface SettingsTeamFormProps { team: SerializedTeam @@ -53,20 +49,10 @@ const SettingsTeamForm = ({ team, teamConversion }: SettingsTeamFormProps) => { ) }, [domain]) - const changeHandler: React.ChangeEventHandler = useCallback( - (event) => { - if ( - event.target != null && - event.target.files != null && - event.target.files.length > 0 - ) { - const file = event.target.files[0] - setIconFile(file) - setFileUrl(URL.createObjectURL(file)) - } - }, - [] - ) + const changeHandler = useCallback((file: File) => { + setIconFile(file) + setFileUrl(URL.createObjectURL(file)) + }, []) const updateHandler = useCallback( async (event: React.FormEvent) => { @@ -128,132 +114,79 @@ const SettingsTeamForm = ({ team, teamConversion }: SettingsTeamFormProps) => { const label = teamConversion ? 'Team' : 'Space' return ( - - - - - -
- {fileUrl != null ? ( - - ) : ( - - )} - - - {fileUrl == null ? 'Add a photo' : 'Change your photo'} + setName(ev.target.value), + }, + }, + ], + }, + ]} + submitButton={{ label: teamConversion ? 'Create' : 'Update' }} + > + {teamConversion && team.personal && ( + setDomain(ev.target.value), + }, + }, + ], + description: ( + + + Your url will look like this: + {slugDomain} - - -
-
-
- - - setName(ev.target.value)} - /> - - {teamConversion && team.personal && ( - - - - setDomain(ev.target.value)} - /> - - Your url will look like this: - {slugDomain} - - - Caution: You can't change it after creating your team. - - - - )} - - - - -
+ + Caution: You can't change it after creating your team. + + + ), + }} + /> + )} + ) } -export default SettingsTeamForm - -const Container = styled.div` - display: flex; - flex-direction: column; - width: 100%; - max-width: 700px; - .profile__icon { - width: 50px; - height: 50px; - color: ${({ theme }) => theme.secondaryBorderColor}; - } - .profile__row { - display: flex; - align-items: center; - margin-bottom: ${({ theme }) => theme.space.small}px; - } - #profile { - display: none; - } - .profile__pic { - margin: auto; - object-fit: cover; - width: 50px; - height: 50px; - background: ${({ theme }) => theme.secondaryBackgroundColor}; - border: 1px solid ${({ theme }) => theme.secondaryBorderColor}; - border-radius: 100%; +const Description = styled.div` + .description + .description { + margin-top: ${({ theme }) => theme.sizes.spaces.md}px; } - .form__header { - margin-bottom: ${({ theme }) => theme.space.medium}px; - button { - margin-right: ${({ theme }) => theme.space.small}px; - } - h5 { - margin: 0; - } - } - .form__row { - margin: ${({ theme }) => theme.space.small}px 0; - > label { - display: block; - width: 200px; - flex: 0 0 auto; - margin: 0; - color: ${({ theme }) => theme.subtleTextColor}; - } - .description { - font-size: ${({ theme }) => theme.fontSizes.xsmall}px; - margin-top: ${({ theme }) => theme.space.xsmall}px; - color: ${({ theme }) => theme.subtleTextColor}; - line-height: ${({ theme }) => theme.fontSizes.xsmall}px; - .underlined { - font-weight: 500; - padding-left: ${({ theme }) => theme.space.xxsmall}px; - text-decoration: underline; - } - } + + .underlined { + font-weight: 500; + padding-left: ${({ theme }) => theme.sizes.spaces.sm}px; + text-decoration: underline; } ` + +export default SettingsTeamForm diff --git a/src/shared/components/atoms/Icon.tsx b/src/shared/components/atoms/Icon.tsx index 48ce3852ba..b1d6b20dd5 100644 --- a/src/shared/components/atoms/Icon.tsx +++ b/src/shared/components/atoms/Icon.tsx @@ -2,7 +2,7 @@ import React from 'react' import { Icon as MdiIcon } from '@mdi/react' import cc from 'classcat' -export type IconSize = 16 | 20 | 26 | 34 +export type IconSize = 16 | 20 | 26 | 34 | 50 interface IconProps { path: string diff --git a/src/shared/components/molecules/Form/atoms/FormImage.tsx b/src/shared/components/molecules/Form/atoms/FormImage.tsx index 43ddb6b0d0..de95c2ca67 100644 --- a/src/shared/components/molecules/Form/atoms/FormImage.tsx +++ b/src/shared/components/molecules/Form/atoms/FormImage.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useState } from 'react' import styled from '../../../../lib/styled' import { AppComponent } from '../../../../lib/types' import cc from 'classcat' -import Icon from '@mdi/react' +import Icon from '../../../atoms/Icon' export interface FormImageProps { onChange?: (file: File) => void @@ -64,6 +64,10 @@ const Container = styled.div` .form__image__wrapper { width: 90px; height: 90px; + display: flex; + align-items: center; + justify-content: center; + color: ${({ theme }) => theme.colors.text.subtle}; } .form__image--img { @@ -77,7 +81,6 @@ const Container = styled.div` .form__image__label { position: relative; - cursor: pointer; margin-left: ${({ theme }) => theme.sizes.spaces.md}px; & > span { diff --git a/src/shared/components/organisms/Settings/atoms/SettingIconInputLabel.tsx b/src/shared/components/organisms/Settings/atoms/SettingIconInputLabel.tsx deleted file mode 100644 index 23913cd809..0000000000 --- a/src/shared/components/organisms/Settings/atoms/SettingIconInputLabel.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import styled from '../../../../lib/styled' - -const SettingIconInputLabel = styled.label` - position: relative; - cursor: pointer; - margin-left: ${({ theme }) => theme.sizes.spaces.md}px; - - & > span { - display: flex; - align-items: center; - height: 32px; - padding: 0 ${({ theme }) => theme.sizes.spaces.md}px; - background-color: ${({ theme }) => theme.colors.variants.secondary.base}; - border-radius: 4px; - color: ${({ theme }) => theme.colors.variants.secondary.text}; - font-size: ${({ theme }) => theme.sizes.fonts.df}px; - transition: 200ms background-color; - - &.focus { - filter: brightness(103%); - } - &:hover { - filter: brightness(106%); - } - &:active, - &.button__state--active { - filter: brightness(112%); - } - } - - & > input[type='file'] { - position: absolute; - opacity: 0; - height: 0px; - width: 0px; - } -` - -export default SettingIconInputLabel diff --git a/src/shared/components/organisms/Settings/atoms/SettingInput.tsx b/src/shared/components/organisms/Settings/atoms/SettingInput.tsx deleted file mode 100644 index 010ff48254..0000000000 --- a/src/shared/components/organisms/Settings/atoms/SettingInput.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React from 'react' -import styled from '../../../../lib/styled' - -interface SettingInputProps { - label?: string - value?: string - placeholder?: string - type?: string - onChange?: React.ChangeEventHandler -} - -const SettingInput = ({ label, value, onChange }: SettingInputProps) => ( - - - - -) - -const Container = styled.div` - label { - display: block; - margin-bottom: ${({ theme }) => theme.sizes.spaces.sm}px; - color: ${({ theme }) => theme.colors.text.secondary}; - font-size: ${({ theme }) => theme.sizes.fonts.df}px; - } - - input { - flex-grow: 1; - flex-shrink: 1; - width: 100%; - height: 40px; - min-width: 300px; - max-width: 450px; - padding: ${({ theme }) => theme.sizes.spaces.xsm}px - ${({ theme }) => theme.sizes.spaces.sm}px; - background-color: ${({ theme }) => theme.colors.background.primary}; - border: 1px solid ${({ theme }) => theme.colors.border.main}; - border-radius: 4px; - color: ${({ theme }) => theme.colors.text.primary}; - - &:focus { - border-color: ${({ theme }) => theme.colors.variants.primary.base}; - } - &::placeholder { - color: ${({ theme }) => theme.colors.text.subtle}; - } - } -` - -export default SettingInput From 89db05c2889ec4424561ce98f28ad1894e012f90 Mon Sep 17 00:00:00 2001 From: davy-c Date: Wed, 19 May 2021 06:24:46 +0900 Subject: [PATCH 77/91] fix breakline --- src/cloud/components/molecules/SettingsTeamForm.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cloud/components/molecules/SettingsTeamForm.tsx b/src/cloud/components/molecules/SettingsTeamForm.tsx index 299558e31b..f1bf504410 100644 --- a/src/cloud/components/molecules/SettingsTeamForm.tsx +++ b/src/cloud/components/molecules/SettingsTeamForm.tsx @@ -178,6 +178,9 @@ const SettingsTeamForm = ({ team, teamConversion }: SettingsTeamFormProps) => { } const Description = styled.div` + .description { + display: block; + } .description + .description { margin-top: ${({ theme }) => theme.sizes.spaces.md}px; } From 790047c6f9fecb5934682905a744cb8166635964 Mon Sep 17 00:00:00 2001 From: davy-c Date: Wed, 19 May 2021 06:25:37 +0900 Subject: [PATCH 78/91] fix markup --- src/cloud/components/molecules/SettingsTeamForm.tsx | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/cloud/components/molecules/SettingsTeamForm.tsx b/src/cloud/components/molecules/SettingsTeamForm.tsx index f1bf504410..cda0f38df0 100644 --- a/src/cloud/components/molecules/SettingsTeamForm.tsx +++ b/src/cloud/components/molecules/SettingsTeamForm.tsx @@ -161,13 +161,13 @@ const SettingsTeamForm = ({ team, teamConversion }: SettingsTeamFormProps) => { ], description: ( - +
Your url will look like this: {slugDomain} - - +
+
Caution: You can't change it after creating your team. - +
), }} @@ -178,9 +178,6 @@ const SettingsTeamForm = ({ team, teamConversion }: SettingsTeamFormProps) => { } const Description = styled.div` - .description { - display: block; - } .description + .description { margin-top: ${({ theme }) => theme.sizes.spaces.md}px; } From ebcdd9725192b76e157e07c17d37d145e5b60ad7 Mon Sep 17 00:00:00 2001 From: davy-c Date: Wed, 19 May 2021 07:26:58 +0900 Subject: [PATCH 79/91] helper in settings --- src/cloud/components/molecules/Helper.tsx | 5 +- .../organisms/settings/SettingsComponent.tsx | 101 ++++++++++++------ src/cloud/lib/intercom/index.ts | 4 +- src/cloud/lib/stores/settings/store.ts | 1 + .../PreferencesModal/PreferencesModal.tsx | 20 ++-- .../molecules/Form/atoms/FormEmoji.tsx | 2 +- .../molecules/Form/atoms/FormInput.tsx | 2 +- .../molecules/Form/atoms/FormSelect.tsx | 2 +- .../molecules/Form/templates/FormRow.tsx | 7 +- .../Settings/atoms/SettingNavItem.tsx | 91 ++++++++++++++++ .../Settings/atoms/SettingTabButton.tsx | 57 ---------- .../Settings/molecules/SettingSidenav.tsx | 42 ++++++++ 12 files changed, 227 insertions(+), 107 deletions(-) create mode 100644 src/shared/components/organisms/Settings/atoms/SettingNavItem.tsx delete mode 100644 src/shared/components/organisms/Settings/atoms/SettingTabButton.tsx create mode 100644 src/shared/components/organisms/Settings/molecules/SettingSidenav.tsx diff --git a/src/cloud/components/molecules/Helper.tsx b/src/cloud/components/molecules/Helper.tsx index d0ea41c3e6..5393a49e5e 100644 --- a/src/cloud/components/molecules/Helper.tsx +++ b/src/cloud/components/molecules/Helper.tsx @@ -83,7 +83,10 @@ const Helper = () => { Help & support guide {intercomAppId != null && ( - diff --git a/src/cloud/components/organisms/settings/SettingsComponent.tsx b/src/cloud/components/organisms/settings/SettingsComponent.tsx index f39b16c049..975df105fd 100644 --- a/src/cloud/components/organisms/settings/SettingsComponent.tsx +++ b/src/cloud/components/organisms/settings/SettingsComponent.tsx @@ -7,7 +7,12 @@ import { isSingleKeyEventOutsideOfInput, } from '../../../lib/keyboard' import { useTranslation } from 'react-i18next' -import { mdiDomain, mdiAccountCircleOutline, mdiClose } from '@mdi/js' +import { + mdiDomain, + mdiAccountCircleOutline, + mdiClose, + mdiHelpCircleOutline, +} from '@mdi/js' import PersonalInfoTab from './PersonalInfoTab' import { usePage } from '../../../lib/stores/pageStore' import TeamInfoTab from './TeamInfoTab' @@ -26,9 +31,14 @@ import ApiTab from './ApiTab' import { PageStoreWithTeam } from '../../../interfaces/pageStore' import Settings from '../../../../shared/components/organisms/Settings' import SettingSidenavHeader from '../../../../shared/components/organisms/Settings/molecules/SettingSidenavHeader' -import SettingTabButton from '../../../../shared/components/organisms/Settings/atoms/SettingTabButton' +import SettingNavButtonItem, { + SettingNavLinkItem, +} from '../../../../shared/components/organisms/Settings/atoms/SettingNavItem' import Button from '../../../../shared/components/atoms/Button' -import styled from '../../../../shared/lib/styled' +import SettingSidenav from '../../../../shared/components/organisms/Settings/molecules/SettingSidenav' +import AppFeedbackForm from '../../molecules/AppFeedbackForm' +import SettingTabContent from '../../../../shared/components/organisms/Settings/atoms/SettingTabContent' +import { intercomAppId } from '../../../lib/consts' const SettingsComponent = () => { const { t } = useTranslation() @@ -98,6 +108,14 @@ const SettingsComponent = () => { return case 'api': return + case 'feedback': + return ( + } + /> + ) default: return } @@ -117,54 +135,58 @@ const SettingsComponent = () => { return ( + + ) : null + } + > - openSettingsTab('personalInfo')} /> - openSettingsTab('preferences')} /> {currentUserPermissions != null && ( <> - openSettingsTab('teamInfo')} /> - openSettingsTab('teamMembers')} /> - openSettingsTab('integrations')} /> - openSettingsTab('api')} /> @@ -175,25 +197,52 @@ const SettingsComponent = () => { currentUserPermissions.role === 'admin' && ( <> {subscription == null || subscription.status === 'trialing' ? ( - openSettingsTab('teamUpgrade')} /> ) : ( - openSettingsTab('teamSubscription')} /> )} - )} + + + + + {intercomAppId != null && ( + + )} + openSettingsTab('feedback')} + /> + } content={ @@ -212,12 +261,4 @@ const SettingsComponent = () => { ) } -const SettingSidenav = styled.nav` - width: 250px; - margin-bottom: 0; - margin-right: 0; - padding: ${({ theme }) => theme.sizes.spaces.sm}px; - overflow: hidden auto; -` - export default SettingsComponent diff --git a/src/cloud/lib/intercom/index.ts b/src/cloud/lib/intercom/index.ts index cd45427064..d3b34ac9ce 100644 --- a/src/cloud/lib/intercom/index.ts +++ b/src/cloud/lib/intercom/index.ts @@ -5,7 +5,7 @@ export function load(appId: string) { w.intercomSettings = { ...(w.intercomSettings || {}), hide_default_launcher: true, - custom_launcher_selector: '#helper-message', + custom_launcher_selector: '.helper-message', } if (typeof ic === 'function') { ic('reattach_activator') @@ -46,7 +46,7 @@ export function boot(appId: string, options = {}) { app_id: appId, ...options, hide_default_launcher: true, - custom_launcher_selector: '#helper-message', + custom_launcher_selector: '.helper-message', }) } diff --git a/src/cloud/lib/stores/settings/store.ts b/src/cloud/lib/stores/settings/store.ts index 5ea03db35d..38f542fdc2 100644 --- a/src/cloud/lib/stores/settings/store.ts +++ b/src/cloud/lib/stores/settings/store.ts @@ -30,6 +30,7 @@ export type SettingsTab = | 'teamUpgrade' | 'teamSubscription' | 'api' + | 'feedback' function useSettingsStore() { const { globalData, setPartialGlobalData } = useGlobalData() diff --git a/src/components/PreferencesModal/PreferencesModal.tsx b/src/components/PreferencesModal/PreferencesModal.tsx index 1856198f85..58deb1808f 100644 --- a/src/components/PreferencesModal/PreferencesModal.tsx +++ b/src/components/PreferencesModal/PreferencesModal.tsx @@ -23,7 +23,7 @@ import StorageTab from './StorageTab' import MigrationPage from './MigrationTab' import { useMigrations } from '../../lib/migrate/store' import KeymapTab from './KeymapTab' -import SettingTabButton from '../../shared/components/organisms/Settings/atoms/SettingTabButton' +import SettingNavButtonItem from '../../shared/components/organisms/Settings/atoms/SettingNavItem' const FullScreenContainer = styled.div` z-index: 7000; @@ -177,29 +177,25 @@ const PreferencesModal = () => { - openTab('about')} /> - openTab('keymap')} /> - openTab('general')} /> {currentStorage != null && ( - openTab( get(currentStorage.id) != null ? 'migration' : 'storage' @@ -207,16 +203,14 @@ const PreferencesModal = () => { } /> )} - openTab('editor')} /> - openTab('markdown')} /> diff --git a/src/shared/components/molecules/Form/atoms/FormEmoji.tsx b/src/shared/components/molecules/Form/atoms/FormEmoji.tsx index d33f6f403e..98ee80ee87 100644 --- a/src/shared/components/molecules/Form/atoms/FormEmoji.tsx +++ b/src/shared/components/molecules/Form/atoms/FormEmoji.tsx @@ -96,7 +96,7 @@ const Container = styled.button` padding: 0 ${({ theme }) => theme.sizes.spaces.sm}px; border-radius: ${({ theme }) => theme.borders.radius}px; font-size: ${({ theme }) => theme.sizes.fonts.df}px; - ${formInputHeight()} + ${formInputHeight} outline: none; background: none; border: 1px solid ${({ theme }) => theme.colors.border.main}; diff --git a/src/shared/components/molecules/Form/atoms/FormInput.tsx b/src/shared/components/molecules/Form/atoms/FormInput.tsx index c7457f426c..cea719c6b3 100644 --- a/src/shared/components/molecules/Form/atoms/FormInput.tsx +++ b/src/shared/components/molecules/Form/atoms/FormInput.tsx @@ -99,7 +99,7 @@ const StyledInput = styled.input` padding: 0 ${({ theme }) => theme.sizes.spaces.sm}px; border-radius: ${({ theme }) => theme.borders.radius}px; font-size: ${({ theme }) => theme.sizes.fonts.df}px; - ${formInputHeight()} + ${formInputHeight} outline: none; background: none; border: 1px solid ${({ theme }) => theme.colors.border.main}; diff --git a/src/shared/components/molecules/Form/atoms/FormSelect.tsx b/src/shared/components/molecules/Form/atoms/FormSelect.tsx index 1eaad04da2..40f711430f 100644 --- a/src/shared/components/molecules/Form/atoms/FormSelect.tsx +++ b/src/shared/components/molecules/Form/atoms/FormSelect.tsx @@ -126,7 +126,7 @@ const Container = styled.div` .form__select .form__select__control, .form__select .form__select__indicator, .form__select .form__select__indicators { - ${formInputHeight()} + ${formInputHeight} } .form__select .form__select__control { diff --git a/src/shared/components/molecules/Form/templates/FormRow.tsx b/src/shared/components/molecules/Form/templates/FormRow.tsx index ca00e6c193..89cedd49c9 100644 --- a/src/shared/components/molecules/Form/templates/FormRow.tsx +++ b/src/shared/components/molecules/Form/templates/FormRow.tsx @@ -41,7 +41,11 @@ export type FormRowProps = { )[] } -const FormRow: AppComponent<{ row: FormRowProps }> = ({ row, className }) => { +const FormRow: AppComponent<{ row: FormRowProps }> = ({ + row, + className, + children, +}) => { return ( = ({ row, className }) => { )}
))} + {children}
)} {row.description != null && ( diff --git a/src/shared/components/organisms/Settings/atoms/SettingNavItem.tsx b/src/shared/components/organisms/Settings/atoms/SettingNavItem.tsx new file mode 100644 index 0000000000..f079fe2833 --- /dev/null +++ b/src/shared/components/organisms/Settings/atoms/SettingNavItem.tsx @@ -0,0 +1,91 @@ +import React, { MouseEventHandler } from 'react' +import cc from 'classcat' +import styled from '../../../../lib/styled' +import { StyledProps } from '../../../../lib/styled/styleFunctions' +import Icon from '../../../atoms/Icon' +import { mdiOpenInNew } from '@mdi/js' + +interface SideNavItemProps { + label: string + id?: string + className?: string +} + +type SettingNavButtonItemProps = SideNavItemProps & { + active?: boolean + onClick?: MouseEventHandler +} + +const SettingNavButtonItem = ({ + label, + active, + id, + className, + onClick, +}: SettingNavButtonItemProps) => { + return ( + + ) +} + +type SettingNavLinkItemProps = SideNavItemProps & { + href: string +} + +export const SettingNavLinkItem = ({ + label, + id, + href, +}: SettingNavLinkItemProps) => { + return ( + + {label} + + ) +} + +const settingNavItemStyle = ({ theme }: StyledProps) => ` +display: flex; +align-items: center; +width: 100%; +margin-bottom: ${theme.sizes.spaces.xsm}px; +padding: ${theme.sizes.spaces.xsm}px + ${theme.sizes.spaces.md}px; +background-color: transparent; +border: none; +border-radius: 2px; +color: ${theme.colors.text.primary}; +cursor: pointer; +font-size: ${theme.sizes.fonts.df}px; +text-align: left; + +&:hover, +&:focus { + background-color: ${theme.colors.background.tertiary}; +} + +&.setting__nav__item--active { + background-color: ${theme.colors.background.quaternary}; +}` + +const Button = styled.button` + ${settingNavItemStyle} +` + +const Link = styled.a` + ${settingNavItemStyle} + text-decoration: none; + + &:hover, + &:focus { + text-decoration: underline; + } +` + +export default SettingNavButtonItem diff --git a/src/shared/components/organisms/Settings/atoms/SettingTabButton.tsx b/src/shared/components/organisms/Settings/atoms/SettingTabButton.tsx deleted file mode 100644 index 167c0a142d..0000000000 --- a/src/shared/components/organisms/Settings/atoms/SettingTabButton.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React, { MouseEventHandler } from 'react' -import cc from 'classcat' -import styled from '../../../../lib/styled' - -interface SettingTabButtonProps { - label: string - active: boolean - tab: string - id?: string - onClick?: MouseEventHandler -} - -const SettingTabButton = ({ - label, - tab, - active, - id, - onClick, -}: SettingTabButtonProps) => { - return ( - - {label} - - ) -} - -const Container = styled.button` - display: flex; - align-items: center; - width: 100%; - margin-bottom: ${({ theme }) => theme.sizes.spaces.xsm}px; - padding: ${({ theme }) => theme.sizes.spaces.xsm}px - ${({ theme }) => theme.sizes.spaces.md}px; - background-color: transparent; - border: none; - border-radius: 2px; - color: ${({ theme }) => theme.colors.text.primary}; - cursor: pointer; - font-size: ${({ theme }) => theme.sizes.fonts.df}px; - text-align: left; - - &:hover, - &:focus { - background-color: ${({ theme }) => theme.colors.background.tertiary}; - } - - &.active { - background-color: ${({ theme }) => theme.colors.background.quaternary}; - } -` - -export default SettingTabButton diff --git a/src/shared/components/organisms/Settings/molecules/SettingSidenav.tsx b/src/shared/components/organisms/Settings/molecules/SettingSidenav.tsx new file mode 100644 index 0000000000..fab4d1ff89 --- /dev/null +++ b/src/shared/components/organisms/Settings/molecules/SettingSidenav.tsx @@ -0,0 +1,42 @@ +import React, { PropsWithChildren } from 'react' +import styled from '../../../../lib/styled' +import cc from 'classcat' + +interface SettingSidenavProps { + className?: string + footer?: React.ReactNode +} + +const SettingSidenav = React.forwardRef< + HTMLDivElement, + PropsWithChildren +>(({ className, children, footer }, ref) => { + return ( + +
{children}
+ {footer != null && ( +
{footer}
+ )} +
+ ) +}) + +const Container = styled.nav` + width: 250px; + height: 100%; + margin: 0; + display: flex; + flex-direction: column; + .setting__sidenav__scroller { + flex: 1 1 auto; + overflow: hidden auto; + padding: ${({ theme }) => theme.sizes.spaces.sm}px; + } + + .setting__sidenav__footer { + flex: 0 0 auto; + padding: ${({ theme }) => theme.sizes.spaces.sm}px; + } +` + +export default SettingSidenav From 14999b0e5e0cdede797b0a05c84188dd96785b3c Mon Sep 17 00:00:00 2001 From: davy-c Date: Wed, 19 May 2021 08:18:46 +0900 Subject: [PATCH 80/91] copy input and switch --- src/cloud/components/atoms/CopyReadInput.tsx | 62 ------------- src/cloud/components/atoms/CustomSwitch.tsx | 57 ------------ src/cloud/components/molecules/DocShare.tsx | 18 +--- .../molecules/OpenInviteSection.tsx | 36 +++----- .../Modal/contents/TemplatesModal/index.tsx | 8 +- .../contents/Workspace/WorkspaceModalForm.tsx | 6 +- src/shared/components/atoms/Button.tsx | 1 + src/shared/components/atoms/Switch.tsx | 90 +++++++++++++------ .../molecules/Form/atoms/FormCopyInput.tsx | 64 +++++++++++++ .../molecules/Form/atoms/FormInput.tsx | 4 + 10 files changed, 151 insertions(+), 195 deletions(-) delete mode 100644 src/cloud/components/atoms/CopyReadInput.tsx delete mode 100644 src/cloud/components/atoms/CustomSwitch.tsx create mode 100644 src/shared/components/molecules/Form/atoms/FormCopyInput.tsx diff --git a/src/cloud/components/atoms/CopyReadInput.tsx b/src/cloud/components/atoms/CopyReadInput.tsx deleted file mode 100644 index c28bbc8862..0000000000 --- a/src/cloud/components/atoms/CopyReadInput.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import React, { useState } from 'react' -import copy from 'copy-to-clipboard' -import styled from '../../lib/styled' -import { - baseButtonStyle, - secondaryButtonStyle, -} from '../../lib/styled/styleFunctions' - -interface CopyReadInputProps { - text: string -} - -const CopyReadInput = ({ text }: CopyReadInputProps) => { - const [copyButtonLabel, setCopyButtonLabel] = useState('Copy link') - - const copyButtonHandler = () => { - copy(text) - setCopyButtonLabel('✓ Copied') - setTimeout(() => { - setCopyButtonLabel('Copy link') - }, 600) - } - - return ( - - - - - ) -} - -const StyledCopyReadInput = styled.div` - width: 100%; - display: flex; - align-items: center; - height: 40px; - - .link { - max-width: 600px; - margin-right: ${({ theme }) => theme.space.xsmall}px; - padding-left: ${({ theme }) => theme.space.xxsmall}px; - background: ${({ theme }) => theme.boldBackgroundColor}; - color: ${({ theme }) => theme.subtleTextColor}; - flex: 1 1 auto; - height: 100%; - line-height: 32px; - border: 1px solid ${({ theme }) => theme.baseBorderColor}; - - &:focus { - border-color: ${({ theme }) => theme.primaryShadowColor}; - } - } - - .copy-button { - ${baseButtonStyle} - ${secondaryButtonStyle} - } -` - -export default CopyReadInput diff --git a/src/cloud/components/atoms/CustomSwitch.tsx b/src/cloud/components/atoms/CustomSwitch.tsx deleted file mode 100644 index 6253d63150..0000000000 --- a/src/cloud/components/atoms/CustomSwitch.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import React, { useMemo } from 'react' -import Switch, { ReactSwitchProps } from 'react-switch' -import { useSettings } from '../../lib/stores/settings' -import { selectTheme } from '../../lib/styled' - -type CustomSwitchProps = ReactSwitchProps & { - variant?: 'primary' | 'success' -} - -const CustomSwitch = ({ - disabled, - id, - className, - onChange, - checked, - uncheckedIcon, - checkedIcon, - height = 20, - width = 45, - variant = 'primary', -}: CustomSwitchProps) => { - const { settings } = useSettings() - - const customStyle: Partial = useMemo(() => { - const appTheme = selectTheme(settings['general.theme']) - switch (variant) { - case 'primary': - return { - onColor: appTheme.primaryBackgroundColor, - } - - case 'success': - default: - return { - onColor: '#5580DC', - } - } - }, [variant, settings]) - - return ( - - ) -} - -export default CustomSwitch diff --git a/src/cloud/components/molecules/DocShare.tsx b/src/cloud/components/molecules/DocShare.tsx index 2c163848a4..f8864e80fc 100644 --- a/src/cloud/components/molecules/DocShare.tsx +++ b/src/cloud/components/molecules/DocShare.tsx @@ -8,7 +8,6 @@ import { regenerateShareLink, updateShareLink, } from '../../api/share' -import Switch from 'react-switch' import { mdiChevronDown, mdiChevronRight, @@ -41,6 +40,7 @@ import { getDocLinkHref } from '../atoms/Link/DocLink' import { usingElectron, openInBrowser } from '../../lib/stores/electron' import UpgradeButton from '../UpgradeButton' import { useToast } from '../../../shared/lib/stores/toast' +import Switch from '../../../shared/components/atoms/Switch' interface DocShareProps { currentDoc: SerializedDocWithBookmark @@ -386,15 +386,9 @@ const DocShare = ({ currentDoc, team }: DocShareProps) => {
@@ -467,15 +461,12 @@ const DocShare = ({ currentDoc, team }: DocShareProps) => {
@@ -520,15 +511,12 @@ const DocShare = ({ currentDoc, team }: DocShareProps) => {
diff --git a/src/cloud/components/molecules/OpenInviteSection.tsx b/src/cloud/components/molecules/OpenInviteSection.tsx index 166fe6a70f..249e66b90a 100644 --- a/src/cloud/components/molecules/OpenInviteSection.tsx +++ b/src/cloud/components/molecules/OpenInviteSection.tsx @@ -12,12 +12,13 @@ import { import { Spinner } from '../atoms/Spinner' import { SerializedUserTeamPermissions } from '../../interfaces/db/userTeamPermissions' import { SerializedOpenInvite } from '../../interfaces/db/openInvite' -import Switch from 'react-switch' import styled from '../../lib/styled' -import CopyReadInput from '../atoms/CopyReadInput' import { getTeamURL, getOpenInviteURL } from '../../lib/utils/patterns' import { boostHubBaseUrl } from '../../lib/consts' import { useToast } from '../../../shared/lib/stores/toast' +import Switch from '../../../shared/components/atoms/Switch' +import Button from '../../../shared/components/atoms/Button' +import FormCopyInput from '../../../shared/components/molecules/Form/atoms/FormCopyInput' interface OpenInvitesSectionProps { userPermissions: SerializedUserTeamPermissions @@ -173,15 +174,9 @@ const OpenInvitesSection = ({ userPermissions }: OpenInvitesSectionProps) => {

Invite with an open Link

{openInviteLink != null && ( @@ -189,12 +184,12 @@ const OpenInvitesSection = ({ userPermissions }: OpenInvitesSectionProps) => {

You can share this secret link to invite people to this workspace. Only admins can see it. You can{' '} - + {' '} to generate a new one.

- + )} {sending && ( @@ -215,22 +210,13 @@ const StyledFlex = styled.div` ` const StyledOpenLinkSection = styled.div` - p { - margin-bottom: ${({ theme }) => theme.space.default}px; + .link-reset { + padding: 0; + margin: 0; } -` -const StyledResetLinkButton = styled.button` - background-color: transparent; - padding: 0; - color: ${({ theme }) => theme.primaryTextColor}; - font-size: inherit; - display: inline; - cursor: pointer; - - &:hover, - &:focus { - text-decoration: underline; + p { + margin-bottom: ${({ theme }) => theme.space.default}px; } ` diff --git a/src/cloud/components/organisms/Modal/contents/TemplatesModal/index.tsx b/src/cloud/components/organisms/Modal/contents/TemplatesModal/index.tsx index 21e578a09a..af3a3acc03 100644 --- a/src/cloud/components/organisms/Modal/contents/TemplatesModal/index.tsx +++ b/src/cloud/components/organisms/Modal/contents/TemplatesModal/index.tsx @@ -37,14 +37,11 @@ import { import { mdiTrashCanOutline, mdiFileDocumentOutline, - mdiEyeOutline, - mdiFileEditOutline, mdiClose, mdiContentSaveOutline, } from '@mdi/js' import Icon from '../../../../atoms/Icon' import EmojiIcon from '../../../../atoms/EmojiIcon' -import CustomSwitch from '../../../../atoms/CustomSwitch' import { useSettings } from '../../../../../lib/stores/settings' import cc from 'classcat' import { useEmojiPicker } from '../../../../../lib/stores/emoji' @@ -53,6 +50,7 @@ import CodeMirrorEditor from '../../../../../lib/editor/components/CodeMirrorEdi import MarkdownView from '../../../../atoms/MarkdownView' import { useToast } from '../../../../../../shared/lib/stores/toast' import { useModal } from '../../../../../../shared/lib/stores/modal' +import Switch from '../../../../../../shared/components/atoms/Switch' interface TemplatesModalProps { callback?: (template: SerializedTemplate) => void @@ -385,10 +383,8 @@ const TemplatesModal = ({ callback }: TemplatesModalProps) => { /> - } - checkedIcon={} checked={!inPreview} width={50} height={20} diff --git a/src/cloud/components/organisms/Modal/contents/Workspace/WorkspaceModalForm.tsx b/src/cloud/components/organisms/Modal/contents/Workspace/WorkspaceModalForm.tsx index e3956a9d55..c201485000 100644 --- a/src/cloud/components/organisms/Modal/contents/Workspace/WorkspaceModalForm.tsx +++ b/src/cloud/components/organisms/Modal/contents/Workspace/WorkspaceModalForm.tsx @@ -17,10 +17,10 @@ import { import { SerializedTeam } from '../../../../../interfaces/db/team' import { useNav } from '../../../../../lib/stores/nav' import Flexbox from '../../../../atoms/Flexbox' -import CustomSwitch from '../../../../atoms/CustomSwitch' import WorkspaceAccess from './WorkspaceAccess' import { useToast } from '../../../../../../shared/lib/stores/toast' import { useModal } from '../../../../../../shared/lib/stores/modal' +import Switch from '../../../../../../shared/components/atoms/Switch' interface WorkspaceModalFormProps { workspace?: SerializedWorkspace @@ -225,7 +225,7 @@ const WorkspaceModalForm = ({ workspace }: WorkspaceModalFormProps) => { )} - { checked={!isPublic} height={28} width={60} - uncheckedIcon={false} - checkedIcon={false} /> diff --git a/src/shared/components/atoms/Button.tsx b/src/shared/components/atoms/Button.tsx index fcd04239bb..92a5f7f460 100644 --- a/src/shared/components/atoms/Button.tsx +++ b/src/shared/components/atoms/Button.tsx @@ -224,6 +224,7 @@ const StyledButton = styled.button` &:focus, &.button__state--active { opacity: 0.8; + text-decoration: underline; } } } diff --git a/src/shared/components/atoms/Switch.tsx b/src/shared/components/atoms/Switch.tsx index 858340f833..0cdb82a25f 100644 --- a/src/shared/components/atoms/Switch.tsx +++ b/src/shared/components/atoms/Switch.tsx @@ -1,6 +1,22 @@ -import React, { useMemo } from 'react' -import ReactSwitch, { ReactSwitchProps } from 'react-switch' +import React from 'react' +import ReactSwitch from 'react-switch' import cc from 'classcat' +import styled from '../../lib/styled' + +interface SwitchProps { + disabled?: boolean + id?: string + className?: string + onChange: ( + checked: boolean, + event: MouseEvent | React.SyntheticEvent, + id: string + ) => void + checked: boolean + height?: number + width?: number + handleSize?: number +} const Switch = ({ disabled, @@ -8,32 +24,54 @@ const Switch = ({ className, onChange, checked, - height = 20, - width = 30, -}: ReactSwitchProps) => { - const customStyle: Partial = useMemo(() => { - if (checked) { - return {} - } - - return {} - }, [checked]) - + height = 24, + width = 38, + handleSize = 16, +}: SwitchProps) => { return ( - + + + ) } +const Container = styled.div` + .switch { + .react-switch-bg { + background: ${({ theme }) => + theme.colors.variants.secondary.base} !important; + } + + .react-switch-handle { + background: ${({ theme }) => + theme.colors.variants.secondary.text} !important; + } + } + + &.switch__wrapper--checked .switch { + .react-switch-bg { + background: ${({ theme }) => + theme.colors.variants.primary.base} !important; + } + .react-switch-handle { + background: ${({ theme }) => + theme.colors.variants.primary.text} !important; + } + } +` + export default Switch diff --git a/src/shared/components/molecules/Form/atoms/FormCopyInput.tsx b/src/shared/components/molecules/Form/atoms/FormCopyInput.tsx new file mode 100644 index 0000000000..1e5ad0ccee --- /dev/null +++ b/src/shared/components/molecules/Form/atoms/FormCopyInput.tsx @@ -0,0 +1,64 @@ +import React, { useState } from 'react' +import copy from 'copy-to-clipboard' +import styled from '../../../../lib/styled' +import { AppComponent } from '../../../../lib/types' +import cc from 'classcat' +import FormInput from './FormInput' +import Button from '../../../atoms/Button' + +interface FormCopyInputProps { + text: string + copyLabel?: string +} + +const FormCopyInput: AppComponent = ({ + text, + className, + copyLabel = 'Copy', +}) => { + const [copyButtonLabel, setCopyButtonLabel] = useState(copyLabel) + + const copyButtonHandler = () => { + copy(text) + setCopyButtonLabel('✓ Copied') + setTimeout(() => { + setCopyButtonLabel(copyLabel) + }, 600) + } + + return ( + + + + + ) +} + +const Container = styled.div` + width: 100%; + display: flex; + align-items: center; + + .form__copy__input { + flex: 1 1 auto; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + + .form__copy__button { + width: 100px; + white-space: nowrap; + flex: 0 0 auto; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } +` + +export default FormCopyInput diff --git a/src/shared/components/molecules/Form/atoms/FormInput.tsx b/src/shared/components/molecules/Form/atoms/FormInput.tsx index cea719c6b3..fc1c69db3e 100644 --- a/src/shared/components/molecules/Form/atoms/FormInput.tsx +++ b/src/shared/components/molecules/Form/atoms/FormInput.tsx @@ -105,6 +105,10 @@ const StyledInput = styled.input` border: 1px solid ${({ theme }) => theme.colors.border.main}; color: ${({ theme }) => theme.colors.text.primary}; + &:read-only { + filter: brightness(80%); + } + &:disabled { cursor: not-allowed; opacity: 0.5; From 0cadede508896bd287919ab98efd63eff10e0075 Mon Sep 17 00:00:00 2001 From: davy-c Date: Wed, 19 May 2021 08:54:20 +0900 Subject: [PATCH 81/91] remove section flex --- src/cloud/components/Application.tsx | 63 +----------- .../components/molecules/NewDocButton.tsx | 99 +++++++++++++++++++ .../UpdateBillingEmailForm.tsx | 42 ++++---- .../UpdateBillingMethodForm.tsx | 40 +++----- .../SubscriptionForm/UpdateBillingPromo.tsx | 46 ++++----- .../molecules/SubscriptionForm/index.tsx | 24 +++-- .../components/organisms/settings/styled.ts | 21 ---- src/shared/components/atoms/ButtonGroup.tsx | 66 +++++++++---- 8 files changed, 218 insertions(+), 183 deletions(-) create mode 100644 src/cloud/components/molecules/NewDocButton.tsx diff --git a/src/cloud/components/Application.tsx b/src/cloud/components/Application.tsx index 1244c4c27b..78bb45ea02 100644 --- a/src/cloud/components/Application.tsx +++ b/src/cloud/components/Application.tsx @@ -73,7 +73,6 @@ import { mdiArchiveOutline, mdiClockOutline, mdiCogOutline, - mdiDotsHorizontal, mdiDownload, mdiFileDocumentMultipleOutline, mdiFileDocumentOutline, @@ -84,13 +83,11 @@ import { mdiMagnify, mdiPaperclip, mdiPencil, - mdiPencilBoxMultipleOutline, mdiPlus, mdiPlusCircleOutline, mdiStar, mdiStarOutline, mdiTag, - mdiTextBoxPlusOutline, mdiTrashCanOutline, mdiWeb, } from '@mdi/js' @@ -140,14 +137,12 @@ import { mapFuzzyNavigationRecentItems, } from '../lib/mappers/fuzzyNavigation' import { ModalOpeningOptions, useModal } from '../../shared/lib/stores/modal' -import ButtonGroup from '../../shared/components/atoms/ButtonGroup' -import Button from '../../shared/components/atoms/Button' -import TemplatesModal from './organisms/Modal/contents/TemplatesModal' import { CreateWorkspaceRequestBody } from '../api/teams/workspaces' import { cloudSidebaCategoryLabels, cloudSidebarOrderedCategoriesDelimiter, } from '../lib/sidebar' +import NewDocButton from './molecules/NewDocButton' interface ApplicationProps { content: ContentLayoutProps @@ -169,7 +164,6 @@ const Application = ({ tagsMap, currentParentFolderId, currentWorkspaceId, - currentPath, } = useNav() const { sideBarOpenedLinksIdsSet, @@ -206,7 +200,6 @@ const Application = ({ openNewFolderForm, openRenameDocForm, openWorkspaceEditForm, - openNewDocForm, } = useCloudUI() const [showFuzzyNavigation, setShowFuzzyNavigation] = useState(false) const { popup } = useContextMenu() @@ -685,59 +678,7 @@ const Application = ({ }, }, ]} - treeTopRows={ - team == null ? null : ( - <> - - - +
) @@ -84,19 +79,20 @@ const UpdateBillingEmailForm = ({ onChange={onEmailInputChangeHandler} /> - - + + - - {sending ? : 'Update'} - - + + Update + +
) diff --git a/src/cloud/components/molecules/SubscriptionForm/UpdateBillingMethodForm.tsx b/src/cloud/components/molecules/SubscriptionForm/UpdateBillingMethodForm.tsx index 532a314073..fe2aaa26dd 100644 --- a/src/cloud/components/molecules/SubscriptionForm/UpdateBillingMethodForm.tsx +++ b/src/cloud/components/molecules/SubscriptionForm/UpdateBillingMethodForm.tsx @@ -2,11 +2,8 @@ import React, { useState, useMemo, useCallback } from 'react' import { SectionIntroduction, SectionFlexRow, - SectionFlexDualButtons, } from '../../organisms/settings/styled' -import CustomButton from '../../atoms/buttons/CustomButton' import { SerializedSubscription } from '../../../interfaces/db/subscription' -import { Spinner } from '../../atoms/Spinner' import { updateSubMethod } from '../../../api/teams/subscription/update' import { useElements, useStripe, CardElement } from '@stripe/react-stripe-js' import { @@ -18,6 +15,10 @@ import { useSettings } from '../../../lib/stores/settings' import { StyledCardElementContainer } from './index' import Alert from '../../../../components/atoms/Alert' import { useToast } from '../../../../shared/lib/stores/toast' +import ButtonGroup from '../../../../shared/components/atoms/ButtonGroup' +import Button, { + LoadingButton, +} from '../../../../shared/components/atoms/Button' interface UpdateBillingMethodFormProps { sub?: SerializedSubscription @@ -114,15 +115,9 @@ const UpdateBillingMethodForm = ({

You need to have a valid subscription to perform this action.

- - - Cancel - - +
) @@ -147,7 +142,7 @@ const UpdateBillingMethodForm = ({ )} - + - - + + - - {sending ? : 'Update'} - - + Update + +
) diff --git a/src/cloud/components/molecules/SubscriptionForm/UpdateBillingPromo.tsx b/src/cloud/components/molecules/SubscriptionForm/UpdateBillingPromo.tsx index 6063847430..9a9c503d1f 100644 --- a/src/cloud/components/molecules/SubscriptionForm/UpdateBillingPromo.tsx +++ b/src/cloud/components/molecules/SubscriptionForm/UpdateBillingPromo.tsx @@ -1,14 +1,13 @@ import React, { useState, useCallback } from 'react' -import { - SectionIntroduction, - SectionFlexDualButtons, -} from '../../organisms/settings/styled' -import CustomButton from '../../atoms/buttons/CustomButton' +import { SectionIntroduction } from '../../organisms/settings/styled' import { SerializedSubscription } from '../../../interfaces/db/subscription' import { StyledBillingInput } from '.' -import { Spinner } from '../../atoms/Spinner' import { redeemPromo } from '../../../api/teams/subscription' import { useToast } from '../../../../shared/lib/stores/toast' +import Button, { + LoadingButton, +} from '../../../../shared/components/atoms/Button' +import ButtonGroup from '../../../../shared/components/atoms/ButtonGroup' interface UpdateBillingPromoFormProps { sub?: SerializedSubscription @@ -68,15 +67,9 @@ const UpdateBillingPromoForm = ({

You need to have a valid subscription to perform this action.

- - - Cancel - - +
) @@ -93,19 +86,20 @@ const UpdateBillingPromoForm = ({ onChange={onPromoInputChangeHandler} /> - - + + - - {sending ? : 'Apply'} - - + + Apply + +
) diff --git a/src/cloud/components/molecules/SubscriptionForm/index.tsx b/src/cloud/components/molecules/SubscriptionForm/index.tsx index dee0d215cd..edaf8f35d2 100644 --- a/src/cloud/components/molecules/SubscriptionForm/index.tsx +++ b/src/cloud/components/molecules/SubscriptionForm/index.tsx @@ -3,13 +3,11 @@ import { useStripe, useElements, CardElement } from '@stripe/react-stripe-js' import { createSubscription } from '../../../api/teams/subscription' import { SerializedTeam } from '../../../interfaces/db/team' import { SerializedSubscription } from '../../../interfaces/db/subscription' -import { SectionFlexDualButtons } from '../../organisms/settings/styled' import { inputStyle } from '../../../lib/styled/styleFunctions' import styled from '../../../lib/styled' import Stripe, { StripeElementStyle } from '@stripe/stripe-js' import { useSettings } from '../../../lib/stores/settings' import { selectTheme } from '../../../lib/styled' -import { Spinner } from '../../atoms/Spinner' import { usePage } from '../../../lib/stores/pageStore' import { stripeProPlanUnit, @@ -23,7 +21,10 @@ import Icon from '../../../../components/atoms/Icon' import { mdiChevronDown, mdiChevronRight } from '@mdi/js' import Alert from '../../../../components/atoms/Alert' import { useToast } from '../../../../shared/lib/stores/toast' -import Button from '../../../../shared/components/atoms/Button' +import Button, { + LoadingButton, +} from '../../../../shared/components/atoms/Button' +import ButtonGroup from '../../../../shared/components/atoms/ButtonGroup' interface SubscriptionFormProps { team: SerializedTeam @@ -236,9 +237,7 @@ const SubscriptionForm = ({ onChange={onPromoCodeInputChangeHandler} /> )} - + {onCancel != null && ( - + Subscribe + + ) } @@ -361,6 +361,10 @@ export const StyledSubscriptionForm = styled.form` margin-left: ${({ theme }) => theme.space.xxsmall}px; } } + + .button__group { + margin-top: 40px; + } ` export const StyledCardElementContainer = styled.div` diff --git a/src/cloud/components/organisms/settings/styled.ts b/src/cloud/components/organisms/settings/styled.ts index d60d4fbb0c..0269a56cf5 100644 --- a/src/cloud/components/organisms/settings/styled.ts +++ b/src/cloud/components/organisms/settings/styled.ts @@ -93,24 +93,3 @@ export const SectionFlexRow = styled.div` } } ` - -export const SectionFlexDualButtons = styled.div` - display: flex; - align-items: center; - justify-content: flex-end; - - &.marginTop { - margin-top: ${({ theme }) => theme.space.medium}px; - } - - button { - margin-left: ${({ theme }) => theme.space.small}px; - - svg { - position: relative !important; - transform: none !important; - top: initial !important; - left: initial !important; - } - } -` diff --git a/src/shared/components/atoms/ButtonGroup.tsx b/src/shared/components/atoms/ButtonGroup.tsx index 225388caab..36a78e835f 100644 --- a/src/shared/components/atoms/ButtonGroup.tsx +++ b/src/shared/components/atoms/ButtonGroup.tsx @@ -1,33 +1,63 @@ import React from 'react' -import styled from '../../../lib/styled' +import { AppComponent } from '../../lib/types' +import cc from 'classcat' +import styled from '../../lib/styled' interface ButtonGroupProps { - children: React.ReactNode + display?: 'inline-flex' | 'flex' + layout?: 'collapsed' | 'spread' + justifyContent?: 'flex-start' | 'flex-end' } -const ButtonGroup = ({ children }: ButtonGroupProps) => { - return {children} +const ButtonGroup: AppComponent = ({ + children, + display = 'inline-flex', + layout = 'collapsed', + justifyContent = 'flex-start', +}) => { + return ( + + {children} + + ) } -const StyledButtonGroup = styled.div` - display: inline-flex; +const StyledButtonGroup = styled.div<{ + display: string + justifyContent: string +}>` + display: ${({ display }) => display}; + justify-content: ${({ justifyContent }) => justifyContent}; position: relative; - & > button:first-child { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - flex: 1 1 auto; - } + &.button__group--spread { + align-items: center; - & > button:last-child { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - margin: 0; - flex: 0 0 auto; + button + button { + margin-left: ${({ theme }) => theme.sizes.spaces.sm}px; + } } - & > button:not(:first-child):not(:last-child) { - border-radius: 0; + &.button__group--collapsed { + > button:nth-child(2n + 1) { + margin: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + + > button:nth-child(2n) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + margin: 0; + } + + > button:not(:first-child):not(:last-child) { + border-radius: 0; + } } ` From 00993d779c6ec553104aa03953ef5895ce74fa7c Mon Sep 17 00:00:00 2001 From: davy-c Date: Wed, 19 May 2021 09:44:50 +0900 Subject: [PATCH 82/91] promises and invalid dom nesting --- .../molecules/OpenInviteSection.tsx | 33 ++++++--- .../molecules/TeamInvitesSection.tsx | 36 +++++++--- .../organisms/settings/MembersTab.tsx | 71 +++++++++++++------ .../organisms/settings/SubscriptionTab.tsx | 4 +- .../organisms/settings/UpgradeTab.tsx | 4 +- 5 files changed, 106 insertions(+), 42 deletions(-) diff --git a/src/cloud/components/molecules/OpenInviteSection.tsx b/src/cloud/components/molecules/OpenInviteSection.tsx index 249e66b90a..e1c4edde50 100644 --- a/src/cloud/components/molecules/OpenInviteSection.tsx +++ b/src/cloud/components/molecules/OpenInviteSection.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback, useMemo } from 'react' +import React, { useState, useCallback, useMemo, useRef, useEffect } from 'react' import { SectionRow } from '../organisms/settings/styled' import { useDialog, DialogIconTypes } from '../../../shared/lib/stores/dialog' import { usePage } from '../../lib/stores/pageStore' @@ -33,6 +33,14 @@ const OpenInvitesSection = ({ userPermissions }: OpenInvitesSectionProps) => { >(undefined) const { messageBox } = useDialog() const { pushApiErrorMessage } = useToast() + const mountedRef = useRef(false) + + useEffect(() => { + mountedRef.current = true + return () => { + mountedRef.current = false + } + }, []) useEffectOnce(() => { fetchOpenInvite() @@ -43,13 +51,22 @@ const OpenInvitesSection = ({ userPermissions }: OpenInvitesSectionProps) => { return } setFetching(true) - try { - const { invite } = await getOpenInvite(team) - setOpenInvite(invite) - setFetching(false) - } catch (error) { - pushApiErrorMessage(error) - } + getOpenInvite(team) + .then(({ invite }) => { + if (!mountedRef.current) { + return + } + setOpenInvite(invite) + }) + .catch((error) => { + pushApiErrorMessage(error) + }) + .then(() => { + if (!mountedRef.current) { + return + } + setFetching(false) + }) }, [team, pushApiErrorMessage]) const createInvite = useCallback(async () => { diff --git a/src/cloud/components/molecules/TeamInvitesSection.tsx b/src/cloud/components/molecules/TeamInvitesSection.tsx index 2c4dd2a993..7acf107bd0 100644 --- a/src/cloud/components/molecules/TeamInvitesSection.tsx +++ b/src/cloud/components/molecules/TeamInvitesSection.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback } from 'react' +import React, { useState, useCallback, useRef, useEffect } from 'react' import { SectionList, SectionListItem, @@ -36,6 +36,14 @@ const TeamInvitesSection = ({ userPermissions }: TeamInvitesSectionProps) => { const [error, setError] = useState() const { messageBox } = useDialog() const [role, setRole] = useState('member') + const mountedRef = useRef(false) + + useEffect(() => { + mountedRef.current = true + return () => { + mountedRef.current = false + } + }, []) useEffectOnce(() => { fetchAndSetInvites() @@ -46,13 +54,25 @@ const TeamInvitesSection = ({ userPermissions }: TeamInvitesSectionProps) => { return } setSending(true) - try { - const { invites } = await getTeamInvites(team) - setPendingInvites(invites) - } catch (error) { - setError(error) - } - setSending(false) + getTeamInvites(team) + .then(({ invites }) => { + if (!mountedRef.current) { + return + } + setPendingInvites(invites) + }) + .catch((error) => { + if (!mountedRef.current) { + return + } + setError(error) + }) + .finally(() => { + if (!mountedRef.current) { + return + } + setSending(false) + }) }, [team]) const onChangeHandler = useCallback( diff --git a/src/cloud/components/organisms/settings/MembersTab.tsx b/src/cloud/components/organisms/settings/MembersTab.tsx index 1d8928e35f..d74aa2578e 100644 --- a/src/cloud/components/organisms/settings/MembersTab.tsx +++ b/src/cloud/components/organisms/settings/MembersTab.tsx @@ -86,21 +86,40 @@ const MembersTab = () => { const [showTeamPersonalForm, setShowTeamPersonalForm] = useState( false ) + const mountedRef = useRef(false) + + useEffect(() => { + mountedRef.current = true + return () => { + mountedRef.current = false + } + }, []) const fetchUserEmails = useCallback( async (teamId: string, ids: string[]) => { add('userEmails') - try { - const res = await getUserEmailsFromPermissions(teamId, ids) - setUserEmailsMap(() => - res.permissionEmails.reduce((acc, val) => { - acc.set(val.id, val.email) - return acc - }, new Map()) - ) - } catch (error) {} - currentUserEmailIds.current = ids - remove('userEmails') + getUserEmailsFromPermissions(teamId, ids) + .then((res) => { + if (!mountedRef.current) { + return + } + setUserEmailsMap(() => + res.permissionEmails.reduce((acc, val) => { + acc.set(val.id, val.email) + return acc + }, new Map()) + ) + }) + .catch(() => { + // + }) + .finally(() => { + if (!mountedRef.current) { + return + } + currentUserEmailIds.current = ids + remove('userEmails') + }) }, [add, remove] ) @@ -108,17 +127,25 @@ const MembersTab = () => { const fetchGuestsEmails = useCallback( async (teamId: string, ids: string[]) => { add('guestEmails') - try { - const res = await getGuestsEmails({ teamId }) - setGuestEmailsMap(() => - res.guestsEmails.reduce((acc, val) => { - acc.set(val.id, val.email) - return acc - }, new Map()) - ) - } catch (error) {} - currentGuestsEmailIds.current = ids - remove('guestEmails') + getGuestsEmails({ teamId }) + .then((res) => { + if (!mountedRef.current) { + return + } + setGuestEmailsMap(() => + res.guestsEmails.reduce((acc, val) => { + acc.set(val.id, val.email) + return acc + }, new Map()) + ) + }) + .catch(() => { + // + }) + .finally(() => { + currentGuestsEmailIds.current = ids + remove('guestEmails') + }) }, [add, remove] ) diff --git a/src/cloud/components/organisms/settings/SubscriptionTab.tsx b/src/cloud/components/organisms/settings/SubscriptionTab.tsx index b431ffa3fd..c3d321dc29 100644 --- a/src/cloud/components/organisms/settings/SubscriptionTab.tsx +++ b/src/cloud/components/organisms/settings/SubscriptionTab.tsx @@ -81,7 +81,7 @@ const SubscriptionTab = () => { title={t('settings.teamSubscription')} body={
-

+

{formtab == null ? ( { ) : null} )} -

+
} > diff --git a/src/cloud/components/organisms/settings/UpgradeTab.tsx b/src/cloud/components/organisms/settings/UpgradeTab.tsx index 2e4d5484b9..a9e5098ef1 100644 --- a/src/cloud/components/organisms/settings/UpgradeTab.tsx +++ b/src/cloud/components/organisms/settings/UpgradeTab.tsx @@ -115,7 +115,7 @@ const UpgradeTab = () => { title={t('settings.teamUpgrade')} body={
-

+

{currentUserPermissions.role !== 'admin' ? ( Only admins can access this content. @@ -138,7 +138,7 @@ const UpgradeTab = () => { ) )} -

+
} > From cf5efd147a56f9795d10b267bf27efd9db559247 Mon Sep 17 00:00:00 2001 From: davy-c Date: Wed, 19 May 2021 10:43:35 +0900 Subject: [PATCH 83/91] rename and fix ordering sidebar bookmarks --- src/cloud/components/Application.tsx | 865 +----------------- .../components/molecules/Editor/index.tsx | 8 +- .../components/molecules/NewDocButton.tsx | 4 +- .../components/organisms/DocPage/View.tsx | 8 +- .../components/organisms/FolderPage/index.tsx | 8 +- .../SideNavigatorFolderControls.tsx | 4 +- .../SideNavigatorWorkspaceControls.tsx | 4 +- .../ControlsContextMenu/FolderContextMenu.tsx | 4 +- .../organisms/WorkspacePage/index.tsx | 4 +- .../useCloudSidebarDnd.ts} | 26 +- .../lib/hooks/sidebar/useCloudSidebarTree.tsx | 802 ++++++++++++++++ .../{useCloudUpdater.ts => useCloudApi.ts} | 2 +- ...CloudUI.tsx => useCloudResourceModals.tsx} | 6 +- src/cloud/lib/mappers/topbarBreadcrumbs.ts | 5 +- 14 files changed, 853 insertions(+), 897 deletions(-) rename src/cloud/lib/hooks/{useCloudDnd.ts => sidebar/useCloudSidebarDnd.ts} (82%) create mode 100644 src/cloud/lib/hooks/sidebar/useCloudSidebarTree.tsx rename src/cloud/lib/hooks/{useCloudUpdater.ts => useCloudApi.ts} (99%) rename src/cloud/lib/hooks/{useCloudUI.tsx => useCloudResourceModals.tsx} (98%) diff --git a/src/cloud/components/Application.tsx b/src/cloud/components/Application.tsx index 78bb45ea02..c45465e71e 100644 --- a/src/cloud/components/Application.tsx +++ b/src/cloud/components/Application.tsx @@ -26,27 +26,16 @@ import { useNav } from '../lib/stores/nav' import EventSource from './organisms/EventSource' import ApplicationLayout from '../../shared/components/molecules/ApplicationLayout' import Sidebar from '../../shared/components/organisms/Sidebar' -import { - CollapsableType, - useSidebarCollapse, -} from '../lib/stores/sidebarCollapse' -import { - MenuItem, - MenuTypes, - useContextMenu, -} from '../../shared/lib/stores/contextMenu' +import { MenuTypes, useContextMenu } from '../../shared/lib/stores/contextMenu' import { useGlobalData } from '../lib/stores/globalData' import { getDocLinkHref } from './atoms/Link/DocLink' import { getFolderHref } from './atoms/Link/FolderLink' -import { getWorkspaceHref } from './atoms/Link/WorkspaceLink' -import { getTagHref } from './atoms/Link/TagLink' import { SidebarSearchHistory, SidebarSearchResult, } from '../../shared/components/organisms/Sidebar/molecules/SidebarSearch' import { SidebarState, - SidebarTreeSortingOrder, SidebarTreeSortingOrders, } from '../../shared/lib/sidebar' import useApi from '../../shared/lib/hooks/useApi' @@ -58,58 +47,24 @@ import { } from '../api/search' import { SidebarToolbarRow } from '../../shared/components/organisms/Sidebar/molecules/SidebarToolbar' import { mapUsers } from '../../shared/lib/mappers/users' -import { SerializedDoc, SerializedDocWithBookmark } from '../interfaces/db/doc' +import { SerializedDoc } from '../interfaces/db/doc' import { SerializedTeam } from '../interfaces/db/team' import { compareDateString } from '../../shared/lib/date' -import { - getDocId, - getDocTitle, - getFolderId, - getTeamURL, -} from '../lib/utils/patterns' +import { getDocTitle, getTeamURL } from '../lib/utils/patterns' import { mdiAccountMultiplePlusOutline, - mdiApplicationCog, - mdiArchiveOutline, mdiClockOutline, mdiCogOutline, mdiDownload, mdiFileDocumentMultipleOutline, mdiFileDocumentOutline, - mdiFilePlusOutline, - mdiFolderPlusOutline, - mdiLock, mdiLogoutVariant, mdiMagnify, - mdiPaperclip, - mdiPencil, - mdiPlus, mdiPlusCircleOutline, - mdiStar, - mdiStarOutline, - mdiTag, - mdiTrashCanOutline, - mdiWeb, } from '@mdi/js' import { getColorFromString } from '../../shared/lib/string' -import { - sortByAttributeAsc, - sortByAttributeDesc, -} from '../../shared/lib/utils/array' import { buildIconUrl } from '../api/files' -import { - SerializedFolder, - SerializedFolderWithBookmark, -} from '../interfaces/db/folder' -import { SerializedWorkspace } from '../interfaces/db/workspace' -import { SerializedTag } from '../interfaces/db/tag' -import { FoldingProps } from '../../shared/components/atoms/FoldingWrapper' -import { getMapValues } from '../../shared/lib/utils/array' -import { - SidebarNavCategory, - SidebarNavControls, - SidebarTreeChildRow, -} from '../../shared/components/organisms/Sidebar/molecules/SidebarTree' +import { SerializedFolder } from '../interfaces/db/folder' import RoundedImage from '../../shared/components/atoms/RoundedImage' import ImportModal from './organisms/Modal/contents/Import/ImportModal' import { SerializedTeamInvite } from '../interfaces/db/teamInvite' @@ -121,15 +76,8 @@ import ContentLayout, { ContentLayoutProps, } from '../../shared/components/templates/ContentLayout' import { getTeamLinkHref } from './atoms/Link/TeamLink' -import CreateWorkspaceModal from './organisms/Modal/contents/Workspace/CreateWorkspaceModal' -import { useCloudUpdater } from '../lib/hooks/useCloudUpdater' -import { CreateFolderRequestBody } from '../api/teams/folders' -import { CreateDocRequestBody } from '../api/teams/docs' -import { useCloudDnd } from '../lib/hooks/useCloudDnd' -import { NavResource } from '../interfaces/resources' -import { SidebarDragState } from '../../shared/lib/dnd' import cc from 'classcat' -import { useCloudUI } from '../lib/hooks/useCloudUI' +import { useCloudResourceModals } from '../lib/hooks/useCloudResourceModals' import { mapTopbarTree } from '../lib/mappers/topbarTree' import FuzzyNavigation from '../../shared/components/organisms/FuzzyNavigation' import { @@ -137,12 +85,8 @@ import { mapFuzzyNavigationRecentItems, } from '../lib/mappers/fuzzyNavigation' import { ModalOpeningOptions, useModal } from '../../shared/lib/stores/modal' -import { CreateWorkspaceRequestBody } from '../api/teams/workspaces' -import { - cloudSidebaCategoryLabels, - cloudSidebarOrderedCategoriesDelimiter, -} from '../lib/sidebar' import NewDocButton from './molecules/NewDocButton' +import { useCloudSidebarTree } from '../lib/hooks/sidebar/useCloudSidebarTree' interface ApplicationProps { content: ContentLayoutProps @@ -161,18 +105,9 @@ const Application = ({ docsMap, foldersMap, workspacesMap, - tagsMap, currentParentFolderId, currentWorkspaceId, } = useNav() - const { - sideBarOpenedLinksIdsSet, - sideBarOpenedFolderIdsSet, - sideBarOpenedWorkspaceIdsSet, - toggleItem, - unfoldItem, - foldItem, - } = useSidebarCollapse() const { team, permissions = [], @@ -195,14 +130,10 @@ const Application = ({ ) const { openSettingsTab, closeSettingsTab } = useSettings() const { usingElectron, sendToElectron } = useElectron() - const { - openRenameFolderForm, - openNewFolderForm, - openRenameDocForm, - openWorkspaceEditForm, - } = useCloudUI() + const { openNewFolderForm } = useCloudResourceModals() const [showFuzzyNavigation, setShowFuzzyNavigation] = useState(false) const { popup } = useContextMenu() + const { treeWithOrderedCategories } = useCloudSidebarTree() usePathnameChangeEffect(() => { setShowFuzzyNavigation(false) @@ -232,193 +163,11 @@ const Application = ({ setSidebarState((prev) => (prev === state ? undefined : state)) }, []) - const getFoldEvents = useCallback( - (type: CollapsableType, key: string, reversed?: boolean) => { - if (reversed) { - return { - fold: () => unfoldItem(type, key), - unfold: () => foldItem(type, key), - toggle: () => toggleItem(type, key), - } - } - - return { - fold: () => foldItem(type, key), - unfold: () => unfoldItem(type, key), - toggle: () => toggleItem(type, key), - } - }, - [toggleItem, unfoldItem, foldItem] - ) - const sidebarResize = useCallback( (width: number) => setPreferences({ sideBarWidth: width }), [setPreferences] ) - const { - draggedCategory, - draggedResource, - dropInDocOrFolder, - dropInWorkspace, - } = useCloudDnd() - const { - sendingMap: treeSendingMap, - createWorkspace, - createDoc, - createFolder, - toggleDocArchive, - toggleDocBookmark, - toggleFolderBookmark, - updateDoc, - updateFolder, - } = useCloudUpdater() - const { deleteWorkspace, deleteFolder } = useCloudUI() - - const tree = useMemo(() => { - return mapTree( - initialLoadDone, - preferences.sidebarTreeSortingOrder, - pathname, - docsMap, - foldersMap, - workspacesMap, - tagsMap, - treeSendingMap, - sideBarOpenedLinksIdsSet, - sideBarOpenedFolderIdsSet, - sideBarOpenedWorkspaceIdsSet, - toggleItem, - getFoldEvents, - push, - openModal, - toggleDocBookmark, - toggleFolderBookmark, - createWorkspace, - deleteWorkspace, - toggleDocArchive, - deleteFolder, - createFolder, - createDoc, - draggedResource, - dropInDocOrFolder, - (id: string) => dropInWorkspace(id, updateFolder, updateDoc), - openRenameFolderForm, - openRenameDocForm, - openWorkspaceEditForm, - team - ) - }, [ - initialLoadDone, - preferences.sidebarTreeSortingOrder, - pathname, - docsMap, - foldersMap, - workspacesMap, - tagsMap, - treeSendingMap, - sideBarOpenedFolderIdsSet, - sideBarOpenedLinksIdsSet, - sideBarOpenedWorkspaceIdsSet, - toggleItem, - getFoldEvents, - push, - openModal, - toggleDocBookmark, - toggleFolderBookmark, - createWorkspace, - deleteWorkspace, - toggleDocArchive, - deleteFolder, - createFolder, - createDoc, - dropInDocOrFolder, - dropInWorkspace, - updateFolder, - updateDoc, - openRenameFolderForm, - openRenameDocForm, - openWorkspaceEditForm, - draggedResource, - team, - ]) - - const treeWithOrderedCategories = useMemo(() => { - if (tree == null) { - return undefined - } - - const orderedCategories = Array.from( - new Set([ - ...preferences.sidebarOrderedCategories.split( - cloudSidebarOrderedCategoriesDelimiter - ), - ...cloudSidebaCategoryLabels, - ]) - ).filter((item) => - cloudSidebaCategoryLabels.find((categoryLabel) => categoryLabel === item) - ) - - const orderedTree = tree.sort((categoryA, categoryB) => { - if ( - orderedCategories.indexOf(categoryA.label) > - orderedCategories.indexOf(categoryB.label) - ) { - return 1 - } else { - return -1 - } - }) - - orderedTree.forEach((category) => { - category.drag = { - onDragStart: () => { - draggedCategory.current = category.label - }, - onDragEnd: () => { - draggedCategory.current = undefined - }, - onDrop: () => { - if (draggedCategory.current == null) { - return - } - const orderedItems = orderedCategories.splice(0) - const categoryIndex = orderedItems.includes(category.label) - ? orderedItems.indexOf(category.label) - : orderedItems.length - 1 - - const reArrangedArray = orderedItems.reduce((acc, val, i) => { - if (i === categoryIndex) { - acc.push(draggedCategory.current!) - } - - if (i !== categoryIndex && val === draggedCategory.current) { - return acc - } - - acc.push(val) - return acc - }, [] as string[]) - - setPreferences({ - sidebarOrderedCategories: reArrangedArray.join( - cloudSidebarOrderedCategoriesDelimiter - ), - }) - - draggedCategory.current = undefined - }, - } - }) - - return orderedTree - }, [ - tree, - preferences.sidebarOrderedCategories, - setPreferences, - draggedCategory, - ]) - const users = useMemo(() => { return mapUsers(permissions, currentUser, [...guestsMap.values()]) }, [permissions, currentUser, guestsMap]) @@ -861,537 +610,6 @@ function mapHistory( return items } -function mapTree( - initialLoadDone: boolean, - sortingOrder: SidebarTreeSortingOrder, - currentPath: string, - docsMap: Map, - foldersMap: Map, - workspacesMap: Map, - tagsMap: Map, - treeSendingMap: Map, - sideBarOpenedLinksIdsSet: Set, - sideBarOpenedFolderIdsSet: Set, - sideBarOpenedWorkspaceIdsSet: Set, - toggleItem: (type: CollapsableType, id: string) => void, - getFoldEvents: ( - type: CollapsableType, - key: string, - reversed?: boolean - ) => FoldingProps, - push: (url: string) => void, - openModal: (cmp: JSX.Element) => void, - toggleDocBookmark: ( - teamId: string, - docId: string, - bookmarked: boolean - ) => void, - toggleFolderBookmark: ( - teamId: string, - id: string, - bookmarked: boolean - ) => void, - createWorkspace: ( - team: SerializedTeam, - body: CreateWorkspaceRequestBody, - options: { - skipRedirect?: boolean - afterSuccess?: (workspace: SerializedWorkspace) => void - } - ) => void, - deleteWorkspace: (wp: SerializedWorkspace) => void, - toggleDocArchive: ( - teamId: string, - docId: string, - archivedAt?: string - ) => void, - deleteFolder: (folder: SerializedFolder) => void, - createFolder: ( - team: SerializedTeam, - body: CreateFolderRequestBody - ) => Promise, - createDoc: ( - team: SerializedTeam, - body: CreateDocRequestBody - ) => Promise, - draggedResource: React.MutableRefObject, - dropInFolderOrDoc: ( - targetedResource: NavResource, - targetedPosition: SidebarDragState - ) => void, - dropInWorkspace: (id: string) => void, - openRenameFolderForm: (folder: SerializedFolder) => void, - openRenameDocForm: (doc: SerializedDoc) => void, - openWorkspaceEditForm: (wp: SerializedWorkspace) => void, - team?: SerializedTeam -) { - if (!initialLoadDone || team == null) { - return undefined - } - - const currentPathWithDomain = `${process.env.BOOST_HUB_BASE_URL}${currentPath}` - const items = new Map() - - const [docs, folders, workspaces] = [ - getMapValues(docsMap), - getMapValues(foldersMap), - getMapValues(workspacesMap), - ] - - let personalWorkspace: SerializedWorkspace | undefined - workspaces.forEach((wp) => { - if (wp.personal) { - personalWorkspace = wp - return - } - - const href = `${process.env.BOOST_HUB_BASE_URL}${getWorkspaceHref( - wp, - team, - 'index' - )}` - items.set(wp.id, { - id: wp.id, - lastUpdated: wp.updatedAt, - label: wp.name, - defaultIcon: !wp.public ? mdiLock : undefined, - children: wp.positions?.orderedIds || [], - folded: !sideBarOpenedWorkspaceIdsSet.has(wp.id), - folding: getFoldEvents('workspaces', wp.id), - href, - active: href === currentPathWithDomain, - navigateTo: () => push(href), - dropIn: true, - onDrop: () => dropInWorkspace(wp.id), - controls: [ - { - icon: mdiFilePlusOutline, - onClick: undefined, - placeholder: 'Doc title..', - create: (title: string) => - createDoc(team, { - workspaceId: wp.id, - title, - }), - }, - { - icon: mdiFolderPlusOutline, - onClick: undefined, - placeholder: 'Folder name..', - create: (folderName: string) => - createFolder(team, { - workspaceId: wp.id, - description: '', - folderName, - }), - }, - ], - contextControls: wp.default - ? [ - { - type: MenuTypes.Normal, - icon: mdiApplicationCog, - label: 'Edit', - onClick: () => openWorkspaceEditForm(wp), - }, - ] - : [ - { - type: MenuTypes.Normal, - icon: mdiApplicationCog, - label: 'Edit', - onClick: () => openWorkspaceEditForm(wp), - }, - { - type: MenuTypes.Normal, - icon: mdiTrashCanOutline, - label: 'Delete', - onClick: () => deleteWorkspace(wp), - }, - ], - }) - }) - - folders.forEach((folder) => { - const folderId = getFolderId(folder) - const href = `${process.env.BOOST_HUB_BASE_URL}${getFolderHref( - folder, - team, - 'index' - )}` - items.set(folderId, { - id: folderId, - lastUpdated: folder.updatedAt, - label: folder.name, - bookmarked: folder.bookmarked, - emoji: folder.emoji, - folded: !sideBarOpenedFolderIdsSet.has(folder.id), - folding: getFoldEvents('folders', folder.id), - href, - active: href === currentPathWithDomain, - navigateTo: () => push(href), - onDrop: (position: SidebarDragState) => - dropInFolderOrDoc({ type: 'folder', result: folder }, position), - onDragStart: () => { - draggedResource.current = { type: 'folder', result: folder } - }, - onDragEnd: () => { - draggedResource.current = undefined - }, - dropIn: true, - dropAround: sortingOrder === 'drag' ? true : false, - controls: [ - { - icon: mdiFilePlusOutline, - onClick: undefined, - placeholder: 'Doc title..', - create: (title: string) => - createDoc(team, { - parentFolderId: folder.id, - workspaceId: folder.workspaceId, - title, - }), - }, - { - icon: mdiFolderPlusOutline, - onClick: undefined, - placeholder: 'Folder name..', - create: (folderName: string) => - createFolder(team, { - parentFolderId: folder.id, - workspaceId: folder.workspaceId, - description: '', - folderName, - }), - }, - ], - contextControls: [ - { - type: MenuTypes.Normal, - icon: folder.bookmarked ? mdiStar : mdiStarOutline, - label: - treeSendingMap.get(folder.id) === 'bookmark' - ? '...' - : folder.bookmarked - ? 'Bookmarked' - : 'Bookmark', - onClick: () => - toggleFolderBookmark(folder.teamId, folder.id, folder.bookmarked), - }, - { - type: MenuTypes.Normal, - icon: mdiPencil, - label: 'Rename', - onClick: () => openRenameFolderForm(folder), - }, - { - type: MenuTypes.Normal, - icon: mdiTrashCanOutline, - label: 'Delete', - onClick: () => deleteFolder(folder), - }, - ], - parentId: - folder.parentFolderId == null - ? folder.workspaceId - : folder.parentFolderId, - children: - typeof folder.positions != null && typeof folder.positions !== 'string' - ? folder.positions.orderedIds - : [], - }) - }) - - docs.forEach((doc) => { - const docId = getDocId(doc) - const href = `${process.env.BOOST_HUB_BASE_URL}${getDocLinkHref( - doc, - team, - 'index' - )}` - items.set(docId, { - id: docId, - lastUpdated: doc.head != null ? doc.head.created : doc.updatedAt, - label: getDocTitle(doc, 'Untitled'), - bookmarked: doc.bookmarked, - emoji: doc.emoji, - defaultIcon: mdiFileDocumentOutline, - archived: doc.archivedAt != null, - children: [], - href, - active: href === currentPathWithDomain, - dropAround: sortingOrder === 'drag' ? true : false, - navigateTo: () => push(href), - onDrop: (position: SidebarDragState) => - dropInFolderOrDoc({ type: 'doc', result: doc }, position), - onDragStart: () => { - draggedResource.current = { type: 'doc', result: doc } - }, - onDragEnd: () => { - draggedResource.current = undefined - }, - contextControls: [ - { - type: MenuTypes.Normal, - icon: doc.bookmarked ? mdiStar : mdiStarOutline, - label: - treeSendingMap.get(doc.id) === 'bookmark' - ? '...' - : doc.bookmarked - ? 'Bookmarked' - : 'Bookmark', - onClick: () => toggleDocBookmark(doc.teamId, doc.id, doc.bookmarked), - }, - { - type: MenuTypes.Normal, - icon: mdiPencil, - label: 'Rename', - onClick: () => openRenameDocForm(doc), - }, - { - type: MenuTypes.Normal, - icon: mdiArchiveOutline, - label: doc.archivedAt == null ? 'Archive' : 'Restore', - onClick: () => toggleDocArchive(doc.teamId, doc.id, doc.archivedAt), - }, - ], - parentId: - doc.parentFolderId == null ? doc.workspaceId : doc.parentFolderId, - }) - }) - - const arrayItems = getMapValues(items) - const tree: Partial[] = [] - - const bookmarked = arrayItems.reduce((acc, val) => { - if (!val.bookmarked) { - return acc - } - - acc.push({ - id: val.id, - depth: 0, - label: val.label, - emoji: val.emoji, - defaultIcon: val.defaultIcon, - href: val.href, - navigateTo: val.navigateTo, - contextControls: val.contextControls, - }) - return acc - }, [] as SidebarTreeChildRow[]) - - const navTree = arrayItems - .filter((item) => item.parentId == null) - .reduce((acc, val) => { - acc.push({ - ...val, - depth: 0, - rows: buildChildrenNavRows(sortingOrder, val.children, 1, items), - }) - return acc - }, [] as SidebarTreeChildRow[]) - - const docsPerTagIdMap = [...docsMap.values()].reduce((acc, doc) => { - const docTags = doc.tags || [] - docTags.forEach((tag) => { - let docIds = acc.get(tag.id) - if (docIds == null) { - docIds = [] - acc.set(tag.id, docIds) - } - docIds.push(doc.id) - }) - return acc - }, new Map()) - - const labels = getMapValues(tagsMap) - .filter((tag) => (docsPerTagIdMap.get(tag.id) || []).length > 0) - .sort((a, b) => { - if (a.text < b.text) { - return -1 - } else { - return 1 - } - }) - .reduce((acc, val) => { - const href = `${process.env.BOOST_HUB_BASE_URL}${getTagHref( - val, - team, - 'index' - )}` - acc.push({ - id: val.id, - depth: 0, - label: val.text, - defaultIcon: mdiTag, - href, - active: href === currentPathWithDomain, - navigateTo: () => push(href), - }) - return acc - }, [] as SidebarTreeChildRow[]) - - if (bookmarked.length > 0) { - tree.push({ - label: 'Bookmarks', - rows: bookmarked, - }) - } - tree.push({ - label: 'Workspaces', - rows: navTree, - controls: [ - { - icon: mdiPlus, - onClick: () => openModal(), - }, - ], - }) - - if (!team.personal) { - tree.push({ - label: 'Private', - rows: - personalWorkspace != null - ? arrayItems - .filter((item) => item.parentId === personalWorkspace!.id) - .reduce((acc, val) => { - acc.push({ - ...val, - depth: 0, - rows: buildChildrenNavRows( - sortingOrder, - val.children, - 1, - items - ), - }) - return acc - }, [] as SidebarTreeChildRow[]) - : [], - controls: [ - { - icon: mdiFilePlusOutline, - onClick: undefined, - placeholder: 'Doc title..', - create: async (title: string) => { - if (personalWorkspace == null) { - return createWorkspace( - team, - { - personal: true, - name: 'Private', - permissions: [], - public: false, - }, - { - skipRedirect: true, - afterSuccess: (wp) => - createDoc(team, { - workspaceId: wp.id, - title, - }), - } - ) - } - - return createDoc(team, { - workspaceId: personalWorkspace!.id, - title, - }) - }, - }, - { - icon: mdiFolderPlusOutline, - onClick: undefined, - placeholder: 'Folder name..', - create: async (folderName: string) => { - if (personalWorkspace == null) { - return createWorkspace( - team, - { - personal: true, - name: 'Private', - permissions: [], - public: false, - }, - { - skipRedirect: true, - afterSuccess: (wp) => - createFolder(team, { - workspaceId: wp.id, - description: '', - folderName, - }), - } - ) - } - - return createFolder(team, { - workspaceId: personalWorkspace!.id, - description: '', - folderName, - }) - }, - }, - ], - }) - } - - if (labels.length > 0) { - tree.push({ - label: 'Labels', - rows: labels, - }) - } - - tree.push({ - label: 'More', - rows: [ - { - id: 'sidenav-attachment', - label: 'Attachments', - defaultIcon: mdiPaperclip, - href: getTeamLinkHref(team, 'uploads'), - active: getTeamLinkHref(team, 'uploads') === currentPath, - navigateTo: () => push(getTeamLinkHref(team, 'uploads')), - depth: 0, - }, - { - id: 'sidenav-shared', - label: 'Shared', - defaultIcon: mdiWeb, - href: getTeamLinkHref(team, 'shared'), - active: getTeamLinkHref(team, 'shared') === currentPath, - navigateTo: () => push(getTeamLinkHref(team, 'shared')), - depth: 0, - }, - { - id: 'sidenav-archived', - label: 'Archived', - defaultIcon: mdiArchiveOutline, - href: getTeamLinkHref(team, 'archived'), - active: getTeamLinkHref(team, 'archived') === currentPath, - navigateTo: () => push(getTeamLinkHref(team, 'archived')), - depth: 0, - }, - ], - }) - - tree.forEach((category) => { - const key = (category.label || '').toLocaleLowerCase() - const foldKey = `fold-${key}` - const hideKey = `hide-${key}` - category.folded = sideBarOpenedLinksIdsSet.has(foldKey) - category.folding = getFoldEvents('links', foldKey, true) - category.hidden = sideBarOpenedLinksIdsSet.has(hideKey) - category.toggleHidden = () => toggleItem('links', hideKey) - }) - - return tree as SidebarNavCategory[] -} - function mapToolbarRows( showSpaces: boolean, setShowSpaces: React.Dispatch>, @@ -1546,73 +764,6 @@ function buildSpacesBottomRows(push: (url: string) => void) { ] } -function buildChildrenNavRows( - sortingOrder: SidebarTreeSortingOrder, - childrenIds: string[], - depth: number, - map: Map -) { - const rows = childrenIds.reduce((acc, childId) => { - const childRow = map.get(childId) - if (childRow == null) { - return acc - } - - if (childRow.archived) { - return acc - } - - acc.push({ - ...childRow, - depth, - rows: buildChildrenNavRows( - sortingOrder, - childRow.children, - depth + 1, - map - ), - }) - - return acc - }, [] as (SidebarTreeChildRow & { lastUpdated: string })[]) - - switch (sortingOrder) { - case 'a-z': - return sortByAttributeAsc('label', rows) - case 'z-a': - return sortByAttributeDesc('label', rows) - case 'last-updated': - return sortByAttributeDesc('lastUpdated', rows) - case 'drag': - default: - return rows - } -} - -type CloudTreeItem = { - id: string - parentId?: string - label: string - defaultIcon?: string - emoji?: string - bookmarked?: boolean - archived?: boolean - children: string[] - folding?: FoldingProps - folded?: boolean - href?: string - active?: boolean - lastUpdated: string - navigateTo?: () => void - controls?: SidebarNavControls[] - contextControls?: MenuItem[] - dropIn?: boolean - dropAround?: boolean - onDragStart?: () => void - onDrop?: (position?: SidebarDragState) => void - onDragEnd?: () => void -} - function isCodeMirrorTextAreaEvent(event: KeyboardEvent) { const target = event.target as HTMLTextAreaElement if (target == null || target.tagName.toLowerCase() !== 'textarea') { diff --git a/src/cloud/components/molecules/Editor/index.tsx b/src/cloud/components/molecules/Editor/index.tsx index 89dbeb8d34..30b453ddd3 100644 --- a/src/cloud/components/molecules/Editor/index.tsx +++ b/src/cloud/components/molecules/Editor/index.tsx @@ -73,8 +73,8 @@ import { useToast } from '../../../../shared/lib/stores/toast' import { LoadingButton } from '../../../../shared/components/atoms/Button' import { trackEvent } from '../../../api/track' import { MixpanelActionTrackTypes } from '../../../interfaces/analytics/mixpanel' -import { useCloudUpdater } from '../../../lib/hooks/useCloudUpdater' -import { useCloudUI } from '../../../lib/hooks/useCloudUI' +import { useCloudApi } from '../../../lib/hooks/useCloudApi' +import { useCloudResourceModals } from '../../../lib/hooks/useCloudResourceModals' import { mapTopbarBreadcrumbs } from '../../../lib/mappers/topbarBreadcrumbs' import { useModal } from '../../../../shared/lib/stores/modal' @@ -151,7 +151,7 @@ const Editor = ({ }) const { docsMap, workspacesMap, foldersMap } = useNav() const suggestionsRef = useRef([]) - const { sendingMap, toggleDocBookmark } = useCloudUpdater() + const { sendingMap, toggleDocBookmark } = useCloudApi() const { openRenameDocForm, openRenameFolderForm, @@ -161,7 +161,7 @@ const Editor = ({ deleteOrArchiveDoc, deleteFolder, deleteWorkspace, - } = useCloudUI() + } = useCloudResourceModals() const userInfo = useMemo(() => { return { diff --git a/src/cloud/components/molecules/NewDocButton.tsx b/src/cloud/components/molecules/NewDocButton.tsx index 682484b267..243231732f 100644 --- a/src/cloud/components/molecules/NewDocButton.tsx +++ b/src/cloud/components/molecules/NewDocButton.tsx @@ -13,7 +13,7 @@ import { import { useModal } from '../../../shared/lib/stores/modal' import styled from '../../../shared/lib/styled' import { SerializedTeam } from '../../interfaces/db/team' -import { useCloudUI } from '../../lib/hooks/useCloudUI' +import { useCloudResourceModals } from '../../lib/hooks/useCloudResourceModals' import { useNav } from '../../lib/stores/nav' import TemplatesModal from '../organisms/Modal/contents/TemplatesModal' @@ -24,7 +24,7 @@ const NewDocButton = ({ team }: { team: SerializedTeam }) => { currentPath, currentParentFolderId, } = useNav() - const { openNewDocForm } = useCloudUI() + const { openNewDocForm } = useCloudResourceModals() const { popup } = useContextMenu() const { openModal } = useModal() diff --git a/src/cloud/components/organisms/DocPage/View.tsx b/src/cloud/components/organisms/DocPage/View.tsx index cef0725d74..dc24bc44d0 100644 --- a/src/cloud/components/organisms/DocPage/View.tsx +++ b/src/cloud/components/organisms/DocPage/View.tsx @@ -26,8 +26,8 @@ import { useToast } from '../../../../shared/lib/stores/toast' import { useRouter } from '../../../lib/router' import { LoadingButton } from '../../../../shared/components/atoms/Button' import { mdiStar, mdiStarOutline } from '@mdi/js' -import { useCloudUpdater } from '../../../lib/hooks/useCloudUpdater' -import { useCloudUI } from '../../../lib/hooks/useCloudUI' +import { useCloudApi } from '../../../lib/hooks/useCloudApi' +import { useCloudResourceModals } from '../../../lib/hooks/useCloudResourceModals' import { mapTopbarBreadcrumbs } from '../../../lib/mappers/topbarBreadcrumbs' interface ViewPageProps { @@ -56,7 +56,7 @@ const ViewPage = ({ const { setPartialPageData, currentUserPermissions } = usePage() const { pushMessage } = useToast() const { preferences } = usePreferences() - const { sendingMap, toggleDocBookmark } = useCloudUpdater() + const { sendingMap, toggleDocBookmark } = useCloudApi() const { openRenameDocForm, openRenameFolderForm, @@ -66,7 +66,7 @@ const ViewPage = ({ deleteOrArchiveDoc: deleteDoc, deleteFolder, deleteWorkspace, - } = useCloudUI() + } = useCloudResourceModals() const unarchiveHandler = useCallback(async () => { try { diff --git a/src/cloud/components/organisms/FolderPage/index.tsx b/src/cloud/components/organisms/FolderPage/index.tsx index a5fb965eeb..8dce741c0f 100644 --- a/src/cloud/components/organisms/FolderPage/index.tsx +++ b/src/cloud/components/organisms/FolderPage/index.tsx @@ -33,8 +33,8 @@ import { useRouter } from '../../../lib/router' import { LoadingButton } from '../../../../shared/components/atoms/Button' import FolderContextMenu from '../Topbar/Controls/ControlsContextMenu/FolderContextMenu' import FlattenedBreadcrumbs from '../../../../shared/components/molecules/FlattenedBreadcrumbs' -import { useCloudUI } from '../../../lib/hooks/useCloudUI' -import { useCloudUpdater } from '../../../lib/hooks/useCloudUpdater' +import { useCloudResourceModals } from '../../../lib/hooks/useCloudResourceModals' +import { useCloudApi } from '../../../lib/hooks/useCloudApi' import { mapTopbarBreadcrumbs } from '../../../lib/mappers/topbarBreadcrumbs' enum FolderHeaderActions { @@ -53,7 +53,7 @@ const FolderPage = () => { } = useNav() const { openEmojiPicker } = useEmojiPicker() const [sending, setSending] = useState() - const { toggleFolderBookmark, sendingMap } = useCloudUpdater() + const { toggleFolderBookmark, sendingMap } = useCloudApi() const { push } = useRouter() const [showContextMenu, setShowContextMenu] = useState(false) const { @@ -65,7 +65,7 @@ const FolderPage = () => { openWorkspaceEditForm, deleteOrArchiveDoc, deleteWorkspace, - } = useCloudUI() + } = useCloudResourceModals() const currentFolder = useMemo(() => { if (pageFolder == null) { diff --git a/src/cloud/components/organisms/Sidebar/SideNavigator/SideNavigatorFolderControls.tsx b/src/cloud/components/organisms/Sidebar/SideNavigator/SideNavigatorFolderControls.tsx index 5e40836a21..beb56528fc 100644 --- a/src/cloud/components/organisms/Sidebar/SideNavigator/SideNavigatorFolderControls.tsx +++ b/src/cloud/components/organisms/Sidebar/SideNavigator/SideNavigatorFolderControls.tsx @@ -36,7 +36,7 @@ import SideNavigatorIconButton from './SideNavigatorIconButton' import Tooltip from '../../../atoms/Tooltip' import IconMdi from '../../../atoms/IconMdi' import { useToast } from '../../../../../shared/lib/stores/toast' -import { useCloudUI } from '../../../../lib/hooks/useCloudUI' +import { useCloudResourceModals } from '../../../../lib/hooks/useCloudResourceModals' interface SideNavigatorFolderControlsProps { folder: SerializedFolderWithBookmark @@ -53,7 +53,7 @@ const SideNavigatorFolderControls = ({ const { pushMessage, pushApiErrorMessage } = useToast() const { popup } = useContextMenu() const [sendingBookmark, setSendingBookmark] = useState(false) - const { openRenameFolderForm } = useCloudUI() + const { openRenameFolderForm } = useCloudResourceModals() const toggleBookmark = useCallback(async () => { if (sendingBookmark) { diff --git a/src/cloud/components/organisms/Sidebar/SidebarWorkspaces/SideNavigatorWorkspaceControls.tsx b/src/cloud/components/organisms/Sidebar/SidebarWorkspaces/SideNavigatorWorkspaceControls.tsx index 622ac858a7..6999b2e1e9 100644 --- a/src/cloud/components/organisms/Sidebar/SidebarWorkspaces/SideNavigatorWorkspaceControls.tsx +++ b/src/cloud/components/organisms/Sidebar/SidebarWorkspaces/SideNavigatorWorkspaceControls.tsx @@ -26,7 +26,7 @@ import { useGlobalData } from '../../../../lib/stores/globalData' import { usePage } from '../../../../lib/stores/pageStore' import IconMdi from '../../../atoms/IconMdi' import { useToast } from '../../../../../shared/lib/stores/toast' -import { useCloudUI } from '../../../../lib/hooks/useCloudUI' +import { useCloudResourceModals } from '../../../../lib/hooks/useCloudResourceModals' interface SideNavigatorWorkspaceControlsProps { workspace: SerializedWorkspace @@ -57,7 +57,7 @@ const SideNavigatorWorkspaceControls = ({ const { popup } = useContextMenu() const { messageBox } = useDialog() const { pushApiErrorMessage, pushMessage } = useToast() - const { openWorkspaceEditForm } = useCloudUI() + const { openWorkspaceEditForm } = useCloudResourceModals() const createChildDoc = useCallback(async () => { try { diff --git a/src/cloud/components/organisms/Topbar/Controls/ControlsContextMenu/FolderContextMenu.tsx b/src/cloud/components/organisms/Topbar/Controls/ControlsContextMenu/FolderContextMenu.tsx index 43ab53861c..6907843e54 100644 --- a/src/cloud/components/organisms/Topbar/Controls/ControlsContextMenu/FolderContextMenu.tsx +++ b/src/cloud/components/organisms/Topbar/Controls/ControlsContextMenu/FolderContextMenu.tsx @@ -29,7 +29,7 @@ import { MetaKeyText } from '../../../../../lib/keyboard' import IconMdi from '../../../../atoms/IconMdi' import { mdiStar, mdiTrashCan, mdiStarOutline, mdiPencil } from '@mdi/js' import { useToast } from '../../../../../../shared/lib/stores/toast' -import { useCloudUI } from '../../../../../lib/hooks/useCloudUI' +import { useCloudResourceModals } from '../../../../../lib/hooks/useCloudResourceModals' interface FolderContextMenuProps { currentFolder: SerializedFolderWithBookmark @@ -44,7 +44,7 @@ const FolderContextMenu = ({ const { updateFoldersMap, deleteFolderHandler } = useNav() const { setPartialPageData } = usePage() const { pushMessage } = useToast() - const { openRenameFolderForm } = useCloudUI() + const { openRenameFolderForm } = useCloudResourceModals() const menuRef = React.createRef() useEffectOnce(() => { diff --git a/src/cloud/components/organisms/WorkspacePage/index.tsx b/src/cloud/components/organisms/WorkspacePage/index.tsx index 863d0369b5..26248f4c9d 100644 --- a/src/cloud/components/organisms/WorkspacePage/index.tsx +++ b/src/cloud/components/organisms/WorkspacePage/index.tsx @@ -13,7 +13,7 @@ import ContentManager from '../../molecules/ContentManager' import Application from '../../Application' import { useRouter } from '../../../lib/router' import FlattenedBreadcrumbs from '../../../../shared/components/molecules/FlattenedBreadcrumbs' -import { useCloudUI } from '../../../lib/hooks/useCloudUI' +import { useCloudResourceModals } from '../../../lib/hooks/useCloudResourceModals' import { mapWorkspaceBreadcrumb } from '../../../lib/mappers/topbarBreadcrumbs' interface WorkspacePage { @@ -35,7 +35,7 @@ const WorkspacePage = ({ workspace }: WorkspacePage) => { openNewDocForm, openWorkspaceEditForm, deleteWorkspace, - } = useCloudUI() + } = useCloudResourceModals() const topbarBreadcrumbs = useMemo(() => { if (team == null) { diff --git a/src/cloud/lib/hooks/useCloudDnd.ts b/src/cloud/lib/hooks/sidebar/useCloudSidebarDnd.ts similarity index 82% rename from src/cloud/lib/hooks/useCloudDnd.ts rename to src/cloud/lib/hooks/sidebar/useCloudSidebarDnd.ts index afe229b13a..061fee1598 100644 --- a/src/cloud/lib/hooks/useCloudDnd.ts +++ b/src/cloud/lib/hooks/sidebar/useCloudSidebarDnd.ts @@ -1,18 +1,18 @@ import { useCallback, useRef } from 'react' -import { UpdateDocRequestBody } from '../../api/teams/docs' -import { UpdateFolderRequestBody } from '../../api/teams/folders' -import { moveResource } from '../../api/teams/resources' -import { SerializedDoc } from '../../interfaces/db/doc' -import { SerializedFolder } from '../../interfaces/db/folder' -import { NavResource } from '../../interfaces/resources' -import { useNav } from '../stores/nav' -import { usePage } from '../stores/pageStore' -import { getResourceId } from '../utils/patterns' -import { SidebarDragState } from '../../../shared/lib/dnd' -import { useToast } from '../../../shared/lib/stores/toast' -import { getMapFromEntityArray } from '../../../shared/lib/utils/array' +import { UpdateDocRequestBody } from '../../../api/teams/docs' +import { UpdateFolderRequestBody } from '../../../api/teams/folders' +import { moveResource } from '../../../api/teams/resources' +import { SerializedDoc } from '../../../interfaces/db/doc' +import { SerializedFolder } from '../../../interfaces/db/folder' +import { NavResource } from '../../../interfaces/resources' +import { useNav } from '../../stores/nav' +import { usePage } from '../../stores/pageStore' +import { getResourceId } from '../../utils/patterns' +import { SidebarDragState } from '../../../../shared/lib/dnd' +import { useToast } from '../../../../shared/lib/stores/toast' +import { getMapFromEntityArray } from '../../../../shared/lib/utils/array' -export function useCloudDnd() { +export function useCloudSidebarDnd() { const draggedCategory = useRef() const draggedResource = useRef() const { diff --git a/src/cloud/lib/hooks/sidebar/useCloudSidebarTree.tsx b/src/cloud/lib/hooks/sidebar/useCloudSidebarTree.tsx new file mode 100644 index 0000000000..98b9dfefa2 --- /dev/null +++ b/src/cloud/lib/hooks/sidebar/useCloudSidebarTree.tsx @@ -0,0 +1,802 @@ +import React, { useCallback, useMemo } from 'react' +import { + mdiApplicationCog, + mdiArchiveOutline, + mdiFileDocumentOutline, + mdiFilePlusOutline, + mdiFolderPlusOutline, + mdiLock, + mdiPaperclip, + mdiPencil, + mdiPlus, + mdiStar, + mdiStarOutline, + mdiTag, + mdiTrashCanOutline, + mdiWeb, +} from '@mdi/js' +import { FoldingProps } from '../../../../shared/components/atoms/FoldingWrapper' +import { + SidebarNavCategory, + SidebarNavControls, + SidebarTreeChildRow, +} from '../../../../shared/components/organisms/Sidebar/molecules/SidebarTree' +import { SidebarDragState } from '../../../../shared/lib/dnd' +import { SidebarTreeSortingOrder } from '../../../../shared/lib/sidebar' +import { MenuItem, MenuTypes } from '../../../../shared/lib/stores/contextMenu' +import { useModal } from '../../../../shared/lib/stores/modal' +import { + getMapValues, + sortByAttributeAsc, + sortByAttributeDesc, +} from '../../../../shared/lib/utils/array' +import { getDocLinkHref } from '../../../components/atoms/Link/DocLink' +import { getFolderHref } from '../../../components/atoms/Link/FolderLink' +import { getTagHref } from '../../../components/atoms/Link/TagLink' +import { getTeamLinkHref } from '../../../components/atoms/Link/TeamLink' +import { getWorkspaceHref } from '../../../components/atoms/Link/WorkspaceLink' +import CreateWorkspaceModal from '../../../components/organisms/Modal/contents/Workspace/CreateWorkspaceModal' +import { SerializedWorkspace } from '../../../interfaces/db/workspace' +import { useRouter } from '../../router' +import { + cloudSidebaCategoryLabels, + cloudSidebarOrderedCategoriesDelimiter, +} from '../../sidebar' +import { useNav } from '../../stores/nav' +import { usePage } from '../../stores/pageStore' +import { usePreferences } from '../../stores/preferences' +import { + CollapsableType, + useSidebarCollapse, +} from '../../stores/sidebarCollapse' +import { getDocId, getDocTitle, getFolderId } from '../../utils/patterns' +import { useCloudApi } from '../useCloudApi' +import { useCloudResourceModals } from '../useCloudResourceModals' +import { useCloudSidebarDnd } from './useCloudSidebarDnd' + +export function useCloudSidebarTree() { + const { team } = usePage() + const { push, pathname } = useRouter() + const { openModal } = useModal() + const { preferences, setPreferences } = usePreferences() + + const { + initialLoadDone, + tagsMap, + docsMap, + foldersMap, + workspacesMap, + } = useNav() + + const { + sideBarOpenedLinksIdsSet, + sideBarOpenedFolderIdsSet, + sideBarOpenedWorkspaceIdsSet, + toggleItem, + unfoldItem, + foldItem, + } = useSidebarCollapse() + + const { + draggedCategory, + draggedResource, + dropInDocOrFolder, + dropInWorkspace, + } = useCloudSidebarDnd() + + const { + sendingMap: treeSendingMap, + createWorkspace, + createDoc, + createFolder, + toggleDocArchive, + toggleDocBookmark, + toggleFolderBookmark, + updateDoc, + updateFolder, + } = useCloudApi() + + const { + deleteWorkspace, + deleteFolder, + openRenameFolderForm, + openRenameDocForm, + openWorkspaceEditForm, + } = useCloudResourceModals() + + const getFoldEvents = useCallback( + (type: CollapsableType, key: string, reversed?: boolean) => { + if (reversed) { + return { + fold: () => unfoldItem(type, key), + unfold: () => foldItem(type, key), + toggle: () => toggleItem(type, key), + } + } + + return { + fold: () => foldItem(type, key), + unfold: () => unfoldItem(type, key), + toggle: () => toggleItem(type, key), + } + }, + [toggleItem, unfoldItem, foldItem] + ) + + const tree = useMemo(() => { + if (!initialLoadDone || team == null) { + return undefined + } + + const currentPathWithDomain = `${process.env.BOOST_HUB_BASE_URL}${pathname}` + const items = new Map() + + const [docs, folders, workspaces] = [ + getMapValues(docsMap), + getMapValues(foldersMap), + getMapValues(workspacesMap), + ] + + let personalWorkspace: SerializedWorkspace | undefined + workspaces.forEach((wp) => { + if (wp.personal) { + personalWorkspace = wp + return + } + + const href = `${process.env.BOOST_HUB_BASE_URL}${getWorkspaceHref( + wp, + team, + 'index' + )}` + items.set(wp.id, { + id: wp.id, + lastUpdated: wp.updatedAt, + label: wp.name, + defaultIcon: !wp.public ? mdiLock : undefined, + children: wp.positions?.orderedIds || [], + folded: !sideBarOpenedWorkspaceIdsSet.has(wp.id), + folding: getFoldEvents('workspaces', wp.id), + href, + active: href === currentPathWithDomain, + navigateTo: () => push(href), + dropIn: true, + onDrop: () => dropInWorkspace(wp.id, updateFolder, updateDoc), + controls: [ + { + icon: mdiFilePlusOutline, + onClick: undefined, + placeholder: 'Doc title..', + create: (title: string) => + createDoc(team, { + workspaceId: wp.id, + title, + }), + }, + { + icon: mdiFolderPlusOutline, + onClick: undefined, + placeholder: 'Folder name..', + create: (folderName: string) => + createFolder(team, { + workspaceId: wp.id, + description: '', + folderName, + }), + }, + ], + contextControls: wp.default + ? [ + { + type: MenuTypes.Normal, + icon: mdiApplicationCog, + label: 'Edit', + onClick: () => openWorkspaceEditForm(wp), + }, + ] + : [ + { + type: MenuTypes.Normal, + icon: mdiApplicationCog, + label: 'Edit', + onClick: () => openWorkspaceEditForm(wp), + }, + { + type: MenuTypes.Normal, + icon: mdiTrashCanOutline, + label: 'Delete', + onClick: () => deleteWorkspace(wp), + }, + ], + }) + }) + + folders.forEach((folder) => { + const folderId = getFolderId(folder) + const href = `${process.env.BOOST_HUB_BASE_URL}${getFolderHref( + folder, + team, + 'index' + )}` + items.set(folderId, { + id: folderId, + lastUpdated: folder.updatedAt, + label: folder.name, + bookmarked: folder.bookmarked, + emoji: folder.emoji, + folded: !sideBarOpenedFolderIdsSet.has(folder.id), + folding: getFoldEvents('folders', folder.id), + href, + active: href === currentPathWithDomain, + navigateTo: () => push(href), + onDrop: (position: SidebarDragState) => + dropInDocOrFolder({ type: 'folder', result: folder }, position), + onDragStart: () => { + draggedResource.current = { type: 'folder', result: folder } + }, + onDragEnd: () => { + draggedResource.current = undefined + }, + dropIn: true, + dropAround: + preferences.sidebarTreeSortingOrder === 'drag' ? true : false, + controls: [ + { + icon: mdiFilePlusOutline, + onClick: undefined, + placeholder: 'Doc title..', + create: (title: string) => + createDoc(team, { + parentFolderId: folder.id, + workspaceId: folder.workspaceId, + title, + }), + }, + { + icon: mdiFolderPlusOutline, + onClick: undefined, + placeholder: 'Folder name..', + create: (folderName: string) => + createFolder(team, { + parentFolderId: folder.id, + workspaceId: folder.workspaceId, + description: '', + folderName, + }), + }, + ], + contextControls: [ + { + type: MenuTypes.Normal, + icon: folder.bookmarked ? mdiStar : mdiStarOutline, + label: + treeSendingMap.get(folder.id) === 'bookmark' + ? '...' + : folder.bookmarked + ? 'Bookmarked' + : 'Bookmark', + onClick: () => + toggleFolderBookmark(folder.teamId, folder.id, folder.bookmarked), + }, + { + type: MenuTypes.Normal, + icon: mdiPencil, + label: 'Rename', + onClick: () => openRenameFolderForm(folder), + }, + { + type: MenuTypes.Normal, + icon: mdiTrashCanOutline, + label: 'Delete', + onClick: () => deleteFolder(folder), + }, + ], + parentId: + folder.parentFolderId == null + ? folder.workspaceId + : folder.parentFolderId, + children: + typeof folder.positions != null && + typeof folder.positions !== 'string' + ? folder.positions.orderedIds + : [], + }) + }) + + docs.forEach((doc) => { + const docId = getDocId(doc) + const href = `${process.env.BOOST_HUB_BASE_URL}${getDocLinkHref( + doc, + team, + 'index' + )}` + items.set(docId, { + id: docId, + lastUpdated: doc.head != null ? doc.head.created : doc.updatedAt, + label: getDocTitle(doc, 'Untitled'), + bookmarked: doc.bookmarked, + emoji: doc.emoji, + defaultIcon: mdiFileDocumentOutline, + archived: doc.archivedAt != null, + children: [], + href, + active: href === currentPathWithDomain, + dropAround: + preferences.sidebarTreeSortingOrder === 'drag' ? true : false, + navigateTo: () => push(href), + onDrop: (position: SidebarDragState) => + dropInDocOrFolder({ type: 'doc', result: doc }, position), + onDragStart: () => { + draggedResource.current = { type: 'doc', result: doc } + }, + onDragEnd: () => { + draggedResource.current = undefined + }, + contextControls: [ + { + type: MenuTypes.Normal, + icon: doc.bookmarked ? mdiStar : mdiStarOutline, + label: + treeSendingMap.get(doc.id) === 'bookmark' + ? '...' + : doc.bookmarked + ? 'Bookmarked' + : 'Bookmark', + onClick: () => + toggleDocBookmark(doc.teamId, doc.id, doc.bookmarked), + }, + { + type: MenuTypes.Normal, + icon: mdiPencil, + label: 'Rename', + onClick: () => openRenameDocForm(doc), + }, + { + type: MenuTypes.Normal, + icon: mdiArchiveOutline, + label: doc.archivedAt == null ? 'Archive' : 'Restore', + onClick: () => toggleDocArchive(doc.teamId, doc.id, doc.archivedAt), + }, + ], + parentId: + doc.parentFolderId == null ? doc.workspaceId : doc.parentFolderId, + }) + }) + + const arrayItems = getMapValues(items) + const tree: Partial[] = [] + + let orderedBookmarked = [] + const bookmarked = arrayItems.reduce((acc, val) => { + if (!val.bookmarked) { + return acc + } + + acc.push({ + id: val.id, + depth: 0, + label: val.label, + emoji: val.emoji, + defaultIcon: val.defaultIcon, + href: val.href, + navigateTo: val.navigateTo, + contextControls: val.contextControls, + lastUpdated: val.lastUpdated, + }) + + return acc + }, [] as (SidebarTreeChildRow & { lastUpdated: string })[]) + + switch (preferences.sidebarTreeSortingOrder) { + case 'a-z': + orderedBookmarked = sortByAttributeAsc('label', bookmarked) + break + case 'z-a': + orderedBookmarked = sortByAttributeDesc('label', bookmarked) + break + case 'last-updated': + orderedBookmarked = sortByAttributeDesc('lastUpdated', bookmarked) + break + case 'drag': + default: + orderedBookmarked = bookmarked + break + } + + const navTree = arrayItems + .filter((item) => item.parentId == null) + .reduce((acc, val) => { + acc.push({ + ...val, + depth: 0, + rows: buildChildrenNavRows( + preferences.sidebarTreeSortingOrder, + val.children, + 1, + items + ), + }) + return acc + }, [] as SidebarTreeChildRow[]) + + const docsPerTagIdMap = [...docsMap.values()].reduce((acc, doc) => { + const docTags = doc.tags || [] + docTags.forEach((tag) => { + let docIds = acc.get(tag.id) + if (docIds == null) { + docIds = [] + acc.set(tag.id, docIds) + } + docIds.push(doc.id) + }) + return acc + }, new Map()) + + const labels = getMapValues(tagsMap) + .filter((tag) => (docsPerTagIdMap.get(tag.id) || []).length > 0) + .sort((a, b) => { + if (a.text < b.text) { + return -1 + } else { + return 1 + } + }) + .reduce((acc, val) => { + const href = `${process.env.BOOST_HUB_BASE_URL}${getTagHref( + val, + team, + 'index' + )}` + acc.push({ + id: val.id, + depth: 0, + label: val.text, + defaultIcon: mdiTag, + href, + active: href === currentPathWithDomain, + navigateTo: () => push(href), + }) + return acc + }, [] as SidebarTreeChildRow[]) + + if (orderedBookmarked.length > 0) { + tree.push({ + label: 'Bookmarks', + rows: orderedBookmarked, + }) + } + tree.push({ + label: 'Workspaces', + rows: navTree, + controls: [ + { + icon: mdiPlus, + onClick: () => openModal(), + }, + ], + }) + + if (!team.personal) { + tree.push({ + label: 'Private', + rows: + personalWorkspace != null + ? arrayItems + .filter((item) => item.parentId === personalWorkspace!.id) + .reduce((acc, val) => { + acc.push({ + ...val, + depth: 0, + rows: buildChildrenNavRows( + preferences.sidebarTreeSortingOrder, + val.children, + 1, + items + ), + }) + return acc + }, [] as SidebarTreeChildRow[]) + : [], + controls: [ + { + icon: mdiFilePlusOutline, + onClick: undefined, + placeholder: 'Doc title..', + create: async (title: string) => { + if (personalWorkspace == null) { + return createWorkspace( + team, + { + personal: true, + name: 'Private', + permissions: [], + public: false, + }, + { + skipRedirect: true, + afterSuccess: (wp) => + createDoc(team, { + workspaceId: wp.id, + title, + }), + } + ) + } + + return createDoc(team, { + workspaceId: personalWorkspace!.id, + title, + }) + }, + }, + { + icon: mdiFolderPlusOutline, + onClick: undefined, + placeholder: 'Folder name..', + create: async (folderName: string) => { + if (personalWorkspace == null) { + return createWorkspace( + team, + { + personal: true, + name: 'Private', + permissions: [], + public: false, + }, + { + skipRedirect: true, + afterSuccess: (wp) => + createFolder(team, { + workspaceId: wp.id, + description: '', + folderName, + }), + } + ) + } + + return createFolder(team, { + workspaceId: personalWorkspace!.id, + description: '', + folderName, + }) + }, + }, + ], + }) + } + + if (labels.length > 0) { + tree.push({ + label: 'Labels', + rows: labels, + }) + } + + tree.push({ + label: 'More', + rows: [ + { + id: 'sidenav-attachment', + label: 'Attachments', + defaultIcon: mdiPaperclip, + href: getTeamLinkHref(team, 'uploads'), + active: getTeamLinkHref(team, 'uploads') === pathname, + navigateTo: () => push(getTeamLinkHref(team, 'uploads')), + depth: 0, + }, + { + id: 'sidenav-shared', + label: 'Shared', + defaultIcon: mdiWeb, + href: getTeamLinkHref(team, 'shared'), + active: getTeamLinkHref(team, 'shared') === pathname, + navigateTo: () => push(getTeamLinkHref(team, 'shared')), + depth: 0, + }, + { + id: 'sidenav-archived', + label: 'Archived', + defaultIcon: mdiArchiveOutline, + href: getTeamLinkHref(team, 'archived'), + active: getTeamLinkHref(team, 'archived') === pathname, + navigateTo: () => push(getTeamLinkHref(team, 'archived')), + depth: 0, + }, + ], + }) + + tree.forEach((category) => { + const key = (category.label || '').toLocaleLowerCase() + const foldKey = `fold-${key}` + const hideKey = `hide-${key}` + category.folded = sideBarOpenedLinksIdsSet.has(foldKey) + category.folding = getFoldEvents('links', foldKey, true) + category.hidden = sideBarOpenedLinksIdsSet.has(hideKey) + category.toggleHidden = () => toggleItem('links', hideKey) + }) + + return tree as SidebarNavCategory[] + }, [ + initialLoadDone, + preferences.sidebarTreeSortingOrder, + pathname, + docsMap, + foldersMap, + workspacesMap, + tagsMap, + treeSendingMap, + sideBarOpenedFolderIdsSet, + sideBarOpenedLinksIdsSet, + sideBarOpenedWorkspaceIdsSet, + toggleItem, + getFoldEvents, + push, + openModal, + toggleDocBookmark, + toggleFolderBookmark, + createWorkspace, + deleteWorkspace, + toggleDocArchive, + deleteFolder, + createFolder, + createDoc, + dropInDocOrFolder, + dropInWorkspace, + updateFolder, + updateDoc, + openRenameFolderForm, + openRenameDocForm, + openWorkspaceEditForm, + draggedResource, + team, + ]) + + const treeWithOrderedCategories = useMemo(() => { + if (tree == null) { + return undefined + } + + const orderedCategories = Array.from( + new Set([ + ...preferences.sidebarOrderedCategories.split( + cloudSidebarOrderedCategoriesDelimiter + ), + ...cloudSidebaCategoryLabels, + ]) + ).filter((item) => + cloudSidebaCategoryLabels.find((categoryLabel) => categoryLabel === item) + ) + + const orderedTree = tree.sort((categoryA, categoryB) => { + if ( + orderedCategories.indexOf(categoryA.label) > + orderedCategories.indexOf(categoryB.label) + ) { + return 1 + } else { + return -1 + } + }) + + orderedTree.forEach((category) => { + category.drag = { + onDragStart: () => { + draggedCategory.current = category.label + }, + onDragEnd: () => { + draggedCategory.current = undefined + }, + onDrop: () => { + if (draggedCategory.current == null) { + return + } + const orderedItems = orderedCategories.splice(0) + const categoryIndex = orderedItems.includes(category.label) + ? orderedItems.indexOf(category.label) + : orderedItems.length - 1 + + const reArrangedArray = orderedItems.reduce((acc, val, i) => { + if (i === categoryIndex) { + acc.push(draggedCategory.current!) + } + + if (i !== categoryIndex && val === draggedCategory.current) { + return acc + } + + acc.push(val) + return acc + }, [] as string[]) + + setPreferences({ + sidebarOrderedCategories: reArrangedArray.join( + cloudSidebarOrderedCategoriesDelimiter + ), + }) + + draggedCategory.current = undefined + }, + } + }) + + return orderedTree + }, [ + tree, + preferences.sidebarOrderedCategories, + setPreferences, + draggedCategory, + ]) + + return { + tree, + treeWithOrderedCategories, + } +} + +function buildChildrenNavRows( + sortingOrder: SidebarTreeSortingOrder, + childrenIds: string[], + depth: number, + map: Map +) { + const rows = childrenIds.reduce((acc, childId) => { + const childRow = map.get(childId) + if (childRow == null) { + return acc + } + + if (childRow.archived) { + return acc + } + + acc.push({ + ...childRow, + depth, + rows: buildChildrenNavRows( + sortingOrder, + childRow.children, + depth + 1, + map + ), + }) + + return acc + }, [] as (SidebarTreeChildRow & { lastUpdated: string })[]) + + switch (sortingOrder) { + case 'a-z': + return sortByAttributeAsc('label', rows) + case 'z-a': + return sortByAttributeDesc('label', rows) + case 'last-updated': + return sortByAttributeDesc('lastUpdated', rows) + case 'drag': + default: + return rows + } +} + +type CloudTreeItem = { + id: string + parentId?: string + label: string + defaultIcon?: string + emoji?: string + bookmarked?: boolean + archived?: boolean + children: string[] + folding?: FoldingProps + folded?: boolean + href?: string + active?: boolean + lastUpdated: string + navigateTo?: () => void + controls?: SidebarNavControls[] + contextControls?: MenuItem[] + dropIn?: boolean + dropAround?: boolean + onDragStart?: () => void + onDrop?: (position?: SidebarDragState) => void + onDragEnd?: () => void +} diff --git a/src/cloud/lib/hooks/useCloudUpdater.ts b/src/cloud/lib/hooks/useCloudApi.ts similarity index 99% rename from src/cloud/lib/hooks/useCloudUpdater.ts rename to src/cloud/lib/hooks/useCloudApi.ts index 0037bee5f8..32907163b4 100644 --- a/src/cloud/lib/hooks/useCloudUpdater.ts +++ b/src/cloud/lib/hooks/useCloudApi.ts @@ -61,7 +61,7 @@ import useBulkApi from '../../../shared/lib/hooks/useBulkApi' import { getMapFromEntityArray } from '../../../shared/lib/utils/array' import { SerializedWorkspace } from '../../interfaces/db/workspace' -export function useCloudUpdater() { +export function useCloudApi() { const { pageDoc, pageFolder, setPartialPageData } = usePage() const { updateWorkspacesMap, diff --git a/src/cloud/lib/hooks/useCloudUI.tsx b/src/cloud/lib/hooks/useCloudResourceModals.tsx similarity index 98% rename from src/cloud/lib/hooks/useCloudUI.tsx rename to src/cloud/lib/hooks/useCloudResourceModals.tsx index e5b003692b..f0cba504dc 100644 --- a/src/cloud/lib/hooks/useCloudUI.tsx +++ b/src/cloud/lib/hooks/useCloudResourceModals.tsx @@ -10,9 +10,9 @@ import { SerializedDoc } from '../../interfaces/db/doc' import { SerializedFolder } from '../../interfaces/db/folder' import { SerializedTeam } from '../../interfaces/db/team' import { SerializedWorkspace } from '../../interfaces/db/workspace' -import { useCloudUpdater } from './useCloudUpdater' +import { useCloudApi } from './useCloudApi' -export function useCloudUI() { +export function useCloudResourceModals() { const { openModal, closeLastModal } = useModal() const { messageBox } = useDialog() const { @@ -24,7 +24,7 @@ export function useCloudUI() { deleteFolderApi, deleteDocApi, toggleDocArchive, - } = useCloudUpdater() + } = useCloudApi() const openWorkspaceEditForm = useCallback( (wp: SerializedWorkspace) => { diff --git a/src/cloud/lib/mappers/topbarBreadcrumbs.ts b/src/cloud/lib/mappers/topbarBreadcrumbs.ts index 1fd5c2b74b..0a7660ae2d 100644 --- a/src/cloud/lib/mappers/topbarBreadcrumbs.ts +++ b/src/cloud/lib/mappers/topbarBreadcrumbs.ts @@ -20,7 +20,10 @@ import { } from '../../interfaces/db/folder' import { SerializedTeam } from '../../interfaces/db/team' import { SerializedWorkspace } from '../../interfaces/db/workspace' -import { CloudNewResourceRequestBody, UIFormOptions } from '../hooks/useCloudUI' +import { + CloudNewResourceRequestBody, + UIFormOptions, +} from '../hooks/useCloudResourceModals' import { getDocTitle, prefixFolders } from '../utils/patterns' import { getHexFromUUID } from '../utils/string' import { topParentId } from './topbarTree' From fb524ec080e589a518765b1f08af0681175133cc Mon Sep 17 00:00:00 2001 From: davy-c Date: Fri, 21 May 2021 09:41:55 +0900 Subject: [PATCH 84/91] tmp --- src/cloud/components/Application.tsx | 35 +++++++++++++++++-- .../Modal/contents/DiscountModal.tsx | 15 ++++++++ src/cloud/interfaces/analytics/mixpanel.ts | 2 ++ src/cloud/lib/subscription.ts | 15 ++++++++ 4 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 src/cloud/components/organisms/Modal/contents/DiscountModal.tsx diff --git a/src/cloud/components/Application.tsx b/src/cloud/components/Application.tsx index c45465e71e..ac78bed5e0 100644 --- a/src/cloud/components/Application.tsx +++ b/src/cloud/components/Application.tsx @@ -58,6 +58,7 @@ import { mdiDownload, mdiFileDocumentMultipleOutline, mdiFileDocumentOutline, + mdiGiftOutline, mdiLogoutVariant, mdiMagnify, mdiPlusCircleOutline, @@ -87,6 +88,11 @@ import { import { ModalOpeningOptions, useModal } from '../../shared/lib/stores/modal' import NewDocButton from './molecules/NewDocButton' import { useCloudSidebarTree } from '../lib/hooks/sidebar/useCloudSidebarTree' +import { SerializedSubscription } from '../interfaces/db/subscription' +import { isEligibleForDiscount } from '../lib/subscription' +import { trackEvent } from '../api/track' +import { MixpanelActionTrackTypes } from '../interfaces/analytics/mixpanel' +import DiscountModal from './organisms/Modal/contents/DiscountModal' interface ApplicationProps { content: ContentLayoutProps @@ -113,6 +119,7 @@ const Application = ({ permissions = [], guestsMap, currentUserPermissions, + subscription, } = usePage() const { openModal } = useModal() const { @@ -180,9 +187,18 @@ const Application = ({ openModal, openSettingsTab, sidebarState, - team + team, + subscription ) - }, [sidebarState, openModal, openSettingsTab, team, openState, showSpaces]) + }, [ + sidebarState, + openModal, + openSettingsTab, + team, + openState, + showSpaces, + subscription, + ]) const topbarTree = useMemo(() => { if (team == null) { @@ -617,7 +633,8 @@ function mapToolbarRows( openModal: (cmp: JSX.Element, options?: ModalOpeningOptions) => void, openSettingsTab: (tab: SettingsTab) => void, sidebarState?: SidebarState, - team?: SerializedTeam + team?: SerializedTeam, + subscription?: SerializedSubscription ) { const rows: SidebarToolbarRow[] = [] if (team != null) { @@ -652,6 +669,18 @@ function mapToolbarRows( icon: mdiClockOutline, onClick: () => openState('timeline'), }) + + if (team != null && subscription == null && isEligibleForDiscount(team)) { + rows.push({ + tooltip: 'New user discount~', + icon: mdiGiftOutline, + onClick: () => { + trackEvent(MixpanelActionTrackTypes.UpgradeDiscount, { team: team.id }) + openModal() + }, + }) + } + rows.push({ tooltip: 'Import', icon: mdiDownload, diff --git a/src/cloud/components/organisms/Modal/contents/DiscountModal.tsx b/src/cloud/components/organisms/Modal/contents/DiscountModal.tsx new file mode 100644 index 0000000000..2f9265bed2 --- /dev/null +++ b/src/cloud/components/organisms/Modal/contents/DiscountModal.tsx @@ -0,0 +1,15 @@ +import React from 'react' +import styled from '../../../../../shared/lib/styled' +import { SerializedTeam } from '../../../../interfaces/db/team' + +interface DiscountModalProps { + team: SerializedTeam +} + +const DiscountModal = ({ team }: DiscountModalProps) => { + return discount there +} + +const Container = styled.div`` + +export default DiscountModal diff --git a/src/cloud/interfaces/analytics/mixpanel.ts b/src/cloud/interfaces/analytics/mixpanel.ts index fe23b821a2..515a1bb41f 100644 --- a/src/cloud/interfaces/analytics/mixpanel.ts +++ b/src/cloud/interfaces/analytics/mixpanel.ts @@ -72,6 +72,7 @@ export enum MixpanelActionTrackTypes { UpgradeRevision = 'upgrade.origin.revision', UpgradeGuest = 'upgrade.origin.guest', UpgradeLimit = 'upgrade.origin.limit', + UpgradeDiscount = 'upgrade.origin.discount', WorkspaceOpen = 'workspace.open', WorkspaceCreate = 'workspace.create', WorkspaceDelete = 'workspace.delete', @@ -100,6 +101,7 @@ export type MixpanelFrontEvent = | MixpanelActionTrackTypes.UpgradePassword | MixpanelActionTrackTypes.UpgradeRevision | MixpanelActionTrackTypes.DocFeatureRevision + | MixpanelActionTrackTypes.UpgradeDiscount export type MixpanelUserEvent = MixpanelActionTrackTypes.AccountDelete diff --git a/src/cloud/lib/subscription.ts b/src/cloud/lib/subscription.ts index d1a4852229..6e8c282cfc 100644 --- a/src/cloud/lib/subscription.ts +++ b/src/cloud/lib/subscription.ts @@ -1,3 +1,6 @@ +import { differenceInDays } from 'date-fns' +import { SerializedTeam } from '../interfaces/db/team' + export const freePlanDocLimit = 30 export const freeTrialPeriodDays = 7 export const guestsPerMember = 3 @@ -7,3 +10,15 @@ export const standardPlanStorageMb = 1000 export const proPlanStorageMb = 10000 export const revisionHistoryStandardDays = 7 +export const newTeamDiscountDays = 7 + +export function isEligibleForDiscount(team: SerializedTeam) { + if ( + differenceInDays(Date.now(), new Date(team.createdAt)) <= + newTeamDiscountDays + ) { + return true + } + + return false +} From 6ed6f643762f92ebc66a9d189427fd4a50a75cf4 Mon Sep 17 00:00:00 2001 From: davy-c Date: Fri, 21 May 2021 12:34:34 +0900 Subject: [PATCH 85/91] tmp - plans renewal --- package-lock.json | 8 + package.json | 1 + src/cloud/components/Application.tsx | 6 +- .../Modal/contents/DiscountModal.tsx | 120 ++++++- .../organisms/Subscription/PlanTables.tsx | 312 +----------------- .../Sidebar/molecules/SidebarToolbar.tsx | 105 ++++-- 6 files changed, 220 insertions(+), 332 deletions(-) diff --git a/package-lock.json b/package-lock.json index e28b4ee180..0403b27441 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27631,6 +27631,14 @@ "tinycolor2": "^1.4.1" } }, + "react-countdown": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/react-countdown/-/react-countdown-2.3.2.tgz", + "integrity": "sha512-Q4SADotHtgOxNWhDdvgupmKVL0pMB9DvoFcxv5AzjsxVhzOVxnttMbAywgqeOdruwEAmnPhOhNv/awAgkwru2w==", + "requires": { + "prop-types": "^15.7.2" + } + }, "react-datepicker": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-3.4.1.tgz", diff --git a/package.json b/package.json index f48005b1a6..3aef91af07 100644 --- a/package.json +++ b/package.json @@ -190,6 +190,7 @@ "react": "^16.9.0", "react-bootstrap": "^1.4.3", "react-color": "^2.19.3", + "react-countdown": "^2.3.2", "react-datepicker": "^3.4.1", "react-dom": "^16.9.0", "react-google-login": "^5.2.2", diff --git a/src/cloud/components/Application.tsx b/src/cloud/components/Application.tsx index ac78bed5e0..56dda677ec 100644 --- a/src/cloud/components/Application.tsx +++ b/src/cloud/components/Application.tsx @@ -150,6 +150,8 @@ const Application = ({ if (query.settings === 'upgrade') { openSettingsTab('teamUpgrade') } + + openModal() }) useEffect(() => { @@ -672,8 +674,10 @@ function mapToolbarRows( if (team != null && subscription == null && isEligibleForDiscount(team)) { rows.push({ - tooltip: 'New user discount~', + position: 'bottom', + tooltip: 'Get the new user discount!', icon: mdiGiftOutline, + pelletVariant: 'danger', onClick: () => { trackEvent(MixpanelActionTrackTypes.UpgradeDiscount, { team: team.id }) openModal() diff --git a/src/cloud/components/organisms/Modal/contents/DiscountModal.tsx b/src/cloud/components/organisms/Modal/contents/DiscountModal.tsx index 2f9265bed2..ab69b301d5 100644 --- a/src/cloud/components/organisms/Modal/contents/DiscountModal.tsx +++ b/src/cloud/components/organisms/Modal/contents/DiscountModal.tsx @@ -1,15 +1,131 @@ import React from 'react' import styled from '../../../../../shared/lib/styled' import { SerializedTeam } from '../../../../interfaces/db/team' +import Countdown from 'react-countdown' +import { newTeamDiscountDays } from '../../../../lib/subscription' +import PlanTables from '../../Subscription/PlanTables' interface DiscountModalProps { team: SerializedTeam } const DiscountModal = ({ team }: DiscountModalProps) => { - return discount there + const eligibilityEnd = new Date(team.createdAt) + eligibilityEnd.setDate(eligibilityEnd.getDate() + newTeamDiscountDays) + + return ( + +
+

+ Subscribe now to receive a three months discount! +

+
Time remaining
+ + +
+
+ ) +} + +const DiscountCountdown = ({ + days, + hours, + minutes, + seconds, + completed, +}: { + days: number + hours: number + minutes: number + seconds: number + completed: boolean +}) => { + if (completed) { + return ( +
+ Your eligibility for a discount has expired +
+ ) + } + + return ( +
+
+
{days}
+ days +
+
+
{hours}
+ hours +
+
+
{minutes}
+ minutes +
+
+
{seconds}
+ seconds +
+
+ ) } -const Container = styled.div`` +const Container = styled.div` + .discount__modal__header { + text-align: center; + } + + .discount__modal__title { + margin: 0; + font-size: ${({ theme }) => theme.sizes.fonts.l}px; + } + + .discount__modal__header > * + .discount__modal__header > * { + margin-top: ${({ theme }) => theme.sizes.spaces.df}px; + } + + .discount__modal__description { + margin: ${({ theme }) => theme.sizes.spaces.df}px 0; + text-transform: uppercase; + color: ${({ theme }) => theme.colors.text.subtle}; + font-size: ${({ theme }) => theme.sizes.fonts.md}; + } + + .countdown { + display: flex; + flex-wrap: nowrap; + justify-content: center; + align-items: top; + margin-bottom: ${({ theme }) => theme.sizes.spaces.df}px; + } + + .countdown__column { + display: flex; + flex-direction: column; + justify-content: center; + flex: 0 0 auto; + } + + .countdown__column + .countdown__column { + margin-left: ${({ theme }) => theme.sizes.spaces.df}px; + } + + .countdown__number { + background: #000; + color: #fff; + width: 70px; + font-size: ${({ theme }) => theme.sizes.fonts.xl}px; + text-align: center; + padding: ${({ theme }) => theme.sizes.spaces.sm}px + ${({ theme }) => theme.sizes.spaces.df}px; + } + + .countdown__description { + margin-top: ${({ theme }) => theme.sizes.spaces.xsm}px; + text-transform: uppercase; + font-size: ${({ theme }) => theme.sizes.fonts.sm}px; + color: ${({ theme }) => theme.colors.text.subtle}; + } +` export default DiscountModal diff --git a/src/cloud/components/organisms/Subscription/PlanTables.tsx b/src/cloud/components/organisms/Subscription/PlanTables.tsx index fdec200a6d..fac8d8c6bf 100644 --- a/src/cloud/components/organisms/Subscription/PlanTables.tsx +++ b/src/cloud/components/organisms/Subscription/PlanTables.tsx @@ -4,7 +4,6 @@ import { useMediaQuery } from 'react-responsive' import { SerializedSubscription } from '../../../interfaces/db/subscription' import { SerializedTeam } from '../../../interfaces/db/team' import { UpgradePlans } from '../../../lib/stripe' -import styled from '../../../lib/styled' import { freePlanDocLimit, freePlanStorageMb, @@ -15,11 +14,13 @@ import { import cc from 'classcat' import Button from '../../../../shared/components/atoms/Button' import Link from '../../../../shared/components/atoms/Link' +import styled from '../../../../shared/lib/styled' interface PlanTablesProps { team: SerializedTeam subscription?: SerializedSubscription selectedPlan: UpgradePlans | 'free' + discounted?: boolean onFreeCallback?: () => void onStandardCallback?: () => void onProCallback?: () => void @@ -34,13 +35,11 @@ const PlanTables = ({ onStandardCallback, onProCallback, subscription, + discounted, }: PlanTablesProps) => { const isTabletOrMobile = useMediaQuery({ maxWidth: 1080 }) const freeTrialContent = useMemo(() => { - if (team == null) { - return null - } if (subscription != null) { if (subscription.status !== 'trialing') { return null @@ -75,227 +74,13 @@ const PlanTables = ({ }, [subscription, team, onTrialCallback]) return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - -
- $0 -
- per user -
- per month -
-
- - {selectedPlan === 'free' ? ( - - ) : ( - - )} -
- -
- $3 -
- per user -
- per month -
-
- {selectedPlan === 'standard' ? ( - - ) : ( - - )} -
- -
- $8 -
- per user -
- per month -
-
- - {selectedPlan === 'pro' ? ( - - ) : ( - - )} - {freeTrialContent} -
Members -
Unlimited
-
-
Unlimited
-
-
Unlimited
-
Documents -
{freePlanDocLimit} per team
-
-
Unlimited
-
-
Unlimited
-
Storage limit -
{freePlanStorageMb}MB per member
-
-
- {standardPlanStorageMb / 1000}GB per member -
-
-
{proPlanStorageMb / 1000}GB per member
-
Integrations -
2000+ integrations
-
-
2000+ integrations
-
-
2000+ integrations
-
Collaborative workspace -
-
-
- -
-
-
- -
-
Revision History -
-
-
- - Last {revisionHistoryStandardDays} days - -
-
-
- -
-
Guest invite -
-
-
-
-
- -
-
Password/Expiration date for sharing -
-
-
-
-
- -
-
Priority support -
-
-
-
-
- -
-
+ +
+
+
+
+
+
) } @@ -304,80 +89,17 @@ const Container = styled.div` width: 100%; overflow: auto; - &.mobile__layout { - border-spacing: 6px 0; - } - - > table { - margin-bottom: ${({ theme }) => theme.space.medium}px; - table-layout: fixed; - border-collapse: separate; - border-spacing: 20px 0; - min-width: 600px; - } - - .first { - width: 30%; - } - - .header { - vertical-align: top; - } - - label { - display: block; - margin-bottom: ${({ theme }) => theme.space.small}px; - font-size: ${({ theme }) => theme.fontSizes.xlarge}px; - font-weight: 600; - } - - .pricing { + .plans__header { display: flex; - align-items: center; - margin-bottom: ${({ theme }) => theme.space.xsmall}px; - - span { - font-size: ${({ theme }) => theme.fontSizes.xxlarge}px; - margin-right: ${({ theme }) => theme.space.xsmall}px; - } - div { - font-size: ${({ theme }) => theme.fontSizes.xsmall}px; - line-height: 1; - opacity: 0.6; - width: 70px; - padding-top: 2px; - } + flex-direction: row; + flex-wrap: nowrap; + flex: 1 1 auto; } - .upgrade-btn { + &.plans--mobile .plans__header { + flex-wrap: wrap; width: 100%; - margin: ${({ theme }) => theme.fontSizes.xsmall}px 0; - } - - tr td { - padding-top: ${({ theme }) => theme.space.xsmall}px; - padding-bottom: ${({ theme }) => theme.space.xsmall}px; - border-bottom: 1px solid ${({ theme }) => theme.subtleBorderColor}; - text-align: left; - min-height: 30px; - - &:not(.first):not(.header) { - padding: ${({ theme }) => theme.space.xsmall}px; - } - - &.first { - color: ${({ theme }) => theme.subtleTextColor}; - } - } - - .perk { - line-height: 1.2; - padding: 6px 0px; - } - - .check { - color: ${({ theme }) => theme.primaryTextColor}; - font-weight: bold; + flex: 0 0 auto; } ` diff --git a/src/shared/components/organisms/Sidebar/molecules/SidebarToolbar.tsx b/src/shared/components/organisms/Sidebar/molecules/SidebarToolbar.tsx index 10728ae1e2..b9aa1ff175 100644 --- a/src/shared/components/organisms/Sidebar/molecules/SidebarToolbar.tsx +++ b/src/shared/components/organisms/Sidebar/molecules/SidebarToolbar.tsx @@ -11,6 +11,7 @@ export type SidebarToolbarRow = { active?: boolean tooltip?: string position?: 'top' | 'bottom' + pelletVariant?: 'primary' | 'danger' | 'warning' onClick?: () => void } @@ -49,44 +50,20 @@ const SidebarToolbar: AppComponent = ({
{sortedRows.top.map((row, i) => ( - - - + ))}
{sortedRows.bottom.map((row, i) => ( - - - + ))}
@@ -94,6 +71,40 @@ const SidebarToolbar: AppComponent = ({ ) } +const SidebarToolbarItem = ({ + row, + iconSize, +}: { + row: SidebarToolbarRow + iconSize: IconSize +}) => ( + + + +) + export default SidebarToolbar const toolbarSpacing = 15 @@ -147,6 +158,7 @@ const Container = styled.div<{ iconSize: number }>` margin: 0 0 ${toolbarSpacing}px 0; position: relative; justify-content: center; + position: relative; &:hover, &.sidebar__toolbar__item--active { @@ -158,4 +170,29 @@ const Container = styled.div<{ iconSize: number }>` } } } + + .sidebar__toolbar__item__pellet { + position: absolute; + z-index: 1; + top: 2px; + right: 2px; + display: block; + width: 6px; + height: 6px; + border-radius: 50%; + background: ${({ theme }) => theme.colors.background.secondary}; + + &.sidebar__toolbar__item__pellet--danger { + background: ${({ theme }) => theme.colors.variants.danger.base}; + filter: brightness(160%); + } + + &.sidebar__toolbar__item__pellet--info { + background: ${({ theme }) => theme.colors.variants.info.base}; + } + + &.sidebar__toolbar__item__pellet--warning { + background: ${({ theme }) => theme.colors.variants.warning.base}; + } + } ` From b51d53eead9a0bd289ffcc69133c8540c2a3d792 Mon Sep 17 00:00:00 2001 From: davy-c Date: Sat, 22 May 2021 10:01:35 +0900 Subject: [PATCH 86/91] discount and upgrade tab --- src/cloud/components/Application.tsx | 4 +- .../Modal/contents/DiscountModal.tsx | 67 +++- .../organisms/Subscription/PlanTables.tsx | 317 ++++++++++++++++-- .../organisms/settings/SettingsComponent.tsx | 13 +- .../organisms/settings/TeamSubLimit.tsx | 31 +- .../organisms/settings/UpgradeTab.tsx | 37 +- src/cloud/lib/stores/settings/store.ts | 12 +- src/cloud/lib/stripe.ts | 21 ++ src/shared/components/atoms/Banner.tsx | 56 ++++ src/shared/lib/styled/dark.ts | 2 +- src/shared/lib/styled/light.ts | 2 +- 11 files changed, 516 insertions(+), 46 deletions(-) create mode 100644 src/shared/components/atoms/Banner.tsx diff --git a/src/cloud/components/Application.tsx b/src/cloud/components/Application.tsx index 56dda677ec..3cd34f95fa 100644 --- a/src/cloud/components/Application.tsx +++ b/src/cloud/components/Application.tsx @@ -150,8 +150,6 @@ const Application = ({ if (query.settings === 'upgrade') { openSettingsTab('teamUpgrade') } - - openModal() }) useEffect(() => { @@ -680,7 +678,7 @@ function mapToolbarRows( pelletVariant: 'danger', onClick: () => { trackEvent(MixpanelActionTrackTypes.UpgradeDiscount, { team: team.id }) - openModal() + openModal(, { showCloseIcon: true, width: 'large' }) }, }) } diff --git a/src/cloud/components/organisms/Modal/contents/DiscountModal.tsx b/src/cloud/components/organisms/Modal/contents/DiscountModal.tsx index ab69b301d5..8cbf163666 100644 --- a/src/cloud/components/organisms/Modal/contents/DiscountModal.tsx +++ b/src/cloud/components/organisms/Modal/contents/DiscountModal.tsx @@ -1,15 +1,34 @@ import React from 'react' import styled from '../../../../../shared/lib/styled' -import { SerializedTeam } from '../../../../interfaces/db/team' import Countdown from 'react-countdown' import { newTeamDiscountDays } from '../../../../lib/subscription' import PlanTables from '../../Subscription/PlanTables' +import { useSettings } from '../../../../lib/stores/settings' +import { useModal } from '../../../../../shared/lib/stores/modal' +import { usePage } from '../../../../lib/stores/pageStore' +import Banner from '../../../../../shared/components/atoms/Banner' +import { mdiExclamation } from '@mdi/js' +import TeamSubLimit from '../../settings/TeamSubLimit' -interface DiscountModalProps { - team: SerializedTeam -} +const DiscountModal = () => { + const { openSettingsTab } = useSettings() + const { closeAllModals } = useModal() + const { team, subscription } = usePage() + + if (team == null) { + return null + } + + if (subscription != null) { + return ( + + + You are already subscribed + + + ) + } -const DiscountModal = ({ team }: DiscountModalProps) => { const eligibilityEnd = new Date(team.createdAt) eligibilityEnd.setDate(eligibilityEnd.getDate() + newTeamDiscountDays) @@ -21,7 +40,41 @@ const DiscountModal = ({ team }: DiscountModalProps) => {
Time remaining
- + { + openSettingsTab('teamUpgrade', { + initialPlan: 'standard', + tabState: 'form', + }) + closeAllModals() + }} + /> + } + onStandardCallback={() => { + openSettingsTab('teamUpgrade', { + initialPlan: 'standard', + tabState: 'form', + }) + closeAllModals() + }} + onProCallback={() => { + openSettingsTab('teamUpgrade', { + initialPlan: 'pro', + tabState: 'form', + }) + closeAllModals() + }} + onTrialCallback={() => { + openSettingsTab('teamUpgrade', { showTrialPopup: true }) + closeAllModals() + }} + />
) @@ -96,7 +149,7 @@ const Container = styled.div` flex-wrap: nowrap; justify-content: center; align-items: top; - margin-bottom: ${({ theme }) => theme.sizes.spaces.df}px; + margin-bottom: ${({ theme }) => theme.sizes.spaces.xl}px; } .countdown__column { diff --git a/src/cloud/components/organisms/Subscription/PlanTables.tsx b/src/cloud/components/organisms/Subscription/PlanTables.tsx index fac8d8c6bf..40bc450571 100644 --- a/src/cloud/components/organisms/Subscription/PlanTables.tsx +++ b/src/cloud/components/organisms/Subscription/PlanTables.tsx @@ -3,7 +3,12 @@ import React, { useMemo } from 'react' import { useMediaQuery } from 'react-responsive' import { SerializedSubscription } from '../../../interfaces/db/subscription' import { SerializedTeam } from '../../../interfaces/db/team' -import { UpgradePlans } from '../../../lib/stripe' +import { + stripeProPlanUnit, + stripeStandardPlanUnit, + UpgradePlans, + discountPlans, +} from '../../../lib/stripe' import { freePlanDocLimit, freePlanStorageMb, @@ -13,14 +18,15 @@ import { } from '../../../lib/subscription' import cc from 'classcat' import Button from '../../../../shared/components/atoms/Button' -import Link from '../../../../shared/components/atoms/Link' import styled from '../../../../shared/lib/styled' +import plur from 'plur' interface PlanTablesProps { team: SerializedTeam subscription?: SerializedSubscription selectedPlan: UpgradePlans | 'free' discounted?: boolean + freePlanFooter?: React.ReactNode onFreeCallback?: () => void onStandardCallback?: () => void onProCallback?: () => void @@ -36,6 +42,7 @@ const PlanTables = ({ onProCallback, subscription, discounted, + freePlanFooter, }: PlanTablesProps) => { const isTabletOrMobile = useMediaQuery({ maxWidth: 1080 }) @@ -59,48 +66,314 @@ const PlanTables = ({ } return ( -

- { - e.preventDefault() - onTrialCallback() - }} - > - 7 days free trial - -

+ ) }, [subscription, team, onTrialCallback]) return ( -
-
-
-
+
+
+ +
+ $0 +
+ per member per month +
+
+
+
+
+ Unlimited members +
+
+ {freePlanDocLimit} docs per team +
+
+ {freePlanStorageMb}MB per member +
+
+
+ {selectedPlan === 'free' ? ( + freePlanFooter != null ? ( + freePlanFooter + ) : ( + + ) + ) : ( + + )} +
+
+
+
+ +
+ + ${stripeStandardPlanUnit} + + {discounted && ( + + ${stripeStandardPlanUnit - discountPlans.standard.amountOff} + + )} +
+ per member per month +
+
+
+ {discounted && ( +
+ {discountPlans.standard.percentageOff}% OFF for{' '} + {discountPlans.standard.durationInMonths}{' '} + {plur('month', discountPlans.standard.durationInMonths)} +
+ )} +
+
+ Support development +
+
+ Unlimited documents +
+
+ + Last {revisionHistoryStandardDays} days of your docs's + revision history + +
+
+ {standardPlanStorageMb / 1000}GB per member +
+
+
+ {selectedPlan === 'standard' ? ( + + ) : ( + + )} +
+
+
+
+ +
+ + ${stripeProPlanUnit} + + {discounted && ( + + ${stripeProPlanUnit - discountPlans.pro.amountOff} + + )} +
+ per member per month +
+
+
+ {discounted && ( +
+ {discountPlans.pro.percentageOff}% OFF for{' '} + {discountPlans.pro.durationInMonths}{' '} + {plur('month', discountPlans.pro.durationInMonths)} +
+ )} +
+
+ Guest invite +
+
+ Password and expiration date when sharing +
+
+ Full access to your docs's revision history +
+
+ {proPlanStorageMb / 1000}GB per member +
+
+
+ {selectedPlan === 'pro' ? ( + + ) : ( + + )} + {freeTrialContent}
) } +const footerPadding = 100 const Container = styled.div` width: 100%; overflow: auto; + display: flex; + flex-direction: row; + flex-wrap: nowrap; + flex: 1 1 auto; + + .plan__item__footer { + position: absolute; + bottom: 0; + height: ${footerPadding}px; + width: 100%; + left: 0; + + .upgrade__btn, + .free__trial__btn { + width: 100%; + display: flex; + } + + > * { + margin: 0; + margin-bottom: ${({ theme }) => theme.sizes.spaces.sm}px; + } + } + + .plan__item__perks { + margin-bottom: ${({ theme }) => theme.sizes.spaces.sm}px; + } + + .plan__item__perk { + position: relative; + color: ${({ theme }) => theme.colors.text.secondary}; + display: flex; + align-items: flex-start; + + &::before { + content: '✓'; + display: block; + color: ${({ theme }) => theme.colors.text.link}; + font-size: ${({ theme }) => theme.sizes.fonts.l}px; + padding-right: ${({ theme }) => theme.sizes.spaces.sm}px; + line-height: ${({ theme }) => theme.sizes.fonts.df}px; + } + } + + .plan__item__perk + .plan__item__perk { + margin-top: ${({ theme }) => theme.sizes.spaces.xsm}px; + } + + .plan__item__discount { + font-size: ${({ theme }) => theme.sizes.fonts.md}px; + color: ${({ theme }) => theme.colors.variants.warning.base}; + margin-bottom: ${({ theme }) => theme.sizes.spaces.sm}px; + } - .plans__header { + .plan__item__price { display: flex; - flex-direction: row; - flex-wrap: nowrap; - flex: 1 1 auto; + align-items: center; + margin-bottom: ${({ theme }) => theme.sizes.spaces.sm}px; + justify-content: flex-start; + + &.plan__item__price--discounted { + .plan__item__price__default::after, + .plan__item__price__default::before { + content: ''; + position: absolute; + left: 0; + width: 100%; + height: 2px; + background: ${({ theme }) => theme.colors.text.primary}; + } + + .plan__item__price__default::after { + bottom: 35%; + } + .plan__item__price__default::before { + top: 35%; + } + } + + .plan__item__price__default, + .plan__item__price__discount { + position: relative; + font-size: ${({ theme }) => theme.sizes.fonts.xl}px; + margin-right: ${({ theme }) => theme.sizes.spaces.sm}px; + } + + .plan__item__price__discount { + color: ${({ theme }) => theme.colors.variants.warning.base}; + } + + .plan__item__price__description { + font-size: ${({ theme }) => theme.sizes.fonts.xsm}px; + line-height: 1; + opacity: 0.6; + width: 70px; + padding-top: 2px; + } } - &.plans--mobile .plans__header { + .plan__item__title { + display: block; + margin-bottom: ${({ theme }) => theme.sizes.spaces.sm}px; + font-size: ${({ theme }) => theme.sizes.fonts.l}px; + font-weight: 600; + } + + &.plans--mobile { flex-wrap: wrap; width: 100%; flex: 0 0 auto; } + + .plan__item { + text-align: left; + width: 29%; + margin: 0 2%; + padding-bottom: ${footerPadding}px; + position: relative; + display: flex; + flex-direction: column; + align-items: stretch; + } ` export default PlanTables diff --git a/src/cloud/components/organisms/settings/SettingsComponent.tsx b/src/cloud/components/organisms/settings/SettingsComponent.tsx index 975df105fd..13686ba341 100644 --- a/src/cloud/components/organisms/settings/SettingsComponent.tsx +++ b/src/cloud/components/organisms/settings/SettingsComponent.tsx @@ -42,10 +42,15 @@ import { intercomAppId } from '../../../lib/consts' const SettingsComponent = () => { const { t } = useTranslation() - const { closed, toggleClosed, settingsTab } = useSettings() + const { + closed, + toggleClosed, + settingsTab, + openSettingsTab, + settingsOpeningOptions, + } = useSettings() const contentSideRef = React.createRef() const menuRef = React.createRef() - const { openSettingsTab } = useSettings() const { team, subscription, currentUserPermissions } = usePage< PageStoreWithTeam >() @@ -101,7 +106,7 @@ const SettingsComponent = () => { case 'teamMembers': return case 'teamUpgrade': - return + return case 'teamSubscription': return case 'integrations': @@ -119,7 +124,7 @@ const SettingsComponent = () => { default: return } - }, [settingsTab, currentUserPermissions]) + }, [settingsTab, currentUserPermissions, settingsOpeningOptions]) useEffect(() => { if (closed) { diff --git a/src/cloud/components/organisms/settings/TeamSubLimit.tsx b/src/cloud/components/organisms/settings/TeamSubLimit.tsx index 8ddaa5b167..126987b56e 100644 --- a/src/cloud/components/organisms/settings/TeamSubLimit.tsx +++ b/src/cloud/components/organisms/settings/TeamSubLimit.tsx @@ -4,7 +4,13 @@ import { usePage } from '../../../lib/stores/pageStore' import { useSettings } from '../../../lib/stores/settings' import styled from '../../../../shared/lib/styled' -const TeamSubLimit = () => { +const TeamSubLimit = ({ + padded = true, + onLimitClick, +}: { + padded?: boolean + onLimitClick?: () => void +}) => { const { subscription, team, currentSubInfo } = usePage() const { openSettingsTab } = useSettings() @@ -18,12 +24,18 @@ const TeamSubLimit = () => { if (currentSubInfo.trialing) { return ( - + { e.preventDefault() + if (onLimitClick != null) { + onLimitClick() + return + } openSettingsTab('teamUpgrade') }} > @@ -41,12 +53,18 @@ const TeamSubLimit = () => { } return ( - + { e.preventDefault() + if (onLimitClick != null) { + onLimitClick() + return + } openSettingsTab('teamUpgrade') }} > @@ -77,6 +95,13 @@ const Container = styled.nav` width: 100%; margin-top: ${({ theme }) => theme.sizes.spaces.l}px; + &.sub__limit--stripped { + margin: 0; + > * { + margin: 0 !important; + } + } + h6 { margin: 0; color: ${({ theme }) => theme.colors.variants.primary.base}; diff --git a/src/cloud/components/organisms/settings/UpgradeTab.tsx b/src/cloud/components/organisms/settings/UpgradeTab.tsx index a9e5098ef1..8fc4dab922 100644 --- a/src/cloud/components/organisms/settings/UpgradeTab.tsx +++ b/src/cloud/components/organisms/settings/UpgradeTab.tsx @@ -16,12 +16,29 @@ import { UpgradePlans } from '../../../lib/stripe' import styled from '../../../lib/styled' import SettingTabContent from '../../../../shared/components/organisms/Settings/atoms/SettingTabContent' import { ExternalLink } from '../../../../shared/components/atoms/Link' +import { + isEligibleForDiscount, + newTeamDiscountDays, +} from '../../../lib/subscription' +import Banner from '../../../../shared/components/atoms/Banner' +import { mdiGift } from '@mdi/js' +import { format } from 'date-fns' const stripePromise = loadStripe(stripePublishableKey) type UpgradeTabs = 'plans' | 'form' -const UpgradeTab = () => { +export interface UpgradeTabOpeningOptions { + tabState?: UpgradeTabs + showTrialPopup?: boolean + initialPlan?: UpgradePlans +} + +const UpgradeTab = ({ + tabState: defaultTabState = 'plans', + showTrialPopup: defaultShowTrial = false, + initialPlan: defaultInitialPlan = 'standard', +}: UpgradeTabOpeningOptions) => { const { t } = useTranslation() const { team, @@ -29,13 +46,15 @@ const UpgradeTab = () => { updateTeamSubscription, permissions = [], } = usePage() - const [tabState, setTabState] = useState('plans') + const [tabState, setTabState] = useState(defaultTabState) const { openSettingsTab } = useSettings() const { globalData: { currentUser }, } = useGlobalData() - const [showTrialPopup, setShowTrialPopup] = useState(false) - const [initialPlan, setInitialPlan] = useState('standard') + const [showTrialPopup, setShowTrialPopup] = useState(defaultShowTrial) + const [initialPlan, setInitialPlan] = useState( + defaultInitialPlan + ) useEffect(() => { if (subscription != null && subscription.status !== 'trialing') { @@ -73,6 +92,9 @@ const UpgradeTab = () => { return null } + const eligibilityEnd = new Date(team.createdAt) + eligibilityEnd.setDate(eligibilityEnd.getDate() + newTeamDiscountDays) + const teamIsEligibleForDiscount = isEligibleForDiscount(team) if (tabState === 'plans') { return ( { /> )}
+ {teamIsEligibleForDiscount && ( + + You will receive a discount as long as you subscribe before{' '} + {format(eligibilityEnd, 'H:m, dd MMM yyyy')} + + )} { onStandardCallback={() => onUpgradeCallback('standard')} onProCallback={() => onUpgradeCallback('pro')} onTrialCallback={() => setShowTrialPopup(true)} + discounted={teamIsEligibleForDiscount} /> * For larger businesses or those in highly regulated industries, diff --git a/src/cloud/lib/stores/settings/store.ts b/src/cloud/lib/stores/settings/store.ts index 38f542fdc2..571165314e 100644 --- a/src/cloud/lib/stores/settings/store.ts +++ b/src/cloud/lib/stores/settings/store.ts @@ -10,6 +10,7 @@ import { toggleSettingsMembersEventEmitter, } from '../../utils/events' import { useToast } from '../../../../shared/lib/stores/toast' +import { UpgradeTabOpeningOptions } from '../../../components/organisms/settings/UpgradeTab' export const baseUserSettings: UserSettings = { 'general.theme': 'dark', @@ -32,10 +33,16 @@ export type SettingsTab = | 'api' | 'feedback' +export type SettingsTabOpeningOptions = UpgradeTabOpeningOptions + function useSettingsStore() { const { globalData, setPartialGlobalData } = useGlobalData() const { currentUserSettings, currentUser } = globalData const [settingsTab, setSettingsTab] = useState('personalInfo') + const [settingsOpeningOptions, setSettingsOpeningOptions] = useState< + SettingsTabOpeningOptions + >() + const { pushMessage } = useToast() const { t } = useTranslation() @@ -94,6 +101,7 @@ function useSettingsStore() { const closeSettingsTab = useCallback(() => { setClosed(true) + setSettingsOpeningOptions(undefined) }, [setClosed]) const currentLanguage = mergedUserSettings['general.language'] @@ -103,8 +111,9 @@ function useSettingsStore() { }, [i18n, currentLanguage]) const openSettingsTab = useCallback( - (tab: SettingsTab) => { + (tab: SettingsTab, options?: SettingsTabOpeningOptions) => { setClosed(false) + setSettingsOpeningOptions(options) setSettingsTab(tab) return }, @@ -155,6 +164,7 @@ function useSettingsStore() { settings: mergedUserSettings, setSettings, settingsTab, + settingsOpeningOptions, openSettingsTab, closeSettingsTab, emailNotifications, diff --git a/src/cloud/lib/stripe.ts b/src/cloud/lib/stripe.ts index a0dd1af6a9..68ae436357 100644 --- a/src/cloud/lib/stripe.ts +++ b/src/cloud/lib/stripe.ts @@ -4,3 +4,24 @@ export const stripeStandardPlanUnit = 3 export const stripeStandardJpyPlanUnit = 300 export type UpgradePlans = 'standard' | 'pro' + +type DiscountParameters = { + durationInMonths: number + amountOff: number + percentageOff: number +} + +type Discounts = Record + +export const discountPlans: Discounts = { + standard: { + durationInMonths: 3, + amountOff: 1, + percentageOff: 33, + }, + pro: { + durationInMonths: 3, + amountOff: 4, + percentageOff: 50, + }, +} diff --git a/src/shared/components/atoms/Banner.tsx b/src/shared/components/atoms/Banner.tsx new file mode 100644 index 0000000000..7a11ce9339 --- /dev/null +++ b/src/shared/components/atoms/Banner.tsx @@ -0,0 +1,56 @@ +import React from 'react' +import { AppComponent } from '../../lib/types' +import Icon from './Icon' +import cc from 'classcat' +import styled from '../../lib/styled' + +interface BannerProps { + variant: 'danger' | 'warning' | 'info' + iconPath?: string +} + +const Banner: AppComponent = ({ + variant, + iconPath, + children, + className, +}) => ( + + {iconPath != null && ( + + )} +
{children}
+
+) + +const Container = styled.div` + display: flex; + width: 100%; + flex: 1 1 auto; + overflow: hidden; + white-space: none; + padding: ${({ theme }) => theme.sizes.spaces.df}px + ${({ theme }) => theme.sizes.spaces.md}px; + margin-bottom: ${({ theme }) => theme.sizes.spaces.md}px; + + .banner__icon { + margin-right: ${({ theme }) => theme.sizes.spaces.sm}px; + } + + &.banner--danger { + background-color: ${({ theme }) => theme.colors.variants.danger.base}; + color: ${({ theme }) => theme.colors.variants.danger.text}; + } + + &.banner--warning { + background-color: ${({ theme }) => theme.colors.variants.warning.base}; + color: ${({ theme }) => theme.colors.variants.warning.text}; + } + + &.banner--info { + background-color: ${({ theme }) => theme.colors.variants.info.base}; + color: ${({ theme }) => theme.colors.variants.info.text}; + } +` + +export default Banner diff --git a/src/shared/lib/styled/dark.ts b/src/shared/lib/styled/dark.ts index dc1cf714f7..ab277b0b55 100644 --- a/src/shared/lib/styled/dark.ts +++ b/src/shared/lib/styled/dark.ts @@ -41,7 +41,7 @@ export const darkTheme: BaseTheme = { }, warning: { base: '#FFBB33', - text: '#fff', + text: '#000', }, success: { base: '#00C851', diff --git a/src/shared/lib/styled/light.ts b/src/shared/lib/styled/light.ts index a66dd14563..cc261bdf1e 100644 --- a/src/shared/lib/styled/light.ts +++ b/src/shared/lib/styled/light.ts @@ -41,7 +41,7 @@ export const lightTheme: BaseTheme = { }, warning: { base: '#FFBB33', - text: '#fff', + text: '#000', }, success: { base: '#00C851', From 58b2b4b963e0048696937b059f963b3510e75c73 Mon Sep 17 00:00:00 2001 From: davy-c Date: Mon, 24 May 2021 09:47:56 +0900 Subject: [PATCH 87/91] front end --- .../UpdateBillingEmailForm.tsx | 59 +++- .../UpdateBillingMethodForm.tsx | 87 +++-- .../SubscriptionForm/UpdateBillingPromo.tsx | 48 ++- .../molecules/SubscriptionForm/index.tsx | 328 ++++++------------ .../organisms/Subscription/PlanTables.tsx | 20 +- .../Subscription/SubscriptionCostSummary.tsx | 155 +++++++++ .../Subscription/SubscriptionManagement.tsx | 285 ++++++--------- .../organisms/settings/SubscriptionTab.tsx | 1 - .../organisms/settings/UpgradeTab.tsx | 56 +-- src/cloud/interfaces/db/subscription.ts | 1 + src/cloud/interfaces/db/team.ts | 1 + src/cloud/lib/stripe.ts | 2 +- src/shared/components/atoms/Button.tsx | 2 +- src/shared/components/atoms/Link.tsx | 2 +- .../molecules/Form/atoms/FormStripeInput.tsx | 72 ++++ .../molecules/Form/templates/FormRow.tsx | 76 ++-- 16 files changed, 633 insertions(+), 562 deletions(-) create mode 100644 src/cloud/components/organisms/Subscription/SubscriptionCostSummary.tsx create mode 100644 src/shared/components/molecules/Form/atoms/FormStripeInput.tsx diff --git a/src/cloud/components/molecules/SubscriptionForm/UpdateBillingEmailForm.tsx b/src/cloud/components/molecules/SubscriptionForm/UpdateBillingEmailForm.tsx index 79a2e439cf..e4c62f5225 100644 --- a/src/cloud/components/molecules/SubscriptionForm/UpdateBillingEmailForm.tsx +++ b/src/cloud/components/molecules/SubscriptionForm/UpdateBillingEmailForm.tsx @@ -4,13 +4,15 @@ import { SectionFlexRow, } from '../../organisms/settings/styled' import { SerializedSubscription } from '../../../interfaces/db/subscription' -import { StyledBillingInput } from '.' import { updateSubEmail } from '../../../api/teams/subscription/update' import { useToast } from '../../../../shared/lib/stores/toast' import Button, { LoadingButton, } from '../../../../shared/components/atoms/Button' import ButtonGroup from '../../../../shared/components/atoms/ButtonGroup' +import FormRow from '../../../../shared/components/molecules/Form/templates/FormRow' +import Form from '../../../../shared/components/molecules/Form' +import styled from '../../../../shared/lib/styled' interface UpdateBillingEmailFormProps { sub?: SerializedSubscription @@ -64,28 +66,41 @@ const UpdateBillingEmailForm = ({ } return ( -
- -

Update your billing email

- - - {sub.email} - + +

Update your billing email

+ + + {sub.email} + - + - - + -
-
+ + ) } +const Container = styled.div` + width: 100%; + + .button__group { + margin-top: ${({ theme }) => theme.sizes.spaces.md}px; + } +` + export default UpdateBillingEmailForm diff --git a/src/cloud/components/molecules/SubscriptionForm/UpdateBillingMethodForm.tsx b/src/cloud/components/molecules/SubscriptionForm/UpdateBillingMethodForm.tsx index fe2aaa26dd..c000bf1c89 100644 --- a/src/cloud/components/molecules/SubscriptionForm/UpdateBillingMethodForm.tsx +++ b/src/cloud/components/molecules/SubscriptionForm/UpdateBillingMethodForm.tsx @@ -6,19 +6,18 @@ import { import { SerializedSubscription } from '../../../interfaces/db/subscription' import { updateSubMethod } from '../../../api/teams/subscription/update' import { useElements, useStripe, CardElement } from '@stripe/react-stripe-js' -import { - StripeElementStyle, - StripeCardElementChangeEvent, -} from '@stripe/stripe-js' -import { selectTheme } from '../../../lib/styled' +import { StripeCardElementChangeEvent } from '@stripe/stripe-js' import { useSettings } from '../../../lib/stores/settings' -import { StyledCardElementContainer } from './index' import Alert from '../../../../components/atoms/Alert' import { useToast } from '../../../../shared/lib/stores/toast' import ButtonGroup from '../../../../shared/components/atoms/ButtonGroup' import Button, { LoadingButton, } from '../../../../shared/components/atoms/Button' +import FormStripeInput from '../../../../shared/components/molecules/Form/atoms/FormStripeInput' +import FormRow from '../../../../shared/components/molecules/Form/templates/FormRow' +import Form from '../../../../shared/components/molecules/Form' +import styled from '../../../../shared/lib/styled' interface UpdateBillingMethodFormProps { sub?: SerializedSubscription @@ -69,20 +68,6 @@ const UpdateBillingMethodForm = ({ } } - const stripeFormStyle: StripeElementStyle = useMemo(() => { - const theme = selectTheme(settings['general.theme']) - return { - base: { - color: theme.emphasizedTextColor, - fontFamily: theme.fontFamily, - fontSize: `${theme.fontSizes.default}px`, - '::placeholder': { - color: theme.subtleTextColor, - }, - }, - } - }, [settings]) - const currentCardBrand = sub?.cardBrand const [newCardBrand, setNewCardBrand] = useState('unknown') @@ -124,7 +109,7 @@ const UpdateBillingMethodForm = ({ } return ( -
+

Update your Credit Card

@@ -142,32 +127,46 @@ const UpdateBillingMethodForm = ({ )} - - - - - - - - + + + + + - Update - - + + + + Update + + +
-
+ ) } +const Container = styled.div` + width: 100%; + + .button__group { + margin-top: ${({ theme }) => theme.sizes.spaces.md}px; + } +` + export default UpdateBillingMethodForm diff --git a/src/cloud/components/molecules/SubscriptionForm/UpdateBillingPromo.tsx b/src/cloud/components/molecules/SubscriptionForm/UpdateBillingPromo.tsx index 9a9c503d1f..af012fa2c9 100644 --- a/src/cloud/components/molecules/SubscriptionForm/UpdateBillingPromo.tsx +++ b/src/cloud/components/molecules/SubscriptionForm/UpdateBillingPromo.tsx @@ -1,13 +1,15 @@ import React, { useState, useCallback } from 'react' import { SectionIntroduction } from '../../organisms/settings/styled' import { SerializedSubscription } from '../../../interfaces/db/subscription' -import { StyledBillingInput } from '.' import { redeemPromo } from '../../../api/teams/subscription' import { useToast } from '../../../../shared/lib/stores/toast' import Button, { LoadingButton, } from '../../../../shared/components/atoms/Button' import ButtonGroup from '../../../../shared/components/atoms/ButtonGroup' +import Form from '../../../../shared/components/molecules/Form' +import FormRow from '../../../../shared/components/molecules/Form/templates/FormRow' +import styled from '../../../../shared/lib/styled' interface UpdateBillingPromoFormProps { sub?: SerializedSubscription @@ -76,17 +78,31 @@ const UpdateBillingPromoForm = ({ } return ( -
- -

Apply a promotion code

- +

Apply a promotion code

+
+ - + @@ -100,9 +116,17 @@ const UpdateBillingPromoForm = ({ Apply - -
+ + ) } +const Container = styled.div` + width: 100%; + + .button__group { + margin-top: ${({ theme }) => theme.sizes.spaces.md}px; + } +` + export default UpdateBillingPromoForm diff --git a/src/cloud/components/molecules/SubscriptionForm/index.tsx b/src/cloud/components/molecules/SubscriptionForm/index.tsx index edaf8f35d2..beaff36086 100644 --- a/src/cloud/components/molecules/SubscriptionForm/index.tsx +++ b/src/cloud/components/molecules/SubscriptionForm/index.tsx @@ -3,28 +3,23 @@ import { useStripe, useElements, CardElement } from '@stripe/react-stripe-js' import { createSubscription } from '../../../api/teams/subscription' import { SerializedTeam } from '../../../interfaces/db/team' import { SerializedSubscription } from '../../../interfaces/db/subscription' -import { inputStyle } from '../../../lib/styled/styleFunctions' -import styled from '../../../lib/styled' -import Stripe, { StripeElementStyle } from '@stripe/stripe-js' +import Stripe from '@stripe/stripe-js' import { useSettings } from '../../../lib/stores/settings' -import { selectTheme } from '../../../lib/styled' import { usePage } from '../../../lib/stores/pageStore' -import { - stripeProPlanUnit, - stripeStandardPlanUnit, - UpgradePlans, - stripeProJpyPlanUnit, - stripeStandardJpyPlanUnit, -} from '../../../lib/stripe' -import plur from 'plur' -import Icon from '../../../../components/atoms/Icon' -import { mdiChevronDown, mdiChevronRight } from '@mdi/js' +import { UpgradePlans } from '../../../lib/stripe' import Alert from '../../../../components/atoms/Alert' import { useToast } from '../../../../shared/lib/stores/toast' import Button, { LoadingButton, } from '../../../../shared/components/atoms/Button' import ButtonGroup from '../../../../shared/components/atoms/ButtonGroup' +import SubscriptionCostSummary from '../../organisms/Subscription/SubscriptionCostSummary' +import { isEligibleForDiscount } from '../../../lib/subscription' +import FormRow from '../../../../shared/components/molecules/Form/templates/FormRow' +import Form from '../../../../shared/components/molecules/Form' +import styled from '../../../../shared/lib/styled' +import FormStripeInput from '../../../../shared/components/molecules/Form/atoms/FormStripeInput' +import { mdiChevronDown, mdiChevronRight } from '@mdi/js' interface SubscriptionFormProps { team: SerializedTeam @@ -126,20 +121,6 @@ const SubscriptionForm = ({ } } - const stripeFormStyle: StripeElementStyle = useMemo(() => { - const theme = selectTheme(settings['general.theme']) - return { - base: { - color: theme.emphasizedTextColor, - fontFamily: theme.fontFamily, - fontSize: `${theme.fontSizes.default}px`, - '::placeholder': { - color: theme.subtleTextColor, - }, - }, - } - }, [settings]) - const [newCardBrand, setNewCardBrand] = useState('unknown') const handleCardElementChange = useCallback( @@ -153,224 +134,117 @@ const SubscriptionForm = ({ return newCardBrand.toLowerCase() === 'jcb' }, [newCardBrand]) - const unitPrice = useMemo(() => { - if (currentPlan === 'pro') { - if (usingJpyPricing) { - return `¥${stripeProJpyPlanUnit}` - } - return `$${stripeProPlanUnit}` - } - if (usingJpyPricing) { - return `¥${stripeStandardJpyPlanUnit}` - } - return `$${stripeStandardPlanUnit}` - }, [currentPlan, usingJpyPricing]) - const numberOfMembers = permissions.length - const totalMonthlyPrice = useMemo(() => { - if (currentPlan === 'pro') { - if (usingJpyPricing) { - return `¥${stripeProJpyPlanUnit * numberOfMembers}` - } - return `$${stripeProPlanUnit * numberOfMembers}` - } - if (usingJpyPricing) { - return `¥${stripeStandardJpyPlanUnit * numberOfMembers}` - } - return `$${stripeStandardPlanUnit * numberOfMembers}` - }, [currentPlan, usingJpyPricing, numberOfMembers]) - return ( - -
- - - - {currentPlan === 'pro' ? 'Pro' : 'Standard'} - {' '} - {unitPrice} × {permissions.length}{' '} - {plur('member', permissions.length)} × 1 month - - - - - {totalMonthlyPrice} - -
- {usingJpyPricing && ( - - We can only accept JPY(Japanese Yen) when paying by JCB cards. - - )} - Payment Method - - +
+ - - - {ongoingTrial != null &&

Your free trial will be stopped.

} - - {showPromoCode && ( - + We can only accept JPY(Japanese Yen) when paying by JCB cards. + + )} + Payment Method + + + + - )} - - {onCancel != null && ( + + + + {showPromoCode && ( + )} - - Subscribe - - - + + {onCancel != null && ( + + )} + + Subscribe + + + + ) } export default SubscriptionForm -export const StyledUpgradePlan = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - padding: ${({ theme }) => theme.space.small}px 0; - border: dashed ${({ theme }) => theme.subtleBorderColor}; - border-width: 1px 0; -` - -export const StyledCalcuration = styled.div` - .plan-name { - display: inline-block; - margin-right: ${({ theme }) => theme.space.xsmall}px; - padding: 2px ${({ theme }) => theme.space.xxsmall}px; - background-color: ${({ theme }) => theme.infoBackgroundColor}; - border-radius: 3px; - color: ${({ theme }) => theme.whiteTextColor}; - font-size: ${({ theme }) => theme.fontSizes.xsmall}px; - } -` - export const StyledPaymentHeader = styled.h3` - margin: ${({ theme }) => theme.space.large}px 0 - ${({ theme }) => theme.space.default}px; - font-size: ${({ theme }) => theme.fontSizes.medium}px; -` - -export const StyledTotal = styled.div` - display: flex; - align-items: center; - justify-content: space-between; - padding: ${({ theme }) => theme.space.small}px 0; - border-bottom: 2px solid ${({ theme }) => theme.subtleBorderColor}; - font-size: ${({ theme }) => theme.fontSizes.large}px; - - label { - margin: 0; - font-weight: bold; - } + margin: ${({ theme }) => theme.sizes.spaces.l}px 0 + ${({ theme }) => theme.sizes.spaces.df}px; + font-size: ${({ theme }) => theme.sizes.fonts.md}px; ` -export const StyledBillingInput = styled.input` - ${inputStyle} - flex-grow: 1; - flex-shrink: 1; - width: 100%; - height: 40px; - margin: ${({ theme }) => theme.space.default}px 0; - padding: ${({ theme }) => theme.space.xsmall}px - ${({ theme }) => theme.space.small}px; - border-radius: 2px; -` - -export const StyledBillingSeatsInput = styled.input` - ${inputStyle} - flex-grow: 0; - flex-shrink: 0; - text-align: right; - width: 100px; - height: 40px; - padding: ${({ theme }) => theme.space.xsmall}px - ${({ theme }) => theme.space.small}px; - border-radius: 2px; -` - -export const StyledSubscriptionForm = styled.form` - width: 540px; - margin-top: ${({ theme }) => theme.space.default}px; - - .btn-primary, - .btn-secondary { - margin-bottom: ${({ theme }) => theme.space.default}px; - } - - .StripeElement { - margin-top: 2px; - } - - .sub__coupon { - display: inline-flex; - align-items: center; - transition: 200ms color; - background: none; - border: none; - outline: none; - padding: 0; - color: ${({ theme }) => theme.primaryTextColor}; - margin-bottom: ${({ theme }) => theme.space.xxsmall}px; - - &:hover, - &:focus, - &:active { - text-decoration: underline; - } - - svg { - margin-left: ${({ theme }) => theme.space.xxsmall}px; - } - } - +export const Container = styled.div` .button__group { margin-top: 40px; } ` - -export const StyledCardElementContainer = styled.div` - ${inputStyle} - height: 40px; - padding: ${({ theme }) => theme.space.xsmall}px - ${({ theme }) => theme.space.small}px; - border-radius: 2px; -` diff --git a/src/cloud/components/organisms/Subscription/PlanTables.tsx b/src/cloud/components/organisms/Subscription/PlanTables.tsx index 40bc450571..8dcd8b20f7 100644 --- a/src/cloud/components/organisms/Subscription/PlanTables.tsx +++ b/src/cloud/components/organisms/Subscription/PlanTables.tsx @@ -7,7 +7,7 @@ import { stripeProPlanUnit, stripeStandardPlanUnit, UpgradePlans, - discountPlans, + newUserDiscountPlans, } from '../../../lib/stripe' import { freePlanDocLimit, @@ -136,7 +136,9 @@ const PlanTables = ({ {discounted && ( - ${stripeStandardPlanUnit - discountPlans.standard.amountOff} + $ + {stripeStandardPlanUnit - + newUserDiscountPlans.standard.amountOff} )}
@@ -146,9 +148,9 @@ const PlanTables = ({
{discounted && (
- {discountPlans.standard.percentageOff}% OFF for{' '} - {discountPlans.standard.durationInMonths}{' '} - {plur('month', discountPlans.standard.durationInMonths)} + {newUserDiscountPlans.standard.percentageOff}% OFF for{' '} + {newUserDiscountPlans.standard.durationInMonths}{' '} + {plur('month', newUserDiscountPlans.standard.durationInMonths)}
)}
@@ -198,7 +200,7 @@ const PlanTables = ({ {discounted && ( - ${stripeProPlanUnit - discountPlans.pro.amountOff} + ${stripeProPlanUnit - newUserDiscountPlans.pro.amountOff} )}
@@ -208,9 +210,9 @@ const PlanTables = ({
{discounted && (
- {discountPlans.pro.percentageOff}% OFF for{' '} - {discountPlans.pro.durationInMonths}{' '} - {plur('month', discountPlans.pro.durationInMonths)} + {newUserDiscountPlans.pro.percentageOff}% OFF for{' '} + {newUserDiscountPlans.pro.durationInMonths}{' '} + {plur('month', newUserDiscountPlans.pro.durationInMonths)}
)}
diff --git a/src/cloud/components/organisms/Subscription/SubscriptionCostSummary.tsx b/src/cloud/components/organisms/Subscription/SubscriptionCostSummary.tsx new file mode 100644 index 0000000000..c0da4ab88f --- /dev/null +++ b/src/cloud/components/organisms/Subscription/SubscriptionCostSummary.tsx @@ -0,0 +1,155 @@ +import React, { useMemo } from 'react' +import styled from '../../../../shared/lib/styled' +import cc from 'classcat' +import { AppComponent } from '../../../../shared/lib/types' +import plur from 'plur' +import { + stripeProJpyPlanUnit, + stripeProPlanUnit, + stripeStandardJpyPlanUnit, + stripeStandardPlanUnit, + UpgradePlans, + newUserDiscountPlans, +} from '../../../lib/stripe' +import Icon from '../../../../shared/components/atoms/Icon' +import { mdiGiftOutline } from '@mdi/js' + +interface SubscriptionCostSummaryProps { + plan: UpgradePlans + seats: number + usingJpyPricing: boolean + discounted?: boolean +} + +const SubscriptionCostSummary: AppComponent = ({ + plan, + seats, + discounted, + usingJpyPricing, + children, + className, +}) => { + const currencyMarker = usingJpyPricing ? '¥' : '$' + const discount = discounted + ? plan === 'pro' + ? newUserDiscountPlans.pro + : newUserDiscountPlans.standard + : undefined + + const pricePerUnit = useMemo(() => { + switch (plan) { + case 'pro': + return usingJpyPricing ? stripeProJpyPlanUnit : stripeProPlanUnit + case 'standard': + default: + return usingJpyPricing + ? stripeStandardJpyPlanUnit + : stripeStandardPlanUnit + } + }, [plan, usingJpyPricing]) + + const discountedPricePerUnit = useMemo(() => { + if (discount == null) { + return 0 + } + + if (!usingJpyPricing) { + return discount.amountOff + } + + return discount.amountOff * 100 + }, [discount, usingJpyPricing]) + + return ( + +
+
+ {plan} + {currencyMarker} + {pricePerUnit} × {seats} {plur('member', seats)} × 1 month +
+
+ {currencyMarker} + {pricePerUnit * seats} +
+
+ {discount != null && ( +
+
+ + {discount.durationInMonths}{' '} + {plur('month', discount.durationInMonths)} discount +
+
+ -{discount.percentageOff}% +
+
+ )} +
+ + Total Monthly Price + + + {currencyMarker} + {pricePerUnit * seats - discountedPricePerUnit * seats} + +
+ {children} +
+ ) +} + +const Container = styled.div` + display: flex; + flex-direction: column; + font-size: ${({ theme }) => theme.sizes.fonts.md}px; + + .subscription__cost__summary__discount__icon { + margin-right: ${({ theme }) => theme.sizes.spaces.sm}px; + } + + .subscription__cost__summary__row { + display: flex; + align-items: center; + justify-content: space-between; + padding: ${({ theme }) => theme.sizes.spaces.sm}px 0; + border-top: 1px solid transparent; + border-bottom: 1px dashed ${({ theme }) => theme.colors.border.main}; + color: ${({ theme }) => theme.colors.text.secondary}; + } + + .subscription__cost__summary__row + .subscription__cost__summary__row { + border-top: 1px dashed ${({ theme }) => theme.colors.border.main}; + } + + .subscription__cost__summary__row__description { + display: flex; + align-items: center; + } + + .subscription__cost__summary__plan { + display: inline-block; + text-transform: capitalize; + margin-right: ${({ theme }) => theme.sizes.spaces.sm}px; + padding: 2px ${({ theme }) => theme.sizes.spaces.xsm}px; + background-color: ${({ theme }) => theme.colors.variants.info.base}; + border-radius: 3px; + color: ${({ theme }) => theme.colors.variants.info.text}; + font-size: ${({ theme }) => theme.sizes.fonts.xsm}px; + } + + .subscription__cost__summary__row--total { + display: flex; + align-items: center; + justify-content: space-between; + padding: ${({ theme }) => theme.sizes.spaces.sm}px 0; + border-bottom: 2px solid ${({ theme }) => theme.colors.border.main}; + font-size: ${({ theme }) => theme.sizes.fonts.l}px; + } +` + +export default SubscriptionCostSummary diff --git a/src/cloud/components/organisms/Subscription/SubscriptionManagement.tsx b/src/cloud/components/organisms/Subscription/SubscriptionManagement.tsx index 07eb900767..6e8bf4e5fa 100644 --- a/src/cloud/components/organisms/Subscription/SubscriptionManagement.tsx +++ b/src/cloud/components/organisms/Subscription/SubscriptionManagement.tsx @@ -1,5 +1,4 @@ import { mdiOpenInNew } from '@mdi/js' -import plur from 'plur' import React, { useCallback, useState } from 'react' import Icon from '../../../../components/atoms/Icon' import Spinner from '../../../../components/atoms/Spinner' @@ -11,24 +10,14 @@ import { SerializedSubscription } from '../../../interfaces/db/subscription' import { SerializedTeam } from '../../../interfaces/db/team' import { getFormattedDateFromUnixTimestamp } from '../../../lib/date' import { usePage } from '../../../lib/stores/pageStore' -import { - stripeProPlanUnit, - stripeStandardPlanUnit, - UpgradePlans, - stripeProJpyPlanUnit, - stripeStandardJpyPlanUnit, -} from '../../../lib/stripe' +import { UpgradePlans } from '../../../lib/stripe' import styled from '../../../lib/styled' import Button from '../../atoms/Button' import Flexbox from '../../atoms/Flexbox' -import { - StyledCalcuration, - StyledTotal, - StyledUpgradePlan, -} from '../../molecules/SubscriptionForm' import { SectionIntroduction } from '../settings/styled' import PlanTables from './PlanTables' import Alert from '../../../../components/atoms/Alert' +import SubscriptionCostSummary from './SubscriptionCostSummary' interface SubscriptionManagementProps { subscription: SerializedSubscription @@ -134,157 +123,104 @@ const SubscriptionManagement = ({ targetedPlan === 'Pro' ? 'Upgrade' : 'Downgrade' const usingJpyPricing = (subscription.cardBrand || '').toLowerCase() === 'jcb' - + console.log(subscription) return ( <> - - - {subscription.plan === 'pro' ? ( -
- - - Pro - {!usingJpyPricing - ? `$${stripeProPlanUnit} ` - : `¥${stripeProJpyPlanUnit} `} - × {subscription.seats}{' '} - {plur('member', subscription.seats)} × 1 month - - - {!usingJpyPricing - ? `$${stripeProPlanUnit * subscription.seats}` - : `¥${stripeProJpyPlanUnit * subscription.seats}`} - - - - - + + + {usingJpyPricing && ( + + We can only accept JPY(Japanese Yen) when paying by JCB cards. + + )} + + {subscription.currentPeriodEnd !== 0 ? ( + subscription.status === 'canceled' ? ( +

+ Your subscription will be canceled on{' '} + {getFormattedDateFromUnixTimestamp( + subscription.currentPeriodEnd + )}{' '} + upon reception of your last invoice . +

+ ) : ( +

+ Will bill to the credit card ending in{' '} - {!usingJpyPricing - ? `$${stripeProPlanUnit * subscription.seats}` - : `¥${stripeProJpyPlanUnit * subscription.seats}`} - - -

- ) : subscription.plan === 'standard' ? ( -
- - - Standard - {!usingJpyPricing - ? `$${stripeStandardPlanUnit} ` - : `¥${stripeStandardJpyPlanUnit} `} - × {subscription.seats}{' '} - {plur('member', subscription.seats)} × 1 month - - - {!usingJpyPricing - ? `$${stripeStandardPlanUnit * subscription.seats}` - : `¥${stripeStandardJpyPlanUnit * subscription.seats}`} - - - - + {subscription.last4} + {subscription.cardBrand != null && + ` (${subscription.cardBrand})`} + {' '} + at{' '} - {!usingJpyPricing - ? `$${stripeStandardPlanUnit * subscription.seats}` - : `¥${stripeStandardJpyPlanUnit * subscription.seats}`} - - -
- ) : null} - {usingJpyPricing && ( - - We can only accept JPY(Japanese Yen) when paying by JCB cards. - - )} - - {subscription.currentPeriodEnd !== 0 ? ( - subscription.status === 'canceled' ? ( -

- Your subscription will be canceled on{' '} {getFormattedDateFromUnixTimestamp( subscription.currentPeriodEnd - )}{' '} - upon reception of your last invoice . -

- ) : ( -

- Will bill to the credit card ending in{' '} - - {subscription.last4} - {subscription.cardBrand != null && - ` (${subscription.cardBrand})`} - {' '} - at{' '} - - {getFormattedDateFromUnixTimestamp( - subscription.currentPeriodEnd - )} - - .{' '} - - Edit Card - -

- ) - ) : null} + )} + + .{' '} + + Edit Card + +

+ ) + ) : null} -

- Billing email is {subscription.email}.{' '} - - Edit Billing Email - -

-

- You can see the{' '} +

+ Billing email is {subscription.email}.{' '} + + Edit Billing Email + +

+

+ You can see the{' '} + + Billing History + {fetchingHistory ? ( + + ) : ( + + )} + +

+

+ + Apply a coupon + +

+

+ {showPlanTables ? ( setShowPlanTables(false)} > - Billing History - {fetchingHistory ? ( - - ) : ( - - )} + Hide -

-

- - Apply a coupon + ) : ( + setShowPlanTables(true)} + > + Change plans -

-

- {showPlanTables ? ( - setShowPlanTables(false)} - > - Hide - - ) : ( - setShowPlanTables(true)} - > - Change plans - - )} -

-
-
-
+ )} +

+ + {showPlanTables && (

-
- - - ${stripeProPlanUnit} × {subscription.seats}{' '} - {plur('member', subscription.seats)} × 1 month - - - - - ${subscription.seats * stripeProPlanUnit} - -
+ ) : ( <> @@ -368,20 +299,13 @@ const SubscriptionManagement = ({

-
- - - ${stripeStandardPlanUnit} × {subscription.seats}{' '} - {plur('member', subscription.seats)} × 1 month - - - - - - ${subscription.seats * stripeStandardPlanUnit} - - -
+ )} @@ -497,11 +421,6 @@ const StyledPopup = styled.div` } ` -const StyledBillingContainer = styled.div` - width: 540px; - margin-top: ${({ theme }) => theme.space.default}px; -` - const StyledBillingDescription = styled.div` margin-top: ${({ theme }) => theme.space.large}px; diff --git a/src/cloud/components/organisms/settings/SubscriptionTab.tsx b/src/cloud/components/organisms/settings/SubscriptionTab.tsx index c3d321dc29..cf00afd2ca 100644 --- a/src/cloud/components/organisms/settings/SubscriptionTab.tsx +++ b/src/cloud/components/organisms/settings/SubscriptionTab.tsx @@ -125,6 +125,5 @@ const SubscriptionTab = () => { export default SubscriptionTab const StyledBillingContainer = styled.div` - width: 540px; margin-top: ${({ theme }) => theme.space.default}px; ` diff --git a/src/cloud/components/organisms/settings/UpgradeTab.tsx b/src/cloud/components/organisms/settings/UpgradeTab.tsx index 8fc4dab922..cc873f18e6 100644 --- a/src/cloud/components/organisms/settings/UpgradeTab.tsx +++ b/src/cloud/components/organisms/settings/UpgradeTab.tsx @@ -1,6 +1,5 @@ import React, { useState, useEffect, useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' -import { SectionRow } from './styled' import { usePage } from '../../../lib/stores/pageStore' import { PageStoreWithTeam } from '../../../interfaces/pageStore' import { Elements } from '@stripe/react-stripe-js' @@ -142,38 +141,41 @@ const UpgradeTab = ({ return ( + Confirm and enter your payment information (Service provided by{' '} + Stripe) + + } body={ -
-
- {currentUserPermissions.role !== 'admin' ? ( - - Only admins can access this content. - - ) : ( - !(subscription != null && subscription.status === 'active') && ( - - - - - - ) - )} -
-
+ + {currentUserPermissions.role !== 'admin' ? ( + + Only admins can access this content. + + ) : ( + !(subscription != null && subscription.status === 'active') && ( + + + + ) + )} + } >
) } +const SubscriptionFormContainer = styled.div`` + const StyledFYI = styled.p` .type-link { text-decoration: underline; diff --git a/src/cloud/interfaces/db/subscription.ts b/src/cloud/interfaces/db/subscription.ts index 0a81478faa..8d0633ffb5 100644 --- a/src/cloud/interfaces/db/subscription.ts +++ b/src/cloud/interfaces/db/subscription.ts @@ -5,6 +5,7 @@ export interface SerializeableSubscriptionProps { plan: UpgradePlans customerId: string subscriptionId: string + discountId?: string seats: number status: | 'active' diff --git a/src/cloud/interfaces/db/team.ts b/src/cloud/interfaces/db/team.ts index 9e67de14e4..2a3ecbc2ba 100644 --- a/src/cloud/interfaces/db/team.ts +++ b/src/cloud/interfaces/db/team.ts @@ -12,6 +12,7 @@ export interface SerializableTeamProps { icon?: SerializedIcon state: TeamOnboardingState personal: boolean + discountId?: string } export interface SerializedUnserializableTeamProps { diff --git a/src/cloud/lib/stripe.ts b/src/cloud/lib/stripe.ts index 68ae436357..deb42699fa 100644 --- a/src/cloud/lib/stripe.ts +++ b/src/cloud/lib/stripe.ts @@ -13,7 +13,7 @@ type DiscountParameters = { type Discounts = Record -export const discountPlans: Discounts = { +export const newUserDiscountPlans: Discounts = { standard: { durationInMonths: 3, amountOff: 1, diff --git a/src/shared/components/atoms/Button.tsx b/src/shared/components/atoms/Button.tsx index 92a5f7f460..8f16d6b1af 100644 --- a/src/shared/components/atoms/Button.tsx +++ b/src/shared/components/atoms/Button.tsx @@ -211,7 +211,7 @@ const StyledButton = styled.button` padding: 0; border: 0; height: auto !important; - display: inline; + display: inline-flex; .button__spinner { border-color: ${({ theme }) => theme.colors.text.link}; diff --git a/src/shared/components/atoms/Link.tsx b/src/shared/components/atoms/Link.tsx index e89e015413..d19d655ad6 100644 --- a/src/shared/components/atoms/Link.tsx +++ b/src/shared/components/atoms/Link.tsx @@ -54,7 +54,7 @@ const Container = styled.a` transition: 200ms color; text-decoration: none; color: ${({ theme }) => theme.colors.text.link} - padding: 0 ${({ theme }) => theme.sizes.spaces.sm}px; + padding: 0 ${({ theme }) => theme.sizes.spaces.xsm}px; cursor: pointer; &:hover { diff --git a/src/shared/components/molecules/Form/atoms/FormStripeInput.tsx b/src/shared/components/molecules/Form/atoms/FormStripeInput.tsx new file mode 100644 index 0000000000..6d4cf0090e --- /dev/null +++ b/src/shared/components/molecules/Form/atoms/FormStripeInput.tsx @@ -0,0 +1,72 @@ +import React, { useMemo } from 'react' +import cc from 'classcat' +import styled from '../../../../lib/styled' +import { + formInputHeight, + selectV2Theme, +} from '../../../../lib/styled/styleFunctions' + +import Stripe, { StripeElementStyle } from '@stripe/stripe-js' +import { CardElement } from '@stripe/react-stripe-js' +import { ThemeTypes } from '../../../../lib/styled/types' + +export interface FormStripeInputProps { + id?: string + className?: string + theme: ThemeTypes + onChange: (event: Stripe.StripeCardElementChangeEvent) => void +} + +const FormStripeInput = ({ + className, + id, + onChange, + theme, +}: FormStripeInputProps) => { + const stripeFormStyle: StripeElementStyle = useMemo(() => { + const themeProps = selectV2Theme(theme) + return { + base: { + fontFamily: themeProps.fonts.family, + fontSize: `${themeProps.sizes.fonts.df}px`, + color: themeProps.colors.text.primary, + '::placeholder': { + color: themeProps.colors.text.subtle, + }, + ':focus': {}, + }, + } + }, [theme]) + + return ( + + + + ) +} + +export default FormStripeInput + +const Container = styled.div` + flex: 1 1 auto; + padding: 0 ${({ theme }) => theme.sizes.spaces.sm}px; + border-radius: ${({ theme }) => theme.borders.radius}px; + ${formInputHeight} + border: 1px solid ${({ theme }) => theme.colors.border.main}; + + .StripeElement { + line-height: 32px !important; + height: 100%; + margin: 0 !important; + display: flex !important; + flex-direction: row !important; + align-items: center !important; + } +` diff --git a/src/shared/components/molecules/Form/templates/FormRow.tsx b/src/shared/components/molecules/Form/templates/FormRow.tsx index 89cedd49c9..2ab34cb978 100644 --- a/src/shared/components/molecules/Form/templates/FormRow.tsx +++ b/src/shared/components/molecules/Form/templates/FormRow.tsx @@ -23,6 +23,7 @@ export type FormRowProps = { title?: React.ReactNode required?: boolean description?: React.ReactNode + fullWidth?: boolean items?: ( | { type: 'input' @@ -41,11 +42,12 @@ export type FormRowProps = { )[] } -const FormRow: AppComponent<{ row: FormRowProps }> = ({ - row, +const FormRow: AppComponent<{ row?: FormRowProps }> = ({ + row = { required: false, title: '', items: [] }, className, children, }) => { + const items = row.items || [] return ( = ({ ])} > {row.title != null &&
{row.title}
} - {row.items != null && ( -
- {row.items.map((item, k) => ( -
- {item.type === 'input' ? ( - - ) : item.type === 'select' ? ( - - ) : item.type === 'select--string' ? ( - - ) : item.type === 'textarea' ? ( - - ) : item.type === 'image' ? ( - - ) : item.type === 'emoji' ? ( - - ) : item.type === 'button' ? ( - - {item.props.label} - - ) : ( - item.element - )} -
- ))} - {children} -
- )} +
+ {items.map((item, k) => ( +
+ {item.type === 'input' ? ( + + ) : item.type === 'select' ? ( + + ) : item.type === 'select--string' ? ( + + ) : item.type === 'textarea' ? ( + + ) : item.type === 'image' ? ( + + ) : item.type === 'emoji' ? ( + + ) : item.type === 'button' ? ( + {item.props.label} + ) : ( + item.element + )} +
+ ))} + {children} +
{row.description != null && (
{row.description}
)} From 3a82310d2f4622dbd0b26e246f7d06ebbe140228 Mon Sep 17 00:00:00 2001 From: davy-c Date: Mon, 24 May 2021 10:12:42 +0900 Subject: [PATCH 88/91] fix trial alert --- src/cloud/components/molecules/SubscriptionForm/index.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/cloud/components/molecules/SubscriptionForm/index.tsx b/src/cloud/components/molecules/SubscriptionForm/index.tsx index beaff36086..e92eb259a5 100644 --- a/src/cloud/components/molecules/SubscriptionForm/index.tsx +++ b/src/cloud/components/molecules/SubscriptionForm/index.tsx @@ -175,10 +175,7 @@ const SubscriptionForm = ({ /> diff --git a/src/cloud/components/molecules/SubscriptionForm/UpdateBillingMethodForm.tsx b/src/cloud/components/molecules/SubscriptionForm/UpdateBillingMethodForm.tsx index c000bf1c89..2f0337fda5 100644 --- a/src/cloud/components/molecules/SubscriptionForm/UpdateBillingMethodForm.tsx +++ b/src/cloud/components/molecules/SubscriptionForm/UpdateBillingMethodForm.tsx @@ -136,12 +136,7 @@ const UpdateBillingMethodForm = ({ /> - + diff --git a/src/cloud/components/molecules/SubscriptionForm/UpdateBillingPromo.tsx b/src/cloud/components/molecules/SubscriptionForm/UpdateBillingPromo.tsx index 07bf61697c..f788471727 100644 --- a/src/cloud/components/molecules/SubscriptionForm/UpdateBillingPromo.tsx +++ b/src/cloud/components/molecules/SubscriptionForm/UpdateBillingPromo.tsx @@ -10,6 +10,8 @@ import ButtonGroup from '../../../../shared/components/atoms/ButtonGroup' import Form from '../../../../shared/components/molecules/Form' import FormRow from '../../../../shared/components/molecules/Form/templates/FormRow' import styled from '../../../../shared/lib/styled' +import Banner from '../../../../shared/components/atoms/Banner' +import { mdiGiftOff } from '@mdi/js' interface UpdateBillingPromoFormProps { sub?: SerializedSubscription @@ -79,6 +81,11 @@ const UpdateBillingPromoForm = ({ return ( + {sub.couponId != null && ( + + Applying a promotion code will end your current discount + + )}

Apply a promotion code

- + diff --git a/src/cloud/components/molecules/SubscriptionForm/index.tsx b/src/cloud/components/molecules/SubscriptionForm/index.tsx index a385a0e8b1..858cf53cad 100644 --- a/src/cloud/components/molecules/SubscriptionForm/index.tsx +++ b/src/cloud/components/molecules/SubscriptionForm/index.tsx @@ -6,7 +6,7 @@ import { SerializedSubscription } from '../../../interfaces/db/subscription' import Stripe from '@stripe/stripe-js' import { useSettings } from '../../../lib/stores/settings' import { usePage } from '../../../lib/stores/pageStore' -import { UpgradePlans } from '../../../lib/stripe' +import { discountPlans, UpgradePlans } from '../../../lib/stripe' import Alert from '../../../../components/atoms/Alert' import { useToast } from '../../../../shared/lib/stores/toast' import Button, { @@ -20,6 +20,7 @@ import Form from '../../../../shared/components/molecules/Form' import styled from '../../../../shared/lib/styled' import FormStripeInput from '../../../../shared/components/molecules/Form/atoms/FormStripeInput' import { mdiChevronDown, mdiChevronRight } from '@mdi/js' +import Banner from '../../../../shared/components/atoms/Banner' interface SubscriptionFormProps { team: SerializedTeam @@ -136,6 +137,21 @@ const SubscriptionForm = ({ const numberOfMembers = permissions.length + const eligibleDiscount = useMemo(() => { + if (!isEligibleForDiscount(team)) { + return + } + + switch (currentPlan) { + case 'pro': + return discountPlans.newUserPro + case 'standard': + return discountPlans.newUserStandard + default: + return + } + }, [currentPlan, team]) + return ( @@ -143,7 +159,7 @@ const SubscriptionForm = ({ usingJpyPricing={usingJpyPricing} plan={currentPlan} seats={numberOfMembers} - discounted={isEligibleForDiscount(team)} + discount={eligibleDiscount} /> {usingJpyPricing && ( @@ -191,20 +207,28 @@ const SubscriptionForm = ({ {showPromoCode && ( - + {isEligibleForDiscount(team) && ( + + Applying a promotion code will prevent you to receive other + discounts + + )} + + ], + }} + /> + )} {onCancel != null && ( diff --git a/src/cloud/components/organisms/FreeTrialPopup/index.tsx b/src/cloud/components/organisms/FreeTrialPopup/index.tsx index 1df4ff098d..11761f3f9d 100644 --- a/src/cloud/components/organisms/FreeTrialPopup/index.tsx +++ b/src/cloud/components/organisms/FreeTrialPopup/index.tsx @@ -1,6 +1,5 @@ import React, { useState, useCallback } from 'react' import styled from '../../../lib/styled' -import CustomButton from '../../atoms/buttons/CustomButton' import Flexbox from '../../atoms/Flexbox' import Spinner from '../../atoms/CustomSpinner' import { startTeamFreeTrial } from '../../../api/teams/subscription/trial' @@ -8,6 +7,7 @@ import { SerializedTeam } from '../../../interfaces/db/team' import { usePage } from '../../../lib/stores/pageStore' import { freeTrialPeriodDays } from '../../../lib/subscription' import { useToast } from '../../../../shared/lib/stores/toast' +import Button from '../../../../shared/components/atoms/Button' interface FreeTrialPopupProps { team: SerializedTeam @@ -56,8 +56,8 @@ const FreeTrialPopup = ({ team, close }: FreeTrialPopupProps) => {

No credit card information is necessary for now.

- - + @@ -96,6 +96,15 @@ const StyledFreeTrialPopup = styled.div` right: 0; bottom: 0; overflow: hidden; + .button__group { + button { + margin: 0; + } + + button + button { + margin-top: 8px; + } + } ` const StyledFreeTrialPopupBackground = styled.div` diff --git a/src/cloud/components/organisms/Subscription/PlanTables.tsx b/src/cloud/components/organisms/Subscription/PlanTables.tsx index 8dcd8b20f7..ec89830240 100644 --- a/src/cloud/components/organisms/Subscription/PlanTables.tsx +++ b/src/cloud/components/organisms/Subscription/PlanTables.tsx @@ -4,10 +4,10 @@ import { useMediaQuery } from 'react-responsive' import { SerializedSubscription } from '../../../interfaces/db/subscription' import { SerializedTeam } from '../../../interfaces/db/team' import { + discountPlans, stripeProPlanUnit, stripeStandardPlanUnit, UpgradePlans, - newUserDiscountPlans, } from '../../../lib/stripe' import { freePlanDocLimit, @@ -137,8 +137,11 @@ const PlanTables = ({ {discounted && ( $ - {stripeStandardPlanUnit - - newUserDiscountPlans.standard.amountOff} + {Math.round( + stripeStandardPlanUnit - + stripeStandardPlanUnit * + (discountPlans.newUserStandard.percentageOff / 100) + )} )}
@@ -148,9 +151,9 @@ const PlanTables = ({
{discounted && (
- {newUserDiscountPlans.standard.percentageOff}% OFF for{' '} - {newUserDiscountPlans.standard.durationInMonths}{' '} - {plur('month', newUserDiscountPlans.standard.durationInMonths)} + {discountPlans.newUserStandard.percentageOff}% OFF for{' '} + {discountPlans.newUserStandard.durationInMonths}{' '} + {plur('month', discountPlans.newUserStandard.durationInMonths)}
)}
@@ -200,7 +203,12 @@ const PlanTables = ({ {discounted && ( - ${stripeProPlanUnit - newUserDiscountPlans.pro.amountOff} + $ + {Math.round( + stripeProPlanUnit - + stripeProPlanUnit * + (discountPlans.newUserPro.percentageOff / 100) + )} )}
@@ -210,9 +218,9 @@ const PlanTables = ({
{discounted && (
- {newUserDiscountPlans.pro.percentageOff}% OFF for{' '} - {newUserDiscountPlans.pro.durationInMonths}{' '} - {plur('month', newUserDiscountPlans.pro.durationInMonths)} + {discountPlans.newUserPro.percentageOff}% OFF for{' '} + {discountPlans.newUserPro.durationInMonths}{' '} + {plur('month', discountPlans.newUserPro.durationInMonths)}
)}
@@ -258,6 +266,7 @@ const Container = styled.div` flex-direction: row; flex-wrap: nowrap; flex: 1 1 auto; + font-size: ${({ theme }) => theme.sizes.fonts.df}px; .plan__item__footer { position: absolute; diff --git a/src/cloud/components/organisms/Subscription/SubscriptionCostSummary.tsx b/src/cloud/components/organisms/Subscription/SubscriptionCostSummary.tsx index c0da4ab88f..ab731cecb5 100644 --- a/src/cloud/components/organisms/Subscription/SubscriptionCostSummary.tsx +++ b/src/cloud/components/organisms/Subscription/SubscriptionCostSummary.tsx @@ -9,7 +9,7 @@ import { stripeStandardJpyPlanUnit, stripeStandardPlanUnit, UpgradePlans, - newUserDiscountPlans, + CloudDiscountParameters, } from '../../../lib/stripe' import Icon from '../../../../shared/components/atoms/Icon' import { mdiGiftOutline } from '@mdi/js' @@ -18,23 +18,18 @@ interface SubscriptionCostSummaryProps { plan: UpgradePlans seats: number usingJpyPricing: boolean - discounted?: boolean + discount?: CloudDiscountParameters } const SubscriptionCostSummary: AppComponent = ({ plan, seats, - discounted, + discount, usingJpyPricing, children, className, }) => { const currencyMarker = usingJpyPricing ? '¥' : '$' - const discount = discounted - ? plan === 'pro' - ? newUserDiscountPlans.pro - : newUserDiscountPlans.standard - : undefined const pricePerUnit = useMemo(() => { switch (plan) { @@ -48,18 +43,6 @@ const SubscriptionCostSummary: AppComponent = ({ } }, [plan, usingJpyPricing]) - const discountedPricePerUnit = useMemo(() => { - if (discount == null) { - return 0 - } - - if (!usingJpyPricing) { - return discount.amountOff - } - - return discount.amountOff * 100 - }, [discount, usingJpyPricing]) - return (
@@ -95,7 +78,12 @@ const SubscriptionCostSummary: AppComponent = ({ {currencyMarker} - {pricePerUnit * seats - discountedPricePerUnit * seats} + {Math.round( + pricePerUnit * seats - + pricePerUnit * + seats * + (discount == null ? 0 : discount.percentageOff / 100) + )}
{children} diff --git a/src/cloud/components/organisms/Subscription/SubscriptionManagement.tsx b/src/cloud/components/organisms/Subscription/SubscriptionManagement.tsx index 6e8bf4e5fa..81589f1c37 100644 --- a/src/cloud/components/organisms/Subscription/SubscriptionManagement.tsx +++ b/src/cloud/components/organisms/Subscription/SubscriptionManagement.tsx @@ -1,5 +1,5 @@ -import { mdiOpenInNew } from '@mdi/js' -import React, { useCallback, useState } from 'react' +import { mdiGiftOff, mdiOpenInNew } from '@mdi/js' +import React, { useCallback, useMemo, useState } from 'react' import Icon from '../../../../components/atoms/Icon' import Spinner from '../../../../components/atoms/Spinner' import { useToast } from '../../../../shared/lib/stores/toast' @@ -10,14 +10,19 @@ import { SerializedSubscription } from '../../../interfaces/db/subscription' import { SerializedTeam } from '../../../interfaces/db/team' import { getFormattedDateFromUnixTimestamp } from '../../../lib/date' import { usePage } from '../../../lib/stores/pageStore' -import { UpgradePlans } from '../../../lib/stripe' +import { discountPlans, UpgradePlans } from '../../../lib/stripe' import styled from '../../../lib/styled' -import Button from '../../atoms/Button' import Flexbox from '../../atoms/Flexbox' import { SectionIntroduction } from '../settings/styled' import PlanTables from './PlanTables' import Alert from '../../../../components/atoms/Alert' import SubscriptionCostSummary from './SubscriptionCostSummary' +import Banner from '../../../../shared/components/atoms/Banner' +import Button from '../../../../shared/components/atoms/Button' +import { + newUserProCouponId, + newUserStandardCouponId, +} from '../../../lib/consts' interface SubscriptionManagementProps { subscription: SerializedSubscription @@ -123,7 +128,25 @@ const SubscriptionManagement = ({ targetedPlan === 'Pro' ? 'Upgrade' : 'Downgrade' const usingJpyPricing = (subscription.cardBrand || '').toLowerCase() === 'jcb' - console.log(subscription) + + const currentSubscriptionDiscount = useMemo(() => { + if (subscription.couponId == null) { + return + } + + console.log(subscription.couponId) + console.log(newUserProCouponId) + console.log(newUserStandardCouponId) + switch (subscription.couponId) { + case newUserProCouponId: + return discountPlans.newUserPro + case newUserStandardCouponId: + return discountPlans.newUserStandard + default: + return discountPlans.migration + } + }, [subscription.couponId]) + return ( <> @@ -131,7 +154,7 @@ const SubscriptionManagement = ({ plan={subscription.plan} seats={subscription.seats} usingJpyPricing={usingJpyPricing} - discounted={subscription.discountId != null} + discount={currentSubscriptionDiscount} /> {usingJpyPricing && ( @@ -246,6 +269,11 @@ const SubscriptionManagement = ({

{subscriptionPlanChange} to {targetedPlan}

+ {currentSubscriptionDiscount?.percentageOff !== 100 && ( + + Changing plans will end your current discount. + + )} {targetedPlan === 'Free' ? ( <>

@@ -277,7 +305,6 @@ const SubscriptionManagement = ({ className='popup__billing' seats={subscription.seats} plan={'pro'} - discounted={subscription.discountId != null} usingJpyPricing={usingJpyPricing} /> @@ -303,13 +330,16 @@ const SubscriptionManagement = ({ className='popup__billing' seats={subscription.seats} plan={'standard'} - discounted={subscription.discountId != null} usingJpyPricing={usingJpyPricing} /> )} - +