# @fatagnus/convex-feedback

Bug reports and feedback collection component for Convex applications with AI analysis, email notifications, and a REST API.

## Features

- 🐛 **Bug Reports** - Capture bug reports with browser diagnostics, console errors, and screenshots
- 💬 **Feedback** - Collect feature requests, change requests, and general feedback
- 🤖 **AI Analysis** - Automatic analysis of submissions using OpenRouter/Claude
- 💬 **AI Interview Mode** - Conversational AI interviews to help users articulate detailed bug reports and feedback
- 📧 **Email Notifications** - Send notifications to reporters and support teams via Resend
- 👥 **Support Teams** - Route notifications to the right teams based on type/severity
- 📸 **Screenshots** - Capture or upload screenshots with reports
- 🔄 **Real-time** - Leverages Convex for real-time updates
- 🎫 **Ticket Numbers** - Human-readable ticket IDs (BUG-2025-0001, FB-2025-0001)
- 🔑 **API Keys** - Secure API key management for external integrations
- 🌐 **REST API** - HTTP endpoints for external tools (Codebuff, CLI tools, etc.)

## Installation

```bash
npm install @fatagnus/convex-feedback
```

### Peer Dependencies

**Required:**
- `convex` (>=1.17.0)
- `react` (>=18.0.0)

**Optional (for React components):**
- `@mantine/core` (>=7.0.0)
- `@mantine/form` (>=7.0.0)
- `@mantine/hooks` (>=7.0.0)
- `@mantine/notifications` (>=7.0.0)
- `@tabler/icons-react` (>=3.0.0)
- `html2canvas` (>=1.4.0)

**Optional (for AI analysis and interview mode):**
- `@convex-dev/agent` (>=0.1.0)
- `@ai-sdk/openai-compatible` (>=0.1.0)
- `zod` (>=3.0.0)

**Optional (for email notifications):**
- `resend` (>=4.0.0)

## Setup

### 1. Add the Component

Add the feedback component to your Convex app configuration:

```typescript
// convex/convex.config.ts
import { defineApp } from "convex/server";
import feedback from "@fatagnus/convex-feedback/convex.config";

const app = defineApp();
app.use(feedback);
export default app;
```

### 2. Register HTTP Routes (Optional)

To enable the REST API for external integrations:

```typescript
// convex/http.ts
import { httpRouter } from "convex/server";
import { registerFeedbackRoutes } from "@fatagnus/convex-feedback";

const http = httpRouter();

// Register feedback API routes with optional prefix
registerFeedbackRoutes(http, { pathPrefix: "/feedback" });

// Your other routes...
export default http;
```

### 3. Configure Environment Variables

Set these environment variables in your Convex dashboard:

| Variable | Required | Description |
|----------|----------|-------------|
| `OPENROUTER_API_KEY` | No | OpenRouter API key for AI analysis |
| `RESEND_API_KEY` | No | Resend API key for email notifications |
| `RESEND_FROM_EMAIL` | No | From email address (default: `bugs@resend.dev`) |

### 4. Add React Components

Wrap your app with the `BugReportProvider` and add the `BugReportButton`:

```tsx
import { BugReportProvider, BugReportButton } from '@fatagnus/convex-feedback/react';
import { api } from './convex/_generated/api';

function App() {
  const user = useCurrentUser(); // Your auth hook

  return (
    <BugReportProvider>
      <YourApp />
      
      {user && (
        <BugReportButton
          reporterType="staff"
          reporterId={user.id}
          reporterEmail={user.email}
          reporterName={user.name}
          bugReportApi={{
            create: api.feedback.bugReports.create,
            generateUploadUrl: api.feedback.bugReports.generateUploadUrl,
          }}
          feedbackApi={{
            create: api.feedback.feedback.create,
            generateUploadUrl: api.feedback.feedback.generateUploadUrl,
          }}
        />
      )}
    </BugReportProvider>
  );
}
```

---

## HTTP REST API

The REST API allows external tools (like Codebuff, CLI tools, CI/CD pipelines) to interact with bug reports and feedback.

### Authentication

All endpoints require an API key in the `Authorization` header:

```
Authorization: Bearer fb_your_api_key_here
```

### Base URL

Your Convex HTTP endpoint + the path prefix you configured:

```
https://your-deployment.convex.site/feedback/api/...
```

### Ticket Number Format

All tickets have human-readable IDs:

| Type | Format | Example |
|------|--------|----------|
| Bug Report | `BUG-YYYY-NNNN` | `BUG-2025-0001` |
| Feedback | `FB-YYYY-NNNN` | `FB-2025-0042` |

---

### API Key Management

API keys are managed via Convex mutations. The full key is only shown once at creation time.

#### Create a Key

```typescript
const result = await ctx.runMutation(api.feedback.apiKeys.create, {
  name: "My Integration",
  expiresAt: Date.now() + 90 * 24 * 60 * 60 * 1000, // 90 days (optional)
});

console.log(result.key);       // fb_a1b2c3d4e5f6g7h8... (save this!)
console.log(result.keyPrefix); // fb_a1b2c3d4
```

#### List Keys

```typescript
const keys = await ctx.runQuery(api.feedback.apiKeys.list, {});
// Returns array of { _id, keyPrefix, name, expiresAt, isRevoked, isExpired, createdAt }
```

#### Revoke a Key

```typescript
await ctx.runMutation(api.feedback.apiKeys.revoke, {
  keyPrefix: "fb_a1b2c3d4",
});
```

#### Rotate a Key

```typescript
const newKey = await ctx.runMutation(api.feedback.apiKeys.rotate, {
  keyPrefix: "fb_a1b2c3d4",
  name: "Rotated Key", // optional
  expiresAt: Date.now() + 90 * 24 * 60 * 60 * 1000, // optional
});
```

---

### Endpoints

#### GET `/api/items`

List bug reports and/or feedback items with optional filters.

**Query Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `itemType` | `bug` \| `feedback` \| `all` | `all` | Filter by item type |
| `status` | string | - | Filter by status |
| `severity` | `low` \| `medium` \| `high` \| `critical` | - | Filter bugs by severity |
| `priority` | `nice_to_have` \| `important` \| `critical` | - | Filter feedback by priority |
| `type` | `feature_request` \| `change_request` \| `general` | - | Filter feedback by type |
| `limit` | number | `50` | Max items to return |
| `includeArchived` | `true` \| `false` | `false` | Include archived items |

**Bug Status Values:** `open`, `in-progress`, `resolved`, `closed`

**Feedback Status Values:** `open`, `under_review`, `planned`, `in_progress`, `completed`, `declined`

**Example:**

```bash
# List all open bugs
curl -H "Authorization: Bearer fb_your_key" \
  "https://your-site.convex.site/feedback/api/items?itemType=bug&status=open"
```

**Response:**

```json
{
  "bugs": [
    {
      "_id": "abc123",
      "ticketNumber": "BUG-2025-0001",
      "title": "Login button not working",
      "severity": "high",
      "status": "open",
      ...
    }
  ],
  "feedback": [...]
}
```

---

#### GET `/api/items/{ticketNumber}`

Fetch a single bug report or feedback item by ticket number.

**Example:**

```bash
curl -H "Authorization: Bearer fb_your_key" \
  "https://your-site.convex.site/feedback/api/items/BUG-2025-0001"
```

**Response:**

```json
{
  "type": "bug",
  "_id": "abc123",
  "ticketNumber": "BUG-2025-0001",
  "title": "Login button not working",
  "description": "When I click the login button, nothing happens",
  "severity": "high",
  "status": "open",
  "aiSummary": "The login form submit handler is not properly bound",
  "aiRootCauseAnalysis": "React event handler not attached correctly",
  "aiSuggestedFix": "Check that the onClick handler is properly bound",
  ...
}
```

---

#### GET `/api/prompt/{ticketNumber}`

Fetch an AI-generated prompt for fixing/implementing a ticket. Designed for AI coding assistants like Codebuff.

**Query Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `template` | `fix` \| `implement` \| `analyze` \| `codebuff` | `fix` (bugs) / `implement` (feedback) | Prompt template |

**Templates:**

| Template | Use Case |
|----------|----------|
| `fix` | Bug fix instructions |
| `implement` | Feature implementation instructions |
| `analyze` | Deep analysis prompt |
| `codebuff` | Codebuff-optimized structured format |

**Example:**

```bash
# Get a Codebuff-optimized prompt
curl -H "Authorization: Bearer fb_your_key" \
  "https://your-site.convex.site/feedback/api/prompt/BUG-2025-0001?template=codebuff"
```

**Response:**

```json
{
  "ticketNumber": "BUG-2025-0001",
  "type": "bug",
  "template": "codebuff",
  "prompt": "# Bug Fix Request: BUG-2025-0001\n\n## Title\nLogin button not working\n\n..."
}
```

---

#### PATCH `/api/items/{ticketNumber}/status`

Update the status of a bug report or feedback item.

**Request Body:**

```json
{ "status": "resolved" }
```

**Example:**

```bash
curl -X PATCH \
  -H "Authorization: Bearer fb_your_key" \
  -H "Content-Type: application/json" \
  -d '{"status": "resolved"}' \
  "https://your-site.convex.site/feedback/api/items/BUG-2025-0001/status"
```

**Response:**

```json
{ "success": true, "ticketNumber": "BUG-2025-0001" }
```

---

#### PATCH `/api/items/{ticketNumber}/archive`

Archive or unarchive a bug report or feedback item.

**Request Body:**

```json
{ "archived": true }
```

**Example:**

```bash
curl -X PATCH \
  -H "Authorization: Bearer fb_your_key" \
  -H "Content-Type: application/json" \
  -d '{"archived": true}' \
  "https://your-site.convex.site/feedback/api/items/BUG-2025-0001/archive"
```

**Response:**

```json
{ "success": true, "ticketNumber": "BUG-2025-0001", "isArchived": true }
```

---

### Error Responses

| Status | Response |
|--------|----------|
| 400 | `{ "error": "Bad request", "message": "..." }` |
| 401 | `{ "error": "Unauthorized", "message": "Invalid or missing API key" }` |
| 404 | `{ "error": "Not found", "message": "Bug report BUG-2025-0001 not found" }` |

---

### Integration Examples

#### Codebuff Integration

```bash
curl -s -H "Authorization: Bearer fb_your_key" \
  "https://your-site.convex.site/feedback/api/prompt/BUG-2025-0001?template=codebuff" \
  | jq -r '.prompt' \
  | codebuff
```

#### GitHub Actions

```yaml
- name: Get open bugs count
  run: |
    curl -s -H "Authorization: Bearer ${{ secrets.FEEDBACK_API_KEY }}" \
      "${{ secrets.CONVEX_SITE_URL }}/feedback/api/items?itemType=bug&status=open" \
      | jq '.bugs | length'
```

---

## Convex API Reference

### Bug Reports

#### `bugReports.create`

Create a new bug report.

```typescript
const bugReportId = await ctx.runMutation(api.feedback.bugReports.create, {
  title: "Button not working",
  description: "The submit button doesn't respond to clicks",
  severity: "high", // low | medium | high | critical
  reporterType: "staff", // staff | customer
  reporterId: "user_123",
  reporterEmail: "user@example.com",
  reporterName: "John Doe",
  url: "https://app.example.com/form",
  route: "/form",
  browserInfo: JSON.stringify({ userAgent: "..." }),
  consoleErrors: JSON.stringify([{ message: "Error..." }]),
  screenshotStorageId: "storage_id_here", // optional
  viewportWidth: 1920,
  viewportHeight: 1080,
  networkState: "online",
});
```

#### `bugReports.list`

List bug reports with optional filters.

```typescript
const reports = await ctx.runQuery(api.feedback.bugReports.list, {
  status: "open", // optional: open | in-progress | resolved | closed
  severity: "high", // optional: low | medium | high | critical
  includeArchived: false, // optional, default: false
  limit: 50, // optional, default: 50
});
```

#### `bugReports.get`

Get a single bug report by ID.

```typescript
const report = await ctx.runQuery(api.feedback.bugReports.get, {
  reportId: "bug_report_id",
});
```

#### `bugReports.updateStatus`

Update the status of a bug report.

```typescript
await ctx.runMutation(api.feedback.bugReports.updateStatus, {
  reportId: "bug_report_id",
  status: "resolved",
});
```

#### `bugReports.archive` / `bugReports.unarchive`

Archive or unarchive a bug report.

```typescript
await ctx.runMutation(api.feedback.bugReports.archive, {
  reportId: "bug_report_id",
});
```

### Feedback

#### `feedback.create`

Create new feedback.

```typescript
const feedbackId = await ctx.runMutation(api.feedback.feedback.create, {
  type: "feature_request", // feature_request | change_request | general
  title: "Dark mode support",
  description: "Please add dark mode to reduce eye strain",
  priority: "important", // nice_to_have | important | critical
  reporterType: "customer",
  reporterId: "customer_123",
  reporterEmail: "customer@example.com",
  reporterName: "Jane Doe",
  url: "https://app.example.com/settings",
  browserInfo: JSON.stringify({ userAgent: "..." }),
  viewportWidth: 1920,
  viewportHeight: 1080,
  networkState: "online",
});
```

#### `feedback.list`

List feedback with optional filters.

```typescript
const items = await ctx.runQuery(api.feedback.feedback.list, {
  status: "open", // optional
  type: "feature_request", // optional
  priority: "important", // optional
  includeArchived: false, // optional
  limit: 50, // optional
});
```

### Support Teams

#### `supportTeams.create`

Create a support team for notification routing.

```typescript
const teamId = await ctx.runMutation(api.feedback.supportTeams.create, {
  teamName: "Engineering Team",
  memberEmails: ["dev1@example.com", "dev2@example.com"],
  feedbackTypes: ["feature_request", "change_request"],
  bugReportSeverities: ["high", "critical"],
});
```

#### `supportTeams.list`

List all support teams.

```typescript
const teams = await ctx.runQuery(api.feedback.supportTeams.list, {
  activeOnly: true, // optional
});
```

#### `supportTeams.update`

Update a support team.

```typescript
await ctx.runMutation(api.feedback.supportTeams.update, {
  teamId: "team_id",
  teamName: "Updated Team Name",
  memberEmails: ["new@example.com"],
  isActive: true,
});
```

---

## React Components

### BugReportProvider

Provider component that captures console errors for bug reports. Wrap your app with this provider.

```tsx
import { BugReportProvider } from '@fatagnus/convex-feedback/react';

function App() {
  return (
    <BugReportProvider maxErrors={20}>
      <YourApp />
    </BugReportProvider>
  );
}
```

**Props:**

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `children` | `ReactNode` | - | Child components |
| `maxErrors` | `number` | `20` | Maximum console errors to capture |

### BugReportButton

Floating action button for submitting bug reports and feedback.

```tsx
import { BugReportButton } from '@fatagnus/convex-feedback/react';

<BugReportButton
  reporterType="staff"
  reporterId={user.id}
  reporterEmail={user.email}
  reporterName={user.name}
  bugReportApi={{
    create: api.feedback.bugReports.create,
    generateUploadUrl: api.feedback.bugReports.generateUploadUrl,
  }}
  feedbackApi={{
    create: api.feedback.feedback.create,
    generateUploadUrl: api.feedback.feedback.generateUploadUrl,
  }}
  position={{ bottom: 20, right: 20 }}
  buttonColor="red"
  showFeedback={true}
  onSuccess={(type) => console.log(`${type} submitted`)}
  onError={(error, type) => console.error(`${type} failed:`, error)}
  // AI Interview Mode (optional)
  enableInterview={true}
  defaultMode="interview"
  interviewApi={{
    startBugInterview: api.feedback.agents.feedbackInterviewAgent.startBugInterview,
    startFeedbackInterview: api.feedback.agents.feedbackInterviewAgent.startFeedbackInterview,
    continueInterview: api.feedback.agents.feedbackInterviewAgent.continueInterview,
    getPendingForThread: api.feedback.inputRequests.getPendingForThread,
    getSessionByThread: api.feedback.agents.feedbackInterviewAgent.getSessionByThread,
  }}
  interviewContext={{
    appName: 'My App',
    appDescription: 'A productivity tool for teams',
    featureAreas: [
      { name: 'Dashboard', description: 'Main overview page' },
      { name: 'Reports', description: 'Analytics and reporting' },
    ],
  }}
/>
```

**Props:**

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `reporterType` | `'staff' \| 'customer'` | - | Type of reporter |
| `reporterId` | `string` | - | Unique reporter identifier |
| `reporterEmail` | `string` | - | Reporter's email |
| `reporterName` | `string` | - | Reporter's display name |
| `bugReportApi` | `object` | - | Convex API references for bug reports |
| `feedbackApi` | `object` | - | Convex API references for feedback |
| `position` | `object` | `{ bottom: 20, right: 20 }` | FAB position |
| `buttonColor` | `string` | `'red'` | Button color |
| `showFeedback` | `boolean` | `true` | Show feedback tab |
| `onSuccess` | `function` | - | Success callback |
| `onError` | `function` | - | Error callback |
| `enableInterview` | `boolean` | `false` | Enable AI interview mode |
| `defaultMode` | `'form' \| 'interview'` | `'form'` | Default input mode when interview is enabled |
| `interviewContext` | `InterviewContext` | - | Context to help AI ask better questions |
| `interviewApi` | `object` | - | Convex API references for interview actions (required if `enableInterview` is true) |

### ErrorBugReportButton

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.

```tsx
import { ErrorBugReportButton } from '@fatagnus/convex-feedback/react';
import { api } from './convex/_generated/api';

function ErrorPage({ error }: { error: Error }) {
  return (
    <div>
      <h1>Something went wrong</h1>
      <p>{error.message}</p>
      
      <ErrorBugReportButton
        error={error}
        reporterType="staff"
        reporterId={user?.id}
        reporterEmail={user?.email}
        reporterName={user?.name}
        bugReportApi={{
          create: api.feedback.bugReports.create,
        }}
        variant="outline"
        color="red"
        onSuccess={() => console.log('Bug report submitted')}
        onError={(err) => console.error('Failed to submit:', err)}
      />
    </div>
  );
}
```

**Props:**

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `error` | `Error` | - | The error to report (required) |
| `reporterType` | `'staff' \| 'customer'` | `'staff'` | Type of reporter |
| `reporterId` | `string` | `'error-page'` | Unique reporter identifier |
| `reporterEmail` | `string` | `'unknown@example.com'` | Reporter's email |
| `reporterName` | `string` | `'Error Page Reporter'` | Reporter's display name |
| `bugReportApi` | `object` | - | Convex API reference with `create` mutation (required) |
| `variant` | `'filled' \| 'outline' \| 'light' \| 'subtle'` | `'outline'` | Button variant |
| `color` | `string` | `'red'` | Button color |
| `onSuccess` | `function` | - | Success callback |
| `onError` | `function` | - | Error callback |

**Note:** Unlike `BugReportButton`, this component only requires the `bugReportApi.create` mutation (no `generateUploadUrl` needed since error pages typically don't include screenshots).

### useBugReportContext

Hook to access the bug report context for error capture.

```tsx
import { useBugReportContext } from '@fatagnus/convex-feedback/react';

function MyComponent() {
  const { getConsoleErrors, clearConsoleErrors } = useBugReportContext();
  
  const errors = getConsoleErrors();
  // ...
}
```

---

## AI Interview Mode

The AI Interview Mode provides a conversational experience to help users articulate their bug reports and feedback more effectively. Instead of filling out a form, users chat with an AI that asks clarifying questions and generates a well-structured report.

### Enabling Interview Mode

To enable AI interviews, you need:

1. `OPENROUTER_API_KEY` environment variable set in your Convex dashboard
2. The `@convex-dev/agent` and `zod` packages installed
3. The `enableInterview` and `interviewApi` props configured

```tsx
import { BugReportButton } from '@fatagnus/convex-feedback/react';
import { api } from './convex/_generated/api';

<BugReportButton
  reporterType="staff"
  reporterId={user.id}
  reporterEmail={user.email}
  reporterName={user.name}
  bugReportApi={{
    create: api.feedback.bugReports.create,
    generateUploadUrl: api.feedback.bugReports.generateUploadUrl,
  }}
  feedbackApi={{
    create: api.feedback.feedback.create,
    generateUploadUrl: api.feedback.feedback.generateUploadUrl,
  }}
  // Enable AI Interview Mode
  enableInterview={true}
  defaultMode="interview" // or "form" to default to manual form
  interviewApi={{
    startBugInterview: api.feedback.agents.feedbackInterviewAgent.startBugInterview,
    startFeedbackInterview: api.feedback.agents.feedbackInterviewAgent.startFeedbackInterview,
    continueInterview: api.feedback.agents.feedbackInterviewAgent.continueInterview,
    getPendingForThread: api.feedback.inputRequests.getPendingForThread,
    getSessionByThread: api.feedback.agents.feedbackInterviewAgent.getSessionByThread,
  }}
/>
```

### Providing Context

You can provide application context to help the AI ask more relevant questions:

```tsx
<BugReportButton
  // ... other props
  interviewContext={{
    // Application information
    appName: 'Logsign SIEM',
    appDescription: 'A security information and event management platform',
    
    // Feature areas (AI will offer these as choices)
    featureAreas: [
      { name: 'Dashboard', description: 'Main overview and metrics' },
      { name: 'Alerts', description: 'Security alert management' },
      { name: 'Reports', description: 'Analytics and reporting' },
      { name: 'Settings', description: 'User and system configuration' },
    ],
    
    // Known issues (AI will check for duplicates)
    knownIssues: [
      { title: 'Slow dashboard loading', description: 'Dashboard takes 5+ seconds on first load' },
      { title: 'Export timeout', description: 'Large reports fail to export' },
    ],
    
    // Custom questions to ask during interview
    customQuestions: {
      bug: [
        'Are you using any browser extensions?',
        'Did this work before a recent update?',
      ],
      feedback: [
        'How urgent is this for your workflow?',
        'Would a workaround help in the meantime?',
      ],
    },
    
    // Additional instructions for the AI
    additionalInstructions: 'Our users are security professionals, so technical terms are okay.',
  }}
/>
```

### InterviewContext Type

```typescript
interface InterviewContext {
  /** Application/product name (e.g., "Logsign SIEM") */
  appName?: string;
  
  /** Brief description of what the app does */
  appDescription?: string;
  
  /** Feature areas/modules in the app */
  featureAreas?: Array<{
    name: string;
    description?: string;
  }>;
  
  /** Known issues or FAQ items to help detect duplicates */
  knownIssues?: Array<{
    title: string;
    description?: string;
  }>;
  
  /** Domain-specific questions to include in the interview */
  customQuestions?: {
    bug?: string[];      // Extra questions for bug reports
    feedback?: string[]; // Extra questions for feedback
  };
  
  /** Free-form text for any other context the agent should know */
  additionalInstructions?: string;
}
```

### How It Works

1. **User clicks the bug report button** - The modal opens with an "AI Interview Mode" toggle
2. **Interview starts** - The AI greets the user and asks what went wrong (for bugs) or what idea they have (for feedback)
3. **Conversational Q&A** - The AI asks 3-5 clarifying questions to understand the issue
4. **Report generated** - The AI generates a well-structured report with title, description, severity/priority, and reproduction steps
5. **Review and submit** - The user can review and edit the generated report before submitting

### Interview Input Types

The AI can ask for input in three formats:

- **Text** - Open-ended questions (single line or multiline)
- **Choice** - Select from predefined options (e.g., feature areas, frequency)
- **Form** - Multiple fields at once (rarely used)

### Fallback Behavior

- If `OPENROUTER_API_KEY` is not configured, interview mode will show an error and users can switch to the form
- Users can always toggle between "AI Interview" and "Quick Form" modes
- Screenshots and browser diagnostics are still captured automatically in interview mode

---

## AI Analysis

When `OPENROUTER_API_KEY` is configured, the component automatically analyzes submissions:

### Bug Reports

- **Summary** - Concise description of the bug
- **Root Cause Analysis** - Potential underlying causes
- **Reproduction Steps** - Steps to reproduce the issue
- **Suggested Fix** - Recommended approach to resolve
- **Estimated Effort** - Low/Medium/High effort estimate
- **Suggested Severity** - AI-recommended severity level

### Feedback

- **Summary** - Concise description of the feedback
- **Impact Analysis** - What areas/users are affected
- **Action Items** - Concrete next steps
- **Estimated Effort** - Low/Medium/High effort estimate
- **Suggested Priority** - AI-recommended priority level

---

## Email Notifications

When `RESEND_API_KEY` is configured, the component sends email notifications:

### To Reporters

- Thank you email with submission summary
- Professional HTML email template

### To Support Teams

- Full submission details with AI analysis
- Routed based on feedback type or bug severity
- Rich HTML template with all diagnostics

---

## Schema

The component creates these tables:

- `bugReports` - Bug report submissions
- `feedback` - Feedback submissions
- `supportTeams` - Team configuration for notification routing
- `feedbackInputRequests` - Pending user input requests during AI interviews
- `interviewSessions` - Interview state and generated reports
- `ticketCounters` - Sequential counters for ticket numbers
- `apiKeys` - API keys for REST API authentication

---

## TypeScript Types

```typescript
import type {
  BugReport,
  Feedback,
  BugSeverity,
  BugStatus,
  FeedbackType,
  FeedbackPriority,
  FeedbackStatus,
  ApiKey,
  ApiKeyCreated,
  PromptTemplate,
  RegisterFeedbackRoutesOptions,
  InterviewContext,
} from '@fatagnus/convex-feedback';
```

---

## License

Apache-2.0
