chore: update empty states, fix: delete issue modal (#1743)

* chore: update empty states

* fix: delete issue modal

---------

Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com>
This commit is contained in:
Nikhil 2023-08-01 13:58:58 +05:30 committed by GitHub
parent e48147f87e
commit 4e297d92f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 206 additions and 116 deletions

View File

@ -21,9 +21,9 @@ import {
GanttChartView, GanttChartView,
} from "components/core"; } from "components/core";
// ui // ui
import { EmptyState, SecondaryButton, Spinner } from "components/ui"; import { EmptyState, Spinner } from "components/ui";
// icons // icons
import { PlusIcon, TrashIcon } from "@heroicons/react/24/outline"; import { TrashIcon } from "@heroicons/react/24/outline";
// images // images
import emptyIssue from "public/empty-state/issue.svg"; import emptyIssue from "public/empty-state/issue.svg";
import emptyIssueArchive from "public/empty-state/issue-archive.svg"; import emptyIssueArchive from "public/empty-state/issue-archive.svg";
@ -39,6 +39,16 @@ type Props = {
addIssueToGroup: (groupTitle: string) => void; addIssueToGroup: (groupTitle: string) => void;
disableUserActions: boolean; disableUserActions: boolean;
dragDisabled?: boolean; dragDisabled?: boolean;
emptyState: {
title: string;
description?: string;
primaryButton?: {
icon: any;
text: string;
onClick: () => void;
};
secondaryButton?: React.ReactNode;
};
handleIssueAction: (issue: IIssue, action: "copy" | "delete" | "edit") => void; handleIssueAction: (issue: IIssue, action: "copy" | "delete" | "edit") => void;
handleOnDragEnd: (result: DropResult) => Promise<void>; handleOnDragEnd: (result: DropResult) => Promise<void>;
openIssuesListModal: (() => void) | null; openIssuesListModal: (() => void) | null;
@ -53,6 +63,7 @@ export const AllViews: React.FC<Props> = ({
addIssueToGroup, addIssueToGroup,
disableUserActions, disableUserActions,
dragDisabled = false, dragDisabled = false,
emptyState,
handleIssueAction, handleIssueAction,
handleOnDragEnd, handleOnDragEnd,
openIssuesListModal, openIssuesListModal,
@ -156,41 +167,28 @@ export const AllViews: React.FC<Props> = ({
title="Archived Issues will be shown here" title="Archived Issues will be shown here"
description="All the issues that have been in the completed or canceled groups for the configured period of time can be viewed here." description="All the issues that have been in the completed or canceled groups for the configured period of time can be viewed here."
image={emptyIssueArchive} image={emptyIssueArchive}
buttonText="Go to Automation Settings" primaryButton={{
onClick={() => { text: "Go to Automation Settings",
router.push(`/${workspaceSlug}/projects/${projectId}/settings/automations`); onClick: () => {
router.push(`/${workspaceSlug}/projects/${projectId}/settings/automations`);
},
}} }}
/> />
) : ( ) : (
<EmptyState <EmptyState
title={ title={emptyState.title}
cycleId description={emptyState.description}
? "Cycle issues will appear here"
: moduleId
? "Module issues will appear here"
: "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."
image={emptyIssue} image={emptyIssue}
buttonText="New Issue" primaryButton={
buttonIcon={<PlusIcon className="h-4 w-4" />} emptyState.primaryButton
secondaryButton={ ? {
cycleId || moduleId ? ( icon: emptyState.primaryButton.icon,
<SecondaryButton text: emptyState.primaryButton.text,
className="flex items-center gap-1.5" onClick: emptyState.primaryButton.onClick,
onClick={openIssuesListModal ?? (() => {})} }
> : undefined
<PlusIcon className="h-4 w-4" />
Add an existing issue
</SecondaryButton>
) : null
} }
onClick={() => { secondaryButton={emptyState.secondaryButton}
const e = new KeyboardEvent("keydown", {
key: "c",
});
document.dispatchEvent(e);
}}
/> />
) )
) : ( ) : (

View File

@ -22,7 +22,7 @@ import { FiltersList, AllViews } from "components/core";
import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues"; import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues";
import { CreateUpdateViewModal } from "components/views"; import { CreateUpdateViewModal } from "components/views";
// ui // ui
import { PrimaryButton } from "components/ui"; import { PrimaryButton, SecondaryButton } from "components/ui";
// icons // icons
import { PlusIcon } from "@heroicons/react/24/outline"; import { PlusIcon } from "@heroicons/react/24/outline";
// helpers // helpers
@ -515,6 +515,35 @@ export const IssuesView: React.FC<Props> = ({
selectedGroup === "labels" || selectedGroup === "labels" ||
selectedGroup === "state_detail.group" selectedGroup === "state_detail.group"
} }
emptyState={{
title: cycleId
? "Cycle issues will appear here"
: moduleId
? "Module issues will appear here"
: "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.",
primaryButton: {
icon: <PlusIcon className="h-4 w-4" />,
text: "New Issue",
onClick: () => {
const e = new KeyboardEvent("keydown", {
key: "c",
});
document.dispatchEvent(e);
},
},
secondaryButton:
cycleId || moduleId ? (
<SecondaryButton
className="flex items-center gap-1.5"
onClick={openIssuesListModal ?? (() => {})}
>
<PlusIcon className="h-4 w-4" />
Add an existing issue
</SecondaryButton>
) : null,
}}
handleOnDragEnd={handleOnDragEnd} handleOnDragEnd={handleOnDragEnd}
handleIssueAction={handleIssueAction} handleIssueAction={handleIssueAction}
openIssuesListModal={openIssuesListModal ? openIssuesListModal : null} openIssuesListModal={openIssuesListModal ? openIssuesListModal : null}

View File

@ -59,8 +59,9 @@ export const DeleteIssueModal: React.FC<Props> = ({ isOpen, handleClose, data, u
}; };
const handleDeletion = async () => { const handleDeletion = async () => {
if (!workspaceSlug || !data) return;
setIsDeleteLoading(true); setIsDeleteLoading(true);
if (!workspaceSlug || !projectId || !data) return;
await issueServices await issueServices
.deleteIssue(workspaceSlug as string, data.project, data.id, user) .deleteIssue(workspaceSlug as string, data.project, data.id, user)
@ -112,7 +113,7 @@ export const DeleteIssueModal: React.FC<Props> = ({ isOpen, handleClose, data, u
} else { } else {
if (cycleId) mutate(CYCLE_ISSUES_WITH_PARAMS(cycleId as string, params)); if (cycleId) mutate(CYCLE_ISSUES_WITH_PARAMS(cycleId as string, params));
else if (moduleId) mutate(MODULE_ISSUES_WITH_PARAMS(moduleId as string, params)); else if (moduleId) mutate(MODULE_ISSUES_WITH_PARAMS(moduleId as string, params));
else mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(projectId as string, params)); else mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(data.project, params));
} }
handleClose(); handleClose();

View File

@ -21,6 +21,7 @@ import { orderArrayBy } from "helpers/array.helper";
import { IIssue, IIssueFilterOptions } from "types"; import { IIssue, IIssueFilterOptions } from "types";
// fetch-keys // fetch-keys
import { USER_ISSUES, WORKSPACE_LABELS } from "constants/fetch-keys"; import { USER_ISSUES, WORKSPACE_LABELS } from "constants/fetch-keys";
import { PlusIcon } from "@heroicons/react/24/outline";
type Props = { type Props = {
openIssuesListModal?: () => void; openIssuesListModal?: () => void;
@ -262,6 +263,20 @@ export const MyIssuesView: React.FC<Props> = ({
addIssueToGroup={addIssueToGroup} addIssueToGroup={addIssueToGroup}
disableUserActions={disableUserActions} disableUserActions={disableUserActions}
dragDisabled={groupBy !== "priority"} dragDisabled={groupBy !== "priority"}
emptyState={{
title: "You don't have any issue assigned to you yet",
description: "Keep track of your work in a single place.",
primaryButton: {
icon: <PlusIcon className="h-4 w-4" />,
text: "New Issue",
onClick: () => {
const e = new KeyboardEvent("keydown", {
key: "c",
});
document.dispatchEvent(e);
},
},
}}
handleOnDragEnd={handleOnDragEnd} handleOnDragEnd={handleOnDragEnd}
handleIssueAction={handleIssueAction} handleIssueAction={handleIssueAction}
openIssuesListModal={openIssuesListModal ? openIssuesListModal : null} openIssuesListModal={openIssuesListModal ? openIssuesListModal : null}

View File

@ -56,13 +56,15 @@ export const RecentPagesList: React.FC<TPagesListProps> = ({ viewType }) => {
title="Have your thoughts in place" title="Have your thoughts in place"
description="You can think of Pages as an AI-powered notepad." description="You can think of Pages as an AI-powered notepad."
image={emptyPage} image={emptyPage}
buttonText="New Page" primaryButton={{
buttonIcon={<PlusIcon className="h-4 w-4" />} icon: <PlusIcon className="h-4 w-4" />,
onClick={() => { text: "New Page",
const e = new KeyboardEvent("keydown", { onClick: () => {
key: "d", const e = new KeyboardEvent("keydown", {
}); key: "d",
document.dispatchEvent(e); });
document.dispatchEvent(e);
},
}} }}
/> />
) )

View File

@ -260,13 +260,15 @@ export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
title="Have your thoughts in place" title="Have your thoughts in place"
description="You can think of Pages as an AI-powered notepad." description="You can think of Pages as an AI-powered notepad."
image={emptyPage} image={emptyPage}
buttonText="New Page" primaryButton={{
buttonIcon={<PlusIcon className="h-4 w-4" />} icon: <PlusIcon className="h-4 w-4" />,
onClick={() => { text: "New Page",
const e = new KeyboardEvent("keydown", { onClick: () => {
key: "d", const e = new KeyboardEvent("keydown", {
}); key: "d",
document.dispatchEvent(e); });
document.dispatchEvent(e);
},
}} }}
/> />
)} )}

View File

@ -1,4 +1,4 @@
import { useCallback, useEffect, useState } from "react"; import { useCallback, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
@ -140,10 +140,26 @@ export const ProfileIssuesView = () => {
] ]
); );
const addIssueToGroup = useCallback((groupTitle: string) => { const addIssueToGroup = useCallback(
setCreateIssueModal(true); (groupTitle: string) => {
return; setCreateIssueModal(true);
}, []);
let preloadedValue: string | string[] = groupTitle;
if (groupByProperty === "labels") {
if (groupTitle === "None") preloadedValue = [];
else preloadedValue = [groupTitle];
}
if (groupByProperty)
setPreloadedData({
[groupByProperty]: preloadedValue,
actionType: "createIssue",
});
else setPreloadedData({ actionType: "createIssue" });
},
[setCreateIssueModal, setPreloadedData, groupByProperty]
);
const addIssueToDate = useCallback( const addIssueToDate = useCallback(
(date: string) => { (date: string) => {
@ -250,6 +266,13 @@ export const ProfileIssuesView = () => {
addIssueToGroup={addIssueToGroup} addIssueToGroup={addIssueToGroup}
disableUserActions={false} disableUserActions={false}
dragDisabled={groupByProperty !== "priority"} dragDisabled={groupByProperty !== "priority"}
emptyState={{
title: router.pathname.includes("assigned")
? `Issues assigned to ${user?.first_name} ${user?.last_name} will appear here`
: router.pathname.includes("created")
? `Issues created by ${user?.first_name} ${user?.last_name} will appear here`
: `Issues subscribed by ${user?.first_name} ${user?.last_name} will appear here`,
}}
handleOnDragEnd={handleOnDragEnd} handleOnDragEnd={handleOnDragEnd}
handleIssueAction={handleIssueAction} handleIssueAction={handleIssueAction}
openIssuesListModal={null} openIssuesListModal={null}

View File

@ -9,10 +9,12 @@ type Props = {
title: string; title: string;
description: React.ReactNode | string; description: React.ReactNode | string;
image: any; image: any;
buttonText?: string; primaryButton?: {
buttonIcon?: any; icon?: any;
text: string;
onClick: () => void;
};
secondaryButton?: React.ReactNode; secondaryButton?: React.ReactNode;
onClick?: () => void;
isFullScreen?: boolean; isFullScreen?: boolean;
}; };
@ -20,9 +22,7 @@ export const EmptyState: React.FC<Props> = ({
title, title,
description, description,
image, image,
onClick, primaryButton,
buttonText,
buttonIcon,
secondaryButton, secondaryButton,
isFullScreen = true, isFullScreen = true,
}) => ( }) => (
@ -32,14 +32,14 @@ export const EmptyState: React.FC<Props> = ({
}`} }`}
> >
<div className="text-center flex flex-col items-center w-full"> <div className="text-center flex flex-col items-center w-full">
<Image src={image} className="w-52 sm:w-60" alt={buttonText} /> <Image src={image} className="w-52 sm:w-60" alt={primaryButton?.text} />
<h6 className="text-xl font-semibold mt-6 sm:mt-8 mb-3">{title}</h6> <h6 className="text-xl font-semibold mt-6 sm:mt-8 mb-3">{title}</h6>
<p className="text-custom-text-300 mb-7 sm:mb-8">{description}</p> <p className="text-custom-text-300 mb-7 sm:mb-8">{description}</p>
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
{buttonText && ( {primaryButton && (
<PrimaryButton className="flex items-center gap-1.5" onClick={onClick}> <PrimaryButton className="flex items-center gap-1.5" onClick={primaryButton.onClick}>
{buttonIcon} {primaryButton.icon}
{buttonText} {primaryButton.text}
</PrimaryButton> </PrimaryButton>
)} )}
{secondaryButton} {secondaryButton}

View File

@ -71,12 +71,14 @@ const ProjectAuthorizationWrapped: React.FC<Props> = ({
title="No such project exists" title="No such project exists"
description="Try creating a new project" description="Try creating a new project"
image={emptyProject} image={emptyProject}
buttonText="Create Project" primaryButton={{
onClick={() => { text: "Create Project",
const e = new KeyboardEvent("keydown", { onClick: () => {
key: "p", const e = new KeyboardEvent("keydown", {
}); key: "p",
document.dispatchEvent(e); });
document.dispatchEvent(e);
},
}} }}
/> />
</div> </div>

View File

@ -134,13 +134,15 @@ const Analytics = () => {
title="You can see your all projects' analytics here" title="You can see your all projects' analytics here"
description="Let's create your first project and analyse the stats with various graphs." description="Let's create your first project and analyse the stats with various graphs."
image={emptyAnalytics} image={emptyAnalytics}
buttonText="New Project" primaryButton={{
buttonIcon={<PlusIcon className="h-4 w-4" />} icon: <PlusIcon className="h-4 w-4" />,
onClick={() => { text: "New Project",
const e = new KeyboardEvent("keydown", { onClick: () => {
key: "p", const e = new KeyboardEvent("keydown", {
}); key: "p",
document.dispatchEvent(e); });
document.dispatchEvent(e);
},
}} }}
/> />
) )

View File

@ -121,13 +121,15 @@ const ProjectCycles: NextPage = () => {
title="Plan your project with cycles" title="Plan your project with cycles"
description="Cycle is a custom time period in which a team works to complete items on their backlog." description="Cycle is a custom time period in which a team works to complete items on their backlog."
image={emptyCycle} image={emptyCycle}
buttonText="New Cycle" primaryButton={{
buttonIcon={<PlusIcon className="h-4 w-4" />} icon: <PlusIcon className="h-4 w-4" />,
onClick={() => { text: "New Cycle",
const e = new KeyboardEvent("keydown", { onClick: () => {
key: "q", const e = new KeyboardEvent("keydown", {
}); key: "q",
document.dispatchEvent(e); });
document.dispatchEvent(e);
},
}} }}
/> />
</div> </div>

View File

@ -146,13 +146,15 @@ const ProjectModules: NextPage = () => {
title="Manage your project with modules" title="Manage your project with modules"
description="Modules are smaller, focused projects that help you group and organize issues." description="Modules are smaller, focused projects that help you group and organize issues."
image={emptyModule} image={emptyModule}
buttonText="New Module" primaryButton={{
buttonIcon={<PlusIcon className="h-4 w-4" />} icon: <PlusIcon className="h-4 w-4" />,
onClick={() => { text: "New Module",
const e = new KeyboardEvent("keydown", { onClick: () => {
key: "m", const e = new KeyboardEvent("keydown", {
}); key: "m",
document.dispatchEvent(e); });
document.dispatchEvent(e);
},
}} }}
/> />
) )

View File

@ -166,11 +166,13 @@ const EstimatesSettings: NextPage = () => {
title="No estimates yet" title="No estimates yet"
description="Estimates help you communicate the complexity of an issue." description="Estimates help you communicate the complexity of an issue."
image={emptyEstimate} image={emptyEstimate}
buttonText="Add Estimate" primaryButton={{
buttonIcon={<PlusIcon className="h-4 w-4" />} icon: <PlusIcon className="h-4 w-4" />,
onClick={() => { text: "Add Estimate",
setEstimateToUpdate(undefined); onClick: () => {
setEstimateFormOpen(true); setEstimateToUpdate(undefined);
setEstimateFormOpen(true);
},
}} }}
/> />
</div> </div>

View File

@ -78,8 +78,10 @@ const ProjectIntegrations: NextPage = () => {
title="You haven't configured integrations" title="You haven't configured integrations"
description="Configure GitHub and other integrations to sync your project issues." description="Configure GitHub and other integrations to sync your project issues."
image={emptyIntegration} image={emptyIntegration}
buttonText="Configure now" primaryButton={{
onClick={() => router.push(`/${workspaceSlug}/settings/integrations`)} text: "Configure now",
onClick: () => router.push(`/${workspaceSlug}/settings/integrations`),
}}
/> />
) )
) : ( ) : (

View File

@ -181,8 +181,10 @@ const LabelsSettings: NextPage = () => {
title="No labels yet" title="No labels yet"
description="Create labels to help organize and filter issues in you project" description="Create labels to help organize and filter issues in you project"
image={emptyLabel} image={emptyLabel}
buttonText="Add label" primaryButton={{
onClick={newLabel} text: "Add label",
onClick: () => newLabel(),
}}
isFullScreen={false} isFullScreen={false}
/> />
) )

View File

@ -118,13 +118,15 @@ const ProjectViews: NextPage = () => {
title="Get focused with views" title="Get focused with views"
description="Views aid in saving your issues by applying various filters and grouping options." description="Views aid in saving your issues by applying various filters and grouping options."
image={emptyView} image={emptyView}
buttonText="New View" primaryButton={{
buttonIcon={<PlusIcon className="h-4 w-4" />} icon: <PlusIcon className="h-4 w-4" />,
onClick={() => { text: "New View",
const e = new KeyboardEvent("keydown", { onClick: () => {
key: "v", const e = new KeyboardEvent("keydown", {
}); key: "v",
document.dispatchEvent(e); });
document.dispatchEvent(e);
},
}} }}
/> />
) )

View File

@ -111,13 +111,15 @@ const ProjectsPage: NextPage = () => {
image={emptyProject} image={emptyProject}
title="No projects yet" title="No projects yet"
description="Get started by creating your first project" description="Get started by creating your first project"
buttonText="New Project" primaryButton={{
buttonIcon={<PlusIcon className="h-4 w-4" />} icon: <PlusIcon className="h-4 w-4" />,
onClick={() => { text: "New Project",
const e = new KeyboardEvent("keydown", { onClick: () => {
key: "p", const e = new KeyboardEvent("keydown", {
}); key: "p",
document.dispatchEvent(e); });
document.dispatchEvent(e);
},
}} }}
/> />
)} )}

View File

@ -186,8 +186,10 @@ const OnBoard: NextPage = () => {
title="No pending invites" title="No pending invites"
description="You can see here if someone invites you to a workspace." description="You can see here if someone invites you to a workspace."
image={emptyInvitation} image={emptyInvitation}
buttonText="Back to Dashboard" primaryButton={{
onClick={() => router.push("/")} text: "Back to Dashboard",
onClick: () => router.push("/"),
}}
/> />
</div> </div>
) )