diff --git a/web/components/common/new-empty-state.tsx b/web/components/common/new-empty-state.tsx new file mode 100644 index 000000000..49caff097 --- /dev/null +++ b/web/components/common/new-empty-state.tsx @@ -0,0 +1,113 @@ +import React, { useState } from "react"; + +import Image from "next/image"; + +// ui +import { Button } from "@plane/ui"; + +type Props = { + title: string; + description?: React.ReactNode; + image: any; + comicBox?: { + direction: "left" | "right"; + title: string; + description: string; + extraPadding?: boolean; + }; + primaryButton?: { + icon?: any; + text: string; + onClick: () => void; + }; + secondaryButton?: React.ReactNode; + disabled?: boolean; +}; + +export const NewEmptyState: React.FC = ({ + title, + description, + image, + primaryButton, + secondaryButton, + disabled = false, + comicBox, +}) => { + const [isHovered, setIsHovered] = useState(false); + + const handleMouseEnter = () => { + setIsHovered(true); + }; + + const handleMouseLeave = () => { + setIsHovered(false); + }; + return ( +
+
+

{title}

+ {description &&

{description}

} +
+ {primaryButton?.text} +
+ +
+ {primaryButton && ( + + )} + {comicBox && + isHovered && + (comicBox.direction === "right" ? ( +
+
+
+
+
+

+

{comicBox?.title}

+

{comicBox?.description}

+ +
+
+ ) : ( +
+
+
+
+
+

+

{comicBox?.title}

+

{comicBox?.description}

+ +
+
+ ))} +
+
+
+ ); +}; diff --git a/web/components/issues/issue-layouts/empty-states/project.tsx b/web/components/issues/issue-layouts/empty-states/project.tsx index ba41cbf80..dc40fb859 100644 --- a/web/components/issues/issue-layouts/empty-states/project.tsx +++ b/web/components/issues/issue-layouts/empty-states/project.tsx @@ -5,8 +5,9 @@ import { useMobxStore } from "lib/mobx/store-provider"; // components import { EmptyState } from "components/common"; // assets -import emptyIssue from "public/empty-state/issue.svg"; +import emptyIssue from "public/empty-state/empty_issues.webp"; import { EProjectStore } from "store/command-palette.store"; +import { NewEmptyState } from "components/common/new-empty-state"; export const ProjectEmptyState: React.FC = observer(() => { const { @@ -16,12 +17,18 @@ export const ProjectEmptyState: React.FC = observer(() => { return (
- , onClick: () => { setTrackElement("PROJECT_EMPTY_STATE"); diff --git a/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx b/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx index 956af7abf..76fcb0a95 100644 --- a/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx @@ -68,20 +68,23 @@ export const CycleLayoutRoot: React.FC = observer(() => {
) : ( <> - {/* */} -
- {activeLayout === "list" ? ( - - ) : activeLayout === "kanban" ? ( - - ) : activeLayout === "calendar" ? ( - - ) : activeLayout === "gantt_chart" ? ( - - ) : activeLayout === "spreadsheet" ? ( - - ) : null} -
+ {Object.keys(getIssues ?? {}).length == 0 ? ( + + ) : ( +
+ {activeLayout === "list" ? ( + + ) : activeLayout === "kanban" ? ( + + ) : activeLayout === "calendar" ? ( + + ) : activeLayout === "gantt_chart" ? ( + + ) : activeLayout === "spreadsheet" ? ( + + ) : null} +
+ )} )}
diff --git a/web/components/issues/issue-layouts/roots/module-layout-root.tsx b/web/components/issues/issue-layouts/roots/module-layout-root.tsx index df314ab3c..54fe84309 100644 --- a/web/components/issues/issue-layouts/roots/module-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/module-layout-root.tsx @@ -53,20 +53,24 @@ export const ModuleLayoutRoot: React.FC = observer(() => {
) : ( <> + {Object.keys(getIssues ?? {}).length == 0 ? ( + + ) : ( +
+ {activeLayout === "list" ? ( + + ) : activeLayout === "kanban" ? ( + + ) : activeLayout === "calendar" ? ( + + ) : activeLayout === "gantt_chart" ? ( + + ) : activeLayout === "spreadsheet" ? ( + + ) : null} +
+ )} {/* */} -
- {activeLayout === "list" ? ( - - ) : activeLayout === "kanban" ? ( - - ) : activeLayout === "calendar" ? ( - - ) : activeLayout === "gantt_chart" ? ( - - ) : activeLayout === "spreadsheet" ? ( - - ) : null} -
)} diff --git a/web/components/issues/issue-layouts/roots/project-layout-root.tsx b/web/components/issues/issue-layouts/roots/project-layout-root.tsx index 4460fa7de..c19b45fb7 100644 --- a/web/components/issues/issue-layouts/roots/project-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/project-layout-root.tsx @@ -53,20 +53,23 @@ export const ProjectLayoutRoot: React.FC = observer(() => { ) : ( <> - {/* {(activeLayout === "list" || activeLayout === "spreadsheet") && issueCount === 0 && } */} -
- {activeLayout === "list" ? ( - - ) : activeLayout === "kanban" ? ( - - ) : activeLayout === "calendar" ? ( - - ) : activeLayout === "gantt_chart" ? ( - - ) : activeLayout === "spreadsheet" ? ( - - ) : null} -
+ {Object.keys(getIssues ?? {}).length == 0 ? ( + + ) : ( +
+ {activeLayout === "list" ? ( + + ) : activeLayout === "kanban" ? ( + + ) : activeLayout === "calendar" ? ( + + ) : activeLayout === "gantt_chart" ? ( + + ) : activeLayout === "spreadsheet" ? ( + + ) : null} +
+ )} )} diff --git a/web/components/modules/modules-list-view.tsx b/web/components/modules/modules-list-view.tsx index 8553ca865..612ed6a91 100644 --- a/web/components/modules/modules-list-view.tsx +++ b/web/components/modules/modules-list-view.tsx @@ -11,7 +11,8 @@ import { EmptyState } from "components/common"; // ui import { Loader } from "@plane/ui"; // assets -import emptyModule from "public/empty-state/module.svg"; +import emptyModule from "public/empty-state/empty_modules.webp"; +import { NewEmptyState } from "components/common/new-empty-state"; export const ModulesListView: React.FC = observer(() => { const router = useRouter(); @@ -78,13 +79,19 @@ export const ModulesListView: React.FC = observer(() => { {modulesView === "gantt_chart" && } ) : ( - , - text: "New Module", + text: "Build your first module", onClick: () => commandPaletteStore.toggleCreateModuleModal(true), }} /> diff --git a/web/components/page-views/workspace-dashboard.tsx b/web/components/page-views/workspace-dashboard.tsx index be569c073..d0116469f 100644 --- a/web/components/page-views/workspace-dashboard.tsx +++ b/web/components/page-views/workspace-dashboard.tsx @@ -12,6 +12,8 @@ import { CompletedIssuesGraph, IssuesList, IssuesPieChart, IssuesStats } from "c import { Button } from "@plane/ui"; // images import emptyDashboard from "public/empty-state/dashboard.svg"; +import { NewEmptyState } from "components/common/new-empty-state"; +import emptyProject from "public/empty-state/dashboard_empty_project.webp"; export const WorkspaceDashboardView = observer(() => { // router @@ -19,7 +21,12 @@ export const WorkspaceDashboardView = observer(() => { const { workspaceSlug } = router.query; // store - const { user: userStore, project: projectStore, commandPalette: commandPaletteStore, trackEvent: { setTrackElement } } = useMobxStore(); + const { + user: userStore, + project: projectStore, + commandPalette: commandPaletteStore, + trackEvent: { setTrackElement }, + } = useMobxStore(); const user = userStore.currentUser; const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : null; @@ -65,22 +72,23 @@ export const WorkspaceDashboardView = observer(() => { ) : ( -
-
-
Create a project
-

Manage your projects by creating issues, cycles, modules, views and pages.

- -
-
- Empty Dashboard -
-
+ { + setTrackElement("DASHBOARD_PAGE"); + commandPaletteStore.toggleCreateProjectModal(true); + }, + }} + /> ) ) : null} diff --git a/web/components/project/card-list.tsx b/web/components/project/card-list.tsx index 2e9ade28f..7bcc559c4 100644 --- a/web/components/project/card-list.tsx +++ b/web/components/project/card-list.tsx @@ -7,9 +7,10 @@ import { ProjectCard } from "components/project"; import { EmptyState } from "components/project/empty-state"; import { Loader } from "@plane/ui"; // images -import emptyProject from "public/empty-state/Project_full_screen.svg"; +import emptyProject from "public/empty-state/empty_project.webp"; // icons import { Plus } from "lucide-react"; +import { NewEmptyState } from "components/common/new-empty-state"; export interface IProjectCardList { workspaceSlug: string; @@ -18,7 +19,11 @@ export interface IProjectCardList { export const ProjectCardList: FC = observer((props) => { const { workspaceSlug } = props; // store - const { project: projectStore, commandPalette: commandPaletteStore, trackEvent: { setTrackElement } } = useMobxStore(); + const { + project: projectStore, + commandPalette: commandPaletteStore, + trackEvent: { setTrackElement }, + } = useMobxStore(); const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : null; @@ -50,17 +55,21 @@ export const ProjectCardList: FC = observer((props) => { )} ) : ( - , - text: "Start something new", + text: "Start your first project", onClick: () => { setTrackElement("PROJECTS_EMPTY_STATE"); - commandPaletteStore.toggleCreateProjectModal(true) - } + commandPaletteStore.toggleCreateProjectModal(true); + }, }} /> )} diff --git a/web/pages/[workspaceSlug]/analytics.tsx b/web/pages/[workspaceSlug]/analytics.tsx index e55628f0a..e11b09fb5 100644 --- a/web/pages/[workspaceSlug]/analytics.tsx +++ b/web/pages/[workspaceSlug]/analytics.tsx @@ -17,13 +17,14 @@ import emptyAnalytics from "public/empty-state/analytics.svg"; import { ANALYTICS_TABS } from "constants/analytics"; // type import { NextPageWithLayout } from "types/app"; +import { NewEmptyState } from "components/common/new-empty-state"; const AnalyticsPage: NextPageWithLayout = observer(() => { // store const { project: { workspaceProjects }, commandPalette: { toggleCreateProjectModal }, - trackEvent: { setTrackElement } + trackEvent: { setTrackElement }, } = useMobxStore(); return ( @@ -36,10 +37,11 @@ const AnalyticsPage: NextPageWithLayout = observer(() => { - `rounded-3xl border border-custom-border-200 px-4 py-2 text-xs hover:bg-custom-background-80 ${selected ? "bg-custom-background-80" : "" + `rounded-3xl border border-custom-border-200 px-4 py-2 text-xs hover:bg-custom-background-80 ${ + selected ? "bg-custom-background-80" : "" }` } - onClick={() => { }} + onClick={() => {}} > {tab.title} @@ -66,8 +68,8 @@ const AnalyticsPage: NextPageWithLayout = observer(() => { text: "New Project", onClick: () => { setTrackElement("ANALYTICS_EMPTY_STATE"); - toggleCreateProjectModal(true) - } + toggleCreateProjectModal(true); + }, }} /> diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx index 9df2357b8..9a85fb0c3 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx @@ -14,7 +14,7 @@ import { CyclesView, ActiveCycleDetails, CycleCreateUpdateModal } from "componen import { EmptyState } from "components/common"; import { Tooltip } from "@plane/ui"; // images -import emptyCycle from "public/empty-state/cycle.svg"; +import emptyCycle from "public/empty-state/empty_cycles.webp"; // types import { TCycleView, TCycleLayout } from "types"; import { NextPageWithLayout } from "types/app"; @@ -22,13 +22,14 @@ import { NextPageWithLayout } from "types/app"; import { CYCLE_TAB_LIST, CYCLE_VIEW_LAYOUTS } from "constants/cycle"; // lib cookie import { setLocalStorage, getLocalStorage } from "lib/local-storage"; +import { NewEmptyState } from "components/common/new-empty-state"; // TODO: use-local-storage hook instead of lib file. const ProjectCyclesPage: NextPageWithLayout = observer(() => { const [createModal, setCreateModal] = useState(false); // store const { project: projectStore, cycle: cycleStore } = useMobxStore(); - const { projectCycles } = cycleStore + const { projectCycles } = cycleStore; // router const router = useRouter(); const { workspaceSlug, projectId, peekCycle } = router.query; @@ -73,7 +74,7 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => { const cycleView = cycleStore?.cycleView; const cycleLayout = cycleStore?.cycleLayout; - const totalCycles = projectCycles?.length ?? 0 + const totalCycles = projectCycles?.length ?? 0; if (!workspaceSlug || !projectId) return null; @@ -87,13 +88,19 @@ const ProjectCyclesPage: NextPageWithLayout = observer(() => { /> {totalCycles === 0 ? (
- , - text: "New Cycle", + text: "Set your first cycle", onClick: () => { setCreateModal(true); }, diff --git a/web/public/empty-state/dashboard_empty_project.webp b/web/public/empty-state/dashboard_empty_project.webp new file mode 100644 index 000000000..c1f633f6b Binary files /dev/null and b/web/public/empty-state/dashboard_empty_project.webp differ diff --git a/web/public/empty-state/empty_cycles.webp b/web/public/empty-state/empty_cycles.webp new file mode 100644 index 000000000..710ae2dce Binary files /dev/null and b/web/public/empty-state/empty_cycles.webp differ diff --git a/web/public/empty-state/empty_issues.webp b/web/public/empty-state/empty_issues.webp new file mode 100644 index 000000000..e60d663d1 Binary files /dev/null and b/web/public/empty-state/empty_issues.webp differ diff --git a/web/public/empty-state/empty_modules.webp b/web/public/empty-state/empty_modules.webp new file mode 100644 index 000000000..745dce48d Binary files /dev/null and b/web/public/empty-state/empty_modules.webp differ diff --git a/web/public/empty-state/empty_project.webp b/web/public/empty-state/empty_project.webp new file mode 100644 index 000000000..ad66f0966 Binary files /dev/null and b/web/public/empty-state/empty_project.webp differ