fix date related exceptions

This commit is contained in:
rahulramesha 2024-03-19 19:57:32 +05:30
parent 1a462711e1
commit f8a5095f14
10 changed files with 81 additions and 44 deletions

View File

@ -7,6 +7,7 @@ import { AlertLabel } from "src/ui/components/alert-label";
import { IVerticalDropdownItemProps, VerticalDropdownMenu } from "src/ui/components/vertical-dropdown-menu"; import { IVerticalDropdownItemProps, VerticalDropdownMenu } from "src/ui/components/vertical-dropdown-menu";
import { SummaryPopover } from "src/ui/components/summary-popover"; import { SummaryPopover } from "src/ui/components/summary-popover";
import { InfoPopover } from "src/ui/components/info-popover"; import { InfoPopover } from "src/ui/components/info-popover";
import { getDate } from "src/utils/date-utils";
interface IEditorHeader { interface IEditorHeader {
editor: Editor; editor: Editor;
@ -72,7 +73,7 @@ export const EditorHeader = (props: IEditorHeader) => {
Icon={Archive} Icon={Archive}
backgroundColor="bg-blue-500/20" backgroundColor="bg-blue-500/20"
textColor="text-blue-500" textColor="text-blue-500"
label={`Archived at ${archivedAt.toLocaleString()}`} label={`Archived at ${getDate(archivedAt)?.toLocaleString()}`}
/> />
)} )}

View File

@ -3,13 +3,15 @@ import { usePopper } from "react-popper";
import { Calendar, History, Info } from "lucide-react"; import { Calendar, History, Info } from "lucide-react";
// types // types
import { DocumentDetails } from "src/types/editor-types"; import { DocumentDetails } from "src/types/editor-types";
//utils
import { getDate } from "src/utils/date-utils";
type Props = { type Props = {
documentDetails: DocumentDetails; documentDetails: DocumentDetails;
}; };
// function to render a Date in the format- 25 May 2023 at 2:53PM // function to render a Date in the format- 25 May 2023 at 2:53PM
const renderDate = (date: Date): string => { const renderDate = (date: Date | undefined): string => {
const options: Intl.DateTimeFormatOptions = { const options: Intl.DateTimeFormatOptions = {
day: "numeric", day: "numeric",
month: "long", month: "long",
@ -52,14 +54,14 @@ export const InfoPopover: React.FC<Props> = (props) => {
<h6 className="text-xs text-custom-text-400">Last updated on</h6> <h6 className="text-xs text-custom-text-400">Last updated on</h6>
<h5 className="flex items-center gap-1 text-sm"> <h5 className="flex items-center gap-1 text-sm">
<History className="h-3 w-3" /> <History className="h-3 w-3" />
{renderDate(documentDetails.last_updated_at)} {renderDate(getDate(documentDetails?.last_updated_at))}
</h5> </h5>
</div> </div>
<div className="space-y-1.5"> <div className="space-y-1.5">
<h6 className="text-xs text-custom-text-400">Created on</h6> <h6 className="text-xs text-custom-text-400">Created on</h6>
<h5 className="flex items-center gap-1 text-sm"> <h5 className="flex items-center gap-1 text-sm">
<Calendar className="h-3 w-3" /> <Calendar className="h-3 w-3" />
{renderDate(documentDetails.created_on)} {renderDate(getDate(documentDetails?.created_on))}
</h5> </h5>
</div> </div>
</div> </div>

View File

@ -0,0 +1,22 @@
function isNumber(value: any) {
return typeof value === "number";
}
/**
* This method returns a date from string of type yyyy-mm-dd
* This method is recommended to use instead of new Date() as this does not introduce any timezone offsets
* @param date
* @returns date or undefined
*/
export const getDate = (date: string | Date | undefined | null): Date | undefined => {
if (!date || date === "") return;
if (typeof date !== "string" && !(date instanceof String)) return date;
const [yearString, monthString, dayString] = date.substring(0, 10).split("-");
const year = parseInt(yearString);
const month = parseInt(monthString);
const day = parseInt(dayString);
if (!isNumber(year) || !isNumber(month) || !isNumber(day)) return;
return new Date(year, month - 1, day);
};

View File

@ -6,7 +6,7 @@ export interface IPage {
archived_at: string | null; archived_at: string | null;
blocks: IPageBlock[]; blocks: IPageBlock[];
color: string; color: string;
created_at: Date; created_at: string | null;
created_by: string; created_by: string;
description: string; description: string;
description_html: string; description_html: string;
@ -20,7 +20,7 @@ export interface IPage {
owned_by: string; owned_by: string;
project: string; project: string;
project_detail: IProjectLite; project_detail: IProjectLite;
updated_at: Date; updated_at: string | null;
updated_by: string; updated_by: string;
workspace: string; workspace: string;
workspace_detail: IWorkspaceLite; workspace_detail: IWorkspaceLite;

View File

@ -13,7 +13,7 @@ import { ArchiveIcon, CustomMenu, Tooltip } from "@plane/ui";
import { snoozeOptions } from "constants/notification"; import { snoozeOptions } from "constants/notification";
// helper // helper
import { replaceUnderscoreIfSnakeCase, truncateText, stripAndTruncateHTML } from "helpers/string.helper"; import { replaceUnderscoreIfSnakeCase, truncateText, stripAndTruncateHTML } from "helpers/string.helper";
import { calculateTimeAgo, renderFormattedTime, renderFormattedDate } from "helpers/date-time.helper"; import { calculateTimeAgo, renderFormattedTime, renderFormattedDate, getDate } from "helpers/date-time.helper";
// type // type
import type { IUserNotification, NotificationType } from "@plane/types"; import type { IUserNotification, NotificationType } from "@plane/types";
// constants // constants
@ -119,7 +119,9 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
const notificationField = notification.data.issue_activity.field; const notificationField = notification.data.issue_activity.field;
const notificationTriggeredBy = notification.triggered_by_details; const notificationTriggeredBy = notification.triggered_by_details;
if (isSnoozedTabOpen && notification.snoozed_till! < new Date()) return null; const snoozedTillDate = getDate(notification?.snoozed_till);
if (snoozedTillDate && isSnoozedTabOpen && snoozedTillDate < new Date()) return null;
return ( return (
<Link <Link

View File

@ -12,6 +12,8 @@ import useToast from "hooks/use-toast";
import { Button, CustomSelect } from "@plane/ui"; import { Button, CustomSelect } from "@plane/ui";
// types // types
import type { IUserNotification } from "@plane/types"; import type { IUserNotification } from "@plane/types";
// helpers
import { getDate } from "helpers/date-time.helper";
type SnoozeModalProps = { type SnoozeModalProps = {
isOpen: boolean; isOpen: boolean;
@ -60,7 +62,7 @@ export const SnoozeNotificationModal: FC<SnoozeModalProps> = (props) => {
if (!formDataDate) return timeStamps; if (!formDataDate) return timeStamps;
const isToday = today.toDateString() === formDataDate.toDateString(); const isToday = today.toDateString() === getDate(formDataDate)?.toDateString();
if (!isToday) return timeStamps; if (!isToday) return timeStamps;
@ -93,9 +95,9 @@ export const SnoozeNotificationModal: FC<SnoozeModalProps> = (props) => {
); );
const minutes = parseInt(time[1]); const minutes = parseInt(time[1]);
const dateTime = formData.date; const dateTime: Date | undefined = getDate(formData?.date);
dateTime.setHours(hours); dateTime?.setHours(hours);
dateTime.setMinutes(minutes); dateTime?.setMinutes(minutes);
await handleSubmitSnooze(notification.id, dateTime).then(() => { await handleSubmitSnooze(notification.id, dateTime).then(() => {
handleClose(); handleClose();
@ -214,10 +216,11 @@ export const SnoozeNotificationModal: FC<SnoozeModalProps> = (props) => {
onClick={() => { onClick={() => {
setValue("period", "AM"); setValue("period", "AM");
}} }}
className={`flex h-full w-1/2 cursor-pointer items-center justify-center text-center ${watch("period") === "AM" className={`flex h-full w-1/2 cursor-pointer items-center justify-center text-center ${
watch("period") === "AM"
? "bg-custom-primary-100/90 text-custom-primary-0" ? "bg-custom-primary-100/90 text-custom-primary-0"
: "bg-custom-background-80" : "bg-custom-background-80"
}`} }`}
> >
AM AM
</div> </div>
@ -225,10 +228,11 @@ export const SnoozeNotificationModal: FC<SnoozeModalProps> = (props) => {
onClick={() => { onClick={() => {
setValue("period", "PM"); setValue("period", "PM");
}} }}
className={`flex h-full w-1/2 cursor-pointer items-center justify-center text-center ${watch("period") === "PM" className={`flex h-full w-1/2 cursor-pointer items-center justify-center text-center ${
watch("period") === "PM"
? "bg-custom-primary-100/90 text-custom-primary-0" ? "bg-custom-primary-100/90 text-custom-primary-0"
: "bg-custom-background-80" : "bg-custom-background-80"
}`} }`}
> >
PM PM
</div> </div>

View File

@ -8,7 +8,7 @@ import isNumber from "lodash/isNumber";
* @param {Date | string} date * @param {Date | string} date
* @example renderFormattedDate("2024-01-01") // Jan 01, 2024 * @example renderFormattedDate("2024-01-01") // Jan 01, 2024
*/ */
export const renderFormattedDate = (date: string | Date | undefined): string | null => { export const renderFormattedDate = (date: string | Date | undefined | null): string | null => {
// Parse the date to check if it is valid // Parse the date to check if it is valid
const parsedDate = getDate(date); const parsedDate = getDate(date);
// return if undefined // return if undefined
@ -65,7 +65,10 @@ export const renderFormattedPayloadDate = (date: Date | string): string | null =
* @example renderFormattedTime("2024-01-01 13:00:00") // 13:00 * @example renderFormattedTime("2024-01-01 13:00:00") // 13:00
* @example renderFormattedTime("2024-01-01 13:00:00", "12-hour") // 01:00 PM * @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 => { export const renderFormattedTime = (
date: string | Date | undefined | null,
timeFormat: "12-hour" | "24-hour" = "24-hour"
): string => {
// Parse the date to check if it is valid // Parse the date to check if it is valid
const parsedDate = getDate(date); const parsedDate = getDate(date);
// return if undefined // return if undefined

View File

@ -272,8 +272,8 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
documentDetails={{ documentDetails={{
title: pageTitle, title: pageTitle,
created_by: created_by, created_by: created_by,
created_on: created_at, created_on: getDate(created_at) ?? new Date(created_at ?? ""),
last_updated_at: updated_at, last_updated_at: getDate(updated_at) ?? new Date(created_at ?? ""),
last_updated_by: updated_by, last_updated_by: updated_by,
}} }}
pageLockConfig={userCanLock && !archived_at ? { action: unlockPage, is_locked: is_locked } : undefined} pageLockConfig={userCanLock && !archived_at ? { action: unlockPage, is_locked: is_locked } : undefined}
@ -283,7 +283,7 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
? { ? {
action: archived_at ? unArchivePage : archivePage, action: archived_at ? unArchivePage : archivePage,
is_archived: archived_at ? true : false, is_archived: archived_at ? true : false,
archived_at: archived_at ? getDate(archived_at) : undefined, archived_at: getDate(archived_at),
} }
: undefined : undefined
} }
@ -299,8 +299,8 @@ const PageDetailsPage: NextPageWithLayout = observer(() => {
documentDetails={{ documentDetails={{
title: pageTitle, title: pageTitle,
created_by: created_by, created_by: created_by,
created_on: created_at, created_on: getDate(created_at) ?? new Date(created_at ?? ""),
last_updated_at: updated_at, last_updated_at: getDate(updated_at) ?? new Date(created_at ?? ""),
last_updated_by: updated_by, last_updated_by: updated_by,
}} }}
uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)} uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)}

View File

@ -10,7 +10,7 @@ export interface IPageStore {
access: number; access: number;
archived_at: string | null; archived_at: string | null;
color: string; color: string;
created_at: Date; created_at: string | null;
created_by: string; created_by: string;
description: string; description: string;
description_html: string; description_html: string;
@ -23,7 +23,7 @@ export interface IPageStore {
name: string; name: string;
owned_by: string; owned_by: string;
project: string; project: string;
updated_at: Date; updated_at: string | null;
updated_by: string; updated_by: string;
workspace: string; workspace: string;
@ -52,7 +52,7 @@ export class PageStore implements IPageStore {
isSubmitting: "submitting" | "submitted" | "saved" = "saved"; isSubmitting: "submitting" | "submitted" | "saved" = "saved";
archived_at: string | null; archived_at: string | null;
color: string; color: string;
created_at: Date; created_at: string | null;
created_by: string; created_by: string;
description: string; description: string;
description_html = ""; description_html = "";
@ -64,7 +64,7 @@ export class PageStore implements IPageStore {
name = ""; name = "";
owned_by: string; owned_by: string;
project: string; project: string;
updated_at: Date; updated_at: string | null;
updated_by: string; updated_by: string;
workspace: string; workspace: string;
oldName = ""; oldName = "";
@ -94,9 +94,9 @@ export class PageStore implements IPageStore {
cleanup: action, cleanup: action,
}); });
this.created_by = page?.created_by || ""; this.created_by = page?.created_by || "";
this.created_at = page?.created_at || new Date(); this.created_at = page?.created_at ?? "";
this.color = page?.color || ""; this.color = page?.color || "";
this.archived_at = page?.archived_at || null; this.archived_at = page?.archived_at ?? null;
this.name = page?.name || ""; this.name = page?.name || "";
this.description = page?.description || ""; this.description = page?.description || "";
this.description_stripped = page?.description_stripped || ""; this.description_stripped = page?.description_stripped || "";
@ -104,7 +104,7 @@ export class PageStore implements IPageStore {
this.access = page?.access || 0; this.access = page?.access || 0;
this.workspace = page?.workspace || ""; this.workspace = page?.workspace || "";
this.updated_by = page?.updated_by || ""; this.updated_by = page?.updated_by || "";
this.updated_at = page?.updated_at || new Date(); this.updated_at = page?.updated_at ?? "";
this.project = page?.project || ""; this.project = page?.project || "";
this.owned_by = page?.owned_by || ""; this.owned_by = page?.owned_by || "";
this.labels = page?.labels || []; this.labels = page?.labels || [];

View File

@ -8,6 +8,7 @@ import { PageStore, IPageStore } from "store/page.store";
import { IPage, IRecentPages } from "@plane/types"; import { IPage, IRecentPages } from "@plane/types";
import { RootStore } from "./root.store"; import { RootStore } from "./root.store";
import { isThisWeek, isToday, isYesterday } from "date-fns"; import { isThisWeek, isToday, isYesterday } from "date-fns";
import { getDate } from "helpers/date-time.helper";
export interface IProjectPageStore { export interface IProjectPageStore {
loader: boolean; loader: boolean;
@ -73,8 +74,8 @@ export class ProjectPageStore implements IProjectPageStore {
const allProjectIds = Object.keys(this.projectPageMap[projectId]); const allProjectIds = Object.keys(this.projectPageMap[projectId]);
return allProjectIds.sort((a, b) => { return allProjectIds.sort((a, b) => {
const dateA = this.projectPageMap[projectId][a].created_at.getTime(); const dateA = getDate(this.projectPageMap[projectId]?.[a]?.created_at)?.getTime() ?? 0;
const dateB = this.projectPageMap[projectId][b].created_at.getTime(); const dateB = getDate(this.projectPageMap[projectId]?.[b]?.created_at)?.getTime() ?? 0;
return dateB - dateA; return dateB - dateA;
}); });
} }
@ -84,8 +85,8 @@ export class ProjectPageStore implements IProjectPageStore {
if (!projectId || !this.projectArchivedPageMap[projectId]) return []; if (!projectId || !this.projectArchivedPageMap[projectId]) return [];
const archivedPages = Object.keys(this.projectArchivedPageMap[projectId]); const archivedPages = Object.keys(this.projectArchivedPageMap[projectId]);
return archivedPages.sort((a, b) => { return archivedPages.sort((a, b) => {
const dateA = this.projectArchivedPageMap[projectId][a].created_at.getTime(); const dateA = getDate(this.projectArchivedPageMap[projectId]?.[a]?.created_at)?.getTime() ?? 0;
const dateB = this.projectArchivedPageMap[projectId][b].created_at.getTime(); const dateB = getDate(this.projectArchivedPageMap[projectId]?.[b]?.created_at)?.getTime() ?? 0;
return dateB - dateA; return dateB - dateA;
}); });
} }
@ -126,22 +127,24 @@ export class ProjectPageStore implements IProjectPageStore {
const projectId = this.rootStore.app.router.projectId; const projectId = this.rootStore.app.router.projectId;
if (!this.projectPageIds || !projectId) return; if (!this.projectPageIds || !projectId) return;
const today: string[] = this.projectPageIds.filter((page) => const today: string[] = this.projectPageIds.filter((page) => {
isToday(this.projectPageMap[projectId][page].updated_at) const updatedAt = getDate(this.projectPageMap[projectId]?.[page]?.updated_at);
); return updatedAt && isToday(updatedAt);
});
const yesterday: string[] = this.projectPageIds.filter((page) => const yesterday: string[] = this.projectPageIds.filter((page) => {
isYesterday(this.projectPageMap[projectId][page].updated_at) const updatedAt = getDate(this.projectPageMap[projectId]?.[page]?.updated_at);
); return updatedAt && isYesterday(updatedAt);
});
const this_week: string[] = this.projectPageIds.filter((page) => { const this_week: string[] = this.projectPageIds.filter((page) => {
const pageUpdatedAt = this.projectPageMap[projectId][page].updated_at; const pageUpdatedAt = getDate(this.projectPageMap[projectId]?.[page]?.updated_at);
return isThisWeek(pageUpdatedAt) && !isToday(pageUpdatedAt) && !isYesterday(pageUpdatedAt); return pageUpdatedAt && isThisWeek(pageUpdatedAt) && !isToday(pageUpdatedAt) && !isYesterday(pageUpdatedAt);
}); });
const older: string[] = this.projectPageIds.filter((page) => { const older: string[] = this.projectPageIds.filter((page) => {
const pageUpdatedAt = this.projectPageMap[projectId][page].updated_at; const pageUpdatedAt = getDate(this.projectPageMap[projectId]?.[page]?.updated_at);
return !isThisWeek(pageUpdatedAt) && !isYesterday(pageUpdatedAt); return pageUpdatedAt && !isThisWeek(pageUpdatedAt) && !isYesterday(pageUpdatedAt);
}); });
return { today, yesterday, this_week, older }; return { today, yesterday, this_week, older };