style: empty state global component (#435)

This commit is contained in:
Anmol Singh Bhatia 2023-03-15 11:01:54 +05:30 committed by GitHub
parent bfab4865cd
commit 636e8e6c60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 331 additions and 86 deletions

View File

@ -14,7 +14,9 @@ import { CompletedCycleIcon } from "components/icons";
import { ICycle, SelectCycleType } from "types"; import { ICycle, SelectCycleType } from "types";
// fetch-keys // fetch-keys
import { CYCLE_COMPLETE_LIST } from "constants/fetch-keys"; import { CYCLE_COMPLETE_LIST } from "constants/fetch-keys";
import { Loader } from "components/ui"; import { EmptyState, Loader } from "components/ui";
// image
import emptyCycle from "public/empty-state/empty-cycle.svg";
export interface CompletedCyclesListProps { export interface CompletedCyclesListProps {
setCreateUpdateCycleModal: React.Dispatch<React.SetStateAction<boolean>>; setCreateUpdateCycleModal: React.Dispatch<React.SetStateAction<boolean>>;
@ -72,13 +74,13 @@ export const CompletedCyclesList: React.FC<CompletedCyclesListProps> = ({
))} ))}
</div> </div>
) : ( ) : (
<div className="flex flex-col items-center justify-center gap-4 text-center"> <EmptyState
<CompletedCycleIcon height="56" width="56" /> type="cycle"
<h3 className="text-gray-500"> title="Create New Cycle"
No completed cycles yet. Create with{" "} description="Sprint more effectively with Cycles by confining your project
<pre className="inline rounded bg-gray-200 px-2 py-1">Q</pre>. to a fixed amount of time. Create new cycle now."
</h3> imgURL={emptyCycle}
</div> />
) )
) : ( ) : (
<Loader className="grid grid-cols-1 gap-9 md:grid-cols-2 lg:grid-cols-3"> <Loader className="grid grid-cols-1 gap-9 md:grid-cols-2 lg:grid-cols-3">

View File

@ -1,8 +1,11 @@
import { useState } from "react"; import { useState } from "react";
// components // components
import { DeleteCycleModal, EmptyCycle, SingleCycleCard } from "components/cycles"; import { DeleteCycleModal, SingleCycleCard } from "components/cycles";
import { Loader } from "components/ui"; import { EmptyState, Loader } from "components/ui";
// image
import emptyCycle from "public/empty-state/empty-cycle.svg";
// types // types
import { ICycle, SelectCycleType } from "types"; import { ICycle, SelectCycleType } from "types";
@ -56,7 +59,13 @@ export const CyclesList: React.FC<TCycleStatsViewProps> = ({
))} ))}
</div> </div>
) : ( ) : (
<EmptyCycle /> <EmptyState
type="cycle"
title="Create New Cycle"
description="Sprint more effectively with Cycles by confining your project
to a fixed amount of time. Create new cycle now."
imgURL={emptyCycle}
/>
) )
) : ( ) : (
<Loader className="grid grid-cols-1 gap-9 md:grid-cols-2 lg:grid-cols-3"> <Loader className="grid grid-cols-1 gap-9 md:grid-cols-2 lg:grid-cols-3">

View File

@ -0,0 +1,59 @@
import React from "react";
import Image from "next/image";
// icon
import { PlusIcon } from "@heroicons/react/24/outline";
// helper
import { capitalizeFirstLetter } from "helpers/string.helper";
type Props = {
type: "cycle" | "module" | "project" | "issue";
title: string;
description: React.ReactNode | string;
imgURL: string;
};
export const EmptyState: React.FC<Props> = ({ type, title, description, imgURL }) => {
const shortcutKey = (type: string) => {
switch (type) {
case "cycle":
return "Q";
case "module":
return "M";
case "project":
return "P";
default:
return "C";
}
};
return (
<div className="flex h-full w-full flex-col items-center justify-center gap-5 text-center">
<div className="h-32 w-72">
<Image src={imgURL} height="128" width="288" alt={type} />
</div>
<h3 className="text-xl font-semibold">{title}</h3>
<span>
Use shortcut{" "}
<span className="rounded-sm mx-1 border border-gray-200 bg-gray-100 px-2 py-1 text-sm font-medium text-gray-800">
{shortcutKey(type)}
</span>{" "}
to create {type} from anywhere.
</span>
<p className="max-w-md text-sm text-gray-500">{description}</p>
<button
className="flex items-center gap-1 rounded-lg bg-theme px-2.5 py-2 text-sm text-white"
onClick={() => {
const e = new KeyboardEvent("keydown", {
key: shortcutKey(type),
});
document.dispatchEvent(e);
}}
>
<PlusIcon className="h-4 w-4 font-bold text-white" />
Create New {capitalizeFirstLetter(type)}
</button>
</div>
);
};

View File

@ -18,3 +18,4 @@ export * from "./spinner";
export * from "./tooltip"; export * from "./tooltip";
export * from "./labels-list"; export * from "./labels-list";
export * from "./linear-progress-indicator"; export * from "./linear-progress-indicator";
export * from "./empty-state";

View File

@ -14,7 +14,7 @@ import { IssueViewContextProvider } from "contexts/issue-view.context";
// components // components
import { IssuesFilterView, IssuesView } from "components/core"; import { IssuesFilterView, IssuesView } from "components/core";
// ui // ui
import { Spinner, EmptySpace, EmptySpaceItem, HeaderButton } from "components/ui"; import { Spinner, EmptySpace, EmptySpaceItem, HeaderButton, EmptyState } from "components/ui";
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs"; import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
// icons // icons
import { RectangleStackIcon, PlusIcon } from "@heroicons/react/24/outline"; import { RectangleStackIcon, PlusIcon } from "@heroicons/react/24/outline";
@ -23,6 +23,8 @@ import type { UserAuth } from "types";
import type { GetServerSidePropsContext, NextPage } from "next"; import type { GetServerSidePropsContext, NextPage } from "next";
// fetch-keys // fetch-keys
import { PROJECT_DETAILS, PROJECT_ISSUES_LIST } from "constants/fetch-keys"; import { PROJECT_DETAILS, PROJECT_ISSUES_LIST } from "constants/fetch-keys";
// image
import emptyIssue from "public/empty-state/empty-issue.svg";
const ProjectIssues: NextPage<UserAuth> = (props) => { const ProjectIssues: NextPage<UserAuth> = (props) => {
const router = useRouter(); const router = useRouter();
@ -76,30 +78,13 @@ const ProjectIssues: NextPage<UserAuth> = (props) => {
userAuth={props} userAuth={props}
/> />
) : ( ) : (
<div className="grid h-full w-full place-items-center px-4 sm:px-0"> <EmptyState
<EmptySpace type="issue"
title="You don't have any issue yet." title="Create New Issue"
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." 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.
Icon={RectangleStackIcon} Create a new issue"
> imgURL={emptyIssue}
<EmptySpaceItem />
title="Create a new issue"
description={
<span>
Use <pre className="inline rounded bg-gray-200 px-2 py-1">C</pre> shortcut to
create a new issue
</span>
}
Icon={PlusIcon}
action={() => {
const e = new KeyboardEvent("keydown", {
key: "c",
});
document.dispatchEvent(e);
}}
/>
</EmptySpace>
</div>
) )
) : ( ) : (
<div className="flex h-full w-full items-center justify-center"> <div className="flex h-full w-full items-center justify-center">

View File

@ -2,7 +2,10 @@ import React, { useEffect, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import useSWR from "swr"; import useSWR from "swr";
import { PlusIcon, RectangleGroupIcon } from "@heroicons/react/24/outline"; import { PlusIcon } from "@heroicons/react/24/outline";
// image
import emptyModule from "public/empty-state/empty-module.svg";
// layouts // layouts
import AppLayout from "layouts/app-layout"; import AppLayout from "layouts/app-layout";
@ -14,7 +17,7 @@ import modulesService from "services/modules.service";
// components // components
import { CreateUpdateModuleModal, SingleModuleCard } from "components/modules"; import { CreateUpdateModuleModal, SingleModuleCard } from "components/modules";
// ui // ui
import { EmptySpace, EmptySpaceItem, HeaderButton, Loader } from "components/ui"; import { EmptyState, HeaderButton, Loader } from "components/ui";
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs"; import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
// icons // icons
// types // types
@ -103,30 +106,12 @@ const ProjectModules: NextPage = () => {
</div> </div>
</div> </div>
) : ( ) : (
<div className="flex h-full w-full flex-col items-center justify-center px-4"> <EmptyState
<EmptySpace type="module"
title="You don't have any module yet." title="Create New Module"
description="Modules are smaller, focused projects that help you group and organize issues within a specific time frame." description="Modules are smaller, focused projects that help you group and organize issues within a specific time frame."
Icon={RectangleGroupIcon} imgURL={emptyModule}
> />
<EmptySpaceItem
title="Create a new module"
description={
<span>
Use <pre className="inline rounded bg-gray-200 px-2 py-1">M</pre> shortcut to
create a new module
</span>
}
Icon={PlusIcon}
action={() => {
const e = new KeyboardEvent("keydown", {
key: "m",
});
document.dispatchEvent(e);
}}
/>
</EmptySpace>
</div>
) )
) : ( ) : (
<Loader className="grid grid-cols-3 gap-4"> <Loader className="grid grid-cols-3 gap-4">

View File

@ -14,14 +14,16 @@ import AppLayout from "layouts/app-layout";
import { JoinProjectModal } from "components/project/join-project-modal"; import { JoinProjectModal } from "components/project/join-project-modal";
import { DeleteProjectModal, SingleProjectCard } from "components/project"; import { DeleteProjectModal, SingleProjectCard } from "components/project";
// ui // ui
import { HeaderButton, EmptySpace, EmptySpaceItem, Loader } from "components/ui"; import { HeaderButton, Loader, EmptyState } from "components/ui";
import { Breadcrumbs, BreadcrumbItem } from "components/breadcrumbs"; import { Breadcrumbs, BreadcrumbItem } from "components/breadcrumbs";
// icons // icons
import { ClipboardDocumentListIcon, PlusIcon } from "@heroicons/react/24/outline"; import { PlusIcon } from "@heroicons/react/24/outline";
// types // types
import type { GetServerSidePropsContext, NextPage } from "next"; import type { GetServerSidePropsContext, NextPage } from "next";
// fetch-keys // fetch-keys
import { PROJECT_MEMBERS } from "constants/fetch-keys"; import { PROJECT_MEMBERS } from "constants/fetch-keys";
// image
import emptyProject from "public/empty-state/empty-project.svg";
const ProjectsPage: NextPage = () => { const ProjectsPage: NextPage = () => {
// router // router
@ -80,28 +82,12 @@ const ProjectsPage: NextPage = () => {
{projects ? ( {projects ? (
<> <>
{projects.length === 0 ? ( {projects.length === 0 ? (
<div className="grid h-full w-full place-items-center px-4 sm:px-0"> <EmptyState
<EmptySpace type="project"
title="You don't have any project yet." title="Create New Project"
description="Projects are a collection of issues. They can be used to represent the development work for a product, project, or service." description="Projects are a collection of issues. They can be used to represent the development work for a product, project, or service."
Icon={ClipboardDocumentListIcon} imgURL={emptyProject}
> />
<EmptySpaceItem
title="Create a new project"
description={
<span>
Use <pre className="inline rounded bg-gray-200 px-2 py-1">P</pre> shortcut to
create a new project
</span>
}
Icon={PlusIcon}
action={() => {
const e = new KeyboardEvent("keydown", { key: "p" });
document.dispatchEvent(e);
}}
/>
</EmptySpace>
</div>
) : ( ) : (
<div className="grid grid-cols-1 gap-9 md:grid-cols-2 lg:grid-cols-3"> <div className="grid grid-cols-1 gap-9 md:grid-cols-2 lg:grid-cols-3">
{projects.map((project) => ( {projects.map((project) => (

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 19 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 24 KiB