@fatagnus/convex-feedback 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -0
- package/dist/react/ErrorBugReportButton.d.ts +57 -0
- package/dist/react/ErrorBugReportButton.d.ts.map +1 -0
- package/dist/react/ErrorBugReportButton.js +142 -0
- package/dist/react/ErrorBugReportButton.js.map +1 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +1 -0
- package/dist/react/index.js.map +1 -1
- package/package.json +1 -1
- package/src/react/ErrorBugReportButton.tsx +311 -0
- package/src/react/index.ts +5 -0
package/README.md
CHANGED
|
@@ -318,6 +318,56 @@ import { BugReportButton } from '@fatagnus/convex-feedback/react';
|
|
|
318
318
|
| `onSuccess` | `function` | - | Success callback |
|
|
319
319
|
| `onError` | `function` | - | Error callback |
|
|
320
320
|
|
|
321
|
+
### ErrorBugReportButton
|
|
322
|
+
|
|
323
|
+
A self-contained bug report button designed for error pages. Pre-fills error details and allows users to submit bug reports directly from error boundaries or error pages.
|
|
324
|
+
|
|
325
|
+
```tsx
|
|
326
|
+
import { ErrorBugReportButton } from '@fatagnus/convex-feedback/react';
|
|
327
|
+
import { api } from './convex/_generated/api';
|
|
328
|
+
|
|
329
|
+
function ErrorPage({ error }: { error: Error }) {
|
|
330
|
+
return (
|
|
331
|
+
<div>
|
|
332
|
+
<h1>Something went wrong</h1>
|
|
333
|
+
<p>{error.message}</p>
|
|
334
|
+
|
|
335
|
+
<ErrorBugReportButton
|
|
336
|
+
error={error}
|
|
337
|
+
reporterType="staff"
|
|
338
|
+
reporterId={user?.id}
|
|
339
|
+
reporterEmail={user?.email}
|
|
340
|
+
reporterName={user?.name}
|
|
341
|
+
bugReportApi={{
|
|
342
|
+
create: api.feedback.bugReports.create,
|
|
343
|
+
}}
|
|
344
|
+
variant="outline"
|
|
345
|
+
color="red"
|
|
346
|
+
onSuccess={() => console.log('Bug report submitted')}
|
|
347
|
+
onError={(err) => console.error('Failed to submit:', err)}
|
|
348
|
+
/>
|
|
349
|
+
</div>
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
**Props:**
|
|
355
|
+
|
|
356
|
+
| Prop | Type | Default | Description |
|
|
357
|
+
|------|------|---------|-------------|
|
|
358
|
+
| `error` | `Error` | - | The error to report (required) |
|
|
359
|
+
| `reporterType` | `'staff' \| 'customer'` | `'staff'` | Type of reporter |
|
|
360
|
+
| `reporterId` | `string` | `'error-page'` | Unique reporter identifier |
|
|
361
|
+
| `reporterEmail` | `string` | `'unknown@example.com'` | Reporter's email |
|
|
362
|
+
| `reporterName` | `string` | `'Error Page Reporter'` | Reporter's display name |
|
|
363
|
+
| `bugReportApi` | `object` | - | Convex API reference with `create` mutation (required) |
|
|
364
|
+
| `variant` | `'filled' \| 'outline' \| 'light' \| 'subtle'` | `'outline'` | Button variant |
|
|
365
|
+
| `color` | `string` | `'red'` | Button color |
|
|
366
|
+
| `onSuccess` | `function` | - | Success callback |
|
|
367
|
+
| `onError` | `function` | - | Error callback |
|
|
368
|
+
|
|
369
|
+
**Note:** Unlike `BugReportButton`, this component only requires the `bugReportApi.create` mutation (no `generateUploadUrl` needed since error pages typically don't include screenshots).
|
|
370
|
+
|
|
321
371
|
### useBugReportContext
|
|
322
372
|
|
|
323
373
|
Hook to access the bug report context for error capture.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { FunctionReference } from 'convex/server';
|
|
2
|
+
/**
|
|
3
|
+
* Props for the ErrorBugReportButton component
|
|
4
|
+
*/
|
|
5
|
+
export interface ErrorBugReportButtonProps {
|
|
6
|
+
/** The error to report */
|
|
7
|
+
error: Error;
|
|
8
|
+
/** Reporter type: 'staff' for internal users, 'customer' for external users */
|
|
9
|
+
reporterType?: 'staff' | 'customer';
|
|
10
|
+
/** Unique identifier for the reporter (defaults to 'error-page') */
|
|
11
|
+
reporterId?: string;
|
|
12
|
+
/** Reporter's email address (defaults to 'unknown@example.com') */
|
|
13
|
+
reporterEmail?: string;
|
|
14
|
+
/** Reporter's display name (defaults to 'Error Page Reporter') */
|
|
15
|
+
reporterName?: string;
|
|
16
|
+
/** Convex API reference for bug reports */
|
|
17
|
+
bugReportApi: {
|
|
18
|
+
create: FunctionReference<'mutation'>;
|
|
19
|
+
};
|
|
20
|
+
/** Button variant (default: 'outline') */
|
|
21
|
+
variant?: 'filled' | 'outline' | 'light' | 'subtle';
|
|
22
|
+
/** Button color (default: 'red') */
|
|
23
|
+
color?: string;
|
|
24
|
+
/** Callback when submission succeeds */
|
|
25
|
+
onSuccess?: () => void;
|
|
26
|
+
/** Callback when submission fails */
|
|
27
|
+
onError?: (error: Error) => void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A self-contained bug report button designed for error pages.
|
|
31
|
+
* Pre-fills the error details and allows users to submit bug reports
|
|
32
|
+
* directly from error pages.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```tsx
|
|
36
|
+
* import { ErrorBugReportButton } from '@fatagnus/convex-feedback/react';
|
|
37
|
+
* import { api } from './convex/_generated/api';
|
|
38
|
+
*
|
|
39
|
+
* function ErrorPage({ error }: { error: Error }) {
|
|
40
|
+
* return (
|
|
41
|
+
* <div>
|
|
42
|
+
* <h1>Something went wrong</h1>
|
|
43
|
+
* <ErrorBugReportButton
|
|
44
|
+
* error={error}
|
|
45
|
+
* reporterType="staff"
|
|
46
|
+
* bugReportApi={{
|
|
47
|
+
* create: api.feedback.bugReports.create,
|
|
48
|
+
* }}
|
|
49
|
+
* />
|
|
50
|
+
* </div>
|
|
51
|
+
* );
|
|
52
|
+
* }
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare function ErrorBugReportButton({ error, reporterType, reporterId, reporterEmail, reporterName, bugReportApi, variant, color, onSuccess, onError, }: ErrorBugReportButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
56
|
+
export default ErrorBugReportButton;
|
|
57
|
+
//# sourceMappingURL=ErrorBugReportButton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorBugReportButton.d.ts","sourceRoot":"","sources":["../../src/react/ErrorBugReportButton.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAKvD;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,0BAA0B;IAC1B,KAAK,EAAE,KAAK,CAAC;IACb,+EAA+E;IAC/E,YAAY,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;IACpC,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2CAA2C;IAC3C,YAAY,EAAE;QACZ,MAAM,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC;KACvC,CAAC;IACF,0CAA0C;IAC1C,OAAO,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;IACpD,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,qCAAqC;IACrC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAcD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,KAAK,EACL,YAAsB,EACtB,UAAyB,EACzB,aAAqC,EACrC,YAAoC,EACpC,YAAY,EACZ,OAAmB,EACnB,KAAa,EACb,SAAS,EACT,OAAO,GACR,EAAE,yBAAyB,2CA+M3B;AAED,eAAe,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useCallback } from 'react';
|
|
3
|
+
import { Button, Modal, TextInput, Textarea, Select, Stack, Text, Group, Paper, Badge, LoadingOverlay, } from '@mantine/core';
|
|
4
|
+
import { useForm } from '@mantine/form';
|
|
5
|
+
import { useDisclosure } from '@mantine/hooks';
|
|
6
|
+
import { notifications } from '@mantine/notifications';
|
|
7
|
+
import * as TablerIcons from '@tabler/icons-react';
|
|
8
|
+
import { useMutation } from 'convex/react';
|
|
9
|
+
const IconBug = TablerIcons.IconBug;
|
|
10
|
+
const IconCheck = TablerIcons.IconCheck;
|
|
11
|
+
/**
|
|
12
|
+
* A self-contained bug report button designed for error pages.
|
|
13
|
+
* Pre-fills the error details and allows users to submit bug reports
|
|
14
|
+
* directly from error pages.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* import { ErrorBugReportButton } from '@fatagnus/convex-feedback/react';
|
|
19
|
+
* import { api } from './convex/_generated/api';
|
|
20
|
+
*
|
|
21
|
+
* function ErrorPage({ error }: { error: Error }) {
|
|
22
|
+
* return (
|
|
23
|
+
* <div>
|
|
24
|
+
* <h1>Something went wrong</h1>
|
|
25
|
+
* <ErrorBugReportButton
|
|
26
|
+
* error={error}
|
|
27
|
+
* reporterType="staff"
|
|
28
|
+
* bugReportApi={{
|
|
29
|
+
* create: api.feedback.bugReports.create,
|
|
30
|
+
* }}
|
|
31
|
+
* />
|
|
32
|
+
* </div>
|
|
33
|
+
* );
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export function ErrorBugReportButton({ error, reporterType = 'staff', reporterId = 'error-page', reporterEmail = 'unknown@example.com', reporterName = 'Error Page Reporter', bugReportApi, variant = 'outline', color = 'red', onSuccess, onError, }) {
|
|
38
|
+
const [opened, { open, close }] = useDisclosure(false);
|
|
39
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
40
|
+
const createReport = useMutation(bugReportApi.create);
|
|
41
|
+
const form = useForm({
|
|
42
|
+
initialValues: {
|
|
43
|
+
title: `Error: ${error?.message?.slice(0, 50) || 'Unknown error'}${(error?.message?.length || 0) > 50 ? '...' : ''}`,
|
|
44
|
+
description: '',
|
|
45
|
+
severity: 'high',
|
|
46
|
+
},
|
|
47
|
+
validate: {
|
|
48
|
+
title: (value) => value.trim().length < 5 ? 'Title must be at least 5 characters' : null,
|
|
49
|
+
description: (value) => value.trim().length < 10
|
|
50
|
+
? 'Please provide more details (at least 10 characters)'
|
|
51
|
+
: null,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
const getBrowserInfo = useCallback(() => {
|
|
55
|
+
return {
|
|
56
|
+
userAgent: navigator.userAgent,
|
|
57
|
+
platform: navigator.platform,
|
|
58
|
+
language: navigator.language,
|
|
59
|
+
cookiesEnabled: navigator.cookieEnabled,
|
|
60
|
+
onLine: navigator.onLine,
|
|
61
|
+
screenWidth: window.screen.width,
|
|
62
|
+
screenHeight: window.screen.height,
|
|
63
|
+
colorDepth: window.screen.colorDepth,
|
|
64
|
+
pixelRatio: window.devicePixelRatio,
|
|
65
|
+
};
|
|
66
|
+
}, []);
|
|
67
|
+
const getErrorDetails = useCallback(() => {
|
|
68
|
+
const parts = [
|
|
69
|
+
'--- Error Details ---',
|
|
70
|
+
`Message: ${error?.message || 'Unknown error'}`,
|
|
71
|
+
];
|
|
72
|
+
if (error?.stack) {
|
|
73
|
+
parts.push('', 'Stack Trace:', error.stack);
|
|
74
|
+
}
|
|
75
|
+
return parts.join('\n');
|
|
76
|
+
}, [error]);
|
|
77
|
+
const handleSubmit = async (values) => {
|
|
78
|
+
setIsSubmitting(true);
|
|
79
|
+
try {
|
|
80
|
+
// Build the full description with error details
|
|
81
|
+
const fullDescription = [
|
|
82
|
+
values.description,
|
|
83
|
+
'',
|
|
84
|
+
getErrorDetails(),
|
|
85
|
+
].join('\n');
|
|
86
|
+
// Create the report
|
|
87
|
+
await createReport({
|
|
88
|
+
title: values.title,
|
|
89
|
+
description: fullDescription,
|
|
90
|
+
severity: values.severity,
|
|
91
|
+
reporterType,
|
|
92
|
+
reporterId,
|
|
93
|
+
reporterEmail,
|
|
94
|
+
reporterName,
|
|
95
|
+
url: window.location.href,
|
|
96
|
+
route: window.location.pathname,
|
|
97
|
+
browserInfo: JSON.stringify(getBrowserInfo()),
|
|
98
|
+
viewportWidth: window.innerWidth,
|
|
99
|
+
viewportHeight: window.innerHeight,
|
|
100
|
+
networkState: navigator.onLine ? 'online' : 'offline',
|
|
101
|
+
});
|
|
102
|
+
notifications.show({
|
|
103
|
+
title: 'Bug report submitted',
|
|
104
|
+
message: 'Thank you for reporting this error! We will investigate.',
|
|
105
|
+
color: 'green',
|
|
106
|
+
icon: _jsx(IconCheck, { size: 16 }),
|
|
107
|
+
});
|
|
108
|
+
onSuccess?.();
|
|
109
|
+
form.reset();
|
|
110
|
+
close();
|
|
111
|
+
}
|
|
112
|
+
catch (submitError) {
|
|
113
|
+
console.error('Failed to submit bug report:', submitError);
|
|
114
|
+
notifications.show({
|
|
115
|
+
title: 'Submission failed',
|
|
116
|
+
message: 'Could not submit bug report. Please try again.',
|
|
117
|
+
color: 'red',
|
|
118
|
+
});
|
|
119
|
+
onError?.(submitError instanceof Error ? submitError : new Error(String(submitError)));
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
setIsSubmitting(false);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
const handleOpen = () => {
|
|
126
|
+
// Reset form with current error info
|
|
127
|
+
form.setValues({
|
|
128
|
+
title: `Error: ${error?.message?.slice(0, 50) || 'Unknown error'}${(error?.message?.length || 0) > 50 ? '...' : ''}`,
|
|
129
|
+
description: '',
|
|
130
|
+
severity: 'high',
|
|
131
|
+
});
|
|
132
|
+
open();
|
|
133
|
+
};
|
|
134
|
+
return (_jsxs(_Fragment, { children: [_jsx(Button, { "data-testid": "error-report-bug-button", leftSection: _jsx(IconBug, { size: 16 }), onClick: handleOpen, variant: variant, color: color, children: "Report Bug" }), _jsxs(Modal, { opened: opened, onClose: close, title: _jsxs(Group, { gap: "xs", children: [_jsx(IconBug, { size: 20 }), _jsx(Text, { fw: 600, children: "Report This Error" })] }), size: "md", children: [_jsx(LoadingOverlay, { visible: isSubmitting }), _jsx("form", { onSubmit: form.onSubmit(handleSubmit), children: _jsxs(Stack, { gap: "md", children: [_jsx(TextInput, { label: "Title", placeholder: "Brief summary of the issue", required: true, ...form.getInputProps('title') }), _jsx(Textarea, { label: "What were you doing when this error occurred?", placeholder: "Please describe what you were trying to do, what you expected to happen, and any other relevant context.", required: true, minRows: 3, ...form.getInputProps('description') }), _jsx(Select, { label: "Severity", placeholder: "How severe is this issue?", data: [
|
|
135
|
+
{ value: 'low', label: 'Low - Minor inconvenience' },
|
|
136
|
+
{ value: 'medium', label: 'Medium - Affects workflow' },
|
|
137
|
+
{ value: 'high', label: 'High - Major functionality broken' },
|
|
138
|
+
{ value: 'critical', label: 'Critical - System unusable' },
|
|
139
|
+
], ...form.getInputProps('severity') }), _jsx(Paper, { withBorder: true, p: "xs", radius: "md", bg: "var(--mantine-color-red-light)", children: _jsxs(Stack, { gap: "xs", children: [_jsx(Group, { gap: "xs", children: _jsx(Badge, { size: "xs", color: "red", children: "Error Details (auto-included)" }) }), _jsx(Text, { size: "xs", c: "dimmed", style: { fontFamily: 'monospace', whiteSpace: 'pre-wrap', wordBreak: 'break-word' }, children: error?.message || 'Unknown error' })] }) }), _jsx(Paper, { withBorder: true, p: "xs", radius: "md", children: _jsxs(Group, { gap: "xs", children: [_jsx(Badge, { size: "xs", variant: "outline", color: "gray", children: "Auto-captured" }), _jsx(Text, { size: "xs", c: "dimmed", children: "Browser info, URL, viewport size, and full stack trace will be included." })] }) }), _jsxs(Group, { justify: "flex-end", mt: "md", children: [_jsx(Button, { variant: "subtle", onClick: close, disabled: isSubmitting, children: "Cancel" }), _jsx(Button, { type: "submit", color: "red", loading: isSubmitting, children: "Submit Report" })] })] }) })] })] }));
|
|
140
|
+
}
|
|
141
|
+
export default ErrorBugReportButton;
|
|
142
|
+
//# sourceMappingURL=ErrorBugReportButton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ErrorBugReportButton.js","sourceRoot":"","sources":["../../src/react/ErrorBugReportButton.tsx"],"names":[],"mappings":"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"}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -33,4 +33,5 @@
|
|
|
33
33
|
*/
|
|
34
34
|
export { BugReportProvider, useBugReportContext, type BugReportProviderProps, type BugReportContextValue, type ConsoleError, } from './BugReportContext';
|
|
35
35
|
export { BugReportButton, type BugReportButtonProps, } from './BugReportButton';
|
|
36
|
+
export { ErrorBugReportButton, type ErrorBugReportButtonProps, } from './ErrorBugReportButton';
|
|
36
37
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,YAAY,GAClB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,eAAe,EACf,KAAK,oBAAoB,GAC1B,MAAM,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,YAAY,GAClB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,eAAe,EACf,KAAK,oBAAoB,GAC1B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,oBAAoB,EACpB,KAAK,yBAAyB,GAC/B,MAAM,wBAAwB,CAAC"}
|
package/dist/react/index.js
CHANGED
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EACL,iBAAiB,EACjB,mBAAmB,GAIpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,eAAe,GAEhB,MAAM,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EACL,iBAAiB,EACjB,mBAAmB,GAIpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,eAAe,GAEhB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,oBAAoB,GAErB,MAAM,wBAAwB,CAAC"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { useState, useCallback } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Button,
|
|
4
|
+
Modal,
|
|
5
|
+
TextInput,
|
|
6
|
+
Textarea,
|
|
7
|
+
Select,
|
|
8
|
+
Stack,
|
|
9
|
+
Text,
|
|
10
|
+
Group,
|
|
11
|
+
Paper,
|
|
12
|
+
Badge,
|
|
13
|
+
LoadingOverlay,
|
|
14
|
+
} from '@mantine/core';
|
|
15
|
+
import { useForm } from '@mantine/form';
|
|
16
|
+
import { useDisclosure } from '@mantine/hooks';
|
|
17
|
+
import { notifications } from '@mantine/notifications';
|
|
18
|
+
import * as TablerIcons from '@tabler/icons-react';
|
|
19
|
+
import { useMutation } from 'convex/react';
|
|
20
|
+
import type { FunctionReference } from 'convex/server';
|
|
21
|
+
|
|
22
|
+
const IconBug = TablerIcons.IconBug as React.FC<{ size?: number }>;
|
|
23
|
+
const IconCheck = TablerIcons.IconCheck as React.FC<{ size?: number }>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Props for the ErrorBugReportButton component
|
|
27
|
+
*/
|
|
28
|
+
export interface ErrorBugReportButtonProps {
|
|
29
|
+
/** The error to report */
|
|
30
|
+
error: Error;
|
|
31
|
+
/** Reporter type: 'staff' for internal users, 'customer' for external users */
|
|
32
|
+
reporterType?: 'staff' | 'customer';
|
|
33
|
+
/** Unique identifier for the reporter (defaults to 'error-page') */
|
|
34
|
+
reporterId?: string;
|
|
35
|
+
/** Reporter's email address (defaults to 'unknown@example.com') */
|
|
36
|
+
reporterEmail?: string;
|
|
37
|
+
/** Reporter's display name (defaults to 'Error Page Reporter') */
|
|
38
|
+
reporterName?: string;
|
|
39
|
+
/** Convex API reference for bug reports */
|
|
40
|
+
bugReportApi: {
|
|
41
|
+
create: FunctionReference<'mutation'>;
|
|
42
|
+
};
|
|
43
|
+
/** Button variant (default: 'outline') */
|
|
44
|
+
variant?: 'filled' | 'outline' | 'light' | 'subtle';
|
|
45
|
+
/** Button color (default: 'red') */
|
|
46
|
+
color?: string;
|
|
47
|
+
/** Callback when submission succeeds */
|
|
48
|
+
onSuccess?: () => void;
|
|
49
|
+
/** Callback when submission fails */
|
|
50
|
+
onError?: (error: Error) => void;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface BrowserInfo {
|
|
54
|
+
userAgent: string;
|
|
55
|
+
platform: string;
|
|
56
|
+
language: string;
|
|
57
|
+
cookiesEnabled: boolean;
|
|
58
|
+
onLine: boolean;
|
|
59
|
+
screenWidth: number;
|
|
60
|
+
screenHeight: number;
|
|
61
|
+
colorDepth: number;
|
|
62
|
+
pixelRatio: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* A self-contained bug report button designed for error pages.
|
|
67
|
+
* Pre-fills the error details and allows users to submit bug reports
|
|
68
|
+
* directly from error pages.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```tsx
|
|
72
|
+
* import { ErrorBugReportButton } from '@fatagnus/convex-feedback/react';
|
|
73
|
+
* import { api } from './convex/_generated/api';
|
|
74
|
+
*
|
|
75
|
+
* function ErrorPage({ error }: { error: Error }) {
|
|
76
|
+
* return (
|
|
77
|
+
* <div>
|
|
78
|
+
* <h1>Something went wrong</h1>
|
|
79
|
+
* <ErrorBugReportButton
|
|
80
|
+
* error={error}
|
|
81
|
+
* reporterType="staff"
|
|
82
|
+
* bugReportApi={{
|
|
83
|
+
* create: api.feedback.bugReports.create,
|
|
84
|
+
* }}
|
|
85
|
+
* />
|
|
86
|
+
* </div>
|
|
87
|
+
* );
|
|
88
|
+
* }
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
export function ErrorBugReportButton({
|
|
92
|
+
error,
|
|
93
|
+
reporterType = 'staff',
|
|
94
|
+
reporterId = 'error-page',
|
|
95
|
+
reporterEmail = 'unknown@example.com',
|
|
96
|
+
reporterName = 'Error Page Reporter',
|
|
97
|
+
bugReportApi,
|
|
98
|
+
variant = 'outline',
|
|
99
|
+
color = 'red',
|
|
100
|
+
onSuccess,
|
|
101
|
+
onError,
|
|
102
|
+
}: ErrorBugReportButtonProps) {
|
|
103
|
+
const [opened, { open, close }] = useDisclosure(false);
|
|
104
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
105
|
+
|
|
106
|
+
const createReport = useMutation(bugReportApi.create);
|
|
107
|
+
|
|
108
|
+
const form = useForm({
|
|
109
|
+
initialValues: {
|
|
110
|
+
title: `Error: ${error?.message?.slice(0, 50) || 'Unknown error'}${(error?.message?.length || 0) > 50 ? '...' : ''}`,
|
|
111
|
+
description: '',
|
|
112
|
+
severity: 'high' as 'low' | 'medium' | 'high' | 'critical',
|
|
113
|
+
},
|
|
114
|
+
validate: {
|
|
115
|
+
title: (value) =>
|
|
116
|
+
value.trim().length < 5 ? 'Title must be at least 5 characters' : null,
|
|
117
|
+
description: (value) =>
|
|
118
|
+
value.trim().length < 10
|
|
119
|
+
? 'Please provide more details (at least 10 characters)'
|
|
120
|
+
: null,
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const getBrowserInfo = useCallback((): BrowserInfo => {
|
|
125
|
+
return {
|
|
126
|
+
userAgent: navigator.userAgent,
|
|
127
|
+
platform: navigator.platform,
|
|
128
|
+
language: navigator.language,
|
|
129
|
+
cookiesEnabled: navigator.cookieEnabled,
|
|
130
|
+
onLine: navigator.onLine,
|
|
131
|
+
screenWidth: window.screen.width,
|
|
132
|
+
screenHeight: window.screen.height,
|
|
133
|
+
colorDepth: window.screen.colorDepth,
|
|
134
|
+
pixelRatio: window.devicePixelRatio,
|
|
135
|
+
};
|
|
136
|
+
}, []);
|
|
137
|
+
|
|
138
|
+
const getErrorDetails = useCallback((): string => {
|
|
139
|
+
const parts = [
|
|
140
|
+
'--- Error Details ---',
|
|
141
|
+
`Message: ${error?.message || 'Unknown error'}`,
|
|
142
|
+
];
|
|
143
|
+
|
|
144
|
+
if (error?.stack) {
|
|
145
|
+
parts.push('', 'Stack Trace:', error.stack);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return parts.join('\n');
|
|
149
|
+
}, [error]);
|
|
150
|
+
|
|
151
|
+
const handleSubmit = async (values: typeof form.values) => {
|
|
152
|
+
setIsSubmitting(true);
|
|
153
|
+
try {
|
|
154
|
+
// Build the full description with error details
|
|
155
|
+
const fullDescription = [
|
|
156
|
+
values.description,
|
|
157
|
+
'',
|
|
158
|
+
getErrorDetails(),
|
|
159
|
+
].join('\n');
|
|
160
|
+
|
|
161
|
+
// Create the report
|
|
162
|
+
await createReport({
|
|
163
|
+
title: values.title,
|
|
164
|
+
description: fullDescription,
|
|
165
|
+
severity: values.severity,
|
|
166
|
+
reporterType,
|
|
167
|
+
reporterId,
|
|
168
|
+
reporterEmail,
|
|
169
|
+
reporterName,
|
|
170
|
+
url: window.location.href,
|
|
171
|
+
route: window.location.pathname,
|
|
172
|
+
browserInfo: JSON.stringify(getBrowserInfo()),
|
|
173
|
+
viewportWidth: window.innerWidth,
|
|
174
|
+
viewportHeight: window.innerHeight,
|
|
175
|
+
networkState: navigator.onLine ? 'online' : 'offline',
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
notifications.show({
|
|
179
|
+
title: 'Bug report submitted',
|
|
180
|
+
message: 'Thank you for reporting this error! We will investigate.',
|
|
181
|
+
color: 'green',
|
|
182
|
+
icon: <IconCheck size={16} />,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
onSuccess?.();
|
|
186
|
+
|
|
187
|
+
form.reset();
|
|
188
|
+
close();
|
|
189
|
+
} catch (submitError) {
|
|
190
|
+
console.error('Failed to submit bug report:', submitError);
|
|
191
|
+
notifications.show({
|
|
192
|
+
title: 'Submission failed',
|
|
193
|
+
message: 'Could not submit bug report. Please try again.',
|
|
194
|
+
color: 'red',
|
|
195
|
+
});
|
|
196
|
+
onError?.(submitError instanceof Error ? submitError : new Error(String(submitError)));
|
|
197
|
+
} finally {
|
|
198
|
+
setIsSubmitting(false);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const handleOpen = () => {
|
|
203
|
+
// Reset form with current error info
|
|
204
|
+
form.setValues({
|
|
205
|
+
title: `Error: ${error?.message?.slice(0, 50) || 'Unknown error'}${(error?.message?.length || 0) > 50 ? '...' : ''}`,
|
|
206
|
+
description: '',
|
|
207
|
+
severity: 'high',
|
|
208
|
+
});
|
|
209
|
+
open();
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
<>
|
|
214
|
+
<Button
|
|
215
|
+
data-testid="error-report-bug-button"
|
|
216
|
+
leftSection={<IconBug size={16} />}
|
|
217
|
+
onClick={handleOpen}
|
|
218
|
+
variant={variant}
|
|
219
|
+
color={color}
|
|
220
|
+
>
|
|
221
|
+
Report Bug
|
|
222
|
+
</Button>
|
|
223
|
+
|
|
224
|
+
<Modal
|
|
225
|
+
opened={opened}
|
|
226
|
+
onClose={close}
|
|
227
|
+
title={
|
|
228
|
+
<Group gap="xs">
|
|
229
|
+
<IconBug size={20} />
|
|
230
|
+
<Text fw={600}>Report This Error</Text>
|
|
231
|
+
</Group>
|
|
232
|
+
}
|
|
233
|
+
size="md"
|
|
234
|
+
>
|
|
235
|
+
<LoadingOverlay visible={isSubmitting} />
|
|
236
|
+
<form onSubmit={form.onSubmit(handleSubmit)}>
|
|
237
|
+
<Stack gap="md">
|
|
238
|
+
<TextInput
|
|
239
|
+
label="Title"
|
|
240
|
+
placeholder="Brief summary of the issue"
|
|
241
|
+
required
|
|
242
|
+
{...form.getInputProps('title')}
|
|
243
|
+
/>
|
|
244
|
+
|
|
245
|
+
<Textarea
|
|
246
|
+
label="What were you doing when this error occurred?"
|
|
247
|
+
placeholder="Please describe what you were trying to do, what you expected to happen, and any other relevant context."
|
|
248
|
+
required
|
|
249
|
+
minRows={3}
|
|
250
|
+
{...form.getInputProps('description')}
|
|
251
|
+
/>
|
|
252
|
+
|
|
253
|
+
<Select
|
|
254
|
+
label="Severity"
|
|
255
|
+
placeholder="How severe is this issue?"
|
|
256
|
+
data={[
|
|
257
|
+
{ value: 'low', label: 'Low - Minor inconvenience' },
|
|
258
|
+
{ value: 'medium', label: 'Medium - Affects workflow' },
|
|
259
|
+
{ value: 'high', label: 'High - Major functionality broken' },
|
|
260
|
+
{ value: 'critical', label: 'Critical - System unusable' },
|
|
261
|
+
]}
|
|
262
|
+
{...form.getInputProps('severity')}
|
|
263
|
+
/>
|
|
264
|
+
|
|
265
|
+
{/* Error Details Preview */}
|
|
266
|
+
<Paper withBorder p="xs" radius="md" bg="var(--mantine-color-red-light)">
|
|
267
|
+
<Stack gap="xs">
|
|
268
|
+
<Group gap="xs">
|
|
269
|
+
<Badge size="xs" color="red">
|
|
270
|
+
Error Details (auto-included)
|
|
271
|
+
</Badge>
|
|
272
|
+
</Group>
|
|
273
|
+
<Text
|
|
274
|
+
size="xs"
|
|
275
|
+
c="dimmed"
|
|
276
|
+
style={{ fontFamily: 'monospace', whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}
|
|
277
|
+
>
|
|
278
|
+
{error?.message || 'Unknown error'}
|
|
279
|
+
</Text>
|
|
280
|
+
</Stack>
|
|
281
|
+
</Paper>
|
|
282
|
+
|
|
283
|
+
{/* Auto-captured Info Badge */}
|
|
284
|
+
<Paper withBorder p="xs" radius="md">
|
|
285
|
+
<Group gap="xs">
|
|
286
|
+
<Badge size="xs" variant="outline" color="gray">
|
|
287
|
+
Auto-captured
|
|
288
|
+
</Badge>
|
|
289
|
+
<Text size="xs" c="dimmed">
|
|
290
|
+
Browser info, URL, viewport size, and full stack trace will be included.
|
|
291
|
+
</Text>
|
|
292
|
+
</Group>
|
|
293
|
+
</Paper>
|
|
294
|
+
|
|
295
|
+
{/* Submit Buttons */}
|
|
296
|
+
<Group justify="flex-end" mt="md">
|
|
297
|
+
<Button variant="subtle" onClick={close} disabled={isSubmitting}>
|
|
298
|
+
Cancel
|
|
299
|
+
</Button>
|
|
300
|
+
<Button type="submit" color="red" loading={isSubmitting}>
|
|
301
|
+
Submit Report
|
|
302
|
+
</Button>
|
|
303
|
+
</Group>
|
|
304
|
+
</Stack>
|
|
305
|
+
</form>
|
|
306
|
+
</Modal>
|
|
307
|
+
</>
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export default ErrorBugReportButton;
|