Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WEB-601] feat: enhanced display filters grouping by cycles and modules in project issues #3834

Merged
merged 5 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/types/src/issues.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ export interface ViewFlags {

export type GroupByColumnTypes =
| "project"
| "cycle"
| "module"
| "state"
| "state_detail.group"
| "priority"
Expand Down
2 changes: 2 additions & 0 deletions packages/types/src/view-props.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export type TIssueGroupByOptions =
| "project"
| "assignees"
| "mentions"
| "cycle"
| "module"
| null;

export type TIssueOrderByOptions =
Expand Down
1 change: 1 addition & 0 deletions web/components/cycles/cycle-mobile-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ export const CycleMobileHeader = () => {
handleDisplayFiltersUpdate={handleDisplayFilters}
displayProperties={issueFilters?.displayProperties ?? {}}
handleDisplayPropertiesUpdate={handleDisplayProperties}
ignoreGroupedFilters={["cycle"]}
/>
</FiltersDropdown>
</div>
Expand Down
1 change: 1 addition & 0 deletions web/components/headers/cycle-issues.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ export const CycleIssuesHeader: React.FC = observer(() => {
handleDisplayFiltersUpdate={handleDisplayFilters}
displayProperties={issueFilters?.displayProperties ?? {}}
handleDisplayPropertiesUpdate={handleDisplayProperties}
ignoreGroupedFilters={["cycle"]}
/>
</FiltersDropdown>

Expand Down
1 change: 1 addition & 0 deletions web/components/headers/module-issues.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
handleDisplayFiltersUpdate={handleDisplayFilters}
displayProperties={issueFilters?.displayProperties ?? {}}
handleDisplayPropertiesUpdate={handleDisplayProperties}
ignoreGroupedFilters={["module"]}
/>
</FiltersDropdown>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
FilterSubGroupBy,
} from "components/issues";
// types
import { IIssueDisplayFilterOptions, IIssueDisplayProperties } from "@plane/types";
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, TIssueGroupByOptions } from "@plane/types";
import { ILayoutDisplayFiltersOptions } from "constants/issue";

type Props = {
Expand All @@ -20,6 +20,7 @@ type Props = {
handleDisplayFiltersUpdate: (updatedDisplayFilter: Partial<IIssueDisplayFilterOptions>) => void;
handleDisplayPropertiesUpdate: (updatedDisplayProperties: Partial<IIssueDisplayProperties>) => void;
layoutDisplayFiltersOptions: ILayoutDisplayFiltersOptions | undefined;
ignoreGroupedFilters?: Partial<TIssueGroupByOptions>[];
};

export const DisplayFiltersSelection: React.FC<Props> = observer((props) => {
Expand All @@ -29,6 +30,7 @@ export const DisplayFiltersSelection: React.FC<Props> = observer((props) => {
handleDisplayFiltersUpdate,
handleDisplayPropertiesUpdate,
layoutDisplayFiltersOptions,
ignoreGroupedFilters = [],
} = props;

const isDisplayFilterEnabled = (displayFilter: keyof IIssueDisplayFilterOptions) =>
Expand All @@ -54,6 +56,7 @@ export const DisplayFiltersSelection: React.FC<Props> = observer((props) => {
group_by: val,
})
}
ignoreGroupedFilters={ignoreGroupedFilters}
/>
</div>
)}
Expand All @@ -71,6 +74,7 @@ export const DisplayFiltersSelection: React.FC<Props> = observer((props) => {
})
}
subGroupByOptions={layoutDisplayFiltersOptions?.display_filters.sub_group_by ?? []}
ignoreGroupedFilters={ignoreGroupedFilters}
/>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useState } from "react";
import { observer } from "mobx-react-lite";

// components
import { FilterHeader, FilterOption } from "components/issues";
// types
Expand All @@ -12,10 +11,11 @@ type Props = {
displayFilters: IIssueDisplayFilterOptions;
groupByOptions: TIssueGroupByOptions[];
handleUpdate: (val: TIssueGroupByOptions) => void;
ignoreGroupedFilters: Partial<TIssueGroupByOptions>[];
};

export const FilterGroupBy: React.FC<Props> = observer((props) => {
const { displayFilters, groupByOptions, handleUpdate } = props;
const { displayFilters, groupByOptions, handleUpdate, ignoreGroupedFilters } = props;

const [previewEnabled, setPreviewEnabled] = useState(true);

Expand All @@ -34,6 +34,7 @@ export const FilterGroupBy: React.FC<Props> = observer((props) => {
{ISSUE_GROUP_BY_OPTIONS.filter((option) => groupByOptions.includes(option.key)).map((groupBy) => {
if (displayFilters.layout === "kanban" && selectedSubGroupBy !== null && groupBy.key === selectedSubGroupBy)
return null;
if (ignoreGroupedFilters.includes(groupBy?.key)) return null;

return (
<FilterOption
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useState } from "react";
import { observer } from "mobx-react-lite";

// components
import { FilterHeader, FilterOption } from "components/issues";
// types
Expand All @@ -12,10 +11,11 @@ type Props = {
displayFilters: IIssueDisplayFilterOptions;
handleUpdate: (val: TIssueGroupByOptions) => void;
subGroupByOptions: TIssueGroupByOptions[];
ignoreGroupedFilters: Partial<TIssueGroupByOptions>[];
};

export const FilterSubGroupBy: React.FC<Props> = observer((props) => {
const { displayFilters, handleUpdate, subGroupByOptions } = props;
const { displayFilters, handleUpdate, subGroupByOptions, ignoreGroupedFilters } = props;

const [previewEnabled, setPreviewEnabled] = useState(true);

Expand All @@ -33,6 +33,7 @@ export const FilterSubGroupBy: React.FC<Props> = observer((props) => {
<div>
{ISSUE_GROUP_BY_OPTIONS.filter((option) => subGroupByOptions.includes(option.key)).map((subGroupBy) => {
if (selectedGroupBy !== null && subGroupBy.key === selectedGroupBy) return null;
if (ignoreGroupedFilters.includes(subGroupBy?.key)) return null;

return (
<FilterOption
Expand Down
17 changes: 14 additions & 3 deletions web/components/issues/issue-layouts/kanban/default.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import { observer } from "mobx-react-lite";
// hooks
import { useIssueDetail, useKanbanView, useLabel, useMember, useProject, useProjectState } from "hooks/store";
import {
useCycle,
useIssueDetail,
useKanbanView,
useLabel,
useMember,
useModule,
useProject,
useProjectState,
} from "hooks/store";
// components
import { HeaderGroupByCard } from "./headers/group-by-card";
import { KanbanGroup } from "./kanban-group";
Expand Down Expand Up @@ -79,14 +88,16 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
const member = useMember();
const project = useProject();
const label = useLabel();
const cycle = useCycle();
const _module = useModule();
const projectState = useProjectState();
const { peekIssue } = useIssueDetail();

const list = getGroupByColumns(group_by as GroupByColumnTypes, project, label, projectState, member);
const list = getGroupByColumns(group_by as GroupByColumnTypes, project, cycle, _module, label, projectState, member);

if (!list) return null;

const groupWithIssues = list.filter((_list) => (issueIds as TGroupedIssues)[_list.id]?.length > 0);
const groupWithIssues = list.filter((_list) => (issueIds as TGroupedIssues)?.[_list.id]?.length > 0);

const groupList = showEmptyGroup ? list : groupWithIssues;

Expand Down
8 changes: 8 additions & 0 deletions web/components/issues/issue-layouts/kanban/kanban-group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ export const KanbanGroup = (props: IKanbanGroup) => {
preloadedData = { ...preloadedData, state_id: groupValue };
} else if (groupByKey === "priority") {
preloadedData = { ...preloadedData, priority: groupValue };
} else if (groupByKey === "cycle") {
preloadedData = { ...preloadedData, cycle_id: groupValue };
} else if (groupByKey === "module") {
preloadedData = { ...preloadedData, module_ids: [groupValue] };
} else if (groupByKey === "labels" && groupValue != "None") {
preloadedData = { ...preloadedData, label_ids: [groupValue] };
} else if (groupByKey === "assignees" && groupValue != "None") {
Expand All @@ -96,6 +100,10 @@ export const KanbanGroup = (props: IKanbanGroup) => {
preloadedData = { ...preloadedData, state_id: subGroupValue };
} else if (subGroupByKey === "priority") {
preloadedData = { ...preloadedData, priority: subGroupValue };
} else if (groupByKey === "cycle") {
preloadedData = { ...preloadedData, cycle_id: subGroupValue };
} else if (groupByKey === "module") {
preloadedData = { ...preloadedData, module_ids: [subGroupValue] };
} else if (subGroupByKey === "labels" && subGroupValue != "None") {
preloadedData = { ...preloadedData, label_ids: [subGroupValue] };
} else if (subGroupByKey === "assignees" && subGroupValue != "None") {
Expand Down
24 changes: 21 additions & 3 deletions web/components/issues/issue-layouts/kanban/swimlanes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
} from "@plane/types";
// constants
import { EIssueActions } from "../types";
import { useLabel, useMember, useProject, useProjectState } from "hooks/store";
import { useCycle, useLabel, useMember, useModule, useProject, useProjectState } from "hooks/store";
import { getGroupByColumns } from "../utils";
import { TCreateModalStoreTypes } from "constants/issue";

Expand Down Expand Up @@ -217,10 +217,28 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
const member = useMember();
const project = useProject();
const label = useLabel();
const cycle = useCycle();
const _module = useModule();
const projectState = useProjectState();

const groupByList = getGroupByColumns(group_by as GroupByColumnTypes, project, label, projectState, member);
const subGroupByList = getGroupByColumns(sub_group_by as GroupByColumnTypes, project, label, projectState, member);
const groupByList = getGroupByColumns(
group_by as GroupByColumnTypes,
project,
cycle,
_module,
label,
projectState,
member
);
const subGroupByList = getGroupByColumns(
sub_group_by as GroupByColumnTypes,
project,
cycle,
_module,
label,
projectState,
member
);

if (!groupByList || !subGroupByList) return null;

Expand Down
15 changes: 13 additions & 2 deletions web/components/issues/issue-layouts/list/default.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useRef } from "react";
import { IssueBlocksList, ListQuickAddIssueForm } from "components/issues";
import { HeaderGroupByCard } from "./headers/group-by-card";
// hooks
import { useLabel, useMember, useProject, useProjectState } from "hooks/store";
import { useCycle, useLabel, useMember, useModule, useProject, useProjectState } from "hooks/store";
// types
import {
GroupByColumnTypes,
Expand Down Expand Up @@ -65,10 +65,21 @@ const GroupByList: React.FC<IGroupByList> = (props) => {
const project = useProject();
const label = useLabel();
const projectState = useProjectState();
const cycle = useCycle();
const _module = useModule();

const containerRef = useRef<HTMLDivElement | null>(null);

const groups = getGroupByColumns(group_by as GroupByColumnTypes, project, label, projectState, member, true);
const groups = getGroupByColumns(
group_by as GroupByColumnTypes,
project,
cycle,
_module,
label,
projectState,
member,
true
);

if (!groups) return null;

Expand Down
85 changes: 80 additions & 5 deletions web/components/issues/issue-layouts/utils.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
import { Avatar, PriorityIcon, StateGroupIcon } from "@plane/ui";
import { EIssueListRow, ISSUE_PRIORITIES } from "constants/issue";
import { renderEmoji } from "helpers/emoji.helper";
import { Avatar, CycleGroupIcon, DiceIcon, PriorityIcon, StateGroupIcon } from "@plane/ui";
// stores
import { IMemberRootStore } from "store/member";
import { IProjectStore } from "store/project/project.store";
import { IStateStore } from "store/state.store";
import { GroupByColumnTypes, IGroupByColumn, IIssueListRow, TGroupedIssues, TUnGroupedIssues } from "@plane/types";
import { STATE_GROUPS } from "constants/state";
import { ILabelStore } from "store/label.store";
import { ICycleStore } from "store/cycle.store";
import { IModuleStore } from "store/module.store";
// helpers
import { renderEmoji } from "helpers/emoji.helper";
// constants
import { STATE_GROUPS } from "constants/state";
import { ISSUE_PRIORITIES } from "constants/issue";
// types
import { GroupByColumnTypes, IGroupByColumn, TCycleGroups } from "@plane/types";
import { ContrastIcon } from "lucide-react";

export const getGroupByColumns = (
groupBy: GroupByColumnTypes | null,
project: IProjectStore,
cycle: ICycleStore,
module: IModuleStore,
label: ILabelStore,
projectState: IStateStore,
member: IMemberRootStore,
Expand All @@ -19,6 +28,10 @@ export const getGroupByColumns = (
switch (groupBy) {
case "project":
return getProjectColumns(project);
case "cycle":
return getCycleColumns(project, cycle);
case "module":
return getModuleColumns(project, module);
case "state":
return getStateColumns(projectState);
case "state_detail.group":
Expand Down Expand Up @@ -55,6 +68,68 @@ const getProjectColumns = (project: IProjectStore): IGroupByColumn[] | undefined
}) as any;
};

const getCycleColumns = (projectStore: IProjectStore, cycleStore: ICycleStore): IGroupByColumn[] | undefined => {
const { currentProjectDetails } = projectStore;
const { getProjectCycleIds, getCycleById } = cycleStore;

if (!currentProjectDetails || !currentProjectDetails?.id) return;

const cycleIds = currentProjectDetails?.id ? getProjectCycleIds(currentProjectDetails?.id) : undefined;
if (!cycleIds) return;

const cycles = [];

cycleIds.map((cycleId) => {
const cycle = getCycleById(cycleId);
if (cycle) {
const cycleStatus = cycle.status ? (cycle.status.toLocaleLowerCase() as TCycleGroups) : "draft";
cycles.push({
id: cycle.id,
name: cycle.name,
icon: <CycleGroupIcon cycleGroup={cycleStatus as TCycleGroups} className="h-3.5 w-3.5" />,
payload: { cycle_id: cycle.id },
});
}
});
cycles.push({
id: "None",
name: "None",
icon: <ContrastIcon className="h-3.5 w-3.5" />,
});

return cycles as any;
};

const getModuleColumns = (projectStore: IProjectStore, moduleStore: IModuleStore): IGroupByColumn[] | undefined => {
const { currentProjectDetails } = projectStore;
const { getProjectModuleIds, getModuleById } = moduleStore;

if (!currentProjectDetails || !currentProjectDetails?.id) return;

const moduleIds = currentProjectDetails?.id ? getProjectModuleIds(currentProjectDetails?.id) : undefined;
if (!moduleIds) return;

const modules = [];

moduleIds.map((moduleId) => {
const _module = getModuleById(moduleId);
if (_module)
modules.push({
id: _module.id,
name: _module.name,
icon: <DiceIcon className="w-3.5 h-3.5" />,
payload: { module_ids: [_module.id] },
});
}) as any;
modules.push({
id: "None",
name: "None",
icon: <DiceIcon className="w-3.5 h-3.5" />,
});

return modules as any;
};

const getStateColumns = (projectState: IStateStore): IGroupByColumn[] | undefined => {
const { projectStates } = projectState;
if (!projectStates) return;
Expand Down
Loading
Loading