From cac3e116a3293f76b235a3cfe4184adac501cbc9 Mon Sep 17 00:00:00 2001 From: Ana Sofia Marin Alexandre <61988046+anamarn@users.noreply.github.com> Date: Wed, 18 Sep 2024 18:32:41 +0200 Subject: [PATCH] Add SettingsCard for Config Data Type and Accounts Settings (#7093) https://github.com/twentyhq/twenty/issues/6950 Add new Settings Card for Config Data Type and accounts Settings Before: Screenshot 2024-09-11 at 17 43 16 After: Screenshot 2024-09-17 at 14 15 18 Screenshot 2024-09-17 at 14 15 38 --- .../SettingsAccountsSettingsSection.tsx | 30 +++++++++--- ...ngsNavigationCard.tsx => SettingsCard.tsx} | 42 +++++++++++------ .../__stories__/SettingsCard.stories.tsx | 24 ++++++++++ .../data-model/constants/RelationTypes.ts | 15 +++--- .../constants/SettingsFieldTypeConfigs.ts | 4 +- .../SettingsDataModelFieldTypeSelect.tsx | 47 +++++++------------ .../SettingsObjectFieldDataType.tsx | 11 ++--- .../icon/assets/illustration-array.svg | 8 ++++ .../icon/assets/illustration-many-to-many.svg | 8 ++++ .../icon/assets/illustration-one-to-many.svg | 4 +- .../icon/assets/illustration-one-to-one.svg | 8 ++++ .../icon/components/IllustrationIconArray.tsx | 21 +++++++++ .../components/IllustrationIconManyToMany.tsx | 24 ++++++++++ .../components/IllustrationIconOneToOne.tsx | 25 ++++++++++ .../components/IllustrationIconWrapper.tsx | 4 +- packages/twenty-ui/src/display/index.ts | 3 ++ .../twenty-ui/src/theme/constants/Icon.ts | 2 +- 17 files changed, 207 insertions(+), 73 deletions(-) rename packages/twenty-front/src/modules/settings/components/{SettingsNavigationCard.tsx => SettingsCard.tsx} (68%) create mode 100644 packages/twenty-front/src/modules/settings/components/__stories__/SettingsCard.stories.tsx create mode 100644 packages/twenty-ui/src/display/icon/assets/illustration-array.svg create mode 100644 packages/twenty-ui/src/display/icon/assets/illustration-many-to-many.svg create mode 100644 packages/twenty-ui/src/display/icon/assets/illustration-one-to-one.svg create mode 100644 packages/twenty-ui/src/display/icon/components/IllustrationIconArray.tsx create mode 100644 packages/twenty-ui/src/display/icon/components/IllustrationIconManyToMany.tsx create mode 100644 packages/twenty-ui/src/display/icon/components/IllustrationIconOneToOne.tsx 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 3bcb19ea0..5ca4cb832 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsSettingsSection.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsSettingsSection.tsx @@ -1,11 +1,12 @@ import styled from '@emotion/styled'; import { H2Title, IconCalendarEvent, IconMailCog } from 'twenty-ui'; -import { SettingsNavigationCard } from '@/settings/components/SettingsNavigationCard'; +import { SettingsCard } from '@/settings/components/SettingsCard'; import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { SettingsPath } from '@/types/SettingsPath'; import { Section } from '@/ui/layout/section/components/Section'; import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink'; +import { useTheme } from '@emotion/react'; const StyledCardsContainer = styled.div` display: flex; @@ -14,6 +15,7 @@ const StyledCardsContainer = styled.div` `; export const SettingsAccountsSettingsSection = () => { + const theme = useTheme(); return (
{ /> - - Set email visibility, manage your blocklist and more. - + + } + title="Emails" + description="Set email visibility, manage your blocklist and more." + /> - - Configure and customize your calendar preferences. - + + } + title="Calendar" + description="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/SettingsCard.tsx similarity index 68% rename from packages/twenty-front/src/modules/settings/components/SettingsNavigationCard.tsx rename to packages/twenty-front/src/modules/settings/components/SettingsCard.tsx index 8d44dfcfc..7383b6e90 100644 --- a/packages/twenty-front/src/modules/settings/components/SettingsNavigationCard.tsx +++ b/packages/twenty-front/src/modules/settings/components/SettingsCard.tsx @@ -1,16 +1,16 @@ -import { ReactNode } from 'react'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; -import { IconChevronRight, IconComponent, Pill } from 'twenty-ui'; +import { IconChevronRight, Pill } from 'twenty-ui'; import { Card } from '@/ui/layout/card/components/Card'; import { CardContent } from '@/ui/layout/card/components/CardContent'; +import { ReactNode } from 'react'; -type SettingsNavigationCardProps = { - children: ReactNode; +type SettingsCardProps = { + description?: string; disabled?: boolean; soon?: boolean; - Icon: IconComponent; + Icon: ReactNode; onClick?: () => void; title: string; className?: string; @@ -24,19 +24,23 @@ const StyledCard = styled(Card)<{ disabled ? theme.font.color.extraLight : theme.font.color.tertiary}; cursor: ${({ disabled, onClick }) => disabled ? 'not-allowed' : onClick ? 'pointer' : 'default'}; + width: 100%; + & :hover { + background-color: ${({ theme }) => theme.background.quaternary}; + } `; -const StyledCardContent = styled(CardContent)` +const StyledCardContent = styled(CardContent)` display: flex; flex-direction: column; gap: ${({ theme }) => theme.spacing(2)}; - padding: ${({ theme }) => theme.spacing(4, 3)}; + padding: ${({ theme }) => theme.spacing(2, 2)}; `; const StyledHeader = styled.div` align-items: center; display: flex; - gap: ${({ theme }) => theme.spacing(3)}; + gap: ${({ theme }) => theme.spacing(2)}; `; const StyledTitle = styled.div<{ disabled?: boolean }>` @@ -54,18 +58,27 @@ const StyledIconChevronRight = styled(IconChevronRight)` `; const StyledDescription = styled.div` - padding-left: ${({ theme }) => theme.spacing(8)}; + padding-bottom: ${({ theme }) => theme.spacing(2)}; + padding-left: ${({ theme }) => theme.spacing(7)}; `; -export const SettingsNavigationCard = ({ - children, +const StyledIconContainer = styled.div` + align-items: center; + display: flex; + height: 24px; + justify-content: center; + width: 24px; +`; + +export const SettingsCard = ({ + description, soon, disabled = soon, Icon, onClick, title, className, -}: SettingsNavigationCardProps) => { +}: SettingsCardProps) => { const theme = useTheme(); return ( @@ -73,17 +86,18 @@ export const SettingsNavigationCard = ({ disabled={disabled} onClick={disabled ? undefined : onClick} className={className} + rounded={true} > - + {Icon} {title} {soon && } - {children} + {description && {description}} ); diff --git a/packages/twenty-front/src/modules/settings/components/__stories__/SettingsCard.stories.tsx b/packages/twenty-front/src/modules/settings/components/__stories__/SettingsCard.stories.tsx new file mode 100644 index 000000000..5bcb36f90 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/components/__stories__/SettingsCard.stories.tsx @@ -0,0 +1,24 @@ +import { SettingsCard } from '@/settings/components/SettingsCard'; +import { Meta, StoryObj } from '@storybook/react'; +import React from 'react'; +import { ComponentDecorator, IconMailCog } from 'twenty-ui'; + +const meta: Meta = { + title: 'Modules/Settings/SettingsCard', + component: SettingsCard, + decorators: [ComponentDecorator], +}; +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + onClick: () => {}, + Icon: React.createElement(IconMailCog), + title: 'Settings Card', + }, + argTypes: { + className: { control: 'false' }, + Icon: { control: 'false' }, + }, +}; diff --git a/packages/twenty-front/src/modules/settings/data-model/constants/RelationTypes.ts b/packages/twenty-front/src/modules/settings/data-model/constants/RelationTypes.ts index a03ceedf1..179f60fe8 100644 --- a/packages/twenty-front/src/modules/settings/data-model/constants/RelationTypes.ts +++ b/packages/twenty-front/src/modules/settings/data-model/constants/RelationTypes.ts @@ -1,9 +1,8 @@ import { IconComponent, - IconRelationManyToMany, - IconRelationManyToOne, - IconRelationOneToMany, - IconRelationOneToOne, + IllustrationIconManyToMany, + IllustrationIconOneToMany, + IllustrationIconOneToOne, } from 'twenty-ui'; import { RelationDefinitionType } from '~/generated-metadata/graphql'; @@ -22,24 +21,24 @@ export const RELATION_TYPES: Record< > = { [RelationDefinitionType.OneToMany]: { label: 'Has many', - Icon: IconRelationOneToMany, + Icon: IllustrationIconOneToMany, imageSrc: OneToManySvg, }, [RelationDefinitionType.OneToOne]: { label: 'Has one', - Icon: IconRelationOneToOne, + Icon: IllustrationIconOneToOne, imageSrc: OneToOneSvg, }, [RelationDefinitionType.ManyToOne]: { label: 'Belongs to one', - Icon: IconRelationManyToOne, + Icon: IllustrationIconOneToMany, imageSrc: OneToManySvg, isImageFlipped: true, }, // Not supported yet [RelationDefinitionType.ManyToMany]: { label: 'Belongs to many', - Icon: IconRelationManyToMany, + Icon: IllustrationIconManyToMany, imageSrc: OneToManySvg, isImageFlipped: true, }, diff --git a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsFieldTypeConfigs.ts b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsFieldTypeConfigs.ts index e604e2e34..1e6dbb981 100644 --- a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsFieldTypeConfigs.ts +++ b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsFieldTypeConfigs.ts @@ -1,6 +1,6 @@ import { - IconBracketsContain, IconComponent, + IllustrationIconArray, IllustrationIconCalendarEvent, IllustrationIconCalendarTime, IllustrationIconCurrency, @@ -186,7 +186,7 @@ export const SETTINGS_FIELD_TYPE_CONFIGS = { }, [FieldMetadataType.Array]: { label: 'Array', - Icon: IconBracketsContain, + Icon: IllustrationIconArray, category: 'Basic', exampleValue: ['value1', 'value2'], }, diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldTypeSelect.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldTypeSelect.tsx index 304843fe4..14e68598e 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldTypeSelect.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldTypeSelect.tsx @@ -1,8 +1,5 @@ -import styled from '@emotion/styled'; -import { Controller, useFormContext } from 'react-hook-form'; -import { z } from 'zod'; - import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; +import { SettingsCard } from '@/settings/components/SettingsCard'; import { SETTINGS_FIELD_TYPE_CATEGORIES } from '@/settings/data-model/constants/SettingsFieldTypeCategories'; import { SETTINGS_FIELD_TYPE_CATEGORY_DESCRIPTIONS } from '@/settings/data-model/constants/SettingsFieldTypeCategoryDescriptions'; import { @@ -13,12 +10,14 @@ import { useBooleanSettingsFormInitialValues } from '@/settings/data-model/field import { useCurrencySettingsFormInitialValues } from '@/settings/data-model/fields/forms/currency/hooks/useCurrencySettingsFormInitialValues'; import { useSelectSettingsFormInitialValues } from '@/settings/data-model/fields/forms/select/hooks/useSelectSettingsFormInitialValues'; import { SettingsSupportedFieldType } from '@/settings/data-model/types/SettingsSupportedFieldType'; -import { Button } from '@/ui/input/button/components/Button'; import { TextInput } from '@/ui/input/components/TextInput'; import { useTheme } from '@emotion/react'; +import styled from '@emotion/styled'; import { Section } from '@react-email/components'; import { useState } from 'react'; -import { H2Title, IconChevronRight, IconSearch } from 'twenty-ui'; +import { Controller, useFormContext } from 'react-hook-form'; +import { H2Title, IconSearch } from 'twenty-ui'; +import { z } from 'zod'; import { FieldMetadataType } from '~/generated-metadata/graphql'; export const settingsDataModelFieldTypeFormSchema = z.object({ @@ -51,13 +50,6 @@ const StyledTypeSelectContainer = styled.div` width: 100%; `; -const StyledButton = styled(Button)<{ isActive: boolean }>` - background: ${({ theme, isActive }) => - isActive ? theme.background.quaternary : theme.background.secondary}; - height: 40px; - width: 100%; - border-radius: ${({ theme }) => theme.border.radius.md}; -`; const StyledContainer = styled.div` display: flex; gap: ${({ theme }) => theme.spacing(2)}; @@ -66,20 +58,13 @@ const StyledContainer = styled.div` width: 100%; `; -const StyledButtonContainer = styled.div` +const StyledCardContainer = styled.div` display: flex; position: relative; width: calc(50% - ${({ theme }) => theme.spacing(1)}); `; -const StyledRightChevron = styled(IconChevronRight)` - color: ${({ theme }) => theme.font.color.secondary}; - position: absolute; - right: ${({ theme }) => theme.spacing(2)}; - top: 50%; - transform: translateY(-50%); -`; const StyledSearchInput = styled(TextInput)` width: 100%; `; @@ -90,9 +75,9 @@ export const SettingsDataModelFieldTypeSelect = ({ fieldMetadataItem, onFieldTypeSelect, }: SettingsDataModelFieldTypeSelectProps) => { + const theme = useTheme(); const { control } = useFormContext(); const [searchQuery, setSearchQuery] = useState(''); - const theme = useTheme(); const fieldTypeConfigs = Object.entries( SETTINGS_FIELD_TYPE_CONFIGS, ).filter( @@ -136,7 +121,7 @@ export const SettingsDataModelFieldTypeSelect = ({ ? (fieldMetadataItem.type as SettingsSupportedFieldType) : FieldMetadataType.Text } - render={({ field: { onChange, value } }) => ( + render={({ field: { onChange } }) => (
config.category === category) .map(([key, config]) => ( - - + { onChange(key as SettingsSupportedFieldType); @@ -168,13 +153,15 @@ export const SettingsDataModelFieldTypeSelect = ({ ); onFieldTypeSelect(); }} + Icon={ + + } title={config.label} - Icon={config.Icon} - size="small" - isActive={value === key} /> - - + ))}
diff --git a/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldDataType.tsx b/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldDataType.tsx index fb6457cf1..4fe7cccf7 100644 --- a/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldDataType.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldDataType.tsx @@ -1,6 +1,6 @@ -import { Link } from 'react-router-dom'; import { css, useTheme } from '@emotion/react'; import styled from '@emotion/styled'; +import { Link } from 'react-router-dom'; import { IconComponent, IconTwentyStar } from 'twenty-ui'; import { SettingsSupportedFieldType } from '@/settings/data-model/types/SettingsSupportedFieldType'; @@ -23,10 +23,9 @@ const StyledDataType = styled.div<{ border-radius: ${({ theme }) => theme.border.radius.sm}; display: flex; font-size: ${({ theme }) => theme.font.size.sm}; - gap: ${({ theme }) => theme.spacing(1)}; + gap: ${({ theme }) => theme.spacing(2)}; height: 20px; overflow: hidden; - padding: 0 ${({ theme }) => theme.spacing(2)}; text-decoration: none; ${({ to }) => @@ -36,11 +35,11 @@ const StyledDataType = styled.div<{ ` : ''} - ${({ theme, value }) => + ${({ value, theme }) => value === FieldMetadataType.Relation ? css` - border-color: ${theme.tag.background.purple}; - color: ${theme.color.purple}; + color: ${theme.font.color.secondary}; + text-decoration: underline; ` : ''} `; diff --git a/packages/twenty-ui/src/display/icon/assets/illustration-array.svg b/packages/twenty-ui/src/display/icon/assets/illustration-array.svg new file mode 100644 index 000000000..56b2864ff --- /dev/null +++ b/packages/twenty-ui/src/display/icon/assets/illustration-array.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/packages/twenty-ui/src/display/icon/assets/illustration-many-to-many.svg b/packages/twenty-ui/src/display/icon/assets/illustration-many-to-many.svg new file mode 100644 index 000000000..06f6a5539 --- /dev/null +++ b/packages/twenty-ui/src/display/icon/assets/illustration-many-to-many.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/packages/twenty-ui/src/display/icon/assets/illustration-one-to-many.svg b/packages/twenty-ui/src/display/icon/assets/illustration-one-to-many.svg index d8c0a8c2f..bc0c08aa5 100644 --- a/packages/twenty-ui/src/display/icon/assets/illustration-one-to-many.svg +++ b/packages/twenty-ui/src/display/icon/assets/illustration-one-to-many.svg @@ -1,7 +1,7 @@ - + - + diff --git a/packages/twenty-ui/src/display/icon/assets/illustration-one-to-one.svg b/packages/twenty-ui/src/display/icon/assets/illustration-one-to-one.svg new file mode 100644 index 000000000..1dcdff806 --- /dev/null +++ b/packages/twenty-ui/src/display/icon/assets/illustration-one-to-one.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/packages/twenty-ui/src/display/icon/components/IllustrationIconArray.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconArray.tsx new file mode 100644 index 000000000..b53d11867 --- /dev/null +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconArray.tsx @@ -0,0 +1,21 @@ +import { useTheme } from '@emotion/react'; +import IllustrationIconArrayRaw from '@ui/display/icon/assets/illustration-array.svg?react'; +import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper'; +import { IconComponentProps } from '@ui/display/icon/types/IconComponent'; +type IllustrationIconArrayProps = Pick; + +export const IllustrationIconArray = (props: IllustrationIconArrayProps) => { + const theme = useTheme(); + const size = props.size ?? theme.icon.size.lg; + const { color, fill } = theme.IllustrationIcon; + return ( + + + + ); +}; diff --git a/packages/twenty-ui/src/display/icon/components/IllustrationIconManyToMany.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconManyToMany.tsx new file mode 100644 index 000000000..d00f479c2 --- /dev/null +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconManyToMany.tsx @@ -0,0 +1,24 @@ +import { useTheme } from '@emotion/react'; +import IllustrationIconManyToManyRaw from '@ui/display/icon/assets/illustration-many-to-many.svg?react'; +import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper'; +import { IconComponentProps } from '@ui/display/icon/types/IconComponent'; + +type IllustrationIconManyToManyProps = Pick; + +export const IllustrationIconManyToMany = ( + props: IllustrationIconManyToManyProps, +) => { + const theme = useTheme(); + const size = props.size ?? theme.icon.size.lg; + const { color, fill } = theme.IllustrationIcon; + return ( + + + + ); +}; diff --git a/packages/twenty-ui/src/display/icon/components/IllustrationIconOneToOne.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconOneToOne.tsx new file mode 100644 index 000000000..0fd6a902a --- /dev/null +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconOneToOne.tsx @@ -0,0 +1,25 @@ +import { useTheme } from '@emotion/react'; +import { IllustrationIconWrapper } from '@ui/display/icon/components/IllustrationIconWrapper'; + +import IllustrationIconOneToOneRaw from '@ui/display/icon/assets/illustration-one-to-one.svg?react'; +import { IconComponentProps } from '@ui/display/icon/types/IconComponent'; + +type IllustrationIconOneToOneProps = Pick; + +export const IllustrationIconOneToOne = ( + props: IllustrationIconOneToOneProps, +) => { + const theme = useTheme(); + const size = props.size ?? theme.icon.size.lg; + const { color, fill } = theme.IllustrationIcon; + return ( + + + + ); +}; diff --git a/packages/twenty-ui/src/display/icon/components/IllustrationIconWrapper.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconWrapper.tsx index d47495ca9..4e583a312 100644 --- a/packages/twenty-ui/src/display/icon/components/IllustrationIconWrapper.tsx +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconWrapper.tsx @@ -2,12 +2,10 @@ import styled from '@emotion/styled'; const StyledRectangleIllustrationIcon = styled('div')` background-color: ${({ theme }) => theme.background.primary}; - border: solid ${({ theme }) => theme.border.color.medium}; + border: 0.75px solid ${({ theme }) => theme.border.color.medium}; border-radius: ${({ theme }) => theme.border.radius.sm}; display: flex; justify-content: center; - size: auto; - box-sizing: content-box; `; export const IllustrationIconWrapper = StyledRectangleIllustrationIcon; diff --git a/packages/twenty-ui/src/display/index.ts b/packages/twenty-ui/src/display/index.ts index 6aae61300..750694eda 100644 --- a/packages/twenty-ui/src/display/index.ts +++ b/packages/twenty-ui/src/display/index.ts @@ -18,14 +18,17 @@ export * from './icon/components/IconMicrosoft'; export * from './icon/components/IconRelationManyToOne'; export * from './icon/components/IconTwentyStar'; export * from './icon/components/IconTwentyStarFilled'; +export * from './icon/components/IllustrationIconArray'; export * from './icon/components/IllustrationIconCalendarEvent'; export * from './icon/components/IllustrationIconCalendarTime'; export * from './icon/components/IllustrationIconCurrency'; export * from './icon/components/IllustrationIconJson'; export * from './icon/components/IllustrationIconMail'; +export * from './icon/components/IllustrationIconManyToMany'; export * from './icon/components/IllustrationIconMap'; export * from './icon/components/IllustrationIconNumbers'; export * from './icon/components/IllustrationIconOneToMany'; +export * from './icon/components/IllustrationIconOneToOne'; export * from './icon/components/IllustrationIconPhone'; export * from './icon/components/IllustrationIconSetting'; export * from './icon/components/IllustrationIconStar'; diff --git a/packages/twenty-ui/src/theme/constants/Icon.ts b/packages/twenty-ui/src/theme/constants/Icon.ts index e103c0b8e..cfe9cbc79 100644 --- a/packages/twenty-ui/src/theme/constants/Icon.ts +++ b/packages/twenty-ui/src/theme/constants/Icon.ts @@ -3,7 +3,7 @@ export const ICON = { sm: 14, md: 16, lg: 20, - xl: 40, + xl: 24, }, stroke: { sm: 1.6,