plane/apps/app/pages/[workspaceSlug]/index.tsx
Anmol Singh Bhatia 4caa4e33b1
fix: ui improvements (#327)
* fix: kanban board header scroll fix

* style: enable scrollbar style added

* fix: emoji picker overflow

* fix: delete project modal text overflow

* fix: cycle card ellipsis

* fix: tooltip position updated and custom class added

* fix: assignees tooltip overflow

* fix: module card

* fix: my issue page  tooltip and responsive title  added

* fix: home page tooltip and responsiveness
2023-02-23 16:46:52 +05:30

260 lines
12 KiB
TypeScript

import React from "react";
import Link from "next/link";
import { useRouter } from "next/router";
// lib
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";
// helpers
import { renderShortNumericDateFormat, findHowManyDaysLeft } from "helpers/date-time.helper";
import { addSpaceIfCamelCase } from "helpers/string.helper";
import { groupBy } 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"),
};
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>
</div>
</div>
</div>
</AppLayout>
);
};
export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
const user = await requiredAuth(ctx.req?.headers.cookie);
const redirectAfterSignIn = ctx.resolvedUrl;
if (!user) {
return {
redirect: {
destination: `/signin?next=${redirectAfterSignIn}`,
permanent: false,
},
};
}
return {
props: {
user,
},
};
};
export default WorkspacePage;