diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/display/components/EmailsFieldDisplay.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/display/components/EmailsFieldDisplay.tsx
index d0baeb1f62..72c18bfe77 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/display/components/EmailsFieldDisplay.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/display/components/EmailsFieldDisplay.tsx
@@ -1,8 +1,26 @@
import { useEmailsFieldDisplay } from '@/object-record/record-field/ui/meta-types/hooks/useEmailsFieldDisplay';
import { EmailsDisplay } from '@/ui/field/display/components/EmailsDisplay';
+import { useLingui } from '@lingui/react/macro';
+import React from 'react';
+import { FieldMetadataSettingsOnClickAction } from 'twenty-shared/types';
+import { useCopyToClipboard } from '~/hooks/useCopyToClipboard';
export const EmailsFieldDisplay = () => {
- const { fieldValue } = useEmailsFieldDisplay();
+ const { fieldValue, fieldDefinition } = useEmailsFieldDisplay();
+ const { copyToClipboard } = useCopyToClipboard();
+ const { t } = useLingui();
- return ;
+ const onClickAction = fieldDefinition.metadata.settings?.clickAction;
+
+ const handleEmailClick = (
+ email: string,
+ event: React.MouseEvent,
+ ) => {
+ if (onClickAction === FieldMetadataSettingsOnClickAction.COPY) {
+ event.preventDefault();
+ copyToClipboard(email, t`Email copied to clipboard`);
+ }
+ };
+
+ return ;
};
diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/display/components/LinksFieldDisplay.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/display/components/LinksFieldDisplay.tsx
index e54d72d6e5..2f82e9b5d1 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/display/components/LinksFieldDisplay.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/display/components/LinksFieldDisplay.tsx
@@ -1,8 +1,27 @@
import { useLinksFieldDisplay } from '@/object-record/record-field/ui/meta-types/hooks/useLinksFieldDisplay';
import { LinksDisplay } from '@/ui/field/display/components/LinksDisplay';
+import { useLingui } from '@lingui/react/macro';
+import React from 'react';
+import { FieldMetadataSettingsOnClickAction } from 'twenty-shared/types';
+
+import { useCopyToClipboard } from '~/hooks/useCopyToClipboard';
export const LinksFieldDisplay = () => {
- const { fieldValue } = useLinksFieldDisplay();
+ const { fieldValue, fieldDefinition } = useLinksFieldDisplay();
+ const { copyToClipboard } = useCopyToClipboard();
+ const { t } = useLingui();
- return ;
+ const onClickAction = fieldDefinition.metadata.settings?.clickAction;
+
+ const handleLinkClick = (
+ url: string,
+ event: React.MouseEvent,
+ ) => {
+ if (onClickAction === FieldMetadataSettingsOnClickAction.COPY) {
+ event.preventDefault();
+ copyToClipboard(url, t`Link copied to clipboard`);
+ }
+ };
+
+ return ;
};
diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/display/components/PhonesFieldDisplay.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/display/components/PhonesFieldDisplay.tsx
index 631a025b8e..fca9ced8bb 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/display/components/PhonesFieldDisplay.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/display/components/PhonesFieldDisplay.tsx
@@ -1,48 +1,27 @@
import { useFieldFocus } from '@/object-record/record-field/ui/hooks/useFieldFocus';
import { usePhonesFieldDisplay } from '@/object-record/record-field/ui/meta-types/hooks/usePhonesFieldDisplay';
-import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { PhonesDisplay } from '@/ui/field/display/components/PhonesDisplay';
import { useLingui } from '@lingui/react/macro';
import React from 'react';
-import { useIcons } from 'twenty-ui/display';
+import { FieldMetadataSettingsOnClickAction } from 'twenty-shared/types';
+import { useCopyToClipboard } from '~/hooks/useCopyToClipboard';
export const PhonesFieldDisplay = () => {
- const { fieldValue } = usePhonesFieldDisplay();
-
+ const { fieldValue, fieldDefinition } = usePhonesFieldDisplay();
+ const { copyToClipboard } = useCopyToClipboard();
const { isFocused } = useFieldFocus();
- const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar();
-
- const { getIcon } = useIcons();
-
const { t } = useLingui();
- const IconCircleCheck = getIcon('IconCircleCheck');
- const IconExclamationCircle = getIcon('IconExclamationCircle');
+ const onClickAction = fieldDefinition.metadata.settings?.clickAction;
const handleClick = async (
phoneNumber: string,
event: React.MouseEvent,
) => {
- event.preventDefault();
-
- try {
- await navigator.clipboard.writeText(phoneNumber);
- enqueueSuccessSnackBar({
- message: t`Phone number copied to clipboard`,
- options: {
- icon: ,
- duration: 2000,
- },
- });
- } catch {
- enqueueErrorSnackBar({
- message: t`Error copying to clipboard`,
- options: {
- icon: ,
- duration: 2000,
- },
- });
+ if (onClickAction === FieldMetadataSettingsOnClickAction.COPY) {
+ event.preventDefault();
+ copyToClipboard(phoneNumber, t`Phone number copied to clipboard`);
}
};
diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/display/components/__stories__/perf/EmailsFieldDisplay.perf.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/display/components/__stories__/perf/EmailsFieldDisplay.perf.stories.tsx
index cd255aa4cf..b06f29e5ce 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/display/components/__stories__/perf/EmailsFieldDisplay.perf.stories.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/display/components/__stories__/perf/EmailsFieldDisplay.perf.stories.tsx
@@ -2,19 +2,23 @@ import { type Meta, type StoryObj } from '@storybook/react';
import { EmailsFieldDisplay } from '@/object-record/record-field/ui/meta-types/display/components/EmailsFieldDisplay';
import { ComponentDecorator } from 'twenty-ui/testing';
+import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
+import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { getFieldDecorator } from '~/testing/decorators/getFieldDecorator';
import { getProfilingStory } from '~/testing/profiling/utils/getProfilingStory';
const meta: Meta = {
title: 'UI/Data/Field/Display/EmailsFieldDisplay',
decorators: [
+ I18nFrontDecorator,
MemoryRouterDecorator,
getFieldDecorator('person', 'emails', {
primaryEmail: 'test@test.com',
additionalEmails: ['toto@test.com'],
}),
ComponentDecorator,
+ SnackBarDecorator,
],
component: EmailsFieldDisplay,
args: {},
diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/display/components/__stories__/perf/LinksFieldDisplay.perf.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/display/components/__stories__/perf/LinksFieldDisplay.perf.stories.tsx
index 0fe52593f8..61529b297a 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/display/components/__stories__/perf/LinksFieldDisplay.perf.stories.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/display/components/__stories__/perf/LinksFieldDisplay.perf.stories.tsx
@@ -5,7 +5,9 @@ import { FieldFocusContext } from '@/object-record/record-field/ui/contexts/Fiel
import { FieldFocusContextProvider } from '@/object-record/record-field/ui/contexts/FieldFocusContextProvider';
import { LinksFieldDisplay } from '@/object-record/record-field/ui/meta-types/display/components/LinksFieldDisplay';
import { ComponentDecorator } from 'twenty-ui/testing';
+import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
+import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { getFieldDecorator } from '~/testing/decorators/getFieldDecorator';
import { getProfilingStory } from '~/testing/profiling/utils/getProfilingStory';
@@ -22,6 +24,7 @@ const FieldFocusEffect = () => {
const meta: Meta = {
title: 'UI/Data/Field/Display/LinksFieldDisplay',
decorators: [
+ I18nFrontDecorator,
MemoryRouterDecorator,
getFieldDecorator('company', 'domainName', {
primaryLinkUrl: 'https://www.google.com',
@@ -29,6 +32,7 @@ const meta: Meta = {
secondaryLinks: ['https://www.toto.com'],
}),
ComponentDecorator,
+ SnackBarDecorator,
],
component: LinksFieldDisplay,
args: {},
diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useEmailsFieldDisplay.ts b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useEmailsFieldDisplay.ts
index f675c29b0b..8a49b65e87 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useEmailsFieldDisplay.ts
+++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useEmailsFieldDisplay.ts
@@ -1,12 +1,17 @@
import { useContext } from 'react';
import { type FieldEmailsValue } from '@/object-record/record-field/ui/types/FieldMetadata';
+import { assertFieldMetadata } from '@/object-record/record-field/ui/types/guards/assertFieldMetadata';
+import { isFieldEmails } from '@/object-record/record-field/ui/types/guards/isFieldEmails';
import { useRecordFieldValue } from '@/object-record/record-store/hooks/useRecordFieldValue';
+import { FieldMetadataType } from 'twenty-shared/types';
import { FieldContext } from '../../contexts/FieldContext';
export const useEmailsFieldDisplay = () => {
const { recordId, fieldDefinition } = useContext(FieldContext);
+ assertFieldMetadata(FieldMetadataType.EMAILS, isFieldEmails, fieldDefinition);
+
const fieldName = fieldDefinition.metadata.fieldName;
const fieldValue = useRecordFieldValue(
diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useLinksFieldDisplay.ts b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useLinksFieldDisplay.ts
index e6729754ac..c780e4135c 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useLinksFieldDisplay.ts
+++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/useLinksFieldDisplay.ts
@@ -2,12 +2,17 @@ import { useContext } from 'react';
import { type FieldLinksValue } from '@/object-record/record-field/ui/types/FieldMetadata';
+import { assertFieldMetadata } from '@/object-record/record-field/ui/types/guards/assertFieldMetadata';
+import { isFieldLinks } from '@/object-record/record-field/ui/types/guards/isFieldLinks';
import { useRecordFieldValue } from '@/object-record/record-store/hooks/useRecordFieldValue';
+import { FieldMetadataType } from 'twenty-shared/types';
import { FieldContext } from '../../contexts/FieldContext';
export const useLinksFieldDisplay = () => {
const { recordId, fieldDefinition } = useContext(FieldContext);
+ assertFieldMetadata(FieldMetadataType.LINKS, isFieldLinks, fieldDefinition);
+
const fieldName = fieldDefinition.metadata.fieldName;
const fieldValue = useRecordFieldValue(
diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/usePhonesFieldDisplay.ts b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/usePhonesFieldDisplay.ts
index 948525fdfc..fea10a6d7f 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/usePhonesFieldDisplay.ts
+++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/hooks/usePhonesFieldDisplay.ts
@@ -2,12 +2,17 @@ import { useContext } from 'react';
import { type FieldPhonesValue } from '@/object-record/record-field/ui/types/FieldMetadata';
+import { assertFieldMetadata } from '@/object-record/record-field/ui/types/guards/assertFieldMetadata';
+import { isFieldPhones } from '@/object-record/record-field/ui/types/guards/isFieldPhones';
import { useRecordFieldValue } from '@/object-record/record-store/hooks/useRecordFieldValue';
+import { FieldMetadataType } from 'twenty-shared/types';
import { FieldContext } from '../../contexts/FieldContext';
export const usePhonesFieldDisplay = () => {
const { recordId, fieldDefinition } = useContext(FieldContext);
+ assertFieldMetadata(FieldMetadataType.PHONES, isFieldPhones, fieldDefinition);
+
const fieldName = fieldDefinition.metadata.fieldName;
const fieldValue = useRecordFieldValue(
diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldIsUniqueForm.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldIsUniqueForm.tsx
index b16f01ab83..38971064e8 100644
--- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldIsUniqueForm.tsx
+++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldIsUniqueForm.tsx
@@ -2,7 +2,6 @@ import { Controller, useFormContext } from 'react-hook-form';
import { useFieldMetadataItemById } from '@/object-metadata/hooks/useFieldMetadataItemById';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
-import { Separator } from '@/settings/components/Separator';
import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect';
import { canBeUnique } from '@/settings/data-model/fields/forms/utils/canBeUnique.util';
import { t } from '@lingui/core/macro';
@@ -66,7 +65,6 @@ export const SettingsDataModelFieldIsUniqueForm = ({
return (
<>
-
{
+ const { t } = useLingui();
+ const { control } =
+ useFormContext();
+
+ const { fieldMetadataItem } = useFieldMetadataItemById(
+ existingFieldMetadataId,
+ );
+
+ const options = [
+ {
+ label: t`Open as link`,
+ value: FieldMetadataSettingsOnClickAction.OPEN_LINK,
+ },
+ {
+ label: t`Copy to clipboard`,
+ value: FieldMetadataSettingsOnClickAction.COPY,
+ },
+ ];
+
+ const description =
+ getSettingsDataModelFieldOnClickActionDescription(fieldType);
+
+ const existingSettings =
+ (fieldMetadataItem?.settings as FieldMetadataMultiItemSettings) ?? {};
+
+ return (
+ {
+ const currentSettings =
+ (value as FieldMetadataMultiItemSettings | undefined) ?? {};
+
+ const clickAction =
+ currentSettings.clickAction ??
+ FieldMetadataSettingsOnClickAction.OPEN_LINK;
+
+ return (
+
+
+ );
+ }}
+ />
+ );
+};
diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldSettingsFormCard.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldSettingsFormCard.tsx
index fb56567b91..ba0439d6a0 100644
--- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldSettingsFormCard.tsx
+++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsDataModelFieldSettingsFormCard.tsx
@@ -25,11 +25,14 @@ import {
settingsDataModelFieldSelectFormSchema,
} from '@/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectForm';
import { SettingsDataModelFieldSelectSettingsFormCard } from '@/settings/data-model/fields/forms/select/components/SettingsDataModelFieldSelectSettingsFormCard';
-import { settingsDataModelFieldMaxValuesSchema } from '@/settings/data-model/fields/forms/utils/settingsDataModelFieldMaxValuesSchema';
import { SettingsDataModelFieldPreviewWidget } from '@/settings/data-model/fields/preview/components/SettingsDataModelFieldPreviewWidget';
import { Separator } from '@/settings/components/Separator';
+import { SettingsDataModelFieldOnClickActionForm } from '@/settings/data-model/fields/forms/components/SettingsDataModelFieldOnClickActionForm';
import { SettingsDataModelFieldRelationFormCard } from '@/settings/data-model/fields/forms/morph-relation/components/SettingsDataModelFieldRelationFormCard';
+import { mergeSettingsSchemas } from '@/settings/data-model/fields/forms/utils/mergeSettingsSchema.util';
+import { settingsDataModelFieldMaxValuesSchema } from '@/settings/data-model/fields/forms/utils/settingsDataModelFieldMaxValuesSchema';
+import { settingsDataModelFieldOnClickActionSchema } from '@/settings/data-model/fields/forms/utils/settingsDataModelFieldOnClickActionSchema';
import { useFormContext } from 'react-hook-form';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { type SettingsDataModelFieldEditFormValues } from '~/pages/settings/data-model/SettingsObjectFieldEdit';
@@ -91,17 +94,27 @@ const phonesFieldFormSchema = z
const emailsFieldFormSchema = z
.object({ type: z.literal(FieldMetadataType.EMAILS) })
- .extend(settingsDataModelFieldMaxValuesSchema.shape)
+ .merge(
+ mergeSettingsSchemas(
+ settingsDataModelFieldMaxValuesSchema,
+ settingsDataModelFieldOnClickActionSchema,
+ ),
+ )
.extend(isUniqueFieldFormSchema.shape);
const linksFieldFormSchema = z
.object({ type: z.literal(FieldMetadataType.LINKS) })
- .extend(settingsDataModelFieldMaxValuesSchema.shape)
+ .merge(
+ mergeSettingsSchemas(
+ settingsDataModelFieldMaxValuesSchema,
+ settingsDataModelFieldOnClickActionSchema,
+ ),
+ )
.extend(isUniqueFieldFormSchema.shape);
const arrayFieldFormSchema = z
.object({ type: z.literal(FieldMetadataType.ARRAY) })
- .extend(settingsDataModelFieldMaxValuesSchema.shape)
+ .merge(mergeSettingsSchemas(settingsDataModelFieldMaxValuesSchema))
.extend(isUniqueFieldFormSchema.shape);
const otherFieldsFormSchema = z
@@ -322,6 +335,22 @@ export const SettingsDataModelFieldSettingsFormCard = ({
>
)}
+ {[FieldMetadataType.EMAILS, FieldMetadataType.LINKS].includes(
+ fieldType,
+ ) && (
+ <>
+
+
+ >
+ )}
+
+
-
+
+
+
{
+ switch (fieldType) {
+ case FieldMetadataType.PHONES:
+ return t`Choose what happens when you click a phone number`;
+ case FieldMetadataType.EMAILS:
+ return t`Choose what happens when you click an email`;
+ case FieldMetadataType.LINKS:
+ return t`Choose what happens when you click a link`;
+ default:
+ assertUnreachable(fieldType, `Invalid field type: ${fieldType}`);
+ }
+};
diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/utils/mergeSettingsSchema.util.ts b/packages/twenty-front/src/modules/settings/data-model/fields/forms/utils/mergeSettingsSchema.util.ts
new file mode 100644
index 0000000000..72a82c9cc2
--- /dev/null
+++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/utils/mergeSettingsSchema.util.ts
@@ -0,0 +1,12 @@
+import z from 'zod';
+
+export const mergeSettingsSchemas = (
+ ...schemas: z.ZodObject<{ settings: z.ZodObject }>[]
+) => {
+ return z.object({
+ settings: schemas.reduce(
+ (acc, schema) => acc.merge(schema.shape.settings),
+ z.object({}) as z.ZodObject,
+ ),
+ });
+};
diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/utils/settingsDataModelFieldOnClickActionSchema.ts b/packages/twenty-front/src/modules/settings/data-model/fields/forms/utils/settingsDataModelFieldOnClickActionSchema.ts
new file mode 100644
index 0000000000..80baae0e07
--- /dev/null
+++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/utils/settingsDataModelFieldOnClickActionSchema.ts
@@ -0,0 +1,11 @@
+import { z } from 'zod';
+
+export const settingsDataModelFieldOnClickActionSchema = z.object({
+ settings: z.object({
+ clickAction: z.enum(['COPY', 'OPEN_LINK']).optional(),
+ }),
+});
+
+export type SettingsDataModelFieldOnClickActionFormValues = z.infer<
+ typeof settingsDataModelFieldOnClickActionSchema
+>;
diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/preview/components/__stories__/SettingsDataModelFieldPreviewWidget.stories.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/preview/components/__stories__/SettingsDataModelFieldPreviewWidget.stories.tsx
index fbf50133fe..16400bb1a8 100644
--- a/packages/twenty-front/src/modules/settings/data-model/fields/preview/components/__stories__/SettingsDataModelFieldPreviewWidget.stories.tsx
+++ b/packages/twenty-front/src/modules/settings/data-model/fields/preview/components/__stories__/SettingsDataModelFieldPreviewWidget.stories.tsx
@@ -4,6 +4,7 @@ import { FieldMetadataType } from '~/generated-metadata/graphql';
import { graphqlMocks } from '~/testing/graphqlMocks';
import { ComponentDecorator } from 'twenty-ui/testing';
+import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
@@ -25,6 +26,7 @@ const meta: Meta = {
'Modules/Settings/DataModel/Fields/Preview/SettingsDataModelFieldPreviewWidget',
component: SettingsDataModelFieldPreviewWidget,
decorators: [
+ I18nFrontDecorator,
MemoryRouterDecorator,
ComponentDecorator,
ObjectMetadataItemsDecorator,
diff --git a/packages/twenty-front/src/modules/ui/field/display/components/EmailsDisplay.tsx b/packages/twenty-front/src/modules/ui/field/display/components/EmailsDisplay.tsx
index db8d2a5ab4..a7616997fb 100644
--- a/packages/twenty-front/src/modules/ui/field/display/components/EmailsDisplay.tsx
+++ b/packages/twenty-front/src/modules/ui/field/display/components/EmailsDisplay.tsx
@@ -1,4 +1,4 @@
-import { useMemo } from 'react';
+import React, { useMemo } from 'react';
import { type FieldEmailsValue } from '@/object-record/record-field/ui/types/FieldMetadata';
import { ExpandableList } from '@/ui/layout/expandable-list/components/ExpandableList';
@@ -10,6 +10,7 @@ import { THEME_COMMON } from 'twenty-ui/theme';
type EmailsDisplayProps = {
value?: FieldEmailsValue;
isFocused?: boolean;
+ onEmailClick?: (email: string, event: React.MouseEvent) => void;
};
const themeSpacing = THEME_COMMON.spacingMultiplicator;
@@ -27,7 +28,11 @@ const StyledContainer = styled.div`
width: 100%;
`;
-export const EmailsDisplay = ({ value, isFocused }: EmailsDisplayProps) => {
+export const EmailsDisplay = ({
+ value,
+ isFocused,
+ onEmailClick,
+}: EmailsDisplayProps) => {
const emails = useMemo(
() =>
[
@@ -40,13 +45,23 @@ export const EmailsDisplay = ({ value, isFocused }: EmailsDisplayProps) => {
return isFocused ? (
{emails.map((email, index) => (
-
+ onEmailClick?.(email, event)}
+ />
))}
) : (
{emails.map((email, index) => (
-
+ onEmailClick?.(email, event)}
+ />
))}
);
diff --git a/packages/twenty-front/src/modules/ui/field/display/components/LinksDisplay.tsx b/packages/twenty-front/src/modules/ui/field/display/components/LinksDisplay.tsx
index 265755a6c2..05e0f197dc 100644
--- a/packages/twenty-front/src/modules/ui/field/display/components/LinksDisplay.tsx
+++ b/packages/twenty-front/src/modules/ui/field/display/components/LinksDisplay.tsx
@@ -1,4 +1,4 @@
-import { useMemo } from 'react';
+import React, { useMemo } from 'react';
import { getFieldLinkDefinedLinks } from '@/object-record/record-field/ui/meta-types/input/utils/getFieldLinkDefinedLinks';
import { type FieldLinksValue } from '@/object-record/record-field/ui/types/FieldMetadata';
@@ -13,9 +13,10 @@ import { checkUrlType } from '~/utils/checkUrlType';
type LinksDisplayProps = {
value?: FieldLinksValue;
+ onLinkClick?: (url: string, event: React.MouseEvent) => void;
};
-export const LinksDisplay = ({ value }: LinksDisplayProps) => {
+export const LinksDisplay = ({ value, onLinkClick }: LinksDisplayProps) => {
const links = useMemo(() => {
if (!isDefined(value)) {
return [];
@@ -43,9 +44,20 @@ export const LinksDisplay = ({ value }: LinksDisplayProps) => {
{links.map(({ url, label, type }, index) =>
type === LinkType.LinkedIn || type === LinkType.Twitter ? (
-
+ onLinkClick?.(url, event)}
+ />
) : (
-
+ onLinkClick?.(url, event)}
+ />
),
)}
diff --git a/packages/twenty-front/src/modules/ui/field/display/components/PhonesDisplay.tsx b/packages/twenty-front/src/modules/ui/field/display/components/PhonesDisplay.tsx
index 8bd17f7da2..8673c655dc 100644
--- a/packages/twenty-front/src/modules/ui/field/display/components/PhonesDisplay.tsx
+++ b/packages/twenty-front/src/modules/ui/field/display/components/PhonesDisplay.tsx
@@ -75,13 +75,6 @@ export const PhonesDisplay = ({
}
};
- const handleClick = (
- number: string,
- event: React.MouseEvent,
- ) => {
- onPhoneNumberClick?.(number, event);
- };
-
return isFocused ? (
{phones.map(({ number, callingCode }, index) => {
@@ -95,7 +88,9 @@ export const PhonesDisplay = ({
label={
parsedPhone ? parsedPhone.formatInternational() : invalidPhone
}
- onClick={(event) => handleClick(callingCode + number, event)}
+ onClick={(event) =>
+ onPhoneNumberClick?.(callingCode + number, event)
+ }
/>
);
})}
@@ -113,7 +108,9 @@ export const PhonesDisplay = ({
label={
parsedPhone ? parsedPhone.formatInternational() : invalidPhone
}
- onClick={(event) => handleClick(callingCode + number, event)}
+ onClick={(event) =>
+ onPhoneNumberClick?.(callingCode + number, event)
+ }
/>
);
})}
diff --git a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldEdit.tsx b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldEdit.tsx
index 6688041a2a..cd17d57f28 100644
--- a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldEdit.tsx
+++ b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectFieldEdit.tsx
@@ -119,6 +119,7 @@ export const SettingsObjectFieldEdit = () => {
label: fieldMetadataItem?.label ?? '',
description: fieldMetadataItem?.description,
isLabelSyncedWithName: fieldMetadataItem?.isLabelSyncedWithName ?? true,
+ settings: fieldMetadataItem?.settings,
},
});
diff --git a/packages/twenty-shared/src/types/FieldMetadataMultiItemSettings.ts b/packages/twenty-shared/src/types/FieldMetadataMultiItemSettings.ts
index 9c84e8379f..4ccf774141 100644
--- a/packages/twenty-shared/src/types/FieldMetadataMultiItemSettings.ts
+++ b/packages/twenty-shared/src/types/FieldMetadataMultiItemSettings.ts
@@ -1,3 +1,9 @@
+export enum FieldMetadataSettingsOnClickAction {
+ COPY = 'COPY',
+ OPEN_LINK = 'OPEN_LINK',
+}
+
export type FieldMetadataMultiItemSettings = {
maxNumberOfValues?: number;
+ clickAction?: FieldMetadataSettingsOnClickAction;
};
diff --git a/packages/twenty-shared/src/types/index.ts b/packages/twenty-shared/src/types/index.ts
index 38d036572d..3eedd2b2d4 100644
--- a/packages/twenty-shared/src/types/index.ts
+++ b/packages/twenty-shared/src/types/index.ts
@@ -84,6 +84,7 @@ export {
FieldMetadataDefaultArray,
} from './FieldMetadataDefaultValue';
export type { FieldMetadataMultiItemSettings } from './FieldMetadataMultiItemSettings';
+export { FieldMetadataSettingsOnClickAction } from './FieldMetadataMultiItemSettings';
export type { TagColor, FieldMetadataOptions } from './FieldMetadataOptions';
export {
FieldMetadataDefaultOption,