# Support Widgets

A unified system of support widgets designed to capture structured user feedback, issues, and resolution data. These widgets are built for seamless integration with AI agents, enabling world-class automated support.

## Overview

The support widget system provides five specialized widgets, each capturing different types of user input with consistent patterns for:

- **User attribution** - Identify who submitted (optional)
- **App context** - Custom data like conversationId, featureName, pageId
- **Auto-captured metadata** - URL, timestamp, viewport, userAgent

All widgets share a common architecture:
- Display as `popover` or `modal` variants
- Submit via callback (`onSubmit`) or API endpoint
- Auto-capture metadata (enabled by default)
- Consistent success/error state handling

## Component Summary

| Widget | Purpose | Key Data |
|--------|---------|----------|
| **Feedback** | General satisfaction feedback | Sentiment (5-point scale) + optional message |
| **Contact** | Sales/support inquiries | Name, email, topic, message |
| **BugReport** | Structured bug reports | Title, expected vs actual behavior, severity, reproduction steps |
| **ErrorReport** | Runtime error reporting | Description, severity, error details (message/stack/code) |
| **Resolution** | Post-support satisfaction | Resolution status, satisfaction rating, follow-up request |

---

## Feedback Widget

Quick sentiment capture for measuring user satisfaction at specific touchpoints.

### When to Use

- After AI chat responses
- After completing a workflow
- On specific feature interactions
- General product feedback collection

### Data Captured

```typescript
interface FeedbackData {
  sentiment: 'very-dissatisfied' | 'dissatisfied' | 'neutral' | 'satisfied' | 'very-satisfied'
  message?: string                    // Optional elaboration
  user?: FeedbackUser                 // { id, email, name }
  context?: Record<string, unknown>   // App-specific context
  metadata?: FeedbackMetadata         // Auto-captured browser/session info
}
```

### Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `variant` | `'popover' \| 'modal'` | `'popover'` | Display style |
| `triggerLabel` | `string` | (icon only) | Button text |
| `title` | `string` | `'Give us feedback'` | Dialog title |
| `user` | `FeedbackUser` | - | User attribution |
| `context` | `Record<string, unknown>` | - | App context |
| `captureMetadata` | `boolean` | `true` | Auto-capture metadata |
| `onSubmit` | `(data: FeedbackData) => Promise<void> \| void` | - | Submit callback |
| `apiEndpoint` | `string` | - | POST endpoint |
| `placeholder` | `string` | `'Write your feedback (optional)'` | Textarea placeholder |
| `submitLabel` | `string` | `'Submit'` | Button text |

### Example Usage

```tsx
import { Feedback } from '@mdxui/widgets/support'

// Basic usage
<Feedback onSubmit={(data) => console.log(data)} />

// With user context for AI chat
<Feedback
  user={{ id: 'user-123', email: 'user@example.com' }}
  context={{ conversationId: 'conv-456', messageId: 'msg-789' }}
  title="Rate this response"
  onSubmit={async (data) => {
    await fetch('/api/feedback', {
      method: 'POST',
      body: JSON.stringify(data)
    })
  }}
/>
```

---

## Contact Widget

Configurable contact form for sales inquiries, support requests, and partnerships.

### When to Use

- Website contact pages
- Support request initiation
- Sales inquiry forms
- Partnership inquiries

### Data Captured

```typescript
interface ContactData {
  name?: string                       // User's name
  email?: string                      // User's email
  topic?: string                      // Selected topic category
  message: string                     // Main message content
  user?: ContactUser                  // Authenticated user info
  context?: Record<string, unknown>   // App-specific context
  metadata?: ContactMetadata          // Auto-captured metadata
}
```

### Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `variant` | `'popover' \| 'modal'` | `'modal'` | Display style |
| `showName` | `boolean` | `true` | Show name field |
| `showEmail` | `boolean` | `true` | Show email field |
| `showTopic` | `boolean` | `true` | Show topic dropdown |
| `showMessage` | `boolean` | `true` | Show message field |
| `requireName` | `boolean` | `true` | Require name |
| `requireEmail` | `boolean` | `true` | Require email |
| `requireTopic` | `boolean` | `false` | Require topic |
| `requireMessage` | `boolean` | `true` | Require message |
| `topics` | `string[]` | `['Sales', 'Support', 'Partnership', 'Other']` | Topic options |
| `user` | `ContactUser` | - | Pre-fill from user data |
| `context` | `Record<string, unknown>` | - | App context |
| `captureMetadata` | `boolean` | `true` | Auto-capture metadata |
| `onSubmit` | `(data: ContactData) => Promise<void> \| void` | - | Submit callback |
| `apiEndpoint` | `string` | - | POST endpoint |

### Example Usage

```tsx
import { Contact } from '@mdxui/widgets/support'

// Basic usage
<Contact onSubmit={(data) => console.log(data)} />

// Custom topics for SaaS product
<Contact
  topics={['Technical Support', 'Billing', 'Feature Request', 'Enterprise Sales']}
  user={currentUser}
  context={{ plan: 'pro', accountAge: 90 }}
  onSubmit={handleContactSubmit}
/>

// Simplified form (message only)
<Contact
  showName={false}
  showEmail={false}
  showTopic={false}
  requireMessage={true}
  onSubmit={handleQuickMessage}
/>
```

---

## BugReport Widget

Structured bug reporting with expected/actual behavior, reproduction steps, and severity.

### When to Use

- In-app bug reporting
- QA feedback collection
- Beta tester feedback
- Developer tools

### Data Captured

```typescript
interface BugReportData {
  title: string                       // Brief bug summary
  expected: string                    // Expected behavior
  actual: string                      // Actual behavior
  stepsToReproduce?: string           // Reproduction steps
  severity: 'cosmetic' | 'minor' | 'major' | 'critical'
  workedBefore?: boolean              // Regression indicator
  user?: BugReportUser                // User attribution
  context?: Record<string, unknown>   // App context
  metadata?: BugReportMetadata        // Auto-captured metadata
}
```

### Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `variant` | `'popover' \| 'modal'` | `'modal'` | Display style |
| `triggerLabel` | `string` | `'Report Bug'` | Button text |
| `title` | `string` | `'Report a Bug'` | Dialog title |
| `showStepsToReproduce` | `boolean` | `true` | Show steps field |
| `showWorkedBefore` | `boolean` | `true` | Show regression toggle |
| `user` | `BugReportUser` | - | User attribution |
| `context` | `Record<string, unknown>` | - | App context |
| `captureMetadata` | `boolean` | `true` | Auto-capture metadata |
| `onSubmit` | `(data: BugReportData) => Promise<void> \| void` | - | Submit callback |
| `apiEndpoint` | `string` | - | POST endpoint |

### Example Usage

```tsx
import { BugReport } from '@mdxui/widgets/support'

// Full bug report form
<BugReport
  user={currentUser}
  context={{ featureName: 'data-export', pageId: 'dashboard' }}
  onSubmit={async (data) => {
    await createGitHubIssue(data)
  }}
/>

// Minimal form (no steps/regression)
<BugReport
  showStepsToReproduce={false}
  showWorkedBefore={false}
  onSubmit={handleBugReport}
/>
```

---

## ErrorReport Widget

Runtime error reporting with optional pre-populated error details.

### When to Use

- Error boundary fallback UIs
- Catch-all error handlers
- Failed operation reporting
- API error feedback

### Data Captured

```typescript
interface ErrorReportData {
  description: string                 // User description of what happened
  severity: 'low' | 'medium' | 'high' | 'critical'
  errorDetails?: ErrorDetails         // { message, stack, code }
  user?: ErrorReportUser              // User attribution
  context?: Record<string, unknown>   // App context
  metadata?: ErrorReportMetadata      // Extended metadata (see below)
}

// ErrorReportMetadata includes additional fields:
interface ErrorReportMetadata {
  url: string
  timestamp: string
  viewport: { width: number; height: number }
  userAgent: string
  consoleErrors?: Array<{ message: string; timestamp: string }>
  failedRequests?: Array<{ url: string; status: number; timestamp: string }>
}
```

### Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `variant` | `'popover' \| 'modal'` | `'modal'` | Display style |
| `triggerLabel` | `string` | `'Report Error'` | Button text |
| `title` | `string` | `'Report an Error'` | Dialog title |
| `showSeverity` | `boolean` | `true` | Show severity dropdown |
| `errorDetails` | `ErrorDetails` | - | Pre-populated error info |
| `user` | `ErrorReportUser` | - | User attribution |
| `context` | `Record<string, unknown>` | - | App context |
| `captureMetadata` | `boolean` | `true` | Auto-capture metadata |
| `onSubmit` | `(data: ErrorReportData) => Promise<void> \| void` | - | Submit callback |
| `apiEndpoint` | `string` | - | POST endpoint |

### Example Usage

```tsx
import { ErrorReport } from '@mdxui/widgets/support'

// In an error boundary
function ErrorFallback({ error }) {
  return (
    <div>
      <h2>Something went wrong</h2>
      <ErrorReport
        errorDetails={{
          message: error.message,
          stack: error.stack,
          code: error.code
        }}
        user={currentUser}
        context={{ componentStack: error.componentStack }}
        onSubmit={reportErrorToSentry}
      />
    </div>
  )
}

// API error reporting
<ErrorReport
  errorDetails={{
    message: 'Request failed',
    code: 'API_TIMEOUT'
  }}
  context={{ endpoint: '/api/users', method: 'GET' }}
  onSubmit={handleErrorReport}
/>
```

---

## Resolution Widget

Post-support satisfaction collection to close the feedback loop.

### When to Use

- After support ticket resolution
- Post-chat support surveys
- Follow-up satisfaction checks
- Support quality measurement

### Data Captured

```typescript
interface ResolutionData {
  resolved: 'yes' | 'no' | 'partially'
  satisfaction: 1 | 2 | 3 | 4 | 5     // 5-point scale
  feedback?: string                   // Improvement suggestions
  stillNeedsHelp?: string             // What's still wrong (if not resolved)
  wantsFollowUp?: boolean             // Request follow-up contact
  ticketId?: string                   // Reference to original ticket
  user?: ResolutionUser               // User attribution
  context?: Record<string, unknown>   // App context
  metadata?: ResolutionMetadata       // Auto-captured metadata
}

// ResolutionMetadata includes:
interface ResolutionMetadata {
  url: string
  timestamp: string
  viewport: { width: number; height: number }
  userAgent: string
  timeToResolution?: number           // Ms from ticket creation to resolution
}
```

### Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `variant` | `'popover' \| 'modal'` | `'modal'` | Display style |
| `triggerLabel` | `string` | `'Rate Support'` | Button text |
| `title` | `string` | `'How did we do?'` | Dialog title |
| `ticketId` | `string` | - | Reference to original ticket |
| `showFollowUp` | `boolean` | `true` | Show follow-up checkbox |
| `user` | `ResolutionUser` | - | User attribution |
| `context` | `Record<string, unknown>` | - | App context |
| `captureMetadata` | `boolean` | `true` | Auto-capture metadata |
| `onSubmit` | `(data: ResolutionData) => Promise<void> \| void` | - | Submit callback |
| `apiEndpoint` | `string` | - | POST endpoint |

### Example Usage

```tsx
import { Resolution } from '@mdxui/widgets/support'

// After support ticket resolution
<Resolution
  ticketId="TICKET-12345"
  user={currentUser}
  context={{ supportAgent: 'agent-456', channel: 'chat' }}
  onSubmit={async (data) => {
    await updateTicketWithResolution(data.ticketId, data)
  }}
/>

// Simplified (no follow-up option)
<Resolution
  showFollowUp={false}
  onSubmit={handleResolutionFeedback}
/>
```

---

## Data Schema Reference

### User Input Fields

| Category | Field | Type | Widget(s) |
|----------|-------|------|-----------|
| **Sentiment** | `sentiment` | `'very-dissatisfied' \| 'dissatisfied' \| 'neutral' \| 'satisfied' \| 'very-satisfied'` | Feedback |
| **Satisfaction** | `satisfaction` | `1 \| 2 \| 3 \| 4 \| 5` | Resolution |
| **Resolution** | `resolved` | `'yes' \| 'no' \| 'partially'` | Resolution |
| **Severity** | `severity` | `'low' \| 'medium' \| 'high' \| 'critical'` | ErrorReport |
| **Bug Severity** | `severity` | `'cosmetic' \| 'minor' \| 'major' \| 'critical'` | BugReport |
| **Text** | `message` | `string` | Feedback, Contact |
| **Text** | `description` | `string` | ErrorReport |
| **Text** | `title` | `string` | BugReport |
| **Text** | `expected` | `string` | BugReport |
| **Text** | `actual` | `string` | BugReport |
| **Text** | `stepsToReproduce` | `string?` | BugReport |
| **Text** | `feedback` | `string?` | Resolution |
| **Text** | `stillNeedsHelp` | `string?` | Resolution |
| **Selection** | `topic` | `string?` | Contact |
| **Boolean** | `workedBefore` | `boolean?` | BugReport |
| **Boolean** | `wantsFollowUp` | `boolean?` | Resolution |

### User Context

| Field | Type | Description |
|-------|------|-------------|
| `user.id` | `string?` | Unique user identifier |
| `user.email` | `string?` | User email address |
| `user.name` | `string?` | User display name |

### App Context

The `context` field accepts any `Record<string, unknown>`. Common patterns:

| Field | Example Value | Purpose |
|-------|---------------|---------|
| `conversationId` | `'conv-456'` | Link to AI chat session |
| `messageId` | `'msg-789'` | Link to specific message |
| `featureName` | `'data-export'` | Feature being used |
| `pageId` | `'dashboard'` | Current page/route |
| `plan` | `'pro'` | User subscription tier |
| `accountAge` | `90` | Days since signup |
| `sessionId` | `'sess-123'` | Browser session ID |
| `componentStack` | `'...'` | React component tree (errors) |

### Auto-Captured Metadata

All widgets capture (when `captureMetadata: true`):

| Field | Type | Description |
|-------|------|-------------|
| `url` | `string` | `window.location.href` |
| `timestamp` | `string` | ISO 8601 timestamp |
| `viewport.width` | `number` | Browser width |
| `viewport.height` | `number` | Browser height |
| `userAgent` | `string` | Browser user agent |

**ErrorReport additional fields:**

| Field | Type | Description |
|-------|------|-------------|
| `consoleErrors` | `Array<{message, timestamp}>` | Recent console errors |
| `failedRequests` | `Array<{url, status, timestamp}>` | Failed HTTP requests |

**Resolution additional fields:**

| Field | Type | Description |
|-------|------|-------------|
| `timeToResolution` | `number?` | Ms from ticket creation to resolution |

---

## Agent Integration Guide

This section describes how AI agents can process support widget data to provide automated, world-class support.

### Data Correlation Patterns

#### 1. User Identity Correlation

```typescript
// Correlate submissions from the same user
const userSubmissions = await db.query(`
  SELECT * FROM support_submissions
  WHERE user_id = $1
  OR user_email = $2
  ORDER BY created_at DESC
`, [data.user?.id, data.user?.email])

// Build user support history
const supportHistory = {
  totalSubmissions: userSubmissions.length,
  averageSentiment: calculateAverageSentiment(userSubmissions),
  commonIssues: extractCommonTopics(userSubmissions),
  lastContactedAt: userSubmissions[0]?.metadata?.timestamp
}
```

#### 2. Context Correlation

```typescript
// Find related issues by context
const relatedIssues = await db.query(`
  SELECT * FROM bug_reports
  WHERE context->>'featureName' = $1
  AND created_at > NOW() - INTERVAL '7 days'
`, [data.context?.featureName])

// Detect patterns
if (relatedIssues.length > 5) {
  await escalateToEngineering({
    feature: data.context?.featureName,
    reportCount: relatedIssues.length,
    samples: relatedIssues.slice(0, 3)
  })
}
```

#### 3. Session Correlation

```typescript
// Correlate user journey across submissions
const sessionData = {
  pagesVisited: extractUrlPath(data.metadata?.url),
  viewport: data.metadata?.viewport,
  browserInfo: parseUserAgent(data.metadata?.userAgent),
  timestamp: new Date(data.metadata?.timestamp)
}
```

### Triage and Routing Logic

#### By Widget Type

```typescript
async function triageSubmission(type: string, data: any) {
  switch (type) {
    case 'feedback':
      return triageFeedback(data)
    case 'contact':
      return triageContact(data)
    case 'bug_report':
      return triageBugReport(data)
    case 'error_report':
      return triageErrorReport(data)
    case 'resolution':
      return processResolution(data)
  }
}
```

#### Feedback Triage

```typescript
async function triageFeedback(data: FeedbackData) {
  const priority = getSentimentPriority(data.sentiment)

  // Very dissatisfied + message = high priority
  if (data.sentiment === 'very-dissatisfied' && data.message) {
    return {
      action: 'IMMEDIATE_RESPONSE',
      priority: 'high',
      assignTo: 'customer-success',
      reason: 'Negative feedback with details'
    }
  }

  // Positive feedback = testimonial candidate
  if (data.sentiment === 'very-satisfied' && data.message?.length > 50) {
    return {
      action: 'TESTIMONIAL_CANDIDATE',
      priority: 'low',
      assignTo: 'marketing',
      reason: 'Potential testimonial'
    }
  }

  return { action: 'LOG', priority: 'low' }
}

function getSentimentPriority(sentiment: Sentiment): 'high' | 'medium' | 'low' {
  const map = {
    'very-dissatisfied': 'high',
    'dissatisfied': 'medium',
    'neutral': 'low',
    'satisfied': 'low',
    'very-satisfied': 'low'
  }
  return map[sentiment]
}
```

#### Contact Triage

```typescript
async function triageContact(data: ContactData) {
  const topic = data.topic?.toLowerCase()

  // Route by topic
  const routing = {
    'sales': { team: 'sales', priority: 'high', sla: '1h' },
    'enterprise sales': { team: 'enterprise', priority: 'high', sla: '30m' },
    'support': { team: 'support', priority: 'medium', sla: '4h' },
    'technical support': { team: 'engineering', priority: 'medium', sla: '4h' },
    'billing': { team: 'billing', priority: 'high', sla: '2h' },
    'partnership': { team: 'partnerships', priority: 'medium', sla: '24h' },
    'feature request': { team: 'product', priority: 'low', sla: '48h' }
  }

  return routing[topic] || { team: 'support', priority: 'medium', sla: '4h' }
}
```

#### Bug Report Triage

```typescript
async function triageBugReport(data: BugReportData) {
  // Severity-based routing
  const severityRouting = {
    'critical': { priority: 'P0', team: 'engineering', sla: '1h' },
    'major': { priority: 'P1', team: 'engineering', sla: '4h' },
    'minor': { priority: 'P2', team: 'engineering', sla: '24h' },
    'cosmetic': { priority: 'P3', team: 'engineering', sla: '1w' }
  }

  let triage = severityRouting[data.severity]

  // Escalate regressions
  if (data.workedBefore) {
    triage = { ...triage, priority: 'P0', reason: 'Regression' }
  }

  // Auto-deduplicate similar bugs
  const similar = await findSimilarBugs(data.title, data.expected, data.actual)
  if (similar.length > 0) {
    triage = { ...triage, duplicateOf: similar[0].id }
  }

  return triage
}
```

#### Error Report Triage

```typescript
async function triageErrorReport(data: ErrorReportData) {
  // Check for known errors
  if (data.errorDetails?.code) {
    const knownError = await lookupErrorCode(data.errorDetails.code)
    if (knownError) {
      return {
        action: 'AUTO_RESPOND',
        template: knownError.responseTemplate,
        priority: 'low'
      }
    }
  }

  // Severity-based routing
  const severityRouting = {
    'critical': { priority: 'P0', team: 'on-call', sla: '15m' },
    'high': { priority: 'P1', team: 'engineering', sla: '1h' },
    'medium': { priority: 'P2', team: 'engineering', sla: '4h' },
    'low': { priority: 'P3', team: 'engineering', sla: '24h' }
  }

  return severityRouting[data.severity]
}
```

### Example Agent Workflows

#### Workflow 1: Negative Feedback Response

```typescript
async function handleNegativeFeedback(data: FeedbackData) {
  // Step 1: Acknowledge immediately
  if (data.user?.email) {
    await sendEmail({
      to: data.user.email,
      template: 'feedback-acknowledgment',
      vars: { name: data.user.name || 'there' }
    })
  }

  // Step 2: Analyze context
  const context = await enrichContext(data)

  // Step 3: Generate response with AI
  const response = await generateResponse({
    sentiment: data.sentiment,
    message: data.message,
    userHistory: context.userHistory,
    featureContext: context.featureContext
  })

  // Step 4: Create support ticket
  const ticket = await createTicket({
    type: 'feedback',
    priority: 'high',
    subject: `Negative feedback from ${data.user?.email || 'anonymous user'}`,
    body: data.message,
    metadata: data
  })

  // Step 5: Notify team
  await notifySlack({
    channel: '#customer-feedback',
    message: `New negative feedback: ${data.message?.slice(0, 100)}...`,
    ticketUrl: ticket.url
  })

  return { ticket, response }
}
```

#### Workflow 2: Bug Report Processing

```typescript
async function handleBugReport(data: BugReportData) {
  // Step 1: Deduplicate
  const duplicates = await findDuplicateBugs(data)
  if (duplicates.length > 0) {
    await linkToExistingBug(duplicates[0], data)
    return { action: 'linked_to_existing', bugId: duplicates[0].id }
  }

  // Step 2: Enrich with system data
  const enrichedData = {
    ...data,
    systemInfo: parseUserAgent(data.metadata?.userAgent),
    pageRoute: new URL(data.metadata?.url).pathname,
    screenshotRequest: data.severity === 'critical'
  }

  // Step 3: Create GitHub issue (if critical/major)
  if (['critical', 'major'].includes(data.severity)) {
    const issue = await createGitHubIssue({
      title: data.title,
      body: formatBugReportForGitHub(enrichedData),
      labels: [data.severity, data.workedBefore ? 'regression' : 'new']
    })
  }

  // Step 4: Acknowledge to user
  if (data.user?.email) {
    await sendEmail({
      to: data.user.email,
      template: 'bug-report-received',
      vars: {
        title: data.title,
        severity: data.severity,
        trackingId: issue?.number
      }
    })
  }

  return { action: 'created', issue }
}
```

#### Workflow 3: Resolution Follow-up

```typescript
async function handleResolution(data: ResolutionData) {
  // Step 1: Update ticket
  if (data.ticketId) {
    await updateTicket(data.ticketId, {
      resolution: data.resolved,
      satisfaction: data.satisfaction,
      closedAt: new Date()
    })
  }

  // Step 2: Handle unresolved issues
  if (data.resolved === 'no' || data.resolved === 'partially') {
    // Reopen or escalate
    await escalateTicket(data.ticketId, {
      reason: data.stillNeedsHelp,
      priority: 'high'
    })

    if (data.wantsFollowUp && data.user?.email) {
      await scheduleFollowUp({
        email: data.user.email,
        ticketId: data.ticketId,
        delay: '1h'
      })
    }
  }

  // Step 3: Calculate and report metrics
  const metrics = {
    resolutionRate: data.resolved === 'yes' ? 1 : 0,
    satisfaction: data.satisfaction,
    timeToResolution: data.metadata?.timeToResolution
  }

  await reportMetrics('support', metrics)

  // Step 4: Trigger feedback loop
  if (data.satisfaction <= 2) {
    await notifyManager({
      type: 'low_satisfaction',
      ticketId: data.ticketId,
      satisfaction: data.satisfaction,
      feedback: data.feedback
    })
  }

  return { metrics }
}
```

### Multi-Widget Data Correlation

Combine data from multiple widgets for comprehensive user context:

```typescript
async function buildUserSupportProfile(userId: string) {
  // Fetch all submissions
  const [feedback, contacts, bugs, errors, resolutions] = await Promise.all([
    getFeedbackByUser(userId),
    getContactsByUser(userId),
    getBugReportsByUser(userId),
    getErrorReportsByUser(userId),
    getResolutionsByUser(userId)
  ])

  // Calculate health score
  const avgSentiment = calculateAverageSentiment(feedback)
  const avgSatisfaction = calculateAverage(resolutions.map(r => r.satisfaction))
  const resolutionRate = resolutions.filter(r => r.resolved === 'yes').length / resolutions.length
  const bugFrequency = bugs.length / daysSinceSignup(userId)

  const healthScore = (
    (avgSentiment * 20) +        // 0-100 from sentiment
    (avgSatisfaction * 20) +     // 0-100 from satisfaction
    (resolutionRate * 30) +      // 0-30 from resolution rate
    (Math.max(0, 30 - bugFrequency * 10)) // Penalty for high bug frequency
  )

  return {
    userId,
    healthScore,
    totalSubmissions: feedback.length + contacts.length + bugs.length + errors.length,
    sentiment: { average: avgSentiment, trend: calculateTrend(feedback) },
    satisfaction: { average: avgSatisfaction, trend: calculateTrend(resolutions) },
    openIssues: bugs.filter(b => b.status !== 'closed').length,
    commonFeatures: extractCommonFeatures([...feedback, ...bugs, ...errors]),
    riskLevel: healthScore < 50 ? 'high' : healthScore < 70 ? 'medium' : 'low'
  }
}
```

---

## Implementation Patterns

### Pattern 1: Global Feedback Button

```tsx
// components/GlobalFeedback.tsx
import { Feedback } from '@mdxui/widgets/support'
import { useUser } from '@/hooks/useUser'
import { usePathname } from 'next/navigation'

export function GlobalFeedback() {
  const user = useUser()
  const pathname = usePathname()

  return (
    <div className="fixed bottom-4 right-4 z-50">
      <Feedback
        user={user ? { id: user.id, email: user.email, name: user.name } : undefined}
        context={{ page: pathname }}
        apiEndpoint="/api/feedback"
      />
    </div>
  )
}
```

### Pattern 2: Error Boundary Integration

```tsx
// components/ErrorBoundary.tsx
import { ErrorReport } from '@mdxui/widgets/support'
import { Component } from 'react'

class ErrorBoundary extends Component {
  state = { hasError: false, error: null }

  static getDerivedStateFromError(error) {
    return { hasError: true, error }
  }

  render() {
    if (this.state.hasError) {
      return (
        <div className="error-fallback">
          <h2>Something went wrong</h2>
          <p>We've been notified, but you can help by providing more details.</p>
          <ErrorReport
            errorDetails={{
              message: this.state.error?.message,
              stack: this.state.error?.stack
            }}
            onSubmit={async (data) => {
              await fetch('/api/errors', {
                method: 'POST',
                body: JSON.stringify(data)
              })
            }}
          />
        </div>
      )
    }

    return this.props.children
  }
}
```

### Pattern 3: Post-Chat Resolution

```tsx
// components/ChatResolution.tsx
import { Resolution } from '@mdxui/widgets/support'

export function ChatResolution({ conversationId, userId }) {
  return (
    <Resolution
      ticketId={conversationId}
      user={{ id: userId }}
      context={{ channel: 'chat', conversationType: 'support' }}
      onSubmit={async (data) => {
        await fetch('/api/chat/resolution', {
          method: 'POST',
          body: JSON.stringify({
            ...data,
            conversationId
          })
        })
      }}
    />
  )
}
```

### Pattern 4: Contextual Bug Report

```tsx
// components/FeatureBugReport.tsx
import { BugReport } from '@mdxui/widgets/support'

export function FeatureBugReport({ featureName, version }) {
  return (
    <BugReport
      triggerLabel="Report Issue"
      context={{
        featureName,
        featureVersion: version,
        releaseDate: new Date().toISOString()
      }}
      onSubmit={async (data) => {
        // Create Jira issue
        const response = await fetch('/api/jira/issues', {
          method: 'POST',
          body: JSON.stringify({
            project: 'PROD',
            issueType: 'Bug',
            summary: data.title,
            description: formatJiraDescription(data),
            labels: [featureName, data.severity]
          })
        })
        return response.json()
      }}
    />
  )
}
```

---

## Best Practices

1. **Always provide user context** when available - this enables correlation and personalized responses
2. **Use meaningful context fields** - include feature names, conversation IDs, and relevant identifiers
3. **Enable metadata capture** (default) - browser info helps debug issues
4. **Connect Resolution to tickets** - pass `ticketId` to close the feedback loop
5. **Handle errors gracefully** - all widgets support error callbacks
6. **Use API endpoints for production** - callbacks are great for development, but endpoints enable server-side processing
