mirror of
				https://github.com/lingble/twenty.git
				synced 2025-10-30 20:27:55 +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:
		![57568882+gitstart-app[bot]@users.noreply.github.com](/assets/img/avatar_default.png) gitstart-app[bot]
					gitstart-app[bot]
				
			
				
					committed by
					
						 GitHub
						GitHub
					
				
			
			
				
	
			
			
			 GitHub
						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