diff --git a/web/components/core/sidebar/sidebar-progress-stats.tsx b/web/components/core/sidebar/sidebar-progress-stats.tsx index ac4ffcdc5..8cea3784f 100644 --- a/web/components/core/sidebar/sidebar-progress-stats.tsx +++ b/web/components/core/sidebar/sidebar-progress-stats.tsx @@ -14,6 +14,7 @@ import { SingleProgressStats } from "components/core"; import { Avatar, StateGroupIcon } from "@plane/ui"; // types import { + IIssueFilterOptions, IModule, TAssigneesDistribution, TCompletionChartDistribution, @@ -35,6 +36,9 @@ type Props = { roundedTab?: boolean; noBackground?: boolean; isPeekView?: boolean; + isCompleted?: boolean; + filters?: IIssueFilterOptions; + handleFiltersUpdate: (key: keyof IIssueFilterOptions, value: string | string[]) => void; }; export const SidebarProgressStats: React.FC = ({ @@ -44,7 +48,10 @@ export const SidebarProgressStats: React.FC = ({ module, roundedTab, noBackground, + isCompleted = false, isPeekView = false, + filters, + handleFiltersUpdate, }) => { const { storedValue: tab, setValue: setTab } = useLocalStorage("tab", "Assignees"); @@ -140,20 +147,11 @@ export const SidebarProgressStats: React.FC = ({ } completed={assignee.completed_issues} total={assignee.total_issues} - {...(!isPeekView && { - onClick: () => { - // TODO: set filters here - // if (filters?.assignees?.includes(assignee.assignee_id ?? "")) - // setFilters({ - // assignees: filters?.assignees?.filter((a) => a !== assignee.assignee_id), - // }); - // else - // setFilters({ - // assignees: [...(filters?.assignees ?? []), assignee.assignee_id ?? ""], - // }); - }, - // selected: filters?.assignees?.includes(assignee.assignee_id ?? ""), - })} + {...(!isPeekView && + !isCompleted && { + onClick: () => handleFiltersUpdate("assignees", assignee.assignee_id ?? ""), + selected: filters?.assignees?.includes(assignee.assignee_id ?? ""), + })} /> ); else @@ -200,17 +198,11 @@ export const SidebarProgressStats: React.FC = ({ } completed={label.completed_issues} total={label.total_issues} - {...(!isPeekView && { - // TODO: set filters here - onClick: () => { - // if (filters.labels?.includes(label.label_id ?? "")) - // setFilters({ - // labels: filters?.labels?.filter((l) => l !== label.label_id), - // }); - // else setFilters({ labels: [...(filters?.labels ?? []), label.label_id ?? ""] }); - }, - // selected: filters?.labels?.includes(label.label_id ?? ""), - })} + {...(!isPeekView && + !isCompleted && { + onClick: () => handleFiltersUpdate("labels", label.label_id ?? ""), + selected: filters?.labels?.includes(label.label_id ?? `no-label-${index}`), + })} /> )) ) : ( diff --git a/web/components/cycles/sidebar.tsx b/web/components/cycles/sidebar.tsx index fa7fef008..1fd1cd05c 100644 --- a/web/components/cycles/sidebar.tsx +++ b/web/components/cycles/sidebar.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useCallback, useEffect, useState } from "react"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { useForm } from "react-hook-form"; @@ -29,7 +29,8 @@ import { renderShortMonthDate, } from "helpers/date-time.helper"; // types -import { ICycle } from "types"; +import { ICycle, IIssueFilterOptions } from "types"; +import { EFilterType } from "store/issues/types"; // constants import { EUserWorkspaceRoles } from "constants/workspace"; // fetch-keys @@ -54,6 +55,7 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { const { cycle: cycleDetailsStore, + cycleIssuesFilter: { issueFilters, updateFilters }, trackEvent: { setTrackElement }, user: { currentProjectRole }, } = useMobxStore(); @@ -245,6 +247,25 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { } }; + const handleFiltersUpdate = useCallback( + (key: keyof IIssueFilterOptions, value: string | string[]) => { + if (!workspaceSlug || !projectId) return; + const newValues = issueFilters?.filters?.[key] ?? []; + + if (Array.isArray(value)) { + value.forEach((val) => { + if (!newValues.includes(val)) newValues.push(val); + }); + } else { + if (issueFilters?.filters?.[key]?.includes(value)) newValues.splice(newValues.indexOf(value), 1); + else newValues.push(value); + } + + updateFilters(workspaceSlug.toString(), projectId.toString(), EFilterType.FILTERS, { [key]: newValues }, cycleId); + }, + [workspaceSlug, projectId, cycleId, issueFilters, updateFilters] + ); + const cycleStatus = cycleDetails?.start_date && cycleDetails?.end_date ? getDateRangeStatus(cycleDetails?.start_date, cycleDetails?.end_date) @@ -538,6 +559,9 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { }} totalIssues={cycleDetails.total_issues} isPeekView={Boolean(peekCycle)} + isCompleted={isCompleted} + filters={issueFilters?.filters} + handleFiltersUpdate={handleFiltersUpdate} /> )} diff --git a/web/components/modules/sidebar.tsx b/web/components/modules/sidebar.tsx index f2db5dbd5..35bf88567 100644 --- a/web/components/modules/sidebar.tsx +++ b/web/components/modules/sidebar.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useCallback, useEffect, useState } from "react"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { Controller, useForm } from "react-hook-form"; @@ -25,7 +25,8 @@ import { } from "helpers/date-time.helper"; import { copyUrlToClipboard } from "helpers/string.helper"; // types -import { ILinkDetails, IModule, ModuleLink } from "types"; +import { IIssueFilterOptions, ILinkDetails, IModule, ModuleLink } from "types"; +import { EFilterType } from "store/issues/types"; // constant import { MODULE_STATUS } from "constants/module"; import { EUserWorkspaceRoles } from "constants/workspace"; @@ -62,6 +63,7 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => { updateModuleLink, deleteModuleLink, }, + moduleIssuesFilter: { issueFilters, updateFilters }, user: userStore, } = useMobxStore(); @@ -211,6 +213,25 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => { } }; + const handleFiltersUpdate = useCallback( + (key: keyof IIssueFilterOptions, value: string | string[]) => { + if (!workspaceSlug || !projectId) return; + const newValues = issueFilters?.filters?.[key] ?? []; + + if (Array.isArray(value)) { + value.forEach((val) => { + if (!newValues.includes(val)) newValues.push(val); + }); + } else { + if (issueFilters?.filters?.[key]?.includes(value)) newValues.splice(newValues.indexOf(value), 1); + else newValues.push(value); + } + + updateFilters(workspaceSlug.toString(), projectId.toString(), EFilterType.FILTERS, { [key]: newValues }, moduleId); + }, + [workspaceSlug, projectId, moduleId, issueFilters, updateFilters] + ); + useEffect(() => { if (moduleDetails) reset({ @@ -544,6 +565,8 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => { totalIssues={moduleDetails.total_issues} module={moduleDetails} isPeekView={Boolean(peekModule)} + filters={issueFilters?.filters} + handleFiltersUpdate={handleFiltersUpdate} /> )}