diff --git a/packages/types/src/view-props.d.ts b/packages/types/src/view-props.d.ts index 6b0970cf683..59d5ffded52 100644 --- a/packages/types/src/view-props.d.ts +++ b/packages/types/src/view-props.d.ts @@ -127,6 +127,7 @@ export interface IIssueDisplayProperties { updated_on?: boolean; modules?: boolean; cycle?: boolean; + issue_type?: boolean; } export type TIssueKanbanFilters = { diff --git a/web/ce/components/issues/issue-details/issue-identifier.tsx b/web/ce/components/issues/issue-details/issue-identifier.tsx index 019acbac575..b12cc6de71c 100644 --- a/web/ce/components/issues/issue-details/issue-identifier.tsx +++ b/web/ce/components/issues/issue-details/issue-identifier.tsx @@ -1,4 +1,6 @@ import { observer } from "mobx-react"; +// types +import { IIssueDisplayProperties } from "@plane/types"; // helpers import { cn } from "@/helpers/common.helper"; // hooks @@ -8,6 +10,7 @@ type TIssueIdentifierBaseProps = { projectId: string; size?: "xs" | "sm" | "md" | "lg"; textContainerClassName?: string; + displayProperties?: IIssueDisplayProperties | undefined; }; type TIssueIdentifierFromStore = TIssueIdentifierBaseProps & { @@ -22,7 +25,7 @@ type TIssueIdentifierWithDetails = TIssueIdentifierBaseProps & { type TIssueIdentifierProps = TIssueIdentifierFromStore | TIssueIdentifierWithDetails; export const IssueIdentifier: React.FC = observer((props) => { - const { projectId, textContainerClassName } = props; + const { projectId, textContainerClassName, displayProperties } = props; // store hooks const { getProjectIdentifierById } = useProject(); const { @@ -34,6 +37,9 @@ export const IssueIdentifier: React.FC = observer((props) const issue = isUsingStoreData ? getIssueById(props.issueId) : null; const projectIdentifier = isUsingStoreData ? getProjectIdentifierById(projectId) : props.projectIdentifier; const issueSequenceId = isUsingStoreData ? issue?.sequence_id : props.issueSequenceId; + const shouldRenderIssueID = displayProperties ? displayProperties.key : true; + + if (!shouldRenderIssueID) return null; return (
diff --git a/web/ce/helpers/issue-filter.helper.ts b/web/ce/helpers/issue-filter.helper.ts new file mode 100644 index 00000000000..e39259cd937 --- /dev/null +++ b/web/ce/helpers/issue-filter.helper.ts @@ -0,0 +1,18 @@ +// types +import { IIssueDisplayProperties } from "@plane/types"; + +export type TShouldRenderDisplayProperty = { + workspaceSlug: string; + projectId: string | undefined; + key: keyof IIssueDisplayProperties; +}; + +export const shouldRenderDisplayProperty = (props: TShouldRenderDisplayProperty) => { + const { key } = props; + switch (key) { + case "issue_type": + return false; + default: + return true; + } +}; diff --git a/web/core/components/issues/issue-layouts/calendar/base-calendar-root.tsx b/web/core/components/issues/issue-layouts/calendar/base-calendar-root.tsx index 296174fcc8b..0639044304e 100644 --- a/web/core/components/issues/issue-layouts/calendar/base-calendar-root.tsx +++ b/web/core/components/issues/issue-layouts/calendar/base-calendar-root.tsx @@ -19,7 +19,7 @@ import { useIssuesActions } from "@/hooks/use-issues-actions"; import { IQuickActionProps } from "../list/list-view-types"; import { handleDragDrop } from "./utils"; -type CalendarStoreType = +export type CalendarStoreType = | EIssuesStoreType.PROJECT | EIssuesStoreType.MODULE | EIssuesStoreType.CYCLE diff --git a/web/core/components/issues/issue-layouts/calendar/issue-block.tsx b/web/core/components/issues/issue-layouts/calendar/issue-block.tsx index f4bbd4ac4dc..fd3e7a46fbb 100644 --- a/web/core/components/issues/issue-layouts/calendar/issue-block.tsx +++ b/web/core/components/issues/issue-layouts/calendar/issue-block.tsx @@ -12,7 +12,8 @@ import { Tooltip, ControlLink } from "@plane/ui"; // helpers import { cn } from "@/helpers/common.helper"; // hooks -import { useIssueDetail, useProjectState } from "@/hooks/store"; +import { useIssueDetail, useIssues, useProjectState } from "@/hooks/store"; +import { useIssueStoreType } from "@/hooks/use-issue-layout-store"; import useIssuePeekOverviewRedirection from "@/hooks/use-issue-peek-overview-redirection"; import useOutsideClickDetector from "@/hooks/use-outside-click-detector"; import { usePlatformOS } from "@/hooks/use-platform-os"; @@ -20,6 +21,7 @@ import { usePlatformOS } from "@/hooks/use-platform-os"; import { IssueIdentifier } from "@/plane-web/components/issues/issue-details"; // local components import { TRenderQuickActions } from "../list/list-view-types"; +import { CalendarStoreType } from "./base-calendar-root"; type Props = { issue: TIssue; @@ -41,6 +43,8 @@ export const CalendarIssueBlock = observer( const { getIsIssuePeeked } = useIssueDetail(); const { handleRedirection } = useIssuePeekOverviewRedirection(); const { isMobile } = usePlatformOS(); + const storeType = useIssueStoreType() as CalendarStoreType; + const { issuesFilter } = useIssues(storeType); const stateColor = getProjectStates(issue?.project_id)?.find((state) => state?.id == issue?.state_id)?.color || ""; @@ -103,6 +107,7 @@ export const CalendarIssueBlock = observer( issueId={issue.id} projectId={issue.project_id} textContainerClassName="text-sm md:text-xs text-custom-text-300" + displayProperties={issuesFilter?.issueFilters?.displayProperties} /> )} diff --git a/web/core/components/issues/issue-layouts/filters/header/display-filters/display-filters-selection.tsx b/web/core/components/issues/issue-layouts/filters/header/display-filters/display-filters-selection.tsx index a3007658cc3..604b12581f0 100644 --- a/web/core/components/issues/issue-layouts/filters/header/display-filters/display-filters-selection.tsx +++ b/web/core/components/issues/issue-layouts/filters/header/display-filters/display-filters-selection.tsx @@ -50,10 +50,11 @@ export const DisplayFiltersSelection: React.FC = observer((props) => { return (
{/* display properties */} - {layoutDisplayFiltersOptions?.display_properties && ( + {layoutDisplayFiltersOptions?.display_properties && layoutDisplayFiltersOptions.display_properties.length > 0 && (
) => void; cycleViewDisabled?: boolean; moduleViewDisabled?: boolean; }; export const FilterDisplayProperties: React.FC = observer((props) => { - const { displayProperties, handleUpdate, cycleViewDisabled = false, moduleViewDisabled = false } = props; - + const { + displayProperties, + displayPropertiesToRender, + handleUpdate, + cycleViewDisabled = false, + moduleViewDisabled = false, + } = props; + // router + const { workspaceSlug, projectId: routerProjectId } = useParams(); + // states const [previewEnabled, setPreviewEnabled] = React.useState(true); + // derived values + const projectId = !!routerProjectId ? routerProjectId?.toString() : undefined; // Filter out "cycle" and "module" keys if cycleViewDisabled or moduleViewDisabled is true + // Also filter out display properties that should not be rendered const filteredDisplayProperties = ISSUE_DISPLAY_PROPERTIES.filter((property) => { - if (cycleViewDisabled && property.key === "cycle") return false; - if (moduleViewDisabled && property.key === "modules") return false; - return true; + if (!displayPropertiesToRender.includes(property.key)) return false; + switch (property.key) { + case "cycle": + return !cycleViewDisabled; + case "modules": + return !moduleViewDisabled; + default: + return shouldRenderDisplayProperty({ workspaceSlug: workspaceSlug?.toString(), projectId, key: property.key }); + } }); return ( diff --git a/web/core/components/issues/issue-layouts/gantt/base-gantt-root.tsx b/web/core/components/issues/issue-layouts/gantt/base-gantt-root.tsx index d60373312d3..62467102aaf 100644 --- a/web/core/components/issues/issue-layouts/gantt/base-gantt-root.tsx +++ b/web/core/components/issues/issue-layouts/gantt/base-gantt-root.tsx @@ -28,7 +28,7 @@ interface IBaseGanttRoot { isCompletedCycle?: boolean; } -type GanttStoreType = +export type GanttStoreType = | EIssuesStoreType.PROJECT | EIssuesStoreType.MODULE | EIssuesStoreType.CYCLE diff --git a/web/core/components/issues/issue-layouts/gantt/blocks.tsx b/web/core/components/issues/issue-layouts/gantt/blocks.tsx index 6f67a87487c..438797db63b 100644 --- a/web/core/components/issues/issue-layouts/gantt/blocks.tsx +++ b/web/core/components/issues/issue-layouts/gantt/blocks.tsx @@ -7,11 +7,14 @@ import { Tooltip, ControlLink } from "@plane/ui"; // helpers import { renderFormattedDate } from "@/helpers/date-time.helper"; // hooks -import { useIssueDetail, useProjectState } from "@/hooks/store"; +import { useIssueDetail, useIssues, useProjectState } from "@/hooks/store"; +import { useIssueStoreType } from "@/hooks/use-issue-layout-store"; import useIssuePeekOverviewRedirection from "@/hooks/use-issue-peek-overview-redirection"; import { usePlatformOS } from "@/hooks/use-platform-os"; // plane web components import { IssueIdentifier } from "@/plane-web/components/issues"; +// local types +import { GanttStoreType } from "./base-gantt-root"; type Props = { issueId: string; @@ -80,6 +83,8 @@ export const IssueGanttSidebarBlock: React.FC = observer((props) => { issue: { getIssueById }, } = useIssueDetail(); const { isMobile } = usePlatformOS(); + const storeType = useIssueStoreType() as GanttStoreType; + const { issuesFilter } = useIssues(storeType); // handlers const { handleRedirection } = useIssuePeekOverviewRedirection(); @@ -102,6 +107,7 @@ export const IssueGanttSidebarBlock: React.FC = observer((props) => { issueId={issueDetails.id} projectId={issueDetails.project_id} textContainerClassName="text-xs text-custom-text-300" + displayProperties={issuesFilter?.issueFilters?.displayProperties} /> )} diff --git a/web/core/components/issues/issue-layouts/kanban/block.tsx b/web/core/components/issues/issue-layouts/kanban/block.tsx index 57ae78f63a2..72b70a81f7b 100644 --- a/web/core/components/issues/issue-layouts/kanban/block.tsx +++ b/web/core/components/issues/issue-layouts/kanban/block.tsx @@ -24,7 +24,6 @@ import { IssueIdentifier } from "@/plane-web/components/issues"; // local components import { TRenderQuickActions } from "../list/list-view-types"; import { IssueProperties } from "../properties/all-properties"; -import { WithDisplayPropertiesHOC } from "../properties/with-display-properties-HOC"; import { getIssueBlockId } from "../utils"; interface IssueBlockProps { @@ -62,28 +61,27 @@ const KanbanIssueDetailsBlock: React.FC = observer((prop return ( <> - -
- {issue.project_id && ( - - )} -
- {quickActions({ - issue, - parentRef: cardRef, - })} -
+
+ {issue.project_id && ( + + )} +
+ {quickActions({ + issue, + parentRef: cardRef, + })}
- +
diff --git a/web/core/components/issues/issue-layouts/list/block.tsx b/web/core/components/issues/issue-layouts/list/block.tsx index a6233a36ea8..e11d261de97 100644 --- a/web/core/components/issues/issue-layouts/list/block.tsx +++ b/web/core/components/issues/issue-layouts/list/block.tsx @@ -126,7 +126,7 @@ export const IssueBlock = observer((props: IssueBlockProps) => { }; //TODO: add better logic. This is to have a min width for ID/Key based on the length of project identifier - const keyMinWidth = (projectIdentifier?.length ?? 0) * 7; + const keyMinWidth = displayProperties?.key ? (projectIdentifier?.length ?? 0) * 7 : 0; return ( {
)} - {displayProperties && displayProperties?.key && ( + {displayProperties && (displayProperties.key || displayProperties.issue_type) && (
{issue.project_id && ( )}
diff --git a/web/core/components/issues/issue-layouts/spreadsheet/issue-row.tsx b/web/core/components/issues/issue-layouts/spreadsheet/issue-row.tsx index 7733514ab56..ee59ab861fd 100644 --- a/web/core/components/issues/issue-layouts/spreadsheet/issue-row.tsx +++ b/web/core/components/issues/issue-layouts/spreadsheet/issue-row.tsx @@ -25,7 +25,6 @@ import { usePlatformOS } from "@/hooks/use-platform-os"; import { IssueIdentifier } from "@/plane-web/components/issues"; // local components import { TRenderQuickActions } from "../list/list-view-types"; -import { WithDisplayPropertiesHOC } from "../properties/with-display-properties-HOC"; import { IssueColumn } from "./issue-column"; interface Props { @@ -222,7 +221,9 @@ const IssueRowDetails = observer((props: IssueRowDetailsProps) => { const canSelectIssues = !disableUserActions && !selectionHelpers.isSelectionDisabled; //TODO: add better logic. This is to have a min width for ID/Key based on the length of project identifier - const keyMinWidth = (getProjectIdentifierById(issueDetail.project_id)?.length ?? 0 + 5) * 7; + const keyMinWidth = displayProperties?.key + ? (getProjectIdentifierById(issueDetail.project_id)?.length ?? 0 + 5) * 7 + : 0; return ( <> @@ -280,7 +281,7 @@ const IssueRowDetails = observer((props: IssueRowDetailsProps) => { {/* sub issues indentation */} {nestingLevel !== 0 &&
} - + {(displayProperties?.key || displayProperties?.issue_type) && (

{issueDetail.project_id && ( @@ -288,11 +289,12 @@ const IssueRowDetails = observer((props: IssueRowDetailsProps) => { issueId={issueDetail.id} projectId={issueDetail.project_id} textContainerClassName="text-sm md:text-xs text-custom-text-300" + displayProperties={displayProperties} /> )}

-
+ )} {/* sub-issues chevron */}
diff --git a/web/core/constants/issue.ts b/web/core/constants/issue.ts index 6b8c400480b..f9a82056a66 100644 --- a/web/core/constants/issue.ts +++ b/web/core/constants/issue.ts @@ -108,14 +108,34 @@ export const ISSUE_FILTER_OPTIONS: { // { key: "draft", title: "Draft Issues" }, ]; +export const ISSUE_DISPLAY_PROPERTIES_KEYS: (keyof IIssueDisplayProperties)[] = [ + "assignee", + "start_date", + "due_date", + "labels", + "key", + "priority", + "state", + "sub_issue_count", + "link", + "attachment_count", + "estimate", + "created_on", + "updated_on", + "modules", + "cycle", + "issue_type", +]; + export const ISSUE_DISPLAY_PROPERTIES: { key: keyof IIssueDisplayProperties; title: string; }[] = [ + { key: "key", title: "ID" }, + { key: "issue_type", title: "Issue Type" }, { key: "assignee", title: "Assignee" }, { key: "start_date", title: "Start date" }, { key: "due_date", title: "Due date" }, - { key: "key", title: "ID" }, { key: "labels", title: "Labels" }, { key: "priority", title: "Priority" }, { key: "state", title: "State" }, @@ -166,7 +186,7 @@ export const ISSUE_LAYOUTS: { export interface ILayoutDisplayFiltersOptions { filters: (keyof IIssueFilterOptions)[]; - display_properties: boolean; + display_properties: (keyof IIssueDisplayProperties)[]; display_filters: { group_by?: TIssueGroupByOptions[]; sub_group_by?: TIssueGroupByOptions[]; @@ -185,7 +205,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { profile_issues: { list: { filters: ["priority", "state_group", "labels", "start_date", "target_date"], - display_properties: true, + display_properties: ISSUE_DISPLAY_PROPERTIES_KEYS, display_filters: { group_by: ["state_detail.group", "priority", "project", "labels", null], order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "priority"], @@ -198,7 +218,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { }, kanban: { filters: ["priority", "state_group", "labels", "start_date", "target_date"], - display_properties: true, + display_properties: ISSUE_DISPLAY_PROPERTIES_KEYS, display_filters: { group_by: ["state_detail.group", "priority", "project", "labels"], order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "priority"], @@ -224,7 +244,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { "target_date", "issue_type", ], - display_properties: true, + display_properties: ISSUE_DISPLAY_PROPERTIES_KEYS, display_filters: { group_by: [ "state", @@ -249,7 +269,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { draft_issues: { list: { filters: ["priority", "state_group", "cycle", "module", "labels", "start_date", "target_date", "issue_type"], - display_properties: true, + display_properties: ISSUE_DISPLAY_PROPERTIES_KEYS, display_filters: { group_by: ["state_detail.group", "cycle", "module", "priority", "project", "labels", null], order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "priority"], @@ -262,7 +282,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { }, kanban: { filters: ["priority", "state_group", "cycle", "module", "labels", "start_date", "target_date", "issue_type"], - display_properties: true, + display_properties: ISSUE_DISPLAY_PROPERTIES_KEYS, display_filters: { group_by: ["state_detail.group", "cycle", "module", "priority", "project", "labels"], order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "priority"], @@ -287,7 +307,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { "start_date", "target_date", ], - display_properties: true, + display_properties: ISSUE_DISPLAY_PROPERTIES_KEYS, display_filters: { order_by: [], type: [null, "active", "backlog"], @@ -309,7 +329,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { "start_date", "target_date", ], - display_properties: true, + display_properties: ISSUE_DISPLAY_PROPERTIES_KEYS, display_filters: { type: [null, "active", "backlog"], }, @@ -334,7 +354,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { "target_date", "issue_type", ], - display_properties: true, + display_properties: ISSUE_DISPLAY_PROPERTIES_KEYS, display_filters: { group_by: ["state", "priority", "cycle", "module", "labels", "assignees", "created_by", null], order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "priority"], @@ -359,7 +379,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { "target_date", "issue_type", ], - display_properties: true, + display_properties: ISSUE_DISPLAY_PROPERTIES_KEYS, display_filters: { group_by: ["state", "priority", "cycle", "module", "labels", "assignees", "created_by"], sub_group_by: ["state", "priority", "cycle", "module", "labels", "assignees", "created_by", null], @@ -384,7 +404,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { "start_date", "issue_type", ], - display_properties: false, + display_properties: ["key", "issue_type"], display_filters: { type: [null, "active", "backlog"], }, @@ -407,7 +427,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { "target_date", "issue_type", ], - display_properties: true, + display_properties: ISSUE_DISPLAY_PROPERTIES_KEYS, display_filters: { order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "priority"], type: [null, "active", "backlog"], @@ -431,7 +451,7 @@ export const ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { "target_date", "issue_type", ], - display_properties: false, + display_properties: ["key", "issue_type"], display_filters: { order_by: ["sort_order", "-created_at", "-updated_at", "start_date", "priority"], type: [null, "active", "backlog"], diff --git a/web/ee/helpers/issue-filter.helper.ts b/web/ee/helpers/issue-filter.helper.ts new file mode 100644 index 00000000000..378b87e72de --- /dev/null +++ b/web/ee/helpers/issue-filter.helper.ts @@ -0,0 +1 @@ +export * from "ce/helpers/issue-filter.helper"; diff --git a/web/helpers/issue.helper.ts b/web/helpers/issue.helper.ts index 7482ca75e4c..106381a2d8a 100644 --- a/web/helpers/issue.helper.ts +++ b/web/helpers/issue.helper.ts @@ -305,4 +305,5 @@ export const getComputedDisplayProperties = ( updated_on: displayProperties?.updated_on ?? true, modules: displayProperties?.modules ?? true, cycle: displayProperties?.cycle ?? true, + issue_type: displayProperties?.issue_type ?? true, });