[WEB-2681] fix: module progress indicator (#5842)

* fix: module progress indicator

* fix: module progress indicator
This commit is contained in:
Anmol Singh Bhatia 2024-10-21 15:48:35 +05:30 committed by GitHub
parent b833e3b10c
commit 6f8df3279c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 32 additions and 56 deletions

View file

@ -7,12 +7,21 @@ import { useParams, usePathname, useSearchParams } from "next/navigation";
import { Info, SquareUser } from "lucide-react"; import { Info, SquareUser } from "lucide-react";
// ui // ui
import { IModule } from "@plane/types"; import { IModule } from "@plane/types";
import { Card, FavoriteStar, LayersIcon, LinearProgressIndicator, TOAST_TYPE, Tooltip, setPromiseToast, setToast } from "@plane/ui"; import {
Card,
FavoriteStar,
LayersIcon,
LinearProgressIndicator,
TOAST_TYPE,
Tooltip,
setPromiseToast,
setToast,
} from "@plane/ui";
// components // components
import { DateRangeDropdown } from "@/components/dropdowns"; import { DateRangeDropdown } from "@/components/dropdowns";
import { ButtonAvatars } from "@/components/dropdowns/member/avatar"; import { ButtonAvatars } from "@/components/dropdowns/member/avatar";
import { ModuleQuickActions } from "@/components/modules"; import { ModuleQuickActions } from "@/components/modules";
import { ModuleStatusDropdown } from "@/components/modules/module-status-dropdown"; import { ModuleStatusDropdown } from "@/components/modules/module-status-dropdown";
// constants // constants
import { PROGRESS_STATE_GROUPS_DETAILS } from "@/constants/common"; import { PROGRESS_STATE_GROUPS_DETAILS } from "@/constants/common";
import { MODULE_FAVORITED, MODULE_UNFAVORITED } from "@/constants/event-tracker"; import { MODULE_FAVORITED, MODULE_UNFAVORITED } from "@/constants/event-tracker";
@ -21,11 +30,10 @@ import { MODULE_STATUS } from "@/constants/module";
import { getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper"; import { getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
import { generateQueryParams } from "@/helpers/router.helper"; import { generateQueryParams } from "@/helpers/router.helper";
// hooks // hooks
import { useEventTracker, useMember, useModule, useProjectEstimates, useUserPermissions } from "@/hooks/store"; import { useEventTracker, useMember, useModule, useUserPermissions } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router"; import { useAppRouter } from "@/hooks/use-app-router";
import { usePlatformOS } from "@/hooks/use-platform-os"; import { usePlatformOS } from "@/hooks/use-platform-os";
// plane web constants // plane web constants
import { EEstimateSystem } from "@/plane-web/constants/estimates";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions"; import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
type Props = { type Props = {
@ -46,7 +54,6 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
const { getModuleById, addModuleToFavorites, removeModuleFromFavorites, updateModuleDetails } = useModule(); const { getModuleById, addModuleToFavorites, removeModuleFromFavorites, updateModuleDetails } = useModule();
const { getUserDetails } = useMember(); const { getUserDetails } = useMember();
const { captureEvent } = useEventTracker(); const { captureEvent } = useEventTracker();
const { currentActiveEstimateId, areEstimateEnabledByProjectId, estimateById } = useProjectEstimates();
// derived values // derived values
const moduleDetails = getModuleById(moduleId); const moduleDetails = getModuleById(moduleId);
@ -57,7 +64,6 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
const isDisabled = !isEditingAllowed || !!moduleDetails?.archived_at; const isDisabled = !isEditingAllowed || !!moduleDetails?.archived_at;
const renderIcon = Boolean(moduleDetails?.start_date) || Boolean(moduleDetails?.target_date); const renderIcon = Boolean(moduleDetails?.start_date) || Boolean(moduleDetails?.target_date);
const { isMobile } = usePlatformOS(); const { isMobile } = usePlatformOS();
const handleAddToFavorites = (e: React.MouseEvent<HTMLButtonElement>) => { const handleAddToFavorites = (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation(); e.stopPropagation();
@ -156,29 +162,14 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
if (!moduleDetails) return null; if (!moduleDetails) return null;
/** const moduleTotalIssues =
* NOTE: This completion percentage calculation is based on the total issues count. moduleDetails.backlog_issues +
* when estimates are available and estimate type is points, we should consider the estimate point count moduleDetails.unstarted_issues +
* when estimates are available and estimate type is not points, then by default we consider the issue count moduleDetails.started_issues +
*/ moduleDetails.completed_issues +
const isEstimateEnabled = moduleDetails.cancelled_issues;
projectId &&
currentActiveEstimateId &&
areEstimateEnabledByProjectId(projectId.toString()) &&
estimateById(currentActiveEstimateId)?.type === EEstimateSystem.POINTS;
const moduleTotalIssues = isEstimateEnabled
? moduleDetails?.total_estimate_points || 0
: moduleDetails.backlog_issues +
moduleDetails.unstarted_issues +
moduleDetails.started_issues +
moduleDetails.completed_issues +
moduleDetails.cancelled_issues;
const moduleCompletedIssues = isEstimateEnabled
? moduleDetails?.completed_estimate_points || 0
: moduleDetails.completed_issues;
const moduleCompletedIssues = moduleDetails.completed_issues;
// const areYearsEqual = startDate.getFullYear() === endDate.getFullYear(); // const areYearsEqual = startDate.getFullYear() === endDate.getFullYear();
@ -186,11 +177,11 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
const issueCount = module const issueCount = module
? !moduleTotalIssues || moduleTotalIssues === 0 ? !moduleTotalIssues || moduleTotalIssues === 0
? `0 ${isEstimateEnabled ? `Point` : `Issue`}` ? `0 Issue`
: moduleTotalIssues === moduleCompletedIssues : moduleTotalIssues === moduleCompletedIssues
? `${moduleTotalIssues} Issue${moduleTotalIssues > 1 ? `s` : ``}` ? `${moduleTotalIssues} Issue${moduleTotalIssues > 1 ? `s` : ``}`
: `${moduleCompletedIssues}/${moduleTotalIssues} ${isEstimateEnabled ? `Points` : `Issues`}` : `${moduleCompletedIssues}/${moduleTotalIssues} Issues`
: `0 ${isEstimateEnabled ? `Point` : `Issue`}`; : `0 Issue`;
const moduleLeadDetails = moduleDetails.lead_id ? getUserDetails(moduleDetails.lead_id) : undefined; const moduleLeadDetails = moduleDetails.lead_id ? getUserDetails(moduleDetails.lead_id) : undefined;
@ -213,9 +204,9 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
<div className="flex items-center gap-2" onClick={handleEventPropagation}> <div className="flex items-center gap-2" onClick={handleEventPropagation}>
{moduleStatus && ( {moduleStatus && (
<ModuleStatusDropdown <ModuleStatusDropdown
isDisabled={isDisabled} isDisabled={isDisabled}
moduleDetails={moduleDetails} moduleDetails={moduleDetails}
handleModuleDetailsChange={handleModuleDetailsChange} handleModuleDetailsChange={handleModuleDetailsChange}
/> />
)} )}
<button onClick={openModuleOverview}> <button onClick={openModuleOverview}>
@ -252,9 +243,9 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
}} }}
onSelect={(val) => { onSelect={(val) => {
handleModuleDetailsChange({ handleModuleDetailsChange({
start_date: (val?.from ? renderFormattedPayloadDate(val.from) : null), start_date: val?.from ? renderFormattedPayloadDate(val.from) : null,
target_date: (val?.to ? renderFormattedPayloadDate(val.to) : null) target_date: val?.to ? renderFormattedPayloadDate(val.to) : null,
}) });
}} }}
placeholder={{ placeholder={{
from: "Start date", from: "Start date",

View file

@ -13,11 +13,9 @@ import { ModuleListItemAction, ModuleQuickActions } from "@/components/modules";
// helpers // helpers
import { generateQueryParams } from "@/helpers/router.helper"; import { generateQueryParams } from "@/helpers/router.helper";
// hooks // hooks
import { useModule, useProjectEstimates } from "@/hooks/store"; import { useModule } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router"; import { useAppRouter } from "@/hooks/use-app-router";
import { usePlatformOS } from "@/hooks/use-platform-os"; import { usePlatformOS } from "@/hooks/use-platform-os";
// plane web constants
import { EEstimateSystem } from "@/plane-web/constants/estimates";
type Props = { type Props = {
moduleId: string; moduleId: string;
@ -35,27 +33,14 @@ export const ModuleListItem: React.FC<Props> = observer((props) => {
// store hooks // store hooks
const { getModuleById } = useModule(); const { getModuleById } = useModule();
const { isMobile } = usePlatformOS(); const { isMobile } = usePlatformOS();
const { currentActiveEstimateId, areEstimateEnabledByProjectId, estimateById } = useProjectEstimates();
// derived values // derived values
const moduleDetails = getModuleById(moduleId); const moduleDetails = getModuleById(moduleId);
if (!moduleDetails) return null; if (!moduleDetails) return null;
/** const completionPercentage =
* NOTE: This completion percentage calculation is based on the total issues count. ((moduleDetails.completed_issues + moduleDetails.cancelled_issues) / moduleDetails.total_issues) * 100;
* when estimates are available and estimate type is points, we should consider the estimate point count
* when estimates are available and estimate type is not points, then by default we consider the issue count
*/
const isEstimateEnabled =
projectId &&
currentActiveEstimateId &&
areEstimateEnabledByProjectId(projectId?.toString()) &&
estimateById(currentActiveEstimateId)?.type === EEstimateSystem.POINTS;
const completionPercentage = isEstimateEnabled
? ((moduleDetails?.completed_estimate_points || 0) / (moduleDetails?.total_estimate_points || 0)) * 100
: ((moduleDetails.completed_issues + moduleDetails.cancelled_issues) / moduleDetails.total_issues) * 100;
const progress = isNaN(completionPercentage) ? 0 : Math.floor(completionPercentage); const progress = isNaN(completionPercentage) ? 0 : Math.floor(completionPercentage);