mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
[WEB-601] feat: enhanced display filters grouping by cycles and modules in project issues (#3834)
* feat: implemented cycle and module for display filters groupBy and sunGroupBy in project issues list and kanban layouts * chore: Enabled drag ability for cycle and handled prepopulated data for quick add * chore: disbaled drag ability for cycle * chore: updated preloaded data * chore: updated module and cycle store router dependancy to prop dependancy
This commit is contained in:
parent
56805203f1
commit
9326fb0762
2
packages/types/src/issues.d.ts
vendored
2
packages/types/src/issues.d.ts
vendored
@ -203,6 +203,8 @@ export interface ViewFlags {
|
||||
|
||||
export type GroupByColumnTypes =
|
||||
| "project"
|
||||
| "cycle"
|
||||
| "module"
|
||||
| "state"
|
||||
| "state_detail.group"
|
||||
| "priority"
|
||||
|
2
packages/types/src/view-props.d.ts
vendored
2
packages/types/src/view-props.d.ts
vendored
@ -14,6 +14,8 @@ export type TIssueGroupByOptions =
|
||||
| "project"
|
||||
| "assignees"
|
||||
| "mentions"
|
||||
| "cycle"
|
||||
| "module"
|
||||
| null;
|
||||
|
||||
export type TIssueOrderByOptions =
|
||||
|
@ -152,6 +152,7 @@ export const CycleMobileHeader = () => {
|
||||
handleDisplayFiltersUpdate={handleDisplayFilters}
|
||||
displayProperties={issueFilters?.displayProperties ?? {}}
|
||||
handleDisplayPropertiesUpdate={handleDisplayProperties}
|
||||
ignoreGroupedFilters={["cycle"]}
|
||||
/>
|
||||
</FiltersDropdown>
|
||||
</div>
|
||||
|
@ -244,6 +244,7 @@ export const CycleIssuesHeader: React.FC = observer(() => {
|
||||
handleDisplayFiltersUpdate={handleDisplayFilters}
|
||||
displayProperties={issueFilters?.displayProperties ?? {}}
|
||||
handleDisplayPropertiesUpdate={handleDisplayProperties}
|
||||
ignoreGroupedFilters={["cycle"]}
|
||||
/>
|
||||
</FiltersDropdown>
|
||||
|
||||
|
@ -248,6 +248,7 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
|
||||
handleDisplayFiltersUpdate={handleDisplayFilters}
|
||||
displayProperties={issueFilters?.displayProperties ?? {}}
|
||||
handleDisplayPropertiesUpdate={handleDisplayProperties}
|
||||
ignoreGroupedFilters={["module"]}
|
||||
/>
|
||||
</FiltersDropdown>
|
||||
</div>
|
||||
|
@ -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 = {
|
||||
@ -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) => {
|
||||
@ -29,6 +30,7 @@ export const DisplayFiltersSelection: React.FC<Props> = observer((props) => {
|
||||
handleDisplayFiltersUpdate,
|
||||
handleDisplayPropertiesUpdate,
|
||||
layoutDisplayFiltersOptions,
|
||||
ignoreGroupedFilters = [],
|
||||
} = props;
|
||||
|
||||
const isDisplayFilterEnabled = (displayFilter: keyof IIssueDisplayFilterOptions) =>
|
||||
@ -54,6 +56,7 @@ export const DisplayFiltersSelection: React.FC<Props> = observer((props) => {
|
||||
group_by: val,
|
||||
})
|
||||
}
|
||||
ignoreGroupedFilters={ignoreGroupedFilters}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@ -71,6 +74,7 @@ export const DisplayFiltersSelection: React.FC<Props> = observer((props) => {
|
||||
})
|
||||
}
|
||||
subGroupByOptions={layoutDisplayFiltersOptions?.display_filters.sub_group_by ?? []}
|
||||
ignoreGroupedFilters={ignoreGroupedFilters}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import React, { useState } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
|
||||
// components
|
||||
import { FilterHeader, FilterOption } from "components/issues";
|
||||
// types
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
@ -1,6 +1,5 @@
|
||||
import React, { useState } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
|
||||
// components
|
||||
import { FilterHeader, FilterOption } from "components/issues";
|
||||
// types
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
@ -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";
|
||||
@ -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;
|
||||
|
||||
|
@ -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") {
|
||||
@ -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") {
|
||||
|
@ -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";
|
||||
|
||||
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
@ -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":
|
||||
@ -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;
|
||||
|
@ -14,153 +14,153 @@ import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOption
|
||||
import { ProjectAnalyticsModal } from "components/analytics";
|
||||
|
||||
export const IssuesMobileHeader = () => {
|
||||
const layouts = [
|
||||
{ key: "list", title: "List", icon: List },
|
||||
{ key: "kanban", title: "Kanban", icon: Kanban },
|
||||
{ key: "calendar", title: "Calendar", icon: Calendar },
|
||||
];
|
||||
const [analyticsModal, setAnalyticsModal] = useState(false);
|
||||
const { workspaceSlug, projectId } = router.query as {
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
};
|
||||
const { currentProjectDetails } = useProject();
|
||||
const { projectStates } = useProjectState();
|
||||
const { projectLabels } = useLabel();
|
||||
const layouts = [
|
||||
{ key: "list", title: "List", icon: List },
|
||||
{ key: "kanban", title: "Kanban", icon: Kanban },
|
||||
{ key: "calendar", title: "Calendar", icon: Calendar },
|
||||
];
|
||||
const [analyticsModal, setAnalyticsModal] = useState(false);
|
||||
const { workspaceSlug, projectId } = router.query as {
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
};
|
||||
const { currentProjectDetails } = useProject();
|
||||
const { projectStates } = useProjectState();
|
||||
const { projectLabels } = useLabel();
|
||||
|
||||
// store hooks
|
||||
const {
|
||||
issuesFilter: { issueFilters, updateFilters },
|
||||
} = useIssues(EIssuesStoreType.PROJECT);
|
||||
const {
|
||||
project: { projectMemberIds },
|
||||
} = useMember();
|
||||
const activeLayout = issueFilters?.displayFilters?.layout;
|
||||
// store hooks
|
||||
const {
|
||||
issuesFilter: { issueFilters, updateFilters },
|
||||
} = useIssues(EIssuesStoreType.PROJECT);
|
||||
const {
|
||||
project: { projectMemberIds },
|
||||
} = useMember();
|
||||
const activeLayout = issueFilters?.displayFilters?.layout;
|
||||
|
||||
const handleLayoutChange = useCallback(
|
||||
(layout: TIssueLayouts) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_FILTERS, { layout: layout });
|
||||
},
|
||||
[workspaceSlug, projectId, updateFilters]
|
||||
);
|
||||
const handleLayoutChange = useCallback(
|
||||
(layout: TIssueLayouts) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_FILTERS, { layout: layout });
|
||||
},
|
||||
[workspaceSlug, projectId, updateFilters]
|
||||
);
|
||||
|
||||
const handleFiltersUpdate = useCallback(
|
||||
(key: keyof IIssueFilterOptions, value: string | string[]) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
const newValues = issueFilters?.filters?.[key] ?? [];
|
||||
const handleFiltersUpdate = useCallback(
|
||||
(key: keyof IIssueFilterOptions, value: string | string[]) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
const newValues = issueFilters?.filters?.[key] ?? [];
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((val) => {
|
||||
if (!newValues.includes(val)) newValues.push(val);
|
||||
});
|
||||
} else {
|
||||
if (issueFilters?.filters?.[key]?.includes(value)) newValues.splice(newValues.indexOf(value), 1);
|
||||
else newValues.push(value);
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((val) => {
|
||||
if (!newValues.includes(val)) newValues.push(val);
|
||||
});
|
||||
} else {
|
||||
if (issueFilters?.filters?.[key]?.includes(value)) newValues.splice(newValues.indexOf(value), 1);
|
||||
else newValues.push(value);
|
||||
}
|
||||
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.FILTERS, { [key]: newValues });
|
||||
},
|
||||
[workspaceSlug, projectId, issueFilters, updateFilters]
|
||||
);
|
||||
|
||||
const handleDisplayFilters = useCallback(
|
||||
(updatedDisplayFilter: Partial<IIssueDisplayFilterOptions>) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_FILTERS, updatedDisplayFilter);
|
||||
},
|
||||
[workspaceSlug, projectId, updateFilters]
|
||||
);
|
||||
|
||||
const handleDisplayProperties = useCallback(
|
||||
(property: Partial<IIssueDisplayProperties>) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_PROPERTIES, property);
|
||||
},
|
||||
[workspaceSlug, projectId, updateFilters]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProjectAnalyticsModal
|
||||
isOpen={analyticsModal}
|
||||
onClose={() => setAnalyticsModal(false)}
|
||||
projectDetails={currentProjectDetails ?? undefined}
|
||||
/>
|
||||
<div className="flex justify-evenly py-2 border-b border-custom-border-200">
|
||||
<CustomMenu
|
||||
maxHeight={"md"}
|
||||
className="flex flex-grow justify-center text-custom-text-200 text-sm"
|
||||
placement="bottom-start"
|
||||
customButton={<span className="flex flex-grow justify-center text-custom-text-200 text-sm">Layout</span>}
|
||||
customButtonClassName="flex flex-grow justify-center text-custom-text-200 text-sm"
|
||||
closeOnSelect
|
||||
>
|
||||
{layouts.map((layout, index) => (
|
||||
<CustomMenu.MenuItem
|
||||
onClick={() => {
|
||||
handleLayoutChange(ISSUE_LAYOUTS[index].key);
|
||||
}}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<layout.icon className="w-3 h-3" />
|
||||
<div className="text-custom-text-300">{layout.title}</div>
|
||||
</CustomMenu.MenuItem>
|
||||
))}
|
||||
</CustomMenu>
|
||||
<div className="flex flex-grow justify-center border-l border-custom-border-200 items-center text-custom-text-200 text-sm">
|
||||
<FiltersDropdown
|
||||
title="Filters"
|
||||
placement="bottom-end"
|
||||
menuButton={
|
||||
<span className="flex items-center text-custom-text-200 text-sm">
|
||||
Filters
|
||||
<ChevronDown className="text-custom-text-200 h-4 w-4 ml-2" />
|
||||
</span>
|
||||
}
|
||||
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.FILTERS, { [key]: newValues });
|
||||
},
|
||||
[workspaceSlug, projectId, issueFilters, updateFilters]
|
||||
);
|
||||
|
||||
const handleDisplayFilters = useCallback(
|
||||
(updatedDisplayFilter: Partial<IIssueDisplayFilterOptions>) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_FILTERS, updatedDisplayFilter);
|
||||
},
|
||||
[workspaceSlug, projectId, updateFilters]
|
||||
);
|
||||
|
||||
const handleDisplayProperties = useCallback(
|
||||
(property: Partial<IIssueDisplayProperties>) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_PROPERTIES, property);
|
||||
},
|
||||
[workspaceSlug, projectId, updateFilters]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProjectAnalyticsModal
|
||||
isOpen={analyticsModal}
|
||||
onClose={() => setAnalyticsModal(false)}
|
||||
projectDetails={currentProjectDetails ?? undefined}
|
||||
>
|
||||
<FilterSelection
|
||||
filters={issueFilters?.filters ?? {}}
|
||||
handleFiltersUpdate={handleFiltersUpdate}
|
||||
layoutDisplayFiltersOptions={
|
||||
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues[activeLayout] : undefined
|
||||
}
|
||||
labels={projectLabels}
|
||||
memberIds={projectMemberIds ?? undefined}
|
||||
states={projectStates}
|
||||
/>
|
||||
<div className="flex justify-evenly py-2 border-b border-custom-border-200">
|
||||
<CustomMenu
|
||||
maxHeight={"md"}
|
||||
className="flex flex-grow justify-center text-custom-text-200 text-sm"
|
||||
placement="bottom-start"
|
||||
customButton={<span className="flex flex-grow justify-center text-custom-text-200 text-sm">Layout</span>}
|
||||
customButtonClassName="flex flex-grow justify-center text-custom-text-200 text-sm"
|
||||
closeOnSelect
|
||||
>
|
||||
{layouts.map((layout, index) => (
|
||||
<CustomMenu.MenuItem
|
||||
onClick={() => {
|
||||
handleLayoutChange(ISSUE_LAYOUTS[index].key);
|
||||
}}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<layout.icon className="w-3 h-3" />
|
||||
<div className="text-custom-text-300">{layout.title}</div>
|
||||
</CustomMenu.MenuItem>
|
||||
))}
|
||||
</CustomMenu>
|
||||
<div className="flex flex-grow justify-center border-l border-custom-border-200 items-center text-custom-text-200 text-sm">
|
||||
<FiltersDropdown
|
||||
title="Filters"
|
||||
placement="bottom-end"
|
||||
menuButton={
|
||||
<span className="flex items-center text-custom-text-200 text-sm">
|
||||
Filters
|
||||
<ChevronDown className="text-custom-text-200 h-4 w-4 ml-2" />
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<FilterSelection
|
||||
filters={issueFilters?.filters ?? {}}
|
||||
handleFiltersUpdate={handleFiltersUpdate}
|
||||
layoutDisplayFiltersOptions={
|
||||
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues[activeLayout] : undefined
|
||||
}
|
||||
labels={projectLabels}
|
||||
memberIds={projectMemberIds ?? undefined}
|
||||
states={projectStates}
|
||||
/>
|
||||
</FiltersDropdown>
|
||||
</div>
|
||||
<div className="flex flex-grow justify-center border-l border-custom-border-200 items-center text-custom-text-200 text-sm">
|
||||
<FiltersDropdown
|
||||
title="Display"
|
||||
placement="bottom-end"
|
||||
menuButton={
|
||||
<span className="flex items-center text-custom-text-200 text-sm">
|
||||
Display
|
||||
<ChevronDown className="text-custom-text-200 h-4 w-4 ml-2" />
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<DisplayFiltersSelection
|
||||
layoutDisplayFiltersOptions={
|
||||
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues[activeLayout] : undefined
|
||||
}
|
||||
displayFilters={issueFilters?.displayFilters ?? {}}
|
||||
handleDisplayFiltersUpdate={handleDisplayFilters}
|
||||
displayProperties={issueFilters?.displayProperties ?? {}}
|
||||
handleDisplayPropertiesUpdate={handleDisplayProperties}
|
||||
/>
|
||||
</FiltersDropdown>
|
||||
</div>
|
||||
</FiltersDropdown>
|
||||
</div>
|
||||
<div className="flex flex-grow justify-center border-l border-custom-border-200 items-center text-custom-text-200 text-sm">
|
||||
<FiltersDropdown
|
||||
title="Display"
|
||||
placement="bottom-end"
|
||||
menuButton={
|
||||
<span className="flex items-center text-custom-text-200 text-sm">
|
||||
Display
|
||||
<ChevronDown className="text-custom-text-200 h-4 w-4 ml-2" />
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<DisplayFiltersSelection
|
||||
layoutDisplayFiltersOptions={
|
||||
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues[activeLayout] : undefined
|
||||
}
|
||||
displayFilters={issueFilters?.displayFilters ?? {}}
|
||||
handleDisplayFiltersUpdate={handleDisplayFilters}
|
||||
displayProperties={issueFilters?.displayProperties ?? {}}
|
||||
handleDisplayPropertiesUpdate={handleDisplayProperties}
|
||||
/>
|
||||
</FiltersDropdown>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => setAnalyticsModal(true)}
|
||||
className="flex flex-grow justify-center text-custom-text-200 text-sm border-l border-custom-border-200"
|
||||
>
|
||||
Analytics
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
<button
|
||||
onClick={() => setAnalyticsModal(true)}
|
||||
className="flex flex-grow justify-center text-custom-text-200 text-sm border-l border-custom-border-200"
|
||||
>
|
||||
Analytics
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -9,154 +9,155 @@ import router from "next/router";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
export const ModuleMobileHeader = () => {
|
||||
const [analyticsModal, setAnalyticsModal] = useState(false);
|
||||
const { getModuleById } = useModule();
|
||||
const layouts = [
|
||||
{ key: "list", title: "List", icon: List },
|
||||
{ key: "kanban", title: "Kanban", icon: Kanban },
|
||||
{ key: "calendar", title: "Calendar", icon: Calendar },
|
||||
];
|
||||
const { workspaceSlug, projectId, moduleId } = router.query as {
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
moduleId: string;
|
||||
};
|
||||
const moduleDetails = moduleId ? getModuleById(moduleId.toString()) : undefined;
|
||||
const [analyticsModal, setAnalyticsModal] = useState(false);
|
||||
const { getModuleById } = useModule();
|
||||
const layouts = [
|
||||
{ key: "list", title: "List", icon: List },
|
||||
{ key: "kanban", title: "Kanban", icon: Kanban },
|
||||
{ key: "calendar", title: "Calendar", icon: Calendar },
|
||||
];
|
||||
const { workspaceSlug, projectId, moduleId } = router.query as {
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
moduleId: string;
|
||||
};
|
||||
const moduleDetails = moduleId ? getModuleById(moduleId.toString()) : undefined;
|
||||
|
||||
const {
|
||||
issuesFilter: { issueFilters, updateFilters },
|
||||
} = useIssues(EIssuesStoreType.MODULE);
|
||||
const activeLayout = issueFilters?.displayFilters?.layout;
|
||||
const { projectStates } = useProjectState();
|
||||
const { projectLabels } = useLabel();
|
||||
const {
|
||||
project: { projectMemberIds },
|
||||
} = useMember();
|
||||
const {
|
||||
issuesFilter: { issueFilters, updateFilters },
|
||||
} = useIssues(EIssuesStoreType.MODULE);
|
||||
const activeLayout = issueFilters?.displayFilters?.layout;
|
||||
const { projectStates } = useProjectState();
|
||||
const { projectLabels } = useLabel();
|
||||
const {
|
||||
project: { projectMemberIds },
|
||||
} = useMember();
|
||||
|
||||
const handleLayoutChange = useCallback(
|
||||
(layout: TIssueLayouts) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_FILTERS, { layout: layout }, moduleId);
|
||||
},
|
||||
[workspaceSlug, projectId, moduleId, updateFilters]
|
||||
);
|
||||
const handleLayoutChange = useCallback(
|
||||
(layout: TIssueLayouts) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_FILTERS, { layout: layout }, moduleId);
|
||||
},
|
||||
[workspaceSlug, projectId, moduleId, updateFilters]
|
||||
);
|
||||
|
||||
const handleFiltersUpdate = useCallback(
|
||||
(key: keyof IIssueFilterOptions, value: string | string[]) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
const newValues = issueFilters?.filters?.[key] ?? [];
|
||||
const handleFiltersUpdate = useCallback(
|
||||
(key: keyof IIssueFilterOptions, value: string | string[]) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
const newValues = issueFilters?.filters?.[key] ?? [];
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((val) => {
|
||||
if (!newValues.includes(val)) newValues.push(val);
|
||||
});
|
||||
} else {
|
||||
if (issueFilters?.filters?.[key]?.includes(value)) newValues.splice(newValues.indexOf(value), 1);
|
||||
else newValues.push(value);
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach((val) => {
|
||||
if (!newValues.includes(val)) newValues.push(val);
|
||||
});
|
||||
} else {
|
||||
if (issueFilters?.filters?.[key]?.includes(value)) newValues.splice(newValues.indexOf(value), 1);
|
||||
else newValues.push(value);
|
||||
}
|
||||
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.FILTERS, { [key]: newValues }, moduleId);
|
||||
},
|
||||
[workspaceSlug, projectId, moduleId, issueFilters, updateFilters]
|
||||
);
|
||||
|
||||
const handleDisplayFilters = useCallback(
|
||||
(updatedDisplayFilter: Partial<IIssueDisplayFilterOptions>) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_FILTERS, updatedDisplayFilter, moduleId);
|
||||
},
|
||||
[workspaceSlug, projectId, moduleId, updateFilters]
|
||||
);
|
||||
|
||||
const handleDisplayProperties = useCallback(
|
||||
(property: Partial<IIssueDisplayProperties>) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_PROPERTIES, property, moduleId);
|
||||
},
|
||||
[workspaceSlug, projectId, moduleId, updateFilters]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="block md:hidden">
|
||||
<ProjectAnalyticsModal
|
||||
isOpen={analyticsModal}
|
||||
onClose={() => setAnalyticsModal(false)}
|
||||
moduleDetails={moduleDetails ?? undefined}
|
||||
/>
|
||||
<div className="flex justify-evenly py-2 border-b border-custom-border-200">
|
||||
<CustomMenu
|
||||
maxHeight={"md"}
|
||||
className="flex flex-grow justify-center text-custom-text-200 text-sm"
|
||||
placement="bottom-start"
|
||||
customButton={<span className="flex flex-grow justify-center text-custom-text-200 text-sm">Layout</span>}
|
||||
customButtonClassName="flex flex-grow justify-center text-custom-text-200 text-sm"
|
||||
closeOnSelect
|
||||
>
|
||||
{layouts.map((layout, index) => (
|
||||
<CustomMenu.MenuItem
|
||||
onClick={() => {
|
||||
handleLayoutChange(ISSUE_LAYOUTS[index].key);
|
||||
}}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<layout.icon className="w-3 h-3" />
|
||||
<div className="text-custom-text-300">{layout.title}</div>
|
||||
</CustomMenu.MenuItem>
|
||||
))}
|
||||
</CustomMenu>
|
||||
<div className="flex flex-grow justify-center border-l border-custom-border-200 items-center text-custom-text-200 text-sm">
|
||||
<FiltersDropdown
|
||||
title="Filters"
|
||||
placement="bottom-end"
|
||||
menuButton={
|
||||
<span className="flex items-center text-custom-text-200 text-sm">
|
||||
Filters
|
||||
<ChevronDown className="text-custom-text-200 h-4 w-4 ml-2" />
|
||||
</span>
|
||||
}
|
||||
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.FILTERS, { [key]: newValues }, moduleId);
|
||||
},
|
||||
[workspaceSlug, projectId, moduleId, issueFilters, updateFilters]
|
||||
);
|
||||
|
||||
const handleDisplayFilters = useCallback(
|
||||
(updatedDisplayFilter: Partial<IIssueDisplayFilterOptions>) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_FILTERS, updatedDisplayFilter, moduleId);
|
||||
},
|
||||
[workspaceSlug, projectId, moduleId, updateFilters]
|
||||
);
|
||||
|
||||
const handleDisplayProperties = useCallback(
|
||||
(property: Partial<IIssueDisplayProperties>) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_PROPERTIES, property, moduleId);
|
||||
},
|
||||
[workspaceSlug, projectId, moduleId, updateFilters]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="block md:hidden">
|
||||
<ProjectAnalyticsModal
|
||||
isOpen={analyticsModal}
|
||||
onClose={() => setAnalyticsModal(false)}
|
||||
moduleDetails={moduleDetails ?? undefined}
|
||||
>
|
||||
<FilterSelection
|
||||
filters={issueFilters?.filters ?? {}}
|
||||
handleFiltersUpdate={handleFiltersUpdate}
|
||||
layoutDisplayFiltersOptions={
|
||||
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues[activeLayout] : undefined
|
||||
}
|
||||
labels={projectLabels}
|
||||
memberIds={projectMemberIds ?? undefined}
|
||||
states={projectStates}
|
||||
/>
|
||||
<div className="flex justify-evenly py-2 border-b border-custom-border-200">
|
||||
<CustomMenu
|
||||
maxHeight={"md"}
|
||||
className="flex flex-grow justify-center text-custom-text-200 text-sm"
|
||||
placement="bottom-start"
|
||||
customButton={<span className="flex flex-grow justify-center text-custom-text-200 text-sm">Layout</span>}
|
||||
customButtonClassName="flex flex-grow justify-center text-custom-text-200 text-sm"
|
||||
closeOnSelect
|
||||
>
|
||||
{layouts.map((layout, index) => (
|
||||
<CustomMenu.MenuItem
|
||||
onClick={() => {
|
||||
handleLayoutChange(ISSUE_LAYOUTS[index].key);
|
||||
}}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<layout.icon className="w-3 h-3" />
|
||||
<div className="text-custom-text-300">{layout.title}</div>
|
||||
</CustomMenu.MenuItem>
|
||||
))}
|
||||
</CustomMenu>
|
||||
<div className="flex flex-grow justify-center border-l border-custom-border-200 items-center text-custom-text-200 text-sm">
|
||||
<FiltersDropdown
|
||||
title="Filters"
|
||||
placement="bottom-end"
|
||||
menuButton={
|
||||
<span className="flex items-center text-custom-text-200 text-sm">
|
||||
Filters
|
||||
<ChevronDown className="text-custom-text-200 h-4 w-4 ml-2" />
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<FilterSelection
|
||||
filters={issueFilters?.filters ?? {}}
|
||||
handleFiltersUpdate={handleFiltersUpdate}
|
||||
layoutDisplayFiltersOptions={
|
||||
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues[activeLayout] : undefined
|
||||
}
|
||||
labels={projectLabels}
|
||||
memberIds={projectMemberIds ?? undefined}
|
||||
states={projectStates}
|
||||
/>
|
||||
</FiltersDropdown>
|
||||
</div>
|
||||
<div className="flex flex-grow justify-center border-l border-custom-border-200 items-center text-custom-text-200 text-sm">
|
||||
<FiltersDropdown
|
||||
title="Display"
|
||||
placement="bottom-end"
|
||||
menuButton={
|
||||
<span className="flex items-center text-custom-text-200 text-sm">
|
||||
Display
|
||||
<ChevronDown className="text-custom-text-200 h-4 w-4 ml-2" />
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<DisplayFiltersSelection
|
||||
layoutDisplayFiltersOptions={
|
||||
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues[activeLayout] : undefined
|
||||
}
|
||||
displayFilters={issueFilters?.displayFilters ?? {}}
|
||||
handleDisplayFiltersUpdate={handleDisplayFilters}
|
||||
displayProperties={issueFilters?.displayProperties ?? {}}
|
||||
handleDisplayPropertiesUpdate={handleDisplayProperties}
|
||||
/>
|
||||
</FiltersDropdown>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => setAnalyticsModal(true)}
|
||||
className="flex flex-grow justify-center text-custom-text-200 text-sm border-l border-custom-border-200"
|
||||
>
|
||||
Analytics
|
||||
</button>
|
||||
</div>
|
||||
</FiltersDropdown>
|
||||
</div>
|
||||
);
|
||||
<div className="flex flex-grow justify-center border-l border-custom-border-200 items-center text-custom-text-200 text-sm">
|
||||
<FiltersDropdown
|
||||
title="Display"
|
||||
placement="bottom-end"
|
||||
menuButton={
|
||||
<span className="flex items-center text-custom-text-200 text-sm">
|
||||
Display
|
||||
<ChevronDown className="text-custom-text-200 h-4 w-4 ml-2" />
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<DisplayFiltersSelection
|
||||
layoutDisplayFiltersOptions={
|
||||
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues[activeLayout] : undefined
|
||||
}
|
||||
displayFilters={issueFilters?.displayFilters ?? {}}
|
||||
handleDisplayFiltersUpdate={handleDisplayFilters}
|
||||
displayProperties={issueFilters?.displayProperties ?? {}}
|
||||
handleDisplayPropertiesUpdate={handleDisplayProperties}
|
||||
ignoreGroupedFilters={["module"]}
|
||||
/>
|
||||
</FiltersDropdown>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => setAnalyticsModal(true)}
|
||||
className="flex flex-grow justify-center text-custom-text-200 text-sm border-l border-custom-border-200"
|
||||
>
|
||||
Analytics
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -49,22 +49,6 @@ export const ISSUE_PRIORITIES: {
|
||||
{ key: "none", title: "None" },
|
||||
];
|
||||
|
||||
export const ISSUE_START_DATE_OPTIONS = [
|
||||
{ key: "last_week", title: "Last Week" },
|
||||
{ key: "2_weeks_from_now", title: "2 weeks from now" },
|
||||
{ key: "1_month_from_now", title: "1 month from now" },
|
||||
{ key: "2_months_from_now", title: "2 months from now" },
|
||||
{ key: "custom", title: "Custom" },
|
||||
];
|
||||
|
||||
export const ISSUE_DUE_DATE_OPTIONS = [
|
||||
{ key: "last_week", title: "Last Week" },
|
||||
{ key: "2_weeks_from_now", title: "2 weeks from now" },
|
||||
{ key: "1_month_from_now", title: "1 month from now" },
|
||||
{ key: "2_months_from_now", title: "2 months from now" },
|
||||
{ key: "custom", title: "Custom" },
|
||||
];
|
||||
|
||||
export const ISSUE_GROUP_BY_OPTIONS: {
|
||||
key: TIssueGroupByOptions;
|
||||
title: string;
|
||||
@ -73,6 +57,8 @@ export const ISSUE_GROUP_BY_OPTIONS: {
|
||||
{ key: "state_detail.group", title: "State Groups" },
|
||||
{ key: "priority", title: "Priority" },
|
||||
{ key: "project", title: "Project" }, // required this on my issues
|
||||
{ key: "cycle", title: "Cycle" }, // required this on my issues
|
||||
{ key: "module", title: "Module" }, // required this on my issues
|
||||
{ key: "labels", title: "Labels" },
|
||||
{ key: "assignees", title: "Assignees" },
|
||||
{ key: "created_by", title: "Created By" },
|
||||
@ -140,81 +126,6 @@ export const ISSUE_LAYOUTS: {
|
||||
{ key: "gantt_chart", title: "Gantt Chart Layout", icon: GanttChartSquare },
|
||||
];
|
||||
|
||||
export const ISSUE_LIST_FILTERS = [
|
||||
{ key: "mentions", title: "Mentions" },
|
||||
{ key: "priority", title: "Priority" },
|
||||
{ key: "state", title: "State" },
|
||||
{ key: "assignees", title: "Assignees" },
|
||||
{ key: "created_by", title: "Created By" },
|
||||
{ key: "labels", title: "Labels" },
|
||||
{ key: "start_date", title: "Start Date" },
|
||||
{ key: "due_date", title: "Due Date" },
|
||||
];
|
||||
|
||||
export const ISSUE_KANBAN_FILTERS = [
|
||||
{ key: "priority", title: "Priority" },
|
||||
{ key: "state", title: "State" },
|
||||
{ key: "assignees", title: "Assignees" },
|
||||
{ key: "created_by", title: "Created By" },
|
||||
{ key: "labels", title: "Labels" },
|
||||
{ key: "start_date", title: "Start Date" },
|
||||
{ key: "due_date", title: "Due Date" },
|
||||
];
|
||||
|
||||
export const ISSUE_CALENDER_FILTERS = [
|
||||
{ key: "priority", title: "Priority" },
|
||||
{ key: "state", title: "State" },
|
||||
{ key: "assignees", title: "Assignees" },
|
||||
{ key: "created_by", title: "Created By" },
|
||||
{ key: "labels", title: "Labels" },
|
||||
];
|
||||
|
||||
export const ISSUE_SPREADSHEET_FILTERS = [
|
||||
{ key: "priority", title: "Priority" },
|
||||
{ key: "state", title: "State" },
|
||||
{ key: "assignees", title: "Assignees" },
|
||||
{ key: "created_by", title: "Created By" },
|
||||
{ key: "labels", title: "Labels" },
|
||||
{ key: "start_date", title: "Start Date" },
|
||||
{ key: "due_date", title: "Due Date" },
|
||||
];
|
||||
|
||||
export const ISSUE_GANTT_FILTERS = [
|
||||
{ key: "priority", title: "Priority" },
|
||||
{ key: "state", title: "State" },
|
||||
{ key: "assignees", title: "Assignees" },
|
||||
{ key: "created_by", title: "Created By" },
|
||||
{ key: "labels", title: "Labels" },
|
||||
{ key: "start_date", title: "Start Date" },
|
||||
{ key: "due_date", title: "Due Date" },
|
||||
];
|
||||
|
||||
export const ISSUE_LIST_DISPLAY_FILTERS = [
|
||||
{ key: "group_by", title: "Group By" },
|
||||
{ key: "order_by", title: "Order By" },
|
||||
{ key: "issue_type", title: "Issue Type" },
|
||||
{ key: "sub_issue", title: "Sub Issue" },
|
||||
{ key: "show_empty_groups", title: "Show Empty Groups" },
|
||||
];
|
||||
|
||||
export const ISSUE_KANBAN_DISPLAY_FILTERS = [
|
||||
{ key: "group_by", title: "Group By" },
|
||||
{ key: "order_by", title: "Order By" },
|
||||
{ key: "issue_type", title: "Issue Type" },
|
||||
{ key: "sub_issue", title: "Sub Issue" },
|
||||
{ key: "show_empty_groups", title: "Show Empty Groups" },
|
||||
];
|
||||
|
||||
export const ISSUE_CALENDER_DISPLAY_FILTERS = [{ key: "issue_type", title: "Issue Type" }];
|
||||
|
||||
export const ISSUE_SPREADSHEET_DISPLAY_FILTERS = [{ key: "issue_type", title: "Issue Type" }];
|
||||
|
||||
export const ISSUE_GANTT_DISPLAY_FILTERS = [
|
||||
{ key: "order_by", title: "Order By" },
|
||||
{ key: "issue_type", title: "Issue Type" },
|
||||
{ key: "sub_issue", title: "Sub Issue" },
|
||||
];
|
||||
|
||||
export interface ILayoutDisplayFiltersOptions {
|
||||
filters: (keyof IIssueFilterOptions)[];
|
||||
display_properties: boolean;
|
||||
@ -276,7 +187,17 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: {
|
||||
],
|
||||
display_properties: true,
|
||||
display_filters: {
|
||||
group_by: ["state", "state_detail.group", "priority", "labels", "assignees", "created_by", null],
|
||||
group_by: [
|
||||
"state",
|
||||
"cycle",
|
||||
"module",
|
||||
"state_detail.group",
|
||||
"priority",
|
||||
"labels",
|
||||
"assignees",
|
||||
"created_by",
|
||||
null,
|
||||
],
|
||||
order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "-priority"],
|
||||
type: [null, "active", "backlog"],
|
||||
},
|
||||
@ -291,7 +212,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: {
|
||||
filters: ["priority", "state_group", "cycle", "module", "labels", "start_date", "target_date"],
|
||||
display_properties: true,
|
||||
display_filters: {
|
||||
group_by: ["state_detail.group", "priority", "project", "labels", null],
|
||||
group_by: ["state_detail.group", "cycle", "module", "priority", "project", "labels", null],
|
||||
order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "-priority"],
|
||||
type: [null, "active", "backlog"],
|
||||
},
|
||||
@ -304,7 +225,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: {
|
||||
filters: ["priority", "state_group", "cycle", "module", "labels", "start_date", "target_date"],
|
||||
display_properties: true,
|
||||
display_filters: {
|
||||
group_by: ["state_detail.group", "priority", "project", "labels"],
|
||||
group_by: ["state_detail.group", "cycle", "module", "priority", "project", "labels"],
|
||||
order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "-priority"],
|
||||
type: [null, "active", "backlog"],
|
||||
},
|
||||
@ -374,7 +295,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: {
|
||||
],
|
||||
display_properties: true,
|
||||
display_filters: {
|
||||
group_by: ["state", "priority", "labels", "assignees", "created_by", null],
|
||||
group_by: ["state", "priority", "cycle", "module", "labels", "assignees", "created_by", null],
|
||||
order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "-priority"],
|
||||
type: [null, "active", "backlog"],
|
||||
},
|
||||
@ -398,8 +319,8 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: {
|
||||
],
|
||||
display_properties: true,
|
||||
display_filters: {
|
||||
group_by: ["state", "priority", "labels", "assignees", "created_by"],
|
||||
sub_group_by: ["state", "priority", "labels", "assignees", "created_by", null],
|
||||
group_by: ["state", "priority", "cycle", "module", "labels", "assignees", "created_by"],
|
||||
sub_group_by: ["state", "priority", "cycle", "module", "labels", "assignees", "created_by", null],
|
||||
order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "-priority", "target_date"],
|
||||
type: [null, "active", "backlog"],
|
||||
},
|
||||
|
@ -36,6 +36,8 @@ export type TIssueHelperStore = {
|
||||
|
||||
const ISSUE_FILTER_DEFAULT_DATA: Record<TIssueDisplayFilterOptions, keyof TIssue> = {
|
||||
project: "project_id",
|
||||
cycle: "cycle_id",
|
||||
module: "module_ids",
|
||||
state: "state_id",
|
||||
"state_detail.group": "state_group" as keyof TIssue, // state_detail.group is only being used for state_group display,
|
||||
priority: "priority",
|
||||
@ -157,6 +159,10 @@ export class IssueHelperStore implements TIssueHelperStore {
|
||||
return Object.keys(this.rootStore?.workSpaceMemberRolesMap || {});
|
||||
case "project":
|
||||
return Object.keys(this.rootStore?.projectMap || {});
|
||||
case "cycle":
|
||||
return Object.keys(this.rootStore?.cycleMap || {});
|
||||
case "module":
|
||||
return Object.keys(this.rootStore?.moduleMap || {});
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user