diff --git a/.eslintrc.react.cjs b/.eslintrc.react.cjs index 0aca87d0e4..77c307222b 100644 --- a/.eslintrc.react.cjs +++ b/.eslintrc.react.cjs @@ -47,6 +47,7 @@ module.exports = { '@nx/workspace-explicit-boolean-predicates-in-if': 'error', '@nx/workspace-use-getLoadable-and-getValue-to-get-atoms': 'error', '@nx/workspace-useRecoilCallback-has-dependency-array': 'error', + '@nx/workspace-no-navigate-prefer-link': 'error', 'react/no-unescaped-entities': 'off', 'react/prop-types': 'off', 'react/jsx-key': 'off', diff --git a/packages/twenty-front/src/modules/object-record/record-index/options/components/RecordIndexOptionsDropdownContent.tsx b/packages/twenty-front/src/modules/object-record/record-index/options/components/RecordIndexOptionsDropdownContent.tsx index 2a3e3d67e2..73149fb6eb 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/options/components/RecordIndexOptionsDropdownContent.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/options/components/RecordIndexOptionsDropdownContent.tsx @@ -1,5 +1,4 @@ import { useState } from 'react'; -import { useNavigate } from 'react-router-dom'; import { Key } from 'ts-key-enum'; import { IconBaselineDensitySmall, @@ -27,6 +26,7 @@ import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenu import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; +import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink'; import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { MenuItemNavigate } from '@/ui/navigation/menu-item/components/MenuItemNavigate'; import { MenuItemToggle } from '@/ui/navigation/menu-item/components/MenuItemToggle'; @@ -50,8 +50,6 @@ export const RecordIndexOptionsDropdownContent = ({ }: RecordIndexOptionsDropdownContentProps) => { const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView(); - const navigate = useNavigate(); - const { closeDropdown } = useDropdown(RECORD_INDEX_OPTIONS_DROPDOWN_ID); const [currentMenu, setCurrentMenu] = useState< @@ -68,13 +66,9 @@ export const RecordIndexOptionsDropdownContent = ({ objectNameSingular: objectNameSingular, }); - const handleEditClick = () => { - navigate( - getSettingsPagePath(SettingsPath.ObjectDetail, { - objectSlug: objectNamePlural, - }), - ); - }; + const settingsUrl = getSettingsPagePath(SettingsPath.ObjectDetail, { + objectSlug: objectNamePlural, + }); useScopedHotkeys( [Key.Escape], @@ -194,13 +188,12 @@ export const RecordIndexOptionsDropdownContent = ({ )} - - - + + + + + + )} diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsSettingsSection.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsSettingsSection.tsx index 659e0445e7..2b43da472f 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsSettingsSection.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsSettingsSection.tsx @@ -1,4 +1,4 @@ -import { useNavigate } from 'react-router-dom'; +import { Link } from 'react-router-dom'; import styled from '@emotion/styled'; import { H2Title, IconCalendarEvent, IconMailCog } from 'twenty-ui'; @@ -6,6 +6,7 @@ import { SettingsNavigationCard } from '@/settings/components/SettingsNavigation import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { SettingsPath } from '@/types/SettingsPath'; import { Section } from '@/ui/layout/section/components/Section'; +import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink'; const StyledCardsContainer = styled.div` display: flex; @@ -14,8 +15,6 @@ const StyledCardsContainer = styled.div` `; export const SettingsAccountsSettingsSection = () => { - const navigate = useNavigate(); - return (
{ description="Configure your emails and calendar settings." /> - - navigate(getSettingsPagePath(SettingsPath.AccountsEmails)) - } - > - Set email visibility, manage your blocklist and more. - - - navigate(getSettingsPagePath(SettingsPath.AccountsCalendars)) - } - > - Configure and customize your calendar preferences. - + + + Set email visibility, manage your blocklist and more. + + + + + Configure and customize your calendar preferences. + +
); diff --git a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionShowContainer.tsx b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionShowContainer.tsx index b4317ee930..03b733e4a9 100644 --- a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionShowContainer.tsx +++ b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionShowContainer.tsx @@ -27,10 +27,6 @@ export const SettingsIntegrationDatabaseConnectionShowContainer = () => { navigate(`${settingsIntegrationsPagePath}/${databaseKey}`); }; - const onEdit = () => { - navigate('./edit'); - }; - const settingsIntegrationsPagePath = getSettingsPagePath( SettingsPath.Integrations, ); @@ -57,7 +53,6 @@ export const SettingsIntegrationDatabaseConnectionShowContainer = () => { connectionId={connection.id} connectionLabel={connection.label} onRemove={deleteConnection} - onEdit={onEdit} />
diff --git a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionSummaryCard.tsx b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionSummaryCard.tsx index 287f6f892b..a29353d223 100644 --- a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionSummaryCard.tsx +++ b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionSummaryCard.tsx @@ -7,6 +7,7 @@ import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; +import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink'; import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; type SettingsIntegrationDatabaseConnectionSummaryCardProps = { @@ -14,7 +15,6 @@ type SettingsIntegrationDatabaseConnectionSummaryCardProps = { connectionId: string; connectionLabel: string; onRemove: () => void; - onEdit: () => void; }; const StyledDatabaseLogoContainer = styled.div` @@ -34,7 +34,6 @@ export const SettingsIntegrationDatabaseConnectionSummaryCard = ({ connectionId, connectionLabel, onRemove, - onEdit, }: SettingsIntegrationDatabaseConnectionSummaryCardProps) => { const dropdownId = 'settings-integration-database-connection-summary-card-dropdown'; @@ -69,11 +68,9 @@ export const SettingsIntegrationDatabaseConnectionSummaryCard = ({ text="Remove" onClick={onRemove} /> - + + + } diff --git a/packages/twenty-front/src/modules/ui/layout/page/PageHeader.tsx b/packages/twenty-front/src/modules/ui/layout/page/PageHeader.tsx index 7cbc7e4a15..ae92a17831 100644 --- a/packages/twenty-front/src/modules/ui/layout/page/PageHeader.tsx +++ b/packages/twenty-front/src/modules/ui/layout/page/PageHeader.tsx @@ -1,6 +1,5 @@ import { ComponentProps, ReactNode } from 'react'; import Skeleton, { SkeletonTheme } from 'react-loading-skeleton'; -import { useNavigate } from 'react-router-dom'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { useRecoilValue } from 'recoil'; @@ -12,6 +11,7 @@ import { } from 'twenty-ui'; import { IconButton } from '@/ui/input/button/components/IconButton'; +import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink'; import { NavigationDrawerCollapseButton } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerCollapseButton'; import { isNavigationDrawerOpenState } from '@/ui/navigation/states/isNavigationDrawerOpenState'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; @@ -103,7 +103,6 @@ export const PageHeader = ({ loading, }: PageHeaderProps) => { const isMobile = useIsMobile(); - const navigate = useNavigate(); const theme = useTheme(); const isNavigationDrawerOpen = useRecoilValue(isNavigationDrawerOpenState); @@ -116,12 +115,13 @@ export const PageHeader = ({ )} {hasBackButton && ( - navigate(-1)} - variant="tertiary" - /> + + + )} {loading ? ( diff --git a/packages/twenty-front/src/modules/ui/navigation/link/components/UndecoratedLink.tsx b/packages/twenty-front/src/modules/ui/navigation/link/components/UndecoratedLink.tsx new file mode 100644 index 0000000000..999de453ec --- /dev/null +++ b/packages/twenty-front/src/modules/ui/navigation/link/components/UndecoratedLink.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import styled from '@emotion/styled'; + +const StyledUndecoratedLink = styled(Link)` + text-decoration: none; +`; + +type UndecoratedLinkProps = { + to: string | number; + children: React.ReactNode; + replace?: boolean; +}; + +export const UndecoratedLink = ({ + children, + to, + replace = false, +}: UndecoratedLinkProps) => { + return ( + + {children} + + ); +}; diff --git a/packages/twenty-front/src/modules/ui/navigation/link/components/__stories__/UndecoratedLink.stories.tsx b/packages/twenty-front/src/modules/ui/navigation/link/components/__stories__/UndecoratedLink.stories.tsx new file mode 100644 index 0000000000..1765a9f9e3 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/navigation/link/components/__stories__/UndecoratedLink.stories.tsx @@ -0,0 +1,32 @@ +import { expect } from '@storybook/jest'; +import { Meta, StoryObj } from '@storybook/react'; +import { userEvent, within } from '@storybook/test'; + +import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink'; +import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator'; + +const meta: Meta = { + title: 'UI/navigation/link/UndecoratedLink', + component: UndecoratedLink, + decorators: [ComponentWithRouterDecorator], +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + children: 'Go Home', + to: '/home', + }, + play: async ({ canvasElement }) => { + const canvas = within(canvasElement); + + const link = canvas.getByText('Go Home'); + + await userEvent.click(link); + + const href = link.getAttribute('href'); + expect(href).toBe('/home'); + }, +}; diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerBackButton.tsx b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerBackButton.tsx index 3c775d230e..ae4d2144dd 100644 --- a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerBackButton.tsx +++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerBackButton.tsx @@ -1,9 +1,9 @@ -import { useNavigate } from 'react-router-dom'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { useRecoilValue } from 'recoil'; import { IconChevronLeft } from 'twenty-ui'; +import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink'; import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState'; type NavigationDrawerBackButtonProps = { @@ -36,22 +36,19 @@ export const NavigationDrawerBackButton = ({ title, }: NavigationDrawerBackButtonProps) => { const theme = useTheme(); - const navigate = useNavigate(); const navigationMemorizedUrl = useRecoilValue(navigationMemorizedUrlState); return ( - { - navigate(navigationMemorizedUrl, { replace: true }); - }} - > - - {title} - + + + + {title} + + ); }; diff --git a/packages/twenty-front/src/pages/auth/Authorize.tsx b/packages/twenty-front/src/pages/auth/Authorize.tsx index d8b93d840f..611e265d45 100644 --- a/packages/twenty-front/src/pages/auth/Authorize.tsx +++ b/packages/twenty-front/src/pages/auth/Authorize.tsx @@ -4,6 +4,7 @@ import styled from '@emotion/styled'; import { AppPath } from '@/types/AppPath'; import { MainButton } from '@/ui/input/button/components/MainButton'; +import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink'; import { useAuthorizeAppMutation } from '~/generated/graphql'; import { isDefined } from '~/utils/isDefined'; @@ -115,12 +116,9 @@ export const Authorize = () => { {app?.name} wants to access your account - navigate(AppPath.Index)} - fullWidth - /> + + + diff --git a/packages/twenty-front/src/pages/auth/PaymentSuccess.tsx b/packages/twenty-front/src/pages/auth/PaymentSuccess.tsx index 028166bd3f..f77d7360e6 100644 --- a/packages/twenty-front/src/pages/auth/PaymentSuccess.tsx +++ b/packages/twenty-front/src/pages/auth/PaymentSuccess.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import { useNavigate } from 'react-router-dom'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { IconCheck, RGBA } from 'twenty-ui'; @@ -8,6 +7,7 @@ import { SubTitle } from '@/auth/components/SubTitle'; import { Title } from '@/auth/components/Title'; import { AppPath } from '@/types/AppPath'; import { MainButton } from '@/ui/input/button/components/MainButton'; +import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink'; import { AnimatedEaseIn } from '@/ui/utilities/animation/components/AnimatedEaseIn'; const StyledCheckContainer = styled.div` @@ -28,11 +28,7 @@ const StyledButtonContainer = styled.div` `; export const PaymentSuccess = () => { - const navigate = useNavigate(); const theme = useTheme(); - const handleButtonClick = () => { - navigate(AppPath.CreateWorkspace); - }; const color = theme.name === 'light' ? theme.grayScale.gray90 : theme.grayScale.gray10; return ( @@ -45,7 +41,9 @@ export const PaymentSuccess = () => { All set! Your account has been activated. - + + + ); diff --git a/packages/twenty-front/src/pages/not-found/NotFound.tsx b/packages/twenty-front/src/pages/not-found/NotFound.tsx index d24a91ac3a..bf3acc27ae 100644 --- a/packages/twenty-front/src/pages/not-found/NotFound.tsx +++ b/packages/twenty-front/src/pages/not-found/NotFound.tsx @@ -1,4 +1,3 @@ -import { useNavigate } from 'react-router-dom'; import styled from '@emotion/styled'; import { SignInBackgroundMockPage } from '@/sign-in-background-mock/components/SignInBackgroundMockPage'; @@ -11,6 +10,7 @@ import { AnimatedPlaceholderErrorSubTitle, AnimatedPlaceholderErrorTitle, } from '@/ui/layout/animated-placeholder/components/ErrorPlaceholderStyled'; +import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink'; import { PageTitle } from '@/ui/utilities/page-title/PageTitle'; const StyledBackDrop = styled.div` @@ -33,8 +33,6 @@ const StyledButtonContainer = styled.div` `; export const NotFound = () => { - const navigate = useNavigate(); - return ( <> @@ -51,11 +49,9 @@ export const NotFound = () => { - navigate(AppPath.Index)} - /> + + + diff --git a/packages/twenty-front/src/pages/settings/SettingsBilling.tsx b/packages/twenty-front/src/pages/settings/SettingsBilling.tsx index a0fdad919b..77763f68bc 100644 --- a/packages/twenty-front/src/pages/settings/SettingsBilling.tsx +++ b/packages/twenty-front/src/pages/settings/SettingsBilling.tsx @@ -1,5 +1,4 @@ import React, { useState } from 'react'; -import { useNavigate } from 'react-router-dom'; import styled from '@emotion/styled'; import { useRecoilValue, useSetRecoilState } from 'recoil'; import { @@ -25,6 +24,7 @@ import { Button } from '@/ui/input/button/components/Button'; import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal'; import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer'; import { Section } from '@/ui/layout/section/components/Section'; +import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink'; import { useBillingPortalSessionQuery, useUpdateBillingSubscriptionMutation, @@ -66,7 +66,6 @@ const SWITCH_INFOS = { }; export const SettingsBilling = () => { - const navigate = useNavigate(); const { enqueueSnackBar } = useSnackBar(); const onboardingStatus = useOnboardingStatus(); const currentWorkspace = useRecoilValue(currentWorkspaceState); @@ -140,10 +139,6 @@ export const SettingsBilling = () => { } }; - const redirectToSubscribePage = () => { - navigate(AppPath.PlanRequired); - }; - return ( @@ -158,20 +153,22 @@ export const SettingsBilling = () => { /> )} {displaySubscriptionCanceledInfo && ( - + + + )} {displaySubscribeInfo ? ( - + + + ) : ( <>
diff --git a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectDetail.tsx b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectDetail.tsx index 999334e0ae..49b1dbd203 100644 --- a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectDetail.tsx +++ b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectDetail.tsx @@ -31,6 +31,7 @@ import { Table } from '@/ui/layout/table/components/Table'; import { TableHeader } from '@/ui/layout/table/components/TableHeader'; import { TableSection } from '@/ui/layout/table/components/TableSection'; import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb'; +import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink'; const StyledDiv = styled.div` display: flex; @@ -214,19 +215,20 @@ export const SettingsObjectDetail = () => { {shouldDisplayAddFieldButton && ( -
diff --git a/packages/twenty-front/src/pages/settings/data-model/SettingsObjects.tsx b/packages/twenty-front/src/pages/settings/data-model/SettingsObjects.tsx index 142178020c..b3549ff54d 100644 --- a/packages/twenty-front/src/pages/settings/data-model/SettingsObjects.tsx +++ b/packages/twenty-front/src/pages/settings/data-model/SettingsObjects.tsx @@ -1,4 +1,3 @@ -import { useNavigate } from 'react-router-dom'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { @@ -29,6 +28,7 @@ import { Section } from '@/ui/layout/section/components/Section'; import { Table } from '@/ui/layout/table/components/Table'; import { TableHeader } from '@/ui/layout/table/components/TableHeader'; import { TableSection } from '@/ui/layout/table/components/TableSection'; +import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink'; const StyledIconChevronRight = styled(IconChevronRight)` color: ${({ theme }) => theme.font.color.tertiary}; @@ -40,7 +40,6 @@ const StyledH1Title = styled(H1Title)` export const SettingsObjects = () => { const theme = useTheme(); - const navigate = useNavigate(); const { activeObjectMetadataItems, inactiveObjectMetadataItems } = useFilteredObjectMetadataItems(); @@ -52,15 +51,14 @@ export const SettingsObjects = () => { - ', + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, + { + code: '