mirror of
https://github.com/twentyhq/twenty.git
synced 2024-11-23 22:12:24 +03:00
feat: add Settings/Accounts/Emails/Inbox Settings visibility section (#3077)
* feat: add Settings/Accounts/Emails/Inbox Settings page Closes #3013 * feat: add Settings/Accounts/Emails/Inbox Settings synchronization section Closes #3014 * feat: add Settings/Accounts/Emails/Inbox Settings visibility section Closes #3015 --------- Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
parent
5bbd1a7c49
commit
6c30556d00
@ -1 +1,8 @@
|
||||
export type Account = { email: string; isSynced?: boolean; uuid: string };
|
||||
import { InboxSettingsVisibilityValue } from '@/settings/accounts/components/SettingsAccountsInboxSettingsVisibilitySection';
|
||||
|
||||
export type Account = {
|
||||
email: string;
|
||||
isSynced?: boolean;
|
||||
uuid: string;
|
||||
visibility: InboxSettingsVisibilityValue;
|
||||
};
|
||||
|
@ -3,10 +3,11 @@ import styled from '@emotion/styled';
|
||||
const StyledCardMedia = styled.div`
|
||||
align-items: center;
|
||||
border: 2px solid ${({ theme }) => theme.border.color.medium};
|
||||
border-radius: ${({ theme }) => theme.border.radius.xs};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(0.5)};
|
||||
height: ${({ theme }) => theme.spacing(8)};
|
||||
justify-content: center;
|
||||
padding: ${({ theme }) => theme.spacing(0.5)};
|
||||
|
@ -0,0 +1,133 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { SettingsAccountsInboxSettingsCardMedia } from '@/settings/accounts/components/SettingsAccountsInboxSettingsCardMedia';
|
||||
import { H2Title } from '@/ui/display/typography/components/H2Title';
|
||||
import { Radio } from '@/ui/input/components/Radio';
|
||||
import { Card } from '@/ui/layout/card/components/Card';
|
||||
import { CardContent } from '@/ui/layout/card/components/CardContent';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
|
||||
export enum InboxSettingsVisibilityValue {
|
||||
Everything = 'everything',
|
||||
SubjectMetadata = 'subject-metadata',
|
||||
Metadata = 'metadata',
|
||||
}
|
||||
|
||||
type SettingsAccountsInboxSettingsVisibilitySectionProps = {
|
||||
onChange: (nextValue: InboxSettingsVisibilityValue) => void;
|
||||
value?: InboxSettingsVisibilityValue;
|
||||
};
|
||||
|
||||
const StyledCardContent = styled(CardContent)`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(4)};
|
||||
`;
|
||||
|
||||
const StyledCardMedia = styled(SettingsAccountsInboxSettingsCardMedia)`
|
||||
align-items: stretch;
|
||||
`;
|
||||
|
||||
const StyledSubjectSkeleton = styled.div<{ isActive?: boolean }>`
|
||||
background-color: ${({ isActive, theme }) =>
|
||||
isActive ? theme.accent.accent4060 : theme.background.quaternary};
|
||||
border-radius: 1px;
|
||||
height: 3px;
|
||||
`;
|
||||
|
||||
const StyledMetadataSkeleton = styled(StyledSubjectSkeleton)`
|
||||
margin-right: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledBodySkeleton = styled(StyledSubjectSkeleton)`
|
||||
border-radius: ${({ theme }) => theme.border.radius.xs};
|
||||
height: 22px;
|
||||
`;
|
||||
|
||||
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 StyledRadio = styled(Radio)`
|
||||
margin-left: auto;
|
||||
`;
|
||||
|
||||
const inboxSettingsVisibilityOptions = [
|
||||
{
|
||||
title: 'Everything',
|
||||
description: 'Subject, body and attachments will be shared with your team.',
|
||||
value: InboxSettingsVisibilityValue.Everything,
|
||||
visibleElements: {
|
||||
metadata: true,
|
||||
subject: true,
|
||||
body: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Subject and metadata',
|
||||
description: 'Subject and metadata will be shared with your team.',
|
||||
value: InboxSettingsVisibilityValue.SubjectMetadata,
|
||||
visibleElements: {
|
||||
metadata: true,
|
||||
subject: true,
|
||||
body: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Metadata',
|
||||
description: 'Timestamp and participants will be shared with your team.',
|
||||
value: InboxSettingsVisibilityValue.Metadata,
|
||||
visibleElements: {
|
||||
metadata: true,
|
||||
subject: false,
|
||||
body: false,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const SettingsAccountsInboxSettingsVisibilitySection = ({
|
||||
onChange,
|
||||
value = InboxSettingsVisibilityValue.Everything,
|
||||
}: SettingsAccountsInboxSettingsVisibilitySectionProps) => (
|
||||
<Section>
|
||||
<H2Title
|
||||
title="Email visibility"
|
||||
description="Define what will be visible to other users in your workspace"
|
||||
/>
|
||||
<Card>
|
||||
{inboxSettingsVisibilityOptions.map(
|
||||
(
|
||||
{ title, description, value: optionValue, visibleElements },
|
||||
index,
|
||||
) => (
|
||||
<StyledCardContent
|
||||
key={value}
|
||||
divider={index < inboxSettingsVisibilityOptions.length - 1}
|
||||
>
|
||||
<StyledCardMedia>
|
||||
<StyledMetadataSkeleton isActive={visibleElements.metadata} />
|
||||
<StyledSubjectSkeleton isActive={visibleElements.subject} />
|
||||
<StyledBodySkeleton isActive={visibleElements.body} />
|
||||
</StyledCardMedia>
|
||||
<div>
|
||||
<StyledTitle>{title}</StyledTitle>
|
||||
<StyledDescription>{description}</StyledDescription>
|
||||
</div>
|
||||
<StyledRadio
|
||||
value={optionValue}
|
||||
onCheckedChange={() => onChange(optionValue)}
|
||||
checked={value === optionValue}
|
||||
/>
|
||||
</StyledCardContent>
|
||||
),
|
||||
)}
|
||||
</Card>
|
||||
</Section>
|
||||
);
|
@ -38,7 +38,15 @@ const StyledRadioInput = styled(motion.input)<RadioInputProps>`
|
||||
appearance: none;
|
||||
background-color: transparent;
|
||||
border: 1px solid ${({ theme }) => theme.font.color.secondary};
|
||||
border-radius: 50%;
|
||||
border-radius: ${({ theme }) => theme.border.radius.rounded};
|
||||
height: ${({ 'radio-size': radioSize }) =>
|
||||
radioSize === RadioSize.Large ? '18px' : '16px'};
|
||||
margin: 0;
|
||||
margin-left: 3px;
|
||||
position: relative;
|
||||
width: ${({ 'radio-size': radioSize }) =>
|
||||
radioSize === RadioSize.Large ? '18px' : '16px'};
|
||||
|
||||
:hover {
|
||||
background-color: ${({ theme, checked }) => {
|
||||
if (!checked) {
|
||||
@ -53,6 +61,7 @@ const StyledRadioInput = styled(motion.input)<RadioInputProps>`
|
||||
return rgba(theme.color.blue, 0.12);
|
||||
}};
|
||||
}
|
||||
|
||||
&:checked {
|
||||
background-color: ${({ theme }) => theme.color.blue};
|
||||
border: none;
|
||||
@ -70,15 +79,11 @@ const StyledRadioInput = styled(motion.input)<RadioInputProps>`
|
||||
radioSize === RadioSize.Large ? '8px' : '6px'};
|
||||
}
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.12;
|
||||
}
|
||||
height: ${({ 'radio-size': radioSize }) =>
|
||||
radioSize === RadioSize.Large ? '18px' : '16px'};
|
||||
position: relative;
|
||||
width: ${({ 'radio-size': radioSize }) =>
|
||||
radioSize === RadioSize.Large ? '18px' : '16px'};
|
||||
`;
|
||||
|
||||
type LabelProps = {
|
||||
@ -99,26 +104,28 @@ const StyledLabel = styled.label<LabelProps>`
|
||||
`;
|
||||
|
||||
export type RadioProps = {
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
checked?: boolean;
|
||||
value?: string;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
label?: string;
|
||||
labelPosition?: LabelPosition;
|
||||
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
onCheckedChange?: (checked: boolean) => void;
|
||||
size?: RadioSize;
|
||||
disabled?: boolean;
|
||||
labelPosition?: LabelPosition;
|
||||
style?: React.CSSProperties;
|
||||
value?: string;
|
||||
};
|
||||
|
||||
export const Radio = ({
|
||||
checked,
|
||||
value,
|
||||
className,
|
||||
disabled = false,
|
||||
label,
|
||||
labelPosition = LabelPosition.Right,
|
||||
onChange,
|
||||
onCheckedChange,
|
||||
size = RadioSize.Small,
|
||||
labelPosition = LabelPosition.Right,
|
||||
disabled = false,
|
||||
className,
|
||||
value,
|
||||
}: RadioProps) => {
|
||||
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
onChange?.(event);
|
||||
@ -133,7 +140,7 @@ export const Radio = ({
|
||||
name="input-radio"
|
||||
data-testid="input-radio"
|
||||
checked={checked}
|
||||
value={value}
|
||||
value={value || label}
|
||||
radio-size={size}
|
||||
disabled={disabled}
|
||||
onChange={handleChange}
|
||||
@ -141,13 +148,13 @@ export const Radio = ({
|
||||
animate={{ scale: checked ? 1.05 : 0.95 }}
|
||||
transition={{ type: 'spring', stiffness: 300, damping: 20 }}
|
||||
/>
|
||||
{value && (
|
||||
{label && (
|
||||
<StyledLabel
|
||||
htmlFor="input-radio"
|
||||
labelPosition={labelPosition}
|
||||
disabled={disabled}
|
||||
>
|
||||
{value}
|
||||
{label}
|
||||
</StyledLabel>
|
||||
)}
|
||||
</StyledContainer>
|
||||
|
@ -16,7 +16,7 @@ type Story = StoryObj<typeof Radio>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
value: 'Radio',
|
||||
label: 'Radio',
|
||||
checked: false,
|
||||
disabled: false,
|
||||
size: RadioSize.Small,
|
||||
@ -26,10 +26,9 @@ export const Default: Story = {
|
||||
|
||||
export const Catalog: CatalogStory<Story, typeof Radio> = {
|
||||
args: {
|
||||
value: 'Radio',
|
||||
label: 'Radio',
|
||||
},
|
||||
argTypes: {
|
||||
value: { control: false },
|
||||
size: { control: false },
|
||||
},
|
||||
parameters: {
|
||||
|
@ -2,6 +2,10 @@ import { useEffect } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
import { SettingsAccountsInboxSettingsSynchronizationSection } from '@/settings/accounts/components/SettingsAccountsInboxSettingsSynchronizationSection';
|
||||
import {
|
||||
InboxSettingsVisibilityValue,
|
||||
SettingsAccountsInboxSettingsVisibilitySection,
|
||||
} from '@/settings/accounts/components/SettingsAccountsInboxSettingsVisibilitySection';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { IconSettings } from '@/ui/display/icon';
|
||||
@ -20,9 +24,11 @@ export const SettingsAccountsEmailsInboxSettings = () => {
|
||||
if (!account) navigate(AppPath.NotFound);
|
||||
}, [account, navigate]);
|
||||
|
||||
const handleToggle = (_value: boolean) => {};
|
||||
if (!account) return null;
|
||||
|
||||
if (!account) return <></>;
|
||||
const handleSynchronizationToggle = (_value: boolean) => {};
|
||||
|
||||
const handleVisibilityChange = (_value: InboxSettingsVisibilityValue) => {};
|
||||
|
||||
return (
|
||||
<SubMenuTopBarContainer Icon={IconSettings} title="Settings">
|
||||
@ -36,7 +42,11 @@ export const SettingsAccountsEmailsInboxSettings = () => {
|
||||
/>
|
||||
<SettingsAccountsInboxSettingsSynchronizationSection
|
||||
account={account}
|
||||
onToggle={handleToggle}
|
||||
onToggle={handleSynchronizationToggle}
|
||||
/>
|
||||
<SettingsAccountsInboxSettingsVisibilitySection
|
||||
value={account.visibility}
|
||||
onChange={handleVisibilityChange}
|
||||
/>
|
||||
</SettingsPageContainer>
|
||||
</SubMenuTopBarContainer>
|
||||
|
@ -1,14 +1,17 @@
|
||||
import { Account } from '@/accounts/types/Account';
|
||||
import { InboxSettingsVisibilityValue } from '@/settings/accounts/components/SettingsAccountsInboxSettingsVisibilitySection';
|
||||
|
||||
export const mockedAccounts: Account[] = [
|
||||
{
|
||||
email: 'thomas@twenty.com',
|
||||
isSynced: true,
|
||||
uuid: '0794b782-f52e-48c3-977e-b0f57f90de24',
|
||||
visibility: InboxSettingsVisibilityValue.Everything,
|
||||
},
|
||||
{
|
||||
email: 'thomas@twenty.dev',
|
||||
isSynced: false,
|
||||
uuid: 'dc66a7ec-56b2-425b-a8e8-26ff0396c3aa',
|
||||
visibility: InboxSettingsVisibilityValue.Metadata,
|
||||
},
|
||||
];
|
||||
|
Loading…
Reference in New Issue
Block a user