mirror of
https://github.com/lingble/twenty.git
synced 2025-10-29 03:42:30 +00:00
New Settings Layout (#6867)
#### \ Description - **Added "Exit Settings" Back Button**: Introduced a new back button labeled "Exit Settings" for easy navigation back to the app content. - **Implemented Settings Navbar Breadcrumb**: A breadcrumb navigation bar for each settings page has been added to improve navigation within the settings. This ensures users can easily trace their location within the settings. - **Persistent CTA Zone**: The Call-to-Action (CTA) zone at the top of the page now remains visible even when the user scrolls down, preventing unresponsive behavior and improving accessibility. - **Page Title**: The page title has been added to each settings page and separated from the breadcrumb, following the app's layout standards. - we could not reproduce the Billing and CMR Migrations settings on the app, but we updated the files according, please let's us know if is there any way to log in with access to these pages, or if is everything ok. - Some breadcrumbs are not following the Figma, are following the current app sections isntead, because we would need to change the sidebar structure, and we should not do it on this PR ### Demo <https://www.loom.com/share/21b20a2cd2f3471e94d61563c9901b19?sid=9dc49456-6cae-48e1-9149-8d706f00ab65> ### Refs: #6144 Fixes #6144 --------- Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com> Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
committed by
GitHub
parent
fe4ca2133d
commit
c42ea57b97
@@ -42,7 +42,7 @@ export const AppNavigationDrawer = ({
|
||||
const drawerProps: NavigationDrawerProps = isSettingsDrawer
|
||||
? {
|
||||
isSubMenu: true,
|
||||
title: 'Settings',
|
||||
title: 'Exit Settings',
|
||||
children: <SettingsNavigationDrawerItems />,
|
||||
footer: <GithubVersionLink />,
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ export const SettingsNavigationDrawerItems = () => {
|
||||
Icon={IconUserCircle}
|
||||
/>
|
||||
<SettingsNavigationDrawerItem
|
||||
label="Appearance"
|
||||
label="Experience"
|
||||
path={SettingsPath.Appearance}
|
||||
Icon={IconColorSwatch}
|
||||
/>
|
||||
|
||||
@@ -10,7 +10,7 @@ const StyledSettingsPageContainer = styled.div<{ width?: number }>`
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(8)};
|
||||
overflow: auto;
|
||||
padding: ${({ theme }) => theme.spacing(8)};
|
||||
padding: ${({ theme }) => theme.spacing(6, 8, 8)};
|
||||
width: ${({ width }) => {
|
||||
if (isDefined(width)) {
|
||||
return width + 'px';
|
||||
|
||||
@@ -6,7 +6,7 @@ export const SettingsReadDocumentationButton = () => {
|
||||
return (
|
||||
<Button
|
||||
title="Read documentation"
|
||||
variant="primary"
|
||||
variant="secondary"
|
||||
accent="default"
|
||||
size="small"
|
||||
Icon={IconBook2}
|
||||
|
||||
@@ -89,7 +89,7 @@ type PageHeaderProps = {
|
||||
hasNextRecord?: boolean;
|
||||
navigateToPreviousRecord?: () => void;
|
||||
navigateToNextRecord?: () => void;
|
||||
Icon: IconComponent;
|
||||
Icon?: IconComponent;
|
||||
children?: ReactNode;
|
||||
width?: number;
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@ const StyledPanel = styled.div`
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
width: 100%;
|
||||
padding-bottom: ${({ theme }) => theme.spacing(10)};
|
||||
`;
|
||||
|
||||
type PagePanelProps = {
|
||||
|
||||
@@ -2,48 +2,52 @@ import styled from '@emotion/styled';
|
||||
import { JSX, ReactNode } from 'react';
|
||||
import { IconComponent } from 'twenty-ui';
|
||||
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
|
||||
import { InformationBannerWrapper } from '@/information-banner/components/InformationBannerWrapper';
|
||||
import { OBJECT_SETTINGS_WIDTH } from '@/settings/data-model/constants/ObjectSettings';
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbProps,
|
||||
} from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
import { PageBody } from './PageBody';
|
||||
import { PageHeader } from './PageHeader';
|
||||
|
||||
type SubMenuTopBarContainerProps = {
|
||||
children: JSX.Element | JSX.Element[];
|
||||
title: string | ReactNode;
|
||||
title?: string;
|
||||
actionButton?: ReactNode;
|
||||
Icon: IconComponent;
|
||||
className?: string;
|
||||
links: BreadcrumbProps['links'];
|
||||
};
|
||||
|
||||
const StyledContainer = styled.div<{ isMobile: boolean }>`
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: ${({ theme, isMobile }) => (!isMobile ? theme.spacing(3) : 0)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledTitle = styled.h3`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
font-size: ${({ theme }) => theme.font.size.lg};
|
||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||
line-height: 1.2;
|
||||
margin: ${({ theme }) => theme.spacing(8, 8, 2)};
|
||||
`;
|
||||
|
||||
export const SubMenuTopBarContainer = ({
|
||||
children,
|
||||
title,
|
||||
actionButton,
|
||||
Icon,
|
||||
className,
|
||||
links,
|
||||
}: SubMenuTopBarContainerProps) => {
|
||||
const isMobile = useIsMobile();
|
||||
|
||||
return (
|
||||
<StyledContainer isMobile={isMobile} className={className}>
|
||||
<PageHeader
|
||||
title={title}
|
||||
Icon={Icon}
|
||||
width={OBJECT_SETTINGS_WIDTH + 4 * 8}
|
||||
>
|
||||
<StyledContainer className={className}>
|
||||
<PageHeader title={<Breadcrumb links={links} />}>
|
||||
{actionButton}
|
||||
</PageHeader>
|
||||
<PageBody>
|
||||
<InformationBannerWrapper />
|
||||
{title && <StyledTitle>{title}</StyledTitle>}
|
||||
{children}
|
||||
</PageBody>
|
||||
</StyledContainer>
|
||||
|
||||
@@ -1,27 +1,22 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { CSSProperties, Fragment, ReactNode } from 'react';
|
||||
import { Fragment, ReactNode } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
type BreadcrumbProps = {
|
||||
export type BreadcrumbProps = {
|
||||
className?: string;
|
||||
links: {
|
||||
href?: string;
|
||||
styles?: CSSProperties;
|
||||
children?: string | ReactNode;
|
||||
}[];
|
||||
links: { children: string | ReactNode; href?: string }[];
|
||||
};
|
||||
|
||||
const StyledWrapper = styled.nav`
|
||||
align-items: center;
|
||||
color: ${({ theme }) => theme.font.color.secondary};
|
||||
display: flex;
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
display: grid;
|
||||
font-size: ${({ theme }) => theme.font.size.md};
|
||||
// font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
line-height: ${({ theme }) => theme.text.lineHeight.lg};
|
||||
white-space: nowrap;
|
||||
grid-auto-flow: column;
|
||||
grid-column-gap: ${({ theme }) => theme.spacing(1)};
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
height: ${({ theme }) => theme.spacing(8)};
|
||||
`;
|
||||
|
||||
const StyledLink = styled(Link)`
|
||||
@@ -39,7 +34,10 @@ const StyledText = styled.span`
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
// TODO: not sure that passing styles to the link is a good idea
|
||||
const StyledDivider = styled.span`
|
||||
width: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
export const Breadcrumb = ({ className, links }: BreadcrumbProps) => {
|
||||
return (
|
||||
<StyledWrapper className={className}>
|
||||
@@ -49,15 +47,13 @@ export const Breadcrumb = ({ className, links }: BreadcrumbProps) => {
|
||||
return (
|
||||
<Fragment key={index}>
|
||||
{link.href ? (
|
||||
<StyledLink style={link.styles} title={text} to={link.href}>
|
||||
<StyledLink title={text} to={link.href}>
|
||||
{link.children}
|
||||
</StyledLink>
|
||||
) : (
|
||||
<StyledText style={link.styles} title={text}>
|
||||
{link.children}
|
||||
</StyledText>
|
||||
<StyledText title={text}>{link.children}</StyledText>
|
||||
)}
|
||||
{index < links.length - 1 && '/'}
|
||||
{index < links.length - 1 && <StyledDivider>/</StyledDivider>}
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ReactNode, useState } from 'react';
|
||||
import { css, useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { motion } from 'framer-motion';
|
||||
import { ReactNode, useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { MOBILE_VIEWPORT } from 'twenty-ui';
|
||||
|
||||
@@ -32,7 +32,7 @@ const StyledContainer = styled.div<{ isSubMenu?: boolean }>`
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(3.5)};
|
||||
gap: ${({ theme }) => theme.spacing(3)};
|
||||
height: 100%;
|
||||
min-width: ${DESKTOP_NAV_DRAWER_WIDTHS.menu}px;
|
||||
padding: ${({ theme }) => theme.spacing(3, 2, 4)};
|
||||
@@ -41,7 +41,6 @@ const StyledContainer = styled.div<{ isSubMenu?: boolean }>`
|
||||
isSubMenu
|
||||
? css`
|
||||
padding-right: ${theme.spacing(8)};
|
||||
padding-top: 41px;
|
||||
`
|
||||
: ''}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { IconChevronLeft } from 'twenty-ui';
|
||||
import { IconX } from 'twenty-ui';
|
||||
|
||||
import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink';
|
||||
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
|
||||
@@ -18,17 +18,22 @@ const StyledIconAndButtonContainer = styled.button`
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: ${({ theme }) => theme.font.size.lg};
|
||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
line-height: ${({ theme }) => theme.text.lineHeight.md};
|
||||
padding: ${({ theme }) => theme.spacing(1)};
|
||||
padding: ${({ theme }) => theme.spacing(1.5, 1)};
|
||||
width: 100%;
|
||||
font-family: ${({ theme }) => theme.font.family};
|
||||
&:hover {
|
||||
background: ${({ theme }) => theme.background.transparent.light};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: ${({ theme }) => theme.spacing(8)};
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
@@ -42,9 +47,10 @@ export const NavigationDrawerBackButton = ({
|
||||
<StyledContainer>
|
||||
<UndecoratedLink to={navigationMemorizedUrl} replace>
|
||||
<StyledIconAndButtonContainer>
|
||||
<IconChevronLeft
|
||||
<IconX
|
||||
size={theme.icon.size.md}
|
||||
stroke={theme.icon.stroke.lg}
|
||||
color={theme.font.color.tertiary}
|
||||
/>
|
||||
<span>{title}</span>
|
||||
</StyledIconAndButtonContainer>
|
||||
|
||||
@@ -3,18 +3,16 @@ import React, { useEffect, useState } from 'react';
|
||||
import rehypeStringify from 'rehype-stringify';
|
||||
import remarkParse from 'remark-parse';
|
||||
import remarkRehype from 'remark-rehype';
|
||||
import { H1Title, IconRocket } from 'twenty-ui';
|
||||
import { IconRocket } from 'twenty-ui';
|
||||
import { unified } from 'unified';
|
||||
import { visit } from 'unist-util-visit';
|
||||
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
||||
|
||||
const StyledH1Title = styled(H1Title)`
|
||||
margin-bottom: 0;
|
||||
`;
|
||||
|
||||
type ReleaseNote = {
|
||||
slug: string;
|
||||
date: string;
|
||||
@@ -108,9 +106,20 @@ export const Releases = () => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<SubMenuTopBarContainer Icon={IconRocket} title="Releases">
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconRocket}
|
||||
title="Releases"
|
||||
links={[
|
||||
{
|
||||
children: 'Others',
|
||||
href: getSettingsPagePath(SettingsPath.Releases),
|
||||
},
|
||||
{
|
||||
children: 'Releases',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<StyledH1Title title="Releases" />
|
||||
<ScrollWrapper contextProviderName="releases">
|
||||
<StyledReleaseContainer>
|
||||
{releases.map((release) => (
|
||||
|
||||
@@ -14,7 +14,9 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { SettingsBillingCoverImage } from '@/billing/components/SettingsBillingCoverImage';
|
||||
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { Info } from '@/ui/display/info/components/Info';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
@@ -140,7 +142,17 @@ export const SettingsBilling = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<SubMenuTopBarContainer Icon={IconCurrencyDollar} title="Billing">
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconCurrencyDollar}
|
||||
title="Billing"
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPagePath(SettingsPath.Workspace),
|
||||
},
|
||||
{ children: 'Billing' },
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<StyledH1Title title="Billing" />
|
||||
<SettingsBillingCoverImage />
|
||||
|
||||
@@ -6,11 +6,23 @@ import { DeleteAccount } from '@/settings/profile/components/DeleteAccount';
|
||||
import { EmailField } from '@/settings/profile/components/EmailField';
|
||||
import { NameFields } from '@/settings/profile/components/NameFields';
|
||||
import { ProfilePictureUploader } from '@/settings/profile/components/ProfilePictureUploader';
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
|
||||
export const SettingsProfile = () => (
|
||||
<SubMenuTopBarContainer Icon={IconUserCircle} title="Profile">
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconUserCircle}
|
||||
title="Profile"
|
||||
links={[
|
||||
{
|
||||
children: 'User',
|
||||
href: getSettingsPagePath(SettingsPath.ProfilePage),
|
||||
},
|
||||
{ children: 'Profile' },
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<Section>
|
||||
<H2Title title="Picture" />
|
||||
|
||||
@@ -2,14 +2,26 @@ import { H2Title, IconSettings } from 'twenty-ui';
|
||||
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { DeleteWorkspace } from '@/settings/profile/components/DeleteWorkspace';
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { NameField } from '@/settings/workspace/components/NameField';
|
||||
import { ToggleImpersonate } from '@/settings/workspace/components/ToggleImpersonate';
|
||||
import { WorkspaceLogoUploader } from '@/settings/workspace/components/WorkspaceLogoUploader';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
|
||||
export const SettingsWorkspace = () => (
|
||||
<SubMenuTopBarContainer Icon={IconSettings} title="General">
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconSettings}
|
||||
title="General"
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPagePath(SettingsPath.Workspace),
|
||||
},
|
||||
{ children: 'General' },
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<Section>
|
||||
<H2Title title="Picture" />
|
||||
|
||||
@@ -9,6 +9,8 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi
|
||||
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { IconButton } from '@/ui/input/button/components/IconButton';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
@@ -46,7 +48,17 @@ export const SettingsWorkspaceMembers = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<SubMenuTopBarContainer Icon={IconUsers} title="Members">
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconUsers}
|
||||
title="Members"
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPagePath(SettingsPath.Workspace),
|
||||
},
|
||||
{ children: 'Members' },
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<Section>
|
||||
<H2Title
|
||||
|
||||
@@ -30,7 +30,7 @@ export const Default: Story = {
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
await canvas.findByText('Theme', undefined, {
|
||||
await canvas.findByText('Appearance', undefined, {
|
||||
timeout: 3000,
|
||||
});
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ import { SettingsAccountsBlocklistSection } from '@/settings/accounts/components
|
||||
import { SettingsAccountsConnectedAccountsListCard } from '@/settings/accounts/components/SettingsAccountsConnectedAccountsListCard';
|
||||
import { SettingsAccountsSettingsSection } from '@/settings/accounts/components/SettingsAccountsSettingsSection';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
|
||||
@@ -33,7 +35,17 @@ export const SettingsAccounts = () => {
|
||||
});
|
||||
|
||||
return (
|
||||
<SubMenuTopBarContainer Icon={IconAt} title="Account">
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconAt}
|
||||
title="Account"
|
||||
links={[
|
||||
{
|
||||
children: 'User',
|
||||
href: getSettingsPagePath(SettingsPath.ProfilePage),
|
||||
},
|
||||
{ children: 'Account' },
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
{loading ? (
|
||||
<SettingsAccountLoader />
|
||||
|
||||
@@ -4,24 +4,24 @@ import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
import { IconCalendarEvent } from 'twenty-ui';
|
||||
|
||||
export const SettingsAccountsCalendars = () => {
|
||||
return (
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconCalendarEvent}
|
||||
title={
|
||||
<Breadcrumb
|
||||
links={[
|
||||
{
|
||||
children: 'Accounts',
|
||||
href: getSettingsPagePath(SettingsPath.Accounts),
|
||||
},
|
||||
{ children: 'Calendars' },
|
||||
]}
|
||||
/>
|
||||
}
|
||||
title="Calendars"
|
||||
links={[
|
||||
{
|
||||
children: 'User',
|
||||
href: getSettingsPagePath(SettingsPath.ProfilePage),
|
||||
},
|
||||
{
|
||||
children: 'Accounts',
|
||||
href: getSettingsPagePath(SettingsPath.Accounts),
|
||||
},
|
||||
{ children: 'Calendars' },
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<Section>
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
import { SettingsAccountsMessageChannelsContainer } from '@/settings/accounts/components/SettingsAccountsMessageChannelsContainer';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
import { IconMail } from 'twenty-ui';
|
||||
|
||||
export const SettingsAccountsEmails = () => (
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconMail}
|
||||
title={
|
||||
<Breadcrumb
|
||||
links={[
|
||||
{ children: 'Accounts', href: '/settings/accounts' },
|
||||
{ children: 'Emails' },
|
||||
]}
|
||||
/>
|
||||
}
|
||||
title="Emails"
|
||||
links={[
|
||||
{
|
||||
children: 'User',
|
||||
href: getSettingsPagePath(SettingsPath.ProfilePage),
|
||||
},
|
||||
{
|
||||
children: 'Accounts',
|
||||
href: getSettingsPagePath(SettingsPath.Accounts),
|
||||
},
|
||||
{ children: 'Emails' },
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<Section>
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
import { SettingsNewAccountSection } from '@/settings/accounts/components/SettingsNewAccountSection';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
import { IconAt } from 'twenty-ui';
|
||||
|
||||
export const SettingsNewAccount = () => {
|
||||
return (
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconAt}
|
||||
title={
|
||||
<Breadcrumb
|
||||
links={[
|
||||
{ children: 'Accounts', href: '/settings/accounts' },
|
||||
{ children: `New` },
|
||||
]}
|
||||
/>
|
||||
}
|
||||
title="New Account"
|
||||
links={[
|
||||
{
|
||||
children: 'User',
|
||||
href: getSettingsPagePath(SettingsPath.ProfilePage),
|
||||
},
|
||||
{
|
||||
children: 'Accounts',
|
||||
href: getSettingsPagePath(SettingsPath.Accounts),
|
||||
},
|
||||
{ children: `New` },
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<SettingsNewAccountSection />
|
||||
|
||||
@@ -6,9 +6,10 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { SettingsHeaderContainer } from '@/settings/components/SettingsHeaderContainer';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { SettingsReadDocumentationButton } from '@/settings/developers/components/SettingsReadDocumentationButton';
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
const REVERT_PUBLIC_KEY = 'pk_live_a87fee8c-28c7-494f-99a3-996ff89f9918';
|
||||
@@ -18,7 +19,14 @@ export const SettingsCRMMigration = () => {
|
||||
return (
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconSettings}
|
||||
title={<Breadcrumb links={[{ children: 'Migrate' }]} />}
|
||||
title="Migrate"
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPagePath(SettingsPath.Workspace),
|
||||
},
|
||||
{ children: 'Migrate' },
|
||||
]}
|
||||
actionButton={<SettingsReadDocumentationButton />}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
|
||||
@@ -7,7 +7,6 @@ import { z } from 'zod';
|
||||
import { useCreateOneObjectMetadataItem } from '@/object-metadata/hooks/useCreateOneObjectMetadataItem';
|
||||
import { getObjectSlug } from '@/object-metadata/utils/getObjectSlug';
|
||||
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
||||
import { SettingsHeaderContainer } from '@/settings/components/SettingsHeaderContainer';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import {
|
||||
SettingsDataModelObjectAboutForm,
|
||||
@@ -20,7 +19,6 @@ import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/Snac
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
|
||||
const newObjectFormSchema = settingsDataModelObjectAboutFormSchema;
|
||||
|
||||
@@ -72,17 +70,18 @@ export const SettingsNewObject = () => {
|
||||
<FormProvider {...formConfig}>
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconHierarchy2}
|
||||
title={
|
||||
<Breadcrumb
|
||||
links={[
|
||||
{
|
||||
children: 'Objects',
|
||||
href: settingsObjectsPagePath,
|
||||
},
|
||||
{ children: 'New' },
|
||||
]}
|
||||
/>
|
||||
}
|
||||
title="New Object"
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPagePath(SettingsPath.Workspace),
|
||||
},
|
||||
{
|
||||
children: 'Objects',
|
||||
href: settingsObjectsPagePath,
|
||||
},
|
||||
{ children: 'New' },
|
||||
]}
|
||||
actionButton={
|
||||
<SaveAndCancelButtons
|
||||
isSaveDisabled={!canSave}
|
||||
@@ -93,7 +92,6 @@ export const SettingsNewObject = () => {
|
||||
}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<SettingsHeaderContainer></SettingsHeaderContainer>
|
||||
<Section>
|
||||
<H2Title
|
||||
title="About"
|
||||
|
||||
@@ -8,7 +8,6 @@ import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink';
|
||||
import styled from '@emotion/styled';
|
||||
import { isNonEmptyArray } from '@sniptt/guards';
|
||||
@@ -49,14 +48,15 @@ export const SettingsObjectDetailPageContent = ({
|
||||
return (
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconHierarchy2}
|
||||
title={
|
||||
<Breadcrumb
|
||||
links={[
|
||||
{ children: 'Objects', href: '/settings/objects' },
|
||||
{ children: objectMetadataItem.labelPlural },
|
||||
]}
|
||||
/>
|
||||
}
|
||||
title={objectMetadataItem.labelPlural}
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPagePath(SettingsPath.Workspace),
|
||||
},
|
||||
{ children: 'Objects', href: '/settings/objects' },
|
||||
{ children: objectMetadataItem.labelPlural },
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<Section>
|
||||
|
||||
@@ -13,7 +13,6 @@ import { useUpdateOneObjectMetadataItem } from '@/object-metadata/hooks/useUpdat
|
||||
import { getObjectSlug } from '@/object-metadata/utils/getObjectSlug';
|
||||
import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
|
||||
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
||||
import { SettingsHeaderContainer } from '@/settings/components/SettingsHeaderContainer';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import {
|
||||
SettingsDataModelObjectAboutForm,
|
||||
@@ -30,7 +29,6 @@ import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
|
||||
const objectEditFormSchema = z
|
||||
.object({})
|
||||
@@ -110,35 +108,36 @@ export const SettingsObjectEdit = () => {
|
||||
<FormProvider {...formConfig}>
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconHierarchy2}
|
||||
title={
|
||||
<Breadcrumb
|
||||
links={[
|
||||
{
|
||||
children: 'Objects',
|
||||
href: settingsObjectsPagePath,
|
||||
},
|
||||
{
|
||||
children: activeObjectMetadataItem.labelPlural,
|
||||
href: `${settingsObjectsPagePath}/${objectSlug}`,
|
||||
},
|
||||
{ children: 'Edit' },
|
||||
]}
|
||||
/>
|
||||
title="Edit"
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPagePath(SettingsPath.Workspace),
|
||||
},
|
||||
{
|
||||
children: 'Objects',
|
||||
href: settingsObjectsPagePath,
|
||||
},
|
||||
{
|
||||
children: activeObjectMetadataItem.labelPlural,
|
||||
href: `${settingsObjectsPagePath}/${objectSlug}`,
|
||||
},
|
||||
{ children: 'Edit Object' },
|
||||
]}
|
||||
actionButton={
|
||||
activeObjectMetadataItem.isCustom && (
|
||||
<SaveAndCancelButtons
|
||||
isSaveDisabled={!canSave}
|
||||
isCancelDisabled={isSubmitting}
|
||||
onCancel={() =>
|
||||
navigate(`${settingsObjectsPagePath}/${objectSlug}`)
|
||||
}
|
||||
onSave={formConfig.handleSubmit(handleSave)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<SettingsHeaderContainer>
|
||||
{activeObjectMetadataItem.isCustom && (
|
||||
<SaveAndCancelButtons
|
||||
isSaveDisabled={!canSave}
|
||||
isCancelDisabled={isSubmitting}
|
||||
onCancel={() =>
|
||||
navigate(`${settingsObjectsPagePath}/${objectSlug}`)
|
||||
}
|
||||
onSave={formConfig.handleSubmit(handleSave)}
|
||||
/>
|
||||
)}
|
||||
</SettingsHeaderContainer>
|
||||
<Section>
|
||||
<H2Title
|
||||
title="About"
|
||||
|
||||
@@ -26,13 +26,14 @@ import { SettingsDataModelFieldIconLabelForm } from '@/settings/data-model/field
|
||||
import { SettingsDataModelFieldSettingsFormCard } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldSettingsFormCard';
|
||||
import { settingsFieldFormSchema } from '@/settings/data-model/fields/forms/validation-schemas/settingsFieldFormSchema';
|
||||
import { SettingsSupportedFieldType } from '@/settings/data-model/types/SettingsSupportedFieldType';
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
@@ -173,23 +174,24 @@ export const SettingsObjectFieldEdit = () => {
|
||||
<FormProvider {...formConfig}>
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconHierarchy2}
|
||||
title={
|
||||
<Breadcrumb
|
||||
links={[
|
||||
{
|
||||
children: 'Objects',
|
||||
href: '/settings/objects',
|
||||
styles: { minWidth: 'max-content' },
|
||||
},
|
||||
{
|
||||
children: activeObjectMetadataItem.labelPlural,
|
||||
href: `/settings/objects/${objectSlug}`,
|
||||
styles: { maxWidth: '60%' },
|
||||
},
|
||||
{ children: activeMetadataField.label },
|
||||
]}
|
||||
/>
|
||||
}
|
||||
title={activeMetadataField?.label}
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPagePath(SettingsPath.Workspace),
|
||||
},
|
||||
{
|
||||
children: 'Objects',
|
||||
href: '/settings/objects',
|
||||
},
|
||||
{
|
||||
children: activeObjectMetadataItem.labelPlural,
|
||||
href: `/settings/objects/${objectSlug}`,
|
||||
},
|
||||
{
|
||||
children: activeMetadataField.label,
|
||||
},
|
||||
]}
|
||||
actionButton={
|
||||
shouldDisplaySaveAndCancel && (
|
||||
<SaveAndCancelButtons
|
||||
|
||||
@@ -9,11 +9,12 @@ import { SettingsPageContainer } from '@/settings/components/SettingsPageContain
|
||||
|
||||
import { useFieldMetadataItem } from '@/object-metadata/hooks/useFieldMetadataItem';
|
||||
import { settingsObjectFieldsFamilyState } from '@/settings/data-model/object-details/states/settingsObjectFieldsFamilyState';
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { SettingsObjectFieldTable } from '~/pages/settings/data-model/SettingsObjectFieldTable';
|
||||
|
||||
@@ -86,18 +87,18 @@ export const SettingsObjectNewFieldStep1 = () => {
|
||||
return (
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconHierarchy2}
|
||||
title={
|
||||
<Breadcrumb
|
||||
links={[
|
||||
{ children: 'Objects', href: '/settings/objects' },
|
||||
{
|
||||
children: activeObjectMetadataItem.labelPlural,
|
||||
href: `/settings/objects/${objectSlug}`,
|
||||
},
|
||||
{ children: 'New Field' },
|
||||
]}
|
||||
/>
|
||||
}
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPagePath(SettingsPath.Workspace),
|
||||
},
|
||||
{ children: 'Objects', href: '/settings/objects' },
|
||||
{
|
||||
children: activeObjectMetadataItem.labelPlural,
|
||||
href: `/settings/objects/${objectSlug}`,
|
||||
},
|
||||
{ children: 'New Field' },
|
||||
]}
|
||||
actionButton={
|
||||
!activeObjectMetadataItem.isRemote && (
|
||||
<SaveAndCancelButtons
|
||||
|
||||
@@ -19,7 +19,6 @@ import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/Snac
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
import { View } from '@/views/types/View';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
@@ -41,6 +40,7 @@ type SettingsDataModelNewFieldFormValues = z.infer<
|
||||
|
||||
const StyledH1Title = styled(H1Title)`
|
||||
margin-bottom: 0;
|
||||
padding-top: ${({ theme }) => theme.spacing(3)};
|
||||
`;
|
||||
export const SettingsObjectNewFieldStep2 = () => {
|
||||
const navigate = useNavigate();
|
||||
@@ -177,30 +177,24 @@ export const SettingsObjectNewFieldStep2 = () => {
|
||||
>
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconHierarchy2}
|
||||
title={
|
||||
<Breadcrumb
|
||||
links={[
|
||||
{
|
||||
children: 'Objects',
|
||||
href: '/settings/objects',
|
||||
styles: { minWidth: 'max-content' },
|
||||
},
|
||||
{
|
||||
children: activeObjectMetadataItem.labelPlural,
|
||||
href: `/settings/objects/${objectSlug}`,
|
||||
styles: { maxWidth: '50%' },
|
||||
},
|
||||
{
|
||||
children: (
|
||||
<SettingsDataModelNewFieldBreadcrumbDropDown
|
||||
isConfigureStep={isConfigureStep}
|
||||
onBreadcrumbClick={setIsConfigureStep}
|
||||
/>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
}
|
||||
links={[
|
||||
{
|
||||
children: 'Objects',
|
||||
href: '/settings/objects',
|
||||
},
|
||||
{
|
||||
children: activeObjectMetadataItem.labelPlural,
|
||||
href: `/settings/objects/${objectSlug}`,
|
||||
},
|
||||
{
|
||||
children: (
|
||||
<SettingsDataModelNewFieldBreadcrumbDropDown
|
||||
isConfigureStep={isConfigureStep}
|
||||
onBreadcrumbClick={setIsConfigureStep}
|
||||
/>
|
||||
),
|
||||
},
|
||||
]}
|
||||
actionButton={
|
||||
!activeObjectMetadataItem.isRemote && (
|
||||
<SaveAndCancelButtons
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
import { ReactFlowProvider } from 'reactflow';
|
||||
|
||||
import { SettingsDataModelOverview } from '@/settings/data-model/graph-overview/components/SettingsDataModelOverview';
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
import { IconHierarchy2 } from 'twenty-ui';
|
||||
|
||||
export const SettingsObjectOverview = () => {
|
||||
return (
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconHierarchy2}
|
||||
title={
|
||||
<Breadcrumb
|
||||
links={[
|
||||
{ children: 'Data model', href: '/settings/objects' },
|
||||
{
|
||||
children: 'Overview',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
}
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPagePath(SettingsPath.Workspace),
|
||||
},
|
||||
{ children: 'Objects', href: '/settings/objects' },
|
||||
{
|
||||
children: 'Overview',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<ReactFlowProvider>
|
||||
<SettingsDataModelOverview />
|
||||
|
||||
@@ -145,6 +145,15 @@ export const SettingsObjects = () => {
|
||||
/>
|
||||
</UndecoratedLink>
|
||||
}
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPagePath(SettingsPath.Workspace),
|
||||
},
|
||||
{
|
||||
children: 'Objects',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<>
|
||||
|
||||
@@ -30,7 +30,6 @@ export type Story = StoryObj<typeof SettingsObjectNewFieldStep1>;
|
||||
export const Default: Story = {
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
await canvas.findByText('Settings');
|
||||
await canvas.findByText('Objects');
|
||||
await canvas.findByText('Companies');
|
||||
await canvas.findByText('Check deactivated fields');
|
||||
|
||||
@@ -5,6 +5,8 @@ import { SettingsPageContainer } from '@/settings/components/SettingsPageContain
|
||||
import { SettingsApiKeysTable } from '@/settings/developers/components/SettingsApiKeysTable';
|
||||
import { SettingsReadDocumentationButton } from '@/settings/developers/components/SettingsReadDocumentationButton';
|
||||
import { SettingsWebhooksTable } from '@/settings/developers/components/SettingsWebhooksTable';
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
@@ -21,6 +23,13 @@ export const SettingsDevelopers = () => {
|
||||
Icon={IconCode}
|
||||
title="Developers"
|
||||
actionButton={<SettingsReadDocumentationButton />}
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPagePath(SettingsPath.Workspace),
|
||||
},
|
||||
{ children: 'Developers' },
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<Section>
|
||||
|
||||
@@ -25,8 +25,7 @@ export type Story = StoryObj<typeof SettingsDevelopersApiKeysNew>;
|
||||
export const Default: Story = {
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
await canvas.findByText('Settings');
|
||||
await canvas.findByText('New API Key');
|
||||
await canvas.findByText('New Key');
|
||||
await canvas.findByText('Name');
|
||||
await canvas.findByText('Expiration Date');
|
||||
|
||||
|
||||
@@ -17,12 +17,13 @@ import { apiKeyTokenState } from '@/settings/developers/states/generatedApiKeyTo
|
||||
import { ApiKey } from '@/settings/developers/types/api-key/ApiKey';
|
||||
import { computeNewExpirationDate } from '@/settings/developers/utils/compute-new-expiration-date';
|
||||
import { formatExpiration } from '@/settings/developers/utils/format-expiration';
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
import { useGenerateApiKeyTokenMutation } from '~/generated/graphql';
|
||||
|
||||
const StyledInfo = styled.span`
|
||||
@@ -65,6 +66,7 @@ export const SettingsDevelopersApiKeyDetail = () => {
|
||||
setApiKeyName(record.name);
|
||||
},
|
||||
});
|
||||
const developerPath = getSettingsPagePath(SettingsPath.Developers);
|
||||
|
||||
const deleteIntegration = async (redirect = true) => {
|
||||
await updateApiKey?.({
|
||||
@@ -72,7 +74,7 @@ export const SettingsDevelopersApiKeyDetail = () => {
|
||||
updateOneRecordInput: { revokedAt: DateTime.now().toString() },
|
||||
});
|
||||
if (redirect) {
|
||||
navigate('/settings/developers');
|
||||
navigate(developerPath);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -122,14 +124,15 @@ export const SettingsDevelopersApiKeyDetail = () => {
|
||||
{apiKeyData?.name && (
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconCode}
|
||||
title={
|
||||
<Breadcrumb
|
||||
links={[
|
||||
{ children: 'Developers', href: '/settings/developers' },
|
||||
{ children: `${apiKeyName} API Key` },
|
||||
]}
|
||||
/>
|
||||
}
|
||||
title={apiKeyData?.name}
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPagePath(SettingsPath.Workspace),
|
||||
},
|
||||
{ children: 'Developers', href: developerPath },
|
||||
{ children: `${apiKeyName} API Key` },
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<Section>
|
||||
|
||||
@@ -10,11 +10,12 @@ import { SettingsPageContainer } from '@/settings/components/SettingsPageContain
|
||||
import { EXPIRATION_DATES } from '@/settings/developers/constants/ExpirationDates';
|
||||
import { apiKeyTokenState } from '@/settings/developers/states/generatedApiKeyTokenState';
|
||||
import { ApiKey } from '@/settings/developers/types/api-key/ApiKey';
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
import { useGenerateApiKeyTokenMutation } from '~/generated/graphql';
|
||||
@@ -65,14 +66,18 @@ export const SettingsDevelopersApiKeysNew = () => {
|
||||
return (
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconCode}
|
||||
title={
|
||||
<Breadcrumb
|
||||
links={[
|
||||
{ children: 'Developers', href: '/settings/developers' },
|
||||
{ children: 'New API Key' },
|
||||
]}
|
||||
/>
|
||||
}
|
||||
title="New key"
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPagePath(SettingsPath.Workspace),
|
||||
},
|
||||
{
|
||||
children: 'Developers',
|
||||
href: getSettingsPagePath(SettingsPath.Developers),
|
||||
},
|
||||
{ children: 'New Key' },
|
||||
]}
|
||||
actionButton={
|
||||
<SaveAndCancelButtons
|
||||
isSaveDisabled={!canSave}
|
||||
|
||||
@@ -11,6 +11,8 @@ import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { Webhook } from '@/settings/developers/types/webhook/Webhook';
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
import { TextArea } from '@/ui/input/components/TextArea';
|
||||
@@ -18,7 +20,6 @@ import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
|
||||
const StyledFilterRow = styled.div`
|
||||
display: flex;
|
||||
@@ -55,9 +56,11 @@ export const SettingsDevelopersWebhooksDetail = () => {
|
||||
objectNameSingular: CoreObjectNameSingular.Webhook,
|
||||
});
|
||||
|
||||
const developerPath = getSettingsPagePath(SettingsPath.Developers);
|
||||
|
||||
const deleteWebhook = () => {
|
||||
deleteOneWebhook(webhookId);
|
||||
navigate('/settings/developers');
|
||||
navigate(developerPath);
|
||||
};
|
||||
|
||||
const fieldTypeOptions = [
|
||||
@@ -81,7 +84,7 @@ export const SettingsDevelopersWebhooksDetail = () => {
|
||||
description: description,
|
||||
},
|
||||
});
|
||||
navigate('/settings/developers');
|
||||
navigate(developerPath);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -89,19 +92,23 @@ export const SettingsDevelopersWebhooksDetail = () => {
|
||||
{webhookData?.targetUrl && (
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconCode}
|
||||
title={
|
||||
<Breadcrumb
|
||||
links={[
|
||||
{ children: 'Developers', href: '/settings/developers' },
|
||||
{ children: 'Webhook' },
|
||||
]}
|
||||
/>
|
||||
}
|
||||
title={webhookData.targetUrl}
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPagePath(SettingsPath.Workspace),
|
||||
},
|
||||
{
|
||||
children: 'Developers',
|
||||
href: developerPath,
|
||||
},
|
||||
{ children: 'Webhook' },
|
||||
]}
|
||||
actionButton={
|
||||
<SaveAndCancelButtons
|
||||
isSaveDisabled={!isDirty}
|
||||
onCancel={() => {
|
||||
navigate('/settings/developers');
|
||||
navigate(developerPath);
|
||||
}}
|
||||
onSave={handleSave}
|
||||
/>
|
||||
|
||||
@@ -7,10 +7,11 @@ import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { Webhook } from '@/settings/developers/types/webhook/Webhook';
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { TextInput } from '@/ui/input/components/TextInput';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
import { isValidUrl } from '~/utils/url/isValidUrl';
|
||||
|
||||
export const SettingsDevelopersWebhooksNew = () => {
|
||||
@@ -49,19 +50,23 @@ export const SettingsDevelopersWebhooksNew = () => {
|
||||
return (
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconCode}
|
||||
title={
|
||||
<Breadcrumb
|
||||
links={[
|
||||
{ children: 'Developers', href: '/settings/developers' },
|
||||
{ children: 'New webhook' },
|
||||
]}
|
||||
/>
|
||||
}
|
||||
title="New Webhook"
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPagePath(SettingsPath.Workspace),
|
||||
},
|
||||
{
|
||||
children: 'Developers',
|
||||
href: getSettingsPagePath(SettingsPath.Developers),
|
||||
},
|
||||
{ children: 'New Webhook' },
|
||||
]}
|
||||
actionButton={
|
||||
<SaveAndCancelButtons
|
||||
isSaveDisabled={!canSave}
|
||||
onCancel={() => {
|
||||
navigate('/settings/developers');
|
||||
navigate(getSettingsPagePath(SettingsPath.Developers));
|
||||
}}
|
||||
onSave={handleSave}
|
||||
/>
|
||||
|
||||
@@ -13,7 +13,6 @@ import { AppPath } from '@/types/AppPath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
|
||||
export const SettingsIntegrationDatabase = () => {
|
||||
const { databaseKey = '' } = useParams();
|
||||
@@ -44,17 +43,18 @@ export const SettingsIntegrationDatabase = () => {
|
||||
return (
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconSettings}
|
||||
title={
|
||||
<Breadcrumb
|
||||
links={[
|
||||
{
|
||||
children: 'Integrations',
|
||||
href: getSettingsPagePath(SettingsPath.Integrations),
|
||||
},
|
||||
{ children: integration.text },
|
||||
]}
|
||||
/>
|
||||
}
|
||||
title={integration.text}
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPagePath(SettingsPath.Workspace),
|
||||
},
|
||||
{
|
||||
children: 'Integrations',
|
||||
href: getSettingsPagePath(SettingsPath.Integrations),
|
||||
},
|
||||
{ children: integration.text },
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<SettingsIntegrationPreview
|
||||
|
||||
@@ -2,21 +2,26 @@ import { IconSettings } from 'twenty-ui';
|
||||
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { SettingsIntegrationEditDatabaseConnectionContainer } from '@/settings/integrations/database-connection/components/SettingsIntegrationEditDatabaseConnectionContainer';
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
|
||||
export const SettingsIntegrationEditDatabaseConnection = () => {
|
||||
return (
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconSettings}
|
||||
title={
|
||||
<Breadcrumb
|
||||
links={[
|
||||
// TODO
|
||||
{ children: 'Edit connection' },
|
||||
]}
|
||||
/>
|
||||
}
|
||||
title="Edit connection"
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPagePath(SettingsPath.Workspace),
|
||||
},
|
||||
{
|
||||
children: 'Integrations',
|
||||
href: getSettingsPagePath(SettingsPath.Integrations),
|
||||
},
|
||||
{ children: 'Edit connection' },
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<SettingsIntegrationEditDatabaseConnectionContainer />
|
||||
|
||||
@@ -23,7 +23,6 @@ import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/Snac
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
import { CreateRemoteServerInput } from '~/generated-metadata/graphql';
|
||||
|
||||
const createRemoteServerInputPostgresSchema =
|
||||
@@ -133,21 +132,22 @@ export const SettingsIntegrationNewDatabaseConnection = () => {
|
||||
return (
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconSettings}
|
||||
title={
|
||||
<Breadcrumb
|
||||
links={[
|
||||
{
|
||||
children: 'Integrations',
|
||||
href: settingsIntegrationsPagePath,
|
||||
},
|
||||
{
|
||||
children: integration.text,
|
||||
href: `${settingsIntegrationsPagePath}/${databaseKey}`,
|
||||
},
|
||||
{ children: 'New' },
|
||||
]}
|
||||
/>
|
||||
}
|
||||
title="New"
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPagePath(SettingsPath.Workspace),
|
||||
},
|
||||
{
|
||||
children: 'Integrations',
|
||||
href: settingsIntegrationsPagePath,
|
||||
},
|
||||
{
|
||||
children: integration.text,
|
||||
href: `${settingsIntegrationsPagePath}/${databaseKey}`,
|
||||
},
|
||||
{ children: 'New' },
|
||||
]}
|
||||
actionButton={
|
||||
<SaveAndCancelButtons
|
||||
isSaveDisabled={!canSave}
|
||||
|
||||
@@ -2,11 +2,27 @@ import { IconSettings } from 'twenty-ui';
|
||||
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { SettingsIntegrationDatabaseConnectionShowContainer } from '@/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionShowContainer';
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
|
||||
export const SettingsIntegrationShowDatabaseConnection = () => {
|
||||
return (
|
||||
<SubMenuTopBarContainer Icon={IconSettings} title="Settings">
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconSettings}
|
||||
title="Database Connection"
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPagePath(SettingsPath.Workspace),
|
||||
},
|
||||
{
|
||||
children: 'Integrations',
|
||||
href: getSettingsPagePath(SettingsPath.Integrations),
|
||||
},
|
||||
{ children: 'Database Connection' },
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<SettingsIntegrationDatabaseConnectionShowContainer />
|
||||
</SettingsPageContainer>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { SettingsIntegrationGroup } from '@/settings/integrations/components/SettingsIntegrationGroup';
|
||||
import { useSettingsIntegrationCategories } from '@/settings/integrations/hooks/useSettingsIntegrationCategories';
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { IconApps } from 'twenty-ui';
|
||||
|
||||
@@ -8,7 +10,17 @@ export const SettingsIntegrations = () => {
|
||||
const integrationCategories = useSettingsIntegrationCategories();
|
||||
|
||||
return (
|
||||
<SubMenuTopBarContainer Icon={IconApps} title="Integrations">
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconApps}
|
||||
title="Integrations"
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPagePath(SettingsPath.Workspace),
|
||||
},
|
||||
{ children: 'Integrations' },
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
{integrationCategories.map((group) => (
|
||||
<SettingsIntegrationGroup key={group.key} integrationGroup={group} />
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { H2Title, IconColorSwatch } from 'twenty-ui';
|
||||
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { ColorSchemePicker } from '@/ui/input/color-scheme/components/ColorSchemePicker';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
@@ -11,10 +13,20 @@ export const SettingsAppearance = () => {
|
||||
const { colorScheme, setColorScheme } = useColorScheme();
|
||||
|
||||
return (
|
||||
<SubMenuTopBarContainer Icon={IconColorSwatch} title="Appearance">
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconColorSwatch}
|
||||
title="Experience"
|
||||
links={[
|
||||
{
|
||||
children: 'User',
|
||||
href: getSettingsPagePath(SettingsPath.ProfilePage),
|
||||
},
|
||||
{ children: 'Experience' },
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<Section>
|
||||
<H2Title title="Theme" />
|
||||
<H2Title title="Appearance" />
|
||||
<ColorSchemePicker value={colorScheme} onChange={setColorScheme} />
|
||||
</Section>
|
||||
<Section>
|
||||
|
||||
@@ -4,25 +4,26 @@ import { SettingsServerlessFunctionSettingsTab } from '@/settings/serverless-fun
|
||||
import { SettingsServerlessFunctionTestTab } from '@/settings/serverless-functions/components/tabs/SettingsServerlessFunctionTestTab';
|
||||
import { SettingsServerlessFunctionTestTabEffect } from '@/settings/serverless-functions/components/tabs/SettingsServerlessFunctionTestTabEffect';
|
||||
import { useExecuteOneServerlessFunction } from '@/settings/serverless-functions/hooks/useExecuteOneServerlessFunction';
|
||||
import { useGetOneServerlessFunctionSourceCode } from '@/settings/serverless-functions/hooks/useGetOneServerlessFunctionSourceCode';
|
||||
import { usePublishOneServerlessFunction } from '@/settings/serverless-functions/hooks/usePublishOneServerlessFunction';
|
||||
import { useServerlessFunctionUpdateFormState } from '@/settings/serverless-functions/hooks/useServerlessFunctionUpdateFormState';
|
||||
import { useUpdateOneServerlessFunction } from '@/settings/serverless-functions/hooks/useUpdateOneServerlessFunction';
|
||||
import { usePublishOneServerlessFunction } from '@/settings/serverless-functions/hooks/usePublishOneServerlessFunction';
|
||||
import { settingsServerlessFunctionInputState } from '@/settings/serverless-functions/states/settingsServerlessFunctionInputState';
|
||||
import { settingsServerlessFunctionOutputState } from '@/settings/serverless-functions/states/settingsServerlessFunctionOutputState';
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { TabList } from '@/ui/layout/tab/components/TabList';
|
||||
import { useTabList } from '@/ui/layout/tab/hooks/useTabList';
|
||||
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
import { useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { IconCode, IconFunction, IconSettings, IconTestPipe } from 'twenty-ui';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
import { useGetOneServerlessFunctionSourceCode } from '@/settings/serverless-functions/hooks/useGetOneServerlessFunctionSourceCode';
|
||||
import { useState } from 'react';
|
||||
import { usePreventOverlapCallback } from '~/hooks/usePreventOverlapCallback';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
const TAB_LIST_COMPONENT_ID = 'serverless-function-detail';
|
||||
|
||||
@@ -204,14 +205,18 @@ export const SettingsServerlessFunctionDetail = () => {
|
||||
!loading && (
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconFunction}
|
||||
title={
|
||||
<Breadcrumb
|
||||
links={[
|
||||
{ children: 'Functions', href: '/settings/functions' },
|
||||
{ children: `${formValues.name}` },
|
||||
]}
|
||||
/>
|
||||
}
|
||||
title={formValues.name}
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPagePath(SettingsPath.Workspace),
|
||||
},
|
||||
{
|
||||
children: 'Functions',
|
||||
href: getSettingsPagePath(SettingsPath.ServerlessFunctions),
|
||||
},
|
||||
{ children: `${formValues.name}` },
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<Section>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { SettingsHeaderContainer } from '@/settings/components/SettingsHeaderContainer';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { SettingsServerlessFunctionsTable } from '@/settings/serverless-functions/components/SettingsServerlessFunctionsTable';
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
@@ -6,7 +5,6 @@ import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink';
|
||||
import { IconFunction, IconPlus } from 'twenty-ui';
|
||||
|
||||
@@ -27,11 +25,17 @@ export const SettingsServerlessFunctions = () => {
|
||||
/>
|
||||
</UndecoratedLink>
|
||||
}
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPagePath(SettingsPath.Workspace),
|
||||
},
|
||||
{
|
||||
children: 'Functions',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<SettingsHeaderContainer>
|
||||
<Breadcrumb links={[{ children: 'Functions' }]} />
|
||||
</SettingsHeaderContainer>
|
||||
<Section>
|
||||
<SettingsServerlessFunctionsTable />
|
||||
</Section>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer';
|
||||
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { SettingsServerlessFunctionNewForm } from '@/settings/serverless-functions/components/SettingsServerlessFunctionNewForm';
|
||||
@@ -81,14 +80,18 @@ export const SettingsServerlessFunctionsNew = () => {
|
||||
return (
|
||||
<SubMenuTopBarContainer
|
||||
Icon={IconFunction}
|
||||
title={
|
||||
<Breadcrumb
|
||||
links={[
|
||||
{ children: 'Functions', href: '/settings/functions' },
|
||||
{ children: 'New' },
|
||||
]}
|
||||
/>
|
||||
}
|
||||
title="New Function"
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPagePath(SettingsPath.Workspace),
|
||||
},
|
||||
{
|
||||
children: 'Functions',
|
||||
href: getSettingsPagePath(SettingsPath.ServerlessFunctions),
|
||||
},
|
||||
{ children: 'New' },
|
||||
]}
|
||||
actionButton={
|
||||
<SaveAndCancelButtons
|
||||
isSaveDisabled={!canSave}
|
||||
|
||||
Reference in New Issue
Block a user