From 9831418a11f4e26bc4720f716713b9eebe4f40a4 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Mon, 25 Sep 2023 19:17:40 +0530 Subject: [PATCH] chore: filters dropdown (#2260) * chore: project issues topbar * style: theming and minor UI fixes * refactor: file structure * chore: layout wise authorization added * style: filter dropdowns * chore: add fetch keys * feat: search option for filters * fix: sticky headers * chore: sub_group_by section added --- .../display-filters-selection.tsx | 85 ++++--- .../display-filters/display-properties.tsx | 6 +- .../display-filters/extra-options.tsx | 12 +- .../display-filters/group-by.tsx | 11 +- .../issue-layouts/display-filters/index.ts | 1 + .../display-filters/issue-type.tsx | 9 +- .../display-filters/order-by.tsx | 11 +- .../display-filters/sub-group-by.tsx | 64 +++++ .../issue-layouts/filters/assignees.tsx | 65 +++-- .../issue-layouts/filters/created-by.tsx | 55 +++-- .../filters/filters-selection.tsx | 230 ++++++++++++++++++ web/components/issue-layouts/filters/index.ts | 2 +- .../issue-layouts/filters/labels.tsx | 62 +++-- .../issue-layouts/filters/priority.tsx | 38 ++- .../issue-layouts/filters/start-date.tsx | 14 +- .../issue-layouts/filters/state-group.tsx | 45 ++-- .../issue-layouts/filters/state.tsx | 61 +++-- .../issue-layouts/filters/target-date.tsx | 12 +- .../issue-layouts/helpers/dropdown.tsx | 4 +- .../issue-layouts/helpers/filter-header.tsx | 2 +- .../issue-layouts/helpers/filter-option.tsx | 2 +- .../issue-layouts/layout-selection.tsx | 32 +-- web/components/issue-layouts/root.tsx | 16 +- web/constants/fetch-keys.ts | 121 +++------ web/constants/issue.ts | 140 +++++++++-- web/helpers/issue.helper.ts | 2 + .../projects/[projectId]/issues/index.tsx | 9 +- web/store/issue_filters.ts | 14 +- web/types/view-props.d.ts | 2 + 29 files changed, 786 insertions(+), 341 deletions(-) create mode 100644 web/components/issue-layouts/display-filters/sub-group-by.tsx create mode 100644 web/components/issue-layouts/filters/filters-selection.tsx diff --git a/web/components/issue-layouts/display-filters/display-filters-selection.tsx b/web/components/issue-layouts/display-filters/display-filters-selection.tsx index 64d6ad0df..01394548d 100644 --- a/web/components/issue-layouts/display-filters/display-filters-selection.tsx +++ b/web/components/issue-layouts/display-filters/display-filters-selection.tsx @@ -10,58 +10,65 @@ import { FilterGroupBy, FilterIssueType, FilterOrderBy, + FilterSubGroupBy, } from "components/issue-layouts"; // helpers -import { issueFilterVisibilityData } from "helpers/issue.helper"; +import { ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "constants/issue"; export const DisplayFiltersSelection = observer(() => { const { issueFilter: issueFilterStore } = useMobxStore(); const isDisplayFilterEnabled = (displayFilter: string) => - issueFilterVisibilityData.issues.display_filters[issueFilterStore.userDisplayFilters.layout ?? "list"].includes( - displayFilter - ); + ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues.display_filters[ + issueFilterStore.userDisplayFilters.layout ?? "list" + ].includes(displayFilter); return ( -
- {/*
Search container
*/} -
- {/* display properties */} - {issueFilterVisibilityData.issues.display_properties[issueFilterStore.userDisplayFilters.layout ?? "list"] && ( -
- -
- )} +
+ {/* display properties */} + {ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues.display_properties[ + issueFilterStore.userDisplayFilters.layout ?? "list" + ] && ( +
+ +
+ )} - {/* group by */} - {isDisplayFilterEnabled("group_by") && ( -
- -
- )} + {/* group by */} + {isDisplayFilterEnabled("group_by") && ( +
+ +
+ )} - {/* order by */} - {isDisplayFilterEnabled("order_by") && ( -
- -
- )} + {/* sub-group by */} + {isDisplayFilterEnabled("sub_group_by") && ( +
+ +
+ )} - {/* issue type */} - {isDisplayFilterEnabled("issue_type") && ( -
- -
- )} + {/* order by */} + {isDisplayFilterEnabled("order_by") && ( +
+ +
+ )} - {/* Options */} - {issueFilterVisibilityData.issues.extra_options[issueFilterStore.userDisplayFilters.layout ?? "list"] - .access && ( -
- -
- )} -
+ {/* issue type */} + {isDisplayFilterEnabled("issue_type") && ( +
+ +
+ )} + + {/* Options */} + {ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues.extra_options[issueFilterStore.userDisplayFilters.layout ?? "list"] + .access && ( +
+ +
+ )}
); }); diff --git a/web/components/issue-layouts/display-filters/display-properties.tsx b/web/components/issue-layouts/display-filters/display-properties.tsx index f7fb87d2c..3b3c92df9 100644 --- a/web/components/issue-layouts/display-filters/display-properties.tsx +++ b/web/components/issue-layouts/display-filters/display-properties.tsx @@ -28,14 +28,14 @@ export const FilterDisplayProperties = observer(() => { }; return ( -
+ <> setPreviewEnabled(!previewEnabled)} /> {previewEnabled && ( -
+
{ISSUE_DISPLAY_PROPERTIES.map((displayProperty) => (
)} -
+ ); }); diff --git a/web/components/issue-layouts/display-filters/extra-options.tsx b/web/components/issue-layouts/display-filters/extra-options.tsx index a321c6793..8b2354435 100644 --- a/web/components/issue-layouts/display-filters/extra-options.tsx +++ b/web/components/issue-layouts/display-filters/extra-options.tsx @@ -6,10 +6,8 @@ import { useMobxStore } from "lib/mobx/store-provider"; // components import { FilterHeader, FilterOption } from "components/issue-layouts"; -// helpers -import { issueFilterVisibilityData } from "helpers/issue.helper"; // constants -import { ISSUE_EXTRA_OPTIONS } from "constants/issue"; +import { ISSUE_EXTRA_OPTIONS, ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "constants/issue"; export const FilterExtraOptions = observer(() => { const [previewEnabled, setPreviewEnabled] = useState(true); @@ -18,19 +16,19 @@ export const FilterExtraOptions = observer(() => { const { issueFilter: issueFilterStore } = store; const isExtraOptionEnabled = (option: string) => - issueFilterVisibilityData.issues.extra_options[ + ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues.extra_options[ issueFilterStore.userDisplayFilters.layout ?? "list" ].values.includes(option); return ( -
+ <> setPreviewEnabled(!previewEnabled)} /> {previewEnabled && ( -
+
{ISSUE_EXTRA_OPTIONS.map((option) => { if (!isExtraOptionEnabled(option.key)) return null; @@ -44,6 +42,6 @@ export const FilterExtraOptions = observer(() => { })}
)} -
+ ); }); diff --git a/web/components/issue-layouts/display-filters/group-by.tsx b/web/components/issue-layouts/display-filters/group-by.tsx index 5b95c61b1..5ad584b58 100644 --- a/web/components/issue-layouts/display-filters/group-by.tsx +++ b/web/components/issue-layouts/display-filters/group-by.tsx @@ -6,8 +6,7 @@ import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { useMobxStore } from "lib/mobx/store-provider"; // components -import { FilterHeader } from "../helpers/filter-header"; -import { FilterOption } from "../helpers/filter-option"; +import { FilterHeader, FilterOption } from "components/issue-layouts"; // types import { TIssueGroupByOptions } from "types"; // constants @@ -33,14 +32,14 @@ export const FilterGroupBy = observer(() => { }; return ( -
+ <> setPreviewEnabled(!previewEnabled)} /> {previewEnabled && ( -
+
{ISSUE_GROUP_BY_OPTIONS.map((groupBy) => ( { ))}
)} -
+ ); }); diff --git a/web/components/issue-layouts/display-filters/index.ts b/web/components/issue-layouts/display-filters/index.ts index 27ed9943b..741d0a22a 100644 --- a/web/components/issue-layouts/display-filters/index.ts +++ b/web/components/issue-layouts/display-filters/index.ts @@ -4,3 +4,4 @@ export * from "./extra-options"; export * from "./group-by"; export * from "./issue-type"; export * from "./order-by"; +export * from "./sub-group-by"; diff --git a/web/components/issue-layouts/display-filters/issue-type.tsx b/web/components/issue-layouts/display-filters/issue-type.tsx index 6af37c8d5..c2fe34857 100644 --- a/web/components/issue-layouts/display-filters/issue-type.tsx +++ b/web/components/issue-layouts/display-filters/issue-type.tsx @@ -6,8 +6,7 @@ import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { useMobxStore } from "lib/mobx/store-provider"; // components -import { FilterHeader } from "../helpers/filter-header"; -import { FilterOption } from "../helpers/filter-option"; +import { FilterHeader, FilterOption } from "components/issue-layouts"; // types import { TIssueTypeFilters } from "types"; // constants @@ -33,14 +32,14 @@ export const FilterIssueType = observer(() => { }; return ( -
+ <> setPreviewEnabled(!previewEnabled)} /> {previewEnabled && ( -
+
{ISSUE_FILTER_OPTIONS.map((issueType) => ( { ))}
)} -
+ ); }); diff --git a/web/components/issue-layouts/display-filters/order-by.tsx b/web/components/issue-layouts/display-filters/order-by.tsx index 59993456c..b355dbd1b 100644 --- a/web/components/issue-layouts/display-filters/order-by.tsx +++ b/web/components/issue-layouts/display-filters/order-by.tsx @@ -6,8 +6,7 @@ import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { useMobxStore } from "lib/mobx/store-provider"; // components -import { FilterHeader } from "../helpers/filter-header"; -import { FilterOption } from "../helpers/filter-option"; +import { FilterHeader, FilterOption } from "components/issue-layouts"; // types import { TIssueOrderByOptions } from "types"; // constants @@ -33,14 +32,14 @@ export const FilterOrderBy = observer(() => { }; return ( -
+ <> setPreviewEnabled(!previewEnabled)} /> {previewEnabled && ( -
+
{ISSUE_ORDER_BY_OPTIONS.map((orderBy) => ( { ))}
)} -
+ ); }); diff --git a/web/components/issue-layouts/display-filters/sub-group-by.tsx b/web/components/issue-layouts/display-filters/sub-group-by.tsx new file mode 100644 index 000000000..752804f04 --- /dev/null +++ b/web/components/issue-layouts/display-filters/sub-group-by.tsx @@ -0,0 +1,64 @@ +import React, { useState } from "react"; + +import { useRouter } from "next/router"; + +// mobx +import { observer } from "mobx-react-lite"; +import { useMobxStore } from "lib/mobx/store-provider"; +// components +import { FilterHeader, FilterOption } from "components/issue-layouts"; +// types +import { TIssueGroupByOptions } from "types"; +// constants +import { ISSUE_GROUP_BY_OPTIONS } from "constants/issue"; + +export const FilterSubGroupBy = observer(() => { + const [previewEnabled, setPreviewEnabled] = useState(true); + + const router = useRouter(); + const { workspaceSlug, projectId } = router.query; + + const store = useMobxStore(); + const { issueFilter: issueFilterStore } = store; + + const handleSubGroupBy = (value: TIssueGroupByOptions) => { + if (!workspaceSlug || !projectId) return; + + issueFilterStore.updateUserFilters(workspaceSlug.toString(), projectId.toString(), { + display_filters: { + sub_group_by: value, + }, + }); + }; + + return ( + <> + setPreviewEnabled(!previewEnabled)} + /> + {previewEnabled && ( +
+ {ISSUE_GROUP_BY_OPTIONS.map((subGroupBy) => { + if ( + issueFilterStore.userDisplayFilters.group_by !== null && + issueFilterStore.userDisplayFilters.sub_group_by === issueFilterStore.userDisplayFilters.group_by + ) + return null; + + return ( + handleSubGroupBy(subGroupBy.key)} + title={subGroupBy.title} + multiple={false} + /> + ); + })} +
+ )} + + ); +}); diff --git a/web/components/issue-layouts/filters/assignees.tsx b/web/components/issue-layouts/filters/assignees.tsx index b60686ea3..f79db0e33 100644 --- a/web/components/issue-layouts/filters/assignees.tsx +++ b/web/components/issue-layouts/filters/assignees.tsx @@ -6,15 +6,12 @@ import { useMobxStore } from "lib/mobx/store-provider"; // components import { FilterHeader, FilterOption } from "components/issue-layouts"; // ui -import { Avatar } from "components/ui"; +import { Avatar, Loader } from "components/ui"; -type Props = { - workspaceSlug: string; - projectId: string; -}; +type Props = { workspaceSlug: string; projectId: string; itemsToRender: number }; export const FilterAssignees: React.FC = observer((props) => { - const { workspaceSlug, projectId } = props; + const { workspaceSlug, projectId, itemsToRender } = props; const [previewEnabled, setPreviewEnabled] = useState(true); @@ -34,31 +31,51 @@ export const FilterAssignees: React.FC = observer((props) => { }); }; + const appliedFiltersCount = issueFilterStore.userFilters?.assignees?.length ?? 0; + + const filteredOptions = projectStore.members?.[projectId?.toString() ?? ""]?.filter((member) => + member.member.display_name.toLowerCase().includes(issueFilterStore.filtersSearchQuery.toLowerCase()) + ); + return ( -
+ <> 0 ? ` (${appliedFiltersCount})` : ""}`} isPreviewEnabled={previewEnabled} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} /> {previewEnabled && ( -
- {projectStore.members?.[projectId?.toString() ?? ""]?.map((member) => ( - handleUpdateAssignees(member.member?.id)} - icon={} - title={member.member?.display_name} - /> - ))} +
+ {filteredOptions ? ( + filteredOptions.length > 0 ? ( + filteredOptions + .slice(0, itemsToRender) + .map((member) => ( + handleUpdateAssignees(member.member?.id)} + icon={} + title={member.member?.display_name} + /> + )) + ) : ( +

No matches found

+ ) + ) : ( + + + + + + )}
)} -
+ ); }); diff --git a/web/components/issue-layouts/filters/created-by.tsx b/web/components/issue-layouts/filters/created-by.tsx index 42fb8082e..c34b461b1 100644 --- a/web/components/issue-layouts/filters/created-by.tsx +++ b/web/components/issue-layouts/filters/created-by.tsx @@ -6,15 +6,12 @@ import { useMobxStore } from "lib/mobx/store-provider"; // components import { FilterHeader, FilterOption } from "components/issue-layouts"; // ui -import { Avatar } from "components/ui"; +import { Avatar, Loader } from "components/ui"; -type Props = { - workspaceSlug: string; - projectId: string; -}; +type Props = { workspaceSlug: string; projectId: string; itemsToRender: number }; export const FilterCreatedBy: React.FC = observer((props) => { - const { workspaceSlug, projectId } = props; + const { workspaceSlug, projectId, itemsToRender } = props; const [previewEnabled, setPreviewEnabled] = useState(true); @@ -34,26 +31,46 @@ export const FilterCreatedBy: React.FC = observer((props) => { }); }; + const appliedFiltersCount = issueFilterStore.userFilters?.created_by?.length ?? 0; + + const filteredOptions = projectStore.members?.[projectId?.toString() ?? ""]?.filter((member) => + member.member.display_name.toLowerCase().includes(issueFilterStore.filtersSearchQuery.toLowerCase()) + ); + return ( -
+ <> 0 ? ` (${appliedFiltersCount})` : ""}`} isPreviewEnabled={previewEnabled} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} /> {previewEnabled && ( -
- {projectStore.members?.[projectId?.toString() ?? ""]?.map((member) => ( - handleUpdateCreatedBy(member.member?.id)} - icon={} - title={member.member?.display_name} - /> - ))} +
+ {filteredOptions ? ( + filteredOptions.length > 0 ? ( + filteredOptions + .slice(0, itemsToRender) + .map((member) => ( + handleUpdateCreatedBy(member.member?.id)} + icon={} + title={member.member?.display_name} + /> + )) + ) : ( +

No matches found

+ ) + ) : ( + + + + + + )}
)} -
+ ); }); diff --git a/web/components/issue-layouts/filters/filters-selection.tsx b/web/components/issue-layouts/filters/filters-selection.tsx new file mode 100644 index 000000000..c0416e356 --- /dev/null +++ b/web/components/issue-layouts/filters/filters-selection.tsx @@ -0,0 +1,230 @@ +import React, { useState } from "react"; + +// mobx +import { observer } from "mobx-react-lite"; +import { useMobxStore } from "lib/mobx/store-provider"; +// components +import { + FilterAssignees, + FilterCreatedBy, + FilterLabels, + FilterPriority, + FilterState, + FilterStateGroup, +} from "components/issue-layouts"; +// icons +import { Search, X } from "lucide-react"; +// helpers +import { getStatesList } from "helpers/state.helper"; +// types +import { IIssueFilterOptions } from "types"; +// constants +import { ISSUE_PRIORITIES, ISSUE_STATE_GROUPS } from "constants/issue"; + +type Props = { + workspaceSlug: string; + projectId: string; +}; + +export const FilterSelection: React.FC = observer((props) => { + const { workspaceSlug, projectId } = props; + + const { issueFilter: issueFilterStore, project: projectStore } = useMobxStore(); + + const statesList = getStatesList(projectStore.states?.[projectId?.toString() ?? ""]); + + const [filtersToRender, setFiltersToRender] = useState<{ + [key in keyof IIssueFilterOptions]: { + currentLength: number; + totalLength: number; + }; + }>({ + assignees: { + currentLength: 5, + totalLength: projectStore.members?.[projectId]?.length ?? 0, + }, + created_by: { + currentLength: 5, + totalLength: projectStore.members?.[projectId]?.length ?? 0, + }, + labels: { + currentLength: 5, + totalLength: projectStore.labels?.[projectId]?.length ?? 0, + }, + priority: { + currentLength: 5, + totalLength: ISSUE_PRIORITIES.length, + }, + state_group: { + currentLength: 5, + totalLength: ISSUE_STATE_GROUPS.length, + }, + state: { + currentLength: 5, + totalLength: statesList?.length ?? 0, + }, + }); + + const handleViewMore = (filterName: keyof IIssueFilterOptions) => { + const filterDetails = filtersToRender[filterName]; + + if (!filterDetails) return; + + if (filterDetails.currentLength <= filterDetails.totalLength) + setFiltersToRender((prev) => ({ + ...prev, + [filterName]: { + ...prev[filterName], + currentLength: filterDetails.currentLength + 5, + }, + })); + }; + + const isViewMoreVisible = (filterName: keyof IIssueFilterOptions): boolean => { + const filterDetails = filtersToRender[filterName]; + + if (!filterDetails) return false; + + return filterDetails.currentLength < filterDetails.totalLength; + }; + + return ( +
+
+
+ + issueFilterStore.updateFiltersSearchQuery(e.target.value)} + autoFocus + /> + {issueFilterStore.filtersSearchQuery !== "" && ( + + )} +
+
+
+ {/* priority */} +
+ + {isViewMoreVisible("priority") && ( + + )} +
+ + {/* state group */} +
+ + {isViewMoreVisible("state_group") && ( + + )} +
+ + {/* state */} +
+ + {isViewMoreVisible("state") && ( + + )} +
+ + {/* assignees */} +
+ + {isViewMoreVisible("assignees") && ( + + )} +
+ + {/* created_by */} +
+ + {isViewMoreVisible("created_by") && ( + + )} +
+ + {/* labels */} +
+ + {isViewMoreVisible("labels") && ( + + )} +
+ + {/* start_date */} + {/*
+ +
*/} + + {/* due_date */} + {/*
+ +
*/} +
+
+ ); +}); diff --git a/web/components/issue-layouts/filters/index.ts b/web/components/issue-layouts/filters/index.ts index e804899d2..40190a10e 100644 --- a/web/components/issue-layouts/filters/index.ts +++ b/web/components/issue-layouts/filters/index.ts @@ -1,6 +1,6 @@ export * from "./assignees"; export * from "./created-by"; -export * from "./filter-selection"; +export * from "./filters-selection"; export * from "./labels"; export * from "./priority"; export * from "./start-date"; diff --git a/web/components/issue-layouts/filters/labels.tsx b/web/components/issue-layouts/filters/labels.tsx index 52191b1bf..b71c82dc2 100644 --- a/web/components/issue-layouts/filters/labels.tsx +++ b/web/components/issue-layouts/filters/labels.tsx @@ -1,23 +1,21 @@ import React, { useState } from "react"; +// mobx +import { observer } from "mobx-react-lite"; +import { useMobxStore } from "lib/mobx/store-provider"; // components import { FilterHeader, FilterOption } from "components/issue-layouts"; -// mobx react lite -import { observer } from "mobx-react-lite"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// ui +import { Loader } from "components/ui"; const LabelIcons = ({ color }: { color: string }) => ( ); -type Props = { - workspaceSlug: string; - projectId: string; -}; +type Props = { workspaceSlug: string; projectId: string; itemsToRender: number }; export const FilterLabels: React.FC = observer((props) => { - const { workspaceSlug, projectId } = props; + const { workspaceSlug, projectId, itemsToRender } = props; const [previewEnabled, setPreviewEnabled] = useState(true); @@ -37,26 +35,46 @@ export const FilterLabels: React.FC = observer((props) => { }); }; + const appliedFiltersCount = issueFilterStore.userFilters?.labels?.length ?? 0; + + const filteredOptions = projectStore.labels?.[projectId?.toString() ?? ""]?.filter((label) => + label.name.toLowerCase().includes(issueFilterStore.filtersSearchQuery.toLowerCase()) + ); + return ( -
+ <> 0 ? ` (${appliedFiltersCount})` : ""}`} isPreviewEnabled={previewEnabled} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} /> {previewEnabled && ( -
- {projectStore.labels?.[projectId?.toString() ?? ""]?.map((label) => ( - handleUpdateLabels(label?.id)} - icon={} - title={label.name} - /> - ))} +
+ {filteredOptions ? ( + filteredOptions.length > 0 ? ( + filteredOptions + .slice(0, itemsToRender) + .map((label) => ( + handleUpdateLabels(label?.id)} + icon={} + title={label.name} + /> + )) + ) : ( +

No matches found

+ ) + ) : ( + + + + + + )}
)} -
+ ); }); diff --git a/web/components/issue-layouts/filters/priority.tsx b/web/components/issue-layouts/filters/priority.tsx index 8260154e9..659f76f32 100644 --- a/web/components/issue-layouts/filters/priority.tsx +++ b/web/components/issue-layouts/filters/priority.tsx @@ -50,10 +50,10 @@ const PriorityIcons = ({ ); }; -type Props = { workspaceSlug: string; projectId: string }; +type Props = { workspaceSlug: string; projectId: string; itemsToRender: number }; export const FilterPriority: React.FC = observer((props) => { - const { workspaceSlug, projectId } = props; + const { workspaceSlug, projectId, itemsToRender } = props; const [previewEnabled, setPreviewEnabled] = useState(true); @@ -73,24 +73,36 @@ export const FilterPriority: React.FC = observer((props) => { }); }; + const appliedFiltersCount = issueFilterStore.userFilters?.priority?.length ?? 0; + + const filteredOptions = ISSUE_PRIORITIES.filter((p) => + p.key.includes(issueFilterStore.filtersSearchQuery.toLowerCase()) + ); + return ( <> 0 ? ` (${appliedFiltersCount})` : ""}`} isPreviewEnabled={previewEnabled} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} /> {previewEnabled && ( -
- {ISSUE_PRIORITIES.map((priority) => ( - handleUpdatePriority(priority.key)} - icon={} - title={priority.title} - /> - ))} +
+ {filteredOptions.length > 0 ? ( + filteredOptions + .slice(0, itemsToRender) + .map((priority) => ( + handleUpdatePriority(priority.key)} + icon={} + title={priority.title} + /> + )) + ) : ( +

No matches found

+ )}
)} diff --git a/web/components/issue-layouts/filters/start-date.tsx b/web/components/issue-layouts/filters/start-date.tsx index c2e038100..c93091a11 100644 --- a/web/components/issue-layouts/filters/start-date.tsx +++ b/web/components/issue-layouts/filters/start-date.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState } from "react"; // mobx import { observer } from "mobx-react-lite"; @@ -7,20 +7,22 @@ import { useMobxStore } from "lib/mobx/store-provider"; import { FilterHeader, FilterOption } from "components/issue-layouts"; export const FilterStartDate = observer(() => { + const [previewEnabled, setPreviewEnabled] = useState(true); + const store = useMobxStore(); const { issueFilter: issueFilterStore } = store; - const [previewEnabled, setPreviewEnabled] = React.useState(true); + const appliedFiltersCount = issueFilterStore.userFilters?.start_date?.length ?? 0; return ( -
+ <> 0 ? ` (${appliedFiltersCount})` : ""}`} isPreviewEnabled={previewEnabled} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} /> {previewEnabled && ( -
+
{issueFilterStore?.userFilters?.start_date && issueFilterStore?.userFilters?.start_date.length > 0 && issueFilterStore?.userFilters?.start_date.map((_startDate) => ( @@ -33,6 +35,6 @@ export const FilterStartDate = observer(() => { ))}
)} -
+ ); }); diff --git a/web/components/issue-layouts/filters/state-group.tsx b/web/components/issue-layouts/filters/state-group.tsx index 7b1613896..2601eb308 100644 --- a/web/components/issue-layouts/filters/state-group.tsx +++ b/web/components/issue-layouts/filters/state-group.tsx @@ -10,13 +10,10 @@ import { StateGroupIcon } from "components/icons"; // constants import { ISSUE_STATE_GROUPS } from "constants/issue"; -type Props = { - workspaceSlug: string; - projectId: string; -}; +type Props = { workspaceSlug: string; projectId: string; itemsToRender: number }; export const FilterStateGroup: React.FC = observer((props) => { - const { workspaceSlug, projectId } = props; + const { workspaceSlug, projectId, itemsToRender } = props; const [previewEnabled, setPreviewEnabled] = useState(true); @@ -36,26 +33,38 @@ export const FilterStateGroup: React.FC = observer((props) => { }); }; + const appliedFiltersCount = issueFilterStore.userFilters?.state_group?.length ?? 0; + + const filteredOptions = ISSUE_STATE_GROUPS.filter((s) => + s.key.includes(issueFilterStore.filtersSearchQuery.toLowerCase()) + ); + return ( -
+ <> 0 ? ` (${appliedFiltersCount})` : ""}`} isPreviewEnabled={previewEnabled} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} /> {previewEnabled && ( -
- {ISSUE_STATE_GROUPS.map((stateGroup) => ( - handleUpdateStateGroup(stateGroup.key)} - icon={} - title={stateGroup.title} - /> - ))} +
+ {filteredOptions.length > 0 ? ( + filteredOptions + .slice(0, itemsToRender) + .map((stateGroup) => ( + handleUpdateStateGroup(stateGroup.key)} + icon={} + title={stateGroup.title} + /> + )) + ) : ( +

No matches found

+ )}
)} -
+ ); }); diff --git a/web/components/issue-layouts/filters/state.tsx b/web/components/issue-layouts/filters/state.tsx index 410541806..a9bace648 100644 --- a/web/components/issue-layouts/filters/state.tsx +++ b/web/components/issue-layouts/filters/state.tsx @@ -1,28 +1,27 @@ -import React from "react"; +import React, { useState } from "react"; // mobx import { observer } from "mobx-react-lite"; import { useMobxStore } from "lib/mobx/store-provider"; // components import { FilterHeader, FilterOption } from "components/issue-layouts"; +// ui +import { Loader } from "components/ui"; // icons import { StateGroupIcon } from "components/icons"; // helpers import { getStatesList } from "helpers/state.helper"; -type Props = { - workspaceSlug: string; - projectId: string; -}; +type Props = { workspaceSlug: string; projectId: string; itemsToRender: number }; export const FilterState: React.FC = observer((props) => { - const { workspaceSlug, projectId } = props; + const { workspaceSlug, projectId, itemsToRender } = props; + + const [previewEnabled, setPreviewEnabled] = useState(true); const store = useMobxStore(); const { issueFilter: issueFilterStore, project: projectStore } = store; - const [previewEnabled, setPreviewEnabled] = React.useState(true); - const statesByGroups = projectStore.states?.[projectId?.toString() ?? ""]; const statesList = getStatesList(statesByGroups); @@ -39,26 +38,46 @@ export const FilterState: React.FC = observer((props) => { }); }; + const appliedFiltersCount = issueFilterStore.userFilters?.state?.length ?? 0; + + const filteredOptions = statesList?.filter((s) => + s.name.toLowerCase().includes(issueFilterStore.filtersSearchQuery.toLowerCase()) + ); + return ( -
+ <> 0 ? ` (${appliedFiltersCount})` : ""}`} isPreviewEnabled={previewEnabled} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} /> {previewEnabled && ( -
- {statesList?.map((state) => ( - handleUpdateState(state?.id)} - icon={} - title={state?.name} - /> - ))} +
+ {filteredOptions ? ( + filteredOptions.length > 0 ? ( + <> + {filteredOptions.slice(0, itemsToRender).map((state) => ( + handleUpdateState(state?.id)} + icon={} + title={state?.name} + /> + ))} + + ) : ( +

No matches found

+ ) + ) : ( + + + + + + )}
)} -
+ ); }); diff --git a/web/components/issue-layouts/filters/target-date.tsx b/web/components/issue-layouts/filters/target-date.tsx index 44a54f7aa..7a00b12a4 100644 --- a/web/components/issue-layouts/filters/target-date.tsx +++ b/web/components/issue-layouts/filters/target-date.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState } from "react"; // mobx import { observer } from "mobx-react-lite"; @@ -7,15 +7,17 @@ import { useMobxStore } from "lib/mobx/store-provider"; import { FilterHeader, FilterOption } from "components/issue-layouts"; export const FilterTargetDate = observer(() => { + const [previewEnabled, setPreviewEnabled] = useState(true); + const store = useMobxStore(); const { issueFilter: issueFilterStore } = store; - const [previewEnabled, setPreviewEnabled] = React.useState(true); + const appliedFiltersCount = issueFilterStore.userFilters?.target_date?.length ?? 0; return ( -
+ <> 0 ? ` (${appliedFiltersCount})` : ""}`} isPreviewEnabled={previewEnabled} handleIsPreviewEnabled={() => setPreviewEnabled(!previewEnabled)} /> @@ -33,6 +35,6 @@ export const FilterTargetDate = observer(() => { ))}
)} -
+ ); }); diff --git a/web/components/issue-layouts/helpers/dropdown.tsx b/web/components/issue-layouts/helpers/dropdown.tsx index 1358b787c..33e813d99 100644 --- a/web/components/issue-layouts/helpers/dropdown.tsx +++ b/web/components/issue-layouts/helpers/dropdown.tsx @@ -36,8 +36,8 @@ export const IssueDropdown = ({ children, title = "Dropdown" }: IIssueDropdown) leaveFrom="opacity-100 translate-y-0" leaveTo="opacity-0 translate-y-1" > - - {children} + +
{children}
diff --git a/web/components/issue-layouts/helpers/filter-header.tsx b/web/components/issue-layouts/helpers/filter-header.tsx index 909e4026c..6dd0c7a02 100644 --- a/web/components/issue-layouts/helpers/filter-header.tsx +++ b/web/components/issue-layouts/helpers/filter-header.tsx @@ -9,7 +9,7 @@ interface IFilterHeader { } export const FilterHeader = ({ title, isPreviewEnabled, handleIsPreviewEnabled }: IFilterHeader) => ( -
+
{title}
+ + + ))}
); diff --git a/web/components/issue-layouts/root.tsx b/web/components/issue-layouts/root.tsx index 1c5752af0..250f3204a 100644 --- a/web/components/issue-layouts/root.tsx +++ b/web/components/issue-layouts/root.tsx @@ -2,7 +2,7 @@ import React from "react"; // components import { LayoutSelection } from "./layout-selection"; import { IssueDropdown } from "./helpers/dropdown"; -import { FilterSelection } from "./filters/filter-selection"; +import { FilterSelection } from "./filters/filters-selection"; import { DisplayFiltersSelection } from "./display-filters"; import { FilterPreview } from "./filters-preview"; @@ -64,13 +64,13 @@ export const IssuesRoot = observer(() => {
Filter Header
- + {/* - + */}
@@ -78,11 +78,11 @@ export const IssuesRoot = observer(() => {
- {issueFilterStore?.issueLayout === "list" && } - {issueFilterStore?.issueLayout === "kanban" && } - {issueFilterStore?.issueLayout === "calendar" && } - {issueFilterStore?.issueLayout === "spreadsheet" && } - {issueFilterStore?.issueLayout === "gantt_chart" && } + {issueFilterStore?.userDisplayFilters.layout === "list" && } + {issueFilterStore?.userDisplayFilters.layout === "kanban" && } + {issueFilterStore?.userDisplayFilters.layout === "calendar" && } + {issueFilterStore?.userDisplayFilters.layout === "spreadsheet" && } + {issueFilterStore?.userDisplayFilters.layout === "gantt_chart" && }
); diff --git a/web/constants/fetch-keys.ts b/web/constants/fetch-keys.ts index 0f0643c66..fa85be713 100644 --- a/web/constants/fetch-keys.ts +++ b/web/constants/fetch-keys.ts @@ -2,17 +2,8 @@ import { objToQueryParams } from "helpers/string.helper"; import { IAnalyticsParams, IJiraMetadata, INotificationParams } from "types"; const paramsToKey = (params: any) => { - const { - state, - priority, - assignees, - created_by, - labels, - start_date, - target_date, - sub_issue, - start_target_date, - } = params; + const { state, priority, assignees, created_by, labels, start_date, target_date, sub_issue, start_target_date } = + params; let stateKey = state ? state.split(",") : []; let priorityKey = priority ? priority.split(",") : []; @@ -50,16 +41,7 @@ const inboxParamsToKey = (params: any) => { }; const myIssuesParamsToKey = (params: any) => { - const { - assignees, - created_by, - labels, - priority, - state_group, - subscriber, - start_date, - target_date, - } = params; + const { assignees, created_by, labels, priority, state_group, subscriber, start_date, target_date } = params; let assigneesKey = assignees ? assignees.split(",") : []; let createdByKey = created_by ? created_by.split(",") : []; @@ -88,15 +70,12 @@ export const CURRENT_USER = "CURRENT_USER"; export const USER_WORKSPACE_INVITATIONS = "USER_WORKSPACE_INVITATIONS"; export const USER_WORKSPACES = "USER_WORKSPACES"; -export const WORKSPACE_DETAILS = (workspaceSlug: string) => - `WORKSPACE_DETAILS_${workspaceSlug.toUpperCase()}`; +export const WORKSPACE_DETAILS = (workspaceSlug: string) => `WORKSPACE_DETAILS_${workspaceSlug.toUpperCase()}`; -export const WORKSPACE_MEMBERS = (workspaceSlug: string) => - `WORKSPACE_MEMBERS_${workspaceSlug.toUpperCase()}`; +export const WORKSPACE_MEMBERS = (workspaceSlug: string) => `WORKSPACE_MEMBERS_${workspaceSlug.toUpperCase()}`; export const WORKSPACE_MEMBERS_WITH_EMAIL = (workspaceSlug: string) => `WORKSPACE_MEMBERS_WITH_EMAIL_${workspaceSlug.toUpperCase()}`; -export const WORKSPACE_MEMBERS_ME = (workspaceSlug: string) => - `WORKSPACE_MEMBERS_ME${workspaceSlug.toUpperCase()}`; +export const WORKSPACE_MEMBERS_ME = (workspaceSlug: string) => `WORKSPACE_MEMBERS_ME${workspaceSlug.toUpperCase()}`; export const WORKSPACE_INVITATIONS = "WORKSPACE_INVITATIONS"; export const WORKSPACE_INVITATION_WITH_EMAIL = (workspaceSlug: string) => `WORKSPACE_INVITATION_WITH_EMAIL_${workspaceSlug.toUpperCase()}`; @@ -111,9 +90,7 @@ export const PROJECTS_LIST = ( ) => { if (!params) return `PROJECTS_LIST_${workspaceSlug.toUpperCase()}`; - return `PROJECTS_LIST_${workspaceSlug.toUpperCase()}_${params.is_favorite - .toString() - .toUpperCase()}`; + return `PROJECTS_LIST_${workspaceSlug.toUpperCase()}_${params.is_favorite.toString().toUpperCase()}`; }; export const PROJECT_DETAILS = (projectId: string) => `PROJECT_DETAILS_${projectId.toUpperCase()}`; @@ -149,35 +126,22 @@ export const PROJECT_DRAFT_ISSUES_LIST_WITH_PARAMS = (projectId: string, params? return `PROJECT_DRAFT_ISSUES_LIST_WITH_PARAMS${projectId.toUpperCase()}_${paramsKey}`; }; -export const PROJECT_ISSUES_DETAILS = (issueId: string) => - `PROJECT_ISSUES_DETAILS_${issueId.toUpperCase()}`; -export const PROJECT_ISSUES_PROPERTIES = (projectId: string) => - `PROJECT_ISSUES_PROPERTIES_${projectId.toUpperCase()}`; -export const PROJECT_ISSUES_COMMENTS = (issueId: string) => - `PROJECT_ISSUES_COMMENTS_${issueId.toUpperCase()}`; -export const PROJECT_ISSUES_ACTIVITY = (issueId: string) => - `PROJECT_ISSUES_ACTIVITY_${issueId.toUpperCase()}`; -export const PROJECT_ISSUE_BY_STATE = (projectId: string) => - `PROJECT_ISSUE_BY_STATE_${projectId.toUpperCase()}`; -export const PROJECT_ISSUE_LABELS = (projectId: string) => - `PROJECT_ISSUE_LABELS_${projectId.toUpperCase()}`; -export const WORKSPACE_LABELS = (workspaceSlug: string) => - `WORKSPACE_LABELS_${workspaceSlug.toUpperCase()}`; -export const PROJECT_GITHUB_REPOSITORY = (projectId: string) => - `PROJECT_GITHUB_REPOSITORY_${projectId.toUpperCase()}`; +export const PROJECT_ISSUES_DETAILS = (issueId: string) => `PROJECT_ISSUES_DETAILS_${issueId.toUpperCase()}`; +export const PROJECT_ISSUES_PROPERTIES = (projectId: string) => `PROJECT_ISSUES_PROPERTIES_${projectId.toUpperCase()}`; +export const PROJECT_ISSUES_COMMENTS = (issueId: string) => `PROJECT_ISSUES_COMMENTS_${issueId.toUpperCase()}`; +export const PROJECT_ISSUES_ACTIVITY = (issueId: string) => `PROJECT_ISSUES_ACTIVITY_${issueId.toUpperCase()}`; +export const PROJECT_ISSUE_BY_STATE = (projectId: string) => `PROJECT_ISSUE_BY_STATE_${projectId.toUpperCase()}`; +export const PROJECT_ISSUE_LABELS = (projectId: string) => `PROJECT_ISSUE_LABELS_${projectId.toUpperCase()}`; +export const WORKSPACE_LABELS = (workspaceSlug: string) => `WORKSPACE_LABELS_${workspaceSlug.toUpperCase()}`; +export const PROJECT_GITHUB_REPOSITORY = (projectId: string) => `PROJECT_GITHUB_REPOSITORY_${projectId.toUpperCase()}`; // cycles export const CYCLES_LIST = (projectId: string) => `CYCLE_LIST_${projectId.toUpperCase()}`; -export const INCOMPLETE_CYCLES_LIST = (projectId: string) => - `INCOMPLETE_CYCLES_LIST_${projectId.toUpperCase()}`; -export const CURRENT_CYCLE_LIST = (projectId: string) => - `CURRENT_CYCLE_LIST_${projectId.toUpperCase()}`; -export const UPCOMING_CYCLES_LIST = (projectId: string) => - `UPCOMING_CYCLES_LIST_${projectId.toUpperCase()}`; -export const DRAFT_CYCLES_LIST = (projectId: string) => - `DRAFT_CYCLES_LIST_${projectId.toUpperCase()}`; -export const COMPLETED_CYCLES_LIST = (projectId: string) => - `COMPLETED_CYCLES_LIST_${projectId.toUpperCase()}`; +export const INCOMPLETE_CYCLES_LIST = (projectId: string) => `INCOMPLETE_CYCLES_LIST_${projectId.toUpperCase()}`; +export const CURRENT_CYCLE_LIST = (projectId: string) => `CURRENT_CYCLE_LIST_${projectId.toUpperCase()}`; +export const UPCOMING_CYCLES_LIST = (projectId: string) => `UPCOMING_CYCLES_LIST_${projectId.toUpperCase()}`; +export const DRAFT_CYCLES_LIST = (projectId: string) => `DRAFT_CYCLES_LIST_${projectId.toUpperCase()}`; +export const COMPLETED_CYCLES_LIST = (projectId: string) => `COMPLETED_CYCLES_LIST_${projectId.toUpperCase()}`; export const CYCLE_ISSUES = (cycleId: string) => `CYCLE_ISSUES_${cycleId.toUpperCase()}`; export const CYCLE_ISSUES_WITH_PARAMS = (cycleId: string, params?: any) => { if (!params) return `CYCLE_ISSUES_WITH_PARAMS_${cycleId.toUpperCase()}`; @@ -199,8 +163,7 @@ export const USER_ISSUES = (workspaceSlug: string, params: any) => { export const USER_ACTIVITY = "USER_ACTIVITY"; export const USER_WORKSPACE_DASHBOARD = (workspaceSlug: string) => `USER_WORKSPACE_DASHBOARD_${workspaceSlug.toUpperCase()}`; -export const USER_PROJECT_VIEW = (projectId: string) => - `USER_PROJECT_VIEW_${projectId.toUpperCase()}`; +export const USER_PROJECT_VIEW = (projectId: string) => `USER_PROJECT_VIEW_${projectId.toUpperCase()}`; export const MODULE_LIST = (projectId: string) => `MODULE_LIST_${projectId.toUpperCase()}`; export const MODULE_ISSUES = (moduleId: string) => `MODULE_ISSUES_${moduleId.toUpperCase()}`; @@ -240,8 +203,7 @@ export const INBOX_ISSUE_DETAILS = (inboxId: string, issueId: string) => export const ISSUE_DETAILS = (issueId: string) => `ISSUE_DETAILS_${issueId.toUpperCase()}`; export const SUB_ISSUES = (issueId: string) => `SUB_ISSUES_${issueId.toUpperCase()}`; export const ISSUE_ATTACHMENTS = (issueId: string) => `ISSUE_ATTACHMENTS_${issueId.toUpperCase()}`; -export const ARCHIVED_ISSUE_DETAILS = (issueId: string) => - `ARCHIVED_ISSUE_DETAILS_${issueId.toUpperCase()}`; +export const ARCHIVED_ISSUE_DETAILS = (issueId: string) => `ARCHIVED_ISSUE_DETAILS_${issueId.toUpperCase()}`; // integrations export const APP_INTEGRATIONS = "APP_INTEGRATIONS"; @@ -271,22 +233,18 @@ export const SLACK_CHANNEL_INFO = (workspaceSlug: string, projectId: string) => `SLACK_CHANNEL_INFO_${workspaceSlug.toString().toUpperCase()}_${projectId.toUpperCase()}`; // Pages -export const RECENT_PAGES_LIST = (projectId: string) => - `RECENT_PAGES_LIST_${projectId.toUpperCase()}`; +export const RECENT_PAGES_LIST = (projectId: string) => `RECENT_PAGES_LIST_${projectId.toUpperCase()}`; export const ALL_PAGES_LIST = (projectId: string) => `ALL_PAGES_LIST_${projectId.toUpperCase()}`; -export const FAVORITE_PAGES_LIST = (projectId: string) => - `FAVORITE_PAGES_LIST_${projectId.toUpperCase()}`; +export const FAVORITE_PAGES_LIST = (projectId: string) => `FAVORITE_PAGES_LIST_${projectId.toUpperCase()}`; export const MY_PAGES_LIST = (projectId: string) => `MY_PAGES_LIST_${projectId.toUpperCase()}`; -export const OTHER_PAGES_LIST = (projectId: string) => - `OTHER_PAGES_LIST_${projectId.toUpperCase()}`; +export const OTHER_PAGES_LIST = (projectId: string) => `OTHER_PAGES_LIST_${projectId.toUpperCase()}`; export const PAGE_DETAILS = (pageId: string) => `PAGE_DETAILS_${pageId.toUpperCase()}`; export const PAGE_BLOCKS_LIST = (pageId: string) => `PAGE_BLOCK_LIST_${pageId.toUpperCase()}`; export const PAGE_BLOCK_DETAILS = (pageId: string) => `PAGE_BLOCK_DETAILS_${pageId.toUpperCase()}`; // estimates export const ESTIMATES_LIST = (projectId: string) => `ESTIMATES_LIST_${projectId.toUpperCase()}`; -export const ESTIMATE_DETAILS = (estimateId: string) => - `ESTIMATE_DETAILS_${estimateId.toUpperCase()}`; +export const ESTIMATE_DETAILS = (estimateId: string) => `ESTIMATE_DETAILS_${estimateId.toUpperCase()}`; // analytics export const ANALYTICS = (workspaceSlug: string, params: IAnalyticsParams) => @@ -294,15 +252,10 @@ export const ANALYTICS = (workspaceSlug: string, params: IAnalyticsParams) => params.segment }_${params.project?.toString()}`; export const DEFAULT_ANALYTICS = (workspaceSlug: string, params?: Partial) => - `DEFAULT_ANALYTICS_${workspaceSlug.toUpperCase()}_${params?.project?.toString()}_${ - params?.cycle - }_${params?.module}`; + `DEFAULT_ANALYTICS_${workspaceSlug.toUpperCase()}_${params?.project?.toString()}_${params?.cycle}_${params?.module}`; // notifications -export const USER_WORKSPACE_NOTIFICATIONS = ( - workspaceSlug: string, - params: INotificationParams -) => { +export const USER_WORKSPACE_NOTIFICATIONS = (workspaceSlug: string, params: INotificationParams) => { const { type, snoozed, archived, read } = params; return `USER_WORKSPACE_NOTIFICATIONS_${workspaceSlug?.toUpperCase()}_TYPE_${( @@ -310,21 +263,13 @@ export const USER_WORKSPACE_NOTIFICATIONS = ( )?.toUpperCase()}_SNOOZED_${snoozed}_ARCHIVED_${archived}_READ_${read}`; }; -export const USER_WORKSPACE_NOTIFICATIONS_DETAILS = ( - workspaceSlug: string, - notificationId: string -) => +export const USER_WORKSPACE_NOTIFICATIONS_DETAILS = (workspaceSlug: string, notificationId: string) => `USER_WORKSPACE_NOTIFICATIONS_DETAILS_${workspaceSlug?.toUpperCase()}_${notificationId?.toUpperCase()}`; export const UNREAD_NOTIFICATIONS_COUNT = (workspaceSlug: string) => `UNREAD_NOTIFICATIONS_COUNT_${workspaceSlug?.toUpperCase()}`; -export const getPaginatedNotificationKey = ( - index: number, - prevData: any, - workspaceSlug: string, - params: any -) => { +export const getPaginatedNotificationKey = (index: number, prevData: any, workspaceSlug: string, params: any) => { if (prevData && !prevData?.results?.length) return null; if (index === 0) @@ -360,9 +305,5 @@ export const USER_PROFILE_ISSUES = (workspaceSlug: string, userId: string, param // reactions export const ISSUE_REACTION_LIST = (workspaceSlug: string, projectId: string, issueId: string) => `ISSUE_REACTION_LIST_${workspaceSlug.toUpperCase()}_${projectId.toUpperCase()}_${issueId.toUpperCase()}`; -export const COMMENT_REACTION_LIST = ( - workspaceSlug: string, - projectId: string, - commendId: string -) => +export const COMMENT_REACTION_LIST = (workspaceSlug: string, projectId: string, commendId: string) => `COMMENT_REACTION_LIST_${workspaceSlug.toUpperCase()}_${projectId.toUpperCase()}_${commendId.toUpperCase()}`; diff --git a/web/constants/issue.ts b/web/constants/issue.ts index 3e8206f21..f7209ad49 100644 --- a/web/constants/issue.ts +++ b/web/constants/issue.ts @@ -117,11 +117,11 @@ export const ISSUE_LAYOUTS: { title: string; icon: any; }[] = [ - { key: "list", title: "List View", icon: List }, - { key: "kanban", title: "Kanban View", icon: Kanban }, - { key: "calendar", title: "Calendar View", icon: Calendar }, - { key: "spreadsheet", title: "Spreadsheet View", icon: Sheet }, - { key: "gantt_chart", title: "Gantt Chart View", icon: GanttChart }, + { key: "list", title: "List Layout", icon: List }, + { key: "kanban", title: "Kanban Layout", icon: Kanban }, + { key: "calendar", title: "Calendar Layout", icon: Calendar }, + { key: "spreadsheet", title: "Spreadsheet Layout", icon: Sheet }, + { key: "gantt_chart", title: "Gantt Chart Layout", icon: GanttChart }, ]; export const ISSUE_LIST_FILTERS = [ @@ -198,26 +198,116 @@ export const ISSUE_GANTT_DISPLAY_FILTERS = [ { key: "sub_issue", title: "Sub Issue" }, ]; -// TODO: update this later -export const ISSUE_EXTRA_DISPLAY_PROPERTIES = { - list: { - access: true, - values: ["show_empty_groups", "sub_issue"], +export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { + [key: string]: { + layout: TIssueLayouts[]; + filters: { + [key in TIssueLayouts]: string[]; + }; + display_properties: { + [key in TIssueLayouts]: boolean; + }; + display_filters: { + [key in TIssueLayouts]: string[]; + }; + extra_options: { + [key in TIssueLayouts]: { + access: boolean; + values: string[]; + }; + }; + }; +} = { + my_issues: { + layout: ["list", "kanban"], + filters: { + list: ["priority", "state_group", "labels", "start_date", "due_date"], + kanban: ["priority", "state_group", "labels", "start_date", "due_date"], + calendar: [], + spreadsheet: [], + gantt_chart: [], + }, + display_properties: { + list: true, + kanban: true, + calendar: true, + spreadsheet: true, + gantt_chart: false, + }, + display_filters: { + list: ["group_by", "sub_group_by", "order_by", "issue_type"], + kanban: ["group_by", "sub_group_by", "order_by", "issue_type"], + calendar: ["issue_type"], + spreadsheet: ["issue_type"], + gantt_chart: ["order_by", "issue_type"], + }, + extra_options: { + list: { + access: true, + values: ["show_empty_groups", "sub_issue"], + }, + kanban: { + access: true, + values: ["show_empty_groups", "sub_issue"], + }, + calendar: { + access: false, + values: [], + }, + spreadsheet: { + access: false, + values: [], + }, + gantt_chart: { + access: false, + values: [], + }, + }, }, - kanban: { - access: true, - values: ["show_empty_groups", "sub_issue"], - }, - calendar: { - access: false, - values: [], - }, - spreadsheet: { - access: false, - values: [], - }, - gantt_chart: { - access: true, - values: ["sub_issue"], + issues: { + layout: ["list", "kanban", "calendar", "spreadsheet", "gantt_chart"], + filters: { + list: ["priority", "state", "assignees", "created_by", "labels", "start_date", "due_date"], + kanban: ["priority", "state", "assignees", "created_by", "labels", "start_date", "due_date"], + calendar: ["priority", "state", "assignees", "created_by", "labels"], + spreadsheet: ["priority", "state", "assignees", "created_by", "labels", "start_date", "due_date"], + gantt_chart: ["priority", "state", "assignees", "created_by", "labels", "start_date", "due_date"], + }, + display_properties: { + list: true, + kanban: true, + calendar: true, + spreadsheet: true, + gantt_chart: false, + }, + display_filters: { + list: ["group_by", "sub_group_by", "order_by", "issue_type"], + kanban: ["group_by", "sub_group_by", "order_by", "issue_type"], + calendar: ["issue_type"], + spreadsheet: ["issue_type"], + gantt_chart: ["order_by", "issue_type"], + }, + extra_options: { + list: { + access: true, + values: ["show_empty_groups", "sub_issue"], + }, + kanban: { + access: true, + values: ["show_empty_groups", "sub_issue"], + }, + calendar: { + access: false, + values: [], + }, + spreadsheet: { + access: false, + values: [], + }, + gantt_chart: { + access: true, + values: ["sub_issue"], + }, + }, }, }; diff --git a/web/helpers/issue.helper.ts b/web/helpers/issue.helper.ts index cf6998b8e..eab6f8a47 100644 --- a/web/helpers/issue.helper.ts +++ b/web/helpers/issue.helper.ts @@ -91,6 +91,7 @@ export const handleIssueQueryParamsByLayout = (_layout: TIssueLayouts | undefine "start_date", "target_date", "group_by", + "sub_group_by", "order_by", "type", "sub_issue", @@ -107,6 +108,7 @@ export const handleIssueQueryParamsByLayout = (_layout: TIssueLayouts | undefine "start_date", "target_date", "group_by", + "sub_group_by", "order_by", "type", "sub_issue", diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/issues/index.tsx b/web/pages/[workspaceSlug]/projects/[projectId]/issues/index.tsx index 86b80f983..a9fef194d 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/issues/index.tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/issues/index.tsx @@ -54,29 +54,30 @@ const ProjectIssues: NextPage = () => { workspaceSlug && projectId ? () => inboxService.getInboxes(workspaceSlug as string, projectId as string) : null ); + // TODO: update the fetch keys useSWR( - workspaceSlug && projectId ? USER_PROJECT_VIEW(projectId.toString()) : null, + workspaceSlug && projectId ? "REVALIDATE_USER_PROJECT_FILTERS" : null, workspaceSlug && projectId ? () => issueFilterStore.fetchUserProjectFilters(workspaceSlug.toString(), projectId.toString()) : null ); useSWR( - workspaceSlug && projectId ? STATES_LIST(projectId.toString()) : null, + workspaceSlug && projectId ? "REVALIDATE_PROJECT_STATES_LIST" : null, workspaceSlug && projectId ? () => projectStore.fetchProjectStates(workspaceSlug.toString(), projectId.toString()) : null ); useSWR( - workspaceSlug && projectId ? PROJECT_ISSUE_LABELS(projectId.toString()) : null, + workspaceSlug && projectId ? "REVALIDATE_PROJECT_LABELS_LIST" : null, workspaceSlug && projectId ? () => projectStore.fetchProjectLabels(workspaceSlug.toString(), projectId.toString()) : null ); useSWR( - workspaceSlug && projectId ? PROJECT_MEMBERS(projectId.toString()) : null, + workspaceSlug && projectId ? "REVALIDATE_PROJECT_MEMBERS_LIST" : null, workspaceSlug && projectId ? () => projectStore.fetchProjectMembers(workspaceSlug.toString(), projectId.toString()) : null diff --git a/web/store/issue_filters.ts b/web/store/issue_filters.ts index f52018d8c..7d95f8813 100644 --- a/web/store/issue_filters.ts +++ b/web/store/issue_filters.ts @@ -2,6 +2,8 @@ import { observable, action, computed, makeObservable, runInAction } from "mobx" // services import { ProjectService } from "services/project.service"; import { IssueService } from "services/issue.service"; +// helpers +import { handleIssueQueryParamsByLayout } from "helpers/issue.helper"; // types import { RootStore } from "./root"; import { @@ -11,7 +13,6 @@ import { IProjectViewProps, TIssueParams, } from "types"; -import { handleIssueQueryParamsByLayout } from "helpers/issue.helper"; export interface IIssueFilterStore { loader: boolean; @@ -21,6 +22,7 @@ export interface IIssueFilterStore { userFilters: IIssueFilterOptions; defaultDisplayFilters: IIssueDisplayFilterOptions; defaultFilters: IIssueFilterOptions; + filtersSearchQuery: string; // action fetchUserProjectFilters: (workspaceSlug: string, projectSlug: string) => Promise; @@ -34,6 +36,7 @@ export interface IIssueFilterStore { projectSlug: string, properties: Partial ) => Promise; + updateFiltersSearchQuery: (query: string) => void; // computed appliedFilters: TIssueParams[] | null; @@ -64,6 +67,7 @@ class IssueFilterStore implements IIssueFilterStore { created_on: true, updated_on: true, }; + filtersSearchQuery: string = ""; // root store rootStore; @@ -83,11 +87,13 @@ class IssueFilterStore implements IIssueFilterStore { userDisplayProperties: observable.ref, userDisplayFilters: observable.ref, userFilters: observable.ref, + filtersSearchQuery: observable.ref, // actions fetchUserProjectFilters: action, updateUserFilters: action, updateDisplayProperties: action, + updateFiltersSearchQuery: action, // computed appliedFilters: computed, @@ -183,6 +189,12 @@ class IssueFilterStore implements IIssueFilterStore { console.log("Failed to update user filters in issue filter store", error); } }; + + updateFiltersSearchQuery: (query: string) => void = (query) => { + runInAction(() => { + this.filtersSearchQuery = query; + }); + }; } export default IssueFilterStore; diff --git a/web/types/view-props.d.ts b/web/types/view-props.d.ts index dbdb72955..6630d527e 100644 --- a/web/types/view-props.d.ts +++ b/web/types/view-props.d.ts @@ -40,6 +40,7 @@ export type TIssueParams = | "start_date" | "target_date" | "group_by" + | "sub_group_by" | "order_by" | "type" | "sub_issue" @@ -62,6 +63,7 @@ export interface IIssueFilterOptions { export interface IIssueDisplayFilterOptions { calendar_date_range?: string; group_by?: TIssueGroupByOptions; + sub_group_by?: TIssueGroupByOptions; layout?: TIssueLayouts; order_by?: TIssueOrderByOptions; show_empty_groups?: boolean;