mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
9075f9441c
* style: added cta at the bottom of sidebar, added missing icons as well, showing dynamic workspace member count on workspace dropdown * refractor: running parallel request, made create/edit label function to async function * fix: sidebar dropdown content going below kanban items outside click detection in need help dropdown * refractor: making parallel api calls fix: create state input comes at bottom, create state input gets on focus automatically, form is getting submitted on enter click * refactoring file structure and signin page * style: changed text and added spinner for signing in loading * refractor: removed unused type * fix: my issue cta in profile page sending to 404 page * fix: added new s3 bucket url in next.config.js file increased image modal height * packaging UI components * eslint config * eslint fixes * refactoring changes * build fixes * minor fixes * adding todo comments for reference * refactor: cleared unused imports and re ordered imports * refactor: removed unused imports * fix: added workspace argument to useissues hook * refactor: removed api-routes file, unnecessary constants * refactor: created helpers folder, removed unnecessary constants * refactor: new context for issue view * refactoring issues page * build fixes * refactoring * refactor: create issue modal * refactor: module ui * fix: sub-issues mutation * fix: create more option in create issue modal * description form debounce issue * refactor: global component for assignees list * fix: link module interface * fix: priority icons and sub-issues count added * fix: cycle mutation in issue details page * fix: remove issue from cycle mutation * fix: create issue modal in home page * fix: removed unnecessary props * fix: updated create issue form status * fix: settings auth breaking * refactor: issue details page Co-authored-by: Dakshesh Jain <dakshesh.jain14@gmail.com> Co-authored-by: Dakshesh Jain <65905942+dakshesh14@users.noreply.github.com> Co-authored-by: venkatesh-soulpage <venkatesh.marreboyina@soulpageit.com> Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com> Co-authored-by: Anmol Singh Bhatia <anmolsinghbhatia1001@gmail.com>
164 lines
5.6 KiB
TypeScript
164 lines
5.6 KiB
TypeScript
// react
|
|
import React from "react";
|
|
// next
|
|
import Link from "next/link";
|
|
import Image from "next/image";
|
|
import { useRouter } from "next/router";
|
|
// swr
|
|
import useSWR from "swr";
|
|
// services
|
|
import { CalendarDaysIcon } from "@heroicons/react/20/solid";
|
|
import { ArrowPathIcon, UserIcon } from "@heroicons/react/24/outline";
|
|
import cyclesService from "services/cycles.service";
|
|
// hooks
|
|
// ui
|
|
import { Button, CustomMenu } from "components/ui";
|
|
// icons
|
|
// helpers
|
|
import { renderShortNumericDateFormat } from "helpers/date-time.helper";
|
|
import { groupBy } from "helpers/array.helper";
|
|
// types
|
|
import { CycleIssueResponse, ICycle } from "types";
|
|
// fetch-keys
|
|
import { CYCLE_ISSUES } from "constants/fetch-keys";
|
|
|
|
type TSingleStatProps = {
|
|
cycle: ICycle;
|
|
handleEditCycle: () => void;
|
|
handleDeleteCycle: () => void;
|
|
};
|
|
|
|
const stateGroupColours: {
|
|
[key: string]: string;
|
|
} = {
|
|
backlog: "#3f76ff",
|
|
unstarted: "#ff9e9e",
|
|
started: "#d687ff",
|
|
cancelled: "#ff5353",
|
|
completed: "#096e8d",
|
|
};
|
|
|
|
const SingleStat: React.FC<TSingleStatProps> = (props) => {
|
|
const { cycle, handleEditCycle, handleDeleteCycle } = props;
|
|
|
|
const router = useRouter();
|
|
const { workspaceSlug, projectId } = router.query;
|
|
|
|
const { data: cycleIssues } = useSWR<CycleIssueResponse[]>(
|
|
workspaceSlug && projectId && cycle.id ? CYCLE_ISSUES(cycle.id as string) : null,
|
|
workspaceSlug && projectId && cycle.id
|
|
? () => cyclesService.getCycleIssues(workspaceSlug as string, projectId as string, cycle.id)
|
|
: null
|
|
);
|
|
|
|
const endDate = new Date(cycle.end_date ?? "");
|
|
const startDate = new Date(cycle.start_date ?? "");
|
|
|
|
const groupedIssues = {
|
|
backlog: [],
|
|
unstarted: [],
|
|
started: [],
|
|
cancelled: [],
|
|
completed: [],
|
|
...groupBy(cycleIssues ?? [], "issue_detail.state_detail.group"),
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<div className="rounded-md border bg-white p-3">
|
|
<div className="grid grid-cols-9 gap-2 divide-x">
|
|
<div className="col-span-3 flex flex-col space-y-3">
|
|
<div className="flex items-center justify-between gap-2">
|
|
<Link href={`/${workspaceSlug}/projects/${projectId as string}/cycles/${cycle.id}`}>
|
|
<a>
|
|
<h2 className="font-medium">{cycle.name}</h2>
|
|
</a>
|
|
</Link>
|
|
<CustomMenu width="auto" ellipsis>
|
|
<CustomMenu.MenuItem onClick={handleEditCycle}>Edit cycle</CustomMenu.MenuItem>
|
|
<CustomMenu.MenuItem onClick={handleDeleteCycle}>
|
|
Delete cycle permanently
|
|
</CustomMenu.MenuItem>
|
|
</CustomMenu>
|
|
</div>
|
|
<div className="grid grid-cols-3 gap-x-2 gap-y-3 text-xs">
|
|
<div className="flex items-center gap-2 text-gray-500">
|
|
<CalendarDaysIcon className="h-4 w-4" />
|
|
Cycle dates
|
|
</div>
|
|
<div className="col-span-2">
|
|
{renderShortNumericDateFormat(startDate)} - {renderShortNumericDateFormat(endDate)}
|
|
</div>
|
|
<div className="flex items-center gap-2 text-gray-500">
|
|
<UserIcon className="h-4 w-4" />
|
|
Created by
|
|
</div>
|
|
<div className="col-span-2 flex items-center gap-2">
|
|
{cycle.owned_by.avatar && cycle.owned_by.avatar !== "" ? (
|
|
<Image
|
|
src={cycle.owned_by.avatar}
|
|
height={16}
|
|
width={16}
|
|
className="rounded-full"
|
|
alt={cycle.owned_by.first_name}
|
|
/>
|
|
) : (
|
|
<span className="grid h-5 w-5 place-items-center rounded-full bg-gray-700 capitalize text-white">
|
|
{cycle.owned_by.first_name.charAt(0)}
|
|
</span>
|
|
)}
|
|
{cycle.owned_by.first_name}
|
|
</div>
|
|
</div>
|
|
<div className="flex h-full items-end">
|
|
<Button
|
|
theme="secondary"
|
|
className="flex items-center gap-2"
|
|
onClick={() =>
|
|
router.push(`/${workspaceSlug}/projects/${projectId}/cycles/${cycle.id}`)
|
|
}
|
|
>
|
|
<ArrowPathIcon className="h-3 w-3" />
|
|
Open Cycle
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
<div className="col-span-2 space-y-3 px-5">
|
|
<h4 className="text-sm tracking-widest">PROGRESS</h4>
|
|
<div className="space-y-3 text-xs">
|
|
{Object.keys(groupedIssues).map((group) => (
|
|
<div key={group} className="flex items-center gap-2">
|
|
<div className="flex basis-2/3 items-center gap-2">
|
|
<span
|
|
className="block h-2 w-2 rounded-full"
|
|
style={{
|
|
backgroundColor: stateGroupColours[group],
|
|
}}
|
|
/>
|
|
<h6 className="text-xs capitalize">{group}</h6>
|
|
</div>
|
|
<div>
|
|
<span>
|
|
{groupedIssues[group].length}{" "}
|
|
<span className="text-gray-500">
|
|
-{" "}
|
|
{cycleIssues && cycleIssues.length > 0
|
|
? `${Math.round(
|
|
(groupedIssues[group].length / cycleIssues.length) * 100
|
|
)}%`
|
|
: "0%"}
|
|
</span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default SingleStat;
|