From 46916fe79a600ab2b9a98a8732cd08a979e6f5bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Palma?= Date: Wed, 9 Oct 2024 23:10:15 +0100 Subject: [PATCH] feat: bug reporting by user on the app --- package-lock.json | 40 ++++++- package.json | 9 +- src/components/FeedbackReport.tsx | 113 +++++++++++++++++++ src/components/layout/Header.tsx | 6 +- src/components/layout/index.tsx | 7 +- src/components/ui/form.tsx | 176 ++++++++++++++++++++++++++++++ src/components/ui/textarea.tsx | 24 ++++ 7 files changed, 365 insertions(+), 10 deletions(-) create mode 100644 src/components/FeedbackReport.tsx create mode 100644 src/components/ui/form.tsx create mode 100644 src/components/ui/textarea.tsx diff --git a/package-lock.json b/package-lock.json index fbe55142..2f4aa92c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,15 +10,16 @@ "dependencies": { "@headlessui/react": "^1.7.17", "@heroicons/react": "^2.1.1", + "@hookform/resolvers": "^3.9.0", "@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", - "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-popover": "^1.0.7", "@radix-ui/react-scroll-area": "^1.0.5", "@radix-ui/react-separator": "^1.0.3", - "@radix-ui/react-slot": "^1.0.2", + "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-tabs": "^1.0.4", "@radix-ui/react-toast": "^1.1.5", "@radix-ui/react-toggle": "^1.0.3", @@ -46,6 +47,7 @@ "plausible-tracker": "^0.3.9", "react": "^18.2.0", "react-dom": "^18.1.0", + "react-hook-form": "^7.53.0", "react-router-dom": "^6.3.0", "react-sortablejs": "^6.1.4", "react-toastify": "^9.1.1", @@ -57,7 +59,8 @@ "usehooks-ts": "^2.6.0", "vite": "^5.4.6", "vite-tsconfig-paths": "^4.3.2", - "web-vitals": "^2.1.4" + "web-vitals": "^2.1.4", + "zod": "^3.23.8" }, "devDependencies": { "@eslint/js": "^9.10.0", @@ -605,6 +608,14 @@ "react": ">= 16" } }, + "node_modules/@hookform/resolvers": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.9.0.tgz", + "integrity": "sha512-bU0Gr4EepJ/EQsH/IwEzYLsT/PEj5C0ynLQ4m+GSHS+xKH4TfSelhluTgOaoc4kA5s7eCsQbM4wvZLzELmWzUg==", + "peerDependencies": { + "react-hook-form": "^7.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -5975,6 +5986,21 @@ "react": "^18.3.1" } }, + "node_modules/react-hook-form": { + "version": "7.53.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.53.0.tgz", + "integrity": "sha512-M1n3HhqCww6S2hxLxciEXy2oISPnAzxY7gvwVPrtlczTM/1dDadXgUxDpHMrMTblDOcm/AXtXxHwZ3jpg1mqKQ==", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -7527,6 +7553,14 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index 31b22fc0..a703570e 100644 --- a/package.json +++ b/package.json @@ -6,15 +6,16 @@ "dependencies": { "@headlessui/react": "^1.7.17", "@heroicons/react": "^2.1.1", + "@hookform/resolvers": "^3.9.0", "@radix-ui/react-alert-dialog": "^1.0.5", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", - "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-popover": "^1.0.7", "@radix-ui/react-scroll-area": "^1.0.5", "@radix-ui/react-separator": "^1.0.3", - "@radix-ui/react-slot": "^1.0.2", + "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-tabs": "^1.0.4", "@radix-ui/react-toast": "^1.1.5", "@radix-ui/react-toggle": "^1.0.3", @@ -42,6 +43,7 @@ "plausible-tracker": "^0.3.9", "react": "^18.2.0", "react-dom": "^18.1.0", + "react-hook-form": "^7.53.0", "react-router-dom": "^6.3.0", "react-sortablejs": "^6.1.4", "react-toastify": "^9.1.1", @@ -53,7 +55,8 @@ "usehooks-ts": "^2.6.0", "vite": "^5.4.6", "vite-tsconfig-paths": "^4.3.2", - "web-vitals": "^2.1.4" + "web-vitals": "^2.1.4", + "zod": "^3.23.8" }, "scripts": { "dev": "vite --host", diff --git a/src/components/FeedbackReport.tsx b/src/components/FeedbackReport.tsx new file mode 100644 index 00000000..499f25ea --- /dev/null +++ b/src/components/FeedbackReport.tsx @@ -0,0 +1,113 @@ +import * as Sentry from "@sentry/react"; +import { BugAntIcon } from "@heroicons/react/24/solid" +import { DropdownMenu } from "@radix-ui/react-dropdown-menu" +import { Button } from "./ui/button" +import { DropdownMenuContent, DropdownMenuTrigger } from "./ui/dropdown-menu" +import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "./ui/form"; +import { useForm } from "react-hook-form"; +import { Input } from "./ui/input"; +import { Textarea } from "./ui/textarea"; +import { z } from "zod"; +import { zodResolver } from "@hookform/resolvers/zod" +import { useToast } from "./ui/use-toast"; +import { useState } from "react"; +import { Tabs, TabsList, TabsTrigger } from "./ui/tabs"; +import { FlagIcon } from "@heroicons/react/24/outline"; + +enum ReportType { + Bug = 1, + Suggestion, +} + +const reportTypeString = ["Bug", "Sugestão"] +const reportTypeSuccessMessage = ["O bug foi submetido com sucesso", "A sugestão foi submetida com sucesso"] + +const bugSchema = z.object({ + email: z.string().optional(), + description: z.string().trim().min(1, { message: "É necessário descreveres o bug" }), +}) + +export const FeedbackReport = () => { + const [open, setOpen] = useState(false); + const [reportType, setReportType] = useState(ReportType.Bug); + + const { toast } = useToast(); + + const form = useForm>({ + resolver: zodResolver(bugSchema), + defaultValues: { + email: undefined, + description: undefined, + }, + }); + + const { register } = form; + + const onSubmit = (values: z.infer) => { + const eventId = Sentry.captureMessage(reportTypeString[reportType]); + + const userFeedback = { + email: values.email ?? "", + message: values.description, + associatedEventId: eventId, + }; + Sentry.captureFeedback(userFeedback); + + setOpen(false); + toast({ + title: "Obrigado!", + description: reportTypeSuccessMessage[reportType], + duration: 3000, + }) + } + + return + + + + + + + setReportType(ReportType.Bug)}>Bug + setReportType(ReportType.Suggestion)}>Sugestão + + +
+ + ( + + Email (opcional) + + + + + + )} + /> + + ( + + Descrição + +