diff --git a/packages/twenty-front/src/modules/ui/field/display/components/LinkDisplay.tsx b/packages/twenty-front/src/modules/ui/field/display/components/LinkDisplay.tsx
index 50b10e58f0..4e8047ba2f 100644
--- a/packages/twenty-front/src/modules/ui/field/display/components/LinkDisplay.tsx
+++ b/packages/twenty-front/src/modules/ui/field/display/components/LinkDisplay.tsx
@@ -1,5 +1,6 @@
import { isNonEmptyString } from '@sniptt/guards';
import { LinkType, RoundedLink, SocialLink } from 'twenty-ui/navigation';
+import { checkUrlType } from '~/utils/checkUrlType';
type LinkDisplayProps = {
value: { url: string; label?: string | null };
@@ -22,13 +23,13 @@ export const LinkDisplay = ({ value }: LinkDisplayProps) => {
? value.label
: url?.replace(/^http[s]?:\/\/(?:[w]+\.)?/gm, '').replace(/^[w]+\./gm, '');
- const type = displayedValue.startsWith('linkedin.')
- ? LinkType.LinkedIn
- : displayedValue.startsWith('twitter.')
- ? LinkType.Twitter
- : LinkType.Url;
+ const type = checkUrlType(absoluteUrl);
- if (type === LinkType.LinkedIn || type === LinkType.Twitter) {
+ if (
+ type === LinkType.LinkedIn ||
+ type === LinkType.Twitter ||
+ type === LinkType.Facebook
+ ) {
return ;
}
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 05e0f197dc..828fee4750 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
@@ -43,7 +43,9 @@ export const LinksDisplay = ({ value, onLinkClick }: LinksDisplayProps) => {
return (
{links.map(({ url, label, type }, index) =>
- type === LinkType.LinkedIn || type === LinkType.Twitter ? (
+ type === LinkType.LinkedIn ||
+ type === LinkType.Twitter ||
+ type === LinkType.Facebook ? (
{
const type = checkUrlType(absoluteUrl);
- if (type === LinkType.LinkedIn || type === LinkType.Twitter) {
+ if (
+ type === LinkType.LinkedIn ||
+ type === LinkType.Twitter ||
+ type === LinkType.Facebook
+ ) {
return (
{
it('should return "url", if neither linkedin nor twitter url', () => {
expect(checkUrlType('https://www.example.com')).toBe('url');
});
+
+ it('should return "facebook", if facebook url', () => {
+ expect(checkUrlType('https://www.facebook.com/john-doe')).toBe('facebook');
+ });
});
diff --git a/packages/twenty-front/src/utils/__tests__/getDisplayValueByUrlType.test.ts b/packages/twenty-front/src/utils/__tests__/getDisplayValueByUrlType.test.ts
deleted file mode 100644
index 1b988874d7..0000000000
--- a/packages/twenty-front/src/utils/__tests__/getDisplayValueByUrlType.test.ts
+++ /dev/null
@@ -1,40 +0,0 @@
-import { getDisplayValueByUrlType } from '~/utils/getDisplayValueByUrlType';
-import { LinkType } from 'twenty-ui/navigation';
-
-describe('getDisplayValueByUrlType', () => {
- it('should return the linkedin username from the url', () => {
- expect(
- getDisplayValueByUrlType({
- type: LinkType.LinkedIn,
- href: 'https://www.linkedin.com/in/håkan-fisk',
- }),
- ).toBe('håkan-fisk');
- expect(
- getDisplayValueByUrlType({
- type: LinkType.LinkedIn,
- href: 'https://www.linkedin.com/in/Matías',
- }),
- ).toBe('Matías');
- expect(
- getDisplayValueByUrlType({
- type: LinkType.LinkedIn,
- href: 'https://www.linkedin.com/in/Mårten',
- }),
- ).toBe('Mårten');
- expect(
- getDisplayValueByUrlType({
- type: LinkType.LinkedIn,
- href: 'https://www.linkedin.com/in/Sörvik',
- }),
- ).toBe('Sörvik');
- });
-
- it('should return the twitter username from the url', () => {
- expect(
- getDisplayValueByUrlType({
- type: LinkType.Twitter,
- href: 'https://www.twitter.com/john-doe',
- }),
- ).toBe('@john-doe');
- });
-});
diff --git a/packages/twenty-front/src/utils/checkUrlType.ts b/packages/twenty-front/src/utils/checkUrlType.ts
index fb8627c3f7..f156b33a15 100644
--- a/packages/twenty-front/src/utils/checkUrlType.ts
+++ b/packages/twenty-front/src/utils/checkUrlType.ts
@@ -9,6 +9,9 @@ export const checkUrlType = (url: string) => {
if (/^(https?:\/\/)?(www\.)?x\.com\/.+$/.test(url)) {
return LinkType.Twitter;
}
+ if (/^(https?:\/\/)?(www\.)?facebook\.com\/.+$/.test(url)) {
+ return LinkType.Facebook;
+ }
return LinkType.Url;
};
diff --git a/packages/twenty-front/src/utils/getDisplayValueByUrlType.ts b/packages/twenty-front/src/utils/getDisplayValueByUrlType.ts
deleted file mode 100644
index 83c7acc582..0000000000
--- a/packages/twenty-front/src/utils/getDisplayValueByUrlType.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { isDefined } from 'twenty-shared/utils';
-import { type LinkType } from 'twenty-ui/navigation';
-
-type getUrlDisplayValueByUrlTypeProps = {
- type: LinkType;
- href: string;
-};
-
-export const getDisplayValueByUrlType = ({
- type,
- href,
-}: getUrlDisplayValueByUrlTypeProps) => {
- if (type === 'linkedin') {
- const matches = href.match(
- /(?:https?:\/\/)?(?:www.)?linkedin.com\/(?:in|company|school)\/(.*)/,
- );
- if (isDefined(matches?.[1])) {
- return decodeURIComponent(matches?.[1]);
- } else {
- return 'LinkedIn';
- }
- }
-
- if (type === 'twitter') {
- const matches = href.match(
- /(?:https?:\/\/)?(?:www.)?twitter.com\/([-a-zA-Z0-9@:%_+.~#?&//=]*)/,
- );
- if (isDefined(matches?.[1])) {
- return `@${matches?.[1]}`;
- } else {
- return '@twitter';
- }
- }
-};
diff --git a/packages/twenty-ui/src/navigation/link/components/SocialLink.tsx b/packages/twenty-ui/src/navigation/link/components/SocialLink.tsx
index 06646c796c..13b69a24db 100644
--- a/packages/twenty-ui/src/navigation/link/components/SocialLink.tsx
+++ b/packages/twenty-ui/src/navigation/link/components/SocialLink.tsx
@@ -7,6 +7,7 @@ export enum LinkType {
Url = 'url',
LinkedIn = 'linkedin',
Twitter = 'twitter',
+ Facebook = 'facebook',
}
type SocialLinkProps = {
diff --git a/packages/twenty-ui/src/utilities/utils/__tests__/getDisplayValueByUrlType.test.ts b/packages/twenty-ui/src/utilities/utils/__tests__/getDisplayValueByUrlType.test.ts
new file mode 100644
index 0000000000..2aa2e162f8
--- /dev/null
+++ b/packages/twenty-ui/src/utilities/utils/__tests__/getDisplayValueByUrlType.test.ts
@@ -0,0 +1,149 @@
+import { LinkType } from '@ui/navigation/link/components/SocialLink';
+
+import { getDisplayValueByUrlType } from '../getDisplayValueByUrlType';
+
+describe('getDisplayValueByUrlType', () => {
+ describe('linkedin', () => {
+ it('should extract username from LinkedIn profile URL', () => {
+ const result = getDisplayValueByUrlType({
+ type: LinkType.LinkedIn,
+ href: 'https://www.linkedin.com/in/johndoe',
+ });
+ expect(result).toBe('johndoe');
+ });
+
+ it('should extract company name from LinkedIn company URL', () => {
+ const result = getDisplayValueByUrlType({
+ type: LinkType.LinkedIn,
+ href: 'https://www.linkedin.com/company/acme-corp',
+ });
+ expect(result).toBe('acme-corp');
+ });
+
+ it('should extract school name from LinkedIn school URL', () => {
+ const result = getDisplayValueByUrlType({
+ type: LinkType.LinkedIn,
+ href: 'https://www.linkedin.com/school/mit',
+ });
+ expect(result).toBe('mit');
+ });
+
+ it('should handle LinkedIn URL without protocol', () => {
+ const result = getDisplayValueByUrlType({
+ type: LinkType.LinkedIn,
+ href: 'linkedin.com/in/johndoe',
+ });
+ expect(result).toBe('johndoe');
+ });
+
+ it('should handle LinkedIn URL without www', () => {
+ const result = getDisplayValueByUrlType({
+ type: LinkType.LinkedIn,
+ href: 'https://linkedin.com/in/johndoe',
+ });
+ expect(result).toBe('johndoe');
+ });
+
+ it('should decode URL-encoded characters in LinkedIn username', () => {
+ const result = getDisplayValueByUrlType({
+ type: LinkType.LinkedIn,
+ href: 'https://www.linkedin.com/in/john%20doe',
+ });
+ expect(result).toBe('john doe');
+ });
+
+ it('should return "LinkedIn" for invalid LinkedIn URLs', () => {
+ const result = getDisplayValueByUrlType({
+ type: LinkType.LinkedIn,
+ href: 'https://www.linkedin.com/feed',
+ });
+ expect(result).toBe('LinkedIn');
+ });
+ });
+
+ describe('twitter', () => {
+ it('should extract username from Twitter URL with @ prefix', () => {
+ const result = getDisplayValueByUrlType({
+ type: LinkType.Twitter,
+ href: 'https://www.twitter.com/johndoe',
+ });
+ expect(result).toBe('@johndoe');
+ });
+
+ it('should handle Twitter URL without protocol', () => {
+ const result = getDisplayValueByUrlType({
+ type: LinkType.Twitter,
+ href: 'twitter.com/johndoe',
+ });
+ expect(result).toBe('@johndoe');
+ });
+
+ it('should handle Twitter URL without www', () => {
+ const result = getDisplayValueByUrlType({
+ type: LinkType.Twitter,
+ href: 'https://twitter.com/johndoe',
+ });
+ expect(result).toBe('@johndoe');
+ });
+
+ it('should return "@twitter" when no username can be extracted', () => {
+ const result = getDisplayValueByUrlType({
+ type: LinkType.Twitter,
+ href: 'https://www.twitter.com',
+ });
+ expect(result).toBe('@twitter');
+ });
+ });
+
+ describe('facebook', () => {
+ it('should extract username from Facebook profile URL', () => {
+ const result = getDisplayValueByUrlType({
+ type: LinkType.Facebook,
+ href: 'https://www.facebook.com/johndoe',
+ });
+ expect(result).toBe('johndoe');
+ });
+
+ it('should handle Facebook URL without protocol', () => {
+ const result = getDisplayValueByUrlType({
+ type: LinkType.Facebook,
+ href: 'facebook.com/johndoe',
+ });
+ expect(result).toBe('johndoe');
+ });
+
+ it('should handle Facebook URL without www', () => {
+ const result = getDisplayValueByUrlType({
+ type: LinkType.Facebook,
+ href: 'https://facebook.com/johndoe',
+ });
+ expect(result).toBe('johndoe');
+ });
+
+ it('should decode URL-encoded characters in Facebook username', () => {
+ const result = getDisplayValueByUrlType({
+ type: LinkType.Facebook,
+ href: 'https://www.facebook.com/john%20doe',
+ });
+ expect(result).toBe('john doe');
+ });
+
+ it('should return "Facebook" for invalid Facebook URLs', () => {
+ const result = getDisplayValueByUrlType({
+ type: LinkType.Facebook,
+ href: 'https://www.facebook.com/',
+ });
+ expect(result).toBe('Facebook');
+ });
+ });
+
+ describe('url type', () => {
+ it('should return undefined for generic url type', () => {
+ const result = getDisplayValueByUrlType({
+ type: LinkType.Url,
+ href: 'https://example.com',
+ });
+ expect(result).toBeUndefined();
+ });
+ });
+});
diff --git a/packages/twenty-ui/src/utilities/utils/getDisplayValueByUrlType.ts b/packages/twenty-ui/src/utilities/utils/getDisplayValueByUrlType.ts
index 7da8fc210b..3db51f9cbe 100644
--- a/packages/twenty-ui/src/utilities/utils/getDisplayValueByUrlType.ts
+++ b/packages/twenty-ui/src/utilities/utils/getDisplayValueByUrlType.ts
@@ -31,4 +31,13 @@ export const getDisplayValueByUrlType = ({
return '@twitter';
}
}
+
+ if (type === 'facebook') {
+ const matches = href.match(/(?:https?:\/\/)?(?:www.)?facebook.com\/(.+)/);
+ if (isDefined(matches?.[1])) {
+ return decodeURIComponent(matches?.[1]);
+ } else {
+ return 'Facebook';
+ }
+ }
};