From 491b9e954a6fd7e0c44498a3d403587a1c35c74d Mon Sep 17 00:00:00 2001 From: "Abdullah." <125115953+mabdullahabaid@users.noreply.github.com> Date: Thu, 25 Jun 2026 15:02:44 +0500 Subject: [PATCH] Rework website EmailsVisual to match twenty-front emails tab (#22146) - Rework `EmailsVisual` to match twenty-front's emails tab: bordered thread card with dividers, `Inbox` header sizing, and twenty-front's row/sender/date colors and hard-clipped sender names (kept `+ Compose`). - Replace the "not shared" row with four fully-populated shared threads (subjects/bodies sourced from twenty-server's dev seed), two participants each, full `MMM D, YYYY` dates. image --- .../EmailsVisual/EmailsVisual.test.tsx | 48 +++++++++++++++ .../EmailsVisual/EmailsVisual.tsx | 29 +++++---- .../components/ThreadRow.test.tsx | 36 +++++++++++ .../EmailsVisual/components/ThreadRow.tsx | 61 ++++++------------- .../EmailsVisual/data/threads.ts | 46 ++++++++++---- .../EmailsVisual/types/email-thread.ts | 7 ++- 6 files changed, 157 insertions(+), 70 deletions(-) create mode 100644 packages/twenty-website/src/sections/product-feature/EmailsVisual/EmailsVisual.test.tsx create mode 100644 packages/twenty-website/src/sections/product-feature/EmailsVisual/components/ThreadRow.test.tsx diff --git a/packages/twenty-website/src/sections/product-feature/EmailsVisual/EmailsVisual.test.tsx b/packages/twenty-website/src/sections/product-feature/EmailsVisual/EmailsVisual.test.tsx new file mode 100644 index 0000000000..ba65001beb --- /dev/null +++ b/packages/twenty-website/src/sections/product-feature/EmailsVisual/EmailsVisual.test.tsx @@ -0,0 +1,48 @@ +import { i18n } from '@lingui/core'; +import { I18nProvider } from '@lingui/react'; +import { render, screen } from '@testing-library/react'; + +import { EmailsVisual } from './EmailsVisual'; + +i18n.load('en', {}); +i18n.activate('en'); + +function renderVisual() { + return render( + + + , + ); +} + +describe('EmailsVisual', () => { + it('renders the inbox chrome', () => { + renderVisual(); + + expect(screen.getByText('Compose')).toBeInTheDocument(); + expect( + screen.getByText('Inbox', { exact: false, selector: 'span' }), + ).toBeInTheDocument(); + }); + + it('renders every thread subject', () => { + renderVisual(); + + expect(screen.getByText('Partnerships - Q4 Strategy')).toBeInTheDocument(); + expect(screen.getByText('Proposal Submission')).toBeInTheDocument(); + expect(screen.getByText('Follow-up on Discussion')).toBeInTheDocument(); + expect(screen.getByText('Customer Feedback')).toBeInTheDocument(); + }); + + it('renders thread previews, participants and dates', () => { + renderVisual(); + + expect( + screen.getByText( + "Hey team, I've been in touch with Notion and Figma about potential integrations.", + ), + ).toBeInTheDocument(); + expect(screen.getByText('Félix, Thomas')).toBeInTheDocument(); + expect(screen.getByText('Jun 24, 2026')).toBeInTheDocument(); + }); +}); diff --git a/packages/twenty-website/src/sections/product-feature/EmailsVisual/EmailsVisual.tsx b/packages/twenty-website/src/sections/product-feature/EmailsVisual/EmailsVisual.tsx index 8865d89b4a..6daf759c88 100644 --- a/packages/twenty-website/src/sections/product-feature/EmailsVisual/EmailsVisual.tsx +++ b/packages/twenty-website/src/sections/product-feature/EmailsVisual/EmailsVisual.tsx @@ -1,5 +1,7 @@ 'use client'; +import { msg } from '@lingui/core/macro'; +import { useLingui } from '@lingui/react'; import { styled } from '@linaria/react'; import { IconPlus } from '@tabler/icons-react'; import { THEME_LIGHT } from 'twenty-ui/theme'; @@ -41,14 +43,13 @@ const InboxTitle = styled.span` align-items: baseline; color: ${THEME_LIGHT.font.color.primary}; display: flex; - font-size: ${previewFontSize(THEME_LIGHT.font.size.md)}; + font-size: ${previewFontSize(THEME_LIGHT.font.size.lg)}; font-weight: ${THEME_LIGHT.font.weight.semiBold}; - gap: 6px; + gap: ${THEME_LIGHT.spacing(2)}; `; const InboxCount = styled.span` color: ${THEME_LIGHT.font.color.light}; - font-weight: ${THEME_LIGHT.font.weight.regular}; `; const ComposeButton = styled.span` @@ -63,34 +64,38 @@ const ComposeButton = styled.span` padding: 4px 8px; `; -const ThreadList = styled.div` - display: flex; - flex: 1; - flex-direction: column; - min-height: 0; +const ThreadCard = styled.div` + border: 1px solid ${THEME_LIGHT.border.color.medium}; + border-radius: ${THEME_LIGHT.border.radius.sm}; + margin: 0 16px; overflow: hidden; + + & > * + * { + border-top: 1px solid ${THEME_LIGHT.border.color.light}; + } `; export function EmailsVisual({ active: _active }: { active: boolean }) { + const { i18n } = useLingui(); return ( - Inbox + {i18n._(msg`Inbox`)} {THREADS.length} - Compose + {i18n._(msg`Compose`)} - + {THREADS.map((thread) => ( ))} - + ); diff --git a/packages/twenty-website/src/sections/product-feature/EmailsVisual/components/ThreadRow.test.tsx b/packages/twenty-website/src/sections/product-feature/EmailsVisual/components/ThreadRow.test.tsx new file mode 100644 index 0000000000..230feb8614 --- /dev/null +++ b/packages/twenty-website/src/sections/product-feature/EmailsVisual/components/ThreadRow.test.tsx @@ -0,0 +1,36 @@ +import { i18n } from '@lingui/core'; +import { I18nProvider } from '@lingui/react'; +import { render, screen } from '@testing-library/react'; + +import { type EmailThread } from '../types/email-thread'; +import { ThreadRow } from './ThreadRow'; + +i18n.load('en', {}); +i18n.activate('en'); + +const THREAD: EmailThread = { + date: 'Jan 1, 2026', + messageCount: 3, + participants: [ + { avatarUrl: '', name: 'Ada' }, + { avatarUrl: '', name: 'Linus' }, + ], + preview: { id: 'A short preview.' }, + subject: { id: 'A subject line' }, +}; + +describe('ThreadRow', () => { + it('renders the subject, preview, participants, count and date', () => { + render( + + + , + ); + + expect(screen.getByText('A subject line')).toBeInTheDocument(); + expect(screen.getByText('A short preview.')).toBeInTheDocument(); + expect(screen.getByText('Ada, Linus')).toBeInTheDocument(); + expect(screen.getByText('3')).toBeInTheDocument(); + expect(screen.getByText('Jan 1, 2026')).toBeInTheDocument(); + }); +}); diff --git a/packages/twenty-website/src/sections/product-feature/EmailsVisual/components/ThreadRow.tsx b/packages/twenty-website/src/sections/product-feature/EmailsVisual/components/ThreadRow.tsx index 615a4c1380..fe06338322 100644 --- a/packages/twenty-website/src/sections/product-feature/EmailsVisual/components/ThreadRow.tsx +++ b/packages/twenty-website/src/sections/product-feature/EmailsVisual/components/ThreadRow.tsx @@ -1,33 +1,36 @@ +'use client'; + +import { useLingui } from '@lingui/react'; import { styled } from '@linaria/react'; -import { IconLock } from '@tabler/icons-react'; import { THEME_LIGHT } from 'twenty-ui/theme'; -import { PersonAvatar } from '@/app-preview/primitives/PersonAvatar'; import { previewFontSize } from '@/app-preview/preview-font-size'; +import { PersonAvatar } from '@/app-preview/primitives/PersonAvatar'; import { type EmailThread } from '../types/email-thread'; const Row = styled.div` align-items: center; + background-color: ${THEME_LIGHT.background.secondary}; box-sizing: border-box; - color: ${THEME_LIGHT.font.color.primary}; + color: ${THEME_LIGHT.font.color.secondary}; display: flex; + font-size: ${previewFontSize(THEME_LIGHT.font.size.md)}; gap: 8px; height: 48px; padding: 0 16px; &:hover { - background-color: ${THEME_LIGHT.background.transparent.light}; + background-color: transparent; } `; -const Heading = styled.div` +const Sender = styled.div` align-items: center; display: flex; - max-width: 40%; + flex: 0 0 20%; min-width: 0; overflow: hidden; - width: fit-content; `; const AvatarStack = styled.div` @@ -41,18 +44,15 @@ const AvatarStack = styled.div` `; const SenderNames = styled.span` - font-size: ${previewFontSize(THEME_LIGHT.font.size.md)}; margin: 0 4px; min-width: 0; overflow: hidden; - text-overflow: ellipsis; white-space: nowrap; `; -const ThreadCount = styled.span` +const MessageCount = styled.span` color: ${THEME_LIGHT.font.color.tertiary}; flex-shrink: 0; - font-size: ${previewFontSize(THEME_LIGHT.font.size.md)}; `; const SubjectBody = styled.div` @@ -66,9 +66,6 @@ const SubjectBody = styled.div` const Subject = styled.span` color: ${THEME_LIGHT.font.color.primary}; - flex-shrink: 0; - font-size: ${previewFontSize(THEME_LIGHT.font.size.md)}; - max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -77,29 +74,13 @@ const Subject = styled.span` const Preview = styled.span` color: ${THEME_LIGHT.font.color.tertiary}; flex: 1; - font-size: ${previewFontSize(THEME_LIGHT.font.size.md)}; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; `; -const NotShared = styled.span` - align-items: center; - background: ${THEME_LIGHT.background.transparent.lighter}; - border: 1px solid ${THEME_LIGHT.border.color.light}; - border-radius: ${THEME_LIGHT.border.radius.sm}; - color: ${THEME_LIGHT.font.color.tertiary}; - display: inline-flex; - flex: 1; - font-size: ${previewFontSize(THEME_LIGHT.font.size.sm)}; - gap: 4px; - height: 20px; - padding: 0 4px; -`; - const ReceivedAt = styled.span` - color: ${THEME_LIGHT.font.color.tertiary}; flex-shrink: 0; font-size: ${previewFontSize(THEME_LIGHT.font.size.sm)}; padding: 0 4px; @@ -107,9 +88,10 @@ const ReceivedAt = styled.span` `; export function ThreadRow({ thread }: { thread: EmailThread }) { + const { i18n } = useLingui(); return ( - + {thread.participants.map((participant) => ( participant.name) .join(', ')} - {thread.messageCount} - + {thread.messageCount} + - {thread.shared ? ( - <> - {thread.subject} - {thread.preview} - - ) : ( - - - Not shared - - )} + {i18n._(thread.subject)} + {i18n._(thread.preview)} {thread.date} diff --git a/packages/twenty-website/src/sections/product-feature/EmailsVisual/data/threads.ts b/packages/twenty-website/src/sections/product-feature/EmailsVisual/data/threads.ts index 58071e5965..91b88a0695 100644 --- a/packages/twenty-website/src/sections/product-feature/EmailsVisual/data/threads.ts +++ b/packages/twenty-website/src/sections/product-feature/EmailsVisual/data/threads.ts @@ -1,3 +1,5 @@ +import { msg } from '@lingui/core/macro'; + import { sharedAssetUrls } from '@/app-preview/data/shared-asset-urls'; import { type EmailThread } from '../types/email-thread'; @@ -6,21 +8,43 @@ const PEOPLE = sharedAssetUrls.peopleAvatars; export const THREADS: EmailThread[] = [ { - date: '1:30pm', - messageCount: 2, - participants: [{ avatarUrl: PEOPLE.anonymousMike, name: 'Mike' }], - shared: false, - }, - { - date: '4 nov 2023', + date: 'Jun 24, 2026', messageCount: 4, participants: [ { avatarUrl: PEOPLE.anonymousFelix, name: 'Félix' }, { avatarUrl: PEOPLE.anonymousThomas, name: 'Thomas' }, ], - preview: - "Hey team, I've been in touch with Notion and Figma about potential integrations.", - shared: true, - subject: 'Partnerships - Q4 Strategy', + preview: msg`Hey team, I've been in touch with Notion and Figma about potential integrations.`, + subject: msg`Partnerships - Q4 Strategy`, + }, + { + date: 'Jun 23, 2026', + messageCount: 2, + participants: [ + { avatarUrl: PEOPLE.anonymousLaura, name: 'Laura' }, + { avatarUrl: PEOPLE.anonymousIndira, name: 'Indira' }, + ], + preview: msg`Dear Team, I am pleased to submit our proposal for your consideration. We have carefully reviewed your requirements.`, + subject: msg`Proposal Submission`, + }, + { + date: 'Jun 20, 2026', + messageCount: 3, + participants: [ + { avatarUrl: PEOPLE.anonymousIndira, name: 'Indira' }, + { avatarUrl: PEOPLE.anonymousMike, name: 'Mike' }, + ], + preview: msg`Hi, I wanted to follow up on our conversation from last week regarding the new initiative.`, + subject: msg`Follow-up on Discussion`, + }, + { + date: 'Jun 18, 2026', + messageCount: 1, + participants: [ + { avatarUrl: PEOPLE.anonymousThomas, name: 'Thomas' }, + { avatarUrl: PEOPLE.anonymousLaura, name: 'Laura' }, + ], + preview: msg`Hello, I wanted to share some positive feedback from our recent customer satisfaction survey.`, + subject: msg`Customer Feedback`, }, ]; diff --git a/packages/twenty-website/src/sections/product-feature/EmailsVisual/types/email-thread.ts b/packages/twenty-website/src/sections/product-feature/EmailsVisual/types/email-thread.ts index 57aa48955e..81f7395daa 100644 --- a/packages/twenty-website/src/sections/product-feature/EmailsVisual/types/email-thread.ts +++ b/packages/twenty-website/src/sections/product-feature/EmailsVisual/types/email-thread.ts @@ -1,3 +1,5 @@ +import { type MessageDescriptor } from '@lingui/core'; + type Participant = { avatarUrl: string; name: string; @@ -7,7 +9,6 @@ export type EmailThread = { date: string; messageCount: number; participants: Participant[]; - preview?: string; - shared: boolean; - subject?: string; + preview: MessageDescriptor; + subject: MessageDescriptor; };