"use client"; import { RATING_OPTIONS } from "@calcom/features/bookings/lib/rating"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { trpc } from "@calcom/trpc/react"; import { Button } from "@coss/ui/components/button"; import { Textarea } from "@coss/ui/components/textarea"; import { toastManager } from "@coss/ui/components/toast"; import { cn } from "@coss/ui/lib/utils"; import { XIcon } from "lucide-react"; import type { ReactElement } from "react"; import { useState } from "react"; export interface FeedbackDialogProps { isOpen: boolean; onClose: () => void; onSubmitSuccess?: () => void; surveyId: string; ratingQuestionId: string; commentQuestionId: string; titleKey?: string; descriptionKey?: string; /** Where to show the dialog: "all" | "desktop" | "mobile". Defaults to "all". */ showOn?: "all" | "desktop" | "mobile"; } /** * Bottom-right positioned feedback card (non-blocking). * Styled similar to the feature opt-in banner. */ export function FeedbackDialog({ isOpen, onClose, onSubmitSuccess, surveyId, ratingQuestionId, commentQuestionId, titleKey = "feedback_dialog_title", descriptionKey = "feedback_dialog_description", showOn = "all", }: FeedbackDialogProps): ReactElement | null { const { t } = useLocale(); const [selectedRating, setSelectedRating] = useState(null); const [comment, setComment] = useState(""); const [isSuccess, setIsSuccess] = useState(false); const submitFeedbackMutation = trpc.viewer.feedback.submitFeedback.useMutation({ onSuccess: () => { setIsSuccess(true); onSubmitSuccess?.(); }, onError: () => { toastManager.add({ title: t("error_submitting_feedback"), type: "error", }); }, }); const handleSubmit = async (): Promise => { if (selectedRating === null) return; await submitFeedbackMutation.mutateAsync({ surveyId, data: { [ratingQuestionId]: selectedRating, [commentQuestionId]: comment, }, }); }; const handleSkip = (): void => { resetAndClose(); }; const resetAndClose = (): void => { setSelectedRating(null); setComment(""); setIsSuccess(false); onClose(); }; if (!isOpen) { return null; } // Visibility classes based on showOn const visibilityClass = showOn === "desktop" ? "hidden sm:block" : showOn === "mobile" ? "sm:hidden" : ""; const showMobileBackdrop = showOn !== "desktop"; if (isSuccess) { return ( <> {/* Mobile-only backdrop */} {showMobileBackdrop && (
)}

{t("feedback_submitted_title")}

{t("feedback_submitted_description")}

); } return ( <> {/* Mobile-only backdrop */} {showMobileBackdrop && (
)}

{t(titleKey)}

{t(descriptionKey)}

{RATING_OPTIONS.map((option) => ( ))}