Skip to content

Commit

Permalink
[OPIK-291] [FE]: add provide a feedback modal in the UI; (#510)
Browse files Browse the repository at this point in the history
* [OPIK-291]: add provide a feedback modal in the UI;

* [OPIK-291]: fix the bug with alignment of the provide feedback function;

---------

Co-authored-by: Sasha <[email protected]>
  • Loading branch information
aadereiko and Sasha authored Oct 30, 2024
1 parent a2ce4a7 commit b7acf2a
Show file tree
Hide file tree
Showing 5 changed files with 318 additions and 70 deletions.
62 changes: 62 additions & 0 deletions apps/opik-frontend/src/api/feedback/useProvideFeedbackMutation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { useMutation } from "@tanstack/react-query";
import get from "lodash/get";

import axios, { AxiosError } from "axios";

import { useToast } from "@/components/ui/use-toast";
import { APP_VERSION } from "@/constants/app";

type UseProvideFeedbackMutationParams = {
feedback: string;
name: string;
email: string;
};

const EVENT_TYPE = "opik_feedback_fe";
const ANONYMOUS_ID = "guest";

const useProvideFeedbackMutation = () => {
const { toast } = useToast();

return useMutation({
mutationFn: async ({
feedback,
name,
email,
}: UseProvideFeedbackMutationParams) => {
// the app's axios instance is not used here
// as we want to avoid having credentials and a workspace in headers
return axios.post("https://stats.comet.com/notify/event/", {
anonymous_id: ANONYMOUS_ID,
event_type: EVENT_TYPE,
event_properties: {
feedback,
name,
email,
version: APP_VERSION || null,
},
});
},
onSuccess: () => {
toast({
title: "Feedback sent",
description: "Thank you for sharing your thoughts with us",
});
},
onError: (error: AxiosError) => {
const message = get(
error,
["response", "data", "message"],
error.message,
);

toast({
title: "Error",
description: message,
variant: "destructive",
});
},
});
};

export default useProvideFeedbackMutation;
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import React, { useEffect, useState } from "react";

import {
Dialog,
DialogClose,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Textarea } from "@/components/ui/textarea";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import useProvideFeedbackMutation from "@/api/feedback/useProvideFeedbackMutation";

type ProvideFeedbackDialogProps = {
open: boolean;
setOpen: (open: boolean) => void;
};

const ProvideFeedbackDialog: React.FunctionComponent<
ProvideFeedbackDialogProps
> = ({ open, setOpen }) => {
const [feedback, setFeedback] = useState("");
const [email, setEmail] = useState("");
const [name, setName] = useState("");

const provideFeedbackMutation = useProvideFeedbackMutation();

const isValid = Boolean(feedback.length);

useEffect(() => {
if (!open) {
setFeedback("");
setEmail("");
setName("");
}
}, [open]);

return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent className="max-w-lg sm:max-w-[560px]">
<DialogHeader>
<DialogTitle>Provide feedback</DialogTitle>
</DialogHeader>

<div className="size-full overflow-y-auto pb-4">
<Label htmlFor="provideFeedback text-foreground-secondary">
Share your thoughts!
</Label>
<Textarea
id="provideFeedback"
value={feedback}
onChange={(event) => setFeedback(event.target.value)}
/>
</div>

<div className="comet-body-s-accented">
Would you like to chat with us?
</div>
<div className="comet-body-s max-w-[570px] text-muted-slate">
We are always happy to get feedback from our users during a quick
call. If you would like to chat with us, please enter your details
below.
</div>

<div className="flex flex-row items-center gap-4 pb-4">
<div className="w-full">
<Label htmlFor="name">Your name</Label>
<Input
id="name"
placeholder="Name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</div>

<div className="w-full">
<Label htmlFor="email">Your email</Label>
<Input
id="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
</div>

<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<DialogClose asChild>
<Button
type="submit"
disabled={!isValid}
onClick={() => {
provideFeedbackMutation.mutate({
feedback,
email,
name,
});
}}
>
Send feedback
</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
);
};

export default ProvideFeedbackDialog;
Loading

0 comments on commit b7acf2a

Please sign in to comment.