Spaces:
Build error
Build error
| import React from "react"; | |
| import hotToast from "react-hot-toast"; | |
| import { useTranslation } from "react-i18next"; | |
| import { I18nKey } from "#/i18n/declaration"; | |
| import { Feedback } from "#/api/open-hands.types"; | |
| import { useSubmitFeedback } from "#/hooks/mutation/use-submit-feedback"; | |
| import { BrandButton } from "../settings/brand-button"; | |
| const FEEDBACK_VERSION = "1.0"; | |
| const VIEWER_PAGE = "https://www.all-hands.dev/share"; | |
| interface FeedbackFormProps { | |
| onClose: () => void; | |
| polarity: "positive" | "negative"; | |
| } | |
| export function FeedbackForm({ onClose, polarity }: FeedbackFormProps) { | |
| const { t } = useTranslation(); | |
| const copiedToClipboardToast = () => { | |
| hotToast(t(I18nKey.FEEDBACK$PASSWORD_COPIED_MESSAGE), { | |
| icon: "📋", | |
| position: "bottom-right", | |
| }); | |
| }; | |
| const onPressToast = (password: string) => { | |
| navigator.clipboard.writeText(password); | |
| copiedToClipboardToast(); | |
| }; | |
| const shareFeedbackToast = ( | |
| message: string, | |
| link: string, | |
| password: string, | |
| ) => { | |
| hotToast( | |
| <div className="flex flex-col gap-1"> | |
| <span>{message}</span> | |
| <a | |
| data-testid="toast-share-url" | |
| className="text-blue-500 underline" | |
| onClick={() => onPressToast(password)} | |
| href={link} | |
| target="_blank" | |
| rel="noreferrer" | |
| > | |
| {t(I18nKey.FEEDBACK$GO_TO_FEEDBACK)} | |
| </a> | |
| <span onClick={() => onPressToast(password)} className="cursor-pointer"> | |
| {t(I18nKey.FEEDBACK$PASSWORD)}: {password}{" "} | |
| <span className="text-gray-500"> | |
| ({t(I18nKey.FEEDBACK$COPY_LABEL)}) | |
| </span> | |
| </span> | |
| </div>, | |
| { duration: 10000 }, | |
| ); | |
| }; | |
| const { mutate: submitFeedback, isPending } = useSubmitFeedback(); | |
| const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => { | |
| event?.preventDefault(); | |
| const formData = new FormData(event.currentTarget); | |
| const email = formData.get("email")?.toString() || ""; | |
| const permissions = (formData.get("permissions")?.toString() || | |
| "private") as "private" | "public"; | |
| const feedback: Feedback = { | |
| version: FEEDBACK_VERSION, | |
| email, | |
| polarity, | |
| permissions, | |
| trajectory: [], | |
| token: "", | |
| }; | |
| submitFeedback( | |
| { feedback }, | |
| { | |
| onSuccess: (data) => { | |
| const { message, feedback_id, password } = data.body; // eslint-disable-line | |
| const link = `${VIEWER_PAGE}?share_id=${feedback_id}`; | |
| shareFeedbackToast(message, link, password); | |
| onClose(); | |
| }, | |
| }, | |
| ); | |
| }; | |
| return ( | |
| <form onSubmit={handleSubmit} className="flex flex-col gap-6 w-full"> | |
| <label className="flex flex-col gap-2"> | |
| <span className="text-xs text-neutral-400"> | |
| {t(I18nKey.FEEDBACK$EMAIL_LABEL)} | |
| </span> | |
| <input | |
| required | |
| name="email" | |
| type="email" | |
| placeholder={t(I18nKey.FEEDBACK$EMAIL_PLACEHOLDER)} | |
| className="bg-[#27272A] px-3 py-[10px] rounded" | |
| /> | |
| </label> | |
| <div className="flex gap-4 text-neutral-400"> | |
| <label className="flex gap-2 cursor-pointer"> | |
| <input | |
| name="permissions" | |
| value="private" | |
| type="radio" | |
| defaultChecked | |
| /> | |
| {t(I18nKey.FEEDBACK$PRIVATE_LABEL)} | |
| </label> | |
| <label className="flex gap-2 cursor-pointer"> | |
| <input name="permissions" value="public" type="radio" /> | |
| {t(I18nKey.FEEDBACK$PUBLIC_LABEL)} | |
| </label> | |
| </div> | |
| <div className="flex gap-2"> | |
| <BrandButton | |
| type="submit" | |
| variant="primary" | |
| className="grow" | |
| isDisabled={isPending} | |
| > | |
| {isPending | |
| ? t(I18nKey.FEEDBACK$SUBMITTING_LABEL) | |
| : t(I18nKey.FEEDBACK$SHARE_LABEL)} | |
| </BrandButton> | |
| <BrandButton | |
| type="button" | |
| variant="secondary" | |
| className="grow" | |
| onClick={onClose} | |
| isDisabled={isPending} | |
| > | |
| {t(I18nKey.FEEDBACK$CANCEL_LABEL)} | |
| </BrandButton> | |
| </div> | |
| {isPending && ( | |
| <p className="text-sm text-center text-neutral-400"> | |
| {t(I18nKey.FEEDBACK$SUBMITTING_MESSAGE)} | |
| </p> | |
| )} | |
| </form> | |
| ); | |
| } | |