From 11581ca9c336162c997dcff31a81dcb2ff7d6f76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tha=C3=AFs?= Date: Tue, 20 Feb 2024 15:28:15 -0300 Subject: [PATCH] feat: create Settings/Accounts/Calendars page (#4090) * feat: create Settings/Accounts/Calendars page Closes #4059 * docs: add SettingsAccountsCalendars stories * refactor: add SettingsNavigationDrawerItem component --- packages/twenty-front/src/App.tsx | 5 + .../SettingsAccountsSettingsSection.tsx | 22 ++-- .../components/SettingsNavigationCard.tsx | 24 +++-- .../SettingsNavigationDrawerItem.tsx | 42 ++++++++ .../SettingsNavigationDrawerItems.tsx | 102 +++++------------- .../settings/utils/getSettingsPagePath.ts | 4 + .../src/modules/types/SettingsPath.ts | 3 +- .../components/NavigationDrawerItem.tsx | 2 +- .../__stories__/NavigationDrawer.stories.tsx | 16 +-- .../modules/workspace/types/FeatureFlagKey.ts | 7 +- .../accounts/SettingsAccountsCalendars.tsx | 22 ++++ .../SettingsAccountsCalendars.stories.tsx | 30 ++++++ .../core/feature-flag/feature-flag.entity.ts | 5 +- .../typeorm-seeds/core/feature-flags.ts | 5 + 14 files changed, 184 insertions(+), 105 deletions(-) create mode 100644 packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItem.tsx create mode 100644 packages/twenty-front/src/modules/settings/utils/getSettingsPagePath.ts create mode 100644 packages/twenty-front/src/pages/settings/accounts/SettingsAccountsCalendars.tsx create mode 100644 packages/twenty-front/src/pages/settings/accounts/__stories__/SettingsAccountsCalendars.stories.tsx diff --git a/packages/twenty-front/src/App.tsx b/packages/twenty-front/src/App.tsx index 9abf50e83..cf74464c5 100644 --- a/packages/twenty-front/src/App.tsx +++ b/packages/twenty-front/src/App.tsx @@ -20,6 +20,7 @@ import { RecordIndexPage } from '~/pages/object-record/RecordIndexPage'; import { RecordShowPage } from '~/pages/object-record/RecordShowPage'; import { Opportunities } from '~/pages/opportunities/Opportunities'; import { SettingsAccounts } from '~/pages/settings/accounts/SettingsAccounts'; +import { SettingsAccountsCalendars } from '~/pages/settings/accounts/SettingsAccountsCalendars'; import { SettingsAccountsEmails } from '~/pages/settings/accounts/SettingsAccountsEmails'; import { SettingsAccountsEmailsInboxSettings } from '~/pages/settings/accounts/SettingsAccountsEmailsInboxSettings'; import { SettingsNewAccount } from '~/pages/settings/accounts/SettingsNewAccount'; @@ -95,6 +96,10 @@ export const App = () => { path={SettingsPath.NewAccount} element={} /> + } + /> } diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsSettingsSection.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsSettingsSection.tsx index 0bd6a76e2..2f09bd5d6 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsSettingsSection.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsSettingsSection.tsx @@ -2,9 +2,12 @@ import { useNavigate } from 'react-router-dom'; import styled from '@emotion/styled'; import { SettingsNavigationCard } from '@/settings/components/SettingsNavigationCard'; +import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; +import { SettingsPath } from '@/types/SettingsPath'; import { IconCalendarEvent, IconMailCog } from '@/ui/display/icon'; import { H2Title } from '@/ui/display/typography/components/H2Title'; import { Section } from '@/ui/layout/section/components/Section'; +import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; const StyledCardsContainer = styled.div` display: flex; @@ -12,12 +15,9 @@ const StyledCardsContainer = styled.div` margin-top: ${({ theme }) => theme.spacing(6)}; `; -const StyledSettingsNavigationCard = styled(SettingsNavigationCard)` - color: ${({ theme }) => theme.font.color.extraLight}; -`; - export const SettingsAccountsSettingsSection = () => { const navigate = useNavigate(); + const isCalendarEnabled = useIsFeatureEnabled('IS_CALENDAR_ENABLED'); return (
@@ -29,18 +29,22 @@ export const SettingsAccountsSettingsSection = () => { navigate('/settings/accounts/emails')} + onClick={() => + navigate(getSettingsPagePath(SettingsPath.AccountsEmails)) + } > Set email visibility, manage your blocklist and more. - + navigate(getSettingsPagePath(SettingsPath.AccountsCalendars)) + } > Configure and customize your calendar preferences. - +
); diff --git a/packages/twenty-front/src/modules/settings/components/SettingsNavigationCard.tsx b/packages/twenty-front/src/modules/settings/components/SettingsNavigationCard.tsx index 3167f4257..9c79a5a58 100644 --- a/packages/twenty-front/src/modules/settings/components/SettingsNavigationCard.tsx +++ b/packages/twenty-front/src/modules/settings/components/SettingsNavigationCard.tsx @@ -11,7 +11,7 @@ import { CardContent } from '@/ui/layout/card/components/CardContent'; type SettingsNavigationCardProps = { children: ReactNode; disabled?: boolean; - hasSoonPill?: boolean; + soon?: boolean; Icon: IconComponent; onClick?: () => void; title: string; @@ -22,7 +22,8 @@ const StyledCard = styled(Card)<{ disabled?: boolean; onClick?: () => void; }>` - color: ${({ theme }) => theme.font.color.tertiary}; + color: ${({ disabled, theme }) => + disabled ? theme.font.color.extraLight : theme.font.color.tertiary}; cursor: ${({ disabled, onClick }) => disabled ? 'not-allowed' : onClick ? 'pointer' : 'default'}; `; @@ -40,8 +41,9 @@ const StyledHeader = styled.div` gap: ${({ theme }) => theme.spacing(3)}; `; -const StyledTitle = styled.div` - color: ${({ theme }) => theme.font.color.secondary}; +const StyledTitle = styled.div<{ disabled?: boolean }>` + color: ${({ disabled, theme }) => + disabled ? 'inherit' : theme.font.color.secondary}; display: flex; flex: 1 0 auto; font-weight: ${({ theme }) => theme.font.weight.medium}; @@ -59,8 +61,8 @@ const StyledDescription = styled.div` export const SettingsNavigationCard = ({ children, - disabled, - hasSoonPill, + soon, + disabled = soon, Icon, onClick, title, @@ -69,13 +71,17 @@ export const SettingsNavigationCard = ({ const theme = useTheme(); return ( - + - + {title} - {hasSoonPill && } + {soon && } diff --git a/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItem.tsx b/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItem.tsx new file mode 100644 index 000000000..60733c0b6 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItem.tsx @@ -0,0 +1,42 @@ +import { useMatch, useResolvedPath } from 'react-router-dom'; + +import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; +import { SettingsPath } from '@/types/SettingsPath'; +import { + NavigationDrawerItem, + NavigationDrawerItemProps, +} from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem'; + +type SettingsNavigationDrawerItemProps = Pick< + NavigationDrawerItemProps, + 'Icon' | 'label' | 'level' | 'soon' +> & { + matchSubPages?: boolean; + path: SettingsPath; +}; + +export const SettingsNavigationDrawerItem = ({ + Icon, + label, + level, + matchSubPages = false, + path, + soon, +}: SettingsNavigationDrawerItemProps) => { + const href = getSettingsPagePath(path); + const isActive = !!useMatch({ + path: useResolvedPath(href).pathname, + end: !matchSubPages, + }); + + return ( + + ); +}; diff --git a/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItems.tsx b/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItems.tsx index 9c7c7a451..1fb4cdbda 100644 --- a/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItems.tsx +++ b/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItems.tsx @@ -1,8 +1,10 @@ import { useCallback } from 'react'; -import { useMatch, useNavigate, useResolvedPath } from 'react-router-dom'; +import { useNavigate } from 'react-router-dom'; import { useAuth } from '@/auth/hooks/useAuth'; +import { SettingsNavigationDrawerItem } from '@/settings/components/SettingsNavigationDrawerItem'; import { AppPath } from '@/types/AppPath'; +import { SettingsPath } from '@/types/SettingsPath'; import { IconApps, IconAt, @@ -31,67 +33,45 @@ export const SettingsNavigationDrawerItems = () => { navigate(AppPath.SignIn); }, [signOut, navigate]); + const isCalendarEnabled = useIsFeatureEnabled('IS_CALENDAR_ENABLED'); const isMessagingEnabled = useIsFeatureEnabled('IS_MESSAGING_ENABLED'); - const isIntegrationsItemActive = !!useMatch({ - path: useResolvedPath('/settings/integrations').pathname, - end: true, - }); - - const isAccountsItemActive = !!useMatch({ - path: useResolvedPath('/settings/accounts').pathname, - end: true, - }); - const isAccountsEmailsItemActive = !!useMatch({ - path: useResolvedPath('/settings/accounts/emails').pathname, - end: true, - }); return ( <> - - + {isMessagingEnabled && ( - - - )} @@ -99,55 +79,31 @@ export const SettingsNavigationDrawerItems = () => { - - - - - diff --git a/packages/twenty-front/src/modules/settings/utils/getSettingsPagePath.ts b/packages/twenty-front/src/modules/settings/utils/getSettingsPagePath.ts new file mode 100644 index 000000000..11c12d61f --- /dev/null +++ b/packages/twenty-front/src/modules/settings/utils/getSettingsPagePath.ts @@ -0,0 +1,4 @@ +import { SettingsPath } from '@/types/SettingsPath'; + +export const getSettingsPagePath = (path: Path) => + `/settings/${path}` as const; diff --git a/packages/twenty-front/src/modules/types/SettingsPath.ts b/packages/twenty-front/src/modules/types/SettingsPath.ts index 24b7446d5..de4a2c620 100644 --- a/packages/twenty-front/src/modules/types/SettingsPath.ts +++ b/packages/twenty-front/src/modules/types/SettingsPath.ts @@ -3,6 +3,7 @@ export enum SettingsPath { Appearance = 'profile/appearance', Accounts = 'accounts', NewAccount = 'accounts/new', + AccountsCalendars = 'accounts/calendars', AccountsEmails = 'accounts/emails', AccountsEmailsInboxSettings = 'accounts/emails/:accountUuid', Objects = 'objects', @@ -14,7 +15,7 @@ export enum SettingsPath { NewObject = 'objects/new', WorkspaceMembersPage = 'workspace-members', Workspace = 'workspace', - Developers = '', + Developers = 'developers', DevelopersNewApiKey = 'api-keys/new', DevelopersApiKeyDetail = 'api-keys/:apiKeyId', Integrations = 'integrations', diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerItem.tsx b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerItem.tsx index 6c542f292..69e1be741 100644 --- a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerItem.tsx +++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerItem.tsx @@ -8,7 +8,7 @@ import { isNavigationDrawerOpenState } from '@/ui/navigation/states/isNavigation import { MOBILE_VIEWPORT } from '@/ui/theme/constants/theme'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; -type NavigationDrawerItemProps = { +export type NavigationDrawerItemProps = { className?: string; label: string; level?: 1 | 2; diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/__stories__/NavigationDrawer.stories.tsx b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/__stories__/NavigationDrawer.stories.tsx index c7d04d18f..9d1739efb 100644 --- a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/__stories__/NavigationDrawer.stories.tsx +++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/__stories__/NavigationDrawer.stories.tsx @@ -1,6 +1,8 @@ import { Meta, StoryObj } from '@storybook/react'; import { Favorites } from '@/favorites/components/Favorites'; +import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; +import { SettingsPath } from '@/types/SettingsPath'; import { IconAt, IconBell, @@ -91,32 +93,32 @@ export const Submenu: Story = { @@ -125,12 +127,12 @@ export const Submenu: Story = { diff --git a/packages/twenty-front/src/modules/workspace/types/FeatureFlagKey.ts b/packages/twenty-front/src/modules/workspace/types/FeatureFlagKey.ts index b0b10fef2..1a85d16de 100644 --- a/packages/twenty-front/src/modules/workspace/types/FeatureFlagKey.ts +++ b/packages/twenty-front/src/modules/workspace/types/FeatureFlagKey.ts @@ -1,5 +1,6 @@ export type FeatureFlagKey = - | 'IS_MESSAGING_ENABLED' | 'IS_BLOCKLIST_ENABLED' - | 'IS_QUICK_ACTIONS_ENABLED' - | 'IS_NEW_RECORD_BOARD_ENABLED'; + | 'IS_CALENDAR_ENABLED' + | 'IS_MESSAGING_ENABLED' + | 'IS_NEW_RECORD_BOARD_ENABLED' + | 'IS_QUICK_ACTIONS_ENABLED'; diff --git a/packages/twenty-front/src/pages/settings/accounts/SettingsAccountsCalendars.tsx b/packages/twenty-front/src/pages/settings/accounts/SettingsAccountsCalendars.tsx new file mode 100644 index 000000000..d9f90f1ad --- /dev/null +++ b/packages/twenty-front/src/pages/settings/accounts/SettingsAccountsCalendars.tsx @@ -0,0 +1,22 @@ +import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; +import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; +import { SettingsPath } from '@/types/SettingsPath'; +import { IconSettings } from '@/ui/display/icon'; +import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer'; +import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb'; + +export const SettingsAccountsCalendars = () => ( + + + + + +); diff --git a/packages/twenty-front/src/pages/settings/accounts/__stories__/SettingsAccountsCalendars.stories.tsx b/packages/twenty-front/src/pages/settings/accounts/__stories__/SettingsAccountsCalendars.stories.tsx new file mode 100644 index 000000000..6b636ea4f --- /dev/null +++ b/packages/twenty-front/src/pages/settings/accounts/__stories__/SettingsAccountsCalendars.stories.tsx @@ -0,0 +1,30 @@ +import { Meta, StoryObj } from '@storybook/react'; + +import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; +import { SettingsPath } from '@/types/SettingsPath'; +import { + PageDecorator, + PageDecoratorArgs, +} from '~/testing/decorators/PageDecorator'; +import { graphqlMocks } from '~/testing/graphqlMocks'; + +import { SettingsAccountsCalendars } from '../SettingsAccountsCalendars'; + +const meta: Meta = { + title: 'Pages/Settings/Accounts/SettingsAccountsCalendars', + component: SettingsAccountsCalendars, + decorators: [PageDecorator], + args: { + routePath: getSettingsPagePath(SettingsPath.AccountsCalendars), + }, + parameters: { + layout: 'fullscreen', + msw: graphqlMocks, + }, +}; + +export default meta; + +export type Story = StoryObj; + +export const Default: Story = {}; diff --git a/packages/twenty-server/src/core/feature-flag/feature-flag.entity.ts b/packages/twenty-server/src/core/feature-flag/feature-flag.entity.ts index f106028ba..ea3528000 100644 --- a/packages/twenty-server/src/core/feature-flag/feature-flag.entity.ts +++ b/packages/twenty-server/src/core/feature-flag/feature-flag.entity.ts @@ -14,10 +14,11 @@ import { IDField } from '@ptc-org/nestjs-query-graphql'; import { Workspace } from 'src/core/workspace/workspace.entity'; export enum FeatureFlagKeys { - IsMessagingEnabled = 'IS_MESSAGING_ENABLED', IsBlocklistEnabled = 'IS_BLOCKLIST_ENABLED', - IsWorkspaceCleanable = 'IS_WORKSPACE_CLEANABLE', + IsCalendarEnabled = 'IS_CALENDAR_ENABLED', + IsMessagingEnabled = 'IS_MESSAGING_ENABLED', IsNewRecordBoardEnabled = 'IS_NEW_RECORD_BOARD_ENABLED', + IsWorkspaceCleanable = 'IS_WORKSPACE_CLEANABLE', } @Entity({ name: 'featureFlag', schema: 'core' }) diff --git a/packages/twenty-server/src/database/typeorm-seeds/core/feature-flags.ts b/packages/twenty-server/src/database/typeorm-seeds/core/feature-flags.ts index 5d0a77fea..63e07708f 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/core/feature-flags.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/core/feature-flags.ts @@ -15,6 +15,11 @@ export const seedFeatureFlags = async ( .into(`${schemaName}.${tableName}`, ['key', 'workspaceId', 'value']) .orIgnore() .values([ + { + key: FeatureFlagKeys.IsCalendarEnabled, + workspaceId: workspaceId, + value: true, + }, { key: FeatureFlagKeys.IsMessagingEnabled, workspaceId: workspaceId,