@fatagnus/convex-feedback 0.1.0

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.
Files changed (78) hide show
  1. package/LICENSE +177 -0
  2. package/README.md +382 -0
  3. package/dist/convex/agents/bugReportAgent.d.ts +30 -0
  4. package/dist/convex/agents/bugReportAgent.d.ts.map +1 -0
  5. package/dist/convex/agents/bugReportAgent.js +243 -0
  6. package/dist/convex/agents/bugReportAgent.js.map +1 -0
  7. package/dist/convex/agents/feedbackAgent.d.ts +29 -0
  8. package/dist/convex/agents/feedbackAgent.d.ts.map +1 -0
  9. package/dist/convex/agents/feedbackAgent.js +232 -0
  10. package/dist/convex/agents/feedbackAgent.js.map +1 -0
  11. package/dist/convex/bugReports.d.ts +49 -0
  12. package/dist/convex/bugReports.d.ts.map +1 -0
  13. package/dist/convex/bugReports.js +321 -0
  14. package/dist/convex/bugReports.js.map +1 -0
  15. package/dist/convex/convex.config.d.ts +3 -0
  16. package/dist/convex/convex.config.d.ts.map +1 -0
  17. package/dist/convex/convex.config.js +6 -0
  18. package/dist/convex/convex.config.js.map +1 -0
  19. package/dist/convex/emails/bugReportEmails.d.ts +16 -0
  20. package/dist/convex/emails/bugReportEmails.d.ts.map +1 -0
  21. package/dist/convex/emails/bugReportEmails.js +403 -0
  22. package/dist/convex/emails/bugReportEmails.js.map +1 -0
  23. package/dist/convex/emails/feedbackEmails.d.ts +16 -0
  24. package/dist/convex/emails/feedbackEmails.d.ts.map +1 -0
  25. package/dist/convex/emails/feedbackEmails.js +389 -0
  26. package/dist/convex/emails/feedbackEmails.js.map +1 -0
  27. package/dist/convex/feedback.d.ts +49 -0
  28. package/dist/convex/feedback.d.ts.map +1 -0
  29. package/dist/convex/feedback.js +327 -0
  30. package/dist/convex/feedback.js.map +1 -0
  31. package/dist/convex/index.d.ts +10 -0
  32. package/dist/convex/index.d.ts.map +1 -0
  33. package/dist/convex/index.js +12 -0
  34. package/dist/convex/index.js.map +1 -0
  35. package/dist/convex/schema.d.ts +200 -0
  36. package/dist/convex/schema.d.ts.map +1 -0
  37. package/dist/convex/schema.js +150 -0
  38. package/dist/convex/schema.js.map +1 -0
  39. package/dist/convex/supportTeams.d.ts +29 -0
  40. package/dist/convex/supportTeams.d.ts.map +1 -0
  41. package/dist/convex/supportTeams.js +159 -0
  42. package/dist/convex/supportTeams.js.map +1 -0
  43. package/dist/index.d.ts +70 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +63 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/react/BugReportButton.d.ts +70 -0
  48. package/dist/react/BugReportButton.d.ts.map +1 -0
  49. package/dist/react/BugReportButton.js +371 -0
  50. package/dist/react/BugReportButton.js.map +1 -0
  51. package/dist/react/BugReportContext.d.ts +59 -0
  52. package/dist/react/BugReportContext.d.ts.map +1 -0
  53. package/dist/react/BugReportContext.js +107 -0
  54. package/dist/react/BugReportContext.js.map +1 -0
  55. package/dist/react/index.d.ts +36 -0
  56. package/dist/react/index.d.ts.map +1 -0
  57. package/dist/react/index.js +36 -0
  58. package/dist/react/index.js.map +1 -0
  59. package/dist/types.d.ts +89 -0
  60. package/dist/types.d.ts.map +1 -0
  61. package/dist/types.js +5 -0
  62. package/dist/types.js.map +1 -0
  63. package/package.json +101 -0
  64. package/src/convex/agents/bugReportAgent.ts +277 -0
  65. package/src/convex/agents/feedbackAgent.ts +264 -0
  66. package/src/convex/bugReports.ts +350 -0
  67. package/src/convex/convex.config.ts +7 -0
  68. package/src/convex/emails/bugReportEmails.ts +479 -0
  69. package/src/convex/emails/feedbackEmails.ts +465 -0
  70. package/src/convex/feedback.ts +356 -0
  71. package/src/convex/index.ts +28 -0
  72. package/src/convex/schema.ts +207 -0
  73. package/src/convex/supportTeams.ts +179 -0
  74. package/src/index.ts +77 -0
  75. package/src/react/BugReportButton.tsx +755 -0
  76. package/src/react/BugReportContext.tsx +146 -0
  77. package/src/react/index.ts +46 -0
  78. package/src/types.ts +93 -0
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @convex-dev/feedback - React Components
3
+ *
4
+ * React components for bug reports and feedback collection.
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * import { BugReportProvider, BugReportButton } from '@convex-dev/feedback/react';
9
+ * import { api } from './convex/_generated/api';
10
+ *
11
+ * function App() {
12
+ * return (
13
+ * <BugReportProvider>
14
+ * <YourApp />
15
+ * <BugReportButton
16
+ * reporterType="staff"
17
+ * reporterId={user.id}
18
+ * reporterEmail={user.email}
19
+ * reporterName={user.name}
20
+ * bugReportApi={{
21
+ * create: api.feedback.bugReports.create,
22
+ * generateUploadUrl: api.feedback.bugReports.generateUploadUrl,
23
+ * }}
24
+ * feedbackApi={{
25
+ * create: api.feedback.feedback.create,
26
+ * generateUploadUrl: api.feedback.feedback.generateUploadUrl,
27
+ * }}
28
+ * />
29
+ * </BugReportProvider>
30
+ * );
31
+ * }
32
+ * ```
33
+ */
34
+ export { BugReportProvider, useBugReportContext, } from './BugReportContext';
35
+ export { BugReportButton, } from './BugReportButton';
36
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Shared types for @convex-dev/feedback
3
+ */
4
+ /**
5
+ * Bug report document type
6
+ * Note: _id is typed as string for portability. Use your app's Id type for full typing.
7
+ */
8
+ export interface BugReport {
9
+ _id: string;
10
+ _creationTime: number;
11
+ title: string;
12
+ description: string;
13
+ severity: 'low' | 'medium' | 'high' | 'critical';
14
+ status: 'open' | 'in-progress' | 'resolved' | 'closed';
15
+ isArchived?: boolean;
16
+ reporterType: 'staff' | 'customer';
17
+ reporterId: string;
18
+ reporterEmail: string;
19
+ reporterName: string;
20
+ url: string;
21
+ route?: string;
22
+ browserInfo: string;
23
+ consoleErrors?: string;
24
+ screenshotStorageId?: string;
25
+ viewportWidth: number;
26
+ viewportHeight: number;
27
+ networkState: string;
28
+ aiSummary?: string;
29
+ aiRootCauseAnalysis?: string;
30
+ aiReproductionSteps?: string[];
31
+ aiSuggestedFix?: string;
32
+ aiEstimatedEffort?: 'low' | 'medium' | 'high';
33
+ aiSuggestedSeverity?: 'low' | 'medium' | 'high' | 'critical';
34
+ aiAnalyzedAt?: number;
35
+ aiThreadId?: string;
36
+ notificationsSentAt?: number;
37
+ createdAt: number;
38
+ updatedAt: number;
39
+ }
40
+ /**
41
+ * Feedback document type
42
+ */
43
+ export interface Feedback {
44
+ _id: string;
45
+ _creationTime: number;
46
+ type: 'feature_request' | 'change_request' | 'general';
47
+ title: string;
48
+ description: string;
49
+ priority: 'nice_to_have' | 'important' | 'critical';
50
+ status: 'open' | 'under_review' | 'planned' | 'in_progress' | 'completed' | 'declined';
51
+ isArchived?: boolean;
52
+ reporterType: 'staff' | 'customer';
53
+ reporterId: string;
54
+ reporterEmail: string;
55
+ reporterName: string;
56
+ url: string;
57
+ route?: string;
58
+ browserInfo: string;
59
+ consoleErrors?: string;
60
+ screenshotStorageId?: string;
61
+ viewportWidth: number;
62
+ viewportHeight: number;
63
+ networkState: string;
64
+ aiSummary?: string;
65
+ aiImpactAnalysis?: string;
66
+ aiActionItems?: string[];
67
+ aiEstimatedEffort?: 'low' | 'medium' | 'high';
68
+ aiSuggestedPriority?: 'nice_to_have' | 'important' | 'critical';
69
+ aiAnalyzedAt?: number;
70
+ aiThreadId?: string;
71
+ notificationsSentAt?: number;
72
+ createdAt: number;
73
+ updatedAt: number;
74
+ }
75
+ /**
76
+ * Support team document type
77
+ */
78
+ export interface SupportTeam {
79
+ _id: string;
80
+ _creationTime: number;
81
+ teamName: string;
82
+ memberEmails: string[];
83
+ feedbackTypes: ('feature_request' | 'change_request' | 'general')[];
84
+ bugReportSeverities: ('low' | 'medium' | 'high' | 'critical')[];
85
+ isActive: boolean;
86
+ createdAt: number;
87
+ updatedAt: number;
88
+ }
89
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Shared types for @convex-dev/feedback
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
package/package.json ADDED
@@ -0,0 +1,101 @@
1
+ {
2
+ "name": "@fatagnus/convex-feedback",
3
+ "version": "0.1.0",
4
+ "description": "Bug reports and feedback collection component for Convex applications with AI analysis and email notifications",
5
+ "license": "Apache-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/ozanturksever/convex-feedback"
9
+ },
10
+ "homepage": "https://github.com/ozanturksever/convex-feedback#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/ozanturksever/convex-feedback/issues"
13
+ },
14
+ "author": "Ozan Turksever <ozan.turksever@gmail.com>",
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "keywords": [
19
+ "convex",
20
+ "feedback",
21
+ "bug-reports",
22
+ "ai-analysis",
23
+ "email-notifications",
24
+ "react"
25
+ ],
26
+ "type": "module",
27
+ "main": "./dist/index.js",
28
+ "module": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "import": "./dist/index.js"
34
+ },
35
+ "./react": {
36
+ "types": "./dist/react/index.d.ts",
37
+ "import": "./dist/react/index.js"
38
+ },
39
+ "./convex.config": "./src/convex/convex.config.ts",
40
+ "./convex/*": "./src/convex/*"
41
+ },
42
+ "files": [
43
+ "dist",
44
+ "src",
45
+ "README.md",
46
+ "LICENSE"
47
+ ],
48
+ "scripts": {
49
+ "build": "tsc",
50
+ "dev": "tsc --watch",
51
+ "typecheck": "tsc --noEmit",
52
+ "clean": "rm -rf dist"
53
+ },
54
+ "peerDependencies": {
55
+ "@ai-sdk/openai-compatible": ">=0.1.0",
56
+ "@convex-dev/agent": ">=0.1.0",
57
+ "@mantine/core": ">=7.0.0",
58
+ "@mantine/form": ">=7.0.0",
59
+ "@mantine/hooks": ">=7.0.0",
60
+ "@mantine/notifications": ">=7.0.0",
61
+ "@tabler/icons-react": ">=3.0.0",
62
+ "convex": ">=1.17.0",
63
+ "html2canvas": ">=1.4.0",
64
+ "react": ">=18.0.0",
65
+ "resend": ">=4.0.0"
66
+ },
67
+ "peerDependenciesMeta": {
68
+ "@convex-dev/agent": {
69
+ "optional": true
70
+ },
71
+ "@ai-sdk/openai-compatible": {
72
+ "optional": true
73
+ },
74
+ "resend": {
75
+ "optional": true
76
+ },
77
+ "@mantine/core": {
78
+ "optional": true
79
+ },
80
+ "@mantine/form": {
81
+ "optional": true
82
+ },
83
+ "@mantine/hooks": {
84
+ "optional": true
85
+ },
86
+ "@mantine/notifications": {
87
+ "optional": true
88
+ },
89
+ "@tabler/icons-react": {
90
+ "optional": true
91
+ },
92
+ "html2canvas": {
93
+ "optional": true
94
+ }
95
+ },
96
+ "devDependencies": {
97
+ "@types/node": "^20.0.0",
98
+ "@types/react": "^18.0.0",
99
+ "typescript": "^5.0.0"
100
+ }
101
+ }
@@ -0,0 +1,277 @@
1
+ /**
2
+ * Bug Report AI Agent
3
+ *
4
+ * Automatically analyzes bug report submissions and generates:
5
+ * - Summary of the bug
6
+ * - Root cause analysis
7
+ * - Reproduction steps
8
+ * - Suggested fix
9
+ * - Estimated effort (low/medium/high)
10
+ * - Suggested severity (low/medium/high/critical)
11
+ *
12
+ * Triggers email notifications to the reporter and assigned team.
13
+ */
14
+
15
+ import { v } from "convex/values";
16
+ import { Agent, createThread } from "@convex-dev/agent";
17
+ import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
18
+ import { components, internal } from "../_generated/api";
19
+ import { internalAction, internalQuery } from "../_generated/server";
20
+ import type { BugSeverity, Effort } from "../schema";
21
+
22
+ // Create OpenRouter provider
23
+ const openrouter = createOpenAICompatible({
24
+ name: "openrouter",
25
+ baseURL: "https://openrouter.ai/api/v1",
26
+ headers: {
27
+ Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}`,
28
+ },
29
+ });
30
+
31
+ // Agent name constant
32
+ const AGENT_NAME = "Bug Report Analyst";
33
+
34
+ // ============================================================================
35
+ // Agent Definition
36
+ // ============================================================================
37
+
38
+ /**
39
+ * Bug Report Analysis Agent
40
+ *
41
+ * Analyzes bug report submissions and generates comprehensive analysis.
42
+ */
43
+ export const bugReportAgent = new Agent(components.agent, {
44
+ name: AGENT_NAME,
45
+ languageModel: openrouter.languageModel("anthropic/claude-sonnet-4"),
46
+
47
+ instructions: `You are the Bug Report Analyst, an AI assistant that analyzes bug report submissions.
48
+
49
+ When given a bug report, you will:
50
+ 1. **Summarize** the bug in 2-3 clear sentences
51
+ 2. **Analyze Root Cause** - identify potential underlying causes based on the symptoms described
52
+ 3. **Extract Reproduction Steps** - create clear, numbered steps to reproduce the bug
53
+ 4. **Suggest Fix** - provide a recommended approach to fix the bug
54
+ 5. **Estimate Effort** - rate as "low" (< 1 day), "medium" (1-3 days), or "high" (> 3 days)
55
+ 6. **Suggest Severity** - rate as "low" (minor inconvenience), "medium" (affects workflow), "high" (blocks work), or "critical" (data loss/security)
56
+
57
+ ## Response Format
58
+ Always respond in the following JSON format:
59
+ \`\`\`json
60
+ {
61
+ "summary": "A 2-3 sentence summary of the bug",
62
+ "rootCauseAnalysis": "Analysis of potential root cause",
63
+ "reproductionSteps": ["Step 1", "Step 2", "Step 3"],
64
+ "suggestedFix": "Recommended approach to fix the bug",
65
+ "estimatedEffort": "low" | "medium" | "high",
66
+ "suggestedSeverity": "low" | "medium" | "high" | "critical",
67
+ "reasoning": "Brief explanation for estimates"
68
+ }
69
+ \`\`\`
70
+
71
+ ## Guidelines
72
+ - Be precise and technical in your analysis
73
+ - Consider edge cases and related issues
74
+ - Reproduction steps should be specific and actionable
75
+ - Suggested fix should be practical and implementable
76
+ - Effort estimation should account for investigation, fix, testing, and deployment
77
+ - Severity should reflect user impact and business criticality`,
78
+
79
+ tools: {},
80
+ maxSteps: 3,
81
+ });
82
+
83
+ // ============================================================================
84
+ // Internal Queries
85
+ // ============================================================================
86
+
87
+ /**
88
+ * Get bug report by ID for analysis
89
+ */
90
+ export const getBugReportForAnalysis = internalQuery({
91
+ args: {
92
+ bugReportId: v.id("bugReports"),
93
+ },
94
+ returns: v.union(
95
+ v.object({
96
+ _id: v.id("bugReports"),
97
+ title: v.string(),
98
+ description: v.string(),
99
+ severity: v.string(),
100
+ reporterName: v.string(),
101
+ reporterEmail: v.string(),
102
+ url: v.string(),
103
+ browserInfo: v.string(),
104
+ consoleErrors: v.optional(v.string()),
105
+ }),
106
+ v.null()
107
+ ),
108
+ handler: async (ctx, args) => {
109
+ const bugReport = await ctx.db.get(args.bugReportId);
110
+ if (!bugReport) {
111
+ return null;
112
+ }
113
+ return {
114
+ _id: bugReport._id,
115
+ title: bugReport.title,
116
+ description: bugReport.description,
117
+ severity: bugReport.severity,
118
+ reporterName: bugReport.reporterName,
119
+ reporterEmail: bugReport.reporterEmail,
120
+ url: bugReport.url,
121
+ browserInfo: bugReport.browserInfo,
122
+ consoleErrors: bugReport.consoleErrors,
123
+ };
124
+ },
125
+ });
126
+
127
+ // ============================================================================
128
+ // Main Processing Action
129
+ // ============================================================================
130
+
131
+ /**
132
+ * Analyze bug report using AI and send notifications.
133
+ * This is the main entry point triggered after bug report creation.
134
+ */
135
+ export const processBugReport = internalAction({
136
+ args: {
137
+ bugReportId: v.id("bugReports"),
138
+ },
139
+ returns: v.object({
140
+ success: v.boolean(),
141
+ error: v.optional(v.string()),
142
+ }),
143
+ handler: async (ctx, args): Promise<{ success: boolean; error?: string }> => {
144
+ // Check if OpenRouter API key is configured
145
+ if (!process.env.OPENROUTER_API_KEY) {
146
+ console.log("OPENROUTER_API_KEY not configured, skipping AI analysis");
147
+ // Still try to send notifications without AI analysis
148
+ try {
149
+ await ctx.runAction(internal.emails.bugReportEmails.sendBugReportNotifications, {
150
+ reportId: args.bugReportId,
151
+ analysis: null,
152
+ });
153
+ } catch {
154
+ console.log("Email notifications not configured");
155
+ }
156
+ return { success: true };
157
+ }
158
+
159
+ // Get the bug report
160
+ const bugReport = await ctx.runQuery(internal.agents.bugReportAgent.getBugReportForAnalysis, {
161
+ bugReportId: args.bugReportId,
162
+ });
163
+
164
+ if (!bugReport) {
165
+ return { success: false, error: "Bug report not found" };
166
+ }
167
+
168
+ // Create a thread for this analysis
169
+ const threadId = await createThread(ctx, components.agent, {
170
+ title: `Bug Report Analysis: ${bugReport.title}`,
171
+ });
172
+
173
+ // Build the prompt for analysis
174
+ const consoleErrorsSection = bugReport.consoleErrors
175
+ ? `**Console Errors:** ${bugReport.consoleErrors}`
176
+ : "";
177
+
178
+ const analysisPrompt = `Please analyze the following bug report:
179
+
180
+ **Title:** ${bugReport.title}
181
+ **Description:** ${bugReport.description}
182
+ **Reporter Severity:** ${bugReport.severity}
183
+ **Submitted From:** ${bugReport.url}
184
+ **Browser Info:** ${bugReport.browserInfo}
185
+ ${consoleErrorsSection}
186
+
187
+ Provide your analysis in the JSON format specified.`;
188
+
189
+ // Generate analysis using the agent
190
+ let result: { text: string };
191
+ try {
192
+ result = await bugReportAgent.generateText(
193
+ ctx,
194
+ { threadId },
195
+ { prompt: analysisPrompt }
196
+ );
197
+ } catch (error) {
198
+ console.error("AI analysis failed:", error);
199
+ return { success: false, error: `AI analysis failed: ${error instanceof Error ? error.message : String(error)}` };
200
+ }
201
+
202
+ // Parse the AI response
203
+ let analysis: {
204
+ summary: string;
205
+ rootCauseAnalysis: string;
206
+ reproductionSteps: string[];
207
+ suggestedFix: string;
208
+ estimatedEffort: Effort;
209
+ suggestedSeverity: BugSeverity;
210
+ };
211
+
212
+ try {
213
+ // Extract JSON from the response (handle markdown code blocks)
214
+ const jsonMatch = result.text.match(/```json\s*([\s\S]*?)\s*```/);
215
+ const jsonStr = jsonMatch ? jsonMatch[1] : result.text;
216
+ const parsed = JSON.parse(jsonStr);
217
+
218
+ analysis = {
219
+ summary: parsed.summary || "Unable to generate summary",
220
+ rootCauseAnalysis: parsed.rootCauseAnalysis || "Unable to generate root cause analysis",
221
+ reproductionSteps: Array.isArray(parsed.reproductionSteps) ? parsed.reproductionSteps : [],
222
+ suggestedFix: parsed.suggestedFix || "Unable to generate suggested fix",
223
+ estimatedEffort: ["low", "medium", "high"].includes(parsed.estimatedEffort)
224
+ ? parsed.estimatedEffort
225
+ : "medium",
226
+ suggestedSeverity: ["low", "medium", "high", "critical"].includes(parsed.suggestedSeverity)
227
+ ? parsed.suggestedSeverity
228
+ : bugReport.severity as BugSeverity,
229
+ };
230
+ } catch {
231
+ // If parsing fails, create a basic analysis from the text
232
+ analysis = {
233
+ summary: result.text.substring(0, 500),
234
+ rootCauseAnalysis: "AI analysis parsing failed. Please review manually.",
235
+ reproductionSteps: ["Review bug report manually", "Contact reporter for clarification"],
236
+ suggestedFix: "Manual investigation required.",
237
+ estimatedEffort: "medium",
238
+ suggestedSeverity: bugReport.severity as BugSeverity,
239
+ };
240
+ }
241
+
242
+ // Update bug report with analysis
243
+ await ctx.runMutation(internal.bugReports.updateWithAnalysis, {
244
+ bugReportId: args.bugReportId,
245
+ aiSummary: analysis.summary,
246
+ aiRootCauseAnalysis: analysis.rootCauseAnalysis,
247
+ aiReproductionSteps: analysis.reproductionSteps,
248
+ aiSuggestedFix: analysis.suggestedFix,
249
+ aiEstimatedEffort: analysis.estimatedEffort,
250
+ aiSuggestedSeverity: analysis.suggestedSeverity,
251
+ aiThreadId: threadId,
252
+ });
253
+
254
+ // Send email notifications
255
+ try {
256
+ await ctx.runAction(internal.emails.bugReportEmails.sendBugReportNotifications, {
257
+ reportId: args.bugReportId,
258
+ analysis: {
259
+ summary: analysis.summary,
260
+ rootCauseAnalysis: analysis.rootCauseAnalysis,
261
+ reproductionSteps: analysis.reproductionSteps,
262
+ suggestedFix: analysis.suggestedFix,
263
+ estimatedEffort: analysis.estimatedEffort,
264
+ },
265
+ });
266
+
267
+ // Mark notifications as sent
268
+ await ctx.runMutation(internal.bugReports.markNotificationsSent, {
269
+ bugReportId: args.bugReportId,
270
+ });
271
+ } catch {
272
+ console.log("Email notifications not configured or failed");
273
+ }
274
+
275
+ return { success: true };
276
+ },
277
+ });