refactor: sidebar stats mutation (#635)

This commit is contained in:
Aaryan Khandelwal 2023-03-31 02:20:44 +05:30 committed by GitHub
parent 66d07e340b
commit e2921539d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 68 additions and 60 deletions

View File

@ -32,6 +32,7 @@ import {
LinkIcon, LinkIcon,
PencilIcon, PencilIcon,
TrashIcon, TrashIcon,
XMarkIcon,
} from "@heroicons/react/24/outline"; } from "@heroicons/react/24/outline";
// helpers // helpers
import { handleIssuesMutation } from "constants/issue"; import { handleIssuesMutation } from "constants/issue";
@ -40,7 +41,9 @@ import { copyTextToClipboard, truncateText } from "helpers/string.helper";
import { IIssue, Properties, TIssueGroupByOptions, UserAuth } from "types"; import { IIssue, Properties, TIssueGroupByOptions, UserAuth } from "types";
// fetch-keys // fetch-keys
import { import {
CYCLE_DETAILS,
CYCLE_ISSUES_WITH_PARAMS, CYCLE_ISSUES_WITH_PARAMS,
MODULE_DETAILS,
MODULE_ISSUES_WITH_PARAMS, MODULE_ISSUES_WITH_PARAMS,
PROJECT_ISSUES_LIST_WITH_PARAMS, PROJECT_ISSUES_LIST_WITH_PARAMS,
} from "constants/fetch-keys"; } from "constants/fetch-keys";
@ -135,10 +138,14 @@ export const SingleBoardIssue: React.FC<Props> = ({
issuesService issuesService
.patchIssue(workspaceSlug as string, projectId as string, issue.id, formData) .patchIssue(workspaceSlug as string, projectId as string, issue.id, formData)
.then((res) => { .then(() => {
if (cycleId) mutate(CYCLE_ISSUES_WITH_PARAMS(cycleId as string, params)); if (cycleId) {
if (moduleId) mutate(MODULE_ISSUES_WITH_PARAMS(moduleId as string, params)); mutate(CYCLE_ISSUES_WITH_PARAMS(cycleId as string, params));
mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(projectId as string, params)); mutate(CYCLE_DETAILS(cycleId as string));
} else if (moduleId) {
mutate(MODULE_ISSUES_WITH_PARAMS(moduleId as string, params));
mutate(MODULE_DETAILS(moduleId as string));
} else mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(projectId as string, params));
}) })
.catch((error) => { .catch((error) => {
console.log(error); console.log(error);
@ -226,27 +233,30 @@ export const SingleBoardIssue: React.FC<Props> = ({
{type && !isNotAllowed && ( {type && !isNotAllowed && (
<CustomMenu width="auto" ellipsis> <CustomMenu width="auto" ellipsis>
<CustomMenu.MenuItem onClick={editIssue}> <CustomMenu.MenuItem onClick={editIssue}>
<span className="flex items-center justify-start gap-2"> <div className="flex items-center justify-start gap-2">
<PencilIcon className="h-4 w-4" /> <PencilIcon className="h-4 w-4" />
<span>Edit issue</span> <span>Edit issue</span>
</span> </div>
</CustomMenu.MenuItem> </CustomMenu.MenuItem>
{type !== "issue" && removeIssue && ( {type !== "issue" && removeIssue && (
<CustomMenu.MenuItem onClick={removeIssue}> <CustomMenu.MenuItem onClick={removeIssue}>
<>Remove from {type}</> <div className="flex items-center justify-start gap-2">
<XMarkIcon className="h-4 w-4" />
<span>Remove from {type}</span>
</div>
</CustomMenu.MenuItem> </CustomMenu.MenuItem>
)} )}
<CustomMenu.MenuItem onClick={() => handleDeleteIssue(issue)}> <CustomMenu.MenuItem onClick={() => handleDeleteIssue(issue)}>
<span className="flex items-center justify-start gap-2"> <div className="flex items-center justify-start gap-2">
<TrashIcon className="h-4 w-4" /> <TrashIcon className="h-4 w-4" />
<span>Delete issue</span> <span>Delete issue</span>
</span> </div>
</CustomMenu.MenuItem> </CustomMenu.MenuItem>
<CustomMenu.MenuItem onClick={handleCopyText}> <CustomMenu.MenuItem onClick={handleCopyText}>
<span className="flex items-center justify-start gap-2"> <div className="flex items-center justify-start gap-2">
<LinkIcon className="h-4 w-4" /> <LinkIcon className="h-4 w-4" />
<span>Copy issue Link</span> <span>Copy issue Link</span>
</span> </div>
</CustomMenu.MenuItem> </CustomMenu.MenuItem>
</CustomMenu> </CustomMenu>
)} )}

View File

@ -35,7 +35,9 @@ import { handleIssuesMutation } from "constants/issue";
import { IIssue, Properties, UserAuth } from "types"; import { IIssue, Properties, UserAuth } from "types";
// fetch-keys // fetch-keys
import { import {
CYCLE_DETAILS,
CYCLE_ISSUES_WITH_PARAMS, CYCLE_ISSUES_WITH_PARAMS,
MODULE_DETAILS,
MODULE_ISSUES_WITH_PARAMS, MODULE_ISSUES_WITH_PARAMS,
PROJECT_ISSUES_LIST_WITH_PARAMS, PROJECT_ISSUES_LIST_WITH_PARAMS,
} from "constants/fetch-keys"; } from "constants/fetch-keys";
@ -123,10 +125,14 @@ export const SingleListIssue: React.FC<Props> = ({
issuesService issuesService
.patchIssue(workspaceSlug as string, projectId as string, issue.id, formData) .patchIssue(workspaceSlug as string, projectId as string, issue.id, formData)
.then((res) => { .then(() => {
if (cycleId) mutate(CYCLE_ISSUES_WITH_PARAMS(cycleId as string, params)); if (cycleId) {
if (moduleId) mutate(MODULE_ISSUES_WITH_PARAMS(moduleId as string, params)); mutate(CYCLE_ISSUES_WITH_PARAMS(cycleId as string, params));
mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(projectId as string, params)); mutate(CYCLE_DETAILS(cycleId as string));
} else if (moduleId) {
mutate(MODULE_ISSUES_WITH_PARAMS(moduleId as string, params));
mutate(MODULE_DETAILS(moduleId as string));
} else mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(projectId as string, params));
}); });
}, },
[workspaceSlug, projectId, cycleId, moduleId, issue, groupTitle, index, selectedGroup, params] [workspaceSlug, projectId, cycleId, moduleId, issue, groupTitle, index, selectedGroup, params]

View File

@ -240,7 +240,7 @@ export const SidebarProgressStats: React.FC<Props> = ({
<span className="text-xs capitalize">{group}</span> <span className="text-xs capitalize">{group}</span>
</div> </div>
} }
completed={groupedIssues[group].length} completed={groupedIssues[group]}
total={issues.length} total={issues.length}
/> />
))} ))}

View File

@ -34,7 +34,6 @@ import { DeleteCycleModal } from "components/cycles";
import { ExclamationIcon } from "components/icons"; import { ExclamationIcon } from "components/icons";
// helpers // helpers
import { capitalizeFirstLetter, copyTextToClipboard } from "helpers/string.helper"; import { capitalizeFirstLetter, copyTextToClipboard } from "helpers/string.helper";
import { groupBy } from "helpers/array.helper";
import { isDateRangeValid, renderDateFormat, renderShortDate } from "helpers/date-time.helper"; import { isDateRangeValid, renderDateFormat, renderShortDate } from "helpers/date-time.helper";
// types // types
import { ICycle, IIssue } from "types"; import { ICycle, IIssue } from "types";
@ -78,15 +77,6 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
: null : null
); );
const groupedIssues = {
backlog: [],
unstarted: [],
started: [],
cancelled: [],
completed: [],
...groupBy(issues ?? [], "state_detail.group"),
};
const { reset, watch } = useForm({ const { reset, watch } = useForm({
defaultValues, defaultValues,
}); });
@ -140,8 +130,8 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
const isStartValid = new Date(`${cycle?.start_date}`) <= new Date(); const isStartValid = new Date(`${cycle?.start_date}`) <= new Date();
const isEndValid = new Date(`${cycle?.end_date}`) >= new Date(`${cycle?.start_date}`); const isEndValid = new Date(`${cycle?.end_date}`) >= new Date(`${cycle?.start_date}`);
const progressPercentage = issues const progressPercentage = cycle
? Math.round((groupedIssues.completed.length / issues?.length) * 100) ? Math.round((cycle.completed_issues / cycle.total_issues) * 100)
: null; : null;
return ( return (
@ -205,7 +195,8 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
setToastAlert({ setToastAlert({
type: "error", type: "error",
title: "Error!", title: "Error!",
message: "The date you have entered is invalid. Please check and enter a valid date.", message:
"The date you have entered is invalid. Please check and enter a valid date.",
}); });
} }
} }
@ -268,7 +259,8 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
setToastAlert({ setToastAlert({
type: "error", type: "error",
title: "Error!", title: "Error!",
message: "The date you have entered is invalid. Please check and enter a valid date.", message:
"The date you have entered is invalid. Please check and enter a valid date.",
}); });
} }
} }
@ -348,12 +340,9 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
<div className="flex items-center gap-2.5 text-gray-800"> <div className="flex items-center gap-2.5 text-gray-800">
<span className="h-4 w-4"> <span className="h-4 w-4">
<ProgressBar <ProgressBar value={cycle.completed_issues} maxValue={cycle.total_issues} />
value={groupedIssues.completed.length}
maxValue={issues?.length}
/>
</span> </span>
{groupedIssues.completed.length}/{issues?.length} {cycle.completed_issues}/{cycle.total_issues}
</div> </div>
</div> </div>
</div> </div>
@ -369,7 +358,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
<div className="flex w-full items-center justify-between gap-2 "> <div className="flex w-full items-center justify-between gap-2 ">
<div className="flex items-center justify-start gap-2 text-sm"> <div className="flex items-center justify-start gap-2 text-sm">
<span className="font-medium text-gray-500">Progress</span> <span className="font-medium text-gray-500">Progress</span>
{!open && issues && progressPercentage ? ( {!open && progressPercentage ? (
<span className="rounded bg-[#09A953]/10 px-1.5 py-0.5 text-xs text-[#09A953]"> <span className="rounded bg-[#09A953]/10 px-1.5 py-0.5 text-xs text-[#09A953]">
{progressPercentage ? `${progressPercentage}%` : ""} {progressPercentage ? `${progressPercentage}%` : ""}
</span> </span>
@ -404,9 +393,8 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
</span> </span>
<span> <span>
Pending Issues -{" "} Pending Issues -{" "}
{issues && {cycle.total_issues -
groupedIssues && (cycle.completed_issues + cycle.cancelled_issues)}
issues?.length - groupedIssues.completed.length}
</span> </span>
</div> </div>
@ -450,7 +438,7 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
<span className="font-medium text-gray-500">Other Information</span> <span className="font-medium text-gray-500">Other Information</span>
</div> </div>
{(issues?.length ?? 0) > 0 ? ( {cycle.total_issues > 0 ? (
<Disclosure.Button> <Disclosure.Button>
<ChevronDownIcon <ChevronDownIcon
className={`h-3 w-3 ${open ? "rotate-180 transform" : ""}`} className={`h-3 w-3 ${open ? "rotate-180 transform" : ""}`}
@ -468,11 +456,17 @@ export const CycleDetailsSidebar: React.FC<Props> = ({
</div> </div>
<Transition show={open}> <Transition show={open}>
<Disclosure.Panel> <Disclosure.Panel>
{(issues?.length ?? 0) > 0 ? ( {cycle.total_issues > 0 ? (
<div className=" h-full w-full py-4"> <div className=" h-full w-full py-4">
<SidebarProgressStats <SidebarProgressStats
issues={issues ?? []} issues={issues ?? []}
groupedIssues={groupedIssues} groupedIssues={{
backlog: cycle.backlog_issues,
unstarted: cycle.unstarted_issues,
started: cycle.started_issues,
completed: cycle.completed_issues,
cancelled: cycle.cancelled_issues,
}}
/> />
</div> </div>
) : ( ) : (

View File

@ -77,15 +77,6 @@ export const ModuleDetailsSidebar: React.FC<Props> = ({
defaultValues, defaultValues,
}); });
const groupedIssues = {
backlog: [],
unstarted: [],
started: [],
cancelled: [],
completed: [],
...groupBy(moduleIssues ?? [], "issue_detail.state_detail.group"),
};
const submitChanges = (data: Partial<IModule>) => { const submitChanges = (data: Partial<IModule>) => {
if (!workspaceSlug || !projectId || !moduleId) return; if (!workspaceSlug || !projectId || !moduleId) return;
@ -179,8 +170,8 @@ export const ModuleDetailsSidebar: React.FC<Props> = ({
const isStartValid = new Date(`${module?.start_date}`) <= new Date(); const isStartValid = new Date(`${module?.start_date}`) <= new Date();
const isEndValid = new Date(`${module?.target_date}`) >= new Date(`${module?.start_date}`); const isEndValid = new Date(`${module?.target_date}`) >= new Date(`${module?.start_date}`);
const progressPercentage = moduleIssues const progressPercentage = module
? Math.round((groupedIssues.completed.length / moduleIssues?.length) * 100) ? Math.round((module.completed_issues / module.total_issues) * 100)
: null; : null;
return ( return (
@ -389,11 +380,11 @@ export const ModuleDetailsSidebar: React.FC<Props> = ({
<div className="flex items-center gap-2.5 text-gray-800"> <div className="flex items-center gap-2.5 text-gray-800">
<span className="h-4 w-4"> <span className="h-4 w-4">
<ProgressBar <ProgressBar
value={groupedIssues.completed.length} value={module.completed_issues}
maxValue={moduleIssues?.length} maxValue={module.total_issues}
/> />
</span> </span>
{groupedIssues.completed.length}/{moduleIssues?.length} {module.completed_issues}/{module.total_issues}
</div> </div>
</div> </div>
</div> </div>
@ -445,7 +436,8 @@ export const ModuleDetailsSidebar: React.FC<Props> = ({
</span> </span>
<span> <span>
Pending Issues -{" "} Pending Issues -{" "}
{moduleIssues?.length - groupedIssues.completed.length}{" "} {module.total_issues -
(module.completed_issues + module.cancelled_issues)}{" "}
</span> </span>
</div> </div>
@ -489,7 +481,7 @@ export const ModuleDetailsSidebar: React.FC<Props> = ({
<span className="font-medium text-gray-500">Other Information</span> <span className="font-medium text-gray-500">Other Information</span>
</div> </div>
{issues.length > 0 ? ( {module.total_issues > 0 ? (
<Disclosure.Button className="p-1"> <Disclosure.Button className="p-1">
<ChevronDownIcon <ChevronDownIcon
className={`h-3 w-3 ${open ? "rotate-180 transform" : ""}`} className={`h-3 w-3 ${open ? "rotate-180 transform" : ""}`}
@ -507,12 +499,18 @@ export const ModuleDetailsSidebar: React.FC<Props> = ({
</div> </div>
<Transition show={open}> <Transition show={open}>
<Disclosure.Panel> <Disclosure.Panel>
{issues.length > 0 ? ( {module.total_issues > 0 ? (
<> <>
<div className=" h-full w-full py-4"> <div className=" h-full w-full py-4">
<SidebarProgressStats <SidebarProgressStats
issues={issues} issues={issues}
groupedIssues={groupedIssues} groupedIssues={{
backlog: module.backlog_issues,
unstarted: module.unstarted_issues,
started: module.started_issues,
completed: module.completed_issues,
cancelled: module.cancelled_issues,
}}
userAuth={userAuth} userAuth={userAuth}
module={module} module={module}
/> />