diff --git a/packages/twenty-chrome-extension/src/options/modules/ui/display/typography/components/H2Title.tsx b/packages/twenty-chrome-extension/src/options/modules/ui/display/typography/components/H2Title.tsx index 2a3cfbf34..3d1dd2706 100644 --- a/packages/twenty-chrome-extension/src/options/modules/ui/display/typography/components/H2Title.tsx +++ b/packages/twenty-chrome-extension/src/options/modules/ui/display/typography/components/H2Title.tsx @@ -3,7 +3,7 @@ import styled from '@emotion/styled'; type H2TitleProps = { title: string; description?: string; - addornment?: React.ReactNode; + adornment?: React.ReactNode; }; const StyledContainer = styled.div` @@ -33,11 +33,11 @@ const StyledDescription = styled.h3` margin-top: ${({ theme }) => theme.spacing(3)}; `; -export const H2Title = ({ title, description, addornment }: H2TitleProps) => ( +export const H2Title = ({ title, description, adornment }: H2TitleProps) => ( {title} - {addornment} + {adornment} {description && {description}} diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelDetails.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelDetails.tsx index d017bf42d..13039837c 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelDetails.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsCalendarChannelDetails.tsx @@ -2,10 +2,10 @@ import { CalendarChannel } from '@/accounts/types/CalendarChannel'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; import { SettingsAccountsEventVisibilitySettingsCard } from '@/settings/accounts/components/SettingsAccountsCalendarVisibilitySettingsCard'; -import { SettingsOptionCardContent } from '@/settings/components/SettingsOptionCardContent'; +import { SettingsOptionCardContentToggle } from '@/settings/components/SettingsOptions/SettingsOptionCardContentToggle'; import styled from '@emotion/styled'; import { Section } from '@react-email/components'; -import { Card, H2Title } from 'twenty-ui'; +import { Card, H2Title, IconUserPlus } from 'twenty-ui'; import { CalendarChannelVisibility } from '~/generated-metadata/graphql'; const StyledDetailsContainer = styled.div` @@ -63,8 +63,9 @@ export const SettingsAccountsCalendarChannelDetails = ({ title="Contact auto-creation" description="Automatically create contacts for people you've participated in an event with." /> - - + - - + - theme.spacing(-6)}; + position: absolute; + top: 0; +`; + +const StyledContent = styled.div` + width: 100%; +`; +const StyledIconTool = styled(IconTool)` + margin-right: ${({ theme }) => theme.spacing(0.5)}; +`; + +type AdvancedSettingsWrapperProps = { + children: React.ReactNode; + dimension?: 'width' | 'height'; + hideIcon?: boolean; +}; + +export const AdvancedSettingsWrapper = ({ + children, + dimension = 'height', + hideIcon = false, +}: AdvancedSettingsWrapperProps) => { + const isAdvancedModeEnabled = useRecoilValue(isAdvancedModeEnabledState); + const { contentRef, motionAnimationVariants } = useExpandedAnimation( + isAdvancedModeEnabled, + dimension, + ); + + return ( + + {isAdvancedModeEnabled && ( + + + {!hideIcon && ( + + + + )} + {children} + + + )} + + ); +}; diff --git a/packages/twenty-front/src/modules/settings/components/SettingsCounter.tsx b/packages/twenty-front/src/modules/settings/components/SettingsCounter.tsx new file mode 100644 index 000000000..c9c7ce309 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/components/SettingsCounter.tsx @@ -0,0 +1,103 @@ +import { TextInput } from '@/ui/input/components/TextInput'; +import styled from '@emotion/styled'; +import { Button, IconMinus, IconPlus } from 'twenty-ui'; +import { castAsNumberOrNull } from '~/utils/cast-as-number-or-null'; + +type SettingsCounterProps = { + value: number; + onChange: (value: number) => void; + minValue?: number; + maxValue?: number; + disabled?: boolean; +}; + +const StyledCounterContainer = styled.div` + align-items: center; + display: flex; + gap: ${({ theme }) => theme.spacing(1)}; + margin-left: auto; + width: ${({ theme }) => theme.spacing(30)}; +`; + +const StyledTextInput = styled(TextInput)` + width: ${({ theme }) => theme.spacing(16)}; + input { + width: ${({ theme }) => theme.spacing(16)}; + height: ${({ theme }) => theme.spacing(6)}; + text-align: center; + font-weight: ${({ theme }) => theme.font.weight.medium}; + } +`; + +const StyledControlButton = styled(Button)` + height: ${({ theme }) => theme.spacing(6)}; + width: ${({ theme }) => theme.spacing(6)}; + padding: 0; + justify-content: center; + svg { + height: ${({ theme }) => theme.spacing(4)}; + width: ${({ theme }) => theme.spacing(4)}; + } +`; + +export const SettingsCounter = ({ + value, + onChange, + minValue = 0, + maxValue = 100, + disabled = false, +}: SettingsCounterProps) => { + const handleIncrementCounter = () => { + if (value < maxValue) { + onChange(value + 1); + } + }; + + const handleDecrementCounter = () => { + if (value > minValue) { + onChange(value - 1); + } + }; + + const handleTextInputChange = (value: string) => { + const castedNumber = castAsNumberOrNull(value); + if (castedNumber === null) { + onChange(minValue); + return; + } + + if (castedNumber < minValue) { + return; + } + + if (castedNumber > maxValue) { + onChange(maxValue); + return; + } + onChange(castedNumber); + }; + + return ( + + + + + + ); +}; diff --git a/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItems.tsx b/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItems.tsx index f762df6e8..c7d8569cf 100644 --- a/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItems.tsx +++ b/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItems.tsx @@ -23,7 +23,7 @@ import { import { useAuth } from '@/auth/hooks/useAuth'; import { billingState } from '@/client-config/states/billingState'; import { SettingsNavigationDrawerItem } from '@/settings/components/SettingsNavigationDrawerItem'; -import { useExpandedHeightAnimation } from '@/settings/hooks/useExpandedHeightAnimation'; +import { useExpandedAnimation } from '@/settings/hooks/useExpandedAnimation'; import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { SettingsPath } from '@/types/SettingsPath'; import { @@ -69,7 +69,7 @@ const StyledIconTool = styled(IconTool)` export const SettingsNavigationDrawerItems = () => { const isAdvancedModeEnabled = useRecoilValue(isAdvancedModeEnabledState); - const { contentRef, motionAnimationVariants } = useExpandedHeightAnimation( + const { contentRef, motionAnimationVariants } = useExpandedAnimation( isAdvancedModeEnabled, ); const { signOut } = useAuth(); diff --git a/packages/twenty-front/src/modules/settings/components/SettingsOptionCardContent.tsx b/packages/twenty-front/src/modules/settings/components/SettingsOptionCardContent.tsx deleted file mode 100644 index 3628119fb..000000000 --- a/packages/twenty-front/src/modules/settings/components/SettingsOptionCardContent.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { useTheme } from '@emotion/react'; -import styled from '@emotion/styled'; - -import { useId } from 'react'; -import { CardContent, IconComponent, Toggle } from 'twenty-ui'; - -type SettingsOptionCardContentProps = { - Icon?: IconComponent; - title: React.ReactNode; - description: string; - divider?: boolean; - checked: boolean; - onChange: (checked: boolean) => void; -}; - -const StyledCardContent = styled(CardContent)` - align-items: center; - display: flex; - gap: ${({ theme }) => theme.spacing(4)}; - cursor: pointer; - position: relative; - - &:hover { - background: ${({ theme }) => theme.background.transparent.lighter}; - } -`; - -const StyledTitle = styled.div` - color: ${({ theme }) => theme.font.color.primary}; - font-weight: ${({ theme }) => theme.font.weight.medium}; - margin-bottom: ${({ theme }) => theme.spacing(2)}; -`; - -const StyledDescription = styled.div` - color: ${({ theme }) => theme.font.color.tertiary}; - font-size: ${({ theme }) => theme.font.size.sm}; -`; - -const StyledIcon = styled.div` - align-items: center; - border: 2px solid ${({ theme }) => theme.border.color.light}; - border-radius: ${({ theme }) => theme.border.radius.sm}; - background-color: ${({ theme }) => theme.background.primary}; - display: flex; - height: ${({ theme }) => theme.spacing(8)}; - justify-content: center; - width: ${({ theme }) => theme.spacing(8)}; - min-width: ${({ theme }) => theme.icon.size.md}; -`; - -const StyledToggle = styled(Toggle)` - margin-left: auto; -`; - -const StyledCover = styled.span` - cursor: pointer; - inset: 0; - position: absolute; -`; - -export const SettingsOptionCardContent = ({ - Icon, - title, - description, - divider, - checked, - onChange, -}: SettingsOptionCardContentProps) => { - const theme = useTheme(); - - const toggleId = useId(); - - return ( - - {Icon && ( - - - - )} - - - - - {title} - - - - - {description} - - - - - ); -}; diff --git a/packages/twenty-front/src/modules/settings/components/SettingsOptions/SettingsOptionCardContentBase.tsx b/packages/twenty-front/src/modules/settings/components/SettingsOptions/SettingsOptionCardContentBase.tsx new file mode 100644 index 000000000..c89ac354a --- /dev/null +++ b/packages/twenty-front/src/modules/settings/components/SettingsOptions/SettingsOptionCardContentBase.tsx @@ -0,0 +1,37 @@ +import styled from '@emotion/styled'; +import { CardContent } from 'twenty-ui'; + +type StyledCardContentProps = { + disabled?: boolean; + divider?: boolean; +}; + +export const StyledSettingsOptionCardContent = styled( + CardContent, +)` + align-items: center; + display: flex; + gap: ${({ theme }) => theme.spacing(4)}; +`; +export const StyledSettingsOptionCardIcon = styled.div` + align-items: center; + border: 2px solid ${({ theme }) => theme.border.color.light}; + border-radius: ${({ theme }) => theme.border.radius.sm}; + background-color: ${({ theme }) => theme.background.primary}; + display: flex; + height: ${({ theme }) => theme.spacing(8)}; + justify-content: center; + width: ${({ theme }) => theme.spacing(8)}; + min-width: ${({ theme }) => theme.icon.size.md}; +`; + +export const StyledSettingsOptionCardTitle = styled.div` + color: ${({ theme }) => theme.font.color.primary}; + font-weight: ${({ theme }) => theme.font.weight.medium}; + margin-bottom: ${({ theme }) => theme.spacing(1)}; +`; + +export const StyledSettingsOptionCardDescription = styled.div` + color: ${({ theme }) => theme.font.color.tertiary}; + font-size: ${({ theme }) => theme.font.size.sm}; +`; diff --git a/packages/twenty-front/src/modules/settings/components/SettingsOptions/SettingsOptionCardContentCounter.tsx b/packages/twenty-front/src/modules/settings/components/SettingsOptions/SettingsOptionCardContentCounter.tsx new file mode 100644 index 000000000..e0d63e22b --- /dev/null +++ b/packages/twenty-front/src/modules/settings/components/SettingsOptions/SettingsOptionCardContentCounter.tsx @@ -0,0 +1,57 @@ +import { SettingsCounter } from '@/settings/components/SettingsCounter'; +import { + StyledSettingsOptionCardContent, + StyledSettingsOptionCardDescription, + StyledSettingsOptionCardIcon, + StyledSettingsOptionCardTitle, +} from '@/settings/components/SettingsOptions/SettingsOptionCardContentBase'; +import { SettingsOptionIconCustomizer } from '@/settings/components/SettingsOptions/SettingsOptionIconCustomizer'; +import { IconComponent } from 'twenty-ui'; + +type SettingsOptionCardContentCounterProps = { + Icon?: IconComponent; + title: React.ReactNode; + description?: string; + divider?: boolean; + disabled?: boolean; + value: number; + onChange: (value: number) => void; + minValue?: number; + maxValue?: number; +}; + +export const SettingsOptionCardContentCounter = ({ + Icon, + title, + description, + divider, + disabled = false, + value, + onChange, + minValue, + maxValue, +}: SettingsOptionCardContentCounterProps) => { + return ( + + {Icon && ( + + + + )} + + {title} + {description && ( + + {description} + + )} + + + + ); +}; diff --git a/packages/twenty-front/src/modules/settings/components/SettingsOptions/SettingsOptionCardContentSelect.tsx b/packages/twenty-front/src/modules/settings/components/SettingsOptions/SettingsOptionCardContentSelect.tsx new file mode 100644 index 000000000..f84c54ec4 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/components/SettingsOptions/SettingsOptionCardContentSelect.tsx @@ -0,0 +1,75 @@ +import { + StyledSettingsOptionCardContent, + StyledSettingsOptionCardDescription, + StyledSettingsOptionCardIcon, + StyledSettingsOptionCardTitle, +} from '@/settings/components/SettingsOptions/SettingsOptionCardContentBase'; +import { SettingsOptionIconCustomizer } from '@/settings/components/SettingsOptions/SettingsOptionIconCustomizer'; +import { Select } from '@/ui/input/components/Select'; +import styled from '@emotion/styled'; +import { IconComponent } from 'twenty-ui'; + +const StyledSettingsOptionCardSelect = styled(Select)` + margin-left: auto; + width: 120px; +`; + +type SelectValue = string | number | boolean | null; + +type SettingsOptionCardContentSelectProps = { + Icon?: IconComponent; + title: React.ReactNode; + description?: string; + divider?: boolean; + disabled?: boolean; + value: Value; + onChange: (value: SelectValue) => void; + options: { + value: Value; + label: string; + Icon?: IconComponent; + }[]; + selectClassName?: string; + dropdownId: string; + fullWidth?: boolean; +}; + +export const SettingsOptionCardContentSelect = ({ + Icon, + title, + description, + divider, + disabled = false, + value, + onChange, + options, + selectClassName, + dropdownId, + fullWidth, +}: SettingsOptionCardContentSelectProps) => { + return ( + + {Icon && ( + + + + )} + + {title} + + {description} + + + + + ); +}; diff --git a/packages/twenty-front/src/modules/settings/components/SettingsOptions/SettingsOptionCardContentToggle.tsx b/packages/twenty-front/src/modules/settings/components/SettingsOptions/SettingsOptionCardContentToggle.tsx new file mode 100644 index 000000000..53f3df5f4 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/components/SettingsOptions/SettingsOptionCardContentToggle.tsx @@ -0,0 +1,89 @@ +import { + StyledSettingsOptionCardContent, + StyledSettingsOptionCardDescription, + StyledSettingsOptionCardIcon, + StyledSettingsOptionCardTitle, +} from '@/settings/components/SettingsOptions/SettingsOptionCardContentBase'; +import { SettingsOptionIconCustomizer } from '@/settings/components/SettingsOptions/SettingsOptionIconCustomizer'; +import { useTheme } from '@emotion/react'; +import styled from '@emotion/styled'; +import { useId } from 'react'; +import { IconComponent, Toggle } from 'twenty-ui'; + +const StyledSettingsOptionCardToggleContent = styled( + StyledSettingsOptionCardContent, +)` + cursor: ${({ disabled }) => (disabled ? 'default' : 'pointer')}; + position: relative; + pointer-events: ${({ disabled }) => (disabled ? 'none' : 'auto')}; + + &:hover { + background: ${({ theme }) => theme.background.transparent.lighter}; + } +`; + +const StyledSettingsOptionCardToggleButton = styled(Toggle)` + margin-left: auto; +`; + +const StyledSettingsOptionCardToggleCover = styled.span` + cursor: pointer; + inset: 0; + position: absolute; +`; + +type SettingsOptionCardContentToggleProps = { + Icon?: IconComponent; + title: React.ReactNode; + description?: string; + divider?: boolean; + disabled?: boolean; + advancedMode?: boolean; + checked: boolean; + onChange: (checked: boolean) => void; +}; + +export const SettingsOptionCardContentToggle = ({ + Icon, + title, + description, + divider, + disabled = false, + advancedMode = false, + checked, + onChange, +}: SettingsOptionCardContentToggleProps) => { + const theme = useTheme(); + const toggleId = useId(); + + return ( + + {Icon && ( + + + + )} + + + + {title} + + + + + {description} + + + + + ); +}; diff --git a/packages/twenty-front/src/modules/settings/components/SettingsOptions/SettingsOptionIconCustomizer.tsx b/packages/twenty-front/src/modules/settings/components/SettingsOptions/SettingsOptionIconCustomizer.tsx new file mode 100644 index 000000000..1f576bb88 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/components/SettingsOptions/SettingsOptionIconCustomizer.tsx @@ -0,0 +1,33 @@ +import { useTheme } from '@emotion/react'; +import styled from '@emotion/styled'; +import { IconComponent } from 'twenty-ui'; + +type SettingsOptionIconCustomizerProps = { + Icon: IconComponent; + zoom?: number; + rotate?: number; +}; + +const StyledIconCustomizer = styled.div<{ zoom: number; rotate: number }>` + display: inline-flex; + align-items: center; + justify-content: center; + transform: scale(${({ zoom }) => zoom}) rotate(${({ rotate }) => rotate}deg); +`; + +export const SettingsOptionIconCustomizer = ({ + Icon, + zoom = 1, + rotate = -4, +}: SettingsOptionIconCustomizerProps) => { + const theme = useTheme(); + return ( + + + + ); +}; diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/boolean/components/SettingsDataModelFieldBooleanForm.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/boolean/components/SettingsDataModelFieldBooleanForm.tsx index 106fbac9a..4e50078d7 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/boolean/components/SettingsDataModelFieldBooleanForm.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/boolean/components/SettingsDataModelFieldBooleanForm.tsx @@ -1,11 +1,10 @@ -import styled from '@emotion/styled'; import { Controller, useFormContext } from 'react-hook-form'; -import { IconCheck, IconX, CardContent } from 'twenty-ui'; +import { IconCheck, IconX } from 'twenty-ui'; import { z } from 'zod'; import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; +import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect'; import { useBooleanSettingsFormInitialValues } from '@/settings/data-model/fields/forms/boolean/hooks/useBooleanSettingsFormInitialValues'; -import { Select } from '@/ui/input/components/Select'; import { isDefined } from '~/utils/isDefined'; export const settingsDataModelFieldBooleanFormSchema = z.object({ @@ -21,18 +20,6 @@ type SettingsDataModelFieldBooleanFormProps = { fieldMetadataItem: Pick; }; -const StyledContainer = styled(CardContent)` - padding-bottom: ${({ theme }) => theme.spacing(3.5)}; -`; - -const StyledLabel = styled.span` - color: ${({ theme }) => theme.font.color.light}; - display: block; - font-size: ${({ theme }) => theme.font.size.xs}; - font-weight: ${({ theme }) => theme.font.weight.semiBold}; - margin-bottom: 6px; -`; - export const SettingsDataModelFieldBooleanForm = ({ className, fieldMetadataItem, @@ -45,37 +32,36 @@ export const SettingsDataModelFieldBooleanForm = ({ }); return ( - - Default Value - ( - - )} - /> - + ( + + )} + /> ); }; diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldToggle.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldToggle.tsx deleted file mode 100644 index 5a9942073..000000000 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldToggle.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { useTheme } from '@emotion/react'; -import styled from '@emotion/styled'; -import { createPortal } from 'react-dom'; -import { - AppTooltip, - IconComponent, - IconInfoCircle, - Toggle, - TooltipDelay, -} from 'twenty-ui'; - -const StyledContainer = styled.div<{ disabled?: boolean }>` - align-items: center; - background-color: ${({ theme }) => theme.background.transparent.lighter}; - border: 1px solid ${({ theme }) => theme.border.color.medium}; - box-sizing: border-box; - border-radius: ${({ theme }) => theme.border.radius.sm}; - color: ${({ disabled, theme }) => - disabled ? theme.font.color.tertiary : theme.font.color.primary}; - display: flex; - gap: ${({ theme }) => theme.spacing(1)}; - height: ${({ theme }) => theme.spacing(8)}; - justify-content: space-between; - padding: 0 ${({ theme }) => theme.spacing(2)}; -`; - -const StyledGroup = styled.div` - align-items: center; - display: flex; - gap: ${({ theme }) => theme.spacing(2)}; -`; - -interface SettingsDataModelFieldToggleProps { - disabled?: boolean; - Icon?: IconComponent; - label: string; - tooltip?: string; - value?: boolean; - onChange: (value: boolean) => void; -} - -export const SettingsDataModelFieldToggle = ({ - disabled, - Icon, - label, - tooltip, - value, - onChange, -}: SettingsDataModelFieldToggleProps) => { - const theme = useTheme(); - const infoCircleElementId = `info-circle-id-${Math.random().toString(36).slice(2)}`; - - return ( - - - {Icon && ( - - )} - {label} - - - {tooltip && ( - - )} - {tooltip && - createPortal( - , - document.body, - )} - - - - ); -}; diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/currency/components/SettingsDataModelFieldCurrencyForm.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/currency/components/SettingsDataModelFieldCurrencyForm.tsx index 8c0c63947..e51936010 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/currency/components/SettingsDataModelFieldCurrencyForm.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/currency/components/SettingsDataModelFieldCurrencyForm.tsx @@ -3,10 +3,10 @@ import { z } from 'zod'; import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { currencyFieldDefaultValueSchema } from '@/object-record/record-field/validation-schemas/currencyFieldDefaultValueSchema'; +import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect'; import { SETTINGS_FIELD_CURRENCY_CODES } from '@/settings/data-model/constants/SettingsFieldCurrencyCodes'; import { useCurrencySettingsFormInitialValues } from '@/settings/data-model/fields/forms/currency/hooks/useCurrencySettingsFormInitialValues'; -import { Select } from '@/ui/input/components/Select'; -import { CardContent } from 'twenty-ui'; +import { IconCurrencyDollar } from 'twenty-ui'; import { applySimpleQuotesToString } from '~/utils/string/applySimpleQuotesToString'; export const settingsDataModelFieldCurrencyFormSchema = z.object({ @@ -41,7 +41,7 @@ export const SettingsDataModelFieldCurrencyForm = ({ useCurrencySettingsFormInitialValues({ fieldMetadataItem }); return ( - + <> ( - )} /> - + > ); }; diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/date/components/SettingsDataModelFieldDateForm.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/date/components/SettingsDataModelFieldDateForm.tsx index c90fba312..c3f109289 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/date/components/SettingsDataModelFieldDateForm.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/date/components/SettingsDataModelFieldDateForm.tsx @@ -2,10 +2,9 @@ import { Controller, useFormContext } from 'react-hook-form'; import { z } from 'zod'; import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; -import { StyledFormCardTitle } from '@/settings/data-model/fields/components/StyledFormCardTitle'; -import { SettingsDataModelFieldToggle } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldToggle'; +import { SettingsOptionCardContentToggle } from '@/settings/components/SettingsOptions/SettingsOptionCardContentToggle'; import { useDateSettingsFormInitialValues } from '@/settings/data-model/fields/forms/date/hooks/useDateSettingsFormInitialValues'; -import { IconClockShare, CardContent } from 'twenty-ui'; +import { IconSlash } from 'twenty-ui'; export const settingsDataModelFieldDateFormSchema = z.object({ settings: z @@ -36,27 +35,19 @@ export const SettingsDataModelFieldDateForm = ({ }); return ( - - ( - <> - Options - - > - )} - /> - + ( + + )} + /> ); }; diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/number/components/SettingsDataModelFieldNumberDecimalInput.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/number/components/SettingsDataModelFieldNumberDecimalInput.tsx deleted file mode 100644 index ce67a9bda..000000000 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/number/components/SettingsDataModelFieldNumberDecimalInput.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import styled from '@emotion/styled'; - -import { TextInput } from '@/ui/input/components/TextInput'; -import { Button, IconInfoCircle, IconMinus, IconPlus } from 'twenty-ui'; -import { castAsNumberOrNull } from '~/utils/cast-as-number-or-null'; - -type SettingsDataModelFieldNumberDecimalsInputProps = { - value: number; - onChange: (value: number) => void; - disabled?: boolean; -}; - -const StyledCounterContainer = styled.div` - align-items: center; - background: ${({ theme }) => theme.background.noisy}; - border: 1px solid ${({ theme }) => theme.border.color.medium}; - border-radius: 4px; - display: flex; - flex-direction: column; - flex: 1; - gap: ${({ theme }) => theme.spacing(1)}; - justify-content: center; -`; - -const StyledExampleText = styled.div` - color: ${({ theme }) => theme.font.color.primary}; - width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-weight: ${({ theme }) => theme.font.weight.regular}; -`; - -const StyledCounterControlsIcons = styled.div` - align-items: center; - display: flex; - gap: ${({ theme }) => theme.spacing(2)}; -`; - -const StyledCounterInnerContainer = styled.div` - align-items: center; - align-self: stretch; - display: flex; - gap: ${({ theme }) => theme.spacing(1)}; - padding: ${({ theme }) => theme.spacing(2)}; - height: 24px; -`; - -const StyledTextInput = styled(TextInput)` - width: ${({ theme }) => theme.spacing(16)}; - input { - width: ${({ theme }) => theme.spacing(16)}; - height: ${({ theme }) => theme.spacing(6)}; - text-align: center; - font-weight: ${({ theme }) => theme.font.weight.medium}; - background: ${({ theme }) => theme.background.noisy}; - } - input ~ div { - padding-right: ${({ theme }) => theme.spacing(0)}; - border-radius: ${({ theme }) => theme.spacing(1)}; - background: ${({ theme }) => theme.background.noisy}; - } -`; - -const StyledTitle = styled.div` - color: ${({ theme }) => theme.font.color.light}; - font-size: ${({ theme }) => theme.font.size.xs}; - font-weight: ${({ theme }) => theme.font.weight.semiBold}; - margin-bottom: ${({ theme }) => theme.spacing(1)}; -`; - -const StyledControlButton = styled(Button)` - height: ${({ theme }) => theme.spacing(6)}; - width: ${({ theme }) => theme.spacing(6)}; - padding: 0; - justify-content: center; - svg { - height: ${({ theme }) => theme.spacing(4)}; - width: ${({ theme }) => theme.spacing(4)}; - } -`; - -const StyledInfoButton = styled(Button)` - height: ${({ theme }) => theme.spacing(6)}; - width: ${({ theme }) => theme.spacing(6)}; - padding: 0; - justify-content: center; - svg { - color: ${({ theme }) => theme.font.color.extraLight}; - height: ${({ theme }) => theme.spacing(4)}; - width: ${({ theme }) => theme.spacing(4)}; - } -`; - -const MIN_VALUE = 0; -const MAX_VALUE = 100; -export const SettingsDataModelFieldNumberDecimalsInput = ({ - value, - onChange, - disabled, -}: SettingsDataModelFieldNumberDecimalsInputProps) => { - const exampleValue = (1000).toFixed(value); - - const handleIncrementCounter = () => { - if (value < MAX_VALUE) { - const newValue = value + 1; - onChange(newValue); - } - }; - - const handleDecrementCounter = () => { - if (value > MIN_VALUE) { - const newValue = value - 1; - onChange(newValue); - } - }; - - const handleTextInputChange = (value: string) => { - const castedNumber = castAsNumberOrNull(value); - if (castedNumber === null) { - onChange(MIN_VALUE); - return; - } - - if (castedNumber < MIN_VALUE) { - return; - } - - if (castedNumber > MAX_VALUE) { - onChange(MAX_VALUE); - return; - } - onChange(castedNumber); - }; - return ( - <> - Number of decimals - - - Example: {exampleValue} - - - - handleTextInputChange(value)} - disabled={disabled} - /> - - - - - > - ); -}; diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/number/components/SettingsDataModelFieldNumberForm.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/number/components/SettingsDataModelFieldNumberForm.tsx index 139be3993..271da1e0c 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/number/components/SettingsDataModelFieldNumberForm.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/number/components/SettingsDataModelFieldNumberForm.tsx @@ -3,10 +3,9 @@ import { z } from 'zod'; import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { numberFieldDefaultValueSchema } from '@/object-record/record-field/validation-schemas/numberFieldDefaultValueSchema'; -import { SettingsDataModelFieldNumberDecimalsInput } from '@/settings/data-model/fields/forms/number/components/SettingsDataModelFieldNumberDecimalInput'; -import { Select } from '@/ui/input/components/Select'; -import styled from '@emotion/styled'; -import { CardContent, IconNumber9, IconPercentage } from 'twenty-ui'; +import { SettingsOptionCardContentCounter } from '@/settings/components/SettingsOptions/SettingsOptionCardContentCounter'; +import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect'; +import { IconDecimal, IconEye, IconNumber9, IconPercentage } from 'twenty-ui'; import { DEFAULT_DECIMAL_VALUE } from '~/utils/format/number'; export const settingsDataModelFieldNumberFormSchema = z.object({ @@ -17,13 +16,6 @@ export type SettingsDataModelFieldNumberFormValues = z.infer< typeof settingsDataModelFieldNumberFormSchema >; -const StyledFormCardTitle = styled.div` - color: ${({ theme }) => theme.font.color.light}; - font-size: ${({ theme }) => theme.font.size.xs}; - font-weight: ${({ theme }) => theme.font.weight.semiBold}; - margin-bottom: ${({ theme }) => theme.spacing(1)}; -`; - type SettingsDataModelFieldNumberFormProps = { disabled?: boolean; fieldMetadataItem: Pick< @@ -39,52 +31,54 @@ export const SettingsDataModelFieldNumberForm = ({ const { control } = useFormContext(); return ( - - { - const count = value?.decimals ?? 0; - const type = value?.type ?? 'number'; + { + const count = value?.decimals ?? 0; + const type = value?.type ?? 'number'; - return ( - <> - Type - onChange({ type: value, decimals: count })} - withSearchInput={false} - dropdownWidthAuto={true} - /> - - onChange({ type: type, decimals: value })} - disabled={disabled} - /> - > - ); - }} - /> - + return ( + <> + onChange({ type: value, decimals: count })} + disabled={disabled} + options={[ + { + Icon: IconNumber9, + label: 'Number', + value: 'number', + }, + { + Icon: IconPercentage, + label: 'Percentage', + value: 'percentage', + }, + ]} + /> + onChange({ type: type, decimals: value })} + disabled={disabled} + minValue={0} + maxValue={100} // needs to be changed + /> + > + ); + }} + /> ); }; diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectForm.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectForm.tsx index 48af1e38e..90980d65b 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectForm.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectForm.tsx @@ -28,9 +28,8 @@ import { moveArrayItem } from '~/utils/array/moveArrayItem'; import { toSpliced } from '~/utils/array/toSpliced'; import { applySimpleQuotesToString } from '~/utils/string/applySimpleQuotesToString'; -import { EXPANDED_WIDTH_ANIMATION_VARIANTS } from '@/settings/constants/ExpandedWidthAnimationVariants'; +import { AdvancedSettingsWrapper } from '@/settings/components/AdvancedSettingsWrapper'; import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState'; -import { AnimatePresence, motion } from 'framer-motion'; import { useRecoilValue } from 'recoil'; import { SettingsDataModelFieldSelectFormOptionRow } from './SettingsDataModelFieldSelectFormOptionRow'; @@ -251,26 +250,14 @@ export const SettingsDataModelFieldSelectForm = ({ <> - - {isAdvancedModeEnabled && ( - - - - - - API values - - - )} - + + + + + + API values + + diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx index b92c97e5d..ffe61d6c4 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectFormOptionRow.tsx @@ -1,3 +1,11 @@ +import { FieldMetadataItemOption } from '@/object-metadata/types/FieldMetadataItem'; +import { AdvancedSettingsWrapper } from '@/settings/components/AdvancedSettingsWrapper'; +import { OPTION_VALUE_MAXIMUM_LENGTH } from '@/settings/data-model/constants/OptionValueMaximumLength'; +import { TextInput } from '@/ui/input/components/TextInput'; +import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; +import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; +import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; +import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { useMemo } from 'react'; @@ -14,18 +22,6 @@ import { MenuItemSelectColor, } from 'twenty-ui'; import { v4 } from 'uuid'; - -import { FieldMetadataItemOption } from '@/object-metadata/types/FieldMetadataItem'; -import { EXPANDED_WIDTH_ANIMATION_VARIANTS } from '@/settings/constants/ExpandedWidthAnimationVariants'; -import { OPTION_VALUE_MAXIMUM_LENGTH } from '@/settings/data-model/constants/OptionValueMaximumLength'; -import { TextInput } from '@/ui/input/components/TextInput'; -import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; -import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; -import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; -import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState'; -import { AnimatePresence, motion } from 'framer-motion'; -import { useRecoilValue } from 'recoil'; import { computeOptionValueFromLabel } from '~/pages/settings/data-model/utils/compute-option-value-from-label.utils'; type SettingsDataModelFieldSelectFormOptionRowProps = { @@ -83,7 +79,6 @@ export const SettingsDataModelFieldSelectFormOptionRow = ({ option, isNewRow, }: SettingsDataModelFieldSelectFormOptionRowProps) => { - const isAdvancedModeEnabled = useRecoilValue(isAdvancedModeEnabledState); const theme = useTheme(); const dropdownIds = useMemo(() => { @@ -111,28 +106,19 @@ export const SettingsDataModelFieldSelectFormOptionRow = ({ stroke={theme.icon.stroke.sm} color={theme.font.color.extraLight} /> - - {isAdvancedModeEnabled && ( - - - onChange({ - ...option, - value: computeOptionValueFromLabel(input), - }) - } - RightIcon={isDefault ? IconCheck : undefined} - maxLength={OPTION_VALUE_MAXIMUM_LENGTH} - /> - - )} - + + + onChange({ + ...option, + value: computeOptionValueFromLabel(input), + }) + } + RightIcon={isDefault ? IconCheck : undefined} + maxLength={OPTION_VALUE_MAXIMUM_LENGTH} + /> + theme.spacing(-6)}; - position: absolute; - height: 100%; -`; - -const StyledIconTool = styled(IconTool)` - margin-right: ${({ theme }) => theme.spacing(0.5)}; -`; - const StyledLabel = styled.span` color: ${({ theme }) => theme.font.color.light}; font-size: ${({ theme }) => theme.font.size.xs}; @@ -115,10 +100,6 @@ export const SettingsDataModelObjectAboutForm = ({ const { control, watch, setValue } = useFormContext(); const theme = useTheme(); - const isAdvancedModeEnabled = useRecoilValue(isAdvancedModeEnabledState); - const { contentRef, motionAnimationVariants } = useExpandedHeightAnimation( - isAdvancedModeEnabled, - ); const isLabelSyncedWithName = watch(IS_LABEL_SYNCED_WITH_NAME_LABEL); const labelSingular = watch('labelSingular'); @@ -235,122 +216,111 @@ export const SettingsDataModelObjectAboutForm = ({ /> )} /> - - {isAdvancedModeEnabled && ( - - - - - - - - {[ - { - label: 'API Name (Singular)', - fieldName: 'nameSingular' as const, - placeholder: 'listing', - defaultValue: objectMetadataItem?.nameSingular, - disabled: - disabled || disableNameEdit || isLabelSyncedWithName, - tooltip: apiNameTooltipText, - }, - { - label: 'API Name (Plural)', - fieldName: 'namePlural' as const, - placeholder: 'listings', - defaultValue: objectMetadataItem?.namePlural, - disabled: - disabled || disableNameEdit || isLabelSyncedWithName, - tooltip: apiNameTooltipText, - }, - ].map( - ({ - defaultValue, - fieldName, - label, - placeholder, - disabled, - tooltip, - }) => ( - - ( - <> - - tooltip && ( - <> - - - > - ) - } - /> - > - )} - /> - - ), - )} - ( - { - onChange(value); - if (value === true) { - fillNamePluralFromLabelPlural(labelPlural); - fillNameSingularFromLabelSingular(labelSingular); - } - onBlur?.(); - }} - /> - )} - /> - - - - - )} - + + + + + {[ + { + label: 'API Name (Singular)', + fieldName: 'nameSingular' as const, + placeholder: 'listing', + defaultValue: objectMetadataItem?.nameSingular, + disabled: + disabled || disableNameEdit || isLabelSyncedWithName, + tooltip: apiNameTooltipText, + }, + { + label: 'API Name (Plural)', + fieldName: 'namePlural' as const, + placeholder: 'listings', + defaultValue: objectMetadataItem?.namePlural, + disabled: + disabled || disableNameEdit || isLabelSyncedWithName, + tooltip: apiNameTooltipText, + }, + ].map( + ({ + defaultValue, + fieldName, + label, + placeholder, + disabled, + tooltip, + }) => ( + + ( + <> + + tooltip && ( + <> + + + > + ) + } + /> + > + )} + /> + + ), + )} + ( + + { + onChange(value); + if (value === true) { + fillNamePluralFromLabelPlural(labelPlural); + fillNameSingularFromLabelSingular(labelSingular); + } + onBlur?.(); + }} + /> + + )} + /> + + + + > ); }; diff --git a/packages/twenty-front/src/modules/settings/data-model/objects/forms/components/SyncObjectLabelAndNameToggle.tsx b/packages/twenty-front/src/modules/settings/data-model/objects/forms/components/SyncObjectLabelAndNameToggle.tsx deleted file mode 100644 index c3252c82c..000000000 --- a/packages/twenty-front/src/modules/settings/data-model/objects/forms/components/SyncObjectLabelAndNameToggle.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { useTheme } from '@emotion/react'; -import styled from '@emotion/styled'; -import { IconRefresh, MAIN_COLORS, Toggle } from 'twenty-ui'; - -const StyledToggleContainer = styled.div` - align-items: center; - border: 1px solid ${({ theme }) => theme.border.color.medium}; - border-radius: ${({ theme }) => theme.border.radius.md}; - display: flex; - justify-content: space-between; - padding: ${({ theme }) => theme.spacing(4)}; - background: ${({ theme }) => theme.background.secondary}; -`; - -const StyledIconRefreshContainer = styled.div` - border: 2px solid ${({ theme }) => theme.border.color.medium}; - border-radius: 3px; - margin-right: ${({ theme }) => theme.spacing(3)}; - width: ${({ theme }) => theme.spacing(8)}; - height: ${({ theme }) => theme.spacing(8)}; - display: flex; - align-items: center; - justify-content: center; -`; - -const StyledTitleContainer = styled.div` - align-items: center; - display: flex; - justify-content: space-between; -`; - -const StyledTitle = styled.h2` - color: ${({ theme }) => theme.font.color.primary}; - font-size: ${({ theme }) => theme.font.size.md}; - font-weight: ${({ theme }) => theme.font.weight.semiBold}; - margin: 0; -`; - -const StyledDescription = styled.h3` - color: ${({ theme }) => theme.font.color.tertiary}; - font-size: ${({ theme }) => theme.font.size.md}; - font-weight: ${({ theme }) => theme.font.weight.regular}; - margin: 0; - margin-top: ${({ theme }) => theme.spacing(1)}; -`; - -type SyncObjectLabelAndNameToggleProps = { - value: boolean; - onChange: (value: boolean) => void; - disabled?: boolean; -}; - -export const SyncObjectLabelAndNameToggle = ({ - value, - onChange, - disabled, -}: SyncObjectLabelAndNameToggleProps) => { - const theme = useTheme(); - return ( - - - - - - - Synchronize Objects Labels and API Names - - Should changing an object's label also change the API? - - - - - - ); -}; diff --git a/packages/twenty-front/src/modules/settings/hooks/useExpandedAnimation.ts b/packages/twenty-front/src/modules/settings/hooks/useExpandedAnimation.ts new file mode 100644 index 000000000..e87da3739 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/hooks/useExpandedAnimation.ts @@ -0,0 +1,63 @@ +import { ADVANCED_SETTINGS_ANIMATION_DURATION } from '@/settings/constants/AdvancedSettingsAnimationDurations'; +import { useEffect, useRef, useState } from 'react'; +import { isDefined } from 'twenty-ui'; + +type AnimationDimension = 'width' | 'height'; + +const getTransitionValues = (dimension: AnimationDimension) => ({ + transition: { + opacity: { duration: ADVANCED_SETTINGS_ANIMATION_DURATION.opacity }, + [dimension]: { duration: ADVANCED_SETTINGS_ANIMATION_DURATION.size }, + }, +}); + +const commonStyles = (dimension: AnimationDimension) => ({ + opacity: 0, + [dimension]: 0, + ...getTransitionValues(dimension), +}); + +const advancedSectionAnimationConfig = ( + isExpanded: boolean, + dimension: AnimationDimension, + measuredValue?: number, +) => ({ + initial: { + ...commonStyles(dimension), + }, + animate: { + opacity: 1, + [dimension]: isExpanded + ? dimension === 'width' + ? '100%' + : measuredValue + : 0, + ...getTransitionValues(dimension), + }, + exit: { + ...commonStyles(dimension), + }, +}); + +export const useExpandedAnimation = ( + isExpanded: boolean, + dimension: AnimationDimension = 'height', +) => { + const contentRef = useRef(null); + const [measuredValue, setMeasuredValue] = useState(0); + + useEffect(() => { + if (dimension === 'height' && isDefined(contentRef.current)) { + setMeasuredValue(contentRef.current.scrollHeight); + } + }, [isExpanded, dimension]); + + return { + contentRef, + motionAnimationVariants: advancedSectionAnimationConfig( + isExpanded, + dimension, + dimension === 'height' ? measuredValue : undefined, + ), + }; +}; diff --git a/packages/twenty-front/src/modules/settings/hooks/useExpandedHeightAnimation.ts b/packages/twenty-front/src/modules/settings/hooks/useExpandedHeightAnimation.ts deleted file mode 100644 index 0e7723c67..000000000 --- a/packages/twenty-front/src/modules/settings/hooks/useExpandedHeightAnimation.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { useEffect, useRef, useState } from 'react'; -import { isDefined } from 'twenty-ui'; - -const transitionValues = { - transition: { - opacity: { duration: 0.2 }, - height: { duration: 0.4 }, - }, -}; - -const commonStyles = { - opacity: 0, - height: 0, - ...transitionValues, -}; - -const advancedSectionAnimationConfig = ( - isExpanded: boolean, - measuredHeight: number, -) => ({ - initial: { - ...commonStyles, - }, - animate: { - opacity: 1, - height: isExpanded ? measuredHeight : 0, - ...transitionValues, - }, - exit: { - ...commonStyles, - }, -}); - -export const useExpandedHeightAnimation = (isExpanded: boolean) => { - const contentRef = useRef(null); - const [measuredHeight, setMeasuredHeight] = useState(0); - - useEffect(() => { - if (isDefined(contentRef.current)) { - setMeasuredHeight(contentRef.current.scrollHeight); - } - }, [isExpanded]); - - return { - contentRef, - measuredHeight, - motionAnimationVariants: advancedSectionAnimationConfig( - isExpanded, - measuredHeight, - ), - }; -}; diff --git a/packages/twenty-front/src/modules/settings/security/components/SettingsSecurityOptionsList.tsx b/packages/twenty-front/src/modules/settings/security/components/SettingsSecurityOptionsList.tsx index ac5eef959..f346d5656 100644 --- a/packages/twenty-front/src/modules/settings/security/components/SettingsSecurityOptionsList.tsx +++ b/packages/twenty-front/src/modules/settings/security/components/SettingsSecurityOptionsList.tsx @@ -1,5 +1,5 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; -import { SettingsOptionCardContent } from '@/settings/components/SettingsOptionCardContent'; +import { SettingsOptionCardContentToggle } from '@/settings/components/SettingsOptions/SettingsOptionCardContentToggle'; import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { useRecoilState } from 'recoil'; @@ -44,12 +44,13 @@ export const SettingsSecurityOptionsList = () => { }; return ( - - + handleChange(!currentWorkspace.isPublicInviteLinkEnabled) } diff --git a/packages/twenty-front/src/modules/ui/input/components/Select.tsx b/packages/twenty-front/src/modules/ui/input/components/Select.tsx index 1895cbb0b..a4fc3ef88 100644 --- a/packages/twenty-front/src/modules/ui/input/components/Select.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/Select.tsx @@ -12,21 +12,24 @@ import { SelectControl } from '@/ui/input/components/SelectControl'; import { isDefined } from '~/utils/isDefined'; import { SelectHotkeyScope } from '../types/SelectHotkeyScope'; -export type SelectOption = { +export type SelectOption = { value: Value; label: string; Icon?: IconComponent; }; +export type SelectSizeVariant = 'small' | 'default'; + type CallToActionButton = { text: string; onClick: (event: MouseEvent) => void; Icon?: IconComponent; }; -export type SelectProps = { +export type SelectProps = { className?: string; disabled?: boolean; + selectSizeVariant?: SelectSizeVariant; disableBlur?: boolean; dropdownId: string; dropdownWidth?: `${string}px` | 'auto' | number; @@ -54,9 +57,10 @@ const StyledLabel = styled.span` margin-bottom: ${({ theme }) => theme.spacing(1)}; `; -export const Select = ({ +export const Select = ({ className, disabled: disabledFromProps, + selectSizeVariant, disableBlur = false, dropdownId, dropdownWidth = 176, @@ -115,6 +119,7 @@ export const Select = ({ ) : ( ({ } disableBlur={disableBlur} @@ -144,7 +150,7 @@ export const Select = ({ {filteredOptions.map((option) => ( { diff --git a/packages/twenty-front/src/modules/ui/input/components/SelectControl.tsx b/packages/twenty-front/src/modules/ui/input/components/SelectControl.tsx index 96564a112..28fc99625 100644 --- a/packages/twenty-front/src/modules/ui/input/components/SelectControl.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/SelectControl.tsx @@ -1,4 +1,4 @@ -import { SelectOption } from '@/ui/input/components/Select'; +import { SelectOption, SelectSizeVariant } from '@/ui/input/components/Select'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { @@ -10,6 +10,7 @@ import { const StyledControlContainer = styled.div<{ disabled?: boolean; hasIcon: boolean; + selectSizeVariant?: SelectSizeVariant; }>` display: grid; grid-template-columns: ${({ hasIcon }) => @@ -17,7 +18,8 @@ const StyledControlContainer = styled.div<{ align-items: center; gap: ${({ theme }) => theme.spacing(1)}; box-sizing: border-box; - height: ${({ theme }) => theme.spacing(8)}; + height: ${({ selectSizeVariant, theme }) => + selectSizeVariant === 'small' ? theme.spacing(6) : theme.spacing(8)}; max-width: 100%; padding: 0 ${({ theme }) => theme.spacing(2)}; background-color: ${({ theme }) => theme.background.transparent.lighter}; @@ -37,13 +39,15 @@ const StyledIconChevronDown = styled(IconChevronDown)<{ `; type SelectControlProps = { - selectedOption: SelectOption; + selectedOption: SelectOption; isDisabled?: boolean; + selectSizeVariant?: SelectSizeVariant; }; export const SelectControl = ({ selectedOption, isDisabled, + selectSizeVariant, }: SelectControlProps) => { const theme = useTheme(); @@ -51,6 +55,7 @@ export const SelectControl = ({ {isDefined(selectedOption.Icon) ? ( ; +type RenderProps = SelectProps; const Render = (args: RenderProps) => { const [value, setValue] = useState(args.value); - const handleChange = (value: string | number | null) => { + const handleChange = (value: string | number | boolean | null) => { args.onChange?.(value); setValue(value); }; diff --git a/packages/twenty-front/src/pages/settings/SettingsWorkspace.tsx b/packages/twenty-front/src/pages/settings/SettingsWorkspace.tsx index 66aed5927..86d85e308 100644 --- a/packages/twenty-front/src/pages/settings/SettingsWorkspace.tsx +++ b/packages/twenty-front/src/pages/settings/SettingsWorkspace.tsx @@ -32,7 +32,7 @@ export const SettingsWorkspace = () => ( } + adornment={} description="Grant Twenty support temporary access to your workspace so we can troubleshoot problems or recover content on your behalf. You can revoke access at any time." /> diff --git a/packages/twenty-front/src/pages/settings/security/SettingsSecurity.tsx b/packages/twenty-front/src/pages/settings/security/SettingsSecurity.tsx index 3e61db091..a0869c60a 100644 --- a/packages/twenty-front/src/pages/settings/security/SettingsSecurity.tsx +++ b/packages/twenty-front/src/pages/settings/security/SettingsSecurity.tsx @@ -1,5 +1,7 @@ -import { H2Title, Section, IconLock, Tag } from 'twenty-ui'; +import styled from '@emotion/styled'; +import { H2Title, IconLock, Section, Tag } from 'twenty-ui'; +import { AdvancedSettingsWrapper } from '@/settings/components/AdvancedSettingsWrapper'; import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; import { SettingsReadDocumentationButton } from '@/settings/developers/components/SettingsReadDocumentationButton'; import { SettingsSSOIdentitiesProvidersListCard } from '@/settings/security/components/SettingsSSOIdentitiesProvidersListCard'; @@ -8,6 +10,21 @@ import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { SettingsPath } from '@/types/SettingsPath'; import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; +const StyledContainer = styled.div` + width: 100%; +`; + +const StyledMainContent = styled.div` + display: flex; + flex-direction: column; + gap: ${({ theme }) => theme.spacing(10)}; + min-height: 200px; +`; + +const StyledSSOSection = styled(Section)` + flex-shrink: 0; +`; + export const SettingsSecurity = () => { return ( { ]} > - - - } - /> - - - - - - + + + + } + /> + + + + + + + + + + + ); diff --git a/packages/twenty-ui/src/display/icon/components/IllustrationIconArray.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconArray.tsx index b53d11867..74c356f34 100644 --- a/packages/twenty-ui/src/display/icon/components/IllustrationIconArray.tsx +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconArray.tsx @@ -11,8 +11,8 @@ export const IllustrationIconArray = (props: IllustrationIconArrayProps) => { return ( diff --git a/packages/twenty-ui/src/display/icon/components/IllustrationIconCalendarEvent.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconCalendarEvent.tsx index 32c4b6e4b..40eb265f7 100644 --- a/packages/twenty-ui/src/display/icon/components/IllustrationIconCalendarEvent.tsx +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconCalendarEvent.tsx @@ -13,8 +13,8 @@ export const IllustrationIconCalendarEvent = ( return ( diff --git a/packages/twenty-ui/src/display/icon/components/IllustrationIconCalendarTime.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconCalendarTime.tsx index 701415963..e2259cd0c 100644 --- a/packages/twenty-ui/src/display/icon/components/IllustrationIconCalendarTime.tsx +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconCalendarTime.tsx @@ -16,8 +16,8 @@ export const IllustrationIconCalendarTime = ( ); diff --git a/packages/twenty-ui/src/display/icon/components/IllustrationIconCurrency.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconCurrency.tsx index cd493e2e6..d2364ee5e 100644 --- a/packages/twenty-ui/src/display/icon/components/IllustrationIconCurrency.tsx +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconCurrency.tsx @@ -17,8 +17,8 @@ export const IllustrationIconCurrency = ( ); diff --git a/packages/twenty-ui/src/display/icon/components/IllustrationIconJson.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconJson.tsx index c95136c9f..a414d4eee 100644 --- a/packages/twenty-ui/src/display/icon/components/IllustrationIconJson.tsx +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconJson.tsx @@ -15,8 +15,8 @@ export const IllustrationIconJson = (props: IllustrationIconJsonProps) => { ); diff --git a/packages/twenty-ui/src/display/icon/components/llustrationIconLink.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconLink.tsx similarity index 93% rename from packages/twenty-ui/src/display/icon/components/llustrationIconLink.tsx rename to packages/twenty-ui/src/display/icon/components/IllustrationIconLink.tsx index 70a480068..6519baa93 100644 --- a/packages/twenty-ui/src/display/icon/components/llustrationIconLink.tsx +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconLink.tsx @@ -15,8 +15,8 @@ export const IllustrationIconLink = (props: IllustrationIconLinkProps) => { ); diff --git a/packages/twenty-ui/src/display/icon/components/IllustrationIconMail.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconMail.tsx index bb5b63186..786dcf882 100644 --- a/packages/twenty-ui/src/display/icon/components/IllustrationIconMail.tsx +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconMail.tsx @@ -14,8 +14,8 @@ export const IllustrationIconMail = (props: IllustrationIconMailProps) => { ); diff --git a/packages/twenty-ui/src/display/icon/components/IllustrationIconManyToMany.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconManyToMany.tsx index d00f479c2..5436fbe14 100644 --- a/packages/twenty-ui/src/display/icon/components/IllustrationIconManyToMany.tsx +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconManyToMany.tsx @@ -16,8 +16,8 @@ export const IllustrationIconManyToMany = ( ); diff --git a/packages/twenty-ui/src/display/icon/components/IllustrationIconMap.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconMap.tsx index 2fac17d9d..dd844f031 100644 --- a/packages/twenty-ui/src/display/icon/components/IllustrationIconMap.tsx +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconMap.tsx @@ -14,8 +14,8 @@ export const IllustrationIconMap = (props: IllustrationIconMapProps) => { ); diff --git a/packages/twenty-ui/src/display/icon/components/IllustrationIconNumbers.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconNumbers.tsx index 74cbe38c7..2f42028e6 100644 --- a/packages/twenty-ui/src/display/icon/components/IllustrationIconNumbers.tsx +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconNumbers.tsx @@ -16,8 +16,8 @@ export const IllustrationIconNumbers = ( ); diff --git a/packages/twenty-ui/src/display/icon/components/IllustrationIconOneToMany.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconOneToMany.tsx index 9bad7f132..abe3f3cd9 100644 --- a/packages/twenty-ui/src/display/icon/components/IllustrationIconOneToMany.tsx +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconOneToMany.tsx @@ -17,8 +17,8 @@ export const IllustrationIconOneToMany = ( ); diff --git a/packages/twenty-ui/src/display/icon/components/IllustrationIconOneToOne.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconOneToOne.tsx index 0fd6a902a..246d0c19e 100644 --- a/packages/twenty-ui/src/display/icon/components/IllustrationIconOneToOne.tsx +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconOneToOne.tsx @@ -17,8 +17,8 @@ export const IllustrationIconOneToOne = ( ); diff --git a/packages/twenty-ui/src/display/icon/components/IllustrationIconPhone.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconPhone.tsx index d1f0f67a3..3747a42d9 100644 --- a/packages/twenty-ui/src/display/icon/components/IllustrationIconPhone.tsx +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconPhone.tsx @@ -16,8 +16,8 @@ export const IllustrationIconPhone = (props: IllustrationIconPhoneProps) => { ); diff --git a/packages/twenty-ui/src/display/icon/components/IllustrationIconSetting.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconSetting.tsx index 348f7bb1e..7fab56e3c 100644 --- a/packages/twenty-ui/src/display/icon/components/IllustrationIconSetting.tsx +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconSetting.tsx @@ -18,8 +18,8 @@ export const IllustrationIconSetting = ( ); diff --git a/packages/twenty-ui/src/display/icon/components/IllustrationIconStar.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconStar.tsx index b57ce132f..c3a72314b 100644 --- a/packages/twenty-ui/src/display/icon/components/IllustrationIconStar.tsx +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconStar.tsx @@ -16,8 +16,8 @@ export const IllustrationIconStar = (props: IllustrationIconStarProps) => { ); diff --git a/packages/twenty-ui/src/display/icon/components/IllustrationIconTag.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconTag.tsx index fdfa37591..9ec76fb1e 100644 --- a/packages/twenty-ui/src/display/icon/components/IllustrationIconTag.tsx +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconTag.tsx @@ -15,8 +15,8 @@ export const IllustrationIconTag = (props: IllustrationIconTagProps) => { ); diff --git a/packages/twenty-ui/src/display/icon/components/IllustrationIconTags.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconTags.tsx index 97fd173bb..b63c361fc 100644 --- a/packages/twenty-ui/src/display/icon/components/IllustrationIconTags.tsx +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconTags.tsx @@ -15,8 +15,8 @@ export const IllustrationIconTags = (props: IllustrationIconTagsProps) => { ); diff --git a/packages/twenty-ui/src/display/icon/components/IllustrationIconText.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconText.tsx index 93ef4d8d3..6d10ecc3f 100644 --- a/packages/twenty-ui/src/display/icon/components/IllustrationIconText.tsx +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconText.tsx @@ -15,8 +15,8 @@ export const IllustrationIconText = (props: IllustrationIconTextProps) => { ); diff --git a/packages/twenty-ui/src/display/icon/components/IllustrationIconToggle.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconToggle.tsx index 2ee16a190..be9224cc5 100644 --- a/packages/twenty-ui/src/display/icon/components/IllustrationIconToggle.tsx +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconToggle.tsx @@ -15,8 +15,8 @@ export const IllustrationIconToggle = (props: IllustrationIconToggleProps) => { ); diff --git a/packages/twenty-ui/src/display/icon/components/IllustrationIconUid.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconUid.tsx index 6c1fa241e..e72116f54 100644 --- a/packages/twenty-ui/src/display/icon/components/IllustrationIconUid.tsx +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconUid.tsx @@ -15,8 +15,8 @@ export const IllustrationIconUid = (props: IllustrationIconUidProps) => { ); diff --git a/packages/twenty-ui/src/display/icon/components/IllustrationIconUser.tsx b/packages/twenty-ui/src/display/icon/components/IllustrationIconUser.tsx index 4993dbb84..fe8dbeafc 100644 --- a/packages/twenty-ui/src/display/icon/components/IllustrationIconUser.tsx +++ b/packages/twenty-ui/src/display/icon/components/IllustrationIconUser.tsx @@ -15,8 +15,8 @@ export const IllustrationIconUser = (props: IllustrationIconUserProps) => { ); diff --git a/packages/twenty-ui/src/display/icon/components/TablerIcons.ts b/packages/twenty-ui/src/display/icon/components/TablerIcons.ts index 2f8a5150a..a5978f566 100644 --- a/packages/twenty-ui/src/display/icon/components/TablerIcons.ts +++ b/packages/twenty-ui/src/display/icon/components/TablerIcons.ts @@ -110,6 +110,7 @@ export { IconCurrencyZloty, IconDatabase, IconDatabaseExport, + IconDecimal, IconDeviceFloppy, IconDoorEnter, IconDotsVertical, @@ -212,6 +213,7 @@ export { IconSend, IconSettings, IconSettingsAutomation, + IconSlash, IconSortDescending, IconSparkles, IconSql, @@ -232,6 +234,7 @@ export { IconUpload, IconUser, IconUserCircle, + IconUserPlus, IconUsers, IconVariablePlus, IconVideo, diff --git a/packages/twenty-ui/src/display/index.ts b/packages/twenty-ui/src/display/index.ts index e8f248ba7..3b009a351 100644 --- a/packages/twenty-ui/src/display/index.ts +++ b/packages/twenty-ui/src/display/index.ts @@ -24,6 +24,7 @@ export * from './icon/components/IllustrationIconCalendarEvent'; export * from './icon/components/IllustrationIconCalendarTime'; export * from './icon/components/IllustrationIconCurrency'; export * from './icon/components/IllustrationIconJson'; +export * from './icon/components/IllustrationIconLink'; export * from './icon/components/IllustrationIconMail'; export * from './icon/components/IllustrationIconManyToMany'; export * from './icon/components/IllustrationIconMap'; @@ -40,7 +41,6 @@ export * from './icon/components/IllustrationIconToggle'; export * from './icon/components/IllustrationIconUid'; export * from './icon/components/IllustrationIconUser'; export * from './icon/components/IllustrationIconWrapper'; -export * from './icon/components/llustrationIconLink'; export * from './icon/components/TablerIcons'; export * from './icon/hooks/useIcons'; export * from './icon/providers/IconsProvider'; diff --git a/packages/twenty-ui/src/display/typography/components/H2Title.tsx b/packages/twenty-ui/src/display/typography/components/H2Title.tsx index 8b99023df..4d2703c33 100644 --- a/packages/twenty-ui/src/display/typography/components/H2Title.tsx +++ b/packages/twenty-ui/src/display/typography/components/H2Title.tsx @@ -3,7 +3,7 @@ import styled from '@emotion/styled'; type H2TitleProps = { title: string; description?: string; - addornment?: React.ReactNode; + adornment?: React.ReactNode; className?: string; }; @@ -37,13 +37,13 @@ const StyledDescription = styled.h3` export const H2Title = ({ title, description, - addornment, + adornment, className, }: H2TitleProps) => ( {title} - {addornment} + {adornment} {description && {description}} diff --git a/packages/twenty-ui/src/theme/constants/IllustrationIconDark.ts b/packages/twenty-ui/src/theme/constants/IllustrationIconDark.ts index 9c9715b4d..7bcc96856 100644 --- a/packages/twenty-ui/src/theme/constants/IllustrationIconDark.ts +++ b/packages/twenty-ui/src/theme/constants/IllustrationIconDark.ts @@ -1,6 +1,13 @@ +import { GRAY_SCALE } from '@ui/theme/constants/GrayScale'; import { COLOR } from './Colors'; export const ILLUSTRATION_ICON_DARK = { - color: COLOR.blue50, - fill: COLOR.blue70, + color: { + blue: COLOR.blue50, + grey: GRAY_SCALE.gray50, + }, + fill: { + blue: COLOR.blue70, + grey: GRAY_SCALE.gray70, + }, }; diff --git a/packages/twenty-ui/src/theme/constants/IllustrationIconLight.ts b/packages/twenty-ui/src/theme/constants/IllustrationIconLight.ts index a292d9bfa..66ab6b478 100644 --- a/packages/twenty-ui/src/theme/constants/IllustrationIconLight.ts +++ b/packages/twenty-ui/src/theme/constants/IllustrationIconLight.ts @@ -1,6 +1,13 @@ +import { GRAY_SCALE } from '@ui/theme/constants/GrayScale'; import { COLOR } from './Colors'; export const ILLUSTRATION_ICON_LIGHT = { - color: COLOR.blue40, - fill: COLOR.blue20, + color: { + blue: COLOR.blue40, + grey: GRAY_SCALE.gray40, + }, + fill: { + blue: COLOR.blue20, + grey: GRAY_SCALE.gray20, + }, };