[WEB-2421] chore: issue display properties and issue identifier improvements. (#5577)

* [WEB-2421] chore: issue display properties and issue identifier improvements.

* chore: remove yarn.lock changes.
This commit is contained in:
Prateek Shourya 2024-09-10 21:49:57 +05:30 committed by GitHub
parent 71f3c5c12a
commit 00b76300f5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 138 additions and 57 deletions

View file

@ -127,6 +127,7 @@ export interface IIssueDisplayProperties {
updated_on?: boolean;
modules?: boolean;
cycle?: boolean;
issue_type?: boolean;
}
export type TIssueKanbanFilters = {

View file

@ -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<TIssueIdentifierProps> = 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<TIssueIdentifierProps> = 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 (
<div className="flex items-center space-x-2">

View file

@ -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;
}
};

View file

@ -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

View file

@ -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}
/>
)}
<Tooltip tooltipContent={issue.name} isMobile={isMobile}>

View file

@ -50,10 +50,11 @@ export const DisplayFiltersSelection: React.FC<Props> = observer((props) => {
return (
<div className="vertical-scrollbar scrollbar-sm relative h-full w-full divide-y divide-custom-border-200 overflow-hidden overflow-y-auto px-2.5">
{/* display properties */}
{layoutDisplayFiltersOptions?.display_properties && (
{layoutDisplayFiltersOptions?.display_properties && layoutDisplayFiltersOptions.display_properties.length > 0 && (
<div className="py-2">
<FilterDisplayProperties
displayProperties={displayProperties}
displayPropertiesToRender={layoutDisplayFiltersOptions.display_properties}
handleUpdate={handleDisplayPropertiesUpdate}
cycleViewDisabled={cycleViewDisabled}
moduleViewDisabled={moduleViewDisabled}

View file

@ -1,29 +1,50 @@
import React from "react";
import { observer } from "mobx-react";
import { IIssueDisplayProperties } from "@plane/types";
// components
import { ISSUE_DISPLAY_PROPERTIES } from "@/constants/issue";
import { FilterHeader } from "../helpers/filter-header";
import { useParams } from "next/navigation";
// types
import { IIssueDisplayProperties } from "@plane/types";
// constants
import { ISSUE_DISPLAY_PROPERTIES } from "@/constants/issue";
// plane web helpers
import { shouldRenderDisplayProperty } from "@/plane-web/helpers/issue-filter.helper";
// components
import { FilterHeader } from "../helpers/filter-header";
type Props = {
displayProperties: IIssueDisplayProperties;
displayPropertiesToRender: (keyof IIssueDisplayProperties)[];
handleUpdate: (updatedDisplayProperties: Partial<IIssueDisplayProperties>) => void;
cycleViewDisabled?: boolean;
moduleViewDisabled?: boolean;
};
export const FilterDisplayProperties: React.FC<Props> = 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 (

View file

@ -28,7 +28,7 @@ interface IBaseGanttRoot {
isCompletedCycle?: boolean;
}
type GanttStoreType =
export type GanttStoreType =
| EIssuesStoreType.PROJECT
| EIssuesStoreType.MODULE
| EIssuesStoreType.CYCLE

View file

@ -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<Props> = 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<Props> = observer((props) => {
issueId={issueDetails.id}
projectId={issueDetails.project_id}
textContainerClassName="text-xs text-custom-text-300"
displayProperties={issuesFilter?.issueFilters?.displayProperties}
/>
)}
<Tooltip tooltipContent={issueDetails?.name} isMobile={isMobile}>

View file

@ -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<IssueDetailsBlockProps> = observer((prop
return (
<>
<WithDisplayPropertiesHOC displayProperties={displayProperties || {}} displayPropertyKey="key">
<div className="relative">
{issue.project_id && (
<IssueIdentifier
issueId={issue.id}
projectId={issue.project_id}
textContainerClassName="line-clamp-1 text-xs text-custom-text-300"
/>
)}
<div
className={cn("absolute -top-1 right-0", {
"hidden group-hover/kanban-block:block": !isMobile,
})}
onClick={handleEventPropagation}
>
{quickActions({
issue,
parentRef: cardRef,
})}
</div>
<div className="relative">
{issue.project_id && (
<IssueIdentifier
issueId={issue.id}
projectId={issue.project_id}
textContainerClassName="line-clamp-1 text-xs text-custom-text-300"
displayProperties={displayProperties}
/>
)}
<div
className={cn("absolute -top-1 right-0", {
"hidden group-hover/kanban-block:block": !isMobile,
})}
onClick={handleEventPropagation}
>
{quickActions({
issue,
parentRef: cardRef,
})}
</div>
</WithDisplayPropertiesHOC>
</div>
<Tooltip tooltipContent={issue.name} isMobile={isMobile} renderByDefault={false}>
<div className="w-full line-clamp-1 text-sm text-custom-text-100">

View file

@ -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 (
<Row
@ -184,13 +184,14 @@ export const IssueBlock = observer((props: IssueBlockProps) => {
</div>
</Tooltip>
)}
{displayProperties && displayProperties?.key && (
{displayProperties && (displayProperties.key || displayProperties.issue_type) && (
<div className="flex-shrink-0" style={{ minWidth: `${keyMinWidth}px` }}>
{issue.project_id && (
<IssueIdentifier
issueId={issueId}
projectId={issue.project_id}
textContainerClassName="text-xs font-medium text-custom-text-300"
displayProperties={displayProperties}
/>
)}
</div>

View file

@ -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 && <div style={{ width: subIssueIndentation }} />}
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="key">
{(displayProperties?.key || displayProperties?.issue_type) && (
<div className="relative flex cursor-pointer items-center text-center text-xs hover:text-custom-text-100">
<p className={`flex font-medium leading-7`} style={{ minWidth: `${keyMinWidth}px` }}>
{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}
/>
)}
</p>
</div>
</WithDisplayPropertiesHOC>
)}
{/* sub-issues chevron */}
<div className="grid place-items-center size-4">

View file

@ -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"],

View file

@ -0,0 +1 @@
export * from "ce/helpers/issue-filter.helper";

View file

@ -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,
});