diff --git a/apps/app/components/cycles/single-cycle-card.tsx b/apps/app/components/cycles/single-cycle-card.tsx index 794a98e25..c255b3eb1 100644 --- a/apps/app/components/cycles/single-cycle-card.tsx +++ b/apps/app/components/cycles/single-cycle-card.tsx @@ -4,7 +4,7 @@ import Link from "next/link"; import Image from "next/image"; import { useRouter } from "next/router"; -import useSWR, { mutate } from "swr"; +import { mutate } from "swr"; // services import cyclesService from "services/cycles.service"; @@ -24,8 +24,7 @@ import { } from "@heroicons/react/24/outline"; // helpers import { getDateRangeStatus, renderShortDateWithYearFormat } from "helpers/date-time.helper"; -import { groupBy } from "helpers/array.helper"; -import { capitalizeFirstLetter, copyTextToClipboard, truncateText } from "helpers/string.helper"; +import { copyTextToClipboard, truncateText } from "helpers/string.helper"; // types import { CompletedCyclesResponse, @@ -38,7 +37,6 @@ import { CYCLE_COMPLETE_LIST, CYCLE_CURRENT_AND_UPCOMING_LIST, CYCLE_DRAFT_LIST, - CYCLE_ISSUES, } from "constants/fetch-keys"; type TSingleStatProps = { @@ -47,43 +45,47 @@ type TSingleStatProps = { handleDeleteCycle: () => void; }; -const stateGroupColors: { - [key: string]: string; -} = { - backlog: "#DEE2E6", - unstarted: "#26B5CE", - started: "#F7AE59", - cancelled: "#D687FF", - completed: "#09A953", -}; - -export const SingleCycleCard: React.FC = (props) => { - const { cycle, handleEditCycle, handleDeleteCycle } = props; +const stateGroups = [ + { + key: "backlog_issues", + title: "Backlog", + color: "#dee2e6", + }, + { + key: "unstarted_issues", + title: "Unstarted", + color: "#26b5ce", + }, + { + key: "started_issues", + title: "Started", + color: "#f7ae59", + }, + { + key: "cancelled_issues", + title: "Cancelled", + color: "#d687ff", + }, + { + key: "completed_issues", + title: "Completed", + color: "#09a953", + }, +]; +export const SingleCycleCard: React.FC = ({ + cycle, + handleEditCycle, + handleDeleteCycle, +}) => { const router = useRouter(); const { workspaceSlug, projectId } = router.query; const { setToastAlert } = useToast(); - const { data: cycleIssues } = useSWR( - 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 ?? [], "state_detail.group"), - }; - const handleAddToFavorites = () => { if (!workspaceSlug && !projectId && !cycle) return; @@ -223,14 +225,14 @@ export const SingleCycleCard: React.FC = (props) => { }); }; - const progressIndicatorData = Object.keys(groupedIssues).map((group, index) => ({ + const progressIndicatorData = stateGroups.map((group, index) => ({ id: index, - name: capitalizeFirstLetter(group), + name: group.title, value: - cycleIssues && cycleIssues.length > 0 - ? (groupedIssues[group].length / cycleIssues.length) * 100 + cycle.total_issues > 0 + ? ((cycle[group.key as keyof ICycle] as number) / cycle.total_issues) * 100 : 0, - color: stateGroupColors[group], + color: group.color, })); return ( @@ -341,25 +343,30 @@ export const SingleCycleCard: React.FC = (props) => {
- {Object.keys(groupedIssues).map((group) => ( -
+ {stateGroups.map((group) => ( +
-
{group}
+
{group.title}
- {groupedIssues[group].length}{" "} + {cycle[group.key as keyof ICycle] as number}{" "} -{" "} - {cycleIssues && cycleIssues.length > 0 + {cycle.total_issues > 0 ? `${Math.round( - (groupedIssues[group].length / cycleIssues.length) * 100 + ((cycle[group.key as keyof ICycle] as number) / + cycle.total_issues) * + 100 )}%` : "0%"} diff --git a/apps/app/components/modules/single-module-card.tsx b/apps/app/components/modules/single-module-card.tsx index f35e0571b..cecdd20c8 100644 --- a/apps/app/components/modules/single-module-card.tsx +++ b/apps/app/components/modules/single-module-card.tsx @@ -4,7 +4,7 @@ import Link from "next/link"; import { useRouter } from "next/router"; import Image from "next/image"; -import useSWR, { mutate } from "swr"; +import { mutate } from "swr"; // services import modulesService from "services/modules.service"; @@ -31,7 +31,7 @@ import { renderShortDateWithYearFormat } from "helpers/date-time.helper"; // types import { IModule } from "types"; // fetch-key -import { MODULE_ISSUES, MODULE_LIST } from "constants/fetch-keys"; +import { MODULE_LIST } from "constants/fetch-keys"; type Props = { module: IModule; @@ -46,18 +46,7 @@ export const SingleModuleCard: React.FC = ({ module, handleEditModule }) const { setToastAlert } = useToast(); - const { data: moduleIssues } = useSWR( - workspaceSlug && projectId && module.id ? MODULE_ISSUES(module.id as string) : null, - workspaceSlug && projectId && module.id - ? () => - modulesService.getModuleIssues(workspaceSlug as string, projectId as string, module.id) - : null - ); - - const completedIssues = (moduleIssues ?? []).filter( - (i) => i.state_detail.group === "completed" || i.state_detail.group === "cancelled" - ).length; - const completionPercentage = (completedIssues / (moduleIssues ?? []).length) * 100; + const completionPercentage = (module.completed_issues / module.total_issues) * 100; const handleDeleteModule = () => { if (!module) return; @@ -271,7 +260,7 @@ export const SingleModuleCard: React.FC = ({ module, handleEditModule })
diff --git a/apps/app/components/project/sidebar-list.tsx b/apps/app/components/project/sidebar-list.tsx index aa3a78975..a02cfe26c 100644 --- a/apps/app/components/project/sidebar-list.tsx +++ b/apps/app/components/project/sidebar-list.tsx @@ -162,7 +162,7 @@ export const ProjectSidebarList: FC = () => { })}
)} -
+
{!sidebarCollapse &&
Projects
} {projects ? ( <> diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx index 5e2a94815..3f02a1d9f 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx @@ -17,7 +17,7 @@ import { IssueViewContextProvider } from "contexts/issue-view.context"; import { ExistingIssuesListModal, IssuesFilterView, IssuesView } from "components/core"; import { CycleDetailsSidebar } from "components/cycles"; // services -import issuesServices from "services/issues.service"; +import issuesService from "services/issues.service"; import cycleServices from "services/cycles.service"; import projectService from "services/project.service"; // ui @@ -29,7 +29,13 @@ import { getDateRangeStatus } from "helpers/date-time.helper"; // types import { UserAuth } from "types"; // fetch-keys -import { CYCLE_ISSUES, CYCLE_LIST, PROJECT_DETAILS, CYCLE_DETAILS } from "constants/fetch-keys"; +import { + CYCLE_ISSUES, + CYCLE_LIST, + PROJECT_DETAILS, + CYCLE_DETAILS, + PROJECT_ISSUES_LIST, +} from "constants/fetch-keys"; const SingleCycle: React.FC = (props) => { const [cycleIssuesListModal, setCycleIssuesListModal] = useState(false); @@ -70,14 +76,11 @@ const SingleCycle: React.FC = (props) => { : ""; const { data: issues } = useSWR( - workspaceSlug && projectId && cycleId ? CYCLE_ISSUES(cycleId as string) : null, - workspaceSlug && projectId && cycleId - ? () => - cycleServices.getCycleIssues( - workspaceSlug as string, - projectId as string, - cycleId as string - ) + workspaceSlug && projectId + ? PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string) + : null, + workspaceSlug && projectId + ? () => issuesService.getIssues(workspaceSlug as string, projectId as string) : null ); @@ -88,7 +91,7 @@ const SingleCycle: React.FC = (props) => { const handleAddIssuesToCycle = async (data: { issues: string[] }) => { if (!workspaceSlug || !projectId) return; - await issuesServices + await issuesService .addIssueToCycle(workspaceSlug as string, projectId as string, cycleId as string, data) .then((res) => { console.log(res); diff --git a/apps/app/types/cycles.d.ts b/apps/app/types/cycles.d.ts index f872339a3..835be2bad 100644 --- a/apps/app/types/cycles.d.ts +++ b/apps/app/types/cycles.d.ts @@ -1,23 +1,26 @@ import type { IUser, IIssue } from "."; export interface ICycle { - id: string; - owned_by: IUser; + backlog_issues: number; + cancelled_issues: number; + completed_issues: number; created_at: Date; - updated_at: Date; - name: string; - description: string; - start_date: string | null; - end_date: string | null; - is_favorite: boolean; created_by: string; - updated_by: string; - project: string; - workspace: string; + description: string; + end_date: string | null; + id: string; + is_favorite: boolean; issue: string; - current_cycle: []; - upcoming_cycle: []; - past_cycles: []; + name: string; + owned_by: IUser; + project: string; + start_date: string | null; + started_issues: number; + total_issues: number; + unstarted_issues: number; + updated_at: Date; + updated_by: string; + workspace: string; } export interface CurrentAndUpcomingCyclesResponse { diff --git a/apps/app/types/modules.d.ts b/apps/app/types/modules.d.ts index ef6749fce..b11d235bc 100644 --- a/apps/app/types/modules.d.ts +++ b/apps/app/types/modules.d.ts @@ -1,6 +1,9 @@ import type { IUser, IUserLite, IIssue, IProject } from "."; export interface IModule { + backlog_issues: number; + cancelled_issues: number; + completed_issues: number; created_at: Date; created_by: string; description: string; @@ -27,8 +30,11 @@ export interface IModule { project: string; project_detail: IProject; start_date: string | null; + started_issues: number; status: "backlog" | "planned" | "in-progress" | "paused" | "completed" | "cancelled" | null; target_date: string | null; + total_issues: number; + unstarted_issues: number; updated_at: Date; updated_by: string; workspace: string;