mirror of
https://github.com/lingble/twenty.git
synced 2025-11-02 21:57:56 +00:00
Changed record chip functionality from onClick to anchor tag (#5462)
[#4422](https://github.com/twentyhq/twenty/issues/4422) Demo: https://github.com/twentyhq/twenty/assets/155670906/f8027ab2-c579-45f7-9f08-f4441a346ae7 Within the demo, we show the various areas in which the Command/CTRL + Click functionality works. The table cells within the People and Companies tab open within both the current tab and new tab due to unchanged functionality within RecordTableCell. We did this to ensure we could get a PR within by the end of the week. In this commit, we ONLY edited EntityChip.tsx. We did this by: - Removing useNavigate() and handleLinkClick/onClick functionality - Wrapping InnerEntityChip in an anchor tag This allowed for Command/CTRL + Click functionality to work. Clickable left cells on tables, left side menu, and data model navigation files/areas DID NOT get updated. --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { useLocation, useNavigate } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||||
import { IconCheckbox, IconSearch, IconSettings } from 'twenty-ui';
|
import { IconCheckbox, IconSearch, IconSettings } from 'twenty-ui';
|
||||||
|
|
||||||
@@ -20,7 +20,6 @@ export const MainNavigationDrawerItems = () => {
|
|||||||
const { toggleCommandMenu } = useCommandMenu();
|
const { toggleCommandMenu } = useCommandMenu();
|
||||||
const isTasksPage = useIsTasksPage();
|
const isTasksPage = useIsTasksPage();
|
||||||
const currentUserDueTaskCount = useRecoilValue(currentUserDueTaskCountState);
|
const currentUserDueTaskCount = useRecoilValue(currentUserDueTaskCountState);
|
||||||
const navigate = useNavigate();
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const setNavigationMemorizedUrl = useSetRecoilState(
|
const setNavigationMemorizedUrl = useSetRecoilState(
|
||||||
navigationMemorizedUrlState,
|
navigationMemorizedUrlState,
|
||||||
@@ -38,9 +37,9 @@ export const MainNavigationDrawerItems = () => {
|
|||||||
/>
|
/>
|
||||||
<NavigationDrawerItem
|
<NavigationDrawerItem
|
||||||
label="Settings"
|
label="Settings"
|
||||||
|
to={'/settings/profile'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setNavigationMemorizedUrl(location.pathname + location.search);
|
setNavigationMemorizedUrl(location.pathname + location.search);
|
||||||
navigate('/settings/profile');
|
|
||||||
}}
|
}}
|
||||||
Icon={IconSettings}
|
Icon={IconSettings}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useLocation, useNavigate } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { useIcons } from 'twenty-ui';
|
import { useIcons } from 'twenty-ui';
|
||||||
|
|
||||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
|
||||||
@@ -10,7 +10,6 @@ import { getObjectMetadataItemViews } from '@/views/utils/getObjectMetadataItemV
|
|||||||
|
|
||||||
export const ObjectMetadataNavItems = () => {
|
export const ObjectMetadataNavItems = () => {
|
||||||
const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
|
const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
|
||||||
const navigate = useNavigate();
|
|
||||||
const { getIcon } = useIcons();
|
const { getIcon } = useIcons();
|
||||||
const currentPath = useLocation().pathname;
|
const currentPath = useLocation().pathname;
|
||||||
|
|
||||||
@@ -63,9 +62,6 @@ export const ObjectMetadataNavItems = () => {
|
|||||||
to={navigationPath}
|
to={navigationPath}
|
||||||
active={currentPath === `/objects/${objectMetadataItem.namePlural}`}
|
active={currentPath === `/objects/${objectMetadataItem.namePlural}`}
|
||||||
Icon={getIcon(objectMetadataItem.icon)}
|
Icon={getIcon(objectMetadataItem.icon)}
|
||||||
onClick={() => {
|
|
||||||
navigate(navigationPath);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Link } from 'react-router-dom';
|
||||||
import { css, useTheme } from '@emotion/react';
|
import { css, useTheme } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { IconComponent, IconTwentyStar } from 'twenty-ui';
|
import { IconComponent, IconTwentyStar } from 'twenty-ui';
|
||||||
@@ -7,13 +8,16 @@ import { getSettingsFieldTypeConfig } from '@/settings/data-model/utils/getSetti
|
|||||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||||
|
|
||||||
type SettingsObjectFieldDataTypeProps = {
|
type SettingsObjectFieldDataTypeProps = {
|
||||||
onClick?: () => void;
|
to?: string;
|
||||||
Icon?: IconComponent;
|
Icon?: IconComponent;
|
||||||
label?: string;
|
label?: string;
|
||||||
value: SettingsSupportedFieldType;
|
value: SettingsSupportedFieldType;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledDataType = styled.div<{ value: SettingsSupportedFieldType }>`
|
const StyledDataType = styled.div<{
|
||||||
|
value: SettingsSupportedFieldType;
|
||||||
|
to?: string;
|
||||||
|
}>`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||||
@@ -23,9 +27,10 @@ const StyledDataType = styled.div<{ value: SettingsSupportedFieldType }>`
|
|||||||
height: 20px;
|
height: 20px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 0 ${({ theme }) => theme.spacing(2)};
|
padding: 0 ${({ theme }) => theme.spacing(2)};
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
${({ onClick }) =>
|
${({ to }) =>
|
||||||
onClick
|
to
|
||||||
? css`
|
? css`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
`
|
`
|
||||||
@@ -47,7 +52,7 @@ const StyledLabelContainer = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const SettingsObjectFieldDataType = ({
|
export const SettingsObjectFieldDataType = ({
|
||||||
onClick,
|
to,
|
||||||
value,
|
value,
|
||||||
Icon: IconFromProps,
|
Icon: IconFromProps,
|
||||||
label: labelFromProps,
|
label: labelFromProps,
|
||||||
@@ -64,7 +69,7 @@ export const SettingsObjectFieldDataType = ({
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledDataType onClick={onClick} value={value}>
|
<StyledDataType as={to ? Link : 'div'} to={to} value={value}>
|
||||||
<StyledIcon size={theme.icon.size.sm} />
|
<StyledIcon size={theme.icon.size.sm} />
|
||||||
<StyledLabelContainer>{label}</StyledLabelContainer>
|
<StyledLabelContainer>{label}</StyledLabelContainer>
|
||||||
</StyledDataType>
|
</StyledDataType>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { ReactNode, useMemo } from 'react';
|
import { ReactNode, useMemo } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { Nullable, useIcons } from 'twenty-ui';
|
import { Nullable, useIcons } from 'twenty-ui';
|
||||||
@@ -22,6 +21,7 @@ type SettingsObjectFieldItemTableRowProps = {
|
|||||||
identifierType?: Nullable<FieldIdentifierType>;
|
identifierType?: Nullable<FieldIdentifierType>;
|
||||||
variant?: 'field-type' | 'identifier';
|
variant?: 'field-type' | 'identifier';
|
||||||
isRemoteObjectField?: boolean;
|
isRemoteObjectField?: boolean;
|
||||||
|
to?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const StyledObjectFieldTableRow = styled(TableRow)`
|
export const StyledObjectFieldTableRow = styled(TableRow)`
|
||||||
@@ -44,11 +44,11 @@ export const SettingsObjectFieldItemTableRow = ({
|
|||||||
identifierType,
|
identifierType,
|
||||||
variant = 'field-type',
|
variant = 'field-type',
|
||||||
isRemoteObjectField,
|
isRemoteObjectField,
|
||||||
|
to,
|
||||||
}: SettingsObjectFieldItemTableRowProps) => {
|
}: SettingsObjectFieldItemTableRowProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { getIcon } = useIcons();
|
const { getIcon } = useIcons();
|
||||||
const Icon = getIcon(fieldMetadataItem.icon);
|
const Icon = getIcon(fieldMetadataItem.icon);
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const getRelationMetadata = useGetRelationMetadata();
|
const getRelationMetadata = useGetRelationMetadata();
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ export const SettingsObjectFieldItemTableRow = ({
|
|||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledObjectFieldTableRow>
|
<StyledObjectFieldTableRow to={to}>
|
||||||
<StyledNameTableCell>
|
<StyledNameTableCell>
|
||||||
{!!Icon && (
|
{!!Icon && (
|
||||||
<Icon size={theme.icon.size.md} stroke={theme.icon.stroke.sm} />
|
<Icon size={theme.icon.size.md} stroke={theme.icon.stroke.sm} />
|
||||||
@@ -90,15 +90,10 @@ export const SettingsObjectFieldItemTableRow = ({
|
|||||||
<SettingsObjectFieldDataType
|
<SettingsObjectFieldDataType
|
||||||
Icon={RelationIcon}
|
Icon={RelationIcon}
|
||||||
label={relationObjectMetadataItem?.labelPlural}
|
label={relationObjectMetadataItem?.labelPlural}
|
||||||
onClick={
|
to={
|
||||||
relationObjectMetadataItem?.namePlural &&
|
relationObjectMetadataItem?.namePlural &&
|
||||||
!relationObjectMetadataItem.isSystem
|
!relationObjectMetadataItem.isSystem
|
||||||
? () =>
|
? `/settings/objects/${getObjectSlug(relationObjectMetadataItem)}`
|
||||||
navigate(
|
|
||||||
`/settings/objects/${getObjectSlug(
|
|
||||||
relationObjectMetadataItem,
|
|
||||||
)}`,
|
|
||||||
)
|
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
value={fieldType}
|
value={fieldType}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { TableRow } from '@/ui/layout/table/components/TableRow';
|
|||||||
type SettingsObjectItemTableRowProps = {
|
type SettingsObjectItemTableRowProps = {
|
||||||
action: ReactNode;
|
action: ReactNode;
|
||||||
objectItem: ObjectMetadataItem;
|
objectItem: ObjectMetadataItem;
|
||||||
onClick?: () => void;
|
to?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const StyledObjectTableRow = styled(TableRow)`
|
export const StyledObjectTableRow = styled(TableRow)`
|
||||||
@@ -33,7 +33,7 @@ const StyledActionTableCell = styled(TableCell)`
|
|||||||
export const SettingsObjectItemTableRow = ({
|
export const SettingsObjectItemTableRow = ({
|
||||||
action,
|
action,
|
||||||
objectItem,
|
objectItem,
|
||||||
onClick,
|
to,
|
||||||
}: SettingsObjectItemTableRowProps) => {
|
}: SettingsObjectItemTableRowProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ export const SettingsObjectItemTableRow = ({
|
|||||||
const objectTypeLabel = getObjectTypeLabel(objectItem);
|
const objectTypeLabel = getObjectTypeLabel(objectItem);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledObjectTableRow key={objectItem.namePlural} onClick={onClick}>
|
<StyledObjectTableRow key={objectItem.namePlural} to={to}>
|
||||||
<StyledNameTableCell>
|
<StyledNameTableCell>
|
||||||
{!!Icon && (
|
{!!Icon && (
|
||||||
<Icon size={theme.icon.size.md} stroke={theme.icon.stroke.sm} />
|
<Icon size={theme.icon.size.md} stroke={theme.icon.stroke.sm} />
|
||||||
|
|||||||
@@ -26,15 +26,15 @@ const StyledIconChevronRight = styled(IconChevronRight)`
|
|||||||
|
|
||||||
export const SettingsApiKeysFieldItemTableRow = ({
|
export const SettingsApiKeysFieldItemTableRow = ({
|
||||||
fieldItem,
|
fieldItem,
|
||||||
onClick,
|
to,
|
||||||
}: {
|
}: {
|
||||||
fieldItem: ApiFieldItem;
|
fieldItem: ApiFieldItem;
|
||||||
onClick: () => void;
|
to: string;
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledApisFieldTableRow onClick={() => onClick()}>
|
<StyledApisFieldTableRow to={to}>
|
||||||
<StyledNameTableCell>{fieldItem.name}</StyledNameTableCell>
|
<StyledNameTableCell>{fieldItem.name}</StyledNameTableCell>
|
||||||
<TableCell
|
<TableCell
|
||||||
color={
|
color={
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
@@ -21,8 +20,6 @@ const StyledTableRow = styled(TableRow)`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const SettingsApiKeysTable = () => {
|
export const SettingsApiKeysTable = () => {
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const { records: apiKeys } = useFindManyRecords<ApiKey>({
|
const { records: apiKeys } = useFindManyRecords<ApiKey>({
|
||||||
objectNameSingular: CoreObjectNameSingular.ApiKey,
|
objectNameSingular: CoreObjectNameSingular.ApiKey,
|
||||||
filter: { revokedAt: { is: 'NULL' } },
|
filter: { revokedAt: { is: 'NULL' } },
|
||||||
@@ -41,9 +38,7 @@ export const SettingsApiKeysTable = () => {
|
|||||||
<SettingsApiKeysFieldItemTableRow
|
<SettingsApiKeysFieldItemTableRow
|
||||||
key={fieldItem.id}
|
key={fieldItem.id}
|
||||||
fieldItem={fieldItem as ApiFieldItem}
|
fieldItem={fieldItem as ApiFieldItem}
|
||||||
onClick={() => {
|
to={`/settings/developers/api-keys/${fieldItem.id}`}
|
||||||
navigate(`/settings/developers/api-keys/${fieldItem.id}`);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</StyledTableBody>
|
</StyledTableBody>
|
||||||
|
|||||||
@@ -28,15 +28,15 @@ const StyledIconChevronRight = styled(IconChevronRight)`
|
|||||||
|
|
||||||
export const SettingsDevelopersWebhookTableRow = ({
|
export const SettingsDevelopersWebhookTableRow = ({
|
||||||
fieldItem,
|
fieldItem,
|
||||||
onClick,
|
to,
|
||||||
}: {
|
}: {
|
||||||
fieldItem: Webhook;
|
fieldItem: Webhook;
|
||||||
onClick: () => void;
|
to: string;
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledApisFieldTableRow onClick={onClick}>
|
<StyledApisFieldTableRow to={to}>
|
||||||
<StyledUrlTableCell>{fieldItem.targetUrl}</StyledUrlTableCell>
|
<StyledUrlTableCell>{fieldItem.targetUrl}</StyledUrlTableCell>
|
||||||
<StyledIconTableCell>
|
<StyledIconTableCell>
|
||||||
<StyledIconChevronRight
|
<StyledIconChevronRight
|
||||||
|
|||||||
@@ -10,9 +10,8 @@ export const SettingsReadDocumentationButton = () => {
|
|||||||
accent="default"
|
accent="default"
|
||||||
size="small"
|
size="small"
|
||||||
Icon={IconBook2}
|
Icon={IconBook2}
|
||||||
onClick={() => {
|
to={'https://docs.twenty.com'}
|
||||||
window.open('https://docs.twenty.com');
|
target="_blank"
|
||||||
}}
|
|
||||||
></Button>
|
></Button>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||||
@@ -19,8 +18,6 @@ const StyledTableRow = styled(TableRow)`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const SettingsWebhooksTable = () => {
|
export const SettingsWebhooksTable = () => {
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const { records: webhooks } = useFindManyRecords<Webhook>({
|
const { records: webhooks } = useFindManyRecords<Webhook>({
|
||||||
objectNameSingular: CoreObjectNameSingular.Webhook,
|
objectNameSingular: CoreObjectNameSingular.Webhook,
|
||||||
});
|
});
|
||||||
@@ -37,11 +34,7 @@ export const SettingsWebhooksTable = () => {
|
|||||||
<SettingsDevelopersWebhookTableRow
|
<SettingsDevelopersWebhookTableRow
|
||||||
key={webhookFieldItem.id}
|
key={webhookFieldItem.id}
|
||||||
fieldItem={webhookFieldItem}
|
fieldItem={webhookFieldItem}
|
||||||
onClick={() => {
|
to={`/settings/developers/webhooks/${webhookFieldItem.id}`}
|
||||||
navigate(
|
|
||||||
`/settings/developers/webhooks/${webhookFieldItem.id}`,
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</StyledTableBody>
|
</StyledTableBody>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { css } from '@emotion/react';
|
import { css } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { IconArrowUpRight, IconBolt, IconPlus, Pill } from 'twenty-ui';
|
import { IconArrowUpRight, IconBolt, IconPlus, Pill } from 'twenty-ui';
|
||||||
@@ -12,7 +12,7 @@ interface SettingsIntegrationComponentProps {
|
|||||||
integration: SettingsIntegration;
|
integration: SettingsIntegration;
|
||||||
}
|
}
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div<{ to?: string }>`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: ${({ theme }) => theme.background.secondary};
|
background: ${({ theme }) => theme.background.secondary};
|
||||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||||
@@ -22,9 +22,11 @@ const StyledContainer = styled.div`
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: ${({ theme }) => theme.spacing(3)};
|
padding: ${({ theme }) => theme.spacing(3)};
|
||||||
|
text-decoration: none;
|
||||||
|
color: ${({ theme }) => theme.font.color.primary};
|
||||||
|
|
||||||
${({ onClick }) =>
|
${({ to }) =>
|
||||||
isDefined(onClick) &&
|
isDefined(to) &&
|
||||||
css`
|
css`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
`}
|
`}
|
||||||
@@ -57,16 +59,10 @@ const StyledLogo = styled.img`
|
|||||||
export const SettingsIntegrationComponent = ({
|
export const SettingsIntegrationComponent = ({
|
||||||
integration,
|
integration,
|
||||||
}: SettingsIntegrationComponentProps) => {
|
}: SettingsIntegrationComponentProps) => {
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const navigateToIntegrationPage = () => navigate(integration.link);
|
|
||||||
const openExternalLink = () => window.open(integration.link);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer
|
<StyledContainer
|
||||||
onClick={
|
to={integration.type === 'Active' ? integration.link : undefined}
|
||||||
integration.type === 'Active' ? navigateToIntegrationPage : undefined
|
as={integration.type === 'Active' ? Link : 'div'}
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<StyledSection>
|
<StyledSection>
|
||||||
<StyledIntegrationLogo>
|
<StyledIntegrationLogo>
|
||||||
@@ -86,21 +82,23 @@ export const SettingsIntegrationComponent = ({
|
|||||||
<Status color="green" text="Active" />
|
<Status color="green" text="Active" />
|
||||||
) : integration.type === 'Add' ? (
|
) : integration.type === 'Add' ? (
|
||||||
<Button
|
<Button
|
||||||
onClick={navigateToIntegrationPage}
|
to={integration.link}
|
||||||
Icon={IconPlus}
|
Icon={IconPlus}
|
||||||
title="Add"
|
title="Add"
|
||||||
size="small"
|
size="small"
|
||||||
/>
|
/>
|
||||||
) : integration.type === 'Use' ? (
|
) : integration.type === 'Use' ? (
|
||||||
<Button
|
<Button
|
||||||
onClick={openExternalLink}
|
to={integration.link}
|
||||||
|
target="_blank"
|
||||||
Icon={IconBolt}
|
Icon={IconBolt}
|
||||||
title="Use"
|
title="Use"
|
||||||
size="small"
|
size="small"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
onClick={openExternalLink}
|
to={integration.link}
|
||||||
|
target="_blank"
|
||||||
Icon={IconArrowUpRight}
|
Icon={IconArrowUpRight}
|
||||||
title={integration.linkText}
|
title={integration.linkText}
|
||||||
size="small"
|
size="small"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Link } from 'react-router-dom';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { SettingsIntegrationComponent } from '@/settings/integrations/components/SettingsIntegrationComponent';
|
import { SettingsIntegrationComponent } from '@/settings/integrations/components/SettingsIntegrationComponent';
|
||||||
@@ -16,13 +17,15 @@ const StyledIntegrationGroupHeader = styled.div`
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledGroupLink = styled.div`
|
const StyledGroupLink = styled(Link)`
|
||||||
align-items: start;
|
align-items: start;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
font-size: ${({ theme }) => theme.font.size.md};
|
font-size: ${({ theme }) => theme.font.size.md};
|
||||||
gap: ${({ theme }) => theme.spacing(1)};
|
gap: ${({ theme }) => theme.spacing(1)};
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
color: ${({ theme }) => theme.font.color.primary};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledIntegrationsSection = styled.div`
|
const StyledIntegrationsSection = styled.div`
|
||||||
@@ -39,7 +42,8 @@ export const SettingsIntegrationGroup = ({
|
|||||||
<H2Title title={integrationGroup.title} />
|
<H2Title title={integrationGroup.title} />
|
||||||
{integrationGroup.hyperlink && (
|
{integrationGroup.hyperlink && (
|
||||||
<StyledGroupLink
|
<StyledGroupLink
|
||||||
onClick={() => window.open(integrationGroup.hyperlink ?? '')}
|
target={'_blank'}
|
||||||
|
to={integrationGroup.hyperlink ?? ''}
|
||||||
>
|
>
|
||||||
<div>{integrationGroup.hyperlinkText}</div>
|
<div>{integrationGroup.hyperlinkText}</div>
|
||||||
<div>→</div>
|
<div>→</div>
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import isPropValid from '@emotion/is-prop-valid';
|
||||||
import { css, useTheme } from '@emotion/react';
|
import { css, useTheme } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { IconComponent, Pill } from 'twenty-ui';
|
import { IconComponent, Pill } from 'twenty-ui';
|
||||||
@@ -22,9 +24,14 @@ export type ButtonProps = {
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
focus?: boolean;
|
focus?: boolean;
|
||||||
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
||||||
|
to?: string;
|
||||||
|
target?: string;
|
||||||
} & React.ComponentProps<'button'>;
|
} & React.ComponentProps<'button'>;
|
||||||
|
|
||||||
const StyledButton = styled.button<
|
const StyledButton = styled('button', {
|
||||||
|
shouldForwardProp: (prop) =>
|
||||||
|
!['fullWidth'].includes(prop) && isPropValid(prop),
|
||||||
|
})<
|
||||||
Pick<
|
Pick<
|
||||||
ButtonProps,
|
ButtonProps,
|
||||||
| 'fullWidth'
|
| 'fullWidth'
|
||||||
@@ -34,6 +41,8 @@ const StyledButton = styled.button<
|
|||||||
| 'accent'
|
| 'accent'
|
||||||
| 'focus'
|
| 'focus'
|
||||||
| 'justify'
|
| 'justify'
|
||||||
|
| 'to'
|
||||||
|
| 'target'
|
||||||
>
|
>
|
||||||
>`
|
>`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -210,6 +219,7 @@ const StyledButton = styled.button<
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
text-decoration: none;
|
||||||
border-radius: ${({ position, theme }) => {
|
border-radius: ${({ position, theme }) => {
|
||||||
switch (position) {
|
switch (position) {
|
||||||
case 'left':
|
case 'left':
|
||||||
@@ -273,6 +283,8 @@ export const Button = ({
|
|||||||
justify = 'flex-start',
|
justify = 'flex-start',
|
||||||
focus = false,
|
focus = false,
|
||||||
onClick,
|
onClick,
|
||||||
|
to,
|
||||||
|
target,
|
||||||
}: ButtonProps) => {
|
}: ButtonProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
@@ -288,6 +300,9 @@ export const Button = ({
|
|||||||
accent={accent}
|
accent={accent}
|
||||||
className={className}
|
className={className}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
|
to={to}
|
||||||
|
as={to ? Link : 'button'}
|
||||||
|
target={target}
|
||||||
>
|
>
|
||||||
{Icon && <Icon size={theme.icon.size.sm} />}
|
{Icon && <Icon size={theme.icon.size.sm} />}
|
||||||
{title}
|
{title}
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import isPropValid from '@emotion/is-prop-valid';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
const StyledTableRow = styled.div<{
|
const StyledTableRow = styled('div', {
|
||||||
|
shouldForwardProp: (prop) =>
|
||||||
|
!['isSelected'].includes(prop) && isPropValid(prop),
|
||||||
|
})<{
|
||||||
isSelected?: boolean;
|
isSelected?: boolean;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
|
to?: string;
|
||||||
}>`
|
}>`
|
||||||
background-color: ${({ isSelected, theme }) =>
|
background-color: ${({ isSelected, theme }) =>
|
||||||
isSelected ? theme.accent.quaternary : 'transparent'};
|
isSelected ? theme.accent.quaternary : 'transparent'};
|
||||||
@@ -13,12 +19,36 @@ const StyledTableRow = styled.div<{
|
|||||||
transition: background-color
|
transition: background-color
|
||||||
${({ theme }) => theme.animation.duration.normal}s;
|
${({ theme }) => theme.animation.duration.normal}s;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: ${({ onClick, theme }) =>
|
background-color: ${({ onClick, to, theme }) =>
|
||||||
onClick ? theme.background.transparent.light : 'transparent'};
|
onClick || to ? theme.background.transparent.light : 'transparent'};
|
||||||
cursor: ${({ onClick }) => (onClick ? 'pointer' : 'default')};
|
cursor: ${({ onClick, to }) => (onClick || to ? 'pointer' : 'default')};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export { StyledTableRow as TableRow };
|
type TableRowProps = {
|
||||||
|
isSelected?: boolean;
|
||||||
|
onClick?: () => void;
|
||||||
|
to?: string;
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TableRow = ({
|
||||||
|
isSelected,
|
||||||
|
onClick,
|
||||||
|
to,
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
}: React.PropsWithChildren<TableRowProps>) => (
|
||||||
|
<StyledTableRow
|
||||||
|
isSelected={isSelected}
|
||||||
|
onClick={onClick}
|
||||||
|
className={className}
|
||||||
|
to={to}
|
||||||
|
as={to ? Link : 'div'}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</StyledTableRow>
|
||||||
|
);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { Link, useNavigate } from 'react-router-dom';
|
||||||
|
import isPropValid from '@emotion/is-prop-valid';
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { isNonEmptyString } from '@sniptt/guards';
|
import { isNonEmptyString } from '@sniptt/guards';
|
||||||
@@ -29,14 +30,19 @@ type StyledItemProps = {
|
|||||||
danger?: boolean;
|
danger?: boolean;
|
||||||
level: 1 | 2;
|
level: 1 | 2;
|
||||||
soon?: boolean;
|
soon?: boolean;
|
||||||
|
to?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledItem = styled.div<StyledItemProps>`
|
const StyledItem = styled('div', {
|
||||||
|
shouldForwardProp: (prop) =>
|
||||||
|
!['active', 'danger', 'soon'].includes(prop) && isPropValid(prop),
|
||||||
|
})<StyledItemProps>`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: ${(props) =>
|
background: ${(props) =>
|
||||||
props.active ? props.theme.background.transparent.light : 'inherit'};
|
props.active ? props.theme.background.transparent.light : 'inherit'};
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||||
|
text-decoration: none;
|
||||||
color: ${(props) => {
|
color: ${(props) => {
|
||||||
if (props.active === true) {
|
if (props.active === true) {
|
||||||
return props.theme.font.color.primary;
|
return props.theme.font.color.primary;
|
||||||
@@ -153,6 +159,8 @@ export const NavigationDrawerItem = ({
|
|||||||
aria-selected={active}
|
aria-selected={active}
|
||||||
danger={danger}
|
danger={danger}
|
||||||
soon={soon}
|
soon={soon}
|
||||||
|
as={to ? Link : 'div'}
|
||||||
|
to={to ? to : undefined}
|
||||||
>
|
>
|
||||||
{Icon && <Icon size={theme.icon.size.md} stroke={theme.icon.stroke.md} />}
|
{Icon && <Icon size={theme.icon.size.md} stroke={theme.icon.stroke.md} />}
|
||||||
<StyledItemLabel>{label}</StyledItemLabel>
|
<StyledItemLabel>{label}</StyledItemLabel>
|
||||||
|
|||||||
@@ -156,6 +156,7 @@ export const SettingsObjectDetail = () => {
|
|||||||
}
|
}
|
||||||
fieldMetadataItem={activeMetadataField}
|
fieldMetadataItem={activeMetadataField}
|
||||||
isRemoteObjectField={activeObjectMetadataItem.isRemote}
|
isRemoteObjectField={activeObjectMetadataItem.isRemote}
|
||||||
|
// to={`./${getFieldSlug(activeMetadataField)}`}
|
||||||
ActionIcon={
|
ActionIcon={
|
||||||
<SettingsObjectFieldActiveActionDropdown
|
<SettingsObjectFieldActiveActionDropdown
|
||||||
isCustomField={!!activeMetadataField.isCustom}
|
isCustomField={!!activeMetadataField.isCustom}
|
||||||
|
|||||||
@@ -184,9 +184,7 @@ export const SettingsObjectNewFieldStep1 = () => {
|
|||||||
title="Add Custom Field"
|
title="Add Custom Field"
|
||||||
size="small"
|
size="small"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={() =>
|
to={`/settings/objects/${objectSlug}/new-field/step-2`}
|
||||||
navigate(`/settings/objects/${objectSlug}/new-field/step-2`)
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</StyledSection>
|
</StyledSection>
|
||||||
</SettingsPageContainer>
|
</SettingsPageContainer>
|
||||||
|
|||||||
@@ -82,13 +82,9 @@ export const SettingsObjects = () => {
|
|||||||
stroke={theme.icon.stroke.sm}
|
stroke={theme.icon.stroke.sm}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
onClick={() =>
|
to={`/settings/objects/${getObjectSlug(
|
||||||
navigate(
|
activeObjectMetadataItem,
|
||||||
`/settings/objects/${getObjectSlug(
|
)}`}
|
||||||
activeObjectMetadataItem,
|
|
||||||
)}`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</TableSection>
|
</TableSection>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { IconPlus, IconSettings } from 'twenty-ui';
|
import { IconPlus, IconSettings } from 'twenty-ui';
|
||||||
|
|
||||||
@@ -20,8 +19,6 @@ const StyledButtonContainer = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const SettingsDevelopers = () => {
|
export const SettingsDevelopers = () => {
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SubMenuTopBarContainer Icon={IconSettings} title="Settings">
|
<SubMenuTopBarContainer Icon={IconSettings} title="Settings">
|
||||||
<SettingsPageContainer>
|
<SettingsPageContainer>
|
||||||
@@ -41,9 +38,7 @@ export const SettingsDevelopers = () => {
|
|||||||
title="Create API key"
|
title="Create API key"
|
||||||
size="small"
|
size="small"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={() => {
|
to={'/settings/developers/api-keys/new'}
|
||||||
navigate('/settings/developers/api-keys/new');
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</StyledButtonContainer>
|
</StyledButtonContainer>
|
||||||
</Section>
|
</Section>
|
||||||
@@ -59,9 +54,7 @@ export const SettingsDevelopers = () => {
|
|||||||
title="Create Webhook"
|
title="Create Webhook"
|
||||||
size="small"
|
size="small"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={() => {
|
to={'/settings/developers/webhooks/new'}
|
||||||
navigate('/settings/developers/webhooks/new');
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</StyledButtonContainer>
|
</StyledButtonContainer>
|
||||||
</Section>
|
</Section>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export const SettingsDevelopersApiKeysNew = () => {
|
|||||||
name: string;
|
name: string;
|
||||||
expirationDate: number | null;
|
expirationDate: number | null;
|
||||||
}>({
|
}>({
|
||||||
expirationDate: EXPIRATION_DATES[0].value,
|
expirationDate: EXPIRATION_DATES[5].value,
|
||||||
name: '',
|
name: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { MouseEvent, ReactNode } from 'react';
|
import { MouseEvent, ReactNode } from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import isPropValid from '@emotion/is-prop-valid';
|
||||||
import { css } from '@emotion/react';
|
import { css } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
@@ -33,17 +35,22 @@ type ChipProps = {
|
|||||||
rightComponent?: ReactNode;
|
rightComponent?: ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
onClick?: (event: MouseEvent<HTMLDivElement>) => void;
|
onClick?: (event: MouseEvent<HTMLDivElement>) => void;
|
||||||
|
to?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledContainer = styled.div<
|
const StyledContainer = styled('div', {
|
||||||
|
shouldForwardProp: (prop) =>
|
||||||
|
!['clickable', 'maxWidth'].includes(prop) && isPropValid(prop),
|
||||||
|
})<
|
||||||
Pick<
|
Pick<
|
||||||
ChipProps,
|
ChipProps,
|
||||||
'accent' | 'clickable' | 'disabled' | 'maxWidth' | 'size' | 'variant'
|
'accent' | 'clickable' | 'disabled' | 'maxWidth' | 'size' | 'variant' | 'to'
|
||||||
>
|
>
|
||||||
>`
|
>`
|
||||||
--chip-horizontal-padding: ${({ theme }) => theme.spacing(1)};
|
--chip-horizontal-padding: ${({ theme }) => theme.spacing(1)};
|
||||||
--chip-vertical-padding: ${({ theme }) => theme.spacing(1)};
|
--chip-vertical-padding: ${({ theme }) => theme.spacing(1)};
|
||||||
|
|
||||||
|
text-decoration: none;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||||
color: ${({ theme, disabled }) =>
|
color: ${({ theme, disabled }) =>
|
||||||
@@ -158,22 +165,27 @@ export const Chip = ({
|
|||||||
maxWidth,
|
maxWidth,
|
||||||
className,
|
className,
|
||||||
onClick,
|
onClick,
|
||||||
}: ChipProps) => (
|
to,
|
||||||
<StyledContainer
|
}: ChipProps) => {
|
||||||
data-testid="chip"
|
return (
|
||||||
clickable={clickable}
|
<StyledContainer
|
||||||
variant={variant}
|
data-testid="chip"
|
||||||
accent={accent}
|
clickable={clickable}
|
||||||
size={size}
|
variant={variant}
|
||||||
disabled={disabled}
|
accent={accent}
|
||||||
className={className}
|
size={size}
|
||||||
maxWidth={maxWidth}
|
disabled={disabled}
|
||||||
onClick={onClick}
|
className={className}
|
||||||
>
|
maxWidth={maxWidth}
|
||||||
{leftComponent}
|
onClick={onClick}
|
||||||
<StyledLabel>
|
as={to ? Link : 'div'}
|
||||||
<OverflowingTextWithTooltip text={label} />
|
to={to ? to : undefined}
|
||||||
</StyledLabel>
|
>
|
||||||
{rightComponent}
|
{leftComponent}
|
||||||
</StyledContainer>
|
<StyledLabel>
|
||||||
);
|
<OverflowingTextWithTooltip text={label} />
|
||||||
|
</StyledLabel>
|
||||||
|
{rightComponent}
|
||||||
|
</StyledContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
import { isNonEmptyString } from '@sniptt/guards';
|
import { isNonEmptyString } from '@sniptt/guards';
|
||||||
|
|
||||||
@@ -37,14 +36,11 @@ export const EntityChip = ({
|
|||||||
className,
|
className,
|
||||||
maxWidth,
|
maxWidth,
|
||||||
}: EntityChipProps) => {
|
}: EntityChipProps) => {
|
||||||
const navigate = useNavigate();
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const handleLinkClick = (event: React.MouseEvent<HTMLDivElement>) => {
|
const handleLinkClick = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||||
if (isNonEmptyString(linkToEntity)) {
|
if (isNonEmptyString(linkToEntity)) {
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
navigate(linkToEntity);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -75,6 +71,7 @@ export const EntityChip = ({
|
|||||||
onClick={handleLinkClick}
|
onClick={handleLinkClick}
|
||||||
className={className}
|
className={className}
|
||||||
maxWidth={maxWidth}
|
maxWidth={maxWidth}
|
||||||
|
to={linkToEntity}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user