Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 54 additions & 1 deletion packages/utils/src/datetime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,59 @@ export const generateDateArray = (startDate: string | Date, endDate: string | Da
};

/**
* @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 | undefined | null): string | undefined => {
// Parse the date to check if it is valid
const parsedDate = getDate(date);
// return if undefined
if (!parsedDate) return;
// Check if the parsed date is valid before formatting
if (!isValid(parsedDate)) return; // Return null for invalid dates
// Format the date in payload format (yyyy-mm-dd)
const formattedDate = format(parsedDate, "yyyy-MM-dd");
return formattedDate;
};

/**
* @returns {string} safely formatted date or fallback text
* @description Safely formats a date using renderFormattedPayloadDate and date-fns format, with fallback for invalid dates
* @param {Date | string | undefined | null} date
* @param {string} formatToken (optional) // default "MMM dd, yyyy"
* @param {string} fallback (optional) // default "Invalid date"
* @example renderSafeFormattedDate("2024-01-01") // "Jan 01, 2024"
* @example renderSafeFormattedDate(null) // "Invalid date"
* @example renderSafeFormattedDate("2024-01-01", "MM/dd/yyyy", "N/A") // "01/01/2024"
*/
export const renderSafeFormattedDate = (
date: Date | string | undefined | null,
formatToken: string = "MMM dd, yyyy",
fallback: string = "Invalid date"
): string => {
if (!date) return fallback;

// Use renderFormattedPayloadDate to get a properly formatted payload date
const payloadDate = renderFormattedPayloadDate(date);

// If renderFormattedPayloadDate returns undefined/null, return fallback
if (!payloadDate) return fallback;

try {
// Parse and format the payload date
const parsedDate = getDate(payloadDate);
if (!parsedDate || !isValid(parsedDate)) return fallback;

return format(parsedDate, formatToken);
} catch (error) {
// Return fallback if any error occurs during formatting
return fallback;
}
};
Comment on lines +355 to +388
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Refactor to eliminate double parsing and improve efficiency.

The current implementation parses and validates the date twice - once in renderFormattedPayloadDate and again in this function. This is inefficient and unnecessarily complex.

Consider this simplified implementation that eliminates double parsing:

-export const renderSafeFormattedDate = (
-  date: Date | string | undefined | null,
-  formatToken: string = "MMM dd, yyyy",
-  fallback: string = "Invalid date"
-): string => {
-  if (!date) return fallback;
-
-  // Use renderFormattedPayloadDate to get a properly formatted payload date
-  const payloadDate = renderFormattedPayloadDate(date);
-
-  // If renderFormattedPayloadDate returns undefined/null, return fallback
-  if (!payloadDate) return fallback;
-
-  try {
-    // Parse and format the payload date
-    const parsedDate = getDate(payloadDate);
-    if (!parsedDate || !isValid(parsedDate)) return fallback;
-
-    return format(parsedDate, formatToken);
-  } catch (error) {
-    // Return fallback if any error occurs during formatting
-    return fallback;
-  }
-};
+export const renderSafeFormattedDate = (
+  date: Date | string | undefined | null,
+  formatToken: string = "MMM dd, yyyy",
+  fallback: string = "Invalid date"
+): string => {
+  if (!date) return fallback;
+
+  try {
+    // Parse the date once
+    const parsedDate = getDate(date);
+    if (!parsedDate || !isValid(parsedDate)) return fallback;
+
+    return format(parsedDate, formatToken);
+  } catch (error) {
+    // Return fallback if any error occurs during formatting
+    return fallback;
+  }
+};

This approach:

  • Eliminates the unnecessary intermediate payload format step
  • Reduces parsing from twice to once
  • Simplifies the logic while maintaining the same safety guarantees
  • Is more performant and easier to understand
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* @returns {string} safely formatted date or fallback text
* @description Safely formats a date using renderFormattedPayloadDate and date-fns format, with fallback for invalid dates
* @param {Date | string | undefined | null} date
* @param {string} formatToken (optional) // default "MMM dd, yyyy"
* @param {string} fallback (optional) // default "Invalid date"
* @example renderSafeFormattedDate("2024-01-01") // "Jan 01, 2024"
* @example renderSafeFormattedDate(null) // "Invalid date"
* @example renderSafeFormattedDate("2024-01-01", "MM/dd/yyyy", "N/A") // "01/01/2024"
*/
export const renderSafeFormattedDate = (
date: Date | string | undefined | null,
formatToken: string = "MMM dd, yyyy",
fallback: string = "Invalid date"
): string => {
if (!date) return fallback;
// Use renderFormattedPayloadDate to get a properly formatted payload date
const payloadDate = renderFormattedPayloadDate(date);
// If renderFormattedPayloadDate returns undefined/null, return fallback
if (!payloadDate) return fallback;
try {
// Parse and format the payload date
const parsedDate = getDate(payloadDate);
if (!parsedDate || !isValid(parsedDate)) return fallback;
return format(parsedDate, formatToken);
} catch (error) {
// Return fallback if any error occurs during formatting
return fallback;
}
};
export const renderSafeFormattedDate = (
date: Date | string | undefined | null,
formatToken: string = "MMM dd, yyyy",
fallback: string = "Invalid date"
): string => {
if (!date) return fallback;
try {
// Parse the date once
const parsedDate = getDate(date);
if (!parsedDate || !isValid(parsedDate)) return fallback;
return format(parsedDate, formatToken);
} catch (error) {
// Return fallback if any error occurs during formatting
return fallback;
}
};
🤖 Prompt for AI Agents
In packages/utils/src/datetime.ts lines 355 to 388, the function
renderSafeFormattedDate currently parses and validates the date twice, once
inside renderFormattedPayloadDate and again in this function, causing
inefficiency. Refactor the function to parse and validate the date only once by
removing the call to renderFormattedPayloadDate and directly parsing the input
date, then validating and formatting it. This will simplify the logic, improve
performance, and maintain the same safety guarantees.


/*
* Formats merged date range display with smart formatting
* - Single date: "Jan 24, 2025"
* - Same year, same month: "Jan 24 - 28, 2025"
Expand Down Expand Up @@ -388,4 +441,4 @@ export const formatDateRange = (
}

return "";
};
};
5 changes: 2 additions & 3 deletions web/core/components/cycles/list/cycle-list-item-action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
import { useLocalStorage } from "@plane/hooks";
import { useTranslation } from "@plane/i18n";
import { ICycle, TCycleGroups } from "@plane/types";
// ui
import { Avatar, AvatarGroup, FavoriteStar, LayersIcon, Tooltip, TransferIcon, setPromiseToast } from "@plane/ui";
// components
import { CycleQuickActions, TransferIssuesModal } from "@/components/cycles";
Expand Down Expand Up @@ -64,7 +63,7 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
const searchParams = useSearchParams();
const pathname = usePathname();
// store hooks
const { addCycleToFavorites, removeCycleFromFavorites, updateCycleDetails } = useCycle();
const { addCycleToFavorites, removeCycleFromFavorites } = useCycle();
const { captureEvent } = useEventTracker();
const { allowPermissions } = useUserPermissions();

Expand All @@ -77,7 +76,7 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
const { getUserDetails } = useMember();

// form
const { control, reset, getValues } = useForm({
const { reset } = useForm({
defaultValues,
});

Expand Down