diff --git a/packages/twenty-front/public/images/placeholders/background/empty_inbox_bg.png b/packages/twenty-front/public/images/placeholders/background/empty_inbox_bg.png new file mode 100644 index 000000000..7668b8e8f Binary files /dev/null and b/packages/twenty-front/public/images/placeholders/background/empty_inbox_bg.png differ diff --git a/packages/twenty-front/public/images/placeholders/moving-image/empty_inbox.png b/packages/twenty-front/public/images/placeholders/moving-image/empty_inbox.png new file mode 100644 index 000000000..7c9fb2576 Binary files /dev/null and b/packages/twenty-front/public/images/placeholders/moving-image/empty_inbox.png differ diff --git a/packages/twenty-front/src/modules/activities/emails/components/EmailThreads.tsx b/packages/twenty-front/src/modules/activities/emails/components/EmailThreads.tsx index 879bda969..0d2567234 100644 --- a/packages/twenty-front/src/modules/activities/emails/components/EmailThreads.tsx +++ b/packages/twenty-front/src/modules/activities/emails/components/EmailThreads.tsx @@ -17,6 +17,13 @@ import { H1TitleFontColor, } from '@/ui/display/typography/components/H1Title'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; +import AnimatedPlaceholder from '@/ui/layout/animated-placeholder/components/AnimatedPlaceholder'; +import { + StyledEmptyContainer, + StyledEmptySubTitle, + StyledEmptyTextContainer, + StyledEmptyTitle, +} from '@/ui/layout/animated-placeholder/components/EmptyPlaceholderStyled'; import { Card } from '@/ui/layout/card/components/Card'; import { Section } from '@/ui/layout/section/components/Section'; import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId'; @@ -141,14 +148,27 @@ export const EmailThreads = ({ const { totalNumberOfThreads, timelineThreads }: TimelineThreadsWithTotal = data?.[queryName] ?? []; + if (!firstQueryLoading && !timelineThreads?.length) { + return ( + + + + Empty Inbox + + No email exchange has occurred with this record yet. + + + + ); + } + return (
- Inbox{' '} - {totalNumberOfThreads ?? 0} + Inbox {totalNumberOfThreads} } fontColor={H1TitleFontColor.Primary} diff --git a/packages/twenty-front/src/modules/ui/layout/animated-placeholder/components/AnimatedPlaceholder.tsx b/packages/twenty-front/src/modules/ui/layout/animated-placeholder/components/AnimatedPlaceholder.tsx index 0c8a6691a..6bb58a61c 100644 --- a/packages/twenty-front/src/modules/ui/layout/animated-placeholder/components/AnimatedPlaceholder.tsx +++ b/packages/twenty-front/src/modules/ui/layout/animated-placeholder/components/AnimatedPlaceholder.tsx @@ -1,6 +1,7 @@ import { useEffect } from 'react'; import styled from '@emotion/styled'; import { motion, useMotionValue, useTransform } from 'framer-motion'; +import { animate } from 'framer-motion'; import { Background, @@ -56,12 +57,27 @@ const AnimatedPlaceholder = ({ type }: AnimatedPlaceholderProps) => { y.set(clientY); }; + const handleLeave = () => { + animate(x, window.innerWidth / 2, { + type: 'spring', + stiffness: 100, + damping: 10, + }); + animate(y, window.innerHeight / 2, { + type: 'spring', + stiffness: 100, + damping: 10, + }); + }; + window.addEventListener('mousemove', handleMove); window.addEventListener('touchmove', handleMove); + window.document.addEventListener('mouseleave', handleLeave); return () => { window.removeEventListener('mousemove', handleMove); window.removeEventListener('touchmove', handleMove); + window.document.removeEventListener('mouseleave', handleLeave); }; }, [x, y]); diff --git a/packages/twenty-front/src/modules/ui/layout/animated-placeholder/components/EmptyPlaceholderStyled.tsx b/packages/twenty-front/src/modules/ui/layout/animated-placeholder/components/EmptyPlaceholderStyled.tsx index 84c4f76da..bbc1dac1e 100644 --- a/packages/twenty-front/src/modules/ui/layout/animated-placeholder/components/EmptyPlaceholderStyled.tsx +++ b/packages/twenty-front/src/modules/ui/layout/animated-placeholder/components/EmptyPlaceholderStyled.tsx @@ -9,7 +9,6 @@ export const StyledEmptyContainer = styled.div` gap: ${({ theme }) => theme.spacing(6)}; justify-content: center; text-align: center; - margin: ${({ theme }) => theme.spacing(16)} 0px; `; export const StyledEmptyTextContainer = styled.div` diff --git a/packages/twenty-front/src/modules/ui/layout/animated-placeholder/constants/AnimatedImages.ts b/packages/twenty-front/src/modules/ui/layout/animated-placeholder/constants/AnimatedImages.ts index 5185a6683..ac941cff2 100644 --- a/packages/twenty-front/src/modules/ui/layout/animated-placeholder/constants/AnimatedImages.ts +++ b/packages/twenty-front/src/modules/ui/layout/animated-placeholder/constants/AnimatedImages.ts @@ -5,6 +5,7 @@ export const Background: Record = { noTask: '/images/placeholders/background/no_task_bg.png', errorIndex: '/images/placeholders/background/error_index_bg.png', emptyTimeline: '/images/placeholders/background/empty_timeline_bg.png', + emptyInbox: '/images/placeholders/background/empty_inbox_bg.png', error404: '/images/placeholders/background/404_bg.png', error500: '/images/placeholders/background/500_bg.png', }; @@ -16,6 +17,7 @@ export const MovingImage: Record = { noTask: '/images/placeholders/moving-image/no_task.png', errorIndex: '/images/placeholders/moving-image/error_index.png', emptyTimeline: '/images/placeholders/moving-image/empty_timeline.png', + emptyInbox: '/images/placeholders/moving-image/empty_inbox.png', error404: '/images/placeholders/moving-image/404.png', error500: '/images/placeholders/moving-image/500.png', }; diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/__stories__/NavigationDrawer.stories.tsx b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/__stories__/NavigationDrawer.stories.tsx index e33dfe1e9..c7d04d18f 100644 --- a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/__stories__/NavigationDrawer.stories.tsx +++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/__stories__/NavigationDrawer.stories.tsx @@ -114,7 +114,7 @@ export const Submenu: Story = { />