From 323b0ea1282abc0945cef1ebf8185bdf9a156f5d Mon Sep 17 00:00:00 2001 From: Sebastien Castiel Date: Tue, 9 Jan 2024 15:32:19 -0500 Subject: Feedback button --- src/app/groups/layout.tsx | 10 +- src/app/layout.tsx | 2 + .../feedback-button/feedback-button-client.tsx | 198 +++++++++++++++++++++ .../feedback-button/feedback-button-common.tsx | 9 + .../feedback-button/feedback-button-email.tsx | 24 +++ src/components/feedback-button/feedback-button.tsx | 27 +++ src/components/ui/textarea.tsx | 24 +++ src/components/ui/toast.tsx | 127 +++++++++++++ src/components/ui/toaster.tsx | 35 ++++ src/components/ui/use-toast.ts | 192 ++++++++++++++++++++ src/lib/env.ts | 3 + src/lib/resend.ts | 5 + 12 files changed, 653 insertions(+), 3 deletions(-) create mode 100644 src/components/feedback-button/feedback-button-client.tsx create mode 100644 src/components/feedback-button/feedback-button-common.tsx create mode 100644 src/components/feedback-button/feedback-button-email.tsx create mode 100644 src/components/feedback-button/feedback-button.tsx create mode 100644 src/components/ui/textarea.tsx create mode 100644 src/components/ui/toast.tsx create mode 100644 src/components/ui/toaster.tsx create mode 100644 src/components/ui/use-toast.ts create mode 100644 src/lib/resend.ts (limited to 'src') diff --git a/src/app/groups/layout.tsx b/src/app/groups/layout.tsx index 8c221e1..dadd3a1 100644 --- a/src/app/groups/layout.tsx +++ b/src/app/groups/layout.tsx @@ -1,9 +1,13 @@ +import { FeedbackButton } from '@/components/feedback-button/feedback-button' import { PropsWithChildren } from 'react' export default function GroupsLayout({ children }: PropsWithChildren<{}>) { return ( -
- {children} -
+ <> +
+ {children} +
+ + ) } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index c6965b9..7ffaab0 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -2,6 +2,7 @@ import { ProgressBar } from '@/components/progress-bar' import { ThemeProvider } from '@/components/theme-provider' import { ThemeToggle } from '@/components/theme-toggle' import { Button } from '@/components/ui/button' +import { Toaster } from '@/components/ui/toaster' import { env } from '@/lib/env' import type { Metadata, Viewport } from 'next' import PlausibleProvider from 'next-plausible' @@ -132,6 +133,7 @@ export default function RootLayout({ + diff --git a/src/components/feedback-button/feedback-button-client.tsx b/src/components/feedback-button/feedback-button-client.tsx new file mode 100644 index 0000000..16debec --- /dev/null +++ b/src/components/feedback-button/feedback-button-client.tsx @@ -0,0 +1,198 @@ +'use client' +import { formSchema } from '@/components/feedback-button/feedback-button-common' +import { Button } from '@/components/ui/button' +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '@/components/ui/dialog' +import { + Drawer, + DrawerContent, + DrawerDescription, + DrawerHeader, + DrawerTitle, + DrawerTrigger, +} from '@/components/ui/drawer' +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/form' +import { Input } from '@/components/ui/input' +import { Textarea } from '@/components/ui/textarea' +import { useToast } from '@/components/ui/use-toast' +import { useMediaQuery } from '@/lib/hooks' +import { zodResolver } from '@hookform/resolvers/zod' +import { Loader2, MessageCircle } from 'lucide-react' +import { useState } from 'react' +import { useForm } from 'react-hook-form' +import * as z from 'zod' + +type FormValues = z.infer + +type Props = { + sendFeedback: (values: FormValues) => Promise +} + +export function FeedbackButtonClient({ sendFeedback }: Props) { + const { toast } = useToast() + const isDesktop = useMediaQuery('(min-width: 768px)') + + async function onSubmit(values: FormValues) { + await sendFeedback(values) + toast({ + title: 'Thank you for your feedback!', + description: + 'We will have a look at it as soon as possible, and will get back to you if needed.', + }) + } + + return ( +
+ {isDesktop ? ( + + ) : ( + + )} +
+ ) +} + +function FeedbackDrawer({ + onSubmit, +}: { + onSubmit: (values: FormValues) => Promise +}) { + const [open, setOpen] = useState(false) + + return ( +
+ + + + + + + Give us your feedback! + + We are always working to improve the user experience, and your + feedback helps us a lot. + + +
+ { + await onSubmit(values) + setOpen(false) + }} + /> +
+
+
+
+ ) +} + +function FeedbackDialog({ + onSubmit, +}: { + onSubmit: (values: FormValues) => Promise +}) { + const [open, setOpen] = useState(false) + + return ( +
+ + + + + + + Give us your feedback! + + We are always working to improve the user experience, and your + feedback helps us a lot. + + + { + await onSubmit(values) + setOpen(false) + }} + /> + + +
+ ) +} + +function FeedbackForm({ + onSubmit, +}: { + onSubmit: (values: FormValues) => Promise +}) { + const form = useForm({ + resolver: zodResolver(formSchema), + defaultValues: { email: '', message: '' }, + }) + + const isSubmitting = form.formState.isSubmitting + return ( +
+ +
+ ( + + Your email address + + + + + Optional. Provide it if you want us to get back to you. + + + + )} + /> + ( + + Your feedback + +