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:
parent
fbbf58481d
commit
59a0925d34
5 changed files with 299 additions and 179 deletions
|
|
@ -3,7 +3,16 @@
|
|||
import React, { FC, useEffect, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
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
|
||||
import { CYCLE_STATUS, CYCLE_UPDATED, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
|
|
@ -11,7 +20,7 @@ import { ICycle } from "@plane/types";
|
|||
// ui
|
||||
import { CustomMenu, setToast, TOAST_TYPE } from "@plane/ui";
|
||||
// components
|
||||
import { DateRangeDropdown } from "@/components/dropdowns";
|
||||
import { DateDropdown } from "@/components/dropdowns";
|
||||
// helpers
|
||||
import { renderFormattedPayloadDate, getDate } from "@/helpers/date-time.helper";
|
||||
import { copyUrlToClipboard } from "@/helpers/string.helper";
|
||||
|
|
@ -54,7 +63,7 @@ export const CycleSidebarHeader: FC<Props> = observer((props) => {
|
|||
const { t } = useTranslation();
|
||||
|
||||
// form info
|
||||
const { control, reset } = useForm({
|
||||
const { control, reset, getValues } = useForm({
|
||||
defaultValues,
|
||||
});
|
||||
|
||||
|
|
@ -145,20 +154,13 @@ export const CycleSidebarHeader: FC<Props> = observer((props) => {
|
|||
}
|
||||
};
|
||||
|
||||
const handleDateChange = async (startDate: Date | undefined, endDate: Date | undefined) => {
|
||||
if (!startDate || !endDate) return;
|
||||
|
||||
const handleDateChange = async (payload: { start_date?: string | null; end_date?: string | null }) => {
|
||||
let isDateValid = false;
|
||||
|
||||
const payload = {
|
||||
start_date: renderFormattedPayloadDate(startDate),
|
||||
end_date: renderFormattedPayloadDate(endDate),
|
||||
};
|
||||
|
||||
if (cycleDetails?.start_date && cycleDetails.end_date)
|
||||
if (cycleDetails?.start_date && cycleDetails?.end_date)
|
||||
isDateValid = await dateChecker({
|
||||
...payload,
|
||||
cycle_id: cycleDetails.id,
|
||||
cycle_id: cycleDetails?.id,
|
||||
});
|
||||
else isDateValid = await dateChecker(payload);
|
||||
|
||||
|
|
@ -177,6 +179,7 @@ export const CycleSidebarHeader: FC<Props> = observer((props) => {
|
|||
});
|
||||
reset({ ...cycleDetails });
|
||||
}
|
||||
return isDateValid;
|
||||
};
|
||||
|
||||
const isEditingAllowed = allowPermissions(
|
||||
|
|
@ -285,39 +288,79 @@ export const CycleSidebarHeader: FC<Props> = observer((props) => {
|
|||
</span>
|
||||
)}
|
||||
</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
|
||||
control={control}
|
||||
name="start_date"
|
||||
render={({ field: { value: startDateValue, onChange: onChangeStartDate } }) => (
|
||||
<Controller
|
||||
control={control}
|
||||
name="end_date"
|
||||
render={({ field: { value: endDateValue, onChange: onChangeEndDate } }) => (
|
||||
<DateRangeDropdown
|
||||
className="h-7"
|
||||
buttonVariant="transparent-with-text"
|
||||
minDate={new Date()}
|
||||
value={{
|
||||
from: getDate(startDateValue),
|
||||
to: getDate(endDateValue),
|
||||
}}
|
||||
onSelect={(val) => {
|
||||
onChangeStartDate(val?.from ? renderFormattedPayloadDate(val.from) : null);
|
||||
onChangeEndDate(val?.to ? renderFormattedPayloadDate(val.to) : null);
|
||||
handleDateChange(val?.from, val?.to);
|
||||
}}
|
||||
placeholder={{
|
||||
from: "Start date",
|
||||
to: "End date",
|
||||
}}
|
||||
required={cycleDetails.status !== "draft"}
|
||||
disabled={!isEditingAllowed || isArchived || isCompleted}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<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 ${!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>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import React, { FC, MouseEvent, useEffect, useMemo, useState } from "react";
|
|||
import { observer } from "mobx-react";
|
||||
import { useParams, usePathname, useSearchParams } from "next/navigation";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { Eye, Users } from "lucide-react";
|
||||
import { CalendarCheck2, CalendarClock, Eye, Users } from "lucide-react";
|
||||
// types
|
||||
import { CYCLE_FAVORITED, CYCLE_UNFAVORITED, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
|
|
@ -23,7 +23,7 @@ import {
|
|||
} from "@plane/ui";
|
||||
// components
|
||||
import { CycleQuickActions, TransferIssuesModal } from "@/components/cycles";
|
||||
import { DateRangeDropdown } from "@/components/dropdowns";
|
||||
import { DateDropdown } from "@/components/dropdowns";
|
||||
import { ButtonAvatars } from "@/components/dropdowns/member/avatar";
|
||||
// constants
|
||||
// helpers
|
||||
|
|
@ -41,7 +41,6 @@ import { CycleAdditionalActions } from "@/plane-web/components/cycles";
|
|||
import { CycleService } from "@/services/cycle.service";
|
||||
|
||||
const cycleService = new CycleService();
|
||||
|
||||
type Props = {
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
|
|
@ -77,7 +76,7 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
|
|||
const { getUserDetails } = useMember();
|
||||
|
||||
// form
|
||||
const { control, reset } = useForm({
|
||||
const { control, reset, getValues } = useForm({
|
||||
defaultValues,
|
||||
});
|
||||
|
||||
|
|
@ -98,7 +97,6 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
|
|||
workspaceSlug,
|
||||
projectId
|
||||
);
|
||||
const renderIcon = Boolean(cycleDetails.start_date) || Boolean(cycleDetails.end_date);
|
||||
|
||||
// handlers
|
||||
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) => {
|
||||
if (!startDate || !endDate) return;
|
||||
|
||||
const handleDateChange = async (payload: { start_date?: string | null; end_date?: string | null }) => {
|
||||
let isDateValid = false;
|
||||
|
||||
const payload = {
|
||||
start_date: renderFormattedPayloadDate(startDate),
|
||||
end_date: renderFormattedPayloadDate(endDate),
|
||||
};
|
||||
|
||||
if (cycleDetails && cycleDetails.start_date && cycleDetails.end_date)
|
||||
if (cycleDetails?.start_date && cycleDetails?.end_date)
|
||||
isDateValid = await dateChecker({
|
||||
...payload,
|
||||
cycle_id: cycleDetails.id,
|
||||
cycle_id: cycleDetails?.id,
|
||||
});
|
||||
else isDateValid = await dateChecker(payload);
|
||||
|
||||
|
|
@ -203,6 +194,7 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
|
|||
});
|
||||
reset({ ...cycleDetails });
|
||||
}
|
||||
return isDateValid;
|
||||
};
|
||||
|
||||
const createdByDetails = cycleDetails.created_by ? getUserDetails(cycleDetails.created_by) : undefined;
|
||||
|
|
@ -268,35 +260,77 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
|
|||
|
||||
{!isActive && (
|
||||
<Controller
|
||||
control={control}
|
||||
name="start_date"
|
||||
render={({ field: { value: startDateValue, onChange: onChangeStartDate } }) => (
|
||||
<Controller
|
||||
control={control}
|
||||
name="end_date"
|
||||
render={({ field: { value: endDateValue, onChange: onChangeEndDate } }) => (
|
||||
<DateRangeDropdown
|
||||
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`}
|
||||
buttonVariant="transparent-with-text"
|
||||
minDate={new Date()}
|
||||
value={{
|
||||
from: getDate(startDateValue),
|
||||
to: getDate(endDateValue),
|
||||
}}
|
||||
onSelect={(val) => {
|
||||
onChangeStartDate(val?.from ? renderFormattedPayloadDate(val.from) : null);
|
||||
onChangeEndDate(val?.to ? renderFormattedPayloadDate(val.to) : null);
|
||||
handleDateChange(val?.from, val?.to);
|
||||
}}
|
||||
placeholder={{
|
||||
from: "Start date",
|
||||
to: "End date",
|
||||
}}
|
||||
required={cycleDetails.status !== "draft"}
|
||||
disabled={isDisabled}
|
||||
hideIcon={{ from: renderIcon ?? true, to: renderIcon }}
|
||||
/>
|
||||
)}
|
||||
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 ${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}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { useParams } from "next/navigation";
|
|||
import { Controller, useForm } from "react-hook-form";
|
||||
import {
|
||||
ArchiveRestoreIcon,
|
||||
CalendarCheck2,
|
||||
CalendarClock,
|
||||
ChevronDown,
|
||||
ChevronRight,
|
||||
|
|
@ -42,7 +43,7 @@ import {
|
|||
TextArea,
|
||||
} from "@plane/ui";
|
||||
// components
|
||||
import { DateRangeDropdown, MemberDropdown } from "@/components/dropdowns";
|
||||
import { DateDropdown, MemberDropdown } from "@/components/dropdowns";
|
||||
import {
|
||||
ArchiveModuleModal,
|
||||
DeleteModuleModal,
|
||||
|
|
@ -104,7 +105,7 @@ export const ModuleAnalyticsSidebar: React.FC<Props> = observer((props) => {
|
|||
const estimateType = areEstimateEnabled && currentActiveEstimateId && estimateById(currentActiveEstimateId);
|
||||
const isEstimatePointValid = estimateType && estimateType?.type == EEstimateSystem.POINTS ? true : false;
|
||||
|
||||
const { reset, control } = useForm({
|
||||
const { reset, control, getValues } = useForm({
|
||||
defaultValues,
|
||||
});
|
||||
|
||||
|
|
@ -194,11 +195,8 @@ export const ModuleAnalyticsSidebar: React.FC<Props> = observer((props) => {
|
|||
});
|
||||
};
|
||||
|
||||
const handleDateChange = async (startDate: Date | undefined, targetDate: Date | undefined) => {
|
||||
submitChanges({
|
||||
start_date: startDate ? renderFormattedPayloadDate(startDate) : null,
|
||||
target_date: targetDate ? renderFormattedPayloadDate(targetDate) : null,
|
||||
});
|
||||
const handleDateChange = async (data: Partial<IModule>) => {
|
||||
submitChanges(data);
|
||||
setToast({
|
||||
type: TOAST_TYPE.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 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 w-2/5 items-center justify-start gap-2 text-custom-text-300">
|
||||
<SquareUser className="h-4 w-4" />
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import React, { SyntheticEvent, useRef } from "react";
|
|||
import { observer } from "mobx-react";
|
||||
import Link from "next/link";
|
||||
import { useParams, usePathname, useSearchParams } from "next/navigation";
|
||||
import { Info, SquareUser } from "lucide-react";
|
||||
import { CalendarCheck2, CalendarClock, Info, SquareUser } from "lucide-react";
|
||||
// plane package imports
|
||||
import {
|
||||
MODULE_STATUS,
|
||||
|
|
@ -14,6 +14,7 @@ import {
|
|||
EUserPermissions,
|
||||
EUserPermissionsLevel,
|
||||
} from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { IModule } from "@plane/types";
|
||||
import {
|
||||
Card,
|
||||
|
|
@ -26,7 +27,7 @@ import {
|
|||
setToast,
|
||||
} from "@plane/ui";
|
||||
// components
|
||||
import { DateRangeDropdown } from "@/components/dropdowns";
|
||||
import { DateDropdown } from "@/components/dropdowns";
|
||||
import { ButtonAvatars } from "@/components/dropdowns/member/avatar";
|
||||
import { ModuleQuickActions } from "@/components/modules";
|
||||
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 { getUserDetails } = useMember();
|
||||
const { captureEvent } = useEventTracker();
|
||||
|
||||
const { t } = useTranslation();
|
||||
// derived values
|
||||
const moduleDetails = getModuleById(moduleId);
|
||||
const isEditingAllowed = allowPermissions(
|
||||
|
|
@ -66,7 +67,6 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
|
|||
EUserPermissionsLevel.PROJECT
|
||||
);
|
||||
const isDisabled = !isEditingAllowed || !!moduleDetails?.archived_at;
|
||||
const renderIcon = Boolean(moduleDetails?.start_date) || Boolean(moduleDetails?.target_date);
|
||||
|
||||
const { isMobile } = usePlatformOS();
|
||||
const handleAddToFavorites = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
|
|
@ -236,27 +236,38 @@ export const ModuleCardItem: React.FC<Props> = observer((props) => {
|
|||
)}
|
||||
</div>
|
||||
<LinearProgressIndicator size="lg" data={progressIndicatorData} />
|
||||
<div className="flex items-center justify-between py-0.5" onClick={handleEventPropagation}>
|
||||
<DateRangeDropdown
|
||||
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`}
|
||||
buttonVariant="transparent-with-text"
|
||||
className="h-7"
|
||||
value={{
|
||||
from: getDate(moduleDetails.start_date),
|
||||
to: getDate(moduleDetails.target_date),
|
||||
}}
|
||||
onSelect={(val) => {
|
||||
<div className="flex items-center gap-2 py-0.5" onClick={handleEventPropagation}>
|
||||
<DateDropdown
|
||||
value={moduleDetails.start_date}
|
||||
onChange={(val) => {
|
||||
handleModuleDetailsChange({
|
||||
start_date: val?.from ? renderFormattedPayloadDate(val.from) : null,
|
||||
target_date: val?.to ? renderFormattedPayloadDate(val.to) : null,
|
||||
start_date: val ? renderFormattedPayloadDate(val) : null,
|
||||
});
|
||||
}}
|
||||
placeholder={{
|
||||
from: "Start date",
|
||||
to: "End date",
|
||||
}}
|
||||
placeholder={t("common.order_by.start_date")}
|
||||
icon={<CalendarClock className="h-3 w-3 flex-shrink-0" />}
|
||||
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}
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -4,15 +4,21 @@ import React, { FC } from "react";
|
|||
import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
// icons
|
||||
import { SquareUser } from "lucide-react";
|
||||
import { CalendarCheck2, CalendarClock, SquareUser } from "lucide-react";
|
||||
// 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 { IModule } from "@plane/types";
|
||||
// ui
|
||||
import { FavoriteStar, TOAST_TYPE, Tooltip, setPromiseToast, setToast } from "@plane/ui";
|
||||
// components
|
||||
import { DateRangeDropdown } from "@/components/dropdowns";
|
||||
import { DateDropdown } from "@/components/dropdowns";
|
||||
import { ModuleQuickActions } from "@/components/modules";
|
||||
import { ModuleStatusDropdown } from "@/components/modules/module-status-dropdown";
|
||||
// constants
|
||||
|
|
@ -132,28 +138,38 @@ export const ModuleListItemAction: FC<Props> = observer((props) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<DateRangeDropdown
|
||||
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`}
|
||||
buttonVariant="transparent-with-text"
|
||||
className="h-7"
|
||||
value={{
|
||||
from: getDate(moduleDetails.start_date),
|
||||
to: getDate(moduleDetails.target_date),
|
||||
}}
|
||||
onSelect={(val) => {
|
||||
<DateDropdown
|
||||
value={moduleDetails.start_date}
|
||||
onChange={(val) => {
|
||||
handleModuleDetailsChange({
|
||||
start_date: val?.from ? renderFormattedPayloadDate(val.from) : null,
|
||||
target_date: val?.to ? renderFormattedPayloadDate(val.to) : null,
|
||||
start_date: val ? renderFormattedPayloadDate(val) : null,
|
||||
});
|
||||
}}
|
||||
placeholder={{
|
||||
from: t("start_date"),
|
||||
to: t("end_date"),
|
||||
}}
|
||||
placeholder={t("common.order_by.start_date")}
|
||||
icon={<CalendarClock className="h-3 w-3 flex-shrink-0" />}
|
||||
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}
|
||||
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 && (
|
||||
<ModuleStatusDropdown
|
||||
isDisabled={isDisabled}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue