diff --git a/web/core/components/cycles/analytics-sidebar/sidebar-header.tsx b/web/core/components/cycles/analytics-sidebar/sidebar-header.tsx index 6c5b3339335..a41be6752ca 100644 --- a/web/core/components/cycles/analytics-sidebar/sidebar-header.tsx +++ b/web/core/components/cycles/analytics-sidebar/sidebar-header.tsx @@ -3,16 +3,7 @@ import React, { FC, useEffect, useState } from "react"; import { observer } from "mobx-react"; import { Controller, useForm } from "react-hook-form"; -import { - ArchiveIcon, - ArchiveRestoreIcon, - CalendarCheck2, - CalendarClock, - ChevronRight, - EllipsisIcon, - LinkIcon, - Trash2, -} from "lucide-react"; +import { ArchiveIcon, ArchiveRestoreIcon, ChevronRight, EllipsisIcon, LinkIcon, Trash2 } from "lucide-react"; // types import { CYCLE_STATUS, CYCLE_UPDATED, EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; @@ -20,7 +11,7 @@ import { ICycle } from "@plane/types"; // ui import { CustomMenu, setToast, TOAST_TYPE } from "@plane/ui"; // components -import { DateDropdown } from "@/components/dropdowns"; +import { DateRangeDropdown } from "@/components/dropdowns"; // helpers import { renderFormattedPayloadDate, getDate } from "@/helpers/date-time.helper"; import { copyUrlToClipboard } from "@/helpers/string.helper"; @@ -63,7 +54,7 @@ export const CycleSidebarHeader: FC = observer((props) => { const { t } = useTranslation(); // form info - const { control, reset, getValues } = useForm({ + const { control, reset } = useForm({ defaultValues, }); @@ -110,10 +101,10 @@ export const CycleSidebarHeader: FC = observer((props) => { }); }; - const submitChanges = (data: Partial, changedProperty: string) => { + const submitChanges = async (data: Partial, changedProperty: string) => { if (!workspaceSlug || !projectId || !cycleDetails.id) return; - updateCycleDetails(workspaceSlug.toString(), projectId.toString(), cycleDetails.id.toString(), data) + await updateCycleDetails(workspaceSlug.toString(), projectId.toString(), cycleDetails.id.toString(), data) .then((res) => { captureCycleEvent({ eventName: CYCLE_UPDATED, @@ -154,16 +145,22 @@ export const CycleSidebarHeader: FC = observer((props) => { } }; - const handleDateChange = async (payload: { start_date?: string | null; end_date?: string | null }) => { + const handleDateChange = async (startDate: Date | undefined, endDate: Date | undefined) => { let isDateValid = false; - if (cycleDetails?.start_date && cycleDetails?.end_date) + const payload = { + start_date: renderFormattedPayloadDate(startDate) || null, + end_date: renderFormattedPayloadDate(endDate) || null, + }; + + if (payload?.start_date && payload.end_date) { isDateValid = await dateChecker({ ...payload, - cycle_id: cycleDetails?.id, + cycle_id: cycleDetails.id, }); - else isDateValid = await dateChecker(payload); - + } else { + isDateValid = true; + } if (isDateValid) { submitChanges(payload, "date_range"); setToast({ @@ -177,7 +174,6 @@ export const CycleSidebarHeader: FC = observer((props) => { title: t("project_cycles.action.update.failed.title"), message: t("project_cycles.action.update.error.already_exists"), }); - reset({ ...cycleDetails }); } return isDateValid; }; @@ -288,79 +284,41 @@ export const CycleSidebarHeader: FC = observer((props) => { )} -
- ( - { - 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={} - 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} - /> - )} - /> - ( - { - 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={} - 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} - /> - )} - /> -
+ ( + ( + { + const isDateValid = await handleDateChange(val?.from, val?.to); + if (isDateValid) { + onChangeStartDate(val?.from ? renderFormattedPayloadDate(val.from) : null); + onChangeEndDate(val?.to ? renderFormattedPayloadDate(val.to) : null); + } + }} + placeholder={{ + from: "Start date", + to: "End date", + }} + required={cycleDetails.status !== "draft"} + disabled={!isEditingAllowed || isArchived || isCompleted} + /> + )} + /> + )} + /> ); diff --git a/web/core/components/cycles/list/cycle-list-item-action.tsx b/web/core/components/cycles/list/cycle-list-item-action.tsx index f009ff19767..b7fc40666f4 100644 --- a/web/core/components/cycles/list/cycle-list-item-action.tsx +++ b/web/core/components/cycles/list/cycle-list-item-action.tsx @@ -3,31 +3,21 @@ 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 { CalendarCheck2, CalendarClock, Eye, Users } from "lucide-react"; +import { useForm } from "react-hook-form"; +import { Eye, Users } from "lucide-react"; // types import { CYCLE_FAVORITED, CYCLE_UNFAVORITED, EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; import { ICycle, TCycleGroups } from "@plane/types"; // ui -import { - Avatar, - AvatarGroup, - FavoriteStar, - LayersIcon, - TOAST_TYPE, - Tooltip, - TransferIcon, - setPromiseToast, - setToast, -} from "@plane/ui"; +import { Avatar, AvatarGroup, FavoriteStar, LayersIcon, Tooltip, TransferIcon, setPromiseToast } from "@plane/ui"; // components import { CycleQuickActions, TransferIssuesModal } from "@/components/cycles"; -import { DateDropdown } from "@/components/dropdowns"; +import { DateRangeDropdown } from "@/components/dropdowns"; import { ButtonAvatars } from "@/components/dropdowns/member/avatar"; // constants // helpers -import { getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper"; +import { getDate } from "@/helpers/date-time.helper"; import { getFileURL } from "@/helpers/file.helper"; // hooks import { generateQueryParams } from "@/helpers/router.helper"; @@ -36,11 +26,7 @@ import { useAppRouter } from "@/hooks/use-app-router"; import { usePlatformOS } from "@/hooks/use-platform-os"; // plane web components import { CycleAdditionalActions } from "@/plane-web/components/cycles"; -// plane web constants -// services -import { CycleService } from "@/services/cycle.service"; -const cycleService = new CycleService(); type Props = { workspaceSlug: string; projectId: string; @@ -155,48 +141,6 @@ export const CycleListItemAction: FC = observer((props) => { }); }; - const submitChanges = (data: Partial) => { - if (!workspaceSlug || !projectId || !cycleId) return; - updateCycleDetails(workspaceSlug.toString(), projectId.toString(), cycleId.toString(), data); - }; - - const dateChecker = async (payload: any) => { - try { - const res = await cycleService.cycleDateCheck(workspaceSlug as string, projectId as string, payload); - return res.status; - } catch { - return false; - } - }; - - const handleDateChange = async (payload: { start_date?: string | null; end_date?: string | null }) => { - let isDateValid = false; - - if (cycleDetails?.start_date && cycleDetails?.end_date) - isDateValid = await dateChecker({ - ...payload, - cycle_id: cycleDetails?.id, - }); - else isDateValid = await dateChecker(payload); - - if (isDateValid) { - submitChanges(payload); - setToast({ - type: TOAST_TYPE.SUCCESS, - title: t("project_cycles.action.update.success.title"), - message: t("project_cycles.action.update.success.description"), - }); - } else { - setToast({ - type: TOAST_TYPE.ERROR, - title: t("project_cycles.action.update.failed.title"), - message: t("project_cycles.action.update.error.already_exists"), - }); - reset({ ...cycleDetails }); - } - return isDateValid; - }; - const createdByDetails = cycleDetails.created_by ? getUserDetails(cycleDetails.created_by) : undefined; useEffect(() => { @@ -206,10 +150,6 @@ export const CycleListItemAction: FC = observer((props) => { }); }, [cycleDetails, reset]); - const isArchived = Boolean(cycleDetails.archived_at); - const isCompleted = cycleStatus === "completed"; - - const isDisabled = !isEditingAllowed || isArchived || isCompleted; // handlers const openCycleOverview = (e: MouseEvent) => { e.preventDefault(); @@ -258,81 +198,27 @@ export const CycleListItemAction: FC = observer((props) => { )} - {!isActive && ( - ( - { - 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={} - 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 && ( - ( - { - 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={} - 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} - /> - )} + {!isActive && cycleDetails.start_date && ( + div]:hover:bg-transparent`} + buttonClassName="p-0" + minDate={new Date()} + value={{ + from: getDate(cycleDetails.start_date), + to: getDate(cycleDetails.end_date), + }} + placeholder={{ + from: "Start date", + to: "End date", + }} + showTooltip + required={cycleDetails.status !== "draft"} + disabled + hideIcon={{ + from: false, + to: false, + }} /> )} diff --git a/web/core/components/dropdowns/date-range.tsx b/web/core/components/dropdowns/date-range.tsx index 7d73216a42d..f33a8f8b7ea 100644 --- a/web/core/components/dropdowns/date-range.tsx +++ b/web/core/components/dropdowns/date-range.tsx @@ -35,7 +35,7 @@ type Props = { }; minDate?: Date; maxDate?: Date; - onSelect: (range: DateRange | undefined) => void; + onSelect?: (range: DateRange | undefined) => void; placeholder?: { from?: string; to?: string; @@ -204,11 +204,7 @@ export const DateRangeDropdown: React.FC = (props) => { classNames={{ root: `p-3 rounded-md` }} selected={dateRange} onSelect={(val) => { - onSelect(val); - setDateRange({ - from: val?.from ?? undefined, - to: val?.to ?? undefined, - }); + onSelect?.(val); }} mode="range" disabled={disabledDays}