From 74ace5612081d344b4a4857841460c76915e1924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Fri, 27 Feb 2026 18:09:51 +0000 Subject: [PATCH] =?UTF-8?q?bug=20report=20tool=20=E2=80=94=20screenshot,?= =?UTF-8?q?=20console=20logs,=20and=20context=20sent=20to=20discord?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- bun.lock | 3 + package.json | 1 + .../Layout/BugReport/BugReportButton.tsx | 27 ++++++ .../Layout/BugReport/BugReportDialog.tsx | 75 +++++++++++++++ .../Layout/BugReport/use-bug-report.ts | 72 ++++++++++++++ .../Dashboard/Layout/DashboardLayout.tsx | 2 + src/apps/officer-web/frontend.tsx | 1 + src/apps/officer-web/lib/console-buffer.ts | 37 ++++++++ src/servers/api/bug-report/bug-report.ts | 95 +++++++++++++++++++ src/servers/hono.ts | 2 + 10 files changed, 315 insertions(+) create mode 100644 src/apps/officer-web/Screens/Dashboard/Layout/BugReport/BugReportButton.tsx create mode 100644 src/apps/officer-web/Screens/Dashboard/Layout/BugReport/BugReportDialog.tsx create mode 100644 src/apps/officer-web/Screens/Dashboard/Layout/BugReport/use-bug-report.ts create mode 100644 src/apps/officer-web/lib/console-buffer.ts create mode 100644 src/servers/api/bug-report/bug-report.ts diff --git a/bun.lock b/bun.lock index 4f73a640..da859ab4 100644 --- a/bun.lock +++ b/bun.lock @@ -70,6 +70,7 @@ "helpers": "workspace:*", "hono": "^4.11.1", "hooks": "workspace:*", + "html-to-image": "^1.11.13", "html2canvas": "^1.4.1", "i18n": "workspace:*", "idb-keyval": "^6.2.2", @@ -1556,6 +1557,8 @@ "hooks": ["hooks@workspace:src/workspaces/hooks"], + "html-to-image": ["html-to-image@1.11.13", "", {}, "sha512-cuOPoI7WApyhBElTTb9oqsawRvZ0rHhaHwghRLlTuffoD1B2aDemlCruLeZrUIIdvG7gs9xeELEPm6PhuASqrg=="], + "html-to-text": ["html-to-text@9.0.5", "", { "dependencies": { "@selderee/plugin-htmlparser2": "^0.11.0", "deepmerge": "^4.3.1", "dom-serializer": "^2.0.0", "htmlparser2": "^8.0.2", "selderee": "^0.11.0" } }, "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg=="], "html-url-attributes": ["html-url-attributes@3.0.1", "", {}, "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ=="], diff --git a/package.json b/package.json index c1de6f22..6ac73aaa 100644 --- a/package.json +++ b/package.json @@ -94,6 +94,7 @@ "helpers": "workspace:*", "hono": "^4.11.1", "hooks": "workspace:*", + "html-to-image": "^1.11.13", "html2canvas": "^1.4.1", "i18n": "workspace:*", "idb-keyval": "^6.2.2", diff --git a/src/apps/officer-web/Screens/Dashboard/Layout/BugReport/BugReportButton.tsx b/src/apps/officer-web/Screens/Dashboard/Layout/BugReport/BugReportButton.tsx new file mode 100644 index 00000000..a93733e5 --- /dev/null +++ b/src/apps/officer-web/Screens/Dashboard/Layout/BugReport/BugReportButton.tsx @@ -0,0 +1,27 @@ +import { Bug } from 'lucide-react'; +import { useBugReport } from './use-bug-report'; +import { BugReportDialog } from './BugReportDialog'; + +export const BugReportButton = () => { + const bugReport = useBugReport(); + + return ( + <> + + + + ); +}; diff --git a/src/apps/officer-web/Screens/Dashboard/Layout/BugReport/BugReportDialog.tsx b/src/apps/officer-web/Screens/Dashboard/Layout/BugReport/BugReportDialog.tsx new file mode 100644 index 00000000..b9952ced --- /dev/null +++ b/src/apps/officer-web/Screens/Dashboard/Layout/BugReport/BugReportDialog.tsx @@ -0,0 +1,75 @@ +import { useState, useMemo } from 'react'; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'; +import { Button } from '@/components/ui/button'; +import { Textarea } from '@/components/ui/textarea'; +import { Loader2 } from 'lucide-react'; + +type BugReportDialogProps = { + open: boolean; + capturing: boolean; + submitting: boolean; + screenshot: Blob | null; + onClose: () => void; + onSubmit: (description: string) => void; +}; + +export const BugReportDialog = ({ open, capturing, submitting, screenshot, onClose, onSubmit }: BugReportDialogProps) => { + const [description, setDescription] = useState(''); + + const previewUrl = useMemo(() => (screenshot ? URL.createObjectURL(screenshot) : null), [screenshot]); + + const handleSubmit = () => { + onSubmit(description); + setDescription(''); + }; + + const canSubmit = description.trim().length > 0 && !capturing && !submitting; + + return ( + + + + Report a Bug + + +
+
+ {capturing ? ( +
+ +
+ ) : previewUrl ? ( + Screenshot + ) : ( +
+ No screenshot captured +
+ )} +
+ +