diff --git a/web/components/analytics/custom-analytics/sidebar/sidebar-header.tsx b/web/components/analytics/custom-analytics/sidebar/sidebar-header.tsx index 2eaaac7fb..ac75be686 100644 --- a/web/components/analytics/custom-analytics/sidebar/sidebar-header.tsx +++ b/web/components/analytics/custom-analytics/sidebar/sidebar-header.tsx @@ -1,11 +1,10 @@ import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; - // mobx store import { useMobxStore } from "lib/mobx/store-provider"; // helpers import { renderEmoji } from "helpers/emoji.helper"; -import { renderShortDate } from "helpers/date-time.helper"; +import { renderFormattedDate } from "helpers/date-time.helper"; // constants import { NETWORK_CHOICES } from "constants/project"; @@ -37,7 +36,7 @@ export const CustomAnalyticsSidebarHeader = observer(() => {
Start Date
{cycleDetails.start_date && cycleDetails.start_date !== "" - ? renderShortDate(cycleDetails.start_date) + ? renderFormattedDate(cycleDetails.start_date) : "No start date"} @@ -45,7 +44,7 @@ export const CustomAnalyticsSidebarHeader = observer(() => {
Target Date
{cycleDetails.end_date && cycleDetails.end_date !== "" - ? renderShortDate(cycleDetails.end_date) + ? renderFormattedDate(cycleDetails.end_date) : "No end date"} @@ -63,7 +62,7 @@ export const CustomAnalyticsSidebarHeader = observer(() => {
Start Date
{moduleDetails.start_date && moduleDetails.start_date !== "" - ? renderShortDate(moduleDetails.start_date) + ? renderFormattedDate(moduleDetails.start_date) : "No start date"} @@ -71,7 +70,7 @@ export const CustomAnalyticsSidebarHeader = observer(() => {
Target Date
{moduleDetails.target_date && moduleDetails.target_date !== "" - ? renderShortDate(moduleDetails.target_date) + ? renderFormattedDate(moduleDetails.target_date) : "No end date"} diff --git a/web/components/analytics/custom-analytics/sidebar/sidebar.tsx b/web/components/analytics/custom-analytics/sidebar/sidebar.tsx index 7d1a6a3eb..951ed3602 100644 --- a/web/components/analytics/custom-analytics/sidebar/sidebar.tsx +++ b/web/components/analytics/custom-analytics/sidebar/sidebar.tsx @@ -14,7 +14,7 @@ import { Button, LayersIcon } from "@plane/ui"; // icons import { CalendarDays, Download, RefreshCw } from "lucide-react"; // helpers -import { renderShortDate } from "helpers/date-time.helper"; +import { renderFormattedDate } from "helpers/date-time.helper"; // types import { IAnalyticsParams, IAnalyticsResponse, IExportAnalyticsFormData, IWorkspace } from "types"; // fetch-keys @@ -156,7 +156,7 @@ export const CustomAnalyticsSidebar: React.FC = observer( {isProjectLevel && (
- {renderShortDate( + {renderFormattedDate( (cycleId ? cycleDetails?.created_at : moduleId diff --git a/web/components/api-token/modal/create-token-modal.tsx b/web/components/api-token/modal/create-token-modal.tsx index 65c5bf362..5df1275ba 100644 --- a/web/components/api-token/modal/create-token-modal.tsx +++ b/web/components/api-token/modal/create-token-modal.tsx @@ -48,7 +48,7 @@ export const CreateApiTokenModal: React.FC = (props) => { const csvData = { Title: data.label, Description: data.description, - Expiry: data.expired_at ? renderFormattedDate(data.expired_at) : "Never expires", + Expiry: data.expired_at ? renderFormattedDate(data.expired_at)?.replace(",", " ") ?? "" : "Never expires", "Secret key": data.token ?? "", }; diff --git a/web/components/api-token/token-list-item.tsx b/web/components/api-token/token-list-item.tsx index 148924d6f..37bb968d3 100644 --- a/web/components/api-token/token-list-item.tsx +++ b/web/components/api-token/token-list-item.tsx @@ -5,7 +5,7 @@ import { DeleteApiTokenModal } from "components/api-token"; // ui import { Tooltip } from "@plane/ui"; // helpers -import { renderFormattedDate, timeAgo } from "helpers/date-time.helper"; +import { renderFormattedDate, calculateTimeAgo } from "helpers/date-time.helper"; // types import { IApiToken } from "types/api_token"; @@ -49,7 +49,7 @@ export const ApiTokenListItem: React.FC = (props) => { ? token.expired_at ? `Expires ${renderFormattedDate(token.expired_at!)}` : "Never expires" - : `Expired ${timeAgo(token.expired_at)}`} + : `Expired ${calculateTimeAgo(token.expired_at)}`}

diff --git a/web/components/common/product-updates-modal.tsx b/web/components/common/product-updates-modal.tsx index 46be10298..cd0a5b9ff 100644 --- a/web/components/common/product-updates-modal.tsx +++ b/web/components/common/product-updates-modal.tsx @@ -1,7 +1,5 @@ import React from "react"; - import useSWR from "swr"; - // headless ui import { Dialog, Transition } from "@headlessui/react"; // services @@ -12,7 +10,7 @@ import { Loader } from "@plane/ui"; // icons import { X } from "lucide-react"; // helpers -import { renderLongDateFormat } from "helpers/date-time.helper"; +import { renderFormattedDate } from "helpers/date-time.helper"; type Props = { isOpen: boolean; @@ -69,7 +67,7 @@ export const ProductUpdatesModal: React.FC = ({ isOpen, setIsOpen }) => { {item.tag_name} - {renderLongDateFormat(item.published_at)} + {renderFormattedDate(item.published_at)} {index === 0 && ( New diff --git a/web/components/core/activity.tsx b/web/components/core/activity.tsx index 1ac34cf73..0fd9e90f1 100644 --- a/web/components/core/activity.tsx +++ b/web/components/core/activity.tsx @@ -22,7 +22,7 @@ import { UsersIcon, } from "lucide-react"; // helpers -import { renderShortDateWithYearFormat } from "helpers/date-time.helper"; +import { renderFormattedDate } from "helpers/date-time.helper"; import { capitalizeFirstLetter } from "helpers/string.helper"; // types import { IIssueActivity } from "types"; @@ -597,7 +597,7 @@ const activityDetails: { <> set the start date to{" "} - {renderShortDateWithYearFormat(activity.new_value)} + {renderFormattedDate(activity.new_value)} {showIssue && ( <> @@ -646,7 +646,7 @@ const activityDetails: { <> set the due date to{" "} - {renderShortDateWithYearFormat(activity.new_value)} + {renderFormattedDate(activity.new_value)} {showIssue && ( <> diff --git a/web/components/core/filters/date-filter-modal.tsx b/web/components/core/filters/date-filter-modal.tsx index efa787c6b..9b460bf28 100644 --- a/web/components/core/filters/date-filter-modal.tsx +++ b/web/components/core/filters/date-filter-modal.tsx @@ -2,7 +2,6 @@ import { Fragment } from "react"; import { Controller, useForm } from "react-hook-form"; import DatePicker from "react-datepicker"; import { Dialog, Transition } from "@headlessui/react"; - // components import { DateFilterSelect } from "./date-filter-select"; // ui @@ -10,7 +9,7 @@ import { Button } from "@plane/ui"; // icons import { X } from "lucide-react"; // helpers -import { renderDateFormat, renderShortDateWithYearFormat } from "helpers/date-time.helper"; +import { renderFormattedPayloadDate, renderFormattedDate } from "helpers/date-time.helper"; type Props = { title: string; @@ -39,8 +38,8 @@ export const DateFilterModal: React.FC = ({ title, handleClose, isOpen, o const handleFormSubmit = (formData: TFormValues) => { const { filterType, date1, date2 } = formData; - if (filterType === "range") onSelect([`${renderDateFormat(date1)};after`, `${renderDateFormat(date2)};before`]); - else onSelect([`${renderDateFormat(date1)};${filterType}`]); + if (filterType === "range") onSelect([`${renderFormattedPayloadDate(date1)};after`, `${renderFormattedPayloadDate(date2)};before`]); + else onSelect([`${renderFormattedPayloadDate(date1)};${filterType}`]); handleClose(); }; @@ -121,9 +120,9 @@ export const DateFilterModal: React.FC = ({ title, handleClose, isOpen, o {watch("filterType") === "range" && (
After: - {renderShortDateWithYearFormat(watch("date1"))} + {renderFormattedDate(watch("date1"))} Before: - {!isInvalid && {renderShortDateWithYearFormat(watch("date2"))}} + {!isInvalid && {renderFormattedDate(watch("date2"))}}
)}
diff --git a/web/components/core/sidebar/links-list.tsx b/web/components/core/sidebar/links-list.tsx index 037d9175b..3edcb9066 100644 --- a/web/components/core/sidebar/links-list.tsx +++ b/web/components/core/sidebar/links-list.tsx @@ -3,7 +3,7 @@ import { ExternalLinkIcon, Tooltip } from "@plane/ui"; // icons import { Pencil, Trash2, LinkIcon } from "lucide-react"; // helpers -import { timeAgo } from "helpers/date-time.helper"; +import { calculateTimeAgo } from "helpers/date-time.helper"; // types import { ILinkDetails, UserAuth } from "types"; // hooks @@ -89,7 +89,7 @@ export const LinksList: React.FC = ({ links, handleDeleteLink, handleEdit

- Added {timeAgo(link.created_at)} + Added {calculateTimeAgo(link.created_at)}
by{" "} {link.created_by_detail.is_bot diff --git a/web/components/core/sidebar/progress-chart.tsx b/web/components/core/sidebar/progress-chart.tsx index 4433e4c09..3c445760d 100644 --- a/web/components/core/sidebar/progress-chart.tsx +++ b/web/components/core/sidebar/progress-chart.tsx @@ -1,9 +1,9 @@ import React from "react"; - +import { eachDayOfInterval } from "date-fns"; // ui import { LineGraph } from "components/ui"; // helpers -import { getDatesInRange, renderShortNumericDateFormat } from "helpers/date-time.helper"; +import { renderFormattedDateWithoutYear } from "helpers/date-time.helper"; //types import { TCompletionChartDistribution } from "types"; @@ -42,25 +42,25 @@ const DashedLine = ({ series, lineGenerator, xScale, yScale }: any) => const ProgressChart: React.FC = ({ distribution, startDate, endDate, totalIssues }) => { const chartData = Object.keys(distribution).map((key) => ({ - currentDate: renderShortNumericDateFormat(key), + currentDate: renderFormattedDateWithoutYear(key), pending: distribution[key], })); const generateXAxisTickValues = () => { - const dates = getDatesInRange(startDate, endDate); + const dates = eachDayOfInterval({ start: new Date(startDate), end: new Date(endDate) }); const maxDates = 4; const totalDates = dates.length; - if (totalDates <= maxDates) return dates.map((d) => renderShortNumericDateFormat(d)); + if (totalDates <= maxDates) return dates.map((d) => renderFormattedDateWithoutYear(d)); else { const interval = Math.ceil(totalDates / maxDates); const limitedDates = []; - for (let i = 0; i < totalDates; i += interval) limitedDates.push(renderShortNumericDateFormat(dates[i])); + for (let i = 0; i < totalDates; i += interval) limitedDates.push(renderFormattedDateWithoutYear(dates[i])); - if (!limitedDates.includes(renderShortNumericDateFormat(dates[totalDates - 1]))) - limitedDates.push(renderShortNumericDateFormat(dates[totalDates - 1])); + if (!limitedDates.includes(renderFormattedDateWithoutYear(dates[totalDates - 1]))) + limitedDates.push(renderFormattedDateWithoutYear(dates[totalDates - 1])); return limitedDates; } diff --git a/web/components/cycles/active-cycle-details.tsx b/web/components/cycles/active-cycle-details.tsx index 47beaa262..c184f80c9 100644 --- a/web/components/cycles/active-cycle-details.tsx +++ b/web/components/cycles/active-cycle-details.tsx @@ -28,7 +28,7 @@ import { ViewIssueLabel } from "components/issues"; // icons import { AlarmClock, AlertTriangle, ArrowRight, CalendarDays, Star, Target } from "lucide-react"; // helpers -import { renderShortDateWithYearFormat, findHowManyDaysLeft } from "helpers/date-time.helper"; +import { renderFormattedDate, findHowManyDaysLeft } from "helpers/date-time.helper"; import { truncateText } from "helpers/string.helper"; // types import { ICycle } from "types"; @@ -267,12 +267,12 @@ export const ActiveCycleDetails: React.FC = observer((props

- {renderShortDateWithYearFormat(startDate)} + {renderFormattedDate(startDate)}
- {renderShortDateWithYearFormat(endDate)} + {renderFormattedDate(endDate)}
diff --git a/web/components/cycles/cycles-board-card.tsx b/web/components/cycles/cycles-board-card.tsx index d43d56872..ca0d487e8 100644 --- a/web/components/cycles/cycles-board-card.tsx +++ b/web/components/cycles/cycles-board-card.tsx @@ -10,7 +10,7 @@ import { Avatar, AvatarGroup, CustomMenu, Tooltip, LayersIcon, CycleGroupIcon } // icons import { Info, LinkIcon, Pencil, Star, Trash2 } from "lucide-react"; // helpers -import { findHowManyDaysLeft, renderShortDate, renderShortMonthDate } from "helpers/date-time.helper"; +import { findHowManyDaysLeft, renderFormattedDate } from "helpers/date-time.helper"; import { copyTextToClipboard } from "helpers/string.helper"; // types import { ICycle, TCycleGroups } from "types"; @@ -53,8 +53,6 @@ export const CyclesBoardCard: FC = (props) => { const currentCycle = CYCLE_STATUS.find((status) => status.value === cycleStatus); - const areYearsEqual = startDate.getFullYear() === endDate.getFullYear(); - const cycleTotalIssues = cycle.backlog_issues + cycle.unstarted_issues + @@ -228,8 +226,7 @@ export const CyclesBoardCard: FC = (props) => {
{isDateValid ? ( - {areYearsEqual ? renderShortDate(startDate, "_ _") : renderShortMonthDate(startDate, "_ _")} -{" "} - {areYearsEqual ? renderShortDate(endDate, "_ _") : renderShortMonthDate(endDate, "_ _")} + {renderFormattedDate(startDate) ?? "_ _"} - {renderFormattedDate(endDate) ?? "_ _"} ) : ( No due date diff --git a/web/components/cycles/cycles-list-item.tsx b/web/components/cycles/cycles-list-item.tsx index 9ea26ab39..cb1c53f41 100644 --- a/web/components/cycles/cycles-list-item.tsx +++ b/web/components/cycles/cycles-list-item.tsx @@ -1,7 +1,6 @@ import { FC, MouseEvent, useState } from "react"; import Link from "next/link"; import { useRouter } from "next/router"; - // stores import { useMobxStore } from "lib/mobx/store-provider"; // hooks @@ -13,7 +12,7 @@ import { CustomMenu, Tooltip, CircularProgressIndicator, CycleGroupIcon, AvatarG // icons import { Check, Info, LinkIcon, Pencil, Star, Trash2, User2 } from "lucide-react"; // helpers -import { findHowManyDaysLeft, renderShortDate, renderShortMonthDate } from "helpers/date-time.helper"; +import { findHowManyDaysLeft, renderFormattedDate } from "helpers/date-time.helper"; import { copyTextToClipboard } from "helpers/string.helper"; // types import { ICycle, TCycleGroups } from "types"; @@ -64,8 +63,6 @@ export const CyclesListItem: FC = (props) => { const renderDate = cycle.start_date || cycle.end_date; - const areYearsEqual = startDate.getFullYear() === endDate.getFullYear(); - const completionPercentage = (cycle.completed_issues / cycleTotalIssues) * 100; const progress = isNaN(completionPercentage) ? 0 : Math.floor(completionPercentage); @@ -204,10 +201,8 @@ export const CyclesListItem: FC = (props) => {
{renderDate && ( - - {areYearsEqual ? renderShortDate(startDate, "_ _") : renderShortMonthDate(startDate, "_ _")} - {" - "} - {areYearsEqual ? renderShortDate(endDate, "_ _") : renderShortMonthDate(endDate, "_ _")} + + {renderFormattedDate(startDate) ?? "_ _"} - {renderFormattedDate(endDate) ?? "_ _"} )} diff --git a/web/components/cycles/gantt-chart/blocks.tsx b/web/components/cycles/gantt-chart/blocks.tsx index 76a4d9235..8f05c45ab 100644 --- a/web/components/cycles/gantt-chart/blocks.tsx +++ b/web/components/cycles/gantt-chart/blocks.tsx @@ -1,9 +1,8 @@ import { useRouter } from "next/router"; - // ui import { Tooltip, ContrastIcon } from "@plane/ui"; // helpers -import { renderShortDate } from "helpers/date-time.helper"; +import { renderFormattedDate } from "helpers/date-time.helper"; // types import { ICycle } from "types"; @@ -35,7 +34,7 @@ export const CycleGanttBlock = ({ data }: { data: ICycle }) => {
{data?.name}
- {renderShortDate(data?.start_date ?? "")} to {renderShortDate(data?.end_date ?? "")} + {renderFormattedDate(data?.start_date ?? "")} to {renderFormattedDate(data?.end_date ?? "")}
} diff --git a/web/components/cycles/sidebar.tsx b/web/components/cycles/sidebar.tsx index f3576fb00..e73168008 100644 --- a/web/components/cycles/sidebar.tsx +++ b/web/components/cycles/sidebar.tsx @@ -32,9 +32,8 @@ import { copyUrlToClipboard } from "helpers/string.helper"; import { findHowManyDaysLeft, isDateGreaterThanToday, - renderDateFormat, - renderShortDate, - renderShortMonthDate, + renderFormattedPayloadDate, + renderFormattedDate, } from "helpers/date-time.helper"; // types import { ICycle, IIssueFilterOptions } from "types"; @@ -141,8 +140,8 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { if (isDateValidForExistingCycle) { submitChanges({ - start_date: renderDateFormat(`${watch("start_date")}`), - end_date: renderDateFormat(`${watch("end_date")}`), + start_date: renderFormattedPayloadDate(`${watch("start_date")}`), + end_date: renderFormattedPayloadDate(`${watch("end_date")}`), }); setToastAlert({ type: "success", @@ -168,8 +167,8 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { if (isDateValid) { submitChanges({ - start_date: renderDateFormat(`${watch("start_date")}`), - end_date: renderDateFormat(`${watch("end_date")}`), + start_date: renderFormattedPayloadDate(`${watch("start_date")}`), + end_date: renderFormattedPayloadDate(`${watch("end_date")}`), }); setToastAlert({ type: "success", @@ -209,8 +208,8 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { if (isDateValidForExistingCycle) { submitChanges({ - start_date: renderDateFormat(`${watch("start_date")}`), - end_date: renderDateFormat(`${watch("end_date")}`), + start_date: renderFormattedPayloadDate(`${watch("start_date")}`), + end_date: renderFormattedPayloadDate(`${watch("end_date")}`), }); setToastAlert({ type: "success", @@ -236,8 +235,8 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { if (isDateValid) { submitChanges({ - start_date: renderDateFormat(`${watch("start_date")}`), - end_date: renderDateFormat(`${watch("end_date")}`), + start_date: renderFormattedPayloadDate(`${watch("start_date")}`), + end_date: renderFormattedPayloadDate(`${watch("end_date")}`), }); setToastAlert({ type: "success", @@ -302,9 +301,6 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { const endDate = new Date(watch("end_date") ?? cycleDetails.end_date ?? ""); const startDate = new Date(watch("start_date") ?? cycleDetails.start_date ?? ""); - const areYearsEqual = - startDate.getFullYear() === endDate.getFullYear() || isNaN(startDate.getFullYear()) || isNaN(endDate.getFullYear()); - const currentCycle = CYCLE_STATUS.find((status) => status.value === cycleStatus); const issueCount = @@ -396,19 +392,17 @@ export const CycleDetailsSidebar: React.FC = observer((props) => {
- {areYearsEqual - ? renderShortDate(startDate, "No date selected") - : renderShortMonthDate(startDate, "No date selected")} + {renderFormattedDate(startDate) ?? "No date selected"} @@ -450,19 +444,17 @@ export const CycleDetailsSidebar: React.FC = observer((props) => { <> - {areYearsEqual - ? renderShortDate(endDate, "No date selected") - : renderShortMonthDate(endDate, "No date selected")} + {renderFormattedDate(endDate) ?? "No date selected"} diff --git a/web/components/cycles/transfer-issues-modal.tsx b/web/components/cycles/transfer-issues-modal.tsx index dd462e360..f47c1ddaa 100644 --- a/web/components/cycles/transfer-issues-modal.tsx +++ b/web/components/cycles/transfer-issues-modal.tsx @@ -136,7 +136,7 @@ export const TransferIssuesModal: React.FC = observer(({ isOpen, handleCl
{option?.name} - {option.status} + {option.status.toLocaleLowerCase()}
diff --git a/web/components/exporter/single-export.tsx b/web/components/exporter/single-export.tsx index a72d05bf2..d2502cefb 100644 --- a/web/components/exporter/single-export.tsx +++ b/web/components/exporter/single-export.tsx @@ -2,7 +2,7 @@ import { useState, FC } from "react"; // ui import { Button } from "@plane/ui"; // helpers -import { renderShortDateWithYearFormat } from "helpers/date-time.helper"; +import { renderFormattedDate } from "helpers/date-time.helper"; // types import { IExportData } from "types"; @@ -50,7 +50,7 @@ export const SingleExport: FC = ({ service, refreshing }) => {
- {renderShortDateWithYearFormat(service.created_at)}| + {renderFormattedDate(service.created_at)}| Exported by {service?.initiated_by_detail?.display_name}
diff --git a/web/components/gantt-chart/blocks/blocks-display.tsx b/web/components/gantt-chart/blocks/blocks-display.tsx index 52a997909..0c368090d 100644 --- a/web/components/gantt-chart/blocks/blocks-display.tsx +++ b/web/components/gantt-chart/blocks/blocks-display.tsx @@ -1,10 +1,9 @@ import { FC } from "react"; - // hooks import { useChart } from "../hooks"; // helpers import { ChartDraggable } from "../helpers/draggable"; -import { renderDateFormat } from "helpers/date-time.helper"; +import { renderFormattedPayloadDate } from "helpers/date-time.helper"; // types import { IBlockUpdateData, IGanttBlock } from "../types"; @@ -64,8 +63,8 @@ export const GanttChartBlocks: FC = (props) => { // call the block update handler with the updated dates blockUpdateHandler(block.data, { - start_date: renderDateFormat(updatedStartDate), - target_date: renderDateFormat(updatedTargetDate), + start_date: renderFormattedPayloadDate(updatedStartDate) ?? undefined, + target_date: renderFormattedPayloadDate(updatedTargetDate) ?? undefined, }); }; diff --git a/web/components/gantt-chart/sidebar/cycle-sidebar.tsx b/web/components/gantt-chart/sidebar/cycle-sidebar.tsx index 13d56e7f5..b7cb41837 100644 --- a/web/components/gantt-chart/sidebar/cycle-sidebar.tsx +++ b/web/components/gantt-chart/sidebar/cycle-sidebar.tsx @@ -94,7 +94,7 @@ export const CycleGanttSidebar: React.FC = (props) => { <> {blocks ? ( blocks.map((block, index) => { - const duration = findTotalDaysInRange(block.start_date ?? "", block.target_date ?? "", true); + const duration = findTotalDaysInRange(block.start_date ?? "", block.target_date ?? ""); return ( = (props) => { <> {blocks ? ( blocks.map((block, index) => { - const duration = findTotalDaysInRange(block.start_date ?? "", block.target_date ?? "", true); + const duration = findTotalDaysInRange(block.start_date ?? "", block.target_date ?? ""); return ( = (props) => { <> {blocks ? ( blocks.map((block, index) => { - const duration = findTotalDaysInRange(block.start_date ?? "", block.target_date ?? "", true); + const duration = findTotalDaysInRange(block.start_date ?? "", block.target_date ?? ""); return ( = (props) => { <> {blocks ? ( blocks.map((block, index) => { - const duration = findTotalDaysInRange(block.start_date ?? "", block.target_date ?? "", true); + const duration = findTotalDaysInRange(block.start_date ?? "", block.target_date ?? ""); return ( = (props) => {
- {renderShortDateWithYearFormat(issue.created_at ?? "")} + {renderFormattedDate(issue.created_at ?? "")}
diff --git a/web/components/inbox/main-content.tsx b/web/components/inbox/main-content.tsx index 3a0faf248..a5d4e18a7 100644 --- a/web/components/inbox/main-content.tsx +++ b/web/components/inbox/main-content.tsx @@ -4,7 +4,6 @@ import { observer } from "mobx-react-lite"; import useSWR from "swr"; import { useForm } from "react-hook-form"; import { AlertTriangle, CheckCircle2, Clock, Copy, ExternalLink, Inbox, XCircle } from "lucide-react"; - // mobx store import { useMobxStore } from "lib/mobx/store-provider"; // components @@ -13,7 +12,7 @@ import { InboxIssueActivity } from "components/inbox"; // ui import { Loader, StateGroupIcon } from "@plane/ui"; // helpers -import { renderShortDateWithYearFormat } from "helpers/date-time.helper"; +import { renderFormattedDate } from "helpers/date-time.helper"; // types import { IInboxIssue, IIssue } from "types"; import { EUserWorkspaceRoles } from "constants/workspace"; @@ -193,12 +192,12 @@ export const InboxMainContent: React.FC = observer(() => { {new Date(issueDetails.issue_inbox[0].snoozed_till ?? "") < new Date() ? (

This issue was snoozed till{" "} - {renderShortDateWithYearFormat(issueDetails.issue_inbox[0].snoozed_till ?? "")}. + {renderFormattedDate(issueDetails.issue_inbox[0].snoozed_till ?? "")}.

) : (

This issue has been snoozed till{" "} - {renderShortDateWithYearFormat(issueDetails.issue_inbox[0].snoozed_till ?? "")}. + {renderFormattedDate(issueDetails.issue_inbox[0].snoozed_till ?? "")}.

)} diff --git a/web/components/integration/single-import.tsx b/web/components/integration/single-import.tsx index 433747f31..18eeef3ac 100644 --- a/web/components/integration/single-import.tsx +++ b/web/components/integration/single-import.tsx @@ -3,7 +3,7 @@ import { CustomMenu } from "@plane/ui"; // icons import { Trash2 } from "lucide-react"; // helpers -import { renderShortDateWithYearFormat } from "helpers/date-time.helper"; +import { renderFormattedDate } from "helpers/date-time.helper"; // types import { IImporterService } from "types"; // constants @@ -39,7 +39,7 @@ export const SingleImport: React.FC = ({ service, refreshing, handleDelet
- {renderShortDateWithYearFormat(service.created_at)}| + {renderFormattedDate(service.created_at)}| Imported by {service.initiated_by_detail.display_name}
diff --git a/web/components/issues/activity.tsx b/web/components/issues/activity.tsx index b2831ab66..09130ead5 100644 --- a/web/components/issues/activity.tsx +++ b/web/components/issues/activity.tsx @@ -9,7 +9,7 @@ import { CommentCard } from "components/issues/comment"; // ui import { Loader, Tooltip } from "@plane/ui"; // helpers -import { render24HourFormatTime, renderLongDateFormat, timeAgo } from "helpers/date-time.helper"; +import { renderFormattedTime, renderFormattedDate, calculateTimeAgo } from "helpers/date-time.helper"; // types import { IIssueActivity } from "types"; import { History } from "lucide-react"; @@ -114,11 +114,11 @@ export const IssueActivitySection: React.FC = ({ )}{" "} {message}{" "} - {timeAgo(activityItem.created_at)} + {calculateTimeAgo(activityItem.created_at)} diff --git a/web/components/issues/attachment/attachments.tsx b/web/components/issues/attachment/attachments.tsx index 1b4915579..da751cbe3 100644 --- a/web/components/issues/attachment/attachments.tsx +++ b/web/components/issues/attachment/attachments.tsx @@ -15,7 +15,7 @@ import { ProjectMemberService } from "services/project"; import { ISSUE_ATTACHMENTS, PROJECT_MEMBERS } from "constants/fetch-keys"; // helper import { truncateText } from "helpers/string.helper"; -import { renderLongDateFormat } from "helpers/date-time.helper"; +import { renderFormattedDate } from "helpers/date-time.helper"; import { convertBytesToSize, getFileExtension, getFileName } from "helpers/attachment.helper"; // type import { IIssueAttachment } from "types"; @@ -77,7 +77,7 @@ export const IssueAttachments: React.FC = (props) => { person.member.id === file.updated_by)?.member.display_name ?? "" - } uploaded on ${renderLongDateFormat(file.updated_at)}`} + } uploaded on ${renderFormattedDate(file.updated_at)}`} > diff --git a/web/components/issues/comment/comment-card.tsx b/web/components/issues/comment/comment-card.tsx index 09f29da73..fa6e42787 100644 --- a/web/components/issues/comment/comment-card.tsx +++ b/web/components/issues/comment/comment-card.tsx @@ -1,21 +1,20 @@ import React, { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; - // services import { FileService } from "services/file.service"; // icons import { Check, Globe2, Lock, MessageSquare, Pencil, Trash2, X } from "lucide-react"; // hooks import useUser from "hooks/use-user"; +import useEditorSuggestions from "hooks/use-editor-suggestions"; // ui import { CustomMenu } from "@plane/ui"; import { CommentReaction } from "components/issues"; import { LiteTextEditorWithRef, LiteReadOnlyEditorWithRef } from "@plane/lite-text-editor"; // helpers -import { timeAgo } from "helpers/date-time.helper"; +import { calculateTimeAgo } from "helpers/date-time.helper"; // types import type { IIssueActivity } from "types"; -import useEditorSuggestions from "hooks/use-editor-suggestions"; // services const fileService = new FileService(); @@ -98,7 +97,7 @@ export const CommentCard: React.FC = ({
{comment.actor_detail.is_bot ? comment.actor_detail.first_name + " Bot" : comment.actor_detail.display_name}
-

commented {timeAgo(comment.created_at)}

+

commented {calculateTimeAgo(comment.created_at)}

diff --git a/web/components/issues/issue-layouts/calendar/day-tile.tsx b/web/components/issues/issue-layouts/calendar/day-tile.tsx index 36caaff20..7e5bda981 100644 --- a/web/components/issues/issue-layouts/calendar/day-tile.tsx +++ b/web/components/issues/issue-layouts/calendar/day-tile.tsx @@ -4,7 +4,7 @@ import { Droppable } from "@hello-pangea/dnd"; // components import { CalendarIssueBlocks, ICalendarDate, CalendarQuickAddIssueForm } from "components/issues"; // helpers -import { renderDateFormat } from "helpers/date-time.helper"; +import { renderFormattedPayloadDate } from "helpers/date-time.helper"; // constants import { MONTHS_LIST } from "constants/calendar"; import { IIssue } from "types"; @@ -52,7 +52,9 @@ export const CalendarDayTile: React.FC = observer((props) => { const [showAllIssues, setShowAllIssues] = useState(false); const calendarLayout = issuesFilterStore?.issueFilters?.displayFilters?.calendar?.layout ?? "month"; - const issueIdList = groupedIssueIds ? groupedIssueIds[renderDateFormat(date.date)] : null; + const formattedDatePayload = renderFormattedPayloadDate(date.date); + if (!formattedDatePayload) return null; + const issueIdList = groupedIssueIds ? groupedIssueIds[formattedDatePayload] : null; const totalIssues = issueIdList?.length ?? 0; return ( @@ -78,7 +80,7 @@ export const CalendarDayTile: React.FC = observer((props) => { {/* content */}
- + {(provided, snapshot) => (
= observer((props) => {
= observer((props) => { return ( = observer((props) => { if (dateParts.length === 2) { const [date, time] = dateParts; - dateLabel = `${capitalizeFirstLetter(time)} ${renderLongDateFormat(date)}`; + dateLabel = `${capitalizeFirstLetter(time)} ${renderFormattedDate(date)}`; } } diff --git a/web/components/issues/issue-layouts/gantt/blocks.tsx b/web/components/issues/issue-layouts/gantt/blocks.tsx index 41085978a..fe46a2a5d 100644 --- a/web/components/issues/issue-layouts/gantt/blocks.tsx +++ b/web/components/issues/issue-layouts/gantt/blocks.tsx @@ -2,7 +2,7 @@ import { useRouter } from "next/router"; // ui import { Tooltip, StateGroupIcon } from "@plane/ui"; // helpers -import { renderShortDate } from "helpers/date-time.helper"; +import { renderFormattedDate } from "helpers/date-time.helper"; // types import { IIssue } from "types"; @@ -34,15 +34,13 @@ export const IssueGanttBlock = ({ data }: { data: IIssue }) => {
{data?.name}
- {renderShortDate(data?.start_date ?? "")} to {renderShortDate(data?.target_date ?? "")} + {renderFormattedDate(data?.start_date ?? "")} to {renderFormattedDate(data?.target_date ?? "")}
} position="top-left" > - -
{data?.name}
-
+
{data?.name}
); diff --git a/web/components/issues/issue-layouts/gantt/quick-add-issue-form.tsx b/web/components/issues/issue-layouts/gantt/quick-add-issue-form.tsx index e4a88e944..394cc35c1 100644 --- a/web/components/issues/issue-layouts/gantt/quick-add-issue-form.tsx +++ b/web/components/issues/issue-layouts/gantt/quick-add-issue-form.tsx @@ -11,11 +11,10 @@ import useKeypress from "hooks/use-keypress"; import useProjectDetails from "hooks/use-project-details"; import useOutsideClickDetector from "hooks/use-outside-click-detector"; // helpers -import { renderDateFormat } from "helpers/date-time.helper"; +import { renderFormattedPayloadDate } from "helpers/date-time.helper"; +import { createIssuePayload } from "helpers/issue.helper"; // types import { IIssue } from "types"; -// helpers -import { createIssuePayload } from "helpers/issue.helper"; type Props = { prePopulatedData?: Partial; @@ -116,8 +115,8 @@ export const GanttInlineCreateIssueForm: React.FC = observer((props) => { const payload = createIssuePayload(workspaceDetail!, projectDetails!, { ...(prePopulatedData ?? {}), ...formData, - start_date: renderDateFormat(new Date()), - target_date: renderDateFormat(new Date(new Date().getTime() + 24 * 60 * 60 * 1000)), + start_date: renderFormattedPayloadDate(new Date()), + target_date: renderFormattedPayloadDate(new Date(new Date().getTime() + 24 * 60 * 60 * 1000)), }); try { diff --git a/web/components/issues/issue-layouts/properties/date.tsx b/web/components/issues/issue-layouts/properties/date.tsx index d0bb29711..b66d2e5b6 100644 --- a/web/components/issues/issue-layouts/properties/date.tsx +++ b/web/components/issues/issue-layouts/properties/date.tsx @@ -12,7 +12,7 @@ import { Tooltip } from "@plane/ui"; // hooks import useDynamicDropdownPosition from "hooks/use-dynamic-dropdown"; // helpers -import { renderDateFormat, renderFormattedDate } from "helpers/date-time.helper"; +import { renderFormattedPayloadDate, renderFormattedDate } from "helpers/date-time.helper"; export interface IIssuePropertyDate { value: string | null; @@ -105,7 +105,7 @@ export const IssuePropertyDate: React.FC = observer((props) onChange={(val, e) => { e?.stopPropagation(); if (onChange && val) { - onChange(renderDateFormat(val)); + onChange(renderFormattedPayloadDate(val)); close(); } }} diff --git a/web/components/issues/issue-layouts/spreadsheet/columns/created-on-column.tsx b/web/components/issues/issue-layouts/spreadsheet/columns/created-on-column.tsx index dfe0e5513..5db7fcd03 100644 --- a/web/components/issues/issue-layouts/spreadsheet/columns/created-on-column.tsx +++ b/web/components/issues/issue-layouts/spreadsheet/columns/created-on-column.tsx @@ -1,9 +1,8 @@ import React from "react"; - // hooks import useSubIssue from "hooks/use-sub-issue"; // helpers -import { renderLongDetailDateFormat } from "helpers/date-time.helper"; +import { renderFormattedDate } from "helpers/date-time.helper"; // types import { IIssue } from "types"; @@ -20,7 +19,7 @@ export const SpreadsheetCreatedOnColumn: React.FC = ({ issue, expandedIss return ( <>
- {renderLongDetailDateFormat(issue.created_at)} + {renderFormattedDate(issue.created_at)}
{isExpanded && diff --git a/web/components/issues/issue-layouts/spreadsheet/columns/updated-on-column.tsx b/web/components/issues/issue-layouts/spreadsheet/columns/updated-on-column.tsx index 97cc0fcff..554f5f82c 100644 --- a/web/components/issues/issue-layouts/spreadsheet/columns/updated-on-column.tsx +++ b/web/components/issues/issue-layouts/spreadsheet/columns/updated-on-column.tsx @@ -1,9 +1,8 @@ import React from "react"; - // hooks import useSubIssue from "hooks/use-sub-issue"; // helpers -import { renderLongDetailDateFormat } from "helpers/date-time.helper"; +import { renderFormattedDate } from "helpers/date-time.helper"; // types import { IIssue } from "types"; @@ -22,7 +21,7 @@ export const SpreadsheetUpdatedOnColumn: React.FC = (props) => { return ( <>
- {renderLongDetailDateFormat(issue.updated_at)} + {renderFormattedDate(issue.updated_at)}
{isExpanded && diff --git a/web/components/issues/peek-overview/activity/card.tsx b/web/components/issues/peek-overview/activity/card.tsx index 86d1a138c..cf9dca381 100644 --- a/web/components/issues/peek-overview/activity/card.tsx +++ b/web/components/issues/peek-overview/activity/card.tsx @@ -7,7 +7,7 @@ import { Loader, Tooltip } from "@plane/ui"; import { ActivityIcon, ActivityMessage } from "components/core"; import { IssueCommentCard } from "./comment-card"; // helpers -import { render24HourFormatTime, renderLongDateFormat, timeAgo } from "helpers/date-time.helper"; +import { renderFormattedTime, renderFormattedDate, calculateTimeAgo } from "helpers/date-time.helper"; // types import { IIssueActivity, IUser } from "types"; @@ -104,11 +104,11 @@ export const IssueActivityCard: FC = (props) => { )} {message} - {timeAgo(activityItem.created_at)} + {calculateTimeAgo(activityItem.created_at)}
diff --git a/web/components/issues/peek-overview/activity/comment-card.tsx b/web/components/issues/peek-overview/activity/comment-card.tsx index be8915ad2..12094c13b 100644 --- a/web/components/issues/peek-overview/activity/comment-card.tsx +++ b/web/components/issues/peek-overview/activity/comment-card.tsx @@ -10,7 +10,7 @@ import { LiteTextEditorWithRef, LiteReadOnlyEditorWithRef } from "@plane/lite-te // components import { IssueCommentReaction } from "./comment-reaction"; // helpers -import { timeAgo } from "helpers/date-time.helper"; +import { calculateTimeAgo } from "helpers/date-time.helper"; // types import type { IIssueActivity, IUser } from "types"; @@ -106,7 +106,7 @@ export const IssueCommentCard: React.FC = (props) => {
{comment.actor_detail.is_bot ? comment.actor_detail.first_name + " Bot" : comment.actor_detail.display_name}
-

commented {timeAgo(comment.created_at)}

+

commented {calculateTimeAgo(comment.created_at)}

diff --git a/web/components/issues/select/date.tsx b/web/components/issues/select/date.tsx index 3c2bb14d8..2c8774564 100644 --- a/web/components/issues/select/date.tsx +++ b/web/components/issues/select/date.tsx @@ -4,8 +4,8 @@ import { Popover, Transition } from "@headlessui/react"; import { CalendarDays, X } from "lucide-react"; // react-datepicker import DatePicker from "react-datepicker"; -// import "react-datepicker/dist/react-datepicker.css"; -import { renderDateFormat, renderShortDateWithYearFormat } from "helpers/date-time.helper"; +// helpers +import { renderFormattedPayloadDate, renderFormattedDate } from "helpers/date-time.helper"; type Props = { label: string; @@ -35,7 +35,7 @@ export const IssueDateSelect: FC = ({ label, maxDate, minDate, onChange, {value ? ( <> - {renderShortDateWithYearFormat(value)} + {renderFormattedDate(value)} @@ -69,7 +69,7 @@ export const IssueDateSelect: FC = ({ label, maxDate, minDate, onChange, selected={value ? new Date(value) : null} onChange={(val) => { if (!val) onChange(""); - else onChange(renderDateFormat(val)); + else onChange(renderFormattedPayloadDate(val)); close(); }} diff --git a/web/components/issues/view-select/due-date.tsx b/web/components/issues/view-select/due-date.tsx index 5b2bfb0ec..b9d59ec2c 100644 --- a/web/components/issues/view-select/due-date.tsx +++ b/web/components/issues/view-select/due-date.tsx @@ -3,12 +3,7 @@ import { CustomDatePicker } from "components/ui"; import { Tooltip } from "@plane/ui"; import { CalendarCheck } from "lucide-react"; // helpers -import { - findHowManyDaysLeft, - renderShortDate, - renderShortDateWithYearFormat, - renderShortMonthDate, -} from "helpers/date-time.helper"; +import { findHowManyDaysLeft, renderFormattedDate } from "helpers/date-time.helper"; // types import { IIssue } from "types"; @@ -36,14 +31,10 @@ export const ViewDueDateSelect: React.FC = ({ const minDate = issue.start_date ? new Date(issue.start_date) : null; minDate?.setDate(minDate.getDate()); - const today = new Date(); - const endDate = new Date(issue.target_date ?? ""); - const areYearsEqual = endDate.getFullYear() === today.getFullYear(); - return (
= ({ {issue.target_date ? ( <> - - {areYearsEqual - ? renderShortDate(issue.target_date ?? "", "_ _") - : renderShortMonthDate(issue.target_date ?? "", "_ _")} - + {renderFormattedDate(issue.target_date) ?? "_ _"} ) : ( <> diff --git a/web/components/issues/view-select/start-date.tsx b/web/components/issues/view-select/start-date.tsx index 8cfac4a64..6b55ab134 100644 --- a/web/components/issues/view-select/start-date.tsx +++ b/web/components/issues/view-select/start-date.tsx @@ -3,7 +3,7 @@ import { CustomDatePicker } from "components/ui"; import { Tooltip } from "@plane/ui"; import { CalendarClock } from "lucide-react"; // helpers -import { renderShortDate, renderShortDateWithYearFormat, renderShortMonthDate } from "helpers/date-time.helper"; +import { renderFormattedDate } from "helpers/date-time.helper"; // types import { IIssue } from "types"; @@ -30,14 +30,11 @@ export const ViewStartDateSelect: React.FC = ({ }) => { const maxDate = issue.target_date ? new Date(issue.target_date) : null; maxDate?.setDate(maxDate.getDate()); - const today = new Date(); - const startDate = new Date(issue.start_date ?? ""); - const areYearsEqual = startDate.getFullYear() === today.getFullYear(); return (
@@ -56,11 +53,7 @@ export const ViewStartDateSelect: React.FC = ({ {issue?.start_date ? ( <> - - {areYearsEqual - ? renderShortDate(issue?.start_date, "_ _") - : renderShortMonthDate(issue?.start_date, "_ _")} - + {renderFormattedDate(issue?.start_date ?? "_ _")} ) : ( <> diff --git a/web/components/modules/gantt-chart/blocks.tsx b/web/components/modules/gantt-chart/blocks.tsx index 957437d08..f66283b7f 100644 --- a/web/components/modules/gantt-chart/blocks.tsx +++ b/web/components/modules/gantt-chart/blocks.tsx @@ -1,9 +1,8 @@ import { useRouter } from "next/router"; - // ui import { Tooltip, ModuleStatusIcon } from "@plane/ui"; // helpers -import { renderShortDate } from "helpers/date-time.helper"; +import { renderFormattedDate } from "helpers/date-time.helper"; // types import { IModule } from "types"; // constants @@ -25,7 +24,7 @@ export const ModuleGanttBlock = ({ data }: { data: IModule }) => {
{data?.name}
- {renderShortDate(data?.start_date ?? "")} to {renderShortDate(data?.target_date ?? "")} + {renderFormattedDate(data?.start_date ?? "")} to {renderFormattedDate(data?.target_date ?? "")}
} diff --git a/web/components/modules/module-card-item.tsx b/web/components/modules/module-card-item.tsx index eb65084c1..a9cc55507 100644 --- a/web/components/modules/module-card-item.tsx +++ b/web/components/modules/module-card-item.tsx @@ -14,7 +14,7 @@ import { Avatar, AvatarGroup, CustomMenu, LayersIcon, Tooltip } from "@plane/ui" import { Info, LinkIcon, Pencil, Star, Trash2 } from "lucide-react"; // helpers import { copyUrlToClipboard } from "helpers/string.helper"; -import { renderShortDate, renderShortMonthDate } from "helpers/date-time.helper"; +import { renderFormattedDate } from "helpers/date-time.helper"; // types import { IModule } from "types"; // constants @@ -56,8 +56,6 @@ export const ModuleCardItem: React.FC = observer((props) => { const isDateValid = module.target_date || module.start_date; - const areYearsEqual = startDate.getFullYear() === endDate.getFullYear(); - const moduleStatus = MODULE_STATUS.find((status) => status.value === module.status); const issueCount = module @@ -213,8 +211,7 @@ export const ModuleCardItem: React.FC = observer((props) => { {isDateValid ? ( <> - {areYearsEqual ? renderShortDate(startDate, "_ _") : renderShortMonthDate(startDate, "_ _")} -{" "} - {areYearsEqual ? renderShortDate(endDate, "_ _") : renderShortMonthDate(endDate, "_ _")} + {renderFormattedDate(startDate) ?? "_ _"} - {renderFormattedDate(endDate) ?? "_ _"} ) : ( diff --git a/web/components/modules/module-list-item.tsx b/web/components/modules/module-list-item.tsx index 005d8beba..d2ab819fe 100644 --- a/web/components/modules/module-list-item.tsx +++ b/web/components/modules/module-list-item.tsx @@ -14,7 +14,7 @@ import { Avatar, AvatarGroup, CircularProgressIndicator, CustomMenu, Tooltip } f import { Check, Info, LinkIcon, Pencil, Star, Trash2, User2 } from "lucide-react"; // helpers import { copyUrlToClipboard } from "helpers/string.helper"; -import { renderShortDate, renderShortMonthDate } from "helpers/date-time.helper"; +import { renderFormattedDate } from "helpers/date-time.helper"; // types import { IModule } from "types"; // constants @@ -49,8 +49,6 @@ export const ModuleListItem: React.FC = observer((props) => { const renderDate = module.start_date || module.target_date; - const areYearsEqual = startDate.getFullYear() === endDate.getFullYear(); - const moduleStatus = MODULE_STATUS.find((status) => status.value === module.status); const progress = isNaN(completionPercentage) ? 0 : Math.floor(completionPercentage); @@ -176,10 +174,8 @@ export const ModuleListItem: React.FC = observer((props) => {
{renderDate && ( - - {areYearsEqual ? renderShortDate(startDate, "_ _") : renderShortMonthDate(startDate, "_ _")} - {" - "} - {areYearsEqual ? renderShortDate(endDate, "_ _") : renderShortMonthDate(endDate, "_ _")} + + {renderFormattedDate(startDate) ?? "_ _"} - {renderFormattedDate(endDate) ?? "_ _"} )} diff --git a/web/components/modules/sidebar.tsx b/web/components/modules/sidebar.tsx index 3fe7bf57e..28cb13b5b 100644 --- a/web/components/modules/sidebar.tsx +++ b/web/components/modules/sidebar.tsx @@ -27,12 +27,7 @@ import { Trash2, } from "lucide-react"; // helpers -import { - isDateGreaterThanToday, - renderDateFormat, - renderShortDate, - renderShortMonthDate, -} from "helpers/date-time.helper"; +import { isDateGreaterThanToday, renderFormattedPayloadDate, renderFormattedDate } from "helpers/date-time.helper"; import { copyUrlToClipboard } from "helpers/string.helper"; // types import { IIssueFilterOptions, ILinkDetails, IModule, ModuleLink } from "types"; @@ -187,8 +182,8 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => { } submitChanges({ - start_date: renderDateFormat(`${watch("start_date")}`), - target_date: renderDateFormat(`${watch("target_date")}`), + start_date: renderFormattedPayloadDate(`${watch("start_date")}`), + target_date: renderFormattedPayloadDate(`${watch("target_date")}`), }); setToastAlert({ type: "success", @@ -212,8 +207,8 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => { } submitChanges({ - start_date: renderDateFormat(`${watch("start_date")}`), - target_date: renderDateFormat(`${watch("target_date")}`), + start_date: renderFormattedPayloadDate(`${watch("start_date")}`), + target_date: renderFormattedPayloadDate(`${watch("target_date")}`), }); setToastAlert({ type: "success", @@ -285,9 +280,6 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => { const startDate = new Date(watch("start_date") ?? moduleDetails.start_date ?? ""); const endDate = new Date(watch("target_date") ?? moduleDetails.target_date ?? ""); - const areYearsEqual = - startDate.getFullYear() === endDate.getFullYear() || isNaN(startDate.getFullYear()) || isNaN(endDate.getFullYear()); - const moduleStatus = MODULE_STATUS.find((status) => status.value === moduleDetails.status); const issueCount = @@ -400,19 +392,17 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => {
- {areYearsEqual - ? renderShortDate(startDate, "No date selected") - : renderShortMonthDate(startDate, "No date selected")} + {renderFormattedDate(startDate) ?? "No date selected"} @@ -453,19 +443,17 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => { <> - {areYearsEqual - ? renderShortDate(endDate, "No date selected") - : renderShortMonthDate(endDate, "No date selected")} + {renderFormattedDate(endDate) ?? "No date selected"} diff --git a/web/components/notifications/notification-card.tsx b/web/components/notifications/notification-card.tsx index 17f8f0f96..bf0ae81bf 100644 --- a/web/components/notifications/notification-card.tsx +++ b/web/components/notifications/notification-card.tsx @@ -11,11 +11,9 @@ import { snoozeOptions } from "constants/notification"; // helper import { replaceUnderscoreIfSnakeCase, truncateText, stripAndTruncateHTML } from "helpers/string.helper"; import { - formatDateDistance, - render12HourFormatTime, - renderLongDateFormat, - renderShortDate, - renderShortDateWithYearFormat, + calculateTimeAgo, + renderFormattedTime, + renderFormattedDate, } from "helpers/date-time.helper"; // type import type { IUserNotification } from "types"; @@ -112,7 +110,7 @@ export const NotificationCard: React.FC = (props) => { {notification.data.issue_activity.field !== "None" ? ( notification.data.issue_activity.field !== "comment" ? ( notification.data.issue_activity.field === "target_date" ? ( - renderShortDateWithYearFormat(notification.data.issue_activity.new_value) + renderFormattedDate(notification.data.issue_activity.new_value) ) : notification.data.issue_activity.field === "attachment" ? ( "the issue" ) : notification.data.issue_activity.field === "description" ? ( @@ -151,11 +149,11 @@ export const NotificationCard: React.FC = (props) => {

- Till {renderShortDate(notification.snoozed_till)}, {render12HourFormatTime(notification.snoozed_till)} + Till {renderFormattedDate(notification.snoozed_till)}, {renderFormattedTime(notification.snoozed_till, '12-hour')}

) : ( -

{formatDateDistance(notification.created_at)}

+

{calculateTimeAgo(notification.created_at)}

)}
@@ -233,7 +231,7 @@ export const NotificationCard: React.FC = (props) => { markSnoozeNotification(notification.id, item.value).then(() => { setToastAlert({ - title: `Notification snoozed till ${renderLongDateFormat(item.value)}`, + title: `Notification snoozed till ${renderFormattedDate(item.value)}`, type: "success", }); }); diff --git a/web/components/notifications/select-snooze-till-modal.tsx b/web/components/notifications/select-snooze-till-modal.tsx index 2df040623..e5792cd06 100644 --- a/web/components/notifications/select-snooze-till-modal.tsx +++ b/web/components/notifications/select-snooze-till-modal.tsx @@ -3,8 +3,8 @@ import { useRouter } from "next/router"; import { useForm, Controller } from "react-hook-form"; import { Transition, Dialog } from "@headlessui/react"; import { X } from "lucide-react"; -// date helper -import { getAllTimeIn30MinutesInterval } from "helpers/date-time.helper"; +// constants +import { allTimeIn30MinutesInterval12HoursFormat } from "constants/notification"; // hooks import useToast from "hooks/use-toast"; // ui @@ -33,7 +33,7 @@ const defaultValues: FormValues = { period: "AM", }; -const timeStamps = getAllTimeIn30MinutesInterval(); +const timeStamps = allTimeIn30MinutesInterval12HoursFormat; export const SnoozeNotificationModal: FC = (props) => { const { isOpen, onClose, notification, onSuccess, onSubmit: handleSubmitSnooze } = props; diff --git a/web/components/pages/pages-list/list-item.tsx b/web/components/pages/pages-list/list-item.tsx index 6f02f7656..1719aecbf 100644 --- a/web/components/pages/pages-list/list-item.tsx +++ b/web/components/pages/pages-list/list-item.tsx @@ -19,7 +19,7 @@ import { useMobxStore } from "lib/mobx/store-provider"; import useToast from "hooks/use-toast"; // helpers import { copyUrlToClipboard } from "helpers/string.helper"; -import { render24HourFormatTime, renderFormattedDate } from "helpers/date-time.helper"; +import { renderFormattedTime, renderFormattedDate } from "helpers/date-time.helper"; // ui import { CustomMenu, Tooltip } from "@plane/ui"; // components @@ -194,19 +194,19 @@ export const PagesListItem: FC = observer((props) => {
{page.archived_at ? ( -

{render24HourFormatTime(page.archived_at)}

+

{renderFormattedTime(page.archived_at)}

) : ( -

{render24HourFormatTime(page.updated_at)}

+

{renderFormattedTime(page.updated_at)}

)} {isEditingAllowed && ( diff --git a/web/components/profile/overview/activity.tsx b/web/components/profile/overview/activity.tsx index 9512a98f2..793527beb 100644 --- a/web/components/profile/overview/activity.tsx +++ b/web/components/profile/overview/activity.tsx @@ -10,7 +10,7 @@ import { Loader } from "@plane/ui"; // image import recentActivityEmptyState from "public/empty-state/recent_activity.svg"; // helpers -import { timeAgo } from "helpers/date-time.helper"; +import { calculateTimeAgo } from "helpers/date-time.helper"; // fetch-keys import { USER_PROFILE_ACTIVITY } from "constants/fetch-keys"; @@ -71,7 +71,7 @@ export const ProfileActivity = () => { )}

-

{timeAgo(activity.created_at)}

+

{calculateTimeAgo(activity.created_at)}

))} diff --git a/web/components/profile/sidebar.tsx b/web/components/profile/sidebar.tsx index 79cc96b5a..4edab9e86 100644 --- a/web/components/profile/sidebar.tsx +++ b/web/components/profile/sidebar.tsx @@ -14,7 +14,7 @@ import { Loader, Tooltip } from "@plane/ui"; // icons import { ChevronDown, Pencil } from "lucide-react"; // helpers -import { renderLongDetailDateFormat } from "helpers/date-time.helper"; +import { renderFormattedDate } from "helpers/date-time.helper"; import { renderEmoji } from "helpers/emoji.helper"; // fetch-keys import { USER_PROFILE_PROJECT_SEGREGATION } from "constants/fetch-keys"; @@ -48,7 +48,7 @@ export const ProfileSidebar = () => { const userDetails = [ { label: "Joined on", - value: renderLongDetailDateFormat(userProjectsData?.user_data.date_joined ?? ""), + value: renderFormattedDate(userProjectsData?.user_data.date_joined ?? ""), }, { label: "Timezone", diff --git a/web/components/project/form.tsx b/web/components/project/form.tsx index 2911e083b..9dddc16e9 100644 --- a/web/components/project/form.tsx +++ b/web/components/project/form.tsx @@ -8,7 +8,7 @@ import { Button, CustomSelect, Input, TextArea } from "@plane/ui"; import { IProject, IWorkspace } from "types"; // helpers import { renderEmoji } from "helpers/emoji.helper"; -import { renderShortDateWithYearFormat } from "helpers/date-time.helper"; +import { renderFormattedDate } from "helpers/date-time.helper"; // constants import { NETWORK_CHOICES } from "constants/project"; // services @@ -310,7 +310,7 @@ export const ProjectDetailsForm: FC = (props) => { {isSubmitting ? "Updating Project..." : "Update Project"} - Created on {renderShortDateWithYearFormat(project?.created_at)} + Created on {renderFormattedDate(project?.created_at)} diff --git a/web/components/ui/date.tsx b/web/components/ui/date.tsx index 44e6c3dc8..4902ba443 100644 --- a/web/components/ui/date.tsx +++ b/web/components/ui/date.tsx @@ -1,12 +1,11 @@ import React from "react"; - import { Popover, Transition } from "@headlessui/react"; // react-datepicker import DatePicker from "react-datepicker"; // icons import { CalendarDays, X } from "lucide-react"; -// import "react-datepicker/dist/react-datepicker.css"; -import { renderDateFormat, renderShortDateWithYearFormat } from "helpers/date-time.helper"; +// helpers +import { renderFormattedPayloadDate, renderFormattedDate } from "helpers/date-time.helper"; type Props = { value: string | null; @@ -25,7 +24,7 @@ export const DateSelect: React.FC = ({ value, onChange, label, minDate, m {value ? ( <> - {renderShortDateWithYearFormat(value)} + {renderFormattedDate(value)} @@ -52,7 +51,7 @@ export const DateSelect: React.FC = ({ value, onChange, label, minDate, m selected={value ? new Date(value) : null} onChange={(val) => { if (!val) onChange(""); - else onChange(renderDateFormat(val)); + else onChange(renderFormattedPayloadDate(val)); if (closeOnSelect) close(); }} diff --git a/web/components/ui/datepicker.tsx b/web/components/ui/datepicker.tsx index 862b10d4d..ef0a6b6e0 100644 --- a/web/components/ui/datepicker.tsx +++ b/web/components/ui/datepicker.tsx @@ -2,7 +2,7 @@ import DatePicker from "react-datepicker"; import "react-datepicker/dist/react-datepicker.css"; // helpers -import { renderDateFormat } from "helpers/date-time.helper"; +import { renderFormattedPayloadDate } from "helpers/date-time.helper"; type Props = { renderAs?: "input" | "button"; @@ -45,7 +45,7 @@ export const CustomDatePicker: React.FC = ({ selected={value ? new Date(value) : null} onChange={(val) => { if (!val) onChange(null); - else onChange(renderDateFormat(val)); + else onChange(renderFormattedPayloadDate(val)); }} onCalendarOpen={handleOnOpen} onCalendarClose={handleOnClose} diff --git a/web/components/ui/product-updates-modal.tsx b/web/components/ui/product-updates-modal.tsx index 3ece1a20d..344dfe53d 100644 --- a/web/components/ui/product-updates-modal.tsx +++ b/web/components/ui/product-updates-modal.tsx @@ -1,7 +1,5 @@ import React from "react"; - import useSWR from "swr"; - // headless ui import { Dialog, Transition } from "@headlessui/react"; // services @@ -12,7 +10,7 @@ import { Loader } from "@plane/ui"; // icons import { X } from "lucide-react"; // helpers -import { renderLongDateFormat } from "helpers/date-time.helper"; +import { renderFormattedDate } from "helpers/date-time.helper"; type Props = { isOpen: boolean; @@ -68,7 +66,7 @@ export const ProductUpdatesModal: React.FC = ({ isOpen, setIsOpen }) => { {item.tag_name} - {renderLongDateFormat(item.published_at)} + {renderFormattedDate(item.published_at)} {index === 0 && ( New diff --git a/web/components/ui/range-datepicker.tsx b/web/components/ui/range-datepicker.tsx index 727180e08..55760ae01 100644 --- a/web/components/ui/range-datepicker.tsx +++ b/web/components/ui/range-datepicker.tsx @@ -2,7 +2,7 @@ import DatePicker from "react-datepicker"; import "react-datepicker/dist/react-datepicker.css"; // helpers -import { renderDateFormat } from "helpers/date-time.helper"; +import { renderFormattedPayloadDate } from "helpers/date-time.helper"; type Props = { renderAs?: "input" | "button"; @@ -38,7 +38,7 @@ export const CustomRangeDatePicker: React.FC = ({ selected={value ? new Date(value) : null} onChange={(val) => { if (!val) onChange(null); - else onChange(renderDateFormat(val)); + else onChange(renderFormattedPayloadDate(val)); }} className={`${ renderAs === "input" diff --git a/web/components/web-hooks/utils.ts b/web/components/web-hooks/utils.ts index 04c6e650d..200d83ea0 100644 --- a/web/components/web-hooks/utils.ts +++ b/web/components/web-hooks/utils.ts @@ -1,4 +1,6 @@ -import { renderDateFormat } from "helpers/date-time.helper"; +// helpers +import { renderFormattedPayloadDate } from "helpers/date-time.helper"; +// types import { IWebhook, IWorkspace } from "types"; export const getCurrentHookAsCSV = ( @@ -8,8 +10,8 @@ export const getCurrentHookAsCSV = ( ) => ({ id: webhook?.id || "", url: webhook?.url || "", - created_at: renderDateFormat(webhook?.created_at), - updated_at: renderDateFormat(webhook?.updated_at), + created_at: renderFormattedPayloadDate(webhook?.created_at || "") ?? "", + updated_at: renderFormattedPayloadDate(webhook?.updated_at || "") ?? "", is_active: webhook?.is_active?.toString() || "", secret_key: secretKey || "", project: webhook?.project?.toString() || "", diff --git a/web/components/workspace/activity-graph.tsx b/web/components/workspace/activity-graph.tsx index 6036d0c69..cc8602c15 100644 --- a/web/components/workspace/activity-graph.tsx +++ b/web/components/workspace/activity-graph.tsx @@ -1,9 +1,8 @@ import { useEffect, useRef, useState } from "react"; - // ui import { Tooltip } from "@plane/ui"; // helpers -import { renderDateFormat, renderShortDateWithYearFormat } from "helpers/date-time.helper"; +import { renderFormattedPayloadDate, renderFormattedDate } from "helpers/date-time.helper"; // types import { IUserActivity } from "types"; // constants @@ -35,7 +34,7 @@ export const ActivityGraph: React.FC = ({ activities }) => { const date = new Date(year, month, 1); while (date.getMonth() === month && date < new Date()) { - dates.push(renderDateFormat(new Date(date))); + dates.push(renderFormattedPayloadDate(new Date(date)) ?? ""); date.setDate(date.getDate() + 1); } @@ -102,7 +101,7 @@ export const ActivityGraph: React.FC = ({ activities }) => { key={`${date}-${index}`} tooltipContent={`${ isActive ? isActive.activity_count : 0 - } activities on ${renderShortDateWithYearFormat(date)}`} + } activities on ${renderFormattedDate(date)}`} >
= ({ issues, type }) => {
{truncateText(issue.name, 30)}
- {renderShortDateWithYearFormat(new Date(date?.toString() ?? ""))} + {renderFormattedDate(new Date(date?.toString() ?? ""))}
diff --git a/web/constants/notification.ts b/web/constants/notification.ts index b36df35dd..04f803151 100644 --- a/web/constants/notification.ts +++ b/web/constants/notification.ts @@ -24,3 +24,34 @@ export const snoozeOptions = [ value: null, }, ]; + +// Constant for all time values in 30 minutes interval in 12 hours format +export const allTimeIn30MinutesInterval12HoursFormat: Array<{ + label: string; + value: string; +}> = [ + { label: "12:00", value: "12:00" }, + { label: "12:30", value: "12:30" }, + { label: "01:00", value: "01:00" }, + { label: "01:30", value: "01:30" }, + { label: "02:00", value: "02:00" }, + { label: "02:30", value: "02:30" }, + { label: "03:00", value: "03:00" }, + { label: "03:30", value: "03:30" }, + { label: "04:00", value: "04:00" }, + { label: "04:30", value: "04:30" }, + { label: "05:00", value: "05:00" }, + { label: "05:30", value: "05:30" }, + { label: "06:00", value: "06:00" }, + { label: "06:30", value: "06:30" }, + { label: "07:00", value: "07:00" }, + { label: "07:30", value: "07:30" }, + { label: "08:00", value: "08:00" }, + { label: "08:30", value: "08:30" }, + { label: "09:00", value: "09:00" }, + { label: "09:30", value: "09:30" }, + { label: "10:00", value: "10:00" }, + { label: "10:30", value: "10:30" }, + { label: "11:00", value: "11:00" }, + { label: "11:30", value: "11:30" }, +]; diff --git a/web/helpers/calendar.helper.ts b/web/helpers/calendar.helper.ts index b00668894..e570a5c9a 100644 --- a/web/helpers/calendar.helper.ts +++ b/web/helpers/calendar.helper.ts @@ -1,5 +1,5 @@ // helpers -import { getWeekNumberOfDate, renderDateFormat } from "helpers/date-time.helper"; +import { getWeekNumberOfDate, renderFormattedPayloadDate } from "helpers/date-time.helper"; // types import { ICalendarDate, ICalendarPayload } from "components/issues"; @@ -73,16 +73,18 @@ export const generateCalendarData = (currentStructure: ICalendarPayload | null, const date = new Date(year, month, dayNumber + 1); - currentWeekObject[renderDateFormat(date)] = { - date, - year, - month, - day: dayNumber + 1, - week: weekNumber, - is_current_month: date.getMonth() === month, - is_current_week: getWeekNumberOfDate(date) === getWeekNumberOfDate(new Date()), - is_today: date.toDateString() === new Date().toDateString(), - }; + const formattedDatePayload = renderFormattedPayloadDate(date); + if (formattedDatePayload) + currentWeekObject[formattedDatePayload] = { + date, + year, + month, + day: dayNumber + 1, + week: weekNumber, + is_current_month: date.getMonth() === month, + is_current_week: getWeekNumberOfDate(date) === getWeekNumberOfDate(new Date()), + is_today: date.toDateString() === new Date().toDateString(), + }; } calendarData[`y-${year}`][`m-${month}`][`w-${weekNumber}`] = currentWeekObject; diff --git a/web/helpers/date-time.helper.ts b/web/helpers/date-time.helper.ts index ab6116d77..bc5daa2a3 100644 --- a/web/helpers/date-time.helper.ts +++ b/web/helpers/date-time.helper.ts @@ -1,384 +1,83 @@ -import { format } from "date-fns"; +import { differenceInDays, format, formatDistanceToNow, isAfter, isValid, parseISO } from "date-fns"; -export const addDays = ({ date, days }: { date: Date; days: number }): Date => { - date.setDate(date.getDate() + days); - return date; +// Format Date Helpers +/** + * @returns {string | null} formatted date in the format of MMM dd, yyyy + * @description Returns date in the formatted format + * @param {Date | string} date + * @example renderFormattedDate("2024-01-01") // Jan 01, 2024 + */ +export const renderFormattedDate = (date: string | Date): string | null => { + if (!date) return null; + // Parse the date to check if it is valid + const parsedDate = new Date(date); + // Check if the parsed date is valid before formatting + if (!isValid(parsedDate)) return null; // Return null for invalid dates + // Format the date in format (MMM dd, yyyy) + const formattedDate = format(parsedDate, "MMM dd, yyyy"); + return formattedDate; }; -export const renderDateFormat = (date: string | Date | null | undefined, dayFirst: boolean = false) => { - if (!date) return "N/A"; - - var d = new Date(date), - month = "" + (d.getMonth() + 1), - day = "" + d.getDate(), - year = d.getFullYear(); - - if (month.length < 2) month = "0" + month; - if (day.length < 2) day = "0" + day; - - return dayFirst ? [day, month, year].join("-") : [year, month, day].join("-"); +/** + * @returns {string} formatted date in the format of MMM dd + * @description Returns date in the formatted format + * @param {string | Date} date + * @example renderShortDateFormat("2024-01-01") // Jan 01 + */ +export const renderFormattedDateWithoutYear = (date: string | Date): string => { + if (!date) return ""; + // Parse the date to check if it is valid + const parsedDate = new Date(date); + // Check if the parsed date is valid before formatting + if (!isValid(parsedDate)) return ""; // Return empty string for invalid dates + // Format the date in short format (MMM dd) + const formattedDate = format(parsedDate, "MMM dd"); + return formattedDate; }; -export const renderShortNumericDateFormat = (date: string | Date) => - new Date(date).toLocaleDateString("en-UK", { - day: "numeric", - month: "short", - }); - -export const renderLongDetailDateFormat = (date: string | Date) => - new Date(date).toLocaleDateString("en-UK", { - day: "numeric", - month: "long", - year: "numeric", - }); - -export const findHowManyDaysLeft = (date: string | Date) => { - const today = new Date(); - const eventDate = new Date(date); - const timeDiff = Math.abs(eventDate.getTime() - today.getTime()); - return Math.ceil(timeDiff / (1000 * 3600 * 24)); +/** + * @returns {string | null} formatted date in the format of yyyy-mm-dd to be used in payload + * @description Returns date in the formatted format to be used in payload + * @param {Date | string} date + * @example renderFormattedPayloadDate("Jan 01, 20224") // "2024-01-01" + */ +export const renderFormattedPayloadDate = (date: Date | string): string | null => { + if (!date) return null; + // Parse the date to check if it is valid + const parsedDate = new Date(date); + // Check if the parsed date is valid before formatting + if (!isValid(parsedDate)) return null; // Return null for invalid dates + // Format the date in payload format (yyyy-mm-dd) + const formattedDate = format(parsedDate, "yyyy-MM-dd"); + return formattedDate; }; -export const getDatesInRange = (startDate: string | Date, endDate: string | Date) => { - startDate = new Date(startDate); - endDate = new Date(endDate); - - const date = new Date(startDate.getTime()); - const dates = []; - - while (date <= endDate) { - dates.push(new Date(date)); - date.setDate(date.getDate() + 1); - } - - return dates; -}; - -export const timeAgo = (time: any) => { - switch (typeof time) { - case "number": - break; - case "string": - time = +new Date(time); - break; - case "object": - if (time.constructor === Date) time = time.getTime(); - break; - default: - time = +new Date(); - } - - var time_formats = [ - [60, "seconds", 1], // 60 - [120, "1 minute ago", "1 minute from now"], // 60*2 - [3600, "minutes", 60], // 60*60, 60 - [7200, "1 hour ago", "1 hour from now"], // 60*60*2 - [86400, "hours", 3600], // 60*60*24, 60*60 - [172800, "Yesterday", "Tomorrow"], // 60*60*24*2 - [604800, "days", 86400], // 60*60*24*7, 60*60*24 - [1209600, "Last week", "Next week"], // 60*60*24*7*4*2 - [2419200, "weeks", 604800], // 60*60*24*7*4, 60*60*24*7 - [4838400, "Last month", "Next month"], // 60*60*24*7*4*2 - [29030400, "months", 2419200], // 60*60*24*7*4*12, 60*60*24*7*4 - [58060800, "Last year", "Next year"], // 60*60*24*7*4*12*2 - [2903040000, "years", 29030400], // 60*60*24*7*4*12*100, 60*60*24*7*4*12 - [5806080000, "Last century", "Next century"], // 60*60*24*7*4*12*100*2 - [58060800000, "centuries", 2903040000], // 60*60*24*7*4*12*100*20, 60*60*24*7*4*12*100 - ]; - - var seconds = (+new Date() - time) / 1000, - token = "ago", - list_choice = 1; - - if (seconds == 0) { - return "Just now"; - } - if (seconds < 0) { - seconds = Math.abs(seconds); - token = "from now"; - list_choice = 2; - } - var i = 0, - format: any[]; - while ((format = time_formats[i++])) - if (seconds < format[0]) { - if (typeof format[2] == "string") return format[list_choice]; - else return Math.floor(seconds / format[2]) + " " + format[1] + " " + token; - } - return time; -}; - -export const formatDateDistance = (date: string | Date) => { - const today = new Date(); - const eventDate = new Date(date); - const timeDiff = Math.abs(eventDate.getTime() - today.getTime()); - const days = Math.ceil(timeDiff / (1000 * 3600 * 24)); - - if (days < 1) { - const hours = Math.ceil(timeDiff / (1000 * 3600)); - if (hours < 1) { - const minutes = Math.ceil(timeDiff / (1000 * 60)); - if (minutes < 1) { - return "Just now"; - } else { - return `${minutes}m`; - } - } else { - return `${hours}h`; - } - } else if (days < 7) { - return `${days}d`; - } else if (days < 30) { - return `${Math.floor(days / 7)}w`; - } else if (days < 365) { - return `${Math.floor(days / 30)}m`; - } else { - return `${Math.floor(days / 365)}y`; - } -}; - -export const formatLongDateDistance = (date: string | Date) => { - const today = new Date(); - const eventDate = new Date(date); - const timeDiff = Math.abs(eventDate.getTime() - today.getTime()); - const days = Math.ceil(timeDiff / (1000 * 3600 * 24)); - - if (days < 1) { - const hours = Math.ceil(timeDiff / (1000 * 3600)); - if (hours < 1) { - const minutes = Math.ceil(timeDiff / (1000 * 60)); - if (minutes < 1) { - return "Just now"; - } else { - return `${minutes} minutes`; - } - } else { - return `${hours} hours`; - } - } else if (days < 7) { - if (days === 1) return `${days} day`; - return `${days} days`; - } else if (days < 30) { - if (Math.floor(days / 7) === 1) return `${Math.floor(days / 7)} week`; - return `${Math.floor(days / 7)} weeks`; - } else if (days < 365) { - if (Math.floor(days / 30) === 1) return `${Math.floor(days / 30)} month`; - return `${Math.floor(days / 30)} months`; - } else { - if (Math.floor(days / 365) === 1) return `${Math.floor(days / 365)} year`; - return `${Math.floor(days / 365)} years`; - } -}; - -export const renderShortDateWithYearFormat = (date: string | Date, placeholder?: string) => { - if (!date || date === "") return null; - - date = new Date(date); - - const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; - const day = date.getDate(); - const month = months[date.getMonth()]; - const year = date.getFullYear(); - - return isNaN(date.getTime()) ? placeholder ?? "N/A" : ` ${month} ${day}, ${year}`; -}; - -export const renderShortDate = (date: string | Date, placeholder?: string) => { - if (!date || date === "") return null; - - date = new Date(date); - - const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; - const day = date.getDate(); - const month = months[date.getMonth()]; - - return isNaN(date.getTime()) ? placeholder ?? "N/A" : `${day} ${month}`; -}; - -export const renderShortMonthDate = (date: string | Date, placeholder?: string) => { - if (!date || date === "") return null; - - date = new Date(date); - - const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; - const month = months[date.getMonth()]; - const year = date.getFullYear(); - - return isNaN(date.getTime()) ? placeholder ?? "N/A" : `${month} ${year}`; -}; - -export const render12HourFormatTime = (date: string | Date): string => { +// Format Time Helpers +/** + * @returns {string} formatted date in the format of hh:mm a or HH:mm + * @description Returns date in 12 hour format if in12HourFormat is true else 24 hour format + * @param {string | Date} date + * @param {boolean} timeFormat (optional) // default 24 hour + * @example renderFormattedTime("2024-01-01 13:00:00") // 13:00 + * @example renderFormattedTime("2024-01-01 13:00:00", "12-hour") // 01:00 PM + */ +export const renderFormattedTime = (date: string | Date, timeFormat: "12-hour" | "24-hour" = "24-hour"): string => { if (!date || date === "") return ""; - - date = new Date(date); - - let hours = date.getHours(); - const minutes = date.getMinutes(); - - let period = "AM"; - - if (hours >= 12) { - period = "PM"; - - if (hours > 12) hours -= 12; + // Parse the date to check if it is valid + const parsedDate = new Date(date); + // Check if the parsed date is valid + if (!isValid(parsedDate)) return ""; // Return empty string for invalid dates + // Format the date in 12 hour format if in12HourFormat is true + if (timeFormat === "12-hour") { + const formattedTime = format(parsedDate, "hh:mm a"); + return formattedTime; } - - return hours + ":" + (minutes < 10 ? `0${minutes}` : minutes) + " " + period; + // Format the date in 24 hour format + const formattedTime = format(parsedDate, "HH:mm"); + return formattedTime; }; -export const render24HourFormatTime = (date: string | Date): string => { - if (!date || date === "") return ""; - - date = new Date(date); - - const hours = date.getHours(); - let minutes: any = date.getMinutes(); - - // add leading zero to single digit minutes - if (minutes < 10) minutes = "0" + minutes; - - return hours + ":" + minutes; -}; - -export const isDateRangeValid = (startDate: string, endDate: string) => new Date(startDate) < new Date(endDate); - -export const isDateGreaterThanToday = (dateStr: string) => { - const date = new Date(dateStr); - const today = new Date(); - return date > today; -}; - -export const renderLongDateFormat = (dateString: string | Date) => { - const date = new Date(dateString); - const day = date.getDate(); - const year = date.getFullYear(); - const monthNames = [ - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December", - ]; - const monthIndex = date.getMonth(); - const monthName = monthNames[monthIndex]; - const suffixes = ["st", "nd", "rd", "th"]; - let suffix = ""; - if (day === 1 || day === 21 || day === 31) { - suffix = suffixes[0]; - } else if (day === 2 || day === 22) { - suffix = suffixes[1]; - } else if (day === 3 || day === 23) { - suffix = suffixes[2]; - } else { - suffix = suffixes[3]; - } - return `${day}${suffix} ${monthName} ${year}`; -}; - -/** - * - * @returns {Array} Array of time objects with label and value as keys - */ - -export const getTimestampAfterCurrentTime = (): Array<{ - label: string; - value: Date; -}> => { - const current = new Date(); - const time = []; - for (let i = 0; i < 24; i++) { - const newTime = new Date(current.getTime() + i * 60 * 60 * 1000); - time.push({ - label: newTime.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }), - value: newTime, - }); - } - return time; -}; - -/** - * @returns {Array} Array of date objects with label and value as keys - * @description Returns an array of date objects starting from current date to 7 days after - */ - -export const getDatesAfterCurrentDate = (): Array<{ - label: string; - value: Date; -}> => { - const current = new Date(); - const date = []; - for (let i = 0; i < 7; i++) { - const newDate = new Date(Math.round(current.getTime() / (30 * 60 * 1000)) * 30 * 60 * 1000); - date.push({ - label: newDate.toLocaleDateString([], { - day: "numeric", - month: "long", - year: "numeric", - }), - value: newDate, - }); - } - return date; -}; - -/** - * @returns {boolean} true if date is valid - * @description Returns true if date is valid - * @param {string} date - * @example checkIfStringIsDate("2021-01-01") // true - * @example checkIfStringIsDate("2021-01-32") // false - */ - -export const checkIfStringIsDate = (date: string): boolean => new Date(date).toString() !== "Invalid Date"; - -// return an array of dates starting from 12:00 to 23:30 with 30 minutes interval as dates -export const getDatesWith30MinutesInterval = (): Array => { - const dates = []; - const current = new Date(); - for (let i = 0; i < 24; i++) { - const newDate = new Date(current.getTime() + i * 60 * 60 * 1000); - dates.push(newDate); - } - return dates; -}; - -export const getAllTimeIn30MinutesInterval = (): Array<{ - label: string; - value: string; -}> => [ - { label: "12:00", value: "12:00" }, - { label: "12:30", value: "12:30" }, - { label: "01:00", value: "01:00" }, - { label: "01:30", value: "01:30" }, - { label: "02:00", value: "02:00" }, - { label: "02:30", value: "02:30" }, - { label: "03:00", value: "03:00" }, - { label: "03:30", value: "03:30" }, - { label: "04:00", value: "04:00" }, - { label: "04:30", value: "04:30" }, - { label: "05:00", value: "05:00" }, - { label: "05:30", value: "05:30" }, - { label: "06:00", value: "06:00" }, - { label: "06:30", value: "06:30" }, - { label: "07:00", value: "07:00" }, - { label: "07:30", value: "07:30" }, - { label: "08:00", value: "08:00" }, - { label: "08:30", value: "08:30" }, - { label: "09:00", value: "09:00" }, - { label: "09:30", value: "09:30" }, - { label: "10:00", value: "10:00" }, - { label: "10:30", value: "10:30" }, - { label: "11:00", value: "11:00" }, - { label: "11:30", value: "11:30" }, -]; - +// Date Difference Helpers /** * @returns {number} total number of days in range * @description Returns total number of days in range @@ -387,25 +86,73 @@ export const getAllTimeIn30MinutesInterval = (): Array<{ * @param {boolean} inclusive * @example checkIfStringIsDate("2021-01-01", "2021-01-08") // 8 */ - -export const findTotalDaysInRange = (startDate: Date | string, endDate: Date | string, inclusive: boolean): number => { +export const findTotalDaysInRange = ( + startDate: Date | string, + endDate: Date | string, + inclusive: boolean = true +): number => { if (!startDate || !endDate) return 0; - - startDate = new Date(startDate); - endDate = new Date(endDate); - - // find number of days between startDate and endDate - const diffInTime = endDate.getTime() - startDate.getTime(); - const diffInDays = Math.floor(diffInTime / (1000 * 3600 * 24)); - - // if inclusive is true, add 1 to diffInDays - if (inclusive) return diffInDays + 1; - - return diffInDays; + // Parse the dates to check if they are valid + const parsedStartDate = new Date(startDate); + const parsedEndDate = new Date(endDate); + // Check if the parsed dates are valid before calculating the difference + if (!isValid(parsedStartDate) || !isValid(parsedEndDate)) return 0; // Return 0 for invalid dates + // Calculate the difference in days + const diffInDays = differenceInDays(parsedEndDate, parsedStartDate); + // Return the difference in days based on inclusive flag + return inclusive ? diffInDays + 1 : diffInDays; }; -export const getUserTimeZoneFromWindow = () => Intl.DateTimeFormat().resolvedOptions().timeZone; +/** + * @returns {number} number of days left from today + * @description Returns number of days left from today + * @param {string | Date} date + * @param {boolean} inclusive (optional) // default true + * @example findHowManyDaysLeft("2024-01-01") // 3 + */ +export const findHowManyDaysLeft = (date: string | Date, inclusive: boolean = true): number => { + if (!date) return 0; + // Pass the date to findTotalDaysInRange function to find the total number of days in range from today + return findTotalDaysInRange(new Date(), date, inclusive); +}; +// Time Difference Helpers +/** + * @returns {string} formatted date in the form of amount of time passed since the event happened + * @description Returns time passed since the event happened + * @param {string | Date} time + * @example calculateTimeAgo("2023-01-01") // 1 year ago + */ +export const calculateTimeAgo = (time: string | number | Date | null): string => { + if (!time) return ""; + // Parse the time to check if it is valid + const parsedTime = typeof time === "string" || typeof time === "number" ? parseISO(String(time)) : time; + if (!parsedTime) return ""; // Return empty string for invalid dates + // Format the time in the form of amount of time passed since the event happened + const distance = formatDistanceToNow(parsedTime, { addSuffix: true }); + return distance; +}; + +// Date Validation Helpers +/** + * @returns {string} boolean value depending on whether the date is greater than today + * @description Returns boolean value depending on whether the date is greater than today + * @param {string} dateStr + * @example isDateGreaterThanToday("2024-01-01") // true + */ +export const isDateGreaterThanToday = (dateStr: string): boolean => { + // Return false if dateStr is not present + if (!dateStr) return false; + // Parse the date to check if it is valid + const date = parseISO(dateStr); + const today = new Date(); + // Check if the parsed date is valid + if (!isValid(date)) return false; // Return false for invalid dates + // Return true if the date is greater than today + return isAfter(date, today); +}; + +// Week Related Helpers /** * @returns {number} week number of date * @description Returns week number of date @@ -414,73 +161,11 @@ export const getUserTimeZoneFromWindow = () => Intl.DateTimeFormat().resolvedOpt */ export const getWeekNumberOfDate = (date: Date): number => { const currentDate = new Date(date); - // Adjust the starting day to Sunday (0) instead of Monday (1) const startDate = new Date(currentDate.getFullYear(), 0, 1); - // Calculate the number of days between currentDate and startDate const days = Math.floor((currentDate.getTime() - startDate.getTime()) / (24 * 60 * 60 * 1000)); - // Adjust the calculation for weekNumber const weekNumber = Math.ceil((days + 1) / 7); - return weekNumber; }; - -/** - * @returns {Date} first date of week - * @description Returns week number of date - * @param {Date} date - * @example getFirstDateOfWeek(35, 2023) // 2023-08-27T00:00:00.000Z - */ -export const getFirstDateOfWeek = (date: Date): Date => { - const year = date.getFullYear(); - const weekNumber = getWeekNumberOfDate(date); - - const januaryFirst: Date = new Date(year, 0, 1); // January is month 0 - const daysToAdd: number = (weekNumber - 1) * 7; // Subtract 1 from the week number since weeks are 0-indexed - const firstDateOfWeek: Date = new Date(januaryFirst); - firstDateOfWeek.setDate(januaryFirst.getDate() + daysToAdd); - - // Adjust the date to Sunday (week start) - const dayOfWeek: number = firstDateOfWeek.getDay(); - firstDateOfWeek.setDate(firstDateOfWeek.getDate() - dayOfWeek); // Move back to Sunday - - return firstDateOfWeek; -}; - -/** - * @returns {string} formatted date in the format of MMM dd, yyyy - * @description Returns date in the formatted format - * @param {Date | string} date - * @example renderFormattedDate("2023-01-01") // Jan 01, 2023 - */ -export const renderFormattedDate = (date: string | Date): string => { - if (!date) return ""; - - date = new Date(date); - - const formattedDate = format(date, "MMM dd, yyyy"); - - return formattedDate; -}; - -/** - * @returns {string | null} formatted date in the format of yyyy-mm-dd to be used in payload - * @description Returns date in the formatted format to be used in payload - * @param {Date | string} date - * @example renderFormattedPayloadDate("2023-01-01") // "2023-01-01" - */ -export const renderFormattedPayloadDate = (date: Date | string): string | null => { - if (!date) return null; - - var d = new Date(date), - month = "" + (d.getMonth() + 1), - day = "" + d.getDate(), - year = d.getFullYear(); - - if (month.length < 2) month = "0" + month; - if (day.length < 2) day = "0" + day; - - return [year, month, day].join("-"); -}; diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/pages/[pageId].tsx b/web/pages/[workspaceSlug]/projects/[projectId]/pages/[pageId].tsx index 44bf94c66..b653d6e18 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/pages/[pageId].tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/pages/[pageId].tsx @@ -20,7 +20,7 @@ import { Spinner } from "@plane/ui"; // assets import emptyPage from "public/empty-state/page.svg"; // helpers -import { renderDateFormat } from "helpers/date-time.helper"; +import { renderFormattedPayloadDate } from "helpers/date-time.helper"; // types import { NextPageWithLayout } from "types/app"; import { IPage, IIssue } from "types"; @@ -279,7 +279,7 @@ const PageDetailsPage: NextPageWithLayout = observer(() => { mutatePageDetailsHelper( pageService.archivePage(workspaceSlug.toString(), projectId.toString(), pageId.toString()), { - archived_at: renderDateFormat(new Date()), + archived_at: renderFormattedPayloadDate(new Date()), }, ["description_html"], () => diff --git a/web/pages/profile/activity.tsx b/web/pages/profile/activity.tsx index c05a9559e..88f370491 100644 --- a/web/pages/profile/activity.tsx +++ b/web/pages/profile/activity.tsx @@ -15,7 +15,7 @@ import { ExternalLinkIcon, Loader } from "@plane/ui"; // fetch-keys import { USER_ACTIVITY } from "constants/fetch-keys"; // helper -import { timeAgo } from "helpers/date-time.helper"; +import { calculateTimeAgo } from "helpers/date-time.helper"; // type import { NextPageWithLayout } from "types/app"; @@ -70,7 +70,7 @@ const ProfileActivityPage: NextPageWithLayout = () => { : activityItem.actor_detail.display_name}

- Commented {timeAgo(activityItem.created_at)} + Commented {calculateTimeAgo(activityItem.created_at)}

@@ -165,7 +165,7 @@ const ProfileActivityPage: NextPageWithLayout = () => {
{message}{" "} - {timeAgo(activityItem.created_at)} + {calculateTimeAgo(activityItem.created_at)}
diff --git a/web/store/calendar.store.ts b/web/store/calendar.store.ts index 3d4f5cfdd..dd3cdb01d 100644 --- a/web/store/calendar.store.ts +++ b/web/store/calendar.store.ts @@ -1,11 +1,10 @@ import { observable, action, makeObservable, runInAction, computed } from "mobx"; - // helpers import { generateCalendarData } from "helpers/calendar.helper"; +import { getWeekNumberOfDate } from "helpers/date-time.helper"; // types import { RootStore } from "./root"; import { ICalendarPayload, ICalendarWeek } from "components/issues"; -import { getWeekNumberOfDate } from "helpers/date-time.helper"; export interface ICalendarStore { calendarFilters: { diff --git a/web/store/issues/project-issues/base-issue.store.ts b/web/store/issues/project-issues/base-issue.store.ts index c4940337f..ef8f524b5 100644 --- a/web/store/issues/project-issues/base-issue.store.ts +++ b/web/store/issues/project-issues/base-issue.store.ts @@ -10,7 +10,7 @@ import { IIssueResponse } from "../types"; // constants import { ISSUE_PRIORITIES, ISSUE_STATE_GROUPS } from "constants/issue"; // helpers -import { renderDateFormat } from "helpers/date-time.helper"; +import { renderFormattedPayloadDate } from "helpers/date-time.helper"; export interface IIssueBaseStore { groupedIssues( @@ -201,7 +201,7 @@ export class IssueBaseStore implements IIssueBaseStore { if (Array.isArray(value)) { if (value.length) return value; else return ["None"]; - } else if (isDate) return [renderDateFormat(value) || "None"]; + } else if (isDate) return [renderFormattedPayloadDate(value ?? "") || "None"]; else return [value || "None"]; } } diff --git a/web/store/page.store.ts b/web/store/page.store.ts index 5fdd04f3f..c24ad6de0 100644 --- a/web/store/page.store.ts +++ b/web/store/page.store.ts @@ -6,7 +6,7 @@ import isThisWeek from "date-fns/isThisWeek"; import { ProjectService } from "services/project"; import { PageService } from "services/page.service"; // helpers -import { renderDateFormat } from "helpers/date-time.helper"; +import { renderFormattedPayloadDate } from "helpers/date-time.helper"; // types import { RootStore } from "./root"; import { IPage, IRecentPages } from "types"; @@ -329,7 +329,7 @@ export class PageStore implements IPageStore { ...this.archivedPages, [projectId]: [ ...this.archivedPages[projectId], - { ...archivedPage, archived_at: renderDateFormat(new Date()) }, + { ...archivedPage, archived_at: renderFormattedPayloadDate(new Date()) }, ], }; });