@@ -4,7 +4,7 @@ import React, { FC, MouseEvent, useEffect, useMemo, useState } from "react";
44import { observer } from "mobx-react" ;
55import { useParams , usePathname , useSearchParams } from "next/navigation" ;
66import { Controller , useForm } from "react-hook-form" ;
7- import { Eye , Users } from "lucide-react" ;
7+ import { CalendarCheck2 , CalendarClock , Eye , Users } from "lucide-react" ;
88// types
99import { CYCLE_FAVORITED , CYCLE_UNFAVORITED , EUserPermissions , EUserPermissionsLevel } from "@plane/constants" ;
1010import { useTranslation } from "@plane/i18n" ;
@@ -23,7 +23,7 @@ import {
2323} from "@plane/ui" ;
2424// components
2525import { CycleQuickActions , TransferIssuesModal } from "@/components/cycles" ;
26- import { DateRangeDropdown } from "@/components/dropdowns" ;
26+ import { DateDropdown } from "@/components/dropdowns" ;
2727import { ButtonAvatars } from "@/components/dropdowns/member/avatar" ;
2828// constants
2929// helpers
@@ -41,7 +41,6 @@ import { CycleAdditionalActions } from "@/plane-web/components/cycles";
4141import { CycleService } from "@/services/cycle.service" ;
4242
4343const cycleService = new CycleService ( ) ;
44-
4544type 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