Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

style: new empty state ui #2923

Merged
merged 1 commit into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions web/components/common/new-empty-state.tsx
Original file line number Diff line number Diff line change
@@ -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<Props> = ({
title,
description,
image,
primaryButton,
secondaryButton,
disabled = false,
comicBox,
}) => {
const [isHovered, setIsHovered] = useState(false);

const handleMouseEnter = () => {
setIsHovered(true);
};

const handleMouseLeave = () => {
setIsHovered(false);
};
return (
<div className=" flex flex-col justify-center items-center h-full w-full ">
<div className="border border-custom-border-200 rounded-xl px-10 py-7 flex flex-col gap-5 max-w-6xl m-5 md:m-16 shadow-sm">
<h3 className="font-semibold text-2xl">{title}</h3>
{description && <p className=" text-lg">{description}</p>}
<div className="relative w-full max-w-6xl">
<Image src={image} className="w-52 sm:w-60" alt={primaryButton?.text} />
</div>

<div className="flex justify-center items-start relative">
{primaryButton && (
<Button
className={`max-w-min m-3 relative !px-6 ${comicBox?.direction === "left" ? "flex-row-reverse" : ""}`}
size="lg"
variant="primary"
onClick={primaryButton.onClick}
disabled={disabled}
>
{primaryButton.text}
<div
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
className={`bg-blue-300 absolute ${
comicBox?.direction === "left" ? "left-0 ml-2" : "right-0 mr-2"
} h-2.5 w-2.5 z-10 rounded-full animate-ping`}
/>
<div
className={`bg-blue-400/40 absolute ${
comicBox?.direction === "left" ? "left-0 ml-2.5" : "right-0 mr-2.5"
} h-1.5 w-1.5 rounded-full`}
/>
</Button>
)}
{comicBox &&
isHovered &&
(comicBox.direction === "right" ? (
<div
className={`flex max-w-sm absolute top-0 left-1/2 ${
comicBox?.extraPadding ? "ml-[125px]" : "ml-[90px]"
} pb-5`}
>
<div className="relative w-0 h-0 border-t-[11px] mt-5 border-custom-border-200 border-b-[11px] border-r-[11px] border-y-transparent">
<div className="absolute top-[-10px] right-[-12px] w-0 h-0 border-t-[10px] border-custom-background-100 border-b-[10px] border-r-[10px] border-y-transparent" />
</div>
<div className="border border-custom-border-200 rounded-md bg-custom-background-100">
<h1 className="p-5">
<h3 className="font-semibold text-lg">{comicBox?.title}</h3>
<h4 className="text-sm mt-1">{comicBox?.description}</h4>
</h1>
</div>
</div>
) : (
<div className="flex flex-row-reverse max-w-sm absolute top-0 right-1/2 mr-[90px] pb-5">
<div className="relative w-0 h-0 border-t-[11px] mt-5 border-custom-border-200 border-b-[11px] border-l-[11px] border-y-transparent">
<div className="absolute top-[-10px] left-[-12px] w-0 h-0 border-t-[10px] border-custom-background-100 border-b-[10px] border-l-[10px] border-y-transparent" />
</div>
<div className="border border-custom-border-200 rounded-md bg-custom-background-100">
<h1 className="p-5">
<h3 className="font-semibold text-lg">{comicBox?.title}</h3>
<h4 className="text-sm mt-1">{comicBox?.description}</h4>
</h1>
</div>
</div>
))}
</div>
</div>
</div>
);
};
17 changes: 12 additions & 5 deletions web/components/issues/issue-layouts/empty-states/project.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -16,12 +17,18 @@ export const ProjectEmptyState: React.FC = observer(() => {

return (
<div className="h-full w-full grid place-items-center">
<EmptyState
title="Project issues will appear here"
description="Issues help you track individual pieces of work. With Issues, keep track of what's going on, who is working on it, and what's done."
<NewEmptyState
title="Create an issue and assign it to someone, even yourself"
description="Think of issues as jobs, tasks, work, or JTBD. Which we like.An issue and its sub-issues are usually time-based actionables assigned to members of your team. Your team creates, assigns, and completes issues to move your project towards its goal."
image={emptyIssue}
comicBox={{
title: "Issues are building blocks in Plane.",
direction: "left",
description:
"Redesign the Plane UI, Rebrand the company, or Launch the new fuel injection system are examples of issues that likely have sub-issues.",
}}
primaryButton={{
text: "New issue",
text: "Create your first issue",
icon: <PlusIcon className="h-3 w-3" strokeWidth={2} />,
onClick: () => {
setTrackElement("PROJECT_EMPTY_STATE");
Expand Down
31 changes: 17 additions & 14 deletions web/components/issues/issue-layouts/roots/cycle-layout-root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,20 +68,23 @@ export const CycleLayoutRoot: React.FC = observer(() => {
</div>
) : (
<>
{/* <CycleEmptyState workspaceSlug={workspaceSlug} projectId={projectId} cycleId={cycleId} /> */}
<div className="h-full w-full overflow-auto">
{activeLayout === "list" ? (
<CycleListLayout />
) : activeLayout === "kanban" ? (
<CycleKanBanLayout />
) : activeLayout === "calendar" ? (
<CycleCalendarLayout />
) : activeLayout === "gantt_chart" ? (
<CycleGanttLayout />
) : activeLayout === "spreadsheet" ? (
<CycleSpreadsheetLayout />
) : null}
</div>
{Object.keys(getIssues ?? {}).length == 0 ? (
<CycleEmptyState workspaceSlug={workspaceSlug} projectId={projectId} cycleId={cycleId} />
) : (
<div className="h-full w-full overflow-auto">
{activeLayout === "list" ? (
<CycleListLayout />
) : activeLayout === "kanban" ? (
<CycleKanBanLayout />
) : activeLayout === "calendar" ? (
<CycleCalendarLayout />
) : activeLayout === "gantt_chart" ? (
<CycleGanttLayout />
) : activeLayout === "spreadsheet" ? (
<CycleSpreadsheetLayout />
) : null}
</div>
)}
</>
)}
</div>
Expand Down
30 changes: 17 additions & 13 deletions web/components/issues/issue-layouts/roots/module-layout-root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,24 @@ export const ModuleLayoutRoot: React.FC = observer(() => {
</div>
) : (
<>
{Object.keys(getIssues ?? {}).length == 0 ? (
<ModuleEmptyState workspaceSlug={workspaceSlug} projectId={projectId} moduleId={moduleId} />
) : (
<div className="h-full w-full overflow-auto">
{activeLayout === "list" ? (
<ModuleListLayout />
) : activeLayout === "kanban" ? (
<ModuleKanBanLayout />
) : activeLayout === "calendar" ? (
<ModuleCalendarLayout />
) : activeLayout === "gantt_chart" ? (
<ModuleGanttLayout />
) : activeLayout === "spreadsheet" ? (
<ModuleSpreadsheetLayout />
) : null}
</div>
)}
{/* <ModuleEmptyState workspaceSlug={workspaceSlug} projectId={projectId} moduleId={moduleId} /> */}
<div className="h-full w-full overflow-auto">
{activeLayout === "list" ? (
<ModuleListLayout />
) : activeLayout === "kanban" ? (
<ModuleKanBanLayout />
) : activeLayout === "calendar" ? (
<ModuleCalendarLayout />
) : activeLayout === "gantt_chart" ? (
<ModuleGanttLayout />
) : activeLayout === "spreadsheet" ? (
<ModuleSpreadsheetLayout />
) : null}
</div>
</>
)}
</div>
Expand Down
31 changes: 17 additions & 14 deletions web/components/issues/issue-layouts/roots/project-layout-root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,23 @@ export const ProjectLayoutRoot: React.FC = observer(() => {
</div>
) : (
<>
{/* {(activeLayout === "list" || activeLayout === "spreadsheet") && issueCount === 0 && <ProjectEmptyState />} */}
<div className="w-full h-full relative overflow-auto">
{activeLayout === "list" ? (
<ListLayout />
) : activeLayout === "kanban" ? (
<KanBanLayout />
) : activeLayout === "calendar" ? (
<CalendarLayout />
) : activeLayout === "gantt_chart" ? (
<GanttLayout />
) : activeLayout === "spreadsheet" ? (
<ProjectSpreadsheetLayout />
) : null}
</div>
{Object.keys(getIssues ?? {}).length == 0 ? (
<ProjectEmptyState />
) : (
<div className="w-full h-full relative overflow-auto">
{activeLayout === "list" ? (
<ListLayout />
) : activeLayout === "kanban" ? (
<KanBanLayout />
) : activeLayout === "calendar" ? (
<CalendarLayout />
) : activeLayout === "gantt_chart" ? (
<GanttLayout />
) : activeLayout === "spreadsheet" ? (
<ProjectSpreadsheetLayout />
) : null}
</div>
)}
</>
)}
</div>
Expand Down
17 changes: 12 additions & 5 deletions web/components/modules/modules-list-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -78,13 +79,19 @@ export const ModulesListView: React.FC = observer(() => {
{modulesView === "gantt_chart" && <ModulesListGanttChartView />}
</>
) : (
<EmptyState
title="Manage your project with modules"
description="Modules are smaller, focused projects that help you group and organize issues."
<NewEmptyState
title="Map your project milestones to Modules and track aggregated work easily."
description="A group of issues that belong to a logical, hierarchical parent form a module. Think of them as a way to track work by project milestones. They have their own periods and deadlines as well as analytics to help you see how close or far you are from a milestone."
image={emptyModule}
comicBox={{
title: "Modules help group work by hierarchy.",
direction: "right",
description:
"A cart module, a chassis module, and a warehouse module are all good example of this grouping.",
}}
primaryButton={{
icon: <Plus className="h-4 w-4" />,
text: "New Module",
text: "Build your first module",
onClick: () => commandPaletteStore.toggleCreateModuleModal(true),
}}
/>
Expand Down
42 changes: 25 additions & 17 deletions web/components/page-views/workspace-dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,21 @@ 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
const router = useRouter();
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;
Expand Down Expand Up @@ -65,22 +72,23 @@ export const WorkspaceDashboardView = observer(() => {
</div>
</div>
) : (
<div className="bg-custom-primary-100/5 flex justify-between gap-5 md:gap-8">
<div className="p-5 md:p-8 pr-0">
<h5 className="text-xl font-semibold">Create a project</h5>
<p className="mt-2 mb-5">Manage your projects by creating issues, cycles, modules, views and pages.</p>
<Button variant="primary" size="sm" onClick={() => {
setTrackElement("DASHBOARD_PAGE");
commandPaletteStore.toggleCreateProjectModal(true)
}
}>
Create Project
</Button>
</div>
<div className="hidden md:block self-end overflow-hidden pt-8">
<Image src={emptyDashboard} alt="Empty Dashboard" />
</div>
</div>
<NewEmptyState
image={emptyProject}
title="Overview of your projects, activity, and metrics"
description="When you have created a project and have issues assigned, you will see metrics, activity, and things you care about here. This is personalized to your role in projects, so project admins will see more than members."
comicBox={{
title: "Everything starts with a project in Plane",
direction: "right",
description: "A project could be a product’s roadmap, a marketing campaign, or launching a new car.",
}}
primaryButton={{
text: "Build your first project",
onClick: () => {
setTrackElement("DASHBOARD_PAGE");
commandPaletteStore.toggleCreateProjectModal(true);
},
}}
/>
)
) : null}
</div>
Expand Down
Loading
Loading