fix: date range picker on cycles and modules list (#6676)

* fix: Handled workspace switcher closing on click

* fix: replaced date range picker with date picker at some places
This commit is contained in:
Akshita Goyal 2025-02-25 21:21:02 +05:30 committed by GitHub
parent fbbf58481d
commit 59a0925d34
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 299 additions and 179 deletions

View file

@ -3,7 +3,16 @@
import React, { FC, useEffect, useState } from "react"; import React, { FC, useEffect, useState } from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { Controller, useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import { ArchiveIcon, ArchiveRestoreIcon, ChevronRight, EllipsisIcon, LinkIcon, Trash2 } from "lucide-react"; import {
ArchiveIcon,
ArchiveRestoreIcon,
CalendarCheck2,
CalendarClock,
ChevronRight,
EllipsisIcon,
LinkIcon,
Trash2,
} from "lucide-react";
// types // types
import { CYCLE_STATUS, CYCLE_UPDATED, EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; import { CYCLE_STATUS, CYCLE_UPDATED, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
import { useTranslation } from "@plane/i18n"; import { useTranslation } from "@plane/i18n";
@ -11,7 +20,7 @@ import { ICycle } from "@plane/types";
// ui // ui
import { CustomMenu, setToast, TOAST_TYPE } from "@plane/ui"; import { CustomMenu, setToast, TOAST_TYPE } from "@plane/ui";
// components // components
import { DateRangeDropdown } from "@/components/dropdowns"; import { DateDropdown } from "@/components/dropdowns";
// helpers // helpers
import { renderFormattedPayloadDate, getDate } from "@/helpers/date-time.helper"; import { renderFormattedPayloadDate, getDate } from "@/helpers/date-time.helper";
import { copyUrlToClipboard } from "@/helpers/string.helper"; import { copyUrlToClipboard } from "@/helpers/string.helper";
@ -54,7 +63,7 @@ export const CycleSidebarHeader: FC<Props> = observer((props) => {
const { t } = useTranslation(); const { t } = useTranslation();
// form info // form info
const { control, reset } = useForm({ const { control, reset, getValues } = useForm({
defaultValues, defaultValues,
}); });
@ -145,20 +154,13 @@ export const CycleSidebarHeader: FC<Props> = observer((props) => {
} }
}; };
const handleDateChange = async (startDate: Date | undefined, endDate: Date | undefined) => { const handleDateChange = async (payload: { start_date?: string | null; end_date?: string | null }) => {
if (!startDate || !endDate) return;
let isDateValid = false; let isDateValid = false;
const payload = { if (cycleDetails?.start_date && cycleDetails?.end_date)
start_date: renderFormattedPayloadDate(startDate),
end_date: renderFormattedPayloadDate(endDate),
};
if (cycleDetails?.start_date && cycleDetails.end_date)
isDateValid = await dateChecker({ isDateValid = await dateChecker({
...payload, ...payload,
cycle_id: cycleDetails.id, cycle_id: cycleDetails?.id,
}); });
else isDateValid = await dateChecker(payload); else isDateValid = await dateChecker(payload);
@ -177,6 +179,7 @@ export const CycleSidebarHeader: FC<Props> = observer((props) => {
}); });
reset({ ...cycleDetails }); reset({ ...cycleDetails });
} }
return isDateValid;
}; };
const isEditingAllowed = allowPermissions( const isEditingAllowed = allowPermissions(
@ -285,39 +288,79 @@ export const CycleSidebarHeader: FC<Props> = observer((props) => {
</span> </span>
)} )}
</div> </div>
<div className="flex items-center gap-2">
<Controller
name="start_date"
control={control}
rules={{ required: "Please select a date" }}
render={({ field: { value, onChange } }) => (
<DateDropdown
value={value ?? null}
onChange={async (val) => {
let isDateValid;
const valDate = val ? renderFormattedPayloadDate(val) : null;
if (getValues("end_date")) {
isDateValid = await handleDateChange({
start_date: valDate,
end_date: renderFormattedPayloadDate(getValues("end_date")),
});
} else {
isDateValid = await handleDateChange({
start_date: valDate,
end_date: valDate,
});
}
isDateValid && onChange(renderFormattedPayloadDate(val));
}}
placeholder={t("common.order_by.start_date")}
icon={<CalendarClock className="h-3 w-3 flex-shrink-0" />}
buttonVariant={value ? "border-with-text" : "border-without-text"}
buttonContainerClassName={`h-6 w-full flex ${!isEditingAllowed || isArchived || isCompleted ? "cursor-not-allowed" : "cursor-pointer"} items-center gap-1.5 text-custom-text-300 rounded text-xs`}
optionsClassName="z-10"
disabled={!isEditingAllowed || isArchived || isCompleted}
showTooltip
maxDate={getDate(getValues("end_date"))}
isClearable={false}
/>
)}
/>
<Controller <Controller
control={control} name="end_date"
name="start_date" control={control}
render={({ field: { value: startDateValue, onChange: onChangeStartDate } }) => ( rules={{ required: "Please select a date" }}
<Controller render={({ field: { value, onChange } }) => (
control={control} <DateDropdown
name="end_date" value={getDate(value) ?? null}
render={({ field: { value: endDateValue, onChange: onChangeEndDate } }) => ( onChange={async (val) => {
<DateRangeDropdown let isDateValid;
className="h-7" const valDate = val ? renderFormattedPayloadDate(val) : null;
buttonVariant="transparent-with-text" if (getValues("start_date")) {
minDate={new Date()} isDateValid = await handleDateChange({
value={{ end_date: valDate,
from: getDate(startDateValue), start_date: renderFormattedPayloadDate(getValues("start_date")),
to: getDate(endDateValue), });
}} } else {
onSelect={(val) => { isDateValid = await handleDateChange({
onChangeStartDate(val?.from ? renderFormattedPayloadDate(val.from) : null); end_date: valDate,
onChangeEndDate(val?.to ? renderFormattedPayloadDate(val.to) : null); start_date: valDate,
handleDateChange(val?.from, val?.to); });
}} }
placeholder={{ isDateValid && onChange(renderFormattedPayloadDate(val));
from: "Start date", }}
to: "End date", placeholder={t("common.order_by.due_date")}
}} icon={<CalendarCheck2 className="h-3 w-3 flex-shrink-0" />}
required={cycleDetails.status !== "draft"} buttonVariant={value ? "border-with-text" : "border-without-text"}
disabled={!isEditingAllowed || isArchived || isCompleted} buttonContainerClassName={`h-6 w-full flex ${!isEditingAllowed || isArchived || isCompleted ? "cursor-not-allowed" : "cursor-pointer"} items-center gap-1.5 text-custom-text-300 rounded text-xs`}
/> optionsClassName="z-10"
)} disabled={!isEditingAllowed || isArchived || isCompleted}
/> showTooltip
)} minDate={getDate(getValues("start_date"))}
/> isClearable={false}
/>
)}
/>
</div>
</div> </div>
</> </>
); );

View file

@ -4,7 +4,7 @@ import React, { FC, MouseEvent, useEffect, useMemo, useState } from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { useParams, usePathname, useSearchParams } from "next/navigation"; import { useParams, usePathname, useSearchParams } from "next/navigation";
import { Controller, useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import { Eye, Users } from "lucide-react"; import { CalendarCheck2, CalendarClock, Eye, Users } from "lucide-react";
// types // types
import { CYCLE_FAVORITED, CYCLE_UNFAVORITED, EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; import { CYCLE_FAVORITED, CYCLE_UNFAVORITED, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
import { useTranslation } from "@plane/i18n"; import { useTranslation } from "@plane/i18n";
@ -23,7 +23,7 @@ import {
} from "@plane/ui"; } from "@plane/ui";
// components // components
import { CycleQuickActions, TransferIssuesModal } from "@/components/cycles"; import { CycleQuickActions, TransferIssuesModal } from "@/components/cycles";
import { DateRangeDropdown } from "@/components/dropdowns"; import { DateDropdown } from "@/components/dropdowns";
import { ButtonAvatars } from "@/components/dropdowns/member/avatar"; import { ButtonAvatars } from "@/components/dropdowns/member/avatar";
// constants // constants
// helpers // helpers
@ -41,7 +41,6 @@ import { CycleAdditionalActions } from "@/plane-web/components/cycles";
import { CycleService } from "@/services/cycle.service"; import { CycleService } from "@/services/cycle.service";
const cycleService = new CycleService(); const cycleService = new CycleService();
type Props = { type Props = {
workspaceSlug: string; workspaceSlug: string;
projectId: string; projectId: string;
@ -77,7 +76,7 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
const { getUserDetails } = useMember(); const { getUserDetails } = useMember();
// form // form
const { control, reset } = useForm({ const { control, reset, getValues } = useForm({
defaultValues, defaultValues,
}); });
@ -98,7 +97,6 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
workspaceSlug, workspaceSlug,
projectId projectId
); );
const renderIcon = Boolean(cycleDetails.start_date) || Boolean(cycleDetails.end_date);
// handlers // handlers
const handleAddToFavorites = (e: MouseEvent<HTMLButtonElement>) => { const handleAddToFavorites = (e: MouseEvent<HTMLButtonElement>) => {
@ -171,20 +169,13 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
} }
}; };
const handleDateChange = async (startDate: Date | undefined, endDate: Date | undefined) => { const handleDateChange = async (payload: { start_date?: string | null; end_date?: string | null }) => {
if (!startDate || !endDate) return;
let isDateValid = false; let isDateValid = false;
const payload = { if (cycleDetails?.start_date && cycleDetails?.end_date)
start_date: renderFormattedPayloadDate(startDate),
end_date: renderFormattedPayloadDate(endDate),
};
if (cycleDetails && cycleDetails.start_date && cycleDetails.end_date)
isDateValid = await dateChecker({ isDateValid = await dateChecker({
...payload, ...payload,
cycle_id: cycleDetails.id, cycle_id: cycleDetails?.id,
}); });
else isDateValid = await dateChecker(payload); else isDateValid = await dateChecker(payload);
@ -203,6 +194,7 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
}); });
reset({ ...cycleDetails }); reset({ ...cycleDetails });
} }
return isDateValid;
}; };
const createdByDetails = cycleDetails.created_by ? getUserDetails(cycleDetails.created_by) : undefined; const createdByDetails = cycleDetails.created_by ? getUserDetails(cycleDetails.created_by) : undefined;
@ -268,35 +260,77 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
{!isActive && ( {!isActive && (
<Controller <Controller
control={control}
name="start_date" name="start_date"
render={({ field: { value: startDateValue, onChange: onChangeStartDate } }) => ( control={control}
<Controller rules={{ required: "Please select a date" }}
control={control} render={({ field: { value, onChange } }) => (
name="end_date" <DateDropdown
render={({ field: { value: endDateValue, onChange: onChangeEndDate } }) => ( value={value ?? null}
<DateRangeDropdown onChange={async (val) => {
buttonContainerClassName={`h-6 w-full flex ${isDisabled ? "cursor-not-allowed" : "cursor-pointer"} items-center gap-1.5 text-custom-text-300 border-[0.5px] border-custom-border-300 rounded text-xs`} let isDateValid;
buttonVariant="transparent-with-text" const valDate = val ? renderFormattedPayloadDate(val) : null;
minDate={new Date()} if (getValues("end_date")) {
value={{ isDateValid = await handleDateChange({
from: getDate(startDateValue), start_date: valDate,
to: getDate(endDateValue), end_date: renderFormattedPayloadDate(getValues("end_date")),
}} });
onSelect={(val) => { } else {
onChangeStartDate(val?.from ? renderFormattedPayloadDate(val.from) : null); isDateValid = await handleDateChange({
onChangeEndDate(val?.to ? renderFormattedPayloadDate(val.to) : null); start_date: valDate,
handleDateChange(val?.from, val?.to); end_date: valDate,
}} });
placeholder={{ }
from: "Start date", isDateValid && onChange(renderFormattedPayloadDate(val));
to: "End date", }}
}} placeholder={t("common.order_by.start_date")}
required={cycleDetails.status !== "draft"} icon={<CalendarClock className="h-3 w-3 flex-shrink-0" />}
disabled={isDisabled} buttonVariant={value ? "border-with-text" : "border-without-text"}
hideIcon={{ from: renderIcon ?? true, to: renderIcon }} buttonContainerClassName={`h-6 w-full flex ${isDisabled ? "cursor-not-allowed" : "cursor-pointer"} items-center gap-1.5 text-custom-text-300 rounded text-xs`}
/> optionsClassName="z-10"
)} disabled={isDisabled}
renderByDefault={isMobile}
showTooltip
maxDate={getDate(getValues("end_date"))}
isClearable={false}
/>
)}
/>
)}
{!isActive && (
<Controller
name="end_date"
control={control}
rules={{ required: "Please select a date" }}
render={({ field: { value, onChange } }) => (
<DateDropdown
value={getDate(value) ?? null}
onChange={async (val) => {
let isDateValid;
const valDate = val ? renderFormattedPayloadDate(val) : null;
if (getValues("start_date")) {
isDateValid = await handleDateChange({
end_date: valDate,
start_date: renderFormattedPayloadDate(getValues("start_date")),
});
} else {
isDateValid = await handleDateChange({
end_date: valDate,
start_date: valDate,
});
}
isDateValid && onChange(renderFormattedPayloadDate(val));
}}
placeholder={t("common.order_by.due_date")}
icon={<CalendarCheck2 className="h-3 w-3 flex-shrink-0" />}
buttonVariant={value ? "border-with-text" : "border-without-text"}
buttonContainerClassName={`h-6 w-full flex ${isDisabled ? "cursor-not-allowed" : "cursor-pointer"} items-center gap-1.5 text-custom-text-300 rounded text-xs`}
optionsClassName="z-10"
disabled={isDisabled}
renderByDefault={isMobile}
showTooltip
minDate={getDate(getValues("start_date"))}
isClearable={false}
/> />
)} )}
/> />

View file

@ -6,6 +6,7 @@ import { useParams } from "next/navigation";
import { Controller, useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import { import {
ArchiveRestoreIcon, ArchiveRestoreIcon,
CalendarCheck2,
CalendarClock, CalendarClock,
ChevronDown, ChevronDown,
ChevronRight, ChevronRight,
@ -42,7 +43,7 @@ import {
TextArea, TextArea,
} from "@plane/ui"; } from "@plane/ui";
// components // components
import { DateRangeDropdown, MemberDropdown } from "@/components/dropdowns"; import { DateDropdown, MemberDropdown } from "@/components/dropdowns";
import { import {
ArchiveModuleModal, ArchiveModuleModal,
DeleteModuleModal, DeleteModuleModal,
@ -104,7 +105,7 @@ export const ModuleAnalyticsSidebar: React.FC<Props> = observer((props) => {
const estimateType = areEstimateEnabled && currentActiveEstimateId && estimateById(currentActiveEstimateId); const estimateType = areEstimateEnabled && currentActiveEstimateId && estimateById(currentActiveEstimateId);
const isEstimatePointValid = estimateType && estimateType?.type == EEstimateSystem.POINTS ? true : false; const isEstimatePointValid = estimateType && estimateType?.type == EEstimateSystem.POINTS ? true : false;
const { reset, control } = useForm({ const { reset, control, getValues } = useForm({
defaultValues, defaultValues,
}); });
@ -194,11 +195,8 @@ export const ModuleAnalyticsSidebar: React.FC<Props> = observer((props) => {
}); });
}; };
const handleDateChange = async (startDate: Date | undefined, targetDate: Date | undefined) => { const handleDateChange = async (data: Partial<IModule>) => {
submitChanges({ submitChanges(data);
start_date: startDate ? renderFormattedPayloadDate(startDate) : null,
target_date: targetDate ? renderFormattedPayloadDate(targetDate) : null,
});
setToast({ setToast({
type: TOAST_TYPE.SUCCESS, type: TOAST_TYPE.SUCCESS,
title: "Success!", title: "Success!",
@ -411,50 +409,68 @@ export const ModuleAnalyticsSidebar: React.FC<Props> = observer((props) => {
/> />
)} )}
<div className="flex items-center justify-start gap-1">
<div className="flex w-2/5 items-center justify-start gap-2 text-custom-text-300">
<CalendarClock className="h-4 w-4" />
<span className="text-base">{t("date_range")}</span>
</div>
<div className="h-7 w-3/5">
<Controller
control={control}
name="start_date"
render={({ field: { value: startDateValue, onChange: onChangeStartDate } }) => (
<Controller
control={control}
name="target_date"
render={({ field: { value: endDateValue, onChange: onChangeEndDate } }) => {
const startDate = getDate(startDateValue);
const endDate = getDate(endDateValue);
return (
<DateRangeDropdown
buttonContainerClassName="w-full"
buttonVariant="background-with-text"
value={{
from: startDate,
to: endDate,
}}
onSelect={(val) => {
onChangeStartDate(val?.from ? renderFormattedPayloadDate(val.from) : null);
onChangeEndDate(val?.to ? renderFormattedPayloadDate(val.to) : null);
handleDateChange(val?.from, val?.to);
}}
placeholder={{
from: t("start_date"),
to: t("target_date"),
}}
disabled={!isEditingAllowed || isArchived}
/>
);
}}
/>
)}
/>
</div>
</div>
<div className="flex flex-col gap-5 pb-6 pt-2.5"> <div className="flex flex-col gap-5 pb-6 pt-2.5">
<div className="flex items-center justify-start gap-1">
<div className="flex w-2/5 items-center justify-start gap-2 text-custom-text-300">
<CalendarClock className="h-4 w-4" />
<span className="text-base">Start date</span>
</div>
<div className="flex flex-wrap items-center gap-2">
<Controller
name="start_date"
control={control}
rules={{ required: "Please select a date" }}
render={({ field: { value, onChange } }) => (
<DateDropdown
value={value ?? null}
onChange={(val) => {
console.log(val);
onChange(renderFormattedPayloadDate(val));
handleDateChange({ start_date: val ? renderFormattedPayloadDate(val) : null });
}}
placeholder={t("common.order_by.start_date")}
icon={<CalendarClock className="h-3 w-3 flex-shrink-0" />}
buttonVariant={value ? "border-with-text" : "border-without-text"}
buttonContainerClassName={`h-6 w-full flex ${!isEditingAllowed || isArchived ? "cursor-not-allowed" : "cursor-pointer"} items-center gap-1.5 text-custom-text-300 rounded text-xs`}
optionsClassName="z-30"
disabled={!isEditingAllowed || isArchived}
showTooltip
maxDate={getDate(getValues("target_date"))}
/>
)}
/>
</div>
</div>
<div className="flex items-center justify-start gap-1">
<div className="flex w-2/5 items-center justify-start gap-2 text-custom-text-300">
<CalendarClock className="h-4 w-4" />
<span className="text-base">Due date</span>
</div>
<div className="flex flex-wrap items-center gap-2">
<Controller
name="target_date"
control={control}
rules={{ required: "Please select a date" }}
render={({ field: { value, onChange } }) => (
<DateDropdown
value={getDate(value) ?? null}
onChange={(val) => {
onChange(renderFormattedPayloadDate(val));
handleDateChange({ target_date: val ? renderFormattedPayloadDate(val) : null });
}}
placeholder={t("common.order_by.due_date")}
icon={<CalendarCheck2 className="h-3 w-3 flex-shrink-0" />}
buttonVariant={value ? "border-with-text" : "border-without-text"}
buttonContainerClassName={`h-6 w-full flex ${!isEditingAllowed || isArchived ? "cursor-not-allowed" : "cursor-pointer"} items-center gap-1.5 text-custom-text-300 rounded text-xs`}
optionsClassName="z-30"
disabled={!isEditingAllowed || isArchived}
showTooltip
minDate={getDate(getValues("start_date"))}
/>
)}
/>
</div>
</div>
<div className="flex items-center justify-start gap-1"> <div className="flex items-center justify-start gap-1">
<div className="flex w-2/5 items-center justify-start gap-2 text-custom-text-300"> <div className="flex w-2/5 items-center justify-start gap-2 text-custom-text-300">
<SquareUser className="h-4 w-4" /> <SquareUser className="h-4 w-4" />

View file

@ -4,7 +4,7 @@ import React, { SyntheticEvent, useRef } from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import Link from "next/link"; import Link from "next/link";
import { useParams, usePathname, useSearchParams } from "next/navigation"; import { useParams, usePathname, useSearchParams } from "next/navigation";
import { Info, SquareUser } from "lucide-react"; import { CalendarCheck2, CalendarClock, Info, SquareUser } from "lucide-react";
// plane package imports // plane package imports
import { import {
MODULE_STATUS, MODULE_STATUS,
@ -14,6 +14,7 @@ import {
EUserPermissions, EUserPermissions,
EUserPermissionsLevel, EUserPermissionsLevel,
} from "@plane/constants"; } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { IModule } from "@plane/types"; import { IModule } from "@plane/types";
import { import {
Card, Card,
@ -26,7 +27,7 @@ import {
setToast, setToast,
} from "@plane/ui"; } from "@plane/ui";
// components // components
import { DateRangeDropdown } from "@/components/dropdowns"; import { DateDropdown } from "@/components/dropdowns";
import { ButtonAvatars } from "@/components/dropdowns/member/avatar"; import { ButtonAvatars } from "@/components/dropdowns/member/avatar";
import { ModuleQuickActions } from "@/components/modules"; import { ModuleQuickActions } from "@/components/modules";
import { ModuleStatusDropdown } from "@/components/modules/module-status-dropdown"; import { ModuleStatusDropdown } from "@/components/modules/module-status-dropdown";
@ -58,7 +59,7 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
const { getModuleById, addModuleToFavorites, removeModuleFromFavorites, updateModuleDetails } = useModule(); const { getModuleById, addModuleToFavorites, removeModuleFromFavorites, updateModuleDetails } = useModule();
const { getUserDetails } = useMember(); const { getUserDetails } = useMember();
const { captureEvent } = useEventTracker(); const { captureEvent } = useEventTracker();
const { t } = useTranslation();
// derived values // derived values
const moduleDetails = getModuleById(moduleId); const moduleDetails = getModuleById(moduleId);
const isEditingAllowed = allowPermissions( const isEditingAllowed = allowPermissions(
@ -66,7 +67,6 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
EUserPermissionsLevel.PROJECT EUserPermissionsLevel.PROJECT
); );
const isDisabled = !isEditingAllowed || !!moduleDetails?.archived_at; const isDisabled = !isEditingAllowed || !!moduleDetails?.archived_at;
const renderIcon = Boolean(moduleDetails?.start_date) || Boolean(moduleDetails?.target_date);
const { isMobile } = usePlatformOS(); const { isMobile } = usePlatformOS();
const handleAddToFavorites = (e: React.MouseEvent<HTMLButtonElement>) => { const handleAddToFavorites = (e: React.MouseEvent<HTMLButtonElement>) => {
@ -236,27 +236,38 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
)} )}
</div> </div>
<LinearProgressIndicator size="lg" data={progressIndicatorData} /> <LinearProgressIndicator size="lg" data={progressIndicatorData} />
<div className="flex items-center justify-between py-0.5" onClick={handleEventPropagation}> <div className="flex items-center gap-2 py-0.5" onClick={handleEventPropagation}>
<DateRangeDropdown <DateDropdown
buttonContainerClassName={`h-6 w-full flex ${isDisabled ? "cursor-not-allowed" : "cursor-pointer"} items-center gap-1.5 text-custom-text-300 border-[0.5px] border-custom-border-300 rounded text-xs`} value={moduleDetails.start_date}
buttonVariant="transparent-with-text" onChange={(val) => {
className="h-7"
value={{
from: getDate(moduleDetails.start_date),
to: getDate(moduleDetails.target_date),
}}
onSelect={(val) => {
handleModuleDetailsChange({ handleModuleDetailsChange({
start_date: val?.from ? renderFormattedPayloadDate(val.from) : null, start_date: val ? renderFormattedPayloadDate(val) : null,
target_date: val?.to ? renderFormattedPayloadDate(val.to) : null,
}); });
}} }}
placeholder={{ placeholder={t("common.order_by.start_date")}
from: "Start date", icon={<CalendarClock className="h-3 w-3 flex-shrink-0" />}
to: "End date", buttonVariant={moduleDetails.start_date ? "border-with-text" : "border-without-text"}
}} buttonContainerClassName={`h-6 w-full flex ${isDisabled ? "cursor-not-allowed" : "cursor-pointer"} items-center gap-1.5 text-custom-text-300 rounded text-xs`}
optionsClassName="z-10"
disabled={isDisabled} disabled={isDisabled}
hideIcon={{ from: renderIcon ?? true, to: renderIcon }} showTooltip
maxDate={getDate(moduleDetails.target_date)}
/>
<DateDropdown
value={moduleDetails.target_date}
onChange={(val) => {
handleModuleDetailsChange({
target_date: val ? renderFormattedPayloadDate(val) : null,
});
}}
placeholder={t("common.order_by.due_date")}
icon={<CalendarCheck2 className="h-3 w-3 flex-shrink-0" />}
buttonVariant={moduleDetails.target_date ? "border-with-text" : "border-without-text"}
buttonContainerClassName={`h-6 w-full flex ${isDisabled ? "cursor-not-allowed" : "cursor-pointer"} items-center gap-1.5 text-custom-text-300 rounded text-xs`}
optionsClassName="z-10"
disabled={isDisabled}
showTooltip
minDate={getDate(moduleDetails.start_date)}
/> />
</div> </div>
</div> </div>

View file

@ -4,15 +4,21 @@ import React, { FC } from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { useParams } from "next/navigation"; import { useParams } from "next/navigation";
// icons // icons
import { SquareUser } from "lucide-react"; import { CalendarCheck2, CalendarClock, SquareUser } from "lucide-react";
// types // types
import { MODULE_STATUS, MODULE_FAVORITED, MODULE_UNFAVORITED , EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; import {
MODULE_STATUS,
MODULE_FAVORITED,
MODULE_UNFAVORITED,
EUserPermissions,
EUserPermissionsLevel,
} from "@plane/constants";
import { useTranslation } from "@plane/i18n"; import { useTranslation } from "@plane/i18n";
import { IModule } from "@plane/types"; import { IModule } from "@plane/types";
// ui // ui
import { FavoriteStar, TOAST_TYPE, Tooltip, setPromiseToast, setToast } from "@plane/ui"; import { FavoriteStar, TOAST_TYPE, Tooltip, setPromiseToast, setToast } from "@plane/ui";
// components // components
import { DateRangeDropdown } from "@/components/dropdowns"; import { DateDropdown } from "@/components/dropdowns";
import { ModuleQuickActions } from "@/components/modules"; import { ModuleQuickActions } from "@/components/modules";
import { ModuleStatusDropdown } from "@/components/modules/module-status-dropdown"; import { ModuleStatusDropdown } from "@/components/modules/module-status-dropdown";
// constants // constants
@ -132,28 +138,38 @@ export const ModuleListItemAction: FC<Props> = observer((props) => {
return ( return (
<> <>
<DateRangeDropdown <DateDropdown
buttonContainerClassName={`h-6 w-full flex ${isDisabled ? "cursor-not-allowed" : "cursor-pointer"} items-center gap-1.5 text-custom-text-300 border-[0.5px] border-custom-border-300 rounded text-xs`} value={moduleDetails.start_date}
buttonVariant="transparent-with-text" onChange={(val) => {
className="h-7"
value={{
from: getDate(moduleDetails.start_date),
to: getDate(moduleDetails.target_date),
}}
onSelect={(val) => {
handleModuleDetailsChange({ handleModuleDetailsChange({
start_date: val?.from ? renderFormattedPayloadDate(val.from) : null, start_date: val ? renderFormattedPayloadDate(val) : null,
target_date: val?.to ? renderFormattedPayloadDate(val.to) : null,
}); });
}} }}
placeholder={{ placeholder={t("common.order_by.start_date")}
from: t("start_date"), icon={<CalendarClock className="h-3 w-3 flex-shrink-0" />}
to: t("end_date"), buttonVariant={moduleDetails.start_date ? "border-with-text" : "border-without-text"}
}} buttonContainerClassName={`h-6 w-full flex ${isDisabled ? "cursor-not-allowed" : "cursor-pointer"} items-center gap-1.5 text-custom-text-300 rounded text-xs`}
optionsClassName="z-10"
disabled={isDisabled} disabled={isDisabled}
hideIcon={{ from: renderIcon ?? true, to: renderIcon }} showTooltip
maxDate={getDate(moduleDetails.target_date)}
/>
<DateDropdown
value={moduleDetails.target_date}
onChange={(val) => {
handleModuleDetailsChange({
target_date: val ? renderFormattedPayloadDate(val) : null,
});
}}
placeholder={t("common.order_by.due_date")}
icon={<CalendarCheck2 className="h-3 w-3 flex-shrink-0" />}
buttonVariant={moduleDetails.target_date ? "border-with-text" : "border-without-text"}
buttonContainerClassName={`h-6 w-full flex ${isDisabled ? "cursor-not-allowed" : "cursor-pointer"} items-center gap-1.5 text-custom-text-300 rounded text-xs`}
optionsClassName="z-10"
disabled={isDisabled}
showTooltip
minDate={getDate(moduleDetails.start_date)}
/> />
{moduleStatus && ( {moduleStatus && (
<ModuleStatusDropdown <ModuleStatusDropdown
isDisabled={isDisabled} isDisabled={isDisabled}