style: new workspace dashboard design (#454)
* style: workspace dashboard * feat: activity graph
This commit is contained in:
parent
8370511a66
commit
96ad751e11
18 changed files with 654 additions and 284 deletions
|
|
@ -1,6 +1,5 @@
|
|||
import React from "react";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
// lib
|
||||
|
|
@ -8,227 +7,74 @@ import { requiredAuth } from "lib/auth";
|
|||
// layouts
|
||||
import AppLayout from "layouts/app-layout";
|
||||
// hooks
|
||||
import useProjects from "hooks/use-projects";
|
||||
import useWorkspaceDetails from "hooks/use-workspace-details";
|
||||
import useIssues from "hooks/use-issues";
|
||||
// components
|
||||
import { WorkspaceHomeCardsList, WorkspaceHomeGreetings } from "components/workspace";
|
||||
// ui
|
||||
import { Spinner, Tooltip } from "components/ui";
|
||||
// icons
|
||||
import {
|
||||
ArrowRightIcon,
|
||||
CalendarDaysIcon,
|
||||
ClipboardDocumentListIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { LayerDiagonalIcon } from "components/icons";
|
||||
import { getPriorityIcon } from "components/icons/priority-icon";
|
||||
CompletedIssuesGraph,
|
||||
IssuesList,
|
||||
IssuesPieChart,
|
||||
IssuesStats,
|
||||
} from "components/workspace";
|
||||
// helpers
|
||||
import { renderShortNumericDateFormat, findHowManyDaysLeft } from "helpers/date-time.helper";
|
||||
import { addSpaceIfCamelCase } from "helpers/string.helper";
|
||||
import { groupBy } from "helpers/array.helper";
|
||||
import { orderArrayBy } from "helpers/array.helper";
|
||||
// types
|
||||
import type { NextPage, GetServerSidePropsContext } from "next";
|
||||
|
||||
const WorkspacePage: NextPage = () => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
// API Fetching
|
||||
const { myIssues } = useIssues(workspaceSlug?.toString());
|
||||
const { projects, recentProjects } = useProjects();
|
||||
const {} = useWorkspaceDetails();
|
||||
|
||||
const groupedIssues = {
|
||||
backlog: [],
|
||||
unstarted: [],
|
||||
started: [],
|
||||
cancelled: [],
|
||||
completed: [],
|
||||
...groupBy(myIssues ?? [], "state_detail.group"),
|
||||
};
|
||||
const { myIssues } = useIssues(workspaceSlug as string);
|
||||
|
||||
const overdueIssues = orderArrayBy(
|
||||
myIssues?.filter(
|
||||
(i) =>
|
||||
i.target_date &&
|
||||
i.target_date !== "" &&
|
||||
!i.completed_at &&
|
||||
new Date(i.target_date) < new Date()
|
||||
) ?? [],
|
||||
"target_date",
|
||||
"descending"
|
||||
);
|
||||
|
||||
const incomingIssues = orderArrayBy(
|
||||
myIssues?.filter(
|
||||
(i) => i.target_date && i.target_date !== "" && new Date(i.target_date) > new Date()
|
||||
) ?? [],
|
||||
"target_date"
|
||||
);
|
||||
|
||||
return (
|
||||
<AppLayout noHeader={true}>
|
||||
<div className="h-full w-full space-y-5">
|
||||
<div className="flex flex-col gap-5">
|
||||
<WorkspaceHomeGreetings />
|
||||
<WorkspaceHomeCardsList
|
||||
groupedIssues={groupedIssues}
|
||||
myIssues={myIssues}
|
||||
projects={projects}
|
||||
/>
|
||||
{/** TODO: Convert the below mentioned HTML Content to separate component */}
|
||||
<div className="grid grid-cols-3 gap-5">
|
||||
<div className="col-span-2 rounded-lg border bg-white">
|
||||
<div className="max-h-[30rem] w-full overflow-y-auto shadow-sm">
|
||||
{myIssues ? (
|
||||
myIssues.length > 0 ? (
|
||||
<div className="flex flex-col space-y-5">
|
||||
<div className="rounded-lg bg-white">
|
||||
<div className="rounded-t-lg bg-gray-100 px-4 py-3">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<LayerDiagonalIcon className="h-5 w-5" color="gray" />
|
||||
<h2 className="font-medium leading-5">My Issues</h2>
|
||||
<p className="text-sm text-gray-500">{myIssues.length}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="divide-y-2">
|
||||
{myIssues.map((issue) => (
|
||||
<div
|
||||
key={issue.id}
|
||||
className="flex items-center justify-between gap-2 rounded px-4 py-3 text-sm"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<span
|
||||
className={`block h-1.5 w-1.5 flex-shrink-0 rounded-full`}
|
||||
style={{
|
||||
backgroundColor: issue.state_detail.color,
|
||||
}}
|
||||
/>
|
||||
<Link
|
||||
href={`/${workspaceSlug}/projects/${issue.project}/issues/${issue.id}`}
|
||||
>
|
||||
<a className="group relative flex items-center gap-2">
|
||||
<Tooltip
|
||||
position="top-left"
|
||||
tooltipHeading="Title"
|
||||
tooltipContent={issue.name}
|
||||
>
|
||||
<span className="w-auto max-w-[225px] md:max-w-[175px] lg:max-w-xs xl:max-w-sm text-ellipsis overflow-hidden whitespace-nowrap">
|
||||
{issue.name}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex flex-shrink-0 flex-wrap items-center gap-x-1 gap-y-2 text-xs">
|
||||
<Tooltip
|
||||
tooltipHeading="Priority"
|
||||
tooltipContent={issue.priority ?? "None"}
|
||||
>
|
||||
<div
|
||||
className={`cursor-pointer rounded px-2 py-1 capitalize shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 ${
|
||||
issue.priority === "urgent"
|
||||
? "bg-red-100 text-red-600"
|
||||
: issue.priority === "high"
|
||||
? "bg-orange-100 text-orange-500"
|
||||
: issue.priority === "medium"
|
||||
? "bg-yellow-100 text-yellow-500"
|
||||
: issue.priority === "low"
|
||||
? "bg-green-100 text-green-500"
|
||||
: "bg-gray-100"
|
||||
}`}
|
||||
>
|
||||
{getPriorityIcon(issue.priority)}
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
tooltipHeading="State"
|
||||
tooltipContent={addSpaceIfCamelCase(issue.state_detail.name)}
|
||||
>
|
||||
<div className="flex cursor-pointer items-center gap-1 rounded border px-2 py-1 text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500">
|
||||
<span
|
||||
className="h-1.5 w-1.5 flex-shrink-0 rounded-full"
|
||||
style={{
|
||||
backgroundColor: issue.state_detail.color,
|
||||
}}
|
||||
/>
|
||||
|
||||
<span>{addSpaceIfCamelCase(issue.state_detail.name)}</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
tooltipHeading="Due Date"
|
||||
tooltipContent={
|
||||
issue.target_date
|
||||
? renderShortNumericDateFormat(issue.target_date)
|
||||
: "N/A"
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={`flex flex-shrink-0 cursor-pointer items-center gap-1 rounded border px-2 py-1 text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 ${
|
||||
issue.target_date === null
|
||||
? ""
|
||||
: issue.target_date < new Date().toISOString()
|
||||
? "text-red-600"
|
||||
: findHowManyDaysLeft(issue.target_date) <= 3 &&
|
||||
"text-orange-400"
|
||||
}`}
|
||||
>
|
||||
<CalendarDaysIcon className="h-4 w-4" />
|
||||
{issue.target_date
|
||||
? renderShortNumericDateFormat(issue.target_date)
|
||||
: "N/A"}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center gap-4 px-3 py-8 text-center">
|
||||
<LayerDiagonalIcon height="56" width="56" />
|
||||
<h3 className="text-gray-500">
|
||||
No issues found. Create a new issue with{" "}
|
||||
<pre className="inline rounded bg-gray-200 px-2 py-1">C</pre>.
|
||||
</h3>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<div className="flex items-center justify-center p-10">
|
||||
<Spinner />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-lg border bg-white">
|
||||
<div className="flex items-center gap-2 rounded-t-lg bg-gray-100 px-4 py-3">
|
||||
<ClipboardDocumentListIcon className="h-4 w-4 text-gray-500" />
|
||||
<h2 className="font-medium leading-5">Recent Projects</h2>
|
||||
</div>
|
||||
<div className="space-y-5 p-4">
|
||||
{recentProjects && workspaceSlug ? (
|
||||
recentProjects.length > 0 ? (
|
||||
recentProjects.map((project) => (
|
||||
<Link
|
||||
href={`/${workspaceSlug}/projects/${project.id}/issues`}
|
||||
key={project.id}
|
||||
>
|
||||
<a className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
{project.icon ? (
|
||||
<span className="grid flex-shrink-0 place-items-center rounded uppercase">
|
||||
{String.fromCodePoint(parseInt(project.icon))}
|
||||
</span>
|
||||
) : (
|
||||
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white">
|
||||
{project?.name.charAt(0)}
|
||||
</span>
|
||||
)}
|
||||
<h3 className="w-[150px] lg:w-[225px] text-ellipsis overflow-hidden">
|
||||
{project.name}
|
||||
</h3>
|
||||
</div>
|
||||
<div className="text-gray-400">
|
||||
<ArrowRightIcon className="h-4 w-4" />
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
))
|
||||
) : (
|
||||
<p className="text-gray-500">No projects has been create for this workspace.</p>
|
||||
)
|
||||
) : (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<Spinner />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="h-full w-full">
|
||||
<div className="flex flex-col gap-8">
|
||||
<div
|
||||
className="flex flex-col justify-between gap-x-2 gap-y-6 rounded-lg px-8 py-6 text-white md:flex-row md:items-center md:py-3"
|
||||
style={{ background: "linear-gradient(90deg, #8e2de2 0%, #4a00e0 100%)" }}
|
||||
>
|
||||
<p>Plane is a open source application, to support us you can star us on GitHub!</p>
|
||||
<div className="flex items-center gap-2">
|
||||
{/* <a href="#" target="_blank" rel="noopener noreferrer">
|
||||
View roadmap
|
||||
</a> */}
|
||||
<a
|
||||
href="https://github.com/makeplane/plane"
|
||||
target="_blank"
|
||||
className="rounded-md border-2 border-white px-3 py-1.5 text-sm duration-300 hover:bg-white hover:text-[#4a00e0]"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Star us on GitHub
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<IssuesStats issues={myIssues} />
|
||||
<div className="grid grid-cols-1 gap-8 lg:grid-cols-2">
|
||||
<IssuesList issues={overdueIssues} type="overdue" />
|
||||
<IssuesList issues={incomingIssues} type="upcoming" />
|
||||
<IssuesPieChart issues={myIssues} />
|
||||
<CompletedIssuesGraph issues={myIssues} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AppLayout>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue