Skip to content

Feedback Control Layer

The feedback control layer provides a comprehensive, user-friendly feedback collection system for DCC-BS Nuxt applications. It enables users to submit feedback directly from your application, with support for ratings, file attachments, and seamless integration with GitHub issues.

Overview

The feedback control layer includes:

  1. FeedbackControl Component - A ready-to-use Vue component for feedback collection
  2. API Integration - Automatically creates GitHub issues from user feedback
  3. File Upload Support - Allows users to attach files (screenshots, logs, etc.)
  4. Rating System - Emoji-based rating for quick sentiment analysis
  5. Form Validation - Built-in validation using Zod schemas
  6. Internationalization - Full i18n support for multi-language applications
  7. Responsive Design - Mobile-friendly interface with smooth animations

How It Works

The feedback control layer provides a floating feedback button that opens a comprehensive feedback form. When users submit feedback:

  1. User opens feedback → Clicks the floating button
  2. Fills in the form → Selects rating, writes message, provides email, adds attachments
  3. Form validation → Client-side validation ensures data integrity
  4. Submission to API → Form data sent to /api/feedback endpoint
  5. GitHub integration → Feedback is automatically converted to a GitHub issue
  6. Success feedback → User sees confirmation with animation
  7. Form reset → Form resets after 3 seconds, ready for next feedback

Architecture

Your Nuxt App

  FeedbackControl Component

  /api/feedback endpoint

  GitHub API

  GitHub Issue created

Quick Start

1. Add Feedback Control Layer to Your Project

In your nuxt.config.ts:

typescript
export default defineNuxtConfig({
  extends: [
    ['github:DCC-BS/nuxt-layers/feedback-control', { install: true }]
  ]
})

2. Configure GitHub Integration

** Local ** Set the required environment variables in your .env file:

VariableRequiredDescriptionExample
FEEDBACK_REPOYesName of the GitHub repository for feedback issuesFeedback
FEEDBACK_REPO_OWNERYesGitHub organization or username that owns the repositoryDCC-BS
FEEDBACK_PROJECTYesGitHub project name for organizationTest
FEEDBACK_GITHUB_TOKENYesPersonal access token with content and issues read & write permissionsghp_xxxxxxxxxxxx

TIP

To set the env variables at runtime prefix the variables with NUXT_ for example NUXT_FEEDBACK_REPO

GitHub Token Setup

To create a GitHub token for the feedback control:

  1. Go to SettingsDeveloper settingsPersonal access tokensFine-grained tokens
  2. Click Generate new token
  3. Give it a descriptive name (e.g., "Feedback Control")
  4. Select only repositories and add you repository
  5. Select the following permissions:
    • contents (read and write content)
    • issues (read and write issues)
  6. Click Generate token
  7. Copy the token immediately (you won't see it again)

** Production ** To set the variables at runtime and not build time you need to prefix the variables with NUXT_:

bash
NUXT_FEEDBACK_REPO=Feedback # Repository name where the issue will be created
NUXT_FEEDBACK_REPO_OWNER=DCC-BS # Repository owner name where the issue will be created
NUXT_FEEDBACK_PROJECT=Test # Project name used in the title of the issue
NUXT_GITHUB_TOKEN=your_github_token # GitHub token with read & wirte access to issues and content

the variables are set in the runtime config of nuxt:

ts
runtimeConfig: {
        feedback: {
            repo: process.env.FEEDBACK_REPO,
            repoOwner: process.env.FEEDBACK_REPO_OWNER,
            project: process.env.FEEDBACK_PROJECT,
            githubToken: process.env.GITHUB_TOKEN,
        },
    },

3. Use Feedback Control in Your App

Add the FeedbackControl component to your layout or a specific page:

vue
<script setup lang="ts">
// No setup needed - just use the component
</script>

<template>
  <div>
    <!-- Your application content -->
    
    <!-- Feedback button appears automatically -->
    <FeedbackControl />
  </div>
</template>

Component Props

The FeedbackControl component accepts the following optional props:

PropTypeDefaultDescription
defaultMailstring""Pre-fill the email address field
inlinebooleanfalseRemoves fixed positioning to allow placement inline with other elements (e.g., in a footer)

Example:

vue
<template>
  <FeedbackControl defaultMail="user@example.com" />
</template>

Component Features

Rating System

The feedback control includes a 5-level emoji-based rating system:

EmojiValueDescription
😕poorVery unsatisfied
😐okaySomewhat satisfied
🙂goodSatisfied
😀greatVery satisfied
🤩excellentExtremely satisfied

Users click on the emoji to select their rating, providing quick sentiment analysis for your team.

Form Fields

The feedback form includes:

  1. Rating Selection (Required)

    • Five emoji buttons for quick rating selection
    • Visual feedback when selected (highlighted border and background)
  2. Message (Required)

    • Multi-line text area for detailed feedback
    • Placeholder text guides users
    • Minimum validation to ensure meaningful feedback
  3. Email (Optional)

    • Email input for follow-up contact
    • Email format validation
    • Optional: users can leave blank for anonymous feedback
  4. File Attachments (Optional)

    • Upload up to 2 files
    • Maximum file size: 75MB per file
    • Drag-and-drop support
    • File type validation

Validation

The feedback control uses Zod for comprehensive form validation:

  • Rating: Must be selected (one of the 5 values)
  • Message: Required, trimmed to ensure non-empty content
  • Email: Valid email format if provided
  • Attachments:
    • Maximum 2 files
    • Maximum 75MB per file
    • File size validation before upload

API Endpoint

The feedback control layer creates a server API endpoint at /api/feedback that processes form submissions.

Request Format

POST /api/feedback

typescript
interface FeedbackBody {
  rating: 'poor' | 'okay' | 'good' | 'great' | 'excellent';
  message: string;
  email?: string;
  attachments: FeedbackAttachment[];
}

interface FeedbackAttachment {
  base64: string;
  fileName: string;
}

GitHub Issue Format

The API creates a GitHub issue with the following structure:

Title:

[Rating] Feedback from user@example.com

Body:

markdown
## Feedback Details

- **Rating**: 😊 good
- **Email**: user@example.com

## Message

This is the feedback message provided by the user.

## Attachments

- [screenshot.png](https://github.com/DCC-BS/Feedback/assets/...)

Styling and Customization

The feedback control uses Nuxt UI components for consistent styling:

Built-in Styles

The component uses these Nuxt UI components:

  • UButton - Submit button and close button
  • UPopover - Dropdown container
  • UTextarea - Message input
  • UInput - Email input
  • UAlert - Error messages
  • UFileUpload - File upload component

Custom Styling

You can customize the appearance using CSS overrides:

vue
<template>
  <div>
    <FeedbackControl />
  </div>
</template>

<style>
/* Customize feedback button */
#feedback-control button {
  /* Your custom styles */
}

/* Customize form container */
#feedback-control .p-2 {
  /* Your custom styles */
}
</style>

Position Customization

By default, the feedback button is fixed at bottom-4 right-4 (16px from bottom and right). You can customize the position in two ways:

1. Using the inline Prop

To place the component inline with other elements (e.g., in a footer or a grid), set the inline prop to true. This removes the fixed positioning entirely, allowing standard layout flow to dictate placement.

vue
<template>
  <footer class="flex justify-between p-4">
    <p>&copy; 2024 My App</p>
    <!-- Button sits inline in the footer -->
    <FeedbackControl :inline="true" />
  </footer>
</template>

2. Using CSS Overrides

If you need to keep the fixed positioning but change the coordinates, use CSS overrides:

css
#feedback-control {
  bottom: 32px !important;
  right: 32px !important;
}

Internationalization

The feedback control layer includes full i18n support with translations for all user-facing text.

Available Translations

The component includes translations for:

  • Button labels
  • Form field labels
  • Placeholder text
  • Help text
  • Error messages
  • Success messages
  • Accessibility labels (aria-labels)

Adding Custom Translations

To add or customize translations, create an i18n configuration file:

typescript
// nuxt.config.ts
export default defineNuxtConfig({
  i18n: {
    locales: [
      { code: 'en', iso: 'en-US', file: 'en.json', name: 'English' },
      { code: 'de', iso: 'de-DE', file: 'de.json', name: 'Deutsch' }
    ],
    defaultLocale: 'en'
  }
})

Add translations for the feedback namespace:

json
// locales/en.json
{
  "feedback": {
    "button": "Feedback",
    "title": "Submit Feedback",
    "rating_label": "How was your experience?",
    "ratings": {
      "poor": "Poor",
      "okay": "Okay",
      "good": "Good",
      "great": "Great",
      "excellent": "Excellent"
    },
    "message_label": "Your feedback",
    "message_placeholder": "Tell us what you think...",
    "email_label": "Email (optional)",
    "email_placeholder": "your@email.com",
    "email_help": "Leave blank for anonymous feedback",
    "attachments_label": "Attachments",
    "attachments_help": "Add up to {maxFiles} files, max {maxSize} each",
    "submit": "Submit Feedback",
    "submitting": "Submitting...",
    "success_title": "Thank you!",
    "success_message": "Your feedback has been submitted successfully.",
    "close": "Close",
    "aria_label": "Open feedback form"
  }
}

Examples

Basic Usage

vue
<template>
  <div>
    <h1>My Application</h1>
    <p>Welcome to the app!</p>
    
    <!-- Feedback control appears automatically -->
    <FeedbackControl />
  </div>
</template>

Pre-fill Email

vue
<script setup lang="ts">
import { useAppAuth } from '#layers/auth/app/composables/useAuth';

const { data } = useAppAuth();
const userEmail = computed(() => data?.user?.email || '');
</script>

<template>
  <div>
    <!-- Pre-fill with authenticated user's email -->
    <FeedbackControl :defaultMail="userEmail" />
  </div>
</template>

Inline Usage

vue
<template>
  <div class="flex items-center gap-4">
    <span>Need help? Give us feedback.</span>
    <!-- Component is placed inline, not fixed -->
    <FeedbackControl :inline="true" />
  </div>
</template>

Developed with ❤️ by the DCC. Documentation released under the MIT License.