Skip to content

Commit

Permalink
Add FeedbackForm component for user feedback handling
Browse files Browse the repository at this point in the history
  • Loading branch information
jordienr committed Jun 8, 2024
1 parent de36907 commit b54097c
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 51 deletions.
90 changes: 39 additions & 51 deletions apps/web/src/components/Feedback.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { useEffect, useState } from "react";
import {
DropdownMenu,
DropdownMenuContent,
Expand All @@ -9,64 +9,52 @@ import { Textarea } from "./ui/textarea";
import { Button } from "./ui/button";
import { toast } from "sonner";
import { getSupabaseBrowserClient } from "@/lib/supabase";
import { FeedbackForm } from "./Feedback/feedback-form";

type Props = {};

const Feedback = (props: Props) => {
const sb = getSupabaseBrowserClient();
const [isSuccess, setIsSuccess] = useState(false);

useEffect(() => {
if (isSuccess) {
setTimeout(() => {
setIsSuccess(false);
}, 4000);
}
}, [isSuccess]);

return (
<div>
<DropdownMenu>
<DropdownMenuTrigger className="rounded-full px-3 py-1 text-sm font-medium text-slate-600 hover:text-orange-600">
Feedback
</DropdownMenuTrigger>
<DropdownMenuContent>
<form
onSubmit={async (e) => {
e.preventDefault();
try {
const form = e.target as HTMLFormElement;
const formData = new FormData(e.target as HTMLFormElement);
const feedback = formData.get("feedback") as string;

const user = await sb.auth.getUser();

if (user.error) {
throw user.error;
}

const { data, error } = await sb
.from("feedback")
.insert({ feedback, user_email: user.data.user.email });

if (error) {
throw error;
}

toast.success("Thanks for the feedback!");
form.reset();
} catch (error) {
toast.error(
"Failed to submit feedback. Check the console for more details."
);
console.error("Failed to submit feedback", error);
}
}}
className="grid gap-2"
>
<Label htmlFor="feedback">
<div className="sr-only">Feedback</div>
<Textarea
placeholder="Your feedback"
name="feedback"
id="feedback"
/>
</Label>
<Button type="submit">Submit</Button>
</form>
</DropdownMenuContent>
</DropdownMenu>
<FeedbackForm
isSuccess={isSuccess}
onSubmit={async ({ feedback, type }) => {
try {
const user = await sb.auth.getUser();

if (user.error) {
throw user.error;
}

const { data, error } = await sb
.from("feedback")
.insert({ feedback, type, user_email: user.data.user.email });

if (error) {
throw error;
}

toast.success("Thanks for the feedback!");
setIsSuccess(true);
} catch (error) {
toast.error(
"Failed to submit feedback. Check the console for more details."
);
console.error("Failed to submit feedback", error);
}
}}
></FeedbackForm>
</div>
);
};
Expand Down
105 changes: 105 additions & 0 deletions apps/web/src/components/Feedback/feedback-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { PropsWithChildren, useState } from "react";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger,
} from "../ui/dropdown-menu";
import { Textarea } from "../ui/textarea";
import { cn } from "@/lib/utils";
import { Button } from "../ui/button";

export function FeedbackFormTypeButton({
label,
icon,
onClick,
selected,
}: {
label: string;
value: string;
icon: React.ReactNode;
onClick: () => void;
selected: boolean;
}) {
return (
<button
type="button"
onClick={onClick}
className={cn(
"flex flex-grow items-center gap-1.5 rounded-lg border border-transparent px-2 py-0.5 text-center text-xs font-medium text-zinc-600 transition-all hover:text-zinc-800",
{
"border-zinc-200 bg-zinc-50 text-zinc-800": selected,
}
)}
>
<span className="text-lg">{icon}</span>
<span>{label}</span>
</button>
);
}

export function FeedbackForm({
onSubmit,
isSuccess,
}: {
onSubmit: (data: { feedback: string; type: string }) => Promise<void>;
isSuccess: boolean;
}) {
const [type, setType] = useState<"issue" | "idea" | "other">("issue");

return (
<DropdownMenu>
<DropdownMenuTrigger className="px-2 py-1 text-sm font-medium text-zinc-600 hover:text-zinc-800">
Feedback
</DropdownMenuTrigger>

<DropdownMenuContent className="w-64">
{isSuccess ? (
<div className="flex flex-col gap-2 p-4 text-center font-medium text-zinc-600">
<h2>Thanks for the feedback! 🙏</h2>
</div>
) : (
<form
onSubmit={(e) => {
e.preventDefault();
const formData = new FormData(e.target as HTMLFormElement);
const feedback = formData.get("feedback") as string;
onSubmit({ feedback, type });
}}
>
<div className="mb-2 flex gap-2">
<FeedbackFormTypeButton
label="Issue"
value="issue"
icon="🐛"
onClick={() => setType("issue")}
selected={type === "issue"}
/>
<FeedbackFormTypeButton
label="Idea"
value="idea"
icon="💡"
onClick={() => setType("idea")}
selected={type === "idea"}
/>
<FeedbackFormTypeButton
label="Other"
value="other"
icon="💬"
onClick={() => setType("other")}
selected={type === "other"}
/>
</div>
<Textarea
required
className="resize-none"
placeholder="Your feedback"
></Textarea>
<div className="actions mt-2">
<Button>Send feedback</Button>
</div>
</form>
)}
</DropdownMenuContent>
</DropdownMenu>
);
}
3 changes: 3 additions & 0 deletions apps/web/src/components/Notifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ const Notifications = (props: Props) => {
<DropdownMenuContent align="end">
{notifications.length === 0 ? (
<div className="p-8 text-center text-zinc-400">
<div className="flex justify-center">
<Bell size="17" className="animate-bounce text-orange-400" />
</div>
No notifications
</div>
) : (
Expand Down

0 comments on commit b54097c

Please sign in to comment.