diff --git a/packages/twenty-front/public/images/placeholders/background/404_bg.png b/packages/twenty-front/public/images/placeholders/background/404_bg.png
new file mode 100644
index 000000000..6e5dbe1da
Binary files /dev/null and b/packages/twenty-front/public/images/placeholders/background/404_bg.png differ
diff --git a/packages/twenty-front/public/images/placeholders/background/500_bg.png b/packages/twenty-front/public/images/placeholders/background/500_bg.png
new file mode 100644
index 000000000..6ea7304f0
Binary files /dev/null and b/packages/twenty-front/public/images/placeholders/background/500_bg.png differ
diff --git a/packages/twenty-front/public/images/placeholders/background/empty_timeline_bg.png b/packages/twenty-front/public/images/placeholders/background/empty_timeline_bg.png
new file mode 100644
index 000000000..8b0df003d
Binary files /dev/null and b/packages/twenty-front/public/images/placeholders/background/empty_timeline_bg.png differ
diff --git a/packages/twenty-front/public/images/placeholders/background/error_index_bg.png b/packages/twenty-front/public/images/placeholders/background/error_index_bg.png
new file mode 100644
index 000000000..a31089e7c
Binary files /dev/null and b/packages/twenty-front/public/images/placeholders/background/error_index_bg.png differ
diff --git a/packages/twenty-front/public/images/placeholders/background/no_file_bg.png b/packages/twenty-front/public/images/placeholders/background/no_file_bg.png
new file mode 100644
index 000000000..84ed918da
Binary files /dev/null and b/packages/twenty-front/public/images/placeholders/background/no_file_bg.png differ
diff --git a/packages/twenty-front/public/images/placeholders/background/no_note_bg.png b/packages/twenty-front/public/images/placeholders/background/no_note_bg.png
new file mode 100644
index 000000000..7d5583ebf
Binary files /dev/null and b/packages/twenty-front/public/images/placeholders/background/no_note_bg.png differ
diff --git a/packages/twenty-front/public/images/placeholders/background/no_record_bg.png b/packages/twenty-front/public/images/placeholders/background/no_record_bg.png
new file mode 100644
index 000000000..06e53a6db
Binary files /dev/null and b/packages/twenty-front/public/images/placeholders/background/no_record_bg.png differ
diff --git a/packages/twenty-front/public/images/placeholders/background/no_task_bg.png b/packages/twenty-front/public/images/placeholders/background/no_task_bg.png
new file mode 100644
index 000000000..29856ab4d
Binary files /dev/null and b/packages/twenty-front/public/images/placeholders/background/no_task_bg.png differ
diff --git a/packages/twenty-front/public/images/placeholders/moving-image/404.png b/packages/twenty-front/public/images/placeholders/moving-image/404.png
new file mode 100644
index 000000000..05053785a
Binary files /dev/null and b/packages/twenty-front/public/images/placeholders/moving-image/404.png differ
diff --git a/packages/twenty-front/public/images/placeholders/moving-image/500.png b/packages/twenty-front/public/images/placeholders/moving-image/500.png
new file mode 100644
index 000000000..0b0a672c1
Binary files /dev/null and b/packages/twenty-front/public/images/placeholders/moving-image/500.png differ
diff --git a/packages/twenty-front/public/images/placeholders/moving-image/empty_timeline.png b/packages/twenty-front/public/images/placeholders/moving-image/empty_timeline.png
new file mode 100644
index 000000000..2f9829f32
Binary files /dev/null and b/packages/twenty-front/public/images/placeholders/moving-image/empty_timeline.png differ
diff --git a/packages/twenty-front/public/images/placeholders/moving-image/error_index.png b/packages/twenty-front/public/images/placeholders/moving-image/error_index.png
new file mode 100644
index 000000000..acab1ccd5
Binary files /dev/null and b/packages/twenty-front/public/images/placeholders/moving-image/error_index.png differ
diff --git a/packages/twenty-front/public/images/placeholders/moving-image/no_file.png b/packages/twenty-front/public/images/placeholders/moving-image/no_file.png
new file mode 100644
index 000000000..6df6f1077
Binary files /dev/null and b/packages/twenty-front/public/images/placeholders/moving-image/no_file.png differ
diff --git a/packages/twenty-front/public/images/placeholders/moving-image/no_note.png b/packages/twenty-front/public/images/placeholders/moving-image/no_note.png
new file mode 100644
index 000000000..086eca597
Binary files /dev/null and b/packages/twenty-front/public/images/placeholders/moving-image/no_note.png differ
diff --git a/packages/twenty-front/public/images/placeholders/moving-image/no_record.png b/packages/twenty-front/public/images/placeholders/moving-image/no_record.png
new file mode 100644
index 000000000..ac3f644d9
Binary files /dev/null and b/packages/twenty-front/public/images/placeholders/moving-image/no_record.png differ
diff --git a/packages/twenty-front/public/images/placeholders/moving-image/no_task.png b/packages/twenty-front/public/images/placeholders/moving-image/no_task.png
new file mode 100644
index 000000000..374e5a917
Binary files /dev/null and b/packages/twenty-front/public/images/placeholders/moving-image/no_task.png differ
diff --git a/packages/twenty-front/src/modules/activities/files/components/Attachments.tsx b/packages/twenty-front/src/modules/activities/files/components/Attachments.tsx
index 7b80a3d6f..2f61f1c1e 100644
--- a/packages/twenty-front/src/modules/activities/files/components/Attachments.tsx
+++ b/packages/twenty-front/src/modules/activities/files/components/Attachments.tsx
@@ -9,32 +9,13 @@ import { useUploadAttachmentFile } from '@/activities/files/hooks/useUploadAttac
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { IconPlus } from '@/ui/display/icon';
import { Button } from '@/ui/input/button/components/Button';
-
-const StyledTaskGroupEmptyContainer = styled.div`
- align-items: center;
- align-self: stretch;
- display: flex;
- flex: 1 0 0;
- flex-direction: column;
- gap: ${({ theme }) => theme.spacing(2)};
- justify-content: center;
- height: 100%;
-`;
-
-const StyledEmptyTaskGroupTitle = styled.div`
- color: ${({ theme }) => theme.font.color.secondary};
- font-size: ${({ theme }) => theme.font.size.xxl};
- font-weight: ${({ theme }) => theme.font.weight.semiBold};
- line-height: ${({ theme }) => theme.text.lineHeight.md};
-`;
-
-const StyledEmptyTaskGroupSubTitle = styled.div`
- color: ${({ theme }) => theme.font.color.extraLight};
- font-size: ${({ theme }) => theme.font.size.xxl};
- font-weight: ${({ theme }) => theme.font.weight.semiBold};
- line-height: ${({ theme }) => theme.text.lineHeight.md};
- margin-bottom: ${({ theme }) => theme.spacing(2)};
-`;
+import AnimatedPlaceholder from '@/ui/layout/animated-placeholder/components/AnimatedPlaceholder';
+import {
+ StyledEmptyContainer,
+ StyledEmptySubTitle,
+ StyledEmptyTextContainer,
+ StyledEmptyTitle,
+} from '@/ui/layout/animated-placeholder/components/EmptyPlaceholderStyled';
const StyledAttachmentsContainer = styled.div`
display: flex;
@@ -85,23 +66,26 @@ export const Attachments = ({
onUploadFile={onUploadFile}
/>
) : (
-
+
+
+
+ No Files
+
+ There are no associated files with this record.
+
+
- No files yet
-
- Upload one:
-
-
+
)}
);
diff --git a/packages/twenty-front/src/modules/activities/notes/components/Notes.tsx b/packages/twenty-front/src/modules/activities/notes/components/Notes.tsx
index 24dbb45d4..1b6e0010d 100644
--- a/packages/twenty-front/src/modules/activities/notes/components/Notes.tsx
+++ b/packages/twenty-front/src/modules/activities/notes/components/Notes.tsx
@@ -6,35 +6,13 @@ import { useNotes } from '@/activities/notes/hooks/useNotes';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { IconPlus } from '@/ui/display/icon';
import { Button } from '@/ui/input/button/components/Button';
-
-const StyledTaskGroupEmptyContainer = styled.div`
- align-items: center;
- align-self: stretch;
- display: flex;
- flex: 1 0 0;
- flex-direction: column;
- gap: ${({ theme }) => theme.spacing(2)};
- justify-content: center;
- padding-bottom: ${({ theme }) => theme.spacing(16)};
- padding-left: ${({ theme }) => theme.spacing(4)};
- padding-right: ${({ theme }) => theme.spacing(4)};
- padding-top: ${({ theme }) => theme.spacing(3)};
-`;
-
-const StyledEmptyTaskGroupTitle = styled.div`
- color: ${({ theme }) => theme.font.color.secondary};
- font-size: ${({ theme }) => theme.font.size.xxl};
- font-weight: ${({ theme }) => theme.font.weight.semiBold};
- line-height: ${({ theme }) => theme.text.lineHeight.md};
-`;
-
-const StyledEmptyTaskGroupSubTitle = styled.div`
- color: ${({ theme }) => theme.font.color.extraLight};
- font-size: ${({ theme }) => theme.font.size.xxl};
- font-weight: ${({ theme }) => theme.font.weight.semiBold};
- line-height: ${({ theme }) => theme.text.lineHeight.md};
- margin-bottom: ${({ theme }) => theme.spacing(2)};
-`;
+import AnimatedPlaceholder from '@/ui/layout/animated-placeholder/components/AnimatedPlaceholder';
+import {
+ StyledEmptyContainer,
+ StyledEmptySubTitle,
+ StyledEmptyTextContainer,
+ StyledEmptyTitle,
+} from '@/ui/layout/animated-placeholder/components/EmptyPlaceholderStyled';
const StyledNotesContainer = styled.div`
display: flex;
@@ -55,9 +33,14 @@ export const Notes = ({
if (notes?.length === 0) {
return (
-
- No note yet
- Create one:
+
+
+
+ No notes
+
+ There are no associated notes with this record.
+
+
-
+
);
}
diff --git a/packages/twenty-front/src/modules/activities/tasks/components/TaskGroups.tsx b/packages/twenty-front/src/modules/activities/tasks/components/TaskGroups.tsx
index 533e34f41..17bb16fa5 100644
--- a/packages/twenty-front/src/modules/activities/tasks/components/TaskGroups.tsx
+++ b/packages/twenty-front/src/modules/activities/tasks/components/TaskGroups.tsx
@@ -7,40 +7,18 @@ import { useTasks } from '@/activities/tasks/hooks/useTasks';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { IconPlus } from '@/ui/display/icon';
import { Button } from '@/ui/input/button/components/Button';
+import AnimatedPlaceholder from '@/ui/layout/animated-placeholder/components/AnimatedPlaceholder';
+import {
+ StyledEmptyContainer,
+ StyledEmptySubTitle,
+ StyledEmptyTextContainer,
+ StyledEmptyTitle,
+} from '@/ui/layout/animated-placeholder/components/EmptyPlaceholderStyled';
import { useTabList } from '@/ui/layout/tab/hooks/useTabList';
import { AddTaskButton } from './AddTaskButton';
import { TaskList } from './TaskList';
-const StyledTaskGroupEmptyContainer = styled.div`
- align-items: center;
- align-self: stretch;
- display: flex;
- flex: 1 0 0;
- flex-direction: column;
- gap: ${({ theme }) => theme.spacing(2)};
- justify-content: center;
- padding-bottom: ${({ theme }) => theme.spacing(16)};
- padding-left: ${({ theme }) => theme.spacing(4)};
- padding-right: ${({ theme }) => theme.spacing(4)};
- padding-top: ${({ theme }) => theme.spacing(3)};
-`;
-
-const StyledEmptyTaskGroupTitle = styled.div`
- color: ${({ theme }) => theme.font.color.secondary};
- font-size: ${({ theme }) => theme.font.size.xxl};
- font-weight: ${({ theme }) => theme.font.weight.semiBold};
- line-height: ${({ theme }) => theme.text.lineHeight.md};
-`;
-
-const StyledEmptyTaskGroupSubTitle = styled.div`
- color: ${({ theme }) => theme.font.color.extraLight};
- font-size: ${({ theme }) => theme.font.size.xxl};
- font-weight: ${({ theme }) => theme.font.weight.semiBold};
- line-height: ${({ theme }) => theme.text.lineHeight.md};
- margin-bottom: ${({ theme }) => theme.spacing(2)};
-`;
-
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
@@ -80,9 +58,14 @@ export const TaskGroups = ({
(activeTabId === 'done' && completedTasks?.length === 0)
) {
return (
-
- No task yet
- Create one:
+
+
+
+ No Task
+
+ There are no associated tasks with this record
+
+
-
+
);
}
diff --git a/packages/twenty-front/src/modules/activities/timeline/components/Timeline.tsx b/packages/twenty-front/src/modules/activities/timeline/components/Timeline.tsx
index 538c2c0fd..63c5dec56 100644
--- a/packages/twenty-front/src/modules/activities/timeline/components/Timeline.tsx
+++ b/packages/twenty-front/src/modules/activities/timeline/components/Timeline.tsx
@@ -4,6 +4,13 @@ import { ActivityCreateButton } from '@/activities/components/ActivityCreateButt
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
import { useTimelineActivities } from '@/activities/timeline/hooks/useTimelineActivities';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
+import AnimatedPlaceholder from '@/ui/layout/animated-placeholder/components/AnimatedPlaceholder';
+import {
+ StyledEmptyContainer,
+ StyledEmptySubTitle,
+ StyledEmptyTextContainer,
+ StyledEmptyTitle,
+} from '@/ui/layout/animated-placeholder/components/EmptyPlaceholderStyled';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { TimelineItemsContainer } from './TimelineItemsContainer';
@@ -20,31 +27,6 @@ const StyledMainContainer = styled.div`
justify-content: center;
`;
-const StyledTimelineEmptyContainer = styled.div`
- align-items: center;
- align-self: stretch;
- display: flex;
- flex: 1 0 0;
- flex-direction: column;
- gap: ${({ theme }) => theme.spacing(2)};
- justify-content: center;
-`;
-
-const StyledEmptyTimelineTitle = styled.div`
- color: ${({ theme }) => theme.font.color.secondary};
- font-size: ${({ theme }) => theme.font.size.xxl};
- font-weight: ${({ theme }) => theme.font.weight.semiBold};
- line-height: ${({ theme }) => theme.text.lineHeight.md};
-`;
-
-const StyledEmptyTimelineSubTitle = styled.div`
- color: ${({ theme }) => theme.font.color.extraLight};
- font-size: ${({ theme }) => theme.font.size.xxl};
- font-weight: ${({ theme }) => theme.font.weight.semiBold};
- line-height: ${({ theme }) => theme.text.lineHeight.md};
- margin-bottom: ${({ theme }) => theme.spacing(2)};
-`;
-
export const Timeline = ({
targetableObject,
}: {
@@ -67,9 +49,14 @@ export const Timeline = ({
if (showEmptyState) {
return (
-
- No activity yet
- Create one:
+
+
+
+ Add your first Activity
+
+ There are no activities associated with this record.{' '}
+
+
openCreateActivity({
@@ -84,7 +71,7 @@ export const Timeline = ({
})
}
/>
-
+
);
}
diff --git a/packages/twenty-front/src/modules/error-handler/components/GenericErrorFallback.tsx b/packages/twenty-front/src/modules/error-handler/components/GenericErrorFallback.tsx
index 17d29da53..8ca2c7d02 100644
--- a/packages/twenty-front/src/modules/error-handler/components/GenericErrorFallback.tsx
+++ b/packages/twenty-front/src/modules/error-handler/components/GenericErrorFallback.tsx
@@ -1,4 +1,14 @@
import { FallbackProps } from 'react-error-boundary';
+import { Button } from 'tsup.ui.index';
+
+import { IconRefresh } from '@/ui/display/icon';
+import AnimatedPlaceholder from '@/ui/layout/animated-placeholder/components/AnimatedPlaceholder';
+import {
+ StyledEmptyContainer,
+ StyledEmptySubTitle,
+ StyledEmptyTextContainer,
+ StyledEmptyTitle,
+} from '@/ui/layout/animated-placeholder/components/EmptyPlaceholderStyled';
type GenericErrorFallbackProps = FallbackProps;
@@ -7,20 +17,18 @@ export const GenericErrorFallback = ({
resetErrorBoundary,
}: GenericErrorFallbackProps) => {
return (
-
-
{error.message}
-
-
+
+
+
+ Server’s on a coffee break
+ {error.message}
+
+
);
};
diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableWithWrappers.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableWithWrappers.tsx
index 68d9ee804..079ba4cbb 100644
--- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableWithWrappers.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableWithWrappers.tsx
@@ -9,6 +9,13 @@ import { EntityDeleteContext } from '@/object-record/record-table/contexts/Entit
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
import { IconPlus } from '@/ui/display/icon';
import { Button } from '@/ui/input/button/components/Button';
+import AnimatedPlaceholder from '@/ui/layout/animated-placeholder/components/AnimatedPlaceholder';
+import {
+ StyledEmptyContainer,
+ StyledEmptySubTitle,
+ StyledEmptyTextContainer,
+ StyledEmptyTitle,
+} from '@/ui/layout/animated-placeholder/components/EmptyPlaceholderStyled';
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import { useViewFields } from '@/views/hooks/internal/useViewFields';
@@ -19,35 +26,6 @@ import { useRecordTable } from '../hooks/useRecordTable';
import { RecordTableInternalEffect } from './RecordTableInternalEffect';
-const StyledObjectEmptyContainer = styled.div`
- align-items: center;
- align-self: stretch;
- display: flex;
- flex: 1 0 0;
- flex-direction: column;
- gap: ${({ theme }) => theme.spacing(2)};
- justify-content: center;
- padding-bottom: ${({ theme }) => theme.spacing(16)};
- padding-left: ${({ theme }) => theme.spacing(4)};
- padding-right: ${({ theme }) => theme.spacing(4)};
- padding-top: ${({ theme }) => theme.spacing(3)};
-`;
-
-const StyledEmptyObjectTitle = styled.div`
- color: ${({ theme }) => theme.font.color.secondary};
- font-size: ${({ theme }) => theme.font.size.xxl};
- font-weight: ${({ theme }) => theme.font.weight.semiBold};
- line-height: ${({ theme }) => theme.text.lineHeight.md};
-`;
-
-const StyledEmptyObjectSubTitle = styled.div`
- color: ${({ theme }) => theme.font.color.extraLight};
- font-size: ${({ theme }) => theme.font.size.xxl};
- font-weight: ${({ theme }) => theme.font.weight.semiBold};
- line-height: ${({ theme }) => theme.text.lineHeight.md};
- margin-bottom: ${({ theme }) => theme.spacing(2)};
-`;
-
const StyledTableWithHeader = styled.div`
display: flex;
flex: 1;
@@ -130,20 +108,24 @@ export const RecordTableWithWrappers = ({
tableBodyRef={tableBodyRef}
/>
{!isRecordTableInitialLoading && numberOfTableRows === 0 && (
-
-
- No {foundObjectMetadataItem?.namePlural}
-
-
- Create one:
-
+
+
+
+
+ Add your first {foundObjectMetadataItem?.namePlural}
+
+
+ Use our API or add your first{' '}
+ {foundObjectMetadataItem?.namePlural} manually
+
+
-
+
)}
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
new file mode 100644
index 000000000..0c8a6691a
--- /dev/null
+++ b/packages/twenty-front/src/modules/ui/layout/animated-placeholder/components/AnimatedPlaceholder.tsx
@@ -0,0 +1,86 @@
+import { useEffect } from 'react';
+import styled from '@emotion/styled';
+import { motion, useMotionValue, useTransform } from 'framer-motion';
+
+import {
+ Background,
+ MovingImage,
+} from '@/ui/layout/animated-placeholder/constants/AnimatedImages';
+
+const StyledContainer = styled.div`
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+`;
+
+interface StyledImageProps {
+ type: string;
+}
+
+const StyledBackgroundImage = styled.img`
+ max-height: ${({ type }) =>
+ type === 'error500' || type === 'error404' ? '245px' : '160px'};
+ max-width: ${({ type }) =>
+ type === 'error500' || type === 'error404' ? '245px' : '160px'};
+`;
+
+const StyledMovingImage = styled(motion.img)`
+ position: absolute;
+ max-width: ${({ type }) =>
+ type === 'error500' || type === 'error404' ? '185px' : '130px'};
+ max-height: ${({ type }) =>
+ type === 'error500' || type === 'error404' ? '185px' : '130px'};
+ z-index: 2;
+`;
+
+interface AnimatedPlaceholderProps {
+ type: keyof typeof Background | keyof typeof MovingImage;
+}
+
+const AnimatedPlaceholder = ({ type }: AnimatedPlaceholderProps) => {
+ const x = useMotionValue(window.innerWidth / 2);
+ const y = useMotionValue(window.innerHeight / 2);
+
+ const translateX = useTransform(x, [0, window.innerWidth], [-2, 2]);
+ const translateY = useTransform(y, [0, window.innerHeight], [-2, 2]);
+
+ useEffect(() => {
+ const handleMove = (event: MouseEvent | TouchEvent) => {
+ const clientX =
+ 'touches' in event ? event.touches[0].clientX : event.clientX;
+ const clientY =
+ 'touches' in event ? event.touches[0].clientY : event.clientY;
+
+ x.set(clientX);
+ y.set(clientY);
+ };
+
+ window.addEventListener('mousemove', handleMove);
+ window.addEventListener('touchmove', handleMove);
+
+ return () => {
+ window.removeEventListener('mousemove', handleMove);
+ window.removeEventListener('touchmove', handleMove);
+ };
+ }, [x, y]);
+
+ return (
+
+
+
+
+ );
+};
+
+export default AnimatedPlaceholder;
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
new file mode 100644
index 000000000..98702693b
--- /dev/null
+++ b/packages/twenty-front/src/modules/ui/layout/animated-placeholder/components/EmptyPlaceholderStyled.tsx
@@ -0,0 +1,40 @@
+import styled from '@emotion/styled';
+
+export const StyledEmptyContainer = styled.div`
+ align-items: center;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ gap: ${({ theme }) => theme.spacing(6)};
+ justify-content: center;
+ text-align: center;
+ margin: ${({ theme }) => theme.spacing(16)} 0px;
+`;
+
+export const StyledEmptyTextContainer = styled.div`
+ align-items: center;
+ display: flex;
+ flex-direction: column;
+ gap: ${({ theme }) => theme.spacing(3)};
+ justify-content: center;
+ text-align: center;
+ width: 100%;
+`;
+
+export const StyledEmptyTitle = styled.div`
+ color: ${({ theme }) => theme.font.color.primary};
+ font-size: ${({ theme }) => theme.font.size.lg};
+ font-weight: ${({ theme }) => theme.font.weight.semiBold};
+ line-height: ${({ theme }) => theme.text.lineHeight.lg};
+`;
+
+export const StyledEmptySubTitle = styled.div`
+ color: ${({ theme }) => theme.font.color.tertiary};
+ font-size: ${({ theme }) => theme.font.size.sm};
+ font-weight: ${({ theme }) => theme.font.weight.regular};
+ line-height: ${({ theme }) => theme.text.lineHeight.md};
+ max-height: 2.4em;
+ overflow: hidden;
+ width: 50%;
+`;
diff --git a/packages/twenty-front/src/modules/ui/layout/animated-placeholder/components/ErrorPlaceholderStyled.tsx b/packages/twenty-front/src/modules/ui/layout/animated-placeholder/components/ErrorPlaceholderStyled.tsx
new file mode 100644
index 000000000..2618d8289
--- /dev/null
+++ b/packages/twenty-front/src/modules/ui/layout/animated-placeholder/components/ErrorPlaceholderStyled.tsx
@@ -0,0 +1,38 @@
+import styled from '@emotion/styled';
+
+export const StyledErrorContainer = styled.div`
+ align-items: center;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ gap: ${({ theme }) => theme.spacing(8)};
+ justify-content: center;
+ text-align: center;
+`;
+
+export const StyledErrorTextContainer = styled.div`
+ align-items: center;
+ display: flex;
+ flex-direction: column;
+ gap: ${({ theme }) => theme.spacing(4)};
+ justify-content: center;
+ text-align: center;
+ width: 100%;
+`;
+
+export const StyledErrorTitle = styled.div`
+ color: ${({ theme }) => theme.font.color.primary};
+ font-size: ${({ theme }) => theme.font.size.xl};
+ font-weight: ${({ theme }) => theme.font.weight.semiBold};
+ line-height: ${({ theme }) => theme.text.lineHeight.lg};
+`;
+
+export const StyledErrorSubTitle = styled.div`
+ color: ${({ theme }) => theme.font.color.tertiary};
+ font-size: ${({ theme }) => theme.font.size.xs};
+ font-weight: ${({ theme }) => theme.font.weight.regular};
+ line-height: ${({ theme }) => theme.text.lineHeight.md};
+ max-height: 2.4em;
+ overflow: hidden;
+`;
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
new file mode 100644
index 000000000..5185a6683
--- /dev/null
+++ b/packages/twenty-front/src/modules/ui/layout/animated-placeholder/constants/AnimatedImages.ts
@@ -0,0 +1,21 @@
+export const Background: Record = {
+ noFile: '/images/placeholders/background/no_file_bg.png',
+ noNote: '/images/placeholders/background/no_note_bg.png',
+ noRecord: '/images/placeholders/background/no_record_bg.png',
+ noTask: '/images/placeholders/background/no_task_bg.png',
+ errorIndex: '/images/placeholders/background/error_index_bg.png',
+ emptyTimeline: '/images/placeholders/background/empty_timeline_bg.png',
+ error404: '/images/placeholders/background/404_bg.png',
+ error500: '/images/placeholders/background/500_bg.png',
+};
+
+export const MovingImage: Record = {
+ noFile: '/images/placeholders/moving-image/no_file.png',
+ noNote: '/images/placeholders/moving-image/no_note.png',
+ noRecord: '/images/placeholders/moving-image/no_record.png',
+ noTask: '/images/placeholders/moving-image/no_task.png',
+ errorIndex: '/images/placeholders/moving-image/error_index.png',
+ emptyTimeline: '/images/placeholders/moving-image/empty_timeline.png',
+ error404: '/images/placeholders/moving-image/404.png',
+ error500: '/images/placeholders/moving-image/500.png',
+};
diff --git a/packages/twenty-front/src/pages/not-found/NotFound.tsx b/packages/twenty-front/src/pages/not-found/NotFound.tsx
index 5be23a23d..613ea3cc4 100644
--- a/packages/twenty-front/src/pages/not-found/NotFound.tsx
+++ b/packages/twenty-front/src/pages/not-found/NotFound.tsx
@@ -3,7 +3,13 @@ import styled from '@emotion/styled';
import { SignInBackgroundMockPage } from '@/sign-in-background-mock/components/SignInBackgroundMockPage';
import { MainButton } from '@/ui/input/button/components/MainButton';
-import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
+import AnimatedPlaceholder from '@/ui/layout/animated-placeholder/components/AnimatedPlaceholder';
+import { StyledEmptyTextContainer } from '@/ui/layout/animated-placeholder/components/EmptyPlaceholderStyled';
+import {
+ StyledErrorContainer,
+ StyledErrorSubTitle,
+ StyledErrorTitle,
+} from '@/ui/layout/animated-placeholder/components/ErrorPlaceholderStyled';
const StyledBackDrop = styled.div`
align-items: center;
@@ -20,48 +26,33 @@ const StyledBackDrop = styled.div`
z-index: 10000;
`;
-const StyledTextContainer = styled.div`
- align-items: center;
- display: flex;
- flex-direction: column;
- justify-content: center;
- padding: ${({ theme }) => theme.spacing(15)};
-`;
-
const StyledButtonContainer = styled.div`
width: 200px;
`;
-type StyledInfoProps = {
- color: 'dark' | 'light';
-};
-
-const StyledInfo = styled.div`
- color: ${(props) =>
- props.color === 'light'
- ? props.theme.font.color.extraLight
- : props.theme.font.color.primary};
- font-size: ${() => (useIsMobile() ? '2.5rem' : '4rem')};
- font-weight: ${({ theme }) => theme.font.weight.semiBold};
-`;
-
export const NotFound = () => {
const navigate = useNavigate();
return (
<>
-
- 404
- Page not found
-
-
- navigate('/')}
- />
-
+
+
+
+ Off the beaten path
+
+ The page you're seeking is either gone or never was. Let's get you
+ back on track
+
+
+
+ navigate('/')}
+ />
+
+
>