mirror of
https://github.com/makeplane/plane
synced 2025-08-07 19:59:33 +00:00
[WEB-4365] fix: dates display properties toggle #7262
This commit is contained in:
@@ -1,12 +1,21 @@
|
|||||||
// plane imports
|
// plane imports
|
||||||
import { SyntheticEvent } from "react";
|
import { SyntheticEvent, useMemo } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
|
import { CalendarCheck2, CalendarClock } from "lucide-react";
|
||||||
|
import { useTranslation } from "@plane/i18n";
|
||||||
import { IIssueDisplayProperties, TIssue } from "@plane/types";
|
import { IIssueDisplayProperties, TIssue } from "@plane/types";
|
||||||
import { getDate, renderFormattedPayloadDate } from "@plane/utils";
|
import { getDate, renderFormattedPayloadDate, shouldHighlightIssueDueDate } from "@plane/utils";
|
||||||
// components
|
// components
|
||||||
import { PriorityDropdown, MemberDropdown, StateDropdown, DateRangeDropdown } from "@/components/dropdowns";
|
import {
|
||||||
|
PriorityDropdown,
|
||||||
|
MemberDropdown,
|
||||||
|
StateDropdown,
|
||||||
|
DateRangeDropdown,
|
||||||
|
DateDropdown,
|
||||||
|
} from "@/components/dropdowns";
|
||||||
// hooks
|
// hooks
|
||||||
import { WithDisplayPropertiesHOC } from "@/components/issues/issue-layouts/properties/with-display-properties-HOC";
|
import { WithDisplayPropertiesHOC } from "@/components/issues/issue-layouts/properties/with-display-properties-HOC";
|
||||||
|
import { useProjectState } from "@/hooks/store/use-project-state";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
@@ -27,6 +36,8 @@ type Props = {
|
|||||||
|
|
||||||
export const SubIssuesListItemProperties: React.FC<Props> = observer((props) => {
|
export const SubIssuesListItemProperties: React.FC<Props> = observer((props) => {
|
||||||
const { workspaceSlug, parentIssueId, issueId, disabled, updateSubIssue, displayProperties, issue } = props;
|
const { workspaceSlug, parentIssueId, issueId, disabled, updateSubIssue, displayProperties, issue } = props;
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { getStateById } = useProjectState();
|
||||||
|
|
||||||
const handleEventPropagation = (e: SyntheticEvent<HTMLDivElement>) => {
|
const handleEventPropagation = (e: SyntheticEvent<HTMLDivElement>) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@@ -49,10 +60,22 @@ export const SubIssuesListItemProperties: React.FC<Props> = observer((props) =>
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//derived values
|
||||||
|
const stateDetails = useMemo(() => getStateById(issue.state_id), [getStateById, issue.state_id]);
|
||||||
|
const shouldHighlight = useMemo(
|
||||||
|
() => shouldHighlightIssueDueDate(issue.target_date, stateDetails?.group),
|
||||||
|
[issue.target_date, stateDetails?.group]
|
||||||
|
);
|
||||||
|
// date range is enabled only when both dates are available and both dates are enabled
|
||||||
|
const isDateRangeEnabled: boolean = Boolean(
|
||||||
|
issue.start_date && issue.target_date && displayProperties?.start_date && displayProperties?.due_date
|
||||||
|
);
|
||||||
|
|
||||||
if (!displayProperties) return <></>;
|
if (!displayProperties) return <></>;
|
||||||
|
|
||||||
const maxDate = getDate(issue.target_date);
|
const maxDate = getDate(issue.target_date);
|
||||||
maxDate?.setDate(maxDate.getDate());
|
const minDate = getDate(issue.start_date);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex items-center gap-2">
|
<div className="relative flex items-center gap-2">
|
||||||
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="state">
|
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="state">
|
||||||
@@ -104,7 +127,7 @@ export const SubIssuesListItemProperties: React.FC<Props> = observer((props) =>
|
|||||||
<WithDisplayPropertiesHOC
|
<WithDisplayPropertiesHOC
|
||||||
displayProperties={displayProperties}
|
displayProperties={displayProperties}
|
||||||
displayPropertyKey={["start_date", "due_date"]}
|
displayPropertyKey={["start_date", "due_date"]}
|
||||||
shouldRenderProperty={(properties) => !!(properties.start_date || properties.due_date)}
|
shouldRenderProperty={() => isDateRangeEnabled}
|
||||||
>
|
>
|
||||||
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||||
<DateRangeDropdown
|
<DateRangeDropdown
|
||||||
@@ -122,6 +145,7 @@ export const SubIssuesListItemProperties: React.FC<Props> = observer((props) =>
|
|||||||
isClearable
|
isClearable
|
||||||
mergeDates
|
mergeDates
|
||||||
buttonVariant={issue.start_date || issue.target_date ? "border-with-text" : "border-without-text"}
|
buttonVariant={issue.start_date || issue.target_date ? "border-with-text" : "border-without-text"}
|
||||||
|
buttonClassName={shouldHighlight ? "text-red-500" : ""}
|
||||||
disabled={!disabled}
|
disabled={!disabled}
|
||||||
showTooltip
|
showTooltip
|
||||||
customTooltipHeading="Date Range"
|
customTooltipHeading="Date Range"
|
||||||
@@ -130,6 +154,50 @@ export const SubIssuesListItemProperties: React.FC<Props> = observer((props) =>
|
|||||||
</div>
|
</div>
|
||||||
</WithDisplayPropertiesHOC>
|
</WithDisplayPropertiesHOC>
|
||||||
|
|
||||||
|
{/* start date */}
|
||||||
|
<WithDisplayPropertiesHOC
|
||||||
|
displayProperties={displayProperties}
|
||||||
|
displayPropertyKey="start_date"
|
||||||
|
shouldRenderProperty={() => !isDateRangeEnabled}
|
||||||
|
>
|
||||||
|
<div className="h-5">
|
||||||
|
<DateDropdown
|
||||||
|
value={issue.start_date ?? null}
|
||||||
|
onChange={handleStartDate}
|
||||||
|
maxDate={maxDate}
|
||||||
|
placeholder={t("common.order_by.start_date")}
|
||||||
|
icon={<CalendarClock className="h-3 w-3 flex-shrink-0" />}
|
||||||
|
buttonVariant={issue.start_date ? "border-with-text" : "border-without-text"}
|
||||||
|
optionsClassName="z-30"
|
||||||
|
disabled={!disabled}
|
||||||
|
showTooltip
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</WithDisplayPropertiesHOC>
|
||||||
|
|
||||||
|
{/* target/due date */}
|
||||||
|
<WithDisplayPropertiesHOC
|
||||||
|
displayProperties={displayProperties}
|
||||||
|
displayPropertyKey="due_date"
|
||||||
|
shouldRenderProperty={() => !isDateRangeEnabled}
|
||||||
|
>
|
||||||
|
<div className="h-5">
|
||||||
|
<DateDropdown
|
||||||
|
value={issue?.target_date ?? null}
|
||||||
|
onChange={handleTargetDate}
|
||||||
|
minDate={minDate}
|
||||||
|
placeholder={t("common.order_by.due_date")}
|
||||||
|
icon={<CalendarCheck2 className="h-3 w-3 flex-shrink-0" />}
|
||||||
|
buttonVariant={issue.target_date ? "border-with-text" : "border-without-text"}
|
||||||
|
buttonClassName={shouldHighlight ? "text-red-500" : ""}
|
||||||
|
clearIconClassName="text-custom-text-100"
|
||||||
|
optionsClassName="z-30"
|
||||||
|
disabled={!disabled}
|
||||||
|
showTooltip
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</WithDisplayPropertiesHOC>
|
||||||
|
|
||||||
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="assignee">
|
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="assignee">
|
||||||
<div className="h-5 flex-shrink-0">
|
<div className="h-5 flex-shrink-0">
|
||||||
<MemberDropdown
|
<MemberDropdown
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import xor from "lodash/xor";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useParams, usePathname } from "next/navigation";
|
import { useParams, usePathname } from "next/navigation";
|
||||||
// icons
|
// icons
|
||||||
import { Layers, Link, Paperclip } from "lucide-react";
|
import { CalendarCheck2, CalendarClock, Layers, Link, Paperclip } from "lucide-react";
|
||||||
// types
|
// types
|
||||||
import { WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
import { WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
||||||
// i18n
|
// i18n
|
||||||
@@ -29,6 +29,7 @@ import {
|
|||||||
CycleDropdown,
|
CycleDropdown,
|
||||||
StateDropdown,
|
StateDropdown,
|
||||||
DateRangeDropdown,
|
DateRangeDropdown,
|
||||||
|
DateDropdown,
|
||||||
} from "@/components/dropdowns";
|
} from "@/components/dropdowns";
|
||||||
// constants
|
// constants
|
||||||
// helpers
|
// helpers
|
||||||
@@ -267,8 +268,16 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
|||||||
|
|
||||||
if (!displayProperties || !issue.project_id) return null;
|
if (!displayProperties || !issue.project_id) return null;
|
||||||
|
|
||||||
|
// date range is enabled only when both dates are available and both dates are enabled
|
||||||
|
const isDateRangeEnabled: boolean = Boolean(
|
||||||
|
issue.start_date && issue.target_date && displayProperties.start_date && displayProperties.due_date
|
||||||
|
);
|
||||||
|
|
||||||
const defaultLabelOptions = issue?.label_ids?.map((id) => labelMap[id]) || [];
|
const defaultLabelOptions = issue?.label_ids?.map((id) => labelMap[id]) || [];
|
||||||
|
|
||||||
|
const minDate = getDate(issue.start_date);
|
||||||
|
const maxDate = getDate(issue.target_date);
|
||||||
|
|
||||||
const handleEventPropagation = (e: SyntheticEvent<HTMLDivElement>) => {
|
const handleEventPropagation = (e: SyntheticEvent<HTMLDivElement>) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -312,7 +321,7 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
|||||||
<WithDisplayPropertiesHOC
|
<WithDisplayPropertiesHOC
|
||||||
displayProperties={displayProperties}
|
displayProperties={displayProperties}
|
||||||
displayPropertyKey={["start_date", "due_date"]}
|
displayPropertyKey={["start_date", "due_date"]}
|
||||||
shouldRenderProperty={(properties) => !!(properties.start_date || properties.due_date)}
|
shouldRenderProperty={() => isDateRangeEnabled}
|
||||||
>
|
>
|
||||||
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||||
<DateRangeDropdown
|
<DateRangeDropdown
|
||||||
@@ -331,6 +340,7 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
|||||||
mergeDates
|
mergeDates
|
||||||
buttonVariant={issue.start_date || issue.target_date ? "border-with-text" : "border-without-text"}
|
buttonVariant={issue.start_date || issue.target_date ? "border-with-text" : "border-without-text"}
|
||||||
buttonClassName={shouldHighlightIssueDueDate(issue.target_date, stateDetails?.group) ? "text-red-500" : ""}
|
buttonClassName={shouldHighlightIssueDueDate(issue.target_date, stateDetails?.group) ? "text-red-500" : ""}
|
||||||
|
clearIconClassName="!text-custom-text-100"
|
||||||
disabled={isReadOnly}
|
disabled={isReadOnly}
|
||||||
renderByDefault={isMobile}
|
renderByDefault={isMobile}
|
||||||
showTooltip
|
showTooltip
|
||||||
@@ -340,6 +350,52 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
|||||||
</div>
|
</div>
|
||||||
</WithDisplayPropertiesHOC>
|
</WithDisplayPropertiesHOC>
|
||||||
|
|
||||||
|
{/* start date */}
|
||||||
|
<WithDisplayPropertiesHOC
|
||||||
|
displayProperties={displayProperties}
|
||||||
|
displayPropertyKey="start_date"
|
||||||
|
shouldRenderProperty={() => !isDateRangeEnabled}
|
||||||
|
>
|
||||||
|
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||||
|
<DateDropdown
|
||||||
|
value={issue.start_date ?? null}
|
||||||
|
onChange={handleStartDate}
|
||||||
|
maxDate={maxDate}
|
||||||
|
placeholder={t("common.order_by.start_date")}
|
||||||
|
icon={<CalendarClock className="h-3 w-3 flex-shrink-0" />}
|
||||||
|
buttonVariant={issue.start_date ? "border-with-text" : "border-without-text"}
|
||||||
|
optionsClassName="z-10"
|
||||||
|
disabled={isReadOnly}
|
||||||
|
renderByDefault={isMobile}
|
||||||
|
showTooltip
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</WithDisplayPropertiesHOC>
|
||||||
|
|
||||||
|
{/* target/due date */}
|
||||||
|
<WithDisplayPropertiesHOC
|
||||||
|
displayProperties={displayProperties}
|
||||||
|
displayPropertyKey="due_date"
|
||||||
|
shouldRenderProperty={() => !isDateRangeEnabled}
|
||||||
|
>
|
||||||
|
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||||
|
<DateDropdown
|
||||||
|
value={issue?.target_date ?? null}
|
||||||
|
onChange={handleTargetDate}
|
||||||
|
minDate={minDate}
|
||||||
|
placeholder={t("common.order_by.due_date")}
|
||||||
|
icon={<CalendarCheck2 className="h-3 w-3 flex-shrink-0" />}
|
||||||
|
buttonVariant={issue.target_date ? "border-with-text" : "border-without-text"}
|
||||||
|
buttonClassName={shouldHighlightIssueDueDate(issue.target_date, stateDetails?.group) ? "text-red-500" : ""}
|
||||||
|
clearIconClassName="!text-custom-text-100"
|
||||||
|
optionsClassName="z-10"
|
||||||
|
disabled={isReadOnly}
|
||||||
|
renderByDefault={isMobile}
|
||||||
|
showTooltip
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</WithDisplayPropertiesHOC>
|
||||||
|
|
||||||
{/* assignee */}
|
{/* assignee */}
|
||||||
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="assignee">
|
<WithDisplayPropertiesHOC displayProperties={displayProperties} displayPropertyKey="assignee">
|
||||||
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
<div className="h-5" onFocus={handleEventPropagation} onClick={handleEventPropagation}>
|
||||||
|
|||||||
Reference in New Issue
Block a user