forked from github/plane
chore: date and time standardization all across the platform. (#3283)
* chore: date and time standardization all across the platform. * chore: update `renderFormattedTime` function. * remove unwanted code. * fix: build errors * chore: update `renderFormattedTime` function params.
This commit is contained in:
parent
d9ee692ce9
commit
1539340113
@ -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(() => {
|
||||
<h6 className="text-custom-text-200">Start Date</h6>
|
||||
<span>
|
||||
{cycleDetails.start_date && cycleDetails.start_date !== ""
|
||||
? renderShortDate(cycleDetails.start_date)
|
||||
? renderFormattedDate(cycleDetails.start_date)
|
||||
: "No start date"}
|
||||
</span>
|
||||
</div>
|
||||
@ -45,7 +44,7 @@ export const CustomAnalyticsSidebarHeader = observer(() => {
|
||||
<h6 className="text-custom-text-200">Target Date</h6>
|
||||
<span>
|
||||
{cycleDetails.end_date && cycleDetails.end_date !== ""
|
||||
? renderShortDate(cycleDetails.end_date)
|
||||
? renderFormattedDate(cycleDetails.end_date)
|
||||
: "No end date"}
|
||||
</span>
|
||||
</div>
|
||||
@ -63,7 +62,7 @@ export const CustomAnalyticsSidebarHeader = observer(() => {
|
||||
<h6 className="text-custom-text-200">Start Date</h6>
|
||||
<span>
|
||||
{moduleDetails.start_date && moduleDetails.start_date !== ""
|
||||
? renderShortDate(moduleDetails.start_date)
|
||||
? renderFormattedDate(moduleDetails.start_date)
|
||||
: "No start date"}
|
||||
</span>
|
||||
</div>
|
||||
@ -71,7 +70,7 @@ export const CustomAnalyticsSidebarHeader = observer(() => {
|
||||
<h6 className="text-custom-text-200">Target Date</h6>
|
||||
<span>
|
||||
{moduleDetails.target_date && moduleDetails.target_date !== ""
|
||||
? renderShortDate(moduleDetails.target_date)
|
||||
? renderFormattedDate(moduleDetails.target_date)
|
||||
: "No end date"}
|
||||
</span>
|
||||
</div>
|
||||
|
@ -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<Props> = observer(
|
||||
{isProjectLevel && (
|
||||
<div className="flex items-center gap-1 rounded-md bg-custom-background-80 px-3 py-1 text-xs text-custom-text-200">
|
||||
<CalendarDays className="h-3.5 w-3.5" />
|
||||
{renderShortDate(
|
||||
{renderFormattedDate(
|
||||
(cycleId
|
||||
? cycleDetails?.created_at
|
||||
: moduleId
|
||||
|
@ -48,7 +48,7 @@ export const CreateApiTokenModal: React.FC<Props> = (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 ?? "",
|
||||
};
|
||||
|
||||
|
@ -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> = (props) => {
|
||||
? token.expired_at
|
||||
? `Expires ${renderFormattedDate(token.expired_at!)}`
|
||||
: "Never expires"
|
||||
: `Expired ${timeAgo(token.expired_at)}`}
|
||||
: `Expired ${calculateTimeAgo(token.expired_at)}`}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -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<Props> = ({ isOpen, setIsOpen }) => {
|
||||
<span className="flex items-center rounded-full border border-custom-border-200 bg-custom-background-90 px-3 py-1.5 text-xs">
|
||||
{item.tag_name}
|
||||
</span>
|
||||
<span>{renderLongDateFormat(item.published_at)}</span>
|
||||
<span>{renderFormattedDate(item.published_at)}</span>
|
||||
{index === 0 && (
|
||||
<span className="flex items-center rounded-full border border-custom-border-200 bg-custom-primary px-3 py-1.5 text-xs text-white">
|
||||
New
|
||||
|
@ -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{" "}
|
||||
<span className="font-medium text-custom-text-100">
|
||||
{renderShortDateWithYearFormat(activity.new_value)}
|
||||
{renderFormattedDate(activity.new_value)}
|
||||
</span>
|
||||
{showIssue && (
|
||||
<>
|
||||
@ -646,7 +646,7 @@ const activityDetails: {
|
||||
<>
|
||||
set the due date to{" "}
|
||||
<span className="font-medium text-custom-text-100">
|
||||
{renderShortDateWithYearFormat(activity.new_value)}
|
||||
{renderFormattedDate(activity.new_value)}
|
||||
</span>
|
||||
{showIssue && (
|
||||
<>
|
||||
|
@ -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<Props> = ({ 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<Props> = ({ title, handleClose, isOpen, o
|
||||
{watch("filterType") === "range" && (
|
||||
<h6 className="flex items-center gap-1 text-xs">
|
||||
<span className="text-custom-text-200">After:</span>
|
||||
<span>{renderShortDateWithYearFormat(watch("date1"))}</span>
|
||||
<span>{renderFormattedDate(watch("date1"))}</span>
|
||||
<span className="ml-1 text-custom-text-200">Before:</span>
|
||||
{!isInvalid && <span>{renderShortDateWithYearFormat(watch("date2"))}</span>}
|
||||
{!isInvalid && <span>{renderFormattedDate(watch("date2"))}</span>}
|
||||
</h6>
|
||||
)}
|
||||
<div className="flex justify-end gap-4">
|
||||
|
@ -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<Props> = ({ links, handleDeleteLink, handleEdit
|
||||
</div>
|
||||
<div className="px-5">
|
||||
<p className="mt-0.5 stroke-[1.5] text-xs text-custom-text-300">
|
||||
Added {timeAgo(link.created_at)}
|
||||
Added {calculateTimeAgo(link.created_at)}
|
||||
<br />
|
||||
by{" "}
|
||||
{link.created_by_detail.is_bot
|
||||
|
@ -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<Props> = ({ 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;
|
||||
}
|
||||
|
@ -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<IActiveCycleDetails> = observer((props
|
||||
<div className="flex items-center justify-start gap-5 text-custom-text-200">
|
||||
<div className="flex items-start gap-1">
|
||||
<CalendarDays className="h-4 w-4" />
|
||||
<span>{renderShortDateWithYearFormat(startDate)}</span>
|
||||
<span>{renderFormattedDate(startDate)}</span>
|
||||
</div>
|
||||
<ArrowRight className="h-4 w-4 text-custom-text-200" />
|
||||
<div className="flex items-start gap-1">
|
||||
<Target className="h-4 w-4" />
|
||||
<span>{renderShortDateWithYearFormat(endDate)}</span>
|
||||
<span>{renderFormattedDate(endDate)}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -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<ICyclesBoardCard> = (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<ICyclesBoardCard> = (props) => {
|
||||
<div className="flex items-center justify-between">
|
||||
{isDateValid ? (
|
||||
<span className="text-xs text-custom-text-300">
|
||||
{areYearsEqual ? renderShortDate(startDate, "_ _") : renderShortMonthDate(startDate, "_ _")} -{" "}
|
||||
{areYearsEqual ? renderShortDate(endDate, "_ _") : renderShortMonthDate(endDate, "_ _")}
|
||||
{renderFormattedDate(startDate) ?? "_ _"} - {renderFormattedDate(endDate) ?? "_ _"}
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-xs text-custom-text-400">No due date</span>
|
||||
|
@ -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<TCyclesListItem> = (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<TCyclesListItem> = (props) => {
|
||||
</div>
|
||||
|
||||
{renderDate && (
|
||||
<span className="flex w-28 items-center justify-center gap-2 text-xs text-custom-text-300">
|
||||
{areYearsEqual ? renderShortDate(startDate, "_ _") : renderShortMonthDate(startDate, "_ _")}
|
||||
{" - "}
|
||||
{areYearsEqual ? renderShortDate(endDate, "_ _") : renderShortMonthDate(endDate, "_ _")}
|
||||
<span className="flex w-40 items-center justify-center gap-2 text-xs text-custom-text-300">
|
||||
{renderFormattedDate(startDate) ?? "_ _"} - {renderFormattedDate(endDate) ?? "_ _"}
|
||||
</span>
|
||||
)}
|
||||
|
||||
|
@ -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 }) => {
|
||||
<div className="space-y-1">
|
||||
<h5>{data?.name}</h5>
|
||||
<div>
|
||||
{renderShortDate(data?.start_date ?? "")} to {renderShortDate(data?.end_date ?? "")}
|
||||
{renderFormattedDate(data?.start_date ?? "")} to {renderFormattedDate(data?.end_date ?? "")}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
@ -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<Props> = 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<Props> = 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<Props> = 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<Props> = 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<Props> = 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<Props> = observer((props) => {
|
||||
<div className="relative flex w-1/2 items-center rounded-sm">
|
||||
<Popover className="flex h-full w-full items-center justify-center rounded-lg">
|
||||
<Popover.Button
|
||||
className={`text-sm font-medium text-custom-text-300 w-full rounded-sm cursor-pointer hover:bg-custom-background-80 ${
|
||||
className={`w-full cursor-pointer rounded-sm text-sm font-medium text-custom-text-300 hover:bg-custom-background-80 ${
|
||||
isEditingAllowed ? "cursor-pointer" : "cursor-not-allowed"
|
||||
}`}
|
||||
disabled={isCompleted || !isEditingAllowed}
|
||||
>
|
||||
<span
|
||||
className={`group flex w-full items-center justify-between gap-2 py-1 px-1.5 text-sm ${
|
||||
className={`group flex w-full items-center justify-between gap-2 px-1.5 py-1 text-sm ${
|
||||
watch("start_date") ? "" : "text-custom-text-400"
|
||||
}`}
|
||||
>
|
||||
{areYearsEqual
|
||||
? renderShortDate(startDate, "No date selected")
|
||||
: renderShortMonthDate(startDate, "No date selected")}
|
||||
{renderFormattedDate(startDate) ?? "No date selected"}
|
||||
</span>
|
||||
</Popover.Button>
|
||||
|
||||
@ -450,19 +444,17 @@ export const CycleDetailsSidebar: React.FC<Props> = observer((props) => {
|
||||
<Popover className="flex h-full w-full items-center justify-center rounded-lg">
|
||||
<>
|
||||
<Popover.Button
|
||||
className={`text-sm font-medium text-custom-text-300 w-full rounded-sm cursor-pointer hover:bg-custom-background-80 ${
|
||||
className={`w-full cursor-pointer rounded-sm text-sm font-medium text-custom-text-300 hover:bg-custom-background-80 ${
|
||||
isEditingAllowed ? "cursor-pointer" : "cursor-not-allowed"
|
||||
}`}
|
||||
disabled={isCompleted || !isEditingAllowed}
|
||||
>
|
||||
<span
|
||||
className={`group flex w-full items-center justify-between gap-2 py-1 px-1.5 text-sm ${
|
||||
className={`group flex w-full items-center justify-between gap-2 px-1.5 py-1 text-sm ${
|
||||
watch("end_date") ? "" : "text-custom-text-400"
|
||||
}`}
|
||||
>
|
||||
{areYearsEqual
|
||||
? renderShortDate(endDate, "No date selected")
|
||||
: renderShortMonthDate(endDate, "No date selected")}
|
||||
{renderFormattedDate(endDate) ?? "No date selected"}
|
||||
</span>
|
||||
</Popover.Button>
|
||||
|
||||
|
@ -136,7 +136,7 @@ export const TransferIssuesModal: React.FC<Props> = observer(({ isOpen, handleCl
|
||||
<div className="flex w-full justify-between">
|
||||
<span>{option?.name}</span>
|
||||
<span className=" flex items-center rounded-full bg-custom-background-80 px-2 capitalize">
|
||||
{option.status}
|
||||
{option.status.toLocaleLowerCase()}
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
|
@ -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<Props> = ({ service, refreshing }) => {
|
||||
</span>
|
||||
</h4>
|
||||
<div className="mt-2 flex items-center gap-2 text-xs text-custom-text-200">
|
||||
<span>{renderShortDateWithYearFormat(service.created_at)}</span>|
|
||||
<span>{renderFormattedDate(service.created_at)}</span>|
|
||||
<span>Exported by {service?.initiated_by_detail?.display_name}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -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<GanttChartBlocksProps> = (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,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -94,7 +94,7 @@ export const CycleGanttSidebar: React.FC<Props> = (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 (
|
||||
<Draggable
|
||||
|
@ -94,7 +94,7 @@ export const ModuleGanttSidebar: React.FC<Props> = (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 (
|
||||
<Draggable
|
||||
|
@ -95,7 +95,7 @@ export const ProjectViewGanttSidebar: React.FC<Props> = (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 (
|
||||
<Draggable
|
||||
|
@ -111,7 +111,7 @@ export const IssueGanttSidebar: React.FC<Props> = (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 (
|
||||
<Draggable
|
||||
|
@ -1,12 +1,11 @@
|
||||
import { useRouter } from "next/router";
|
||||
import Link from "next/link";
|
||||
|
||||
// ui
|
||||
import { Tooltip, PriorityIcon } from "@plane/ui";
|
||||
// icons
|
||||
import { AlertTriangle, CalendarDays, CheckCircle2, Clock, Copy, XCircle } from "lucide-react";
|
||||
// helpers
|
||||
import { renderShortDateWithYearFormat } from "helpers/date-time.helper";
|
||||
import { renderFormattedDate } from "helpers/date-time.helper";
|
||||
// types
|
||||
import { IInboxIssue } from "types";
|
||||
// constants
|
||||
@ -45,11 +44,11 @@ export const InboxIssueCard: React.FC<Props> = (props) => {
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
tooltipHeading="Created on"
|
||||
tooltipContent={`${renderShortDateWithYearFormat(issue.created_at ?? "")}`}
|
||||
tooltipContent={`${renderFormattedDate(issue.created_at ?? "")}`}
|
||||
>
|
||||
<div className="flex items-center gap-1 rounded border border-custom-border-200 px-2 py-[0.19rem] text-xs text-custom-text-200 shadow-sm">
|
||||
<CalendarDays size={12} strokeWidth={1.5} />
|
||||
<span>{renderShortDateWithYearFormat(issue.created_at ?? "")}</span>
|
||||
<span>{renderFormattedDate(issue.created_at ?? "")}</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
@ -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() ? (
|
||||
<p>
|
||||
This issue was snoozed till{" "}
|
||||
{renderShortDateWithYearFormat(issueDetails.issue_inbox[0].snoozed_till ?? "")}.
|
||||
{renderFormattedDate(issueDetails.issue_inbox[0].snoozed_till ?? "")}.
|
||||
</p>
|
||||
) : (
|
||||
<p>
|
||||
This issue has been snoozed till{" "}
|
||||
{renderShortDateWithYearFormat(issueDetails.issue_inbox[0].snoozed_till ?? "")}.
|
||||
{renderFormattedDate(issueDetails.issue_inbox[0].snoozed_till ?? "")}.
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
|
@ -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<Props> = ({ service, refreshing, handleDelet
|
||||
</span>
|
||||
</h4>
|
||||
<div className="mt-2 flex items-center gap-2 text-xs text-custom-text-200">
|
||||
<span>{renderShortDateWithYearFormat(service.created_at)}</span>|
|
||||
<span>{renderFormattedDate(service.created_at)}</span>|
|
||||
<span>Imported by {service.initiated_by_detail.display_name}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -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<Props> = ({
|
||||
)}{" "}
|
||||
{message}{" "}
|
||||
<Tooltip
|
||||
tooltipContent={`${renderLongDateFormat(activityItem.created_at)}, ${render24HourFormatTime(
|
||||
tooltipContent={`${renderFormattedDate(activityItem.created_at)}, ${renderFormattedTime(
|
||||
activityItem.created_at
|
||||
)}`}
|
||||
>
|
||||
<span className="whitespace-nowrap">{timeAgo(activityItem.created_at)}</span>
|
||||
<span className="whitespace-nowrap">{calculateTimeAgo(activityItem.created_at)}</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -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> = (props) => {
|
||||
<Tooltip
|
||||
tooltipContent={`${
|
||||
people?.find((person) => person.member.id === file.updated_by)?.member.display_name ?? ""
|
||||
} uploaded on ${renderLongDateFormat(file.updated_at)}`}
|
||||
} uploaded on ${renderFormattedDate(file.updated_at)}`}
|
||||
>
|
||||
<span>
|
||||
<AlertCircle className="h-3 w-3" />
|
||||
|
@ -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<Props> = ({
|
||||
<div className="text-xs">
|
||||
{comment.actor_detail.is_bot ? comment.actor_detail.first_name + " Bot" : comment.actor_detail.display_name}
|
||||
</div>
|
||||
<p className="mt-0.5 text-xs text-custom-text-200">commented {timeAgo(comment.created_at)}</p>
|
||||
<p className="mt-0.5 text-xs text-custom-text-200">commented {calculateTimeAgo(comment.created_at)}</p>
|
||||
</div>
|
||||
<div className="issue-comments-section p-0">
|
||||
<form className={`flex-col gap-2 ${isEditing ? "flex" : "hidden"}`}>
|
||||
|
@ -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<Props> = 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<Props> = observer((props) => {
|
||||
|
||||
{/* content */}
|
||||
<div className="h-full w-full">
|
||||
<Droppable droppableId={renderDateFormat(date.date)} isDropDisabled={false}>
|
||||
<Droppable droppableId={formattedDatePayload} isDropDisabled={false}>
|
||||
{(provided, snapshot) => (
|
||||
<div
|
||||
className={`h-full w-full select-none overflow-y-auto ${
|
||||
@ -100,9 +102,9 @@ export const CalendarDayTile: React.FC<Props> = observer((props) => {
|
||||
<div className="px-2 py-1">
|
||||
<CalendarQuickAddIssueForm
|
||||
formKey="target_date"
|
||||
groupId={renderDateFormat(date.date)}
|
||||
groupId={formattedDatePayload}
|
||||
prePopulatedData={{
|
||||
target_date: renderDateFormat(date.date),
|
||||
target_date: renderFormattedPayloadDate(date.date),
|
||||
}}
|
||||
quickAddCallback={quickAddCallback}
|
||||
viewId={viewId}
|
||||
|
@ -2,7 +2,7 @@ import { observer } from "mobx-react-lite";
|
||||
// components
|
||||
import { CalendarDayTile } from "components/issues";
|
||||
// helpers
|
||||
import { renderDateFormat } from "helpers/date-time.helper";
|
||||
import { renderFormattedPayloadDate } from "helpers/date-time.helper";
|
||||
// types
|
||||
import { ICalendarDate, ICalendarWeek } from "./types";
|
||||
import { IIssue } from "types";
|
||||
@ -65,7 +65,7 @@ export const CalendarWeekDays: React.FC<Props> = observer((props) => {
|
||||
return (
|
||||
<CalendarDayTile
|
||||
issuesFilterStore={issuesFilterStore}
|
||||
key={renderDateFormat(date.date)}
|
||||
key={renderFormattedPayloadDate(date.date)}
|
||||
date={date}
|
||||
issues={issues}
|
||||
groupedIssueIds={groupedIssueIds}
|
||||
|
@ -1,9 +1,8 @@
|
||||
import { observer } from "mobx-react-lite";
|
||||
|
||||
// icons
|
||||
import { X } from "lucide-react";
|
||||
// helpers
|
||||
import { renderLongDateFormat } from "helpers/date-time.helper";
|
||||
import { renderFormattedDate } from "helpers/date-time.helper";
|
||||
import { capitalizeFirstLetter } from "helpers/string.helper";
|
||||
// constants
|
||||
import { DATE_FILTER_OPTIONS } from "constants/filters";
|
||||
@ -28,7 +27,7 @@ export const AppliedDateFilters: React.FC<Props> = observer((props) => {
|
||||
if (dateParts.length === 2) {
|
||||
const [date, time] = dateParts;
|
||||
|
||||
dateLabel = `${capitalizeFirstLetter(time)} ${renderLongDateFormat(date)}`;
|
||||
dateLabel = `${capitalizeFirstLetter(time)} ${renderFormattedDate(date)}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 }) => {
|
||||
<div className="space-y-1">
|
||||
<h5>{data?.name}</h5>
|
||||
<div>
|
||||
{renderShortDate(data?.start_date ?? "")} to {renderShortDate(data?.target_date ?? "")}
|
||||
{renderFormattedDate(data?.start_date ?? "")} to {renderFormattedDate(data?.target_date ?? "")}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
position="top-left"
|
||||
>
|
||||
<Tooltip tooltipHeading="Title" tooltipContent={data.name}>
|
||||
<div className="relative w-full truncate px-2.5 py-1 text-sm text-custom-text-100">{data?.name}</div>
|
||||
</Tooltip>
|
||||
<div className="relative w-full truncate px-2.5 py-1 text-sm text-custom-text-100">{data?.name}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
|
@ -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<IIssue>;
|
||||
@ -116,8 +115,8 @@ export const GanttInlineCreateIssueForm: React.FC<Props> = 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 {
|
||||
|
@ -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<IIssuePropertyDate> = observer((props)
|
||||
onChange={(val, e) => {
|
||||
e?.stopPropagation();
|
||||
if (onChange && val) {
|
||||
onChange(renderDateFormat(val));
|
||||
onChange(renderFormattedPayloadDate(val));
|
||||
close();
|
||||
}
|
||||
}}
|
||||
|
@ -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<Props> = ({ issue, expandedIss
|
||||
return (
|
||||
<>
|
||||
<div className="flex h-11 w-full items-center justify-center text-xs border-b-[0.5px] border-custom-border-200 hover:bg-custom-background-80">
|
||||
{renderLongDetailDateFormat(issue.created_at)}
|
||||
{renderFormattedDate(issue.created_at)}
|
||||
</div>
|
||||
|
||||
{isExpanded &&
|
||||
|
@ -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> = (props) => {
|
||||
return (
|
||||
<>
|
||||
<div className="flex h-11 w-full items-center justify-center text-xs border-b-[0.5px] border-custom-border-200 hover:bg-custom-background-80">
|
||||
{renderLongDetailDateFormat(issue.updated_at)}
|
||||
{renderFormattedDate(issue.updated_at)}
|
||||
</div>
|
||||
|
||||
{isExpanded &&
|
||||
|
@ -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<IIssueActivityCard> = (props) => {
|
||||
)}
|
||||
{message}
|
||||
<Tooltip
|
||||
tooltipContent={`${renderLongDateFormat(activityItem.created_at)}, ${render24HourFormatTime(
|
||||
tooltipContent={`${renderFormattedDate(activityItem.created_at)}, ${renderFormattedTime(
|
||||
activityItem.created_at
|
||||
)}`}
|
||||
>
|
||||
<span className="whitespace-nowrap">{timeAgo(activityItem.created_at)}</span>
|
||||
<span className="whitespace-nowrap">{calculateTimeAgo(activityItem.created_at)}</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -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<IIssueCommentCard> = (props) => {
|
||||
<div className="text-xs">
|
||||
{comment.actor_detail.is_bot ? comment.actor_detail.first_name + " Bot" : comment.actor_detail.display_name}
|
||||
</div>
|
||||
<p className="mt-0.5 text-xs text-custom-text-200">commented {timeAgo(comment.created_at)}</p>
|
||||
<p className="mt-0.5 text-xs text-custom-text-200">commented {calculateTimeAgo(comment.created_at)}</p>
|
||||
</div>
|
||||
|
||||
<div className="issue-comments-section p-0">
|
||||
|
@ -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<Props> = ({ label, maxDate, minDate, onChange,
|
||||
{value ? (
|
||||
<>
|
||||
<CalendarDays className="h-3 w-3 flex-shrink-0" />
|
||||
<span>{renderShortDateWithYearFormat(value)}</span>
|
||||
<span>{renderFormattedDate(value)}</span>
|
||||
<button onClick={() => onChange(null)}>
|
||||
<X className="h-3 w-3 flex-shrink-0" />
|
||||
</button>
|
||||
@ -69,7 +69,7 @@ export const IssueDateSelect: FC<Props> = ({ label, maxDate, minDate, onChange,
|
||||
selected={value ? new Date(value) : null}
|
||||
onChange={(val) => {
|
||||
if (!val) onChange("");
|
||||
else onChange(renderDateFormat(val));
|
||||
else onChange(renderFormattedPayloadDate(val));
|
||||
|
||||
close();
|
||||
}}
|
||||
|
@ -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<Props> = ({
|
||||
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 (
|
||||
<Tooltip
|
||||
tooltipHeading="Due date"
|
||||
tooltipContent={issue.target_date ? renderShortDateWithYearFormat(issue.target_date) ?? "N/A" : "N/A"}
|
||||
tooltipContent={issue.target_date ? renderFormattedDate(issue.target_date) ?? "N/A" : "N/A"}
|
||||
position={tooltipPosition}
|
||||
>
|
||||
<div
|
||||
@ -68,11 +59,7 @@ export const ViewDueDateSelect: React.FC<Props> = ({
|
||||
{issue.target_date ? (
|
||||
<>
|
||||
<CalendarCheck className="h-3.5 w-3.5 flex-shrink-0" />
|
||||
<span>
|
||||
{areYearsEqual
|
||||
? renderShortDate(issue.target_date ?? "", "_ _")
|
||||
: renderShortMonthDate(issue.target_date ?? "", "_ _")}
|
||||
</span>
|
||||
<span>{renderFormattedDate(issue.target_date) ?? "_ _"}</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
|
@ -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<Props> = ({
|
||||
}) => {
|
||||
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 (
|
||||
<Tooltip
|
||||
tooltipHeading="Start date"
|
||||
tooltipContent={issue.start_date ? renderShortDateWithYearFormat(issue.start_date) ?? "N/A" : "N/A"}
|
||||
tooltipContent={issue.start_date ? renderFormattedDate(issue.start_date) ?? "N/A" : "N/A"}
|
||||
position={tooltipPosition}
|
||||
>
|
||||
<div className={`group max-w-[6.5rem] flex-shrink-0 ${className}`}>
|
||||
@ -56,11 +53,7 @@ export const ViewStartDateSelect: React.FC<Props> = ({
|
||||
{issue?.start_date ? (
|
||||
<>
|
||||
<CalendarClock className="h-3.5 w-3.5 flex-shrink-0" />
|
||||
<span>
|
||||
{areYearsEqual
|
||||
? renderShortDate(issue?.start_date, "_ _")
|
||||
: renderShortMonthDate(issue?.start_date, "_ _")}
|
||||
</span>
|
||||
<span>{renderFormattedDate(issue?.start_date ?? "_ _")}</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
|
@ -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 }) => {
|
||||
<div className="space-y-1">
|
||||
<h5>{data?.name}</h5>
|
||||
<div>
|
||||
{renderShortDate(data?.start_date ?? "")} to {renderShortDate(data?.target_date ?? "")}
|
||||
{renderFormattedDate(data?.start_date ?? "")} to {renderFormattedDate(data?.target_date ?? "")}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
@ -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<Props> = 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<Props> = observer((props) => {
|
||||
{isDateValid ? (
|
||||
<>
|
||||
<span className="text-xs text-custom-text-300">
|
||||
{areYearsEqual ? renderShortDate(startDate, "_ _") : renderShortMonthDate(startDate, "_ _")} -{" "}
|
||||
{areYearsEqual ? renderShortDate(endDate, "_ _") : renderShortMonthDate(endDate, "_ _")}
|
||||
{renderFormattedDate(startDate) ?? "_ _"} - {renderFormattedDate(endDate) ?? "_ _"}
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
|
@ -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<Props> = 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<Props> = observer((props) => {
|
||||
</div>
|
||||
|
||||
{renderDate && (
|
||||
<span className="flex w-28 items-center justify-center gap-2 text-xs text-custom-text-300">
|
||||
{areYearsEqual ? renderShortDate(startDate, "_ _") : renderShortMonthDate(startDate, "_ _")}
|
||||
{" - "}
|
||||
{areYearsEqual ? renderShortDate(endDate, "_ _") : renderShortMonthDate(endDate, "_ _")}
|
||||
<span className="flex w-40 items-center justify-center gap-2 text-xs text-custom-text-300">
|
||||
{renderFormattedDate(startDate) ?? "_ _"} - {renderFormattedDate(endDate) ?? "_ _"}
|
||||
</span>
|
||||
)}
|
||||
|
||||
|
@ -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<Props> = 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<Props> = 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<Props> = 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<Props> = observer((props) => {
|
||||
<div className="relative flex w-1/2 items-center rounded-sm">
|
||||
<Popover className="flex h-full w-full items-center justify-center rounded-lg">
|
||||
<Popover.Button
|
||||
className={`text-sm font-medium text-custom-text-300 w-full rounded-sm cursor-pointer hover:bg-custom-background-80 ${
|
||||
className={`w-full cursor-pointer rounded-sm text-sm font-medium text-custom-text-300 hover:bg-custom-background-80 ${
|
||||
isEditingAllowed ? "cursor-pointer" : "cursor-not-allowed"
|
||||
}`}
|
||||
disabled={!isEditingAllowed}
|
||||
>
|
||||
<span
|
||||
className={`group flex w-full items-center justify-between gap-2 py-1 px-1.5 text-sm ${
|
||||
className={`group flex w-full items-center justify-between gap-2 px-1.5 py-1 text-sm ${
|
||||
watch("start_date") ? "" : "text-custom-text-400"
|
||||
}`}
|
||||
>
|
||||
{areYearsEqual
|
||||
? renderShortDate(startDate, "No date selected")
|
||||
: renderShortMonthDate(startDate, "No date selected")}
|
||||
{renderFormattedDate(startDate) ?? "No date selected"}
|
||||
</span>
|
||||
</Popover.Button>
|
||||
|
||||
@ -453,19 +443,17 @@ export const ModuleDetailsSidebar: React.FC<Props> = observer((props) => {
|
||||
<Popover className="flex h-full w-full items-center justify-center rounded-lg">
|
||||
<>
|
||||
<Popover.Button
|
||||
className={`text-sm font-medium text-custom-text-300 w-full rounded-sm cursor-pointer hover:bg-custom-background-80 ${
|
||||
className={`w-full cursor-pointer rounded-sm text-sm font-medium text-custom-text-300 hover:bg-custom-background-80 ${
|
||||
isEditingAllowed ? "cursor-pointer" : "cursor-not-allowed"
|
||||
}`}
|
||||
disabled={!isEditingAllowed}
|
||||
>
|
||||
<span
|
||||
className={`group flex w-full items-center justify-between gap-2 py-1 px-1.5 text-sm ${
|
||||
className={`group flex w-full items-center justify-between gap-2 px-1.5 py-1 text-sm ${
|
||||
watch("target_date") ? "" : "text-custom-text-400"
|
||||
}`}
|
||||
>
|
||||
{areYearsEqual
|
||||
? renderShortDate(endDate, "No date selected")
|
||||
: renderShortMonthDate(endDate, "No date selected")}
|
||||
{renderFormattedDate(endDate) ?? "No date selected"}
|
||||
</span>
|
||||
</Popover.Button>
|
||||
|
||||
|
@ -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<NotificationCardProps> = (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<NotificationCardProps> = (props) => {
|
||||
<p className="flex flex-shrink-0 items-center justify-end gap-x-1 text-custom-text-300">
|
||||
<Clock className="h-4 w-4" />
|
||||
<span>
|
||||
Till {renderShortDate(notification.snoozed_till)}, {render12HourFormatTime(notification.snoozed_till)}
|
||||
Till {renderFormattedDate(notification.snoozed_till)}, {renderFormattedTime(notification.snoozed_till, '12-hour')}
|
||||
</span>
|
||||
</p>
|
||||
) : (
|
||||
<p className="flex-shrink-0 text-custom-text-300">{formatDateDistance(notification.created_at)}</p>
|
||||
<p className="flex-shrink-0 text-custom-text-300">{calculateTimeAgo(notification.created_at)}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@ -233,7 +231,7 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
|
||||
|
||||
markSnoozeNotification(notification.id, item.value).then(() => {
|
||||
setToastAlert({
|
||||
title: `Notification snoozed till ${renderLongDateFormat(item.value)}`,
|
||||
title: `Notification snoozed till ${renderFormattedDate(item.value)}`,
|
||||
type: "success",
|
||||
});
|
||||
});
|
||||
|
@ -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<SnoozeModalProps> = (props) => {
|
||||
const { isOpen, onClose, notification, onSuccess, onSubmit: handleSubmitSnooze } = props;
|
||||
|
@ -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<IPagesListItem> = observer((props) => {
|
||||
<div className="flex items-center gap-2.5">
|
||||
{page.archived_at ? (
|
||||
<Tooltip
|
||||
tooltipContent={`Archived at ${render24HourFormatTime(page.archived_at)} on ${renderFormattedDate(
|
||||
tooltipContent={`Archived at ${renderFormattedTime(page.archived_at)} on ${renderFormattedDate(
|
||||
page.archived_at
|
||||
)}`}
|
||||
>
|
||||
<p className="text-sm text-custom-text-200">{render24HourFormatTime(page.archived_at)}</p>
|
||||
<p className="text-sm text-custom-text-200">{renderFormattedTime(page.archived_at)}</p>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Tooltip
|
||||
tooltipContent={`Last updated at ${render24HourFormatTime(
|
||||
tooltipContent={`Last updated at ${renderFormattedTime(
|
||||
page.updated_at
|
||||
)} on ${renderFormattedDate(page.updated_at)}`}
|
||||
>
|
||||
<p className="text-sm text-custom-text-200">{render24HourFormatTime(page.updated_at)}</p>
|
||||
<p className="text-sm text-custom-text-200">{renderFormattedTime(page.updated_at)}</p>
|
||||
</Tooltip>
|
||||
)}
|
||||
{isEditingAllowed && (
|
||||
|
@ -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 = () => {
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
<p className="text-xs text-custom-text-200">{timeAgo(activity.created_at)}</p>
|
||||
<p className="text-xs text-custom-text-200">{calculateTimeAgo(activity.created_at)}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
@ -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",
|
||||
|
@ -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<IProjectDetailsForm> = (props) => {
|
||||
{isSubmitting ? "Updating Project..." : "Update Project"}
|
||||
</Button>
|
||||
<span className="text-sm italic text-custom-sidebar-text-400">
|
||||
Created on {renderShortDateWithYearFormat(project?.created_at)}
|
||||
Created on {renderFormattedDate(project?.created_at)}
|
||||
</span>
|
||||
</>
|
||||
</div>
|
||||
|
@ -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<Props> = ({ value, onChange, label, minDate, m
|
||||
{value ? (
|
||||
<>
|
||||
<CalendarDays className="h-3 w-3 flex-shrink-0" />
|
||||
<span>{renderShortDateWithYearFormat(value)}</span>
|
||||
<span>{renderFormattedDate(value)}</span>
|
||||
<button onClick={() => onChange(null)}>
|
||||
<X className="h-3 w-3" />
|
||||
</button>
|
||||
@ -52,7 +51,7 @@ export const DateSelect: React.FC<Props> = ({ 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();
|
||||
}}
|
||||
|
@ -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<Props> = ({
|
||||
selected={value ? new Date(value) : null}
|
||||
onChange={(val) => {
|
||||
if (!val) onChange(null);
|
||||
else onChange(renderDateFormat(val));
|
||||
else onChange(renderFormattedPayloadDate(val));
|
||||
}}
|
||||
onCalendarOpen={handleOnOpen}
|
||||
onCalendarClose={handleOnClose}
|
||||
|
@ -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<Props> = ({ isOpen, setIsOpen }) => {
|
||||
<span className="flex items-center rounded-full border border-custom-border-200 bg-custom-background-90 px-3 py-1.5 text-xs">
|
||||
{item.tag_name}
|
||||
</span>
|
||||
<span>{renderLongDateFormat(item.published_at)}</span>
|
||||
<span>{renderFormattedDate(item.published_at)}</span>
|
||||
{index === 0 && (
|
||||
<span className="flex items-center rounded-full border border-custom-border-200 bg-custom-primary px-3 py-1.5 text-xs text-white">
|
||||
New
|
||||
|
@ -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<Props> = ({
|
||||
selected={value ? new Date(value) : null}
|
||||
onChange={(val) => {
|
||||
if (!val) onChange(null);
|
||||
else onChange(renderDateFormat(val));
|
||||
else onChange(renderFormattedPayloadDate(val));
|
||||
}}
|
||||
className={`${
|
||||
renderAs === "input"
|
||||
|
@ -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() || "",
|
||||
|
@ -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<Props> = ({ 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<Props> = ({ activities }) => {
|
||||
key={`${date}-${index}`}
|
||||
tooltipContent={`${
|
||||
isActive ? isActive.activity_count : 0
|
||||
} activities on ${renderShortDateWithYearFormat(date)}`}
|
||||
} activities on ${renderFormattedDate(date)}`}
|
||||
>
|
||||
<div
|
||||
className={`${date === "" ? "pointer-events-none opacity-0" : ""} h-4 w-4 rounded ${
|
||||
|
@ -1,11 +1,10 @@
|
||||
import { useRouter } from "next/router";
|
||||
import Link from "next/link";
|
||||
|
||||
// icons
|
||||
import { AlertTriangle } from "lucide-react";
|
||||
import { LayersIcon, Loader } from "@plane/ui";
|
||||
// helpers
|
||||
import { renderShortDateWithYearFormat } from "helpers/date-time.helper";
|
||||
import { renderFormattedDate } from "helpers/date-time.helper";
|
||||
import { truncateText } from "helpers/string.helper";
|
||||
// types
|
||||
import { IIssueLite } from "types";
|
||||
@ -67,7 +66,7 @@ export const IssuesList: React.FC<Props> = ({ issues, type }) => {
|
||||
</h5>
|
||||
<h5 className="col-span-2">{truncateText(issue.name, 30)}</h5>
|
||||
<h5 className="cursor-default">
|
||||
{renderShortDateWithYearFormat(new Date(date?.toString() ?? ""))}
|
||||
{renderFormattedDate(new Date(date?.toString() ?? ""))}
|
||||
</h5>
|
||||
</div>
|
||||
</span>
|
||||
|
@ -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" },
|
||||
];
|
||||
|
@ -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;
|
||||
|
@ -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<Date> => {
|
||||
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("-");
|
||||
};
|
||||
|
@ -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"],
|
||||
() =>
|
||||
|
@ -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}
|
||||
</div>
|
||||
<p className="mt-0.5 text-xs text-custom-text-200">
|
||||
Commented {timeAgo(activityItem.created_at)}
|
||||
Commented {calculateTimeAgo(activityItem.created_at)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="issue-comments-section p-0">
|
||||
@ -165,7 +165,7 @@ const ProfileActivityPage: NextPageWithLayout = () => {
|
||||
<div className="flex gap-1 truncate">
|
||||
{message}{" "}
|
||||
<span className="flex-shrink-0 whitespace-nowrap">
|
||||
{timeAgo(activityItem.created_at)}
|
||||
{calculateTimeAgo(activityItem.created_at)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -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: {
|
||||
|
@ -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"];
|
||||
}
|
||||
}
|
||||
|
@ -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()) },
|
||||
],
|
||||
};
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user