-
Notifications
You must be signed in to change notification settings - Fork 131
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[OPIK-291] [FE]: add provide a feedback modal in the UI; (#510)
* [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
Showing
5 changed files
with
318 additions
and
70 deletions.
There are no files selected for viewing
62 changes: 62 additions & 0 deletions
62
apps/opik-frontend/src/api/feedback/useProvideFeedbackMutation.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
115 changes: 115 additions & 0 deletions
115
apps/opik-frontend/src/components/layout/SideBar/FeedbackDialog/ProvideFeedbackDialog.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
Oops, something went wrong.