Migrate to a monorepo structure (#2909)
This commit is contained in:
@ -0,0 +1,9 @@
|
||||
import { LightButton } from '@/ui/input/button/components/LightButton';
|
||||
|
||||
type CancelButtonProps = {
|
||||
onCancel?: () => void;
|
||||
};
|
||||
|
||||
export const CancelButton = ({ onCancel }: CancelButtonProps) => {
|
||||
return <LightButton title="Cancel" accent="tertiary" onClick={onCancel} />;
|
||||
};
|
||||
@ -0,0 +1,29 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { CancelButton } from './CancelButton';
|
||||
import { SaveButton } from './SaveButton';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
type SaveAndCancelButtonsProps = {
|
||||
onSave?: () => void;
|
||||
onCancel?: () => void;
|
||||
isSaveDisabled?: boolean;
|
||||
};
|
||||
|
||||
export const SaveAndCancelButtons = ({
|
||||
onSave,
|
||||
onCancel,
|
||||
isSaveDisabled,
|
||||
}: SaveAndCancelButtonsProps) => {
|
||||
return (
|
||||
<StyledContainer>
|
||||
<CancelButton onCancel={onCancel} />
|
||||
<SaveButton onSave={onSave} disabled={isSaveDisabled} />
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,21 @@
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
import { IconDeviceFloppy } from '@/ui/input/constants/icons';
|
||||
|
||||
type SaveButtonProps = {
|
||||
onSave?: () => void;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export const SaveButton = ({ onSave, disabled }: SaveButtonProps) => {
|
||||
return (
|
||||
<Button
|
||||
title="Save"
|
||||
variant="primary"
|
||||
size="small"
|
||||
accent="blue"
|
||||
disabled={disabled}
|
||||
onClick={onSave}
|
||||
Icon={IconDeviceFloppy}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,9 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
const StyledSettingsHeaderContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
export { StyledSettingsHeaderContainer as SettingsHeaderContainer };
|
||||
@ -0,0 +1,79 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { IconChevronRight } from '@/ui/display/icon';
|
||||
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
|
||||
import { SoonPill } from '@/ui/display/pill/components/SoonPill';
|
||||
import { Card } from '@/ui/layout/card/components/Card';
|
||||
|
||||
type SettingsNavigationCardProps = {
|
||||
children: ReactNode;
|
||||
disabled?: boolean;
|
||||
hasSoonPill?: boolean;
|
||||
Icon: IconComponent;
|
||||
onClick?: () => void;
|
||||
title: string;
|
||||
};
|
||||
|
||||
const StyledCard = styled(Card)<{
|
||||
disabled?: boolean;
|
||||
onClick?: () => void;
|
||||
}>`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
cursor: ${({ disabled, onClick }) =>
|
||||
disabled ? 'not-allowed' : onClick ? 'pointer' : 'default'};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
padding-bottom: ${({ theme }) => theme.spacing(4)};
|
||||
padding-top: ${({ theme }) => theme.spacing(4)};
|
||||
`;
|
||||
|
||||
const StyledHeader = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(3)};
|
||||
`;
|
||||
|
||||
const StyledTitle = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.secondary};
|
||||
display: flex;
|
||||
flex: 1 0 auto;
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
justify-content: flex-start;
|
||||
`;
|
||||
|
||||
const StyledIconChevronRight = styled(IconChevronRight)`
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
`;
|
||||
|
||||
const StyledDescription = styled.div`
|
||||
padding-left: ${({ theme }) => theme.spacing(8)};
|
||||
`;
|
||||
|
||||
export const SettingsNavigationCard = ({
|
||||
children,
|
||||
disabled,
|
||||
hasSoonPill,
|
||||
Icon,
|
||||
onClick,
|
||||
title,
|
||||
}: SettingsNavigationCardProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<StyledCard disabled={disabled} onClick={onClick}>
|
||||
<StyledHeader>
|
||||
<Icon size={theme.icon.size.lg} stroke={theme.icon.stroke.sm} />
|
||||
<StyledTitle>
|
||||
{title}
|
||||
{hasSoonPill && <SoonPill />}
|
||||
</StyledTitle>
|
||||
<StyledIconChevronRight size={theme.icon.size.sm} />
|
||||
</StyledHeader>
|
||||
<StyledDescription>{children}</StyledDescription>
|
||||
</StyledCard>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,152 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useMatch, useNavigate, useResolvedPath } from 'react-router-dom';
|
||||
|
||||
import { useAuth } from '@/auth/hooks/useAuth';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import {
|
||||
IconAt,
|
||||
IconCalendarEvent,
|
||||
IconColorSwatch,
|
||||
IconHierarchy2,
|
||||
IconLogout,
|
||||
IconMail,
|
||||
IconRobot,
|
||||
IconSettings,
|
||||
IconUserCircle,
|
||||
IconUsers,
|
||||
} from '@/ui/display/icon';
|
||||
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
|
||||
import { NavigationDrawerItemGroup } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItemGroup';
|
||||
import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
|
||||
import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
|
||||
export const SettingsNavigationDrawerItems = () => {
|
||||
const navigate = useNavigate();
|
||||
const { signOut } = useAuth();
|
||||
|
||||
const handleLogout = useCallback(() => {
|
||||
signOut();
|
||||
navigate(AppPath.SignIn);
|
||||
}, [signOut, navigate]);
|
||||
|
||||
const isMessagingEnabled = useIsFeatureEnabled('IS_MESSAGING_ENABLED');
|
||||
const isAccountsItemActive = !!useMatch({
|
||||
path: useResolvedPath('/settings/accounts').pathname,
|
||||
end: true,
|
||||
});
|
||||
const isAccountsEmailsItemActive = !!useMatch({
|
||||
path: useResolvedPath('/settings/accounts/emails').pathname,
|
||||
end: true,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<NavigationDrawerSection>
|
||||
<NavigationDrawerSectionTitle label="User" />
|
||||
<NavigationDrawerItem
|
||||
label="Profile"
|
||||
to="/settings/profile"
|
||||
Icon={IconUserCircle}
|
||||
active={
|
||||
!!useMatch({
|
||||
path: useResolvedPath('/settings/profile').pathname,
|
||||
end: true,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<NavigationDrawerItem
|
||||
label="Appearance"
|
||||
to="/settings/profile/appearance"
|
||||
Icon={IconColorSwatch}
|
||||
active={
|
||||
!!useMatch({
|
||||
path: useResolvedPath('/settings/profile/appearance').pathname,
|
||||
end: true,
|
||||
})
|
||||
}
|
||||
/>
|
||||
{isMessagingEnabled && (
|
||||
<NavigationDrawerItemGroup>
|
||||
<NavigationDrawerItem
|
||||
label="Accounts"
|
||||
to="/settings/accounts"
|
||||
Icon={IconAt}
|
||||
active={isAccountsItemActive}
|
||||
/>
|
||||
<NavigationDrawerItem
|
||||
level={2}
|
||||
label="Emails"
|
||||
to="/settings/accounts/emails"
|
||||
Icon={IconMail}
|
||||
active={isAccountsEmailsItemActive}
|
||||
/>
|
||||
<NavigationDrawerItem
|
||||
level={2}
|
||||
label="Calendars"
|
||||
Icon={IconCalendarEvent}
|
||||
soon
|
||||
/>
|
||||
</NavigationDrawerItemGroup>
|
||||
)}
|
||||
</NavigationDrawerSection>
|
||||
|
||||
<NavigationDrawerSection>
|
||||
<NavigationDrawerSectionTitle label="Workspace" />
|
||||
<NavigationDrawerItem
|
||||
label="General"
|
||||
to="/settings/workspace"
|
||||
Icon={IconSettings}
|
||||
active={
|
||||
!!useMatch({
|
||||
path: useResolvedPath('/settings/workspace').pathname,
|
||||
end: true,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<NavigationDrawerItem
|
||||
label="Members"
|
||||
to="/settings/workspace-members"
|
||||
Icon={IconUsers}
|
||||
active={
|
||||
!!useMatch({
|
||||
path: useResolvedPath('/settings/workspace-members').pathname,
|
||||
end: true,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<NavigationDrawerItem
|
||||
label="Data model"
|
||||
to="/settings/objects"
|
||||
Icon={IconHierarchy2}
|
||||
active={
|
||||
!!useMatch({
|
||||
path: useResolvedPath('/settings/objects').pathname,
|
||||
end: false,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<NavigationDrawerItem
|
||||
label="Developers"
|
||||
to="/settings/developers/api-keys"
|
||||
Icon={IconRobot}
|
||||
active={
|
||||
!!useMatch({
|
||||
path: useResolvedPath('/settings/developers/api-keys').pathname,
|
||||
end: true,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</NavigationDrawerSection>
|
||||
|
||||
<NavigationDrawerSection>
|
||||
<NavigationDrawerSectionTitle label="Other" />
|
||||
<NavigationDrawerItem
|
||||
label="Logout"
|
||||
onClick={handleLogout}
|
||||
Icon={IconLogout}
|
||||
/>
|
||||
</NavigationDrawerSection>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,14 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { objectSettingsWidth } from '../data-model/constants/objectSettings';
|
||||
|
||||
const StyledSettingsPageContainer = styled.div<{ width?: number }>`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(8)};
|
||||
overflow: auto;
|
||||
padding: ${({ theme }) => theme.spacing(8)};
|
||||
width: ${({ width }) => (width ? width + 'px' : objectSettingsWidth)};
|
||||
`;
|
||||
|
||||
export { StyledSettingsPageContainer as SettingsPageContainer };
|
||||
@ -0,0 +1,20 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { CancelButton } from '../SaveAndCancelButtons/CancelButton';
|
||||
|
||||
const meta: Meta<typeof CancelButton> = {
|
||||
title: 'Modules/Settings/CancelButton',
|
||||
component: CancelButton,
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof CancelButton>;
|
||||
|
||||
export const Default: Story = {
|
||||
argTypes: {
|
||||
onCancel: { control: false },
|
||||
},
|
||||
args: {
|
||||
onCancel: () => {},
|
||||
},
|
||||
};
|
||||
@ -0,0 +1,31 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { SaveButton } from '../SaveAndCancelButtons/SaveButton';
|
||||
|
||||
const meta: Meta<typeof SaveButton> = {
|
||||
title: 'Modules/Settings/SaveButton',
|
||||
component: SaveButton,
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof SaveButton>;
|
||||
|
||||
export const Default: Story = {
|
||||
argTypes: {
|
||||
onSave: { control: false },
|
||||
},
|
||||
args: {
|
||||
onSave: () => {},
|
||||
disabled: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
argTypes: {
|
||||
onSave: { control: false },
|
||||
},
|
||||
args: {
|
||||
onSave: () => {},
|
||||
disabled: true,
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user