From 727042468a542c3cc32ab5e06a7dd87bc81d1fe5 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Fri, 29 Sep 2023 16:14:47 +0530 Subject: [PATCH 1/2] dev: spreadsheet layout implementation using MobX (#2306) * dev: implement spreadsheet view using mobx * refactor: remove console logs and props --- web/components/core/views/all-views.tsx | 4 +- .../views/spreadsheet-view/single-issue.tsx | 92 ++-------- .../spreadsheet-view/spreadsheet-columns.tsx | 74 +++----- .../spreadsheet-view/spreadsheet-issues.tsx | 2 +- .../spreadsheet-view/spreadsheet-view.tsx | 173 +++++++++--------- web/components/issues/delete-issue-modal.tsx | 16 -- web/components/issues/draft-issue-modal.tsx | 13 -- .../issues/issue-layouts/gantt/root.tsx | 4 - web/components/issues/issue-layouts/index.ts | 1 + .../issues/issue-layouts/spreadsheet/index.ts | 1 + .../issues/issue-layouts/spreadsheet/root.tsx | 160 ++++++++++++++++ web/components/issues/modal.tsx | 12 -- web/hooks/gantt-chart/issue-view.tsx | 41 ----- web/hooks/use-spreadsheet-issues-view.tsx | 116 ------------ 14 files changed, 290 insertions(+), 419 deletions(-) create mode 100644 web/components/issues/issue-layouts/spreadsheet/index.ts create mode 100644 web/components/issues/issue-layouts/spreadsheet/root.tsx delete mode 100644 web/hooks/gantt-chart/issue-view.tsx delete mode 100644 web/hooks/use-spreadsheet-issues-view.tsx diff --git a/web/components/core/views/all-views.tsx b/web/components/core/views/all-views.tsx index 5973a43cf..e8478e71a 100644 --- a/web/components/core/views/all-views.tsx +++ b/web/components/core/views/all-views.tsx @@ -6,7 +6,7 @@ import useSWR from "swr"; // mobx store import { useMobxStore } from "lib/mobx/store-provider"; // components -import { CalendarLayout, GanttLayout, KanBanLayout } from "components/issues"; +import { CalendarLayout, GanttLayout, KanBanLayout, SpreadsheetLayout } from "components/issues"; export const AllViews: React.FC = observer(() => { const router = useRouter(); @@ -41,6 +41,8 @@ export const AllViews: React.FC = observer(() => { ) : activeLayout === "gantt_chart" ? ( + ) : activeLayout === "spreadsheet" ? ( + ) : null} ); diff --git a/web/components/core/views/spreadsheet-view/single-issue.tsx b/web/components/core/views/spreadsheet-view/single-issue.tsx index f9c9f1cee..12a9eeecb 100644 --- a/web/components/core/views/spreadsheet-view/single-issue.tsx +++ b/web/components/core/views/spreadsheet-view/single-issue.tsx @@ -1,38 +1,27 @@ import React, { useCallback, useState } from "react"; - import { useRouter } from "next/router"; - import { mutate } from "swr"; +import { Popover2 } from "@blueprintjs/popover2"; +// icons +import { Icon } from "components/ui"; +import { EllipsisHorizontalIcon, LinkIcon, PencilIcon, TrashIcon } from "@heroicons/react/24/outline"; +// services +import issuesService from "services/issue.service"; +import trackEventServices from "services/track_event.service"; +// hooks +import useToast from "hooks/use-toast"; // components import { ViewDueDateSelect, ViewEstimateSelect, ViewStartDateSelect } from "components/issues"; import { LabelSelect, MembersSelect, PrioritySelect } from "components/project"; import { StateSelect } from "components/states"; -import { Popover2 } from "@blueprintjs/popover2"; -// icons -import { Icon } from "components/ui"; -import { EllipsisHorizontalIcon, LinkIcon, PencilIcon, TrashIcon } from "@heroicons/react/24/outline"; -// hooks -import useSpreadsheetIssuesView from "hooks/use-spreadsheet-issues-view"; -import useToast from "hooks/use-toast"; -// services -import issuesService from "services/issue.service"; -import trackEventServices from "services/track_event.service"; -// constant -import { - CYCLE_DETAILS, - CYCLE_ISSUES_WITH_PARAMS, - MODULE_DETAILS, - MODULE_ISSUES_WITH_PARAMS, - PROJECT_ISSUES_LIST_WITH_PARAMS, - SUB_ISSUES, - VIEW_ISSUES, -} from "constants/fetch-keys"; -// types -import { ICurrentUserResponse, IIssue, IState, ISubIssueResponse, Properties, TIssuePriorities, UserAuth } from "types"; -// helper +// helpers import { copyTextToClipboard } from "helpers/string.helper"; import { renderLongDetailDateFormat } from "helpers/date-time.helper"; +// types +import { ICurrentUserResponse, IIssue, IState, Properties, TIssuePriorities, UserAuth } from "types"; +// constant +import { CYCLE_DETAILS, MODULE_DETAILS, SUB_ISSUES } from "constants/fetch-keys"; type Props = { issue: IIssue; @@ -67,9 +56,7 @@ export const SingleSpreadsheetIssue: React.FC = ({ const router = useRouter(); - const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query; - - const { params } = useSpreadsheetIssuesView(); + const { workspaceSlug, projectId, cycleId, moduleId } = router.query; const { setToastAlert } = useToast(); @@ -77,59 +64,12 @@ export const SingleSpreadsheetIssue: React.FC = ({ (formData: Partial, issue: IIssue) => { if (!workspaceSlug || !projectId) return; - const fetchKey = cycleId - ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), params) - : moduleId - ? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), params) - : viewId - ? VIEW_ISSUES(viewId.toString(), params) - : PROJECT_ISSUES_LIST_WITH_PARAMS(projectId.toString(), params); - - if (issue.parent) - mutate( - SUB_ISSUES(issue.parent.toString()), - (prevData) => { - if (!prevData) return prevData; - - return { - ...prevData, - sub_issues: (prevData.sub_issues ?? []).map((i) => { - if (i.id === issue.id) { - return { - ...i, - ...formData, - }; - } - return i; - }), - }; - }, - false - ); - else - mutate( - fetchKey, - (prevData) => - (prevData ?? []).map((p) => { - if (p.id === issue.id) { - return { - ...p, - ...formData, - }; - } - return p; - }), - false - ); - issuesService .patchIssue(workspaceSlug as string, projectId as string, issue.id as string, formData, user) .then(() => { if (issue.parent) { mutate(SUB_ISSUES(issue.parent as string)); } else { - mutate(fetchKey); - if (cycleId) mutate(CYCLE_DETAILS(cycleId as string)); if (moduleId) mutate(MODULE_DETAILS(moduleId as string)); } @@ -138,7 +78,7 @@ export const SingleSpreadsheetIssue: React.FC = ({ console.log(error); }); }, - [workspaceSlug, projectId, cycleId, moduleId, viewId, params, user] + [workspaceSlug, projectId, cycleId, moduleId, user] ); const openPeekOverview = () => { diff --git a/web/components/core/views/spreadsheet-view/spreadsheet-columns.tsx b/web/components/core/views/spreadsheet-view/spreadsheet-columns.tsx index f52f1ab38..484faaa0c 100644 --- a/web/components/core/views/spreadsheet-view/spreadsheet-columns.tsx +++ b/web/components/core/views/spreadsheet-view/spreadsheet-columns.tsx @@ -1,48 +1,46 @@ import React from "react"; // hooks -import useSpreadsheetIssuesView from "hooks/use-spreadsheet-issues-view"; import useLocalStorage from "hooks/use-local-storage"; // component import { CustomMenu, Icon } from "components/ui"; // icon import { CheckIcon, ChevronDownIcon } from "@heroicons/react/24/outline"; // types -import { TIssueOrderByOptions } from "types"; +import { IIssueDisplayFilterOptions, TIssueOrderByOptions } from "types"; type Props = { columnData: any; + displayFilters: IIssueDisplayFilterOptions; gridTemplateColumns: string; + handleDisplayFiltersUpdate: (updatedDisplayFilter: Partial) => void; }; -export const SpreadsheetColumns: React.FC = ({ columnData, gridTemplateColumns }) => { +export const SpreadsheetColumns: React.FC = (props) => { + const { columnData, displayFilters, gridTemplateColumns, handleDisplayFiltersUpdate } = props; + const { storedValue: selectedMenuItem, setValue: setSelectedMenuItem } = useLocalStorage( "spreadsheetViewSorting", "" ); - const { storedValue: activeSortingProperty, setValue: setActiveSortingProperty } = - useLocalStorage("spreadsheetViewActiveSortingProperty", ""); - - const { displayFilters, setDisplayFilters } = useSpreadsheetIssuesView(); + const { storedValue: activeSortingProperty, setValue: setActiveSortingProperty } = useLocalStorage( + "spreadsheetViewActiveSortingProperty", + "" + ); const handleOrderBy = (order: TIssueOrderByOptions, itemKey: string) => { - setDisplayFilters({ order_by: order }); + handleDisplayFiltersUpdate({ order_by: order }); setSelectedMenuItem(`${order}_${itemKey}`); setActiveSortingProperty(order === "-created_at" ? "" : itemKey); }; return ( -
+
{columnData.map((col: any) => { if (col.isActive) { return (
{col.propertyName === "title" ? ( @@ -108,10 +106,7 @@ export const SpreadsheetColumns: React.FC = ({ columnData, gridTemplateCo {col.propertyName === "assignee" || col.propertyName === "labels" ? ( <> - + A @@ -123,10 +118,7 @@ export const SpreadsheetColumns: React.FC = ({ columnData, gridTemplateCo col.propertyName === "updated_on" ? ( <> - + New @@ -136,10 +128,7 @@ export const SpreadsheetColumns: React.FC = ({ columnData, gridTemplateCo ) : ( <> - + First @@ -151,18 +140,14 @@ export const SpreadsheetColumns: React.FC = ({ columnData, gridTemplateCo
{ @@ -180,10 +165,7 @@ export const SpreadsheetColumns: React.FC = ({ columnData, gridTemplateCo {col.propertyName === "assignee" || col.propertyName === "labels" ? ( <> - + = ({ columnData, gridTemplateCo ) : col.propertyName === "due_date" ? ( <> - + = ({ columnData, gridTemplateCo ) : ( <> - + = ({ columnData, gridTemplateCo
@@ -243,9 +217,7 @@ export const SpreadsheetColumns: React.FC = ({ columnData, gridTemplateCo selectedMenuItem.includes(col.propertyName) && ( { diff --git a/web/components/core/views/spreadsheet-view/spreadsheet-issues.tsx b/web/components/core/views/spreadsheet-view/spreadsheet-issues.tsx index 6677e8849..8290984fa 100644 --- a/web/components/core/views/spreadsheet-view/spreadsheet-issues.tsx +++ b/web/components/core/views/spreadsheet-view/spreadsheet-issues.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React from "react"; // components import { SingleSpreadsheetIssue } from "components/core"; diff --git a/web/components/core/views/spreadsheet-view/spreadsheet-view.tsx b/web/components/core/views/spreadsheet-view/spreadsheet-view.tsx index 1076f30d0..dc5431a67 100644 --- a/web/components/core/views/spreadsheet-view/spreadsheet-view.tsx +++ b/web/components/core/views/spreadsheet-view/spreadsheet-view.tsx @@ -9,7 +9,6 @@ import { CustomMenu, Spinner } from "components/ui"; import { IssuePeekOverview } from "components/issues"; // hooks import useIssuesProperties from "hooks/use-issue-properties"; -import useSpreadsheetIssuesView from "hooks/use-spreadsheet-issues-view"; // types import { ICurrentUserResponse, IIssue, Properties, UserAuth } from "types"; // constants @@ -39,8 +38,6 @@ export const SpreadsheetView: React.FC = ({ const type = cycleId ? "cycle" : moduleId ? "module" : "issue"; - const { spreadsheetIssues, mutateIssues } = useSpreadsheetIssuesView(); - const [properties] = useIssuesProperties(workspaceSlug as string, projectId as string); const columnData = SPREADSHEET_COLUMN.map((column) => ({ @@ -59,89 +56,89 @@ export const SpreadsheetView: React.FC = ({ .map((column) => column.colSize) .join(" "); - return ( - <> - mutateIssues()} - projectId={projectId?.toString() ?? ""} - workspaceSlug={workspaceSlug?.toString() ?? ""} - readOnly={disableUserActions} - /> -
-
- -
- {spreadsheetIssues ? ( -
- {spreadsheetIssues.map((issue: IIssue, index) => ( - - ))} -
- {type === "issue" ? ( - - ) : ( - !disableUserActions && ( - - - Add Issue - - } - position="left" - optionsClassName="left-5 !w-36" - noBorder - > - { - const e = new KeyboardEvent("keydown", { key: "c" }); - document.dispatchEvent(e); - }} - > - Create new - - {openIssuesListModal && ( - - Add an existing issue - - )} - - ) - )} -
-
- ) : ( - - )} -
- - ); + return null; + + // return ( + // <> + // mutateIssues()} + // projectId={projectId?.toString() ?? ""} + // workspaceSlug={workspaceSlug?.toString() ?? ""} + // readOnly={disableUserActions} + // /> + //
+ //
+ // + //
+ // {spreadsheetIssues ? ( + //
+ // {spreadsheetIssues.map((issue: IIssue, index) => ( + // + // ))} + //
+ // {type === "issue" ? ( + // + // ) : ( + // !disableUserActions && ( + // + // + // Add Issue + // + // } + // position="left" + // optionsClassName="left-5 !w-36" + // noBorder + // > + // { + // const e = new KeyboardEvent("keydown", { key: "c" }); + // document.dispatchEvent(e); + // }} + // > + // Create new + // + // {openIssuesListModal && ( + // Add an existing issue + // )} + // + // ) + // )} + //
+ //
+ // ) : ( + // + // )} + //
+ // + // ); }; diff --git a/web/components/issues/delete-issue-modal.tsx b/web/components/issues/delete-issue-modal.tsx index 81e2d6263..0b7219a1d 100644 --- a/web/components/issues/delete-issue-modal.tsx +++ b/web/components/issues/delete-issue-modal.tsx @@ -12,7 +12,6 @@ import issueServices from "services/issue.service"; import useIssuesView from "hooks/use-issues-view"; import useCalendarIssuesView from "hooks/use-calendar-issues-view"; import useToast from "hooks/use-toast"; -import useSpreadsheetIssuesView from "hooks/use-spreadsheet-issues-view"; // icons import { ExclamationTriangleIcon } from "@heroicons/react/24/outline"; // ui @@ -54,7 +53,6 @@ export const DeleteIssueModal: React.FC = ({ const { displayFilters, params } = useIssuesView(); const { params: calendarParams } = useCalendarIssuesView(); - const { params: spreadsheetParams } = useSpreadsheetIssuesView(); const { setToastAlert } = useToast(); @@ -86,13 +84,6 @@ export const DeleteIssueModal: React.FC = ({ mutate(calendarFetchKey, (prevData) => (prevData ?? []).filter((p) => p.id !== data.id), false); } else if (displayFilters.layout === "spreadsheet") { - const spreadsheetFetchKey = cycleId - ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), spreadsheetParams) - : moduleId - ? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), spreadsheetParams) - : viewId - ? VIEW_ISSUES(viewId.toString(), spreadsheetParams) - : PROJECT_ISSUES_LIST_WITH_PARAMS(data.project, spreadsheetParams); if (data.parent) { mutate( SUB_ISSUES(data.parent.toString()), @@ -107,13 +98,6 @@ export const DeleteIssueModal: React.FC = ({ }, false ); - mutate(spreadsheetFetchKey); - } else { - mutate( - spreadsheetFetchKey, - (prevData) => (prevData ?? []).filter((p) => p.id !== data.id), - false - ); } } else { if (cycleId) mutate(CYCLE_ISSUES_WITH_PARAMS(cycleId as string, params)); diff --git a/web/components/issues/draft-issue-modal.tsx b/web/components/issues/draft-issue-modal.tsx index b1dcf0cc0..0a6ce7ec7 100644 --- a/web/components/issues/draft-issue-modal.tsx +++ b/web/components/issues/draft-issue-modal.tsx @@ -13,7 +13,6 @@ import useUser from "hooks/use-user"; import useIssuesView from "hooks/use-issues-view"; import useCalendarIssuesView from "hooks/use-calendar-issues-view"; import useToast from "hooks/use-toast"; -import useSpreadsheetIssuesView from "hooks/use-spreadsheet-issues-view"; import useLocalStorage from "hooks/use-local-storage"; import useProjects from "hooks/use-projects"; import useMyIssues from "hooks/my-issues/use-my-issues"; @@ -81,7 +80,6 @@ export const CreateUpdateDraftIssueModal: React.FC = (props) = const { displayFilters, params } = useIssuesView(); const { params: calendarParams } = useCalendarIssuesView(); const { ...viewGanttParams } = params; - const { params: spreadsheetParams } = useSpreadsheetIssuesView(); const { user } = useUser(); const { projects } = useProjects(); @@ -156,14 +154,6 @@ export const CreateUpdateDraftIssueModal: React.FC = (props) = ? VIEW_ISSUES(viewId.toString(), calendarParams) : PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject?.toString() ?? "", calendarParams); - const spreadsheetFetchKey = cycleId - ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), spreadsheetParams) - : moduleId - ? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), spreadsheetParams) - : viewId - ? VIEW_ISSUES(viewId.toString(), spreadsheetParams) - : PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject?.toString() ?? "", spreadsheetParams); - const ganttFetchKey = cycleId ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString()) : moduleId @@ -187,7 +177,6 @@ export const CreateUpdateDraftIssueModal: React.FC = (props) = start_target_date: true, order_by: "sort_order", }); - if (displayFilters.layout === "spreadsheet") mutate(spreadsheetFetchKey); if (groupedIssues) mutateMyIssues(); setToastAlert({ @@ -222,7 +211,6 @@ export const CreateUpdateDraftIssueModal: React.FC = (props) = mutate(PROJECT_ISSUES_DETAILS, (prevData) => ({ ...prevData, ...res }), false); } else { if (displayFilters.layout === "calendar") mutate(calendarFetchKey); - if (displayFilters.layout === "spreadsheet") mutate(spreadsheetFetchKey); if (payload.parent) mutate(SUB_ISSUES(payload.parent.toString())); mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject ?? "", params)); mutate(PROJECT_DRAFT_ISSUES_LIST_WITH_PARAMS(activeProject ?? "", params)); @@ -308,7 +296,6 @@ export const CreateUpdateDraftIssueModal: React.FC = (props) = start_target_date: true, order_by: "sort_order", }); - if (displayFilters.layout === "spreadsheet") mutate(spreadsheetFetchKey); if (groupedIssues) mutateMyIssues(); setToastAlert({ diff --git a/web/components/issues/issue-layouts/gantt/root.tsx b/web/components/issues/issue-layouts/gantt/root.tsx index fd4df3e69..732134b46 100644 --- a/web/components/issues/issue-layouts/gantt/root.tsx +++ b/web/components/issues/issue-layouts/gantt/root.tsx @@ -25,9 +25,6 @@ export const GanttLayout: React.FC = observer(() => { const isAllowed = projectDetails?.member_role === 20 || projectDetails?.member_role === 15; - console.log("issues", issues); - console.log("appliedFilters", issueFilterStore.appliedFilters); - return ( <> { enableBlockRightResize={isAllowed} enableBlockMove={isAllowed} enableReorder={appliedDisplayFilters.order_by === "sort_order" && isAllowed} - bottomSpacing />
diff --git a/web/components/issues/issue-layouts/index.ts b/web/components/issues/issue-layouts/index.ts index b1490b52e..f000ec7bf 100644 --- a/web/components/issues/issue-layouts/index.ts +++ b/web/components/issues/issue-layouts/index.ts @@ -2,3 +2,4 @@ export * from "./calendar"; export * from "./gantt"; export * from "./header"; export * from "./kanban"; +export * from "./spreadsheet"; diff --git a/web/components/issues/issue-layouts/spreadsheet/index.ts b/web/components/issues/issue-layouts/spreadsheet/index.ts new file mode 100644 index 000000000..1efe34c51 --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/index.ts @@ -0,0 +1 @@ +export * from "./root"; diff --git a/web/components/issues/issue-layouts/spreadsheet/root.tsx b/web/components/issues/issue-layouts/spreadsheet/root.tsx new file mode 100644 index 000000000..4ceb15fc6 --- /dev/null +++ b/web/components/issues/issue-layouts/spreadsheet/root.tsx @@ -0,0 +1,160 @@ +import React, { useCallback, useState } from "react"; +import { useRouter } from "next/router"; +import { observer } from "mobx-react-lite"; + +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import useUser from "hooks/use-user"; +import useProjectDetails from "hooks/use-project-details"; +// components +import { SpreadsheetColumns, SpreadsheetIssues } from "components/core"; +import { IssuePeekOverview } from "components/issues"; +// ui +import { CustomMenu, Spinner } from "components/ui"; +// icon +import { PlusIcon } from "@heroicons/react/24/outline"; +// types +import { IIssue, IIssueDisplayFilterOptions, IIssueDisplayProperties } from "types"; +import { IIssueUnGroupedStructure } from "store/issue"; +// constants +import { SPREADSHEET_COLUMN } from "constants/spreadsheet"; + +export const SpreadsheetLayout: React.FC = observer(() => { + const [expandedIssues, setExpandedIssues] = useState([]); + + const router = useRouter(); + const { workspaceSlug, projectId, cycleId, moduleId } = router.query; + + const { user } = useUser(); + const { projectDetails } = useProjectDetails(); + + const { issue: issueStore, issueFilter: issueFilterStore } = useMobxStore(); + + const issues = issueStore.getIssues; + const issueDisplayProperties = issueFilterStore.userDisplayProperties; + + const handleDisplayFiltersUpdate = useCallback( + (updatedDisplayFilter: Partial) => { + if (!workspaceSlug || !projectId) return; + + issueFilterStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), { + display_filters: { + ...updatedDisplayFilter, + }, + }); + }, + [issueFilterStore, projectId, workspaceSlug] + ); + + const type = cycleId ? "cycle" : moduleId ? "module" : "issue"; + + const columnData = SPREADSHEET_COLUMN.map((column) => ({ + ...column, + isActive: issueDisplayProperties + ? column.propertyName === "labels" + ? issueDisplayProperties[column.propertyName as keyof IIssueDisplayProperties] + : column.propertyName === "title" + ? true + : issueDisplayProperties[column.propertyName as keyof IIssueDisplayProperties] + : false, + })); + + const gridTemplateColumns = columnData + .filter((column) => column.isActive) + .map((column) => column.colSize) + .join(" "); + + const isAllowed = projectDetails?.member_role === 20 || projectDetails?.member_role === 15; + + return ( + <> + +
+
+ +
+ {issues ? ( +
+ {(issues as IIssueUnGroupedStructure).map((issue: IIssue, index) => ( + {}} + disableUserActions={!isAllowed} + user={user} + userAuth={{ + isViewer: projectDetails?.member_role === 5, + isGuest: projectDetails?.member_role === 10, + isMember: projectDetails?.member_role === 15, + isOwner: projectDetails?.member_role === 20, + }} + /> + ))} +
+ {type === "issue" ? ( + + ) : ( + isAllowed && ( + + + Add Issue + + } + position="left" + optionsClassName="left-5 !w-36" + noBorder + > + { + const e = new KeyboardEvent("keydown", { key: "c" }); + document.dispatchEvent(e); + }} + > + Create new + + {true && {}}>Add an existing issue} + + ) + )} +
+
+ ) : ( + + )} +
+ + ); +}); diff --git a/web/components/issues/modal.tsx b/web/components/issues/modal.tsx index 9ea5f1871..28da11878 100644 --- a/web/components/issues/modal.tsx +++ b/web/components/issues/modal.tsx @@ -16,7 +16,6 @@ import useIssuesView from "hooks/use-issues-view"; import useCalendarIssuesView from "hooks/use-calendar-issues-view"; import useToast from "hooks/use-toast"; import useInboxView from "hooks/use-inbox-view"; -import useSpreadsheetIssuesView from "hooks/use-spreadsheet-issues-view"; import useProjects from "hooks/use-projects"; import useMyIssues from "hooks/my-issues/use-my-issues"; import useLocalStorage from "hooks/use-local-storage"; @@ -87,7 +86,6 @@ export const CreateUpdateIssueModal: React.FC = ({ const { params: calendarParams } = useCalendarIssuesView(); const { ...viewGanttParams } = params; const { params: inboxParams } = useInboxView(); - const { params: spreadsheetParams } = useSpreadsheetIssuesView(); const { user } = useUser(); const { projects } = useProjects(); @@ -280,14 +278,6 @@ export const CreateUpdateIssueModal: React.FC = ({ ? VIEW_ISSUES(viewId.toString(), calendarParams) : PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject?.toString() ?? "", calendarParams); - const spreadsheetFetchKey = cycleId - ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), spreadsheetParams) - : moduleId - ? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), spreadsheetParams) - : viewId - ? VIEW_ISSUES(viewId.toString(), spreadsheetParams) - : PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject?.toString() ?? "", spreadsheetParams); - const ganttFetchKey = cycleId ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString()) : moduleId @@ -314,7 +304,6 @@ export const CreateUpdateIssueModal: React.FC = ({ start_target_date: true, order_by: "sort_order", }); - if (displayFilters.layout === "spreadsheet") mutate(spreadsheetFetchKey); if (groupedIssues) mutateMyIssues(); setToastAlert({ @@ -387,7 +376,6 @@ export const CreateUpdateIssueModal: React.FC = ({ mutate(PROJECT_ISSUES_DETAILS, (prevData) => ({ ...prevData, ...res }), false); } else { if (displayFilters.layout === "calendar") mutate(calendarFetchKey); - if (displayFilters.layout === "spreadsheet") mutate(spreadsheetFetchKey); if (payload.parent) mutate(SUB_ISSUES(payload.parent.toString())); mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject ?? "", params)); } diff --git a/web/hooks/gantt-chart/issue-view.tsx b/web/hooks/gantt-chart/issue-view.tsx deleted file mode 100644 index 2f9fd9c18..000000000 --- a/web/hooks/gantt-chart/issue-view.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import useSWR from "swr"; - -// services -import issuesService from "services/issue.service"; -// hooks -import useIssuesView from "hooks/use-issues-view"; -// fetch-keys -import { PROJECT_ISSUES_LIST_WITH_PARAMS } from "constants/fetch-keys"; - -const useGanttChartIssues = (workspaceSlug: string | undefined, projectId: string | undefined) => { - const { displayFilters, filters } = useIssuesView(); - - const params: any = { - order_by: displayFilters.order_by, - type: displayFilters?.type ? displayFilters?.type : undefined, - sub_issue: displayFilters.sub_issue, - assignees: filters?.assignees ? filters?.assignees.join(",") : undefined, - state: filters?.state ? filters?.state.join(",") : undefined, - priority: filters?.priority ? filters?.priority.join(",") : undefined, - labels: filters?.labels ? filters?.labels.join(",") : undefined, - created_by: filters?.created_by ? filters?.created_by.join(",") : undefined, - start_date: filters?.start_date ? filters?.start_date.join(",") : undefined, - target_date: filters?.target_date ? filters?.target_date.join(",") : undefined, - start_target_date: true, // to fetch only issues with a start and target date - }; - - // all issues under the workspace and project - const { data: ganttIssues, mutate: mutateGanttIssues } = useSWR( - workspaceSlug && projectId ? PROJECT_ISSUES_LIST_WITH_PARAMS(projectId, params) : null, - workspaceSlug && projectId - ? () => issuesService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), params) - : null - ); - - return { - ganttIssues, - mutateGanttIssues, - }; -}; - -export default useGanttChartIssues; diff --git a/web/hooks/use-spreadsheet-issues-view.tsx b/web/hooks/use-spreadsheet-issues-view.tsx deleted file mode 100644 index 54cc8670a..000000000 --- a/web/hooks/use-spreadsheet-issues-view.tsx +++ /dev/null @@ -1,116 +0,0 @@ -import { useContext } from "react"; - -import { useRouter } from "next/router"; - -import useSWR from "swr"; - -// contexts -import { issueViewContext } from "contexts/issue-view.context"; -// services -import issuesService from "services/issue.service"; -import cyclesService from "services/cycles.service"; -import modulesService from "services/modules.service"; -// types -import { IIssue } from "types"; -// fetch-keys -import { - CYCLE_ISSUES_WITH_PARAMS, - MODULE_ISSUES_WITH_PARAMS, - PROJECT_ISSUES_LIST_WITH_PARAMS, - VIEW_ISSUES, -} from "constants/fetch-keys"; - -const useSpreadsheetIssuesView = () => { - const { - display_filters: displayFilters, - setDisplayFilters, - filters, - setFilters, - resetFilterToDefault, - setNewFilterDefaultView, - } = useContext(issueViewContext); - - const router = useRouter(); - const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query; - - const params: any = { - order_by: displayFilters?.order_by, - assignees: filters?.assignees ? filters?.assignees.join(",") : undefined, - state: filters?.state ? filters?.state.join(",") : undefined, - priority: filters?.priority ? filters?.priority.join(",") : undefined, - type: displayFilters?.type ? displayFilters?.type : undefined, - labels: filters?.labels ? filters?.labels.join(",") : undefined, - created_by: filters?.created_by ? filters?.created_by.join(",") : undefined, - start_date: filters?.start_date ? filters?.start_date.join(",") : undefined, - target_date: filters?.target_date ? filters?.target_date.join(",") : undefined, - sub_issue: "false", - }; - - const { data: projectSpreadsheetIssues, mutate: mutateProjectSpreadsheetIssues } = useSWR( - workspaceSlug && projectId ? PROJECT_ISSUES_LIST_WITH_PARAMS(projectId.toString(), params) : null, - workspaceSlug && projectId - ? () => issuesService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), params) - : null - ); - - const { data: cycleSpreadsheetIssues, mutate: mutateCycleSpreadsheetIssues } = useSWR( - workspaceSlug && projectId && cycleId ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), params) : null, - workspaceSlug && projectId && cycleId - ? () => - cyclesService.getCycleIssuesWithParams( - workspaceSlug.toString(), - projectId.toString(), - cycleId.toString(), - params - ) - : null - ); - - const { data: moduleSpreadsheetIssues, mutate: mutateModuleSpreadsheetIssues } = useSWR( - workspaceSlug && projectId && moduleId ? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), params) : null, - workspaceSlug && projectId && moduleId - ? () => - modulesService.getModuleIssuesWithParams( - workspaceSlug.toString(), - projectId.toString(), - moduleId.toString(), - params - ) - : null - ); - - const { data: viewSpreadsheetIssues, mutate: mutateViewSpreadsheetIssues } = useSWR( - workspaceSlug && projectId && viewId && params ? VIEW_ISSUES(viewId.toString(), params) : null, - workspaceSlug && projectId && viewId && params - ? () => issuesService.getIssuesWithParams(workspaceSlug.toString(), projectId.toString(), params) - : null - ); - - const spreadsheetIssues = cycleId - ? (cycleSpreadsheetIssues as IIssue[]) - : moduleId - ? (moduleSpreadsheetIssues as IIssue[]) - : viewId - ? (viewSpreadsheetIssues as IIssue[]) - : (projectSpreadsheetIssues as IIssue[]); - - return { - displayFilters, - setDisplayFilters, - mutateIssues: cycleId - ? mutateCycleSpreadsheetIssues - : moduleId - ? mutateModuleSpreadsheetIssues - : viewId - ? mutateViewSpreadsheetIssues - : mutateProjectSpreadsheetIssues, - spreadsheetIssues: spreadsheetIssues ?? [], - filters, - setFilters, - params, - resetFilterToDefault, - setNewFilterDefaultView, - } as const; -}; - -export default useSpreadsheetIssuesView; From 569a6c3383f89b706c15b67aa995af2c31b86518 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Mon, 2 Oct 2023 12:41:40 +0530 Subject: [PATCH 2/2] dev: applied filters list implementation using MobX (#2325) * dev: applied filters list UI * fix: filter item height * chore: remove unnecessary classes * fix: params generator --- web/components/core/views/all-views.tsx | 5 +- web/components/issue-layouts/root.tsx | 8 +- .../calendar/dropdowns/months-dropdown.tsx | 4 +- .../issue-layouts/calendar/issue-blocks.tsx | 6 +- .../filters/applied-filters/date.tsx | 54 +++++++ .../filters/applied-filters/filters-list.tsx | 141 ++++++++++++++++++ .../filters/applied-filters/index.ts | 7 + .../filters/applied-filters/label.tsx | 45 ++++++ .../filters/applied-filters/members.tsx | 42 ++++++ .../filters/applied-filters/priority.tsx | 47 ++++++ .../filters/applied-filters/state-group.tsx | 33 ++++ .../filters/applied-filters/state.tsx | 45 ++++++ .../display-filters-selection.tsx | 0 .../display-filters/display-properties.tsx | 0 .../header/display-filters/extra-options.tsx | 0 .../header/display-filters/group-by.tsx | 0 .../header/display-filters/index.ts | 0 .../header/display-filters/issue-type.tsx | 0 .../header/display-filters/order-by.tsx | 0 .../header/display-filters/sub-group-by.tsx | 0 .../header/filters/assignee.tsx} | 0 .../header/filters/created-by.tsx | 0 .../header/filters/filters-selection.tsx | 2 +- .../{ => filters}/header/filters/index.ts | 2 +- .../{ => filters}/header/filters/labels.tsx | 0 .../{ => filters}/header/filters/priority.tsx | 0 .../header/filters/start-date.tsx | 0 .../header/filters/state-group.tsx | 0 .../{ => filters}/header/filters/state.tsx | 0 .../header/filters/target-date.tsx | 0 .../{ => filters}/header/helpers/dropdown.tsx | 0 .../header/helpers/filter-header.tsx | 0 .../header/helpers/filter-option.tsx | 0 .../{ => filters}/header/helpers/index.ts | 0 .../{ => filters}/header/index.ts | 0 .../{ => filters}/header/layout-selection.tsx | 0 .../issues/issue-layouts/filters/index.ts | 2 + web/components/issues/issue-layouts/index.ts | 2 +- web/store/issue_filters.ts | 22 +-- 39 files changed, 434 insertions(+), 33 deletions(-) create mode 100644 web/components/issues/issue-layouts/filters/applied-filters/date.tsx create mode 100644 web/components/issues/issue-layouts/filters/applied-filters/filters-list.tsx create mode 100644 web/components/issues/issue-layouts/filters/applied-filters/index.ts create mode 100644 web/components/issues/issue-layouts/filters/applied-filters/label.tsx create mode 100644 web/components/issues/issue-layouts/filters/applied-filters/members.tsx create mode 100644 web/components/issues/issue-layouts/filters/applied-filters/priority.tsx create mode 100644 web/components/issues/issue-layouts/filters/applied-filters/state-group.tsx create mode 100644 web/components/issues/issue-layouts/filters/applied-filters/state.tsx rename web/components/issues/issue-layouts/{ => filters}/header/display-filters/display-filters-selection.tsx (100%) rename web/components/issues/issue-layouts/{ => filters}/header/display-filters/display-properties.tsx (100%) rename web/components/issues/issue-layouts/{ => filters}/header/display-filters/extra-options.tsx (100%) rename web/components/issues/issue-layouts/{ => filters}/header/display-filters/group-by.tsx (100%) rename web/components/issues/issue-layouts/{ => filters}/header/display-filters/index.ts (100%) rename web/components/issues/issue-layouts/{ => filters}/header/display-filters/issue-type.tsx (100%) rename web/components/issues/issue-layouts/{ => filters}/header/display-filters/order-by.tsx (100%) rename web/components/issues/issue-layouts/{ => filters}/header/display-filters/sub-group-by.tsx (100%) rename web/components/issues/issue-layouts/{header/filters/assignees.tsx => filters/header/filters/assignee.tsx} (100%) rename web/components/issues/issue-layouts/{ => filters}/header/filters/created-by.tsx (100%) rename web/components/issues/issue-layouts/{ => filters}/header/filters/filters-selection.tsx (99%) rename web/components/issues/issue-layouts/{ => filters}/header/filters/index.ts (89%) rename web/components/issues/issue-layouts/{ => filters}/header/filters/labels.tsx (100%) rename web/components/issues/issue-layouts/{ => filters}/header/filters/priority.tsx (100%) rename web/components/issues/issue-layouts/{ => filters}/header/filters/start-date.tsx (100%) rename web/components/issues/issue-layouts/{ => filters}/header/filters/state-group.tsx (100%) rename web/components/issues/issue-layouts/{ => filters}/header/filters/state.tsx (100%) rename web/components/issues/issue-layouts/{ => filters}/header/filters/target-date.tsx (100%) rename web/components/issues/issue-layouts/{ => filters}/header/helpers/dropdown.tsx (100%) rename web/components/issues/issue-layouts/{ => filters}/header/helpers/filter-header.tsx (100%) rename web/components/issues/issue-layouts/{ => filters}/header/helpers/filter-option.tsx (100%) rename web/components/issues/issue-layouts/{ => filters}/header/helpers/index.ts (100%) rename web/components/issues/issue-layouts/{ => filters}/header/index.ts (100%) rename web/components/issues/issue-layouts/{ => filters}/header/layout-selection.tsx (100%) create mode 100644 web/components/issues/issue-layouts/filters/index.ts diff --git a/web/components/core/views/all-views.tsx b/web/components/core/views/all-views.tsx index e8478e71a..c5fcc7300 100644 --- a/web/components/core/views/all-views.tsx +++ b/web/components/core/views/all-views.tsx @@ -6,7 +6,7 @@ import useSWR from "swr"; // mobx store import { useMobxStore } from "lib/mobx/store-provider"; // components -import { CalendarLayout, GanttLayout, KanBanLayout, SpreadsheetLayout } from "components/issues"; +import { AppliedFiltersList, CalendarLayout, GanttLayout, KanBanLayout, SpreadsheetLayout } from "components/issues"; export const AllViews: React.FC = observer(() => { const router = useRouter(); @@ -34,7 +34,8 @@ export const AllViews: React.FC = observer(() => { const activeLayout = issueFilterStore.userDisplayFilters.layout; return ( -
+
+ {activeLayout === "kanban" ? ( ) : activeLayout === "calendar" ? ( diff --git a/web/components/issue-layouts/root.tsx b/web/components/issue-layouts/root.tsx index 38029c633..90e1394d4 100644 --- a/web/components/issue-layouts/root.tsx +++ b/web/components/issue-layouts/root.tsx @@ -1,9 +1,9 @@ import React from "react"; // components -import { LayoutSelection } from "../issues/issue-layouts/header/layout-selection"; -import { IssueDropdown } from "../issues/issue-layouts/header/helpers/dropdown"; -import { FilterSelection } from "../issues/issue-layouts/header/filters/filters-selection"; -import { DisplayFiltersSelection } from "../issues/issue-layouts/header/display-filters"; +import { LayoutSelection } from "../issues/issue-layouts/filters/header/layout-selection"; +import { IssueDropdown } from "../issues/issue-layouts/filters/header/helpers/dropdown"; +import { FilterSelection } from "../issues/issue-layouts/filters/header/filters/filters-selection"; +import { DisplayFiltersSelection } from "../issues/issue-layouts/filters/header/display-filters"; import { FilterPreview } from "./filters-preview"; diff --git a/web/components/issues/issue-layouts/calendar/dropdowns/months-dropdown.tsx b/web/components/issues/issue-layouts/calendar/dropdowns/months-dropdown.tsx index a6e410e09..2b6c43b25 100644 --- a/web/components/issues/issue-layouts/calendar/dropdowns/months-dropdown.tsx +++ b/web/components/issues/issue-layouts/calendar/dropdowns/months-dropdown.tsx @@ -96,9 +96,7 @@ export const CalendarMonthsDropdown: React.FC = observer(() => { +
+ ))} +
+ ); +}); diff --git a/web/components/issues/issue-layouts/filters/applied-filters/filters-list.tsx b/web/components/issues/issue-layouts/filters/applied-filters/filters-list.tsx new file mode 100644 index 000000000..1a103f8c7 --- /dev/null +++ b/web/components/issues/issue-layouts/filters/applied-filters/filters-list.tsx @@ -0,0 +1,141 @@ +import { useRouter } from "next/router"; +import { observer } from "mobx-react-lite"; + +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +// components +import { + AppliedDateFilters, + AppliedLabelsFilters, + AppliedMembersFilters, + AppliedPriorityFilters, + AppliedStateFilters, + AppliedStateGroupFilters, +} from "components/issues"; +// icons +import { X } from "lucide-react"; +// helpers +import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper"; +// types +import { IIssueFilterOptions } from "types"; + +export const AppliedFiltersList: React.FC = observer(() => { + const router = useRouter(); + const { workspaceSlug, projectId } = router.query; + + const { issueFilter: issueFilterStore, project: projectStore } = useMobxStore(); + + const userFilters = issueFilterStore.userFilters; + + // filters whose value not null or empty array + const appliedFilters: IIssueFilterOptions = {}; + Object.entries(userFilters).forEach(([key, value]) => { + if (!value) return; + + if (Array.isArray(value) && value.length === 0) return; + + appliedFilters[key as keyof IIssueFilterOptions] = value; + }); + + const handleRemoveFilter = (key: keyof IIssueFilterOptions, value: string | null) => { + if (!workspaceSlug || !projectId) return; + + // remove all values of the key if value is null + if (!value) { + issueFilterStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), { + filters: { + [key]: null, + }, + }); + return; + } + + // remove the passed value from the key + let newValues = issueFilterStore.userFilters?.[key] ?? []; + newValues = newValues.filter((val) => val !== value); + + issueFilterStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), { + filters: { + [key]: newValues, + }, + }); + }; + + const handleClearAllFilters = () => { + if (!workspaceSlug || !projectId) return; + + const newFilters: IIssueFilterOptions = {}; + Object.keys(userFilters).forEach((key) => { + newFilters[key as keyof IIssueFilterOptions] = null; + }); + + issueFilterStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), { + filters: { ...newFilters }, + }); + }; + + // return if no filters are applied + if (Object.keys(appliedFilters).length === 0) return null; + + return ( +
+ {Object.entries(appliedFilters).map(([key, value]) => { + const filterKey = key as keyof IIssueFilterOptions; + + return ( +
+ {replaceUnderscoreIfSnakeCase(filterKey)} + {(filterKey === "assignees" || filterKey === "created_by" || filterKey === "subscriber") && ( + handleRemoveFilter(filterKey, val)} + members={projectStore.members?.[projectId?.toString() ?? ""]?.map((m) => m.member)} + values={value} + /> + )} + {(filterKey === "start_date" || filterKey === "target_date") && ( + handleRemoveFilter(filterKey, val)} values={value} /> + )} + {filterKey === "labels" && ( + handleRemoveFilter("labels", val)} + labels={projectStore.labels?.[projectId?.toString() ?? ""] ?? []} + values={value} + /> + )} + {filterKey === "priority" && ( + handleRemoveFilter("priority", val)} values={value} /> + )} + {filterKey === "state" && ( + handleRemoveFilter("state", val)} + states={projectStore.states?.[projectId?.toString() ?? ""]} + values={value} + /> + )} + {filterKey === "state_group" && ( + handleRemoveFilter("state_group", val)} values={value} /> + )} + +
+ ); + })} + +
+ ); +}); diff --git a/web/components/issues/issue-layouts/filters/applied-filters/index.ts b/web/components/issues/issue-layouts/filters/applied-filters/index.ts new file mode 100644 index 000000000..4f9bff886 --- /dev/null +++ b/web/components/issues/issue-layouts/filters/applied-filters/index.ts @@ -0,0 +1,7 @@ +export * from "./date"; +export * from "./filters-list"; +export * from "./label"; +export * from "./members"; +export * from "./priority"; +export * from "./state"; +export * from "./state-group"; diff --git a/web/components/issues/issue-layouts/filters/applied-filters/label.tsx b/web/components/issues/issue-layouts/filters/applied-filters/label.tsx new file mode 100644 index 000000000..abc5a8b2c --- /dev/null +++ b/web/components/issues/issue-layouts/filters/applied-filters/label.tsx @@ -0,0 +1,45 @@ +import { observer } from "mobx-react-lite"; + +// icons +import { X } from "lucide-react"; +// types +import { IIssueLabels } from "types"; + +type Props = { + handleRemove: (val: string) => void; + labels: IIssueLabels[] | undefined; + values: string[]; +}; + +export const AppliedLabelsFilters: React.FC = observer((props) => { + const { handleRemove, labels, values } = props; + + return ( +
+ {values.map((labelId) => { + const labelDetails = labels?.find((l) => l.id === labelId); + + if (!labelDetails) return null; + + return ( +
+ + {labelDetails.name} + +
+ ); + })} +
+ ); +}); diff --git a/web/components/issues/issue-layouts/filters/applied-filters/members.tsx b/web/components/issues/issue-layouts/filters/applied-filters/members.tsx new file mode 100644 index 000000000..0c6d0a6b8 --- /dev/null +++ b/web/components/issues/issue-layouts/filters/applied-filters/members.tsx @@ -0,0 +1,42 @@ +import { observer } from "mobx-react-lite"; + +// ui +import { Avatar } from "components/ui"; +// icons +import { X } from "lucide-react"; +// types +import { IUserLite } from "types"; + +type Props = { + handleRemove: (val: string) => void; + members: IUserLite[] | undefined; + values: string[]; +}; + +export const AppliedMembersFilters: React.FC = observer((props) => { + const { handleRemove, members, values } = props; + + return ( +
+ {values.map((memberId) => { + const memberDetails = members?.find((m) => m.id === memberId); + + if (!memberDetails) return null; + + return ( +
+ + {memberDetails.display_name} + +
+ ); + })} +
+ ); +}); diff --git a/web/components/issues/issue-layouts/filters/applied-filters/priority.tsx b/web/components/issues/issue-layouts/filters/applied-filters/priority.tsx new file mode 100644 index 000000000..44328afff --- /dev/null +++ b/web/components/issues/issue-layouts/filters/applied-filters/priority.tsx @@ -0,0 +1,47 @@ +import { observer } from "mobx-react-lite"; + +// icons +import { PriorityIcon } from "components/icons"; +import { X } from "lucide-react"; +// types +import { TIssuePriorities } from "types"; + +type Props = { + handleRemove: (val: string) => void; + values: string[]; +}; + +export const AppliedPriorityFilters: React.FC = observer((props) => { + const { handleRemove, values } = props; + + return ( +
+ {values.map((priority) => ( +
+ + {priority} + +
+ ))} +
+ ); +}); diff --git a/web/components/issues/issue-layouts/filters/applied-filters/state-group.tsx b/web/components/issues/issue-layouts/filters/applied-filters/state-group.tsx new file mode 100644 index 000000000..bfeab73ab --- /dev/null +++ b/web/components/issues/issue-layouts/filters/applied-filters/state-group.tsx @@ -0,0 +1,33 @@ +import { observer } from "mobx-react-lite"; + +// icons +import { StateGroupIcon } from "components/icons"; +import { X } from "lucide-react"; +import { TStateGroups } from "types"; + +type Props = { + handleRemove: (val: string) => void; + values: string[]; +}; + +export const AppliedStateGroupFilters: React.FC = observer((props) => { + const { handleRemove, values } = props; + + return ( +
+ {values.map((stateGroup) => ( +
+ + {stateGroup} + +
+ ))} +
+ ); +}); diff --git a/web/components/issues/issue-layouts/filters/applied-filters/state.tsx b/web/components/issues/issue-layouts/filters/applied-filters/state.tsx new file mode 100644 index 000000000..830c92088 --- /dev/null +++ b/web/components/issues/issue-layouts/filters/applied-filters/state.tsx @@ -0,0 +1,45 @@ +import { observer } from "mobx-react-lite"; + +// icons +import { StateGroupIcon } from "components/icons"; +import { X } from "lucide-react"; +// helpers +import { getStatesList } from "helpers/state.helper"; +// types +import { IStateResponse } from "types"; + +type Props = { + handleRemove: (val: string) => void; + states: IStateResponse | undefined; + values: string[]; +}; + +export const AppliedStateFilters: React.FC = observer((props) => { + const { handleRemove, states, values } = props; + + const statesList = getStatesList(states); + + return ( +
+ {values.map((stateId) => { + const stateDetails = statesList?.find((s) => s.id === stateId); + + if (!stateDetails) return null; + + return ( +
+ + {stateDetails.name} + +
+ ); + })} +
+ ); +}); diff --git a/web/components/issues/issue-layouts/header/display-filters/display-filters-selection.tsx b/web/components/issues/issue-layouts/filters/header/display-filters/display-filters-selection.tsx similarity index 100% rename from web/components/issues/issue-layouts/header/display-filters/display-filters-selection.tsx rename to web/components/issues/issue-layouts/filters/header/display-filters/display-filters-selection.tsx diff --git a/web/components/issues/issue-layouts/header/display-filters/display-properties.tsx b/web/components/issues/issue-layouts/filters/header/display-filters/display-properties.tsx similarity index 100% rename from web/components/issues/issue-layouts/header/display-filters/display-properties.tsx rename to web/components/issues/issue-layouts/filters/header/display-filters/display-properties.tsx diff --git a/web/components/issues/issue-layouts/header/display-filters/extra-options.tsx b/web/components/issues/issue-layouts/filters/header/display-filters/extra-options.tsx similarity index 100% rename from web/components/issues/issue-layouts/header/display-filters/extra-options.tsx rename to web/components/issues/issue-layouts/filters/header/display-filters/extra-options.tsx diff --git a/web/components/issues/issue-layouts/header/display-filters/group-by.tsx b/web/components/issues/issue-layouts/filters/header/display-filters/group-by.tsx similarity index 100% rename from web/components/issues/issue-layouts/header/display-filters/group-by.tsx rename to web/components/issues/issue-layouts/filters/header/display-filters/group-by.tsx diff --git a/web/components/issues/issue-layouts/header/display-filters/index.ts b/web/components/issues/issue-layouts/filters/header/display-filters/index.ts similarity index 100% rename from web/components/issues/issue-layouts/header/display-filters/index.ts rename to web/components/issues/issue-layouts/filters/header/display-filters/index.ts diff --git a/web/components/issues/issue-layouts/header/display-filters/issue-type.tsx b/web/components/issues/issue-layouts/filters/header/display-filters/issue-type.tsx similarity index 100% rename from web/components/issues/issue-layouts/header/display-filters/issue-type.tsx rename to web/components/issues/issue-layouts/filters/header/display-filters/issue-type.tsx diff --git a/web/components/issues/issue-layouts/header/display-filters/order-by.tsx b/web/components/issues/issue-layouts/filters/header/display-filters/order-by.tsx similarity index 100% rename from web/components/issues/issue-layouts/header/display-filters/order-by.tsx rename to web/components/issues/issue-layouts/filters/header/display-filters/order-by.tsx diff --git a/web/components/issues/issue-layouts/header/display-filters/sub-group-by.tsx b/web/components/issues/issue-layouts/filters/header/display-filters/sub-group-by.tsx similarity index 100% rename from web/components/issues/issue-layouts/header/display-filters/sub-group-by.tsx rename to web/components/issues/issue-layouts/filters/header/display-filters/sub-group-by.tsx diff --git a/web/components/issues/issue-layouts/header/filters/assignees.tsx b/web/components/issues/issue-layouts/filters/header/filters/assignee.tsx similarity index 100% rename from web/components/issues/issue-layouts/header/filters/assignees.tsx rename to web/components/issues/issue-layouts/filters/header/filters/assignee.tsx diff --git a/web/components/issues/issue-layouts/header/filters/created-by.tsx b/web/components/issues/issue-layouts/filters/header/filters/created-by.tsx similarity index 100% rename from web/components/issues/issue-layouts/header/filters/created-by.tsx rename to web/components/issues/issue-layouts/filters/header/filters/created-by.tsx diff --git a/web/components/issues/issue-layouts/header/filters/filters-selection.tsx b/web/components/issues/issue-layouts/filters/header/filters/filters-selection.tsx similarity index 99% rename from web/components/issues/issue-layouts/header/filters/filters-selection.tsx rename to web/components/issues/issue-layouts/filters/header/filters/filters-selection.tsx index f651f8a05..2e31a16a2 100644 --- a/web/components/issues/issue-layouts/header/filters/filters-selection.tsx +++ b/web/components/issues/issue-layouts/filters/header/filters/filters-selection.tsx @@ -147,7 +147,7 @@ export const FilterSelection: React.FC = observer((props) => { )} -
+
{/* priority */} {isFilterEnabled("priority") && (
diff --git a/web/components/issues/issue-layouts/header/filters/index.ts b/web/components/issues/issue-layouts/filters/header/filters/index.ts similarity index 89% rename from web/components/issues/issue-layouts/header/filters/index.ts rename to web/components/issues/issue-layouts/filters/header/filters/index.ts index 40190a10e..847b30874 100644 --- a/web/components/issues/issue-layouts/header/filters/index.ts +++ b/web/components/issues/issue-layouts/filters/header/filters/index.ts @@ -1,4 +1,4 @@ -export * from "./assignees"; +export * from "./assignee"; export * from "./created-by"; export * from "./filters-selection"; export * from "./labels"; diff --git a/web/components/issues/issue-layouts/header/filters/labels.tsx b/web/components/issues/issue-layouts/filters/header/filters/labels.tsx similarity index 100% rename from web/components/issues/issue-layouts/header/filters/labels.tsx rename to web/components/issues/issue-layouts/filters/header/filters/labels.tsx diff --git a/web/components/issues/issue-layouts/header/filters/priority.tsx b/web/components/issues/issue-layouts/filters/header/filters/priority.tsx similarity index 100% rename from web/components/issues/issue-layouts/header/filters/priority.tsx rename to web/components/issues/issue-layouts/filters/header/filters/priority.tsx diff --git a/web/components/issues/issue-layouts/header/filters/start-date.tsx b/web/components/issues/issue-layouts/filters/header/filters/start-date.tsx similarity index 100% rename from web/components/issues/issue-layouts/header/filters/start-date.tsx rename to web/components/issues/issue-layouts/filters/header/filters/start-date.tsx diff --git a/web/components/issues/issue-layouts/header/filters/state-group.tsx b/web/components/issues/issue-layouts/filters/header/filters/state-group.tsx similarity index 100% rename from web/components/issues/issue-layouts/header/filters/state-group.tsx rename to web/components/issues/issue-layouts/filters/header/filters/state-group.tsx diff --git a/web/components/issues/issue-layouts/header/filters/state.tsx b/web/components/issues/issue-layouts/filters/header/filters/state.tsx similarity index 100% rename from web/components/issues/issue-layouts/header/filters/state.tsx rename to web/components/issues/issue-layouts/filters/header/filters/state.tsx diff --git a/web/components/issues/issue-layouts/header/filters/target-date.tsx b/web/components/issues/issue-layouts/filters/header/filters/target-date.tsx similarity index 100% rename from web/components/issues/issue-layouts/header/filters/target-date.tsx rename to web/components/issues/issue-layouts/filters/header/filters/target-date.tsx diff --git a/web/components/issues/issue-layouts/header/helpers/dropdown.tsx b/web/components/issues/issue-layouts/filters/header/helpers/dropdown.tsx similarity index 100% rename from web/components/issues/issue-layouts/header/helpers/dropdown.tsx rename to web/components/issues/issue-layouts/filters/header/helpers/dropdown.tsx diff --git a/web/components/issues/issue-layouts/header/helpers/filter-header.tsx b/web/components/issues/issue-layouts/filters/header/helpers/filter-header.tsx similarity index 100% rename from web/components/issues/issue-layouts/header/helpers/filter-header.tsx rename to web/components/issues/issue-layouts/filters/header/helpers/filter-header.tsx diff --git a/web/components/issues/issue-layouts/header/helpers/filter-option.tsx b/web/components/issues/issue-layouts/filters/header/helpers/filter-option.tsx similarity index 100% rename from web/components/issues/issue-layouts/header/helpers/filter-option.tsx rename to web/components/issues/issue-layouts/filters/header/helpers/filter-option.tsx diff --git a/web/components/issues/issue-layouts/header/helpers/index.ts b/web/components/issues/issue-layouts/filters/header/helpers/index.ts similarity index 100% rename from web/components/issues/issue-layouts/header/helpers/index.ts rename to web/components/issues/issue-layouts/filters/header/helpers/index.ts diff --git a/web/components/issues/issue-layouts/header/index.ts b/web/components/issues/issue-layouts/filters/header/index.ts similarity index 100% rename from web/components/issues/issue-layouts/header/index.ts rename to web/components/issues/issue-layouts/filters/header/index.ts diff --git a/web/components/issues/issue-layouts/header/layout-selection.tsx b/web/components/issues/issue-layouts/filters/header/layout-selection.tsx similarity index 100% rename from web/components/issues/issue-layouts/header/layout-selection.tsx rename to web/components/issues/issue-layouts/filters/header/layout-selection.tsx diff --git a/web/components/issues/issue-layouts/filters/index.ts b/web/components/issues/issue-layouts/filters/index.ts new file mode 100644 index 000000000..427d96943 --- /dev/null +++ b/web/components/issues/issue-layouts/filters/index.ts @@ -0,0 +1,2 @@ +export * from "./header"; +export * from "./applied-filters"; diff --git a/web/components/issues/issue-layouts/index.ts b/web/components/issues/issue-layouts/index.ts index f000ec7bf..8840e08df 100644 --- a/web/components/issues/issue-layouts/index.ts +++ b/web/components/issues/issue-layouts/index.ts @@ -1,5 +1,5 @@ export * from "./calendar"; +export * from "./filters"; export * from "./gantt"; -export * from "./header"; export * from "./kanban"; export * from "./spreadsheet"; diff --git a/web/store/issue_filters.ts b/web/store/issue_filters.ts index ab2d6e0dd..c8af5a51f 100644 --- a/web/store/issue_filters.ts +++ b/web/store/issue_filters.ts @@ -112,22 +112,6 @@ class IssueFilterStore implements IIssueFilterStore { return computedFilters; }; - calendarLayoutDateRange = () => { - const { activeMonthDate, activeWeekDate } = this.rootStore.calendar.calendarFilters; - - const calendarLayout = this.userDisplayFilters.calendar?.layout ?? "month"; - - let filterDate = new Date(); - - if (calendarLayout === "month") filterDate = activeMonthDate; - else filterDate = activeWeekDate; - - const startOfMonth = renderDateFormat(new Date(filterDate.getFullYear(), filterDate.getMonth(), 1)); - const endOfMonth = renderDateFormat(new Date(filterDate.getFullYear(), filterDate.getMonth() + 1, 0)); - - return [`${startOfMonth};after`, `${endOfMonth};before`]; - }; - get appliedFilters(): TIssueParams[] | null { if ( !this.userFilters || @@ -155,12 +139,12 @@ class IssueFilterStore implements IIssueFilterStore { start_target_date: this.userDisplayFilters?.start_target_date || true, }; - if (this.userDisplayFilters.layout === "calendar") filteredRouteParams.target_date = this.calendarLayoutDateRange(); - if (this.userDisplayFilters.layout === "gantt_chart") filteredRouteParams.start_target_date = true; - const filteredParams = handleIssueQueryParamsByLayout(this.userDisplayFilters.layout, "issues"); if (filteredParams) filteredRouteParams = this.computedFilter(filteredRouteParams, filteredParams); + if (this.userDisplayFilters.layout === "calendar") filteredRouteParams.group_by = "target_date"; + if (this.userDisplayFilters.layout === "gantt_chart") filteredRouteParams.start_target_date = true; + return filteredRouteParams; }