Skip to content

Commit 59a0925

Browse files
authored
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
1 parent fbbf584 commit 59a0925

File tree

5 files changed

+296
-176
lines changed

5 files changed

+296
-176
lines changed

web/core/components/cycles/analytics-sidebar/sidebar-header.tsx

Lines changed: 88 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,24 @@
33
import React, { FC, useEffect, useState } from "react";
44
import { observer } from "mobx-react";
55
import { Controller, useForm } from "react-hook-form";
6-
import { ArchiveIcon, ArchiveRestoreIcon, ChevronRight, EllipsisIcon, LinkIcon, Trash2 } from "lucide-react";
6+
import {
7+
ArchiveIcon,
8+
ArchiveRestoreIcon,
9+
CalendarCheck2,
10+
CalendarClock,
11+
ChevronRight,
12+
EllipsisIcon,
13+
LinkIcon,
14+
Trash2,
15+
} from "lucide-react";
716
// types
817
import { CYCLE_STATUS, CYCLE_UPDATED, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
918
import { useTranslation } from "@plane/i18n";
1019
import { ICycle } from "@plane/types";
1120
// ui
1221
import { CustomMenu, setToast, TOAST_TYPE } from "@plane/ui";
1322
// components
14-
import { DateRangeDropdown } from "@/components/dropdowns";
23+
import { DateDropdown } from "@/components/dropdowns";
1524
// helpers
1625
import { renderFormattedPayloadDate, getDate } from "@/helpers/date-time.helper";
1726
import { copyUrlToClipboard } from "@/helpers/string.helper";
@@ -54,7 +63,7 @@ export const CycleSidebarHeader: FC<Props> = observer((props) => {
5463
const { t } = useTranslation();
5564

5665
// form info
57-
const { control, reset } = useForm({
66+
const { control, reset, getValues } = useForm({
5867
defaultValues,
5968
});
6069

@@ -145,20 +154,13 @@ export const CycleSidebarHeader: FC<Props> = observer((props) => {
145154
}
146155
};
147156

148-
const handleDateChange = async (startDate: Date | undefined, endDate: Date | undefined) => {
149-
if (!startDate || !endDate) return;
150-
157+
const handleDateChange = async (payload: { start_date?: string | null; end_date?: string | null }) => {
151158
let isDateValid = false;
152159

153-
const payload = {
154-
start_date: renderFormattedPayloadDate(startDate),
155-
end_date: renderFormattedPayloadDate(endDate),
156-
};
157-
158-
if (cycleDetails?.start_date && cycleDetails.end_date)
160+
if (cycleDetails?.start_date && cycleDetails?.end_date)
159161
isDateValid = await dateChecker({
160162
...payload,
161-
cycle_id: cycleDetails.id,
163+
cycle_id: cycleDetails?.id,
162164
});
163165
else isDateValid = await dateChecker(payload);
164166

@@ -177,6 +179,7 @@ export const CycleSidebarHeader: FC<Props> = observer((props) => {
177179
});
178180
reset({ ...cycleDetails });
179181
}
182+
return isDateValid;
180183
};
181184

182185
const isEditingAllowed = allowPermissions(
@@ -285,39 +288,79 @@ export const CycleSidebarHeader: FC<Props> = observer((props) => {
285288
</span>
286289
)}
287290
</div>
291+
<div className="flex items-center gap-2">
292+
<Controller
293+
name="start_date"
294+
control={control}
295+
rules={{ required: "Please select a date" }}
296+
render={({ field: { value, onChange } }) => (
297+
<DateDropdown
298+
value={value ?? null}
299+
onChange={async (val) => {
300+
let isDateValid;
301+
const valDate = val ? renderFormattedPayloadDate(val) : null;
302+
if (getValues("end_date")) {
303+
isDateValid = await handleDateChange({
304+
start_date: valDate,
305+
end_date: renderFormattedPayloadDate(getValues("end_date")),
306+
});
307+
} else {
308+
isDateValid = await handleDateChange({
309+
start_date: valDate,
310+
end_date: valDate,
311+
});
312+
}
313+
isDateValid && onChange(renderFormattedPayloadDate(val));
314+
}}
315+
placeholder={t("common.order_by.start_date")}
316+
icon={<CalendarClock className="h-3 w-3 flex-shrink-0" />}
317+
buttonVariant={value ? "border-with-text" : "border-without-text"}
318+
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`}
319+
optionsClassName="z-10"
320+
disabled={!isEditingAllowed || isArchived || isCompleted}
321+
showTooltip
322+
maxDate={getDate(getValues("end_date"))}
323+
isClearable={false}
324+
/>
325+
)}
326+
/>
288327

289-
<Controller
290-
control={control}
291-
name="start_date"
292-
render={({ field: { value: startDateValue, onChange: onChangeStartDate } }) => (
293-
<Controller
294-
control={control}
295-
name="end_date"
296-
render={({ field: { value: endDateValue, onChange: onChangeEndDate } }) => (
297-
<DateRangeDropdown
298-
className="h-7"
299-
buttonVariant="transparent-with-text"
300-
minDate={new Date()}
301-
value={{
302-
from: getDate(startDateValue),
303-
to: getDate(endDateValue),
304-
}}
305-
onSelect={(val) => {
306-
onChangeStartDate(val?.from ? renderFormattedPayloadDate(val.from) : null);
307-
onChangeEndDate(val?.to ? renderFormattedPayloadDate(val.to) : null);
308-
handleDateChange(val?.from, val?.to);
309-
}}
310-
placeholder={{
311-
from: "Start date",
312-
to: "End date",
313-
}}
314-
required={cycleDetails.status !== "draft"}
315-
disabled={!isEditingAllowed || isArchived || isCompleted}
316-
/>
317-
)}
318-
/>
319-
)}
320-
/>
328+
<Controller
329+
name="end_date"
330+
control={control}
331+
rules={{ required: "Please select a date" }}
332+
render={({ field: { value, onChange } }) => (
333+
<DateDropdown
334+
value={getDate(value) ?? null}
335+
onChange={async (val) => {
336+
let isDateValid;
337+
const valDate = val ? renderFormattedPayloadDate(val) : null;
338+
if (getValues("start_date")) {
339+
isDateValid = await handleDateChange({
340+
end_date: valDate,
341+
start_date: renderFormattedPayloadDate(getValues("start_date")),
342+
});
343+
} else {
344+
isDateValid = await handleDateChange({
345+
end_date: valDate,
346+
start_date: valDate,
347+
});
348+
}
349+
isDateValid && onChange(renderFormattedPayloadDate(val));
350+
}}
351+
placeholder={t("common.order_by.due_date")}
352+
icon={<CalendarCheck2 className="h-3 w-3 flex-shrink-0" />}
353+
buttonVariant={value ? "border-with-text" : "border-without-text"}
354+
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`}
355+
optionsClassName="z-10"
356+
disabled={!isEditingAllowed || isArchived || isCompleted}
357+
showTooltip
358+
minDate={getDate(getValues("start_date"))}
359+
isClearable={false}
360+
/>
361+
)}
362+
/>
363+
</div>
321364
</div>
322365
</>
323366
);

web/core/components/cycles/list/cycle-list-item-action.tsx

Lines changed: 77 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import React, { FC, MouseEvent, useEffect, useMemo, useState } from "react";
44
import { observer } from "mobx-react";
55
import { useParams, usePathname, useSearchParams } from "next/navigation";
66
import { Controller, useForm } from "react-hook-form";
7-
import { Eye, Users } from "lucide-react";
7+
import { CalendarCheck2, CalendarClock, Eye, Users } from "lucide-react";
88
// types
99
import { CYCLE_FAVORITED, CYCLE_UNFAVORITED, EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
1010
import { useTranslation } from "@plane/i18n";
@@ -23,7 +23,7 @@ import {
2323
} from "@plane/ui";
2424
// components
2525
import { CycleQuickActions, TransferIssuesModal } from "@/components/cycles";
26-
import { DateRangeDropdown } from "@/components/dropdowns";
26+
import { DateDropdown } from "@/components/dropdowns";
2727
import { ButtonAvatars } from "@/components/dropdowns/member/avatar";
2828
// constants
2929
// helpers
@@ -41,7 +41,6 @@ import { CycleAdditionalActions } from "@/plane-web/components/cycles";
4141
import { CycleService } from "@/services/cycle.service";
4242

4343
const cycleService = new CycleService();
44-
4544
type Props = {
4645
workspaceSlug: string;
4746
projectId: string;
@@ -77,7 +76,7 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
7776
const { getUserDetails } = useMember();
7877

7978
// form
80-
const { control, reset } = useForm({
79+
const { control, reset, getValues } = useForm({
8180
defaultValues,
8281
});
8382

@@ -98,7 +97,6 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
9897
workspaceSlug,
9998
projectId
10099
);
101-
const renderIcon = Boolean(cycleDetails.start_date) || Boolean(cycleDetails.end_date);
102100

103101
// handlers
104102
const handleAddToFavorites = (e: MouseEvent<HTMLButtonElement>) => {
@@ -171,20 +169,13 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
171169
}
172170
};
173171

174-
const handleDateChange = async (startDate: Date | undefined, endDate: Date | undefined) => {
175-
if (!startDate || !endDate) return;
176-
172+
const handleDateChange = async (payload: { start_date?: string | null; end_date?: string | null }) => {
177173
let isDateValid = false;
178174

179-
const payload = {
180-
start_date: renderFormattedPayloadDate(startDate),
181-
end_date: renderFormattedPayloadDate(endDate),
182-
};
183-
184-
if (cycleDetails && cycleDetails.start_date && cycleDetails.end_date)
175+
if (cycleDetails?.start_date && cycleDetails?.end_date)
185176
isDateValid = await dateChecker({
186177
...payload,
187-
cycle_id: cycleDetails.id,
178+
cycle_id: cycleDetails?.id,
188179
});
189180
else isDateValid = await dateChecker(payload);
190181

@@ -203,6 +194,7 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
203194
});
204195
reset({ ...cycleDetails });
205196
}
197+
return isDateValid;
206198
};
207199

208200
const createdByDetails = cycleDetails.created_by ? getUserDetails(cycleDetails.created_by) : undefined;
@@ -268,35 +260,77 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
268260

269261
{!isActive && (
270262
<Controller
271-
control={control}
272263
name="start_date"
273-
render={({ field: { value: startDateValue, onChange: onChangeStartDate } }) => (
274-
<Controller
275-
control={control}
276-
name="end_date"
277-
render={({ field: { value: endDateValue, onChange: onChangeEndDate } }) => (
278-
<DateRangeDropdown
279-
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`}
280-
buttonVariant="transparent-with-text"
281-
minDate={new Date()}
282-
value={{
283-
from: getDate(startDateValue),
284-
to: getDate(endDateValue),
285-
}}
286-
onSelect={(val) => {
287-
onChangeStartDate(val?.from ? renderFormattedPayloadDate(val.from) : null);
288-
onChangeEndDate(val?.to ? renderFormattedPayloadDate(val.to) : null);
289-
handleDateChange(val?.from, val?.to);
290-
}}
291-
placeholder={{
292-
from: "Start date",
293-
to: "End date",
294-
}}
295-
required={cycleDetails.status !== "draft"}
296-
disabled={isDisabled}
297-
hideIcon={{ from: renderIcon ?? true, to: renderIcon }}
298-
/>
299-
)}
264+
control={control}
265+
rules={{ required: "Please select a date" }}
266+
render={({ field: { value, onChange } }) => (
267+
<DateDropdown
268+
value={value ?? null}
269+
onChange={async (val) => {
270+
let isDateValid;
271+
const valDate = val ? renderFormattedPayloadDate(val) : null;
272+
if (getValues("end_date")) {
273+
isDateValid = await handleDateChange({
274+
start_date: valDate,
275+
end_date: renderFormattedPayloadDate(getValues("end_date")),
276+
});
277+
} else {
278+
isDateValid = await handleDateChange({
279+
start_date: valDate,
280+
end_date: valDate,
281+
});
282+
}
283+
isDateValid && onChange(renderFormattedPayloadDate(val));
284+
}}
285+
placeholder={t("common.order_by.start_date")}
286+
icon={<CalendarClock className="h-3 w-3 flex-shrink-0" />}
287+
buttonVariant={value ? "border-with-text" : "border-without-text"}
288+
buttonContainerClassName={`h-6 w-full flex ${isDisabled ? "cursor-not-allowed" : "cursor-pointer"} items-center gap-1.5 text-custom-text-300 rounded text-xs`}
289+
optionsClassName="z-10"
290+
disabled={isDisabled}
291+
renderByDefault={isMobile}
292+
showTooltip
293+
maxDate={getDate(getValues("end_date"))}
294+
isClearable={false}
295+
/>
296+
)}
297+
/>
298+
)}
299+
300+
{!isActive && (
301+
<Controller
302+
name="end_date"
303+
control={control}
304+
rules={{ required: "Please select a date" }}
305+
render={({ field: { value, onChange } }) => (
306+
<DateDropdown
307+
value={getDate(value) ?? null}
308+
onChange={async (val) => {
309+
let isDateValid;
310+
const valDate = val ? renderFormattedPayloadDate(val) : null;
311+
if (getValues("start_date")) {
312+
isDateValid = await handleDateChange({
313+
end_date: valDate,
314+
start_date: renderFormattedPayloadDate(getValues("start_date")),
315+
});
316+
} else {
317+
isDateValid = await handleDateChange({
318+
end_date: valDate,
319+
start_date: valDate,
320+
});
321+
}
322+
isDateValid && onChange(renderFormattedPayloadDate(val));
323+
}}
324+
placeholder={t("common.order_by.due_date")}
325+
icon={<CalendarCheck2 className="h-3 w-3 flex-shrink-0" />}
326+
buttonVariant={value ? "border-with-text" : "border-without-text"}
327+
buttonContainerClassName={`h-6 w-full flex ${isDisabled ? "cursor-not-allowed" : "cursor-pointer"} items-center gap-1.5 text-custom-text-300 rounded text-xs`}
328+
optionsClassName="z-10"
329+
disabled={isDisabled}
330+
renderByDefault={isMobile}
331+
showTooltip
332+
minDate={getDate(getValues("start_date"))}
333+
isClearable={false}
300334
/>
301335
)}
302336
/>

0 commit comments

Comments
 (0)