From 5324832f13fac8f3fdc30d7fd54771dfb5ef1f0e Mon Sep 17 00:00:00 2001 From: Eunjae Lee Date: Wed, 27 Aug 2025 15:54:52 +0200 Subject: [PATCH] refactor: extract rating values and emojis into reusable constants (#23340) * refactor: extract rating values and emojis into reusable constants - Create packages/lib/rating.ts with RATING_OPTIONS array and validation utilities - Update bookings-single-view.tsx to use extracted constants - Add TypeScript interfaces for type safety - Enable reuse of rating constants across the codebase Co-Authored-By: eunjae@cal.com * refactor: move rating utilities to bookings feature directory - Move packages/lib/rating.ts to packages/features/bookings/lib/rating.ts - Update import path in bookings-single-view.tsx to use new location - Maintain domain-specific organization for booking-related utilities Co-Authored-By: eunjae@cal.com * fix: resolve linting issues in event-types.tsx - Fix quote style consistency (single to double quotes) - Add missing trailing comma - Resolve pre-commit hook formatting changes Co-Authored-By: eunjae@cal.com --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../bookings/views/bookings-single-view.tsx | 73 ++++--------------- packages/features/bookings/lib/rating.ts | 48 ++++++++++++ 2 files changed, 64 insertions(+), 57 deletions(-) create mode 100644 packages/features/bookings/lib/rating.ts diff --git a/apps/web/modules/bookings/views/bookings-single-view.tsx b/apps/web/modules/bookings/views/bookings-single-view.tsx index c1c8728f35..4f55f9e2a9 100644 --- a/apps/web/modules/bookings/views/bookings-single-view.tsx +++ b/apps/web/modules/bookings/views/bookings-single-view.tsx @@ -26,6 +26,7 @@ import { SystemField, TITLE_FIELD, } from "@calcom/features/bookings/lib/SystemField"; +import { RATING_OPTIONS, validateRating } from "@calcom/features/bookings/lib/rating"; import { getCalendarLinks, CalendarLinkType } from "@calcom/lib/bookings/getCalendarLinks"; import { APP_NAME } from "@calcom/lib/constants"; import { formatToLocalizedDate, formatToLocalizedTime, formatToLocalizedTimezone } from "@calcom/lib/dayjs"; @@ -179,14 +180,13 @@ export default function Success(props: PageProps) { const shouldAlignCentrally = !isEmbed || shouldAlignCentrallyInEmbed; const [calculatedDuration, setCalculatedDuration] = useState(undefined); const [comment, setComment] = useState(""); - const parsedRating = rating ? parseInt(rating, 10) : 3; const currentUserEmail = searchParams?.get("rescheduledBy") ?? searchParams?.get("cancelledBy") ?? session?.user?.email ?? undefined; - const defaultRating = isNaN(parsedRating) ? 3 : parsedRating > 5 ? 5 : parsedRating < 1 ? 1 : parsedRating; + const defaultRating = validateRating(rating); const [rateValue, setRateValue] = useState(defaultRating); const [isFeedbackSubmitted, setIsFeedbackSubmitted] = useState(false); @@ -1026,61 +1026,20 @@ export default function Success(props: PageProps) { ) : ( <>
- - - - - + {RATING_OPTIONS.map((option) => ( + + ))}

{t("submitted_feedback")}

diff --git a/packages/features/bookings/lib/rating.ts b/packages/features/bookings/lib/rating.ts new file mode 100644 index 0000000000..00b977419c --- /dev/null +++ b/packages/features/bookings/lib/rating.ts @@ -0,0 +1,48 @@ +export interface RatingOption { + value: number; + emoji: string; +} + +export const MIN_RATING = 1; +export const MAX_RATING = 5; +export const DEFAULT_RATING = 3; + +export const RATING_OPTIONS: RatingOption[] = [ + { value: 1, emoji: "😠" }, + { value: 2, emoji: "🙁" }, + { value: 3, emoji: "😐" }, + { value: 4, emoji: "😄" }, + { value: 5, emoji: "😍" }, +]; + +/** + * Validates and normalizes a rating value to ensure it's within the valid range + * @param rating - The rating value to validate (can be string or number) + * @param defaultValue - The default value to use if rating is invalid (defaults to DEFAULT_RATING) + * @returns A valid rating number between MIN_RATING and MAX_RATING + */ +export function validateRating( + rating: string | number | null | undefined, + defaultValue = DEFAULT_RATING +): number { + const parsedRating = typeof rating === "string" ? parseInt(rating, 10) : rating; + + if (!parsedRating || isNaN(parsedRating)) { + return defaultValue; + } + + if (parsedRating > MAX_RATING) return MAX_RATING; + if (parsedRating < MIN_RATING) return MIN_RATING; + + return parsedRating; +} + +/** + * Gets the emoji for a given rating value + * @param rating - The rating value + * @returns The corresponding emoji or empty string if not found + */ +export function getRatingEmoji(rating: number): string { + const option = RATING_OPTIONS.find((opt) => opt.value === rating); + return option?.emoji || ""; +}