react-visual-feedback 1.3.6 → 1.4.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.
package/README.md CHANGED
@@ -1,34 +1,33 @@
1
1
  # React Visual Feedback
2
2
 
3
- A powerful, visual feedback collection tool for React applications with an integrated dashboard for managing user feedback. Users can select any element on your page, and the widget automatically captures a screenshot and context information.
3
+ A powerful, visual feedback collection tool for React applications with screen recording, session replay, and an integrated dashboard for managing user feedback.
4
4
 
5
5
  ## Features
6
6
 
7
7
  ### Feedback Collection
8
- - 🎯 Visual element selection with hover highlighting
9
- - 📸 Automatic screenshot capture with perfect CSS rendering
10
- - 📝 Rich feedback form with context
11
- - Lightweight and performant
12
- - 🎨 Works with any CSS framework (Tailwind, Bootstrap, Material-UI, etc.)
13
- - ⌨️ Keyboard shortcuts (Ctrl+Q to activate, Esc to cancel)
14
- - 🌓 Dark mode support
15
-
16
- ### Feedback Dashboard
17
- - 📊 Professional dashboard with localStorage or custom data source
18
- - 👨‍💻 Developer mode with full technical details
19
- - 👤 User mode for simplified feedback view
20
- - 🏷️ Status management with 7 professional status options
21
- - 💬 Status change modal with developer comments
22
- - 🔒 Permission system - Users can only view, developers can manage
23
- - 🔄 Status change callbacks for database synchronization
24
- - ⌨️ Dashboard keyboard shortcut (Ctrl+Shift+Q)
25
-
26
- ### Update Notifications
27
- - 🔔 Beautiful notification component for feedback updates
28
- - 📬 Show users when their feedback status changes
29
- - 🎨 Grouped by status (Completed, In Progress, Other)
30
- - 👋 Dismiss individual updates or all at once
31
- - ⏰ Smart time formatting (e.g., "2 hours ago", "3 days ago")
8
+ - **Visual Element Selection** - Click any element with hover highlighting
9
+ - **Screenshot Capture** - Automatic pixel-perfect screenshot with CSS rendering
10
+ - **Screen Recording** - Record screen with audio and capture console/network logs
11
+ - **Canvas Drawing** - Annotate screenshots with drawings and highlights
12
+ - **React Component Detection** - Automatically detects React component names and source files
13
+ - **Keyboard Shortcuts** - `Alt+Q` to activate, `Esc` to cancel
14
+
15
+ ### Session Replay
16
+ - **Video Playback** - Watch recorded user sessions
17
+ - **Console Logs** - See console.log, errors, warnings synced with video
18
+ - **Network Requests** - Track API calls and responses
19
+ - **Expandable Logs Panel** - Slide-out panel on the right side (customizable)
20
+
21
+ ### Dashboard
22
+ - **Professional UI** - Clean 700px slide-out panel
23
+ - **Developer Mode** - Full technical details (source file, component stack, viewport)
24
+ - **User Mode** - Simplified view for end users
25
+ - **8 Status Options** - New, Open, In Progress, Under Review, On Hold, Resolved, Closed, Won't Fix
26
+ - **Status Callbacks** - Sync with your database on status changes
27
+
28
+ ### Theming
29
+ - **Light/Dark Mode** - Full theme support
30
+ - **styled-components** - No external CSS required
32
31
 
33
32
  ## Installation
34
33
 
@@ -36,25 +35,22 @@ A powerful, visual feedback collection tool for React applications with an integ
36
35
  npm install react-visual-feedback
37
36
  ```
38
37
 
39
- **Important:** Import the CSS file in your application:
40
-
41
- ```jsx
42
- import 'react-visual-feedback/dist/index.css';
38
+ **Peer Dependencies:**
39
+ ```bash
40
+ npm install react react-dom styled-components
43
41
  ```
44
42
 
45
43
  ## Quick Start
46
44
 
47
- ### Basic Usage (Feedback Only)
45
+ ### Basic Usage
48
46
 
49
47
  ```jsx
50
48
  import React from 'react';
51
49
  import { FeedbackProvider } from 'react-visual-feedback';
52
- import 'react-visual-feedback/dist/index.css';
53
50
 
54
51
  function App() {
55
52
  const handleFeedbackSubmit = async (feedbackData) => {
56
53
  console.log('Feedback received:', feedbackData);
57
- // Send to your backend
58
54
  await fetch('/api/feedback', {
59
55
  method: 'POST',
60
56
  headers: { 'Content-Type': 'application/json' },
@@ -68,27 +64,23 @@ function App() {
68
64
  </FeedbackProvider>
69
65
  );
70
66
  }
71
-
72
- export default App;
73
67
  ```
74
68
 
75
- ### With Dashboard (Full Feature Set)
69
+ ### With Dashboard & Screen Recording
76
70
 
77
71
  ```jsx
78
72
  import React from 'react';
79
73
  import { FeedbackProvider, useFeedback } from 'react-visual-feedback';
80
- import 'react-visual-feedback/dist/index.css';
81
74
 
82
75
  function FeedbackButtons() {
83
- const { isActive, setIsActive, setIsDashboardOpen } = useFeedback();
76
+ const { isActive, setIsActive, setIsDashboardOpen, startRecording } = useFeedback();
84
77
 
85
78
  return (
86
79
  <div style={{ position: 'fixed', bottom: 20, right: 20, display: 'flex', gap: 10 }}>
87
- <button onClick={() => setIsDashboardOpen(true)}>
88
- 📊 Dashboard
89
- </button>
80
+ <button onClick={() => setIsDashboardOpen(true)}>Dashboard</button>
81
+ <button onClick={startRecording}>Record Screen</button>
90
82
  <button onClick={() => setIsActive(!isActive)}>
91
- {isActive ? 'Cancel' : '💬 Report Issue'}
83
+ {isActive ? 'Cancel' : 'Report Issue'}
92
84
  </button>
93
85
  </div>
94
86
  );
@@ -96,6 +88,7 @@ function FeedbackButtons() {
96
88
 
97
89
  function App() {
98
90
  const handleFeedbackSubmit = async (feedbackData) => {
91
+ // feedbackData contains: feedback, screenshot, video, eventLogs, elementInfo, etc.
99
92
  await fetch('/api/feedback', {
100
93
  method: 'POST',
101
94
  headers: { 'Content-Type': 'application/json' },
@@ -104,7 +97,6 @@ function App() {
104
97
  };
105
98
 
106
99
  const handleStatusChange = async ({ id, status, comment }) => {
107
- // Update your database with status and developer comment
108
100
  await fetch(`/api/feedback/${id}/status`, {
109
101
  method: 'PATCH',
110
102
  headers: { 'Content-Type': 'application/json' },
@@ -117,286 +109,477 @@ function App() {
117
109
  onSubmit={handleFeedbackSubmit}
118
110
  onStatusChange={handleStatusChange}
119
111
  dashboard={true}
120
- isDeveloper={true} // Set to false for user mode
112
+ isDeveloper={true}
121
113
  userName="John Doe"
122
114
  userEmail="john@example.com"
115
+ mode="light"
123
116
  >
124
117
  <YourApp />
125
118
  <FeedbackButtons />
126
119
  </FeedbackProvider>
127
120
  );
128
121
  }
129
-
130
- export default App;
131
122
  ```
132
123
 
133
- ### With Update Notifications
134
-
135
- ```jsx
136
- import React, { useState } from 'react';
137
- import {
138
- FeedbackProvider,
139
- FeedbackUpdatesNotification
140
- } from 'react-visual-feedback';
141
- import 'react-visual-feedback/dist/index.css';
142
-
143
- function App() {
144
- const [showNotifications, setShowNotifications] = useState(false);
145
- const [updates, setUpdates] = useState([
146
- {
147
- id: '1',
148
- title: 'Button not working on mobile',
149
- feedback: 'The submit button is not clickable',
150
- status: 'resolved',
151
- responseMessage: 'Fixed! The button now works on all devices.',
152
- resolvedBy: 'John Developer',
153
- updatedAt: '2025-11-02T14:30:00Z'
154
- },
155
- {
156
- id: '2',
157
- title: 'Add dark mode',
158
- feedback: 'Would be great to have dark mode',
159
- status: 'inProgress',
160
- responseMessage: 'Working on it! Should be ready next week.',
161
- assignedTo: 'Sarah Designer',
162
- estimatedResolutionDate: '2025-11-10T00:00:00Z',
163
- updatedAt: '2025-11-01T11:00:00Z'
164
- }
165
- ]);
166
-
167
- const handleDismissUpdate = (updateId) => {
168
- setUpdates(prev => prev.filter(u => u.id !== updateId));
169
- };
170
-
171
- const handleDismissAll = () => {
172
- setUpdates([]);
173
- setShowNotifications(false);
174
- };
175
-
176
- return (
177
- <FeedbackProvider onSubmit={handleFeedbackSubmit}>
178
- <YourApp />
179
-
180
- <button onClick={() => setShowNotifications(true)}>
181
- 🔔 Updates ({updates.length})
182
- </button>
183
-
184
- <FeedbackUpdatesNotification
185
- isOpen={showNotifications}
186
- onClose={() => setShowNotifications(false)}
187
- updates={updates}
188
- onDismissUpdate={handleDismissUpdate}
189
- onDismissAll={handleDismissAll}
190
- />
191
- </FeedbackProvider>
192
- );
193
- }
194
-
195
- export default App;
196
- ```
197
-
198
- ## Status Options
199
-
200
- The dashboard includes 7 professional status options:
201
-
202
- | Status | Color | Description |
203
- |--------|-------|-------------|
204
- | **Reported** 🔴 | Red | Initial feedback submission |
205
- | **Opened** 🟠 | Amber | Acknowledged and under review |
206
- | **In Progress** 🔵 | Blue | Actively being worked on |
207
- | **Resolved** 🟢 | Green | Fixed and ready |
208
- | **Released** 🟣 | Purple | Deployed to production |
209
- | **Blocked** 🔴 | Red | Waiting on dependencies |
210
- | **Won't Fix** ⚪ | Gray | Not planned for implementation |
211
-
212
124
  ## API Reference
213
125
 
214
126
  ### FeedbackProvider Props
215
127
 
216
- | Prop | Type | Required | Default | Description |
217
- |------|------|----------|---------|-------------|
218
- | `onSubmit` | `(feedbackData) => Promise<void>` | Yes | - | Callback when feedback is submitted |
219
- | `onStatusChange` | `({ id, status, comment }) => void` | No | - | Callback when status changes (includes optional developer comment) |
220
- | `children` | `ReactNode` | Yes | - | Your app components |
221
- | `dashboard` | `boolean` | No | `false` | Enable dashboard feature |
222
- | `dashboardData` | `Array` | No | `undefined` | Custom feedback data (uses localStorage if undefined) |
223
- | `isDeveloper` | `boolean` | No | `false` | Enable developer mode with full permissions |
224
- | `isUser` | `boolean` | No | `true` | Enable user mode (read-only) |
225
- | `userName` | `string` | No | `'Anonymous'` | User name for feedback submissions |
226
- | `userEmail` | `string` | No | `null` | User email for feedback submissions |
227
- | `mode` | `'light' \| 'dark'` | No | `'light'` | Theme mode |
128
+ | Prop | Type | Default | Description |
129
+ |------|------|---------|-------------|
130
+ | `onSubmit` | `(data) => Promise` | required | Callback when feedback is submitted |
131
+ | `onStatusChange` | `({id, status, comment}) => void` | - | Callback when status changes |
132
+ | `dashboard` | `boolean` | `false` | Enable dashboard feature |
133
+ | `dashboardData` | `Array` | - | Custom data (uses localStorage if undefined) |
134
+ | `isDeveloper` | `boolean` | `false` | Enable developer mode |
135
+ | `isUser` | `boolean` | `true` | Enable user mode |
136
+ | `userName` | `string` | `'Anonymous'` | User name |
137
+ | `userEmail` | `string` | `null` | User email |
138
+ | `mode` | `'light' \| 'dark'` | `'light'` | Theme mode |
139
+ | `isActive` | `boolean` | - | Controlled active state |
140
+ | `onActiveChange` | `(active) => void` | - | Callback for controlled mode |
228
141
 
229
142
  ### useFeedback Hook
230
143
 
231
144
  ```jsx
232
- const { isActive, setIsActive, setIsDashboardOpen } = useFeedback();
145
+ const {
146
+ isActive, // boolean - feedback mode active
147
+ setIsActive, // (active: boolean) => void
148
+ setIsDashboardOpen, // (open: boolean) => void
149
+ startRecording // () => void - start screen recording
150
+ } = useFeedback();
233
151
  ```
234
152
 
235
- **Returns:**
236
- - `isActive`: `boolean` - Whether feedback mode is active
237
- - `setIsActive`: `(active: boolean) => void` - Activate/deactivate feedback mode
238
- - `setIsDashboardOpen`: `(open: boolean) => void` - Open/close dashboard
153
+ ### SessionReplay Props (for custom implementations)
239
154
 
240
- ### FeedbackUpdatesNotification Props
155
+ ```jsx
156
+ import { SessionReplay } from 'react-visual-feedback';
157
+
158
+ <SessionReplay
159
+ videoSrc={videoDataUrl} // Video source (data URL, blob URL, or http URL)
160
+ eventLogs={logs} // Array of log objects with timestamp
161
+ mode="light" // Theme mode
162
+ showLogsButton={true} // Show/hide logs toggle button
163
+ logsPanelWidth="320px" // Width of logs panel
164
+ defaultLogsOpen={false} // Start with logs panel open
165
+ />
166
+ ```
167
+
168
+ ## Data Structures
241
169
 
242
- | Prop | Type | Required | Default | Description |
243
- |------|------|----------|---------|-------------|
244
- | `isOpen` | `boolean` | Yes | - | Controls notification visibility |
245
- | `onClose` | `() => void` | Yes | - | Callback when notification is closed |
246
- | `updates` | `Array` | Yes | - | Array of feedback updates to display |
247
- | `onDismissUpdate` | `(id: string) => void` | No | - | Callback when a single update is dismissed |
248
- | `onDismissAll` | `() => void` | No | - | Callback when all updates are dismissed |
170
+ ### Feedback Data (submitted via onSubmit)
249
171
 
250
- **Update Object Structure:**
251
172
  ```typescript
252
- {
253
- id: string,
254
- title?: string,
255
- feedback: string,
256
- status: 'reported' | 'opened' | 'inProgress' | 'resolved' | 'released' | 'blocked' | 'wontFix',
257
- responseMessage?: string,
258
- assignedTo?: string,
259
- resolvedBy?: string,
260
- estimatedResolutionDate?: string,
261
- updatedAt: string,
262
- createdAt?: string
263
- }
264
- ```
173
+ interface FeedbackData {
174
+ id: string; // UUID
175
+ feedback: string; // User's feedback text
176
+ type: 'bug' | 'feature' | 'improvement' | 'question' | 'other';
177
+ userName: string;
178
+ userEmail: string | null;
179
+ status: string; // 'new', 'open', 'inProgress', etc.
180
+ timestamp: string; // ISO 8601
181
+ url: string; // Page URL
182
+ userAgent: string;
183
+ viewport: {
184
+ width: number;
185
+ height: number;
186
+ };
265
187
 
266
- ### Feedback Data Structure
188
+ // Screenshot (for element selection)
189
+ screenshot?: string; // Base64 PNG data URL
190
+
191
+ // Video (for screen recording)
192
+ video?: string; // Base64 webm data URL
193
+ eventLogs?: EventLog[]; // Console/network logs
194
+
195
+ // Element info (for element selection)
196
+ elementInfo?: {
197
+ tagName: string;
198
+ id: string;
199
+ className: string;
200
+ selector: string;
201
+ text: string;
202
+ position: { x: number; y: number; width: number; height: number };
203
+ styles: { backgroundColor: string; color: string; fontSize: string };
204
+ sourceFile?: string; // React source file path
205
+ lineNumber?: number;
206
+ columnNumber?: number;
207
+ componentStack?: string[]; // React component hierarchy
208
+ };
209
+ }
267
210
 
268
- ```typescript
269
- {
270
- id: string,
271
- feedback: string,
272
- userName: string,
273
- userEmail: string | null,
274
- status: 'reported' | 'opened' | 'inProgress' | 'resolved' | 'released' | 'blocked' | 'wontFix',
275
- timestamp: string, // ISO 8601 format
276
- url: string,
277
- elementInfo: {
278
- tagName: string,
279
- id: string,
280
- className: string,
281
- selector: string,
282
- text: string,
283
- position: { x: number, y: number, width: number, height: number },
284
- styles: { backgroundColor: string, color: string, fontSize: string, fontFamily: string }
285
- },
286
- screenshot: string, // Base64 encoded PNG
287
- viewport: { width: number, height: number },
288
- userAgent: string
211
+ interface EventLog {
212
+ timestamp: number; // Milliseconds from recording start
213
+ type: 'log' | 'warn' | 'error' | 'info' | 'network';
214
+ message: string;
215
+ data?: any;
289
216
  }
290
217
  ```
291
218
 
292
- ## Keyboard Shortcuts
293
-
294
- | Shortcut | Action |
295
- |----------|--------|
296
- | `Ctrl+Q` | Activate feedback mode |
297
- | `Ctrl+Shift+Q` | Open dashboard (when dashboard is enabled) |
298
- | `Esc` | Cancel/close feedback mode or dashboard |
299
- | `Ctrl+Enter` | Submit feedback (when form is open) |
300
-
301
- ## Developer vs User Mode
302
-
303
- ### Developer Mode (`isDeveloper={true}`)
304
- - View all technical details (element info, CSS, viewport, user agent)
305
- - Change feedback status with optional comments
306
- - ✅ Delete feedback items
307
- - Full control over feedback management
308
-
309
- ### User Mode (`isDeveloper={false}`)
310
- - ✅ View feedback submissions
311
- - See current status and developer responses
312
- - ❌ Cannot change status
313
- - ❌ Cannot delete items
314
-
315
- ## How It Works
316
-
317
- ### Feedback Collection Flow
318
-
319
- 1. User activates the widget (Ctrl+Q or button click)
320
- 2. User hovers over elements to see them highlighted
321
- 3. User clicks on the problematic element
322
- 4. Widget captures a pixel-perfect screenshot
323
- 5. Feedback form appears with context pre-filled
324
- 6. User enters feedback and submits
325
- 7. Your `onSubmit` handler receives all data
326
-
327
- ### Dashboard Flow
328
-
329
- 1. User opens dashboard (Ctrl+Shift+Q or programmatically)
330
- 2. Dashboard displays all feedback submissions
331
- 3. Developer clicks status dropdown and selects new status
332
- 4. Status change modal appears for optional developer comment
333
- 5. Developer adds comment (optional) and confirms
334
- 6. `onStatusChange` callback triggered with `{ id, status, comment }`
335
- 7. Your backend updates the database
336
- 8. Dashboard reflects the new status
337
-
338
- ## Browser Support
219
+ ## Database Schema (SQL)
220
+
221
+ ### PostgreSQL
222
+
223
+ ```sql
224
+ -- Enable UUID extension
225
+ CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
226
+
227
+ -- Main feedback table
228
+ CREATE TABLE feedback (
229
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
230
+ feedback TEXT NOT NULL,
231
+ type VARCHAR(20) DEFAULT 'bug' CHECK (type IN ('bug', 'feature', 'improvement', 'question', 'other')),
232
+ status VARCHAR(20) DEFAULT 'new' CHECK (status IN ('new', 'open', 'inProgress', 'underReview', 'onHold', 'resolved', 'closed', 'wontFix')),
233
+
234
+ -- User info
235
+ user_name VARCHAR(255) DEFAULT 'Anonymous',
236
+ user_email VARCHAR(255),
237
+
238
+ -- Page context
239
+ url TEXT,
240
+ user_agent TEXT,
241
+ viewport_width INTEGER,
242
+ viewport_height INTEGER,
243
+
244
+ -- Timestamps
245
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
246
+ updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
247
+ );
248
+
249
+ -- Screenshots table (separate for large data)
250
+ CREATE TABLE feedback_screenshots (
251
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
252
+ feedback_id UUID NOT NULL REFERENCES feedback(id) ON DELETE CASCADE,
253
+ screenshot TEXT NOT NULL, -- Base64 encoded PNG
254
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
255
+ );
256
+
257
+ -- Videos table (for screen recordings)
258
+ CREATE TABLE feedback_videos (
259
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
260
+ feedback_id UUID NOT NULL REFERENCES feedback(id) ON DELETE CASCADE,
261
+ video_data TEXT, -- Base64 encoded webm (for small videos)
262
+ video_url TEXT, -- URL to cloud storage (for large videos)
263
+ duration_ms INTEGER,
264
+ file_size_bytes BIGINT,
265
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
266
+ );
267
+
268
+ -- Event logs table (console logs, network requests)
269
+ CREATE TABLE feedback_event_logs (
270
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
271
+ feedback_id UUID NOT NULL REFERENCES feedback(id) ON DELETE CASCADE,
272
+ timestamp_ms INTEGER NOT NULL, -- Milliseconds from recording start
273
+ log_type VARCHAR(20) NOT NULL CHECK (log_type IN ('log', 'warn', 'error', 'info', 'network')),
274
+ message TEXT NOT NULL,
275
+ data JSONB, -- Additional log data
276
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
277
+ );
278
+
279
+ -- Element info table (for element selection feedback)
280
+ CREATE TABLE feedback_element_info (
281
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
282
+ feedback_id UUID NOT NULL REFERENCES feedback(id) ON DELETE CASCADE,
283
+ tag_name VARCHAR(50),
284
+ element_id VARCHAR(255),
285
+ class_name TEXT,
286
+ css_selector TEXT,
287
+ inner_text TEXT,
288
+ position_x INTEGER,
289
+ position_y INTEGER,
290
+ width INTEGER,
291
+ height INTEGER,
292
+ background_color VARCHAR(50),
293
+ color VARCHAR(50),
294
+ font_size VARCHAR(20),
295
+
296
+ -- React component info
297
+ source_file TEXT,
298
+ line_number INTEGER,
299
+ column_number INTEGER,
300
+ component_stack TEXT[],
301
+
302
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
303
+ );
304
+
305
+ -- Status history table (track status changes)
306
+ CREATE TABLE feedback_status_history (
307
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
308
+ feedback_id UUID NOT NULL REFERENCES feedback(id) ON DELETE CASCADE,
309
+ old_status VARCHAR(20),
310
+ new_status VARCHAR(20) NOT NULL,
311
+ comment TEXT,
312
+ changed_by VARCHAR(255),
313
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
314
+ );
315
+
316
+ -- Indexes for performance
317
+ CREATE INDEX idx_feedback_status ON feedback(status);
318
+ CREATE INDEX idx_feedback_user_email ON feedback(user_email);
319
+ CREATE INDEX idx_feedback_created_at ON feedback(created_at DESC);
320
+ CREATE INDEX idx_feedback_type ON feedback(type);
321
+ CREATE INDEX idx_event_logs_feedback_id ON feedback_event_logs(feedback_id);
322
+ CREATE INDEX idx_event_logs_timestamp ON feedback_event_logs(timestamp_ms);
323
+ CREATE INDEX idx_status_history_feedback_id ON feedback_status_history(feedback_id);
324
+
325
+ -- Update timestamp trigger
326
+ CREATE OR REPLACE FUNCTION update_updated_at_column()
327
+ RETURNS TRIGGER AS $$
328
+ BEGIN
329
+ NEW.updated_at = CURRENT_TIMESTAMP;
330
+ RETURN NEW;
331
+ END;
332
+ $$ language 'plpgsql';
333
+
334
+ CREATE TRIGGER update_feedback_updated_at
335
+ BEFORE UPDATE ON feedback
336
+ FOR EACH ROW
337
+ EXECUTE FUNCTION update_updated_at_column();
338
+ ```
339
339
 
340
- - ✅ Chrome/Edge
341
- - ✅ Firefox
342
- - ✅ Safari
343
- - Opera
340
+ ### MySQL
341
+
342
+ ```sql
343
+ -- Main feedback table
344
+ CREATE TABLE feedback (
345
+ id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
346
+ feedback TEXT NOT NULL,
347
+ type ENUM('bug', 'feature', 'improvement', 'question', 'other') DEFAULT 'bug',
348
+ status ENUM('new', 'open', 'inProgress', 'underReview', 'onHold', 'resolved', 'closed', 'wontFix') DEFAULT 'new',
349
+
350
+ -- User info
351
+ user_name VARCHAR(255) DEFAULT 'Anonymous',
352
+ user_email VARCHAR(255),
353
+
354
+ -- Page context
355
+ url TEXT,
356
+ user_agent TEXT,
357
+ viewport_width INT,
358
+ viewport_height INT,
359
+
360
+ -- Timestamps
361
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
362
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
363
+
364
+ INDEX idx_status (status),
365
+ INDEX idx_user_email (user_email),
366
+ INDEX idx_created_at (created_at DESC),
367
+ INDEX idx_type (type)
368
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
369
+
370
+ -- Screenshots table
371
+ CREATE TABLE feedback_screenshots (
372
+ id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
373
+ feedback_id CHAR(36) NOT NULL,
374
+ screenshot LONGTEXT NOT NULL,
375
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
376
+
377
+ FOREIGN KEY (feedback_id) REFERENCES feedback(id) ON DELETE CASCADE
378
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
379
+
380
+ -- Videos table
381
+ CREATE TABLE feedback_videos (
382
+ id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
383
+ feedback_id CHAR(36) NOT NULL,
384
+ video_data LONGTEXT,
385
+ video_url TEXT,
386
+ duration_ms INT,
387
+ file_size_bytes BIGINT,
388
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
389
+
390
+ FOREIGN KEY (feedback_id) REFERENCES feedback(id) ON DELETE CASCADE
391
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
392
+
393
+ -- Event logs table
394
+ CREATE TABLE feedback_event_logs (
395
+ id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
396
+ feedback_id CHAR(36) NOT NULL,
397
+ timestamp_ms INT NOT NULL,
398
+ log_type ENUM('log', 'warn', 'error', 'info', 'network') NOT NULL,
399
+ message TEXT NOT NULL,
400
+ data JSON,
401
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
402
+
403
+ FOREIGN KEY (feedback_id) REFERENCES feedback(id) ON DELETE CASCADE,
404
+ INDEX idx_feedback_id (feedback_id),
405
+ INDEX idx_timestamp (timestamp_ms)
406
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
407
+
408
+ -- Element info table
409
+ CREATE TABLE feedback_element_info (
410
+ id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
411
+ feedback_id CHAR(36) NOT NULL,
412
+ tag_name VARCHAR(50),
413
+ element_id VARCHAR(255),
414
+ class_name TEXT,
415
+ css_selector TEXT,
416
+ inner_text TEXT,
417
+ position_x INT,
418
+ position_y INT,
419
+ width INT,
420
+ height INT,
421
+ background_color VARCHAR(50),
422
+ color VARCHAR(50),
423
+ font_size VARCHAR(20),
424
+ source_file TEXT,
425
+ line_number INT,
426
+ column_number INT,
427
+ component_stack JSON,
428
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
429
+
430
+ FOREIGN KEY (feedback_id) REFERENCES feedback(id) ON DELETE CASCADE
431
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
432
+
433
+ -- Status history table
434
+ CREATE TABLE feedback_status_history (
435
+ id CHAR(36) PRIMARY KEY DEFAULT (UUID()),
436
+ feedback_id CHAR(36) NOT NULL,
437
+ old_status VARCHAR(20),
438
+ new_status VARCHAR(20) NOT NULL,
439
+ comment TEXT,
440
+ changed_by VARCHAR(255),
441
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
442
+
443
+ FOREIGN KEY (feedback_id) REFERENCES feedback(id) ON DELETE CASCADE,
444
+ INDEX idx_feedback_id (feedback_id)
445
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
446
+ ```
344
447
 
345
- ## Local Development
448
+ ### Example API Endpoints (Node.js/Express)
449
+
450
+ ```javascript
451
+ // POST /api/feedback - Submit new feedback
452
+ app.post('/api/feedback', async (req, res) => {
453
+ const { feedback, type, userName, userEmail, url, userAgent,
454
+ viewport, screenshot, video, eventLogs, elementInfo } = req.body;
455
+
456
+ // Start transaction
457
+ const client = await pool.connect();
458
+ try {
459
+ await client.query('BEGIN');
460
+
461
+ // Insert main feedback
462
+ const feedbackResult = await client.query(`
463
+ INSERT INTO feedback (feedback, type, user_name, user_email, url, user_agent, viewport_width, viewport_height)
464
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
465
+ RETURNING id
466
+ `, [feedback, type, userName, userEmail, url, userAgent, viewport?.width, viewport?.height]);
467
+
468
+ const feedbackId = feedbackResult.rows[0].id;
469
+
470
+ // Insert screenshot if exists
471
+ if (screenshot) {
472
+ await client.query(`
473
+ INSERT INTO feedback_screenshots (feedback_id, screenshot)
474
+ VALUES ($1, $2)
475
+ `, [feedbackId, screenshot]);
476
+ }
346
477
 
347
- ```bash
348
- # Clone repository
349
- git clone https://github.com/Murali1889/react-feedback-widget.git
350
- cd react-feedback-widget
478
+ // Insert video if exists
479
+ if (video) {
480
+ await client.query(`
481
+ INSERT INTO feedback_videos (feedback_id, video_data)
482
+ VALUES ($1, $2)
483
+ `, [feedbackId, video]);
484
+ }
351
485
 
352
- # Install dependencies
353
- npm install
486
+ // Insert event logs if exist
487
+ if (eventLogs?.length) {
488
+ for (const log of eventLogs) {
489
+ await client.query(`
490
+ INSERT INTO feedback_event_logs (feedback_id, timestamp_ms, log_type, message, data)
491
+ VALUES ($1, $2, $3, $4, $5)
492
+ `, [feedbackId, log.timestamp, log.type, log.message, JSON.stringify(log.data)]);
493
+ }
494
+ }
354
495
 
355
- # Build the widget
356
- npm run build
496
+ // Insert element info if exists
497
+ if (elementInfo) {
498
+ await client.query(`
499
+ INSERT INTO feedback_element_info
500
+ (feedback_id, tag_name, element_id, class_name, css_selector, inner_text,
501
+ position_x, position_y, width, height, source_file, line_number, column_number, component_stack)
502
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
503
+ `, [feedbackId, elementInfo.tagName, elementInfo.id, elementInfo.className,
504
+ elementInfo.selector, elementInfo.text, elementInfo.position?.x, elementInfo.position?.y,
505
+ elementInfo.position?.width, elementInfo.position?.height, elementInfo.sourceFile,
506
+ elementInfo.lineNumber, elementInfo.columnNumber, elementInfo.componentStack]);
507
+ }
357
508
 
358
- # Run example app
359
- cd example
360
- npm install
361
- npm run dev
509
+ await client.query('COMMIT');
510
+ res.json({ success: true, id: feedbackId });
511
+ } catch (error) {
512
+ await client.query('ROLLBACK');
513
+ res.status(500).json({ error: error.message });
514
+ } finally {
515
+ client.release();
516
+ }
517
+ });
518
+
519
+ // PATCH /api/feedback/:id/status - Update status
520
+ app.patch('/api/feedback/:id/status', async (req, res) => {
521
+ const { id } = req.params;
522
+ const { status, comment, changedBy } = req.body;
523
+
524
+ const client = await pool.connect();
525
+ try {
526
+ await client.query('BEGIN');
527
+
528
+ // Get current status
529
+ const current = await client.query('SELECT status FROM feedback WHERE id = $1', [id]);
530
+ const oldStatus = current.rows[0]?.status;
531
+
532
+ // Update status
533
+ await client.query('UPDATE feedback SET status = $1 WHERE id = $2', [status, id]);
534
+
535
+ // Record history
536
+ await client.query(`
537
+ INSERT INTO feedback_status_history (feedback_id, old_status, new_status, comment, changed_by)
538
+ VALUES ($1, $2, $3, $4, $5)
539
+ `, [id, oldStatus, status, comment, changedBy]);
540
+
541
+ await client.query('COMMIT');
542
+ res.json({ success: true });
543
+ } catch (error) {
544
+ await client.query('ROLLBACK');
545
+ res.status(500).json({ error: error.message });
546
+ } finally {
547
+ client.release();
548
+ }
549
+ });
362
550
  ```
363
551
 
364
- Visit `http://localhost:8080` to see the demo!
552
+ ## Keyboard Shortcuts
365
553
 
366
- ## Components Exported
554
+ | Shortcut | Action |
555
+ |----------|--------|
556
+ | `Alt+Q` | Activate feedback mode |
557
+ | `Alt+Shift+Q` | Open dashboard |
558
+ | `Esc` | Cancel/close |
367
559
 
368
- ```jsx
369
- import {
370
- FeedbackProvider, // Main provider component
371
- useFeedback, // Hook to control feedback state
372
- FeedbackUpdatesNotification // Notification component for updates
373
- } from 'react-visual-feedback';
374
- ```
560
+ ## Status Options
375
561
 
376
- ## What's New in v1.3.0
562
+ | Status | Description |
563
+ |--------|-------------|
564
+ | **New** | Initial submission |
565
+ | **Open** | Acknowledged |
566
+ | **In Progress** | Being worked on |
567
+ | **Under Review** | Code review |
568
+ | **On Hold** | Paused |
569
+ | **Resolved** | Fixed |
570
+ | **Closed** | Completed |
571
+ | **Won't Fix** | Not planned |
377
572
 
378
- ### Status Change with Comments
379
- - 💬 Developers can add optional comments when changing status
380
- - 📝 Comments are passed to `onStatusChange` callback
381
- - 👥 Comments visible to users as developer responses
573
+ ## Browser Support
382
574
 
383
- ### Update Notifications Component
384
- - 🔔 New `FeedbackUpdatesNotification` component
385
- - 📬 Show users updates on their feedback
386
- - 🎨 Beautifully grouped by status
387
- - 👋 Dismiss individual or all updates
575
+ - Chrome/Edge (recommended for screen recording)
576
+ - Firefox
577
+ - Safari
578
+ - Opera
388
579
 
389
580
  ## License
390
581
 
391
- MIT © 2025 Murali Vvrsn Gurajapu
392
-
393
- ## Contributing
394
-
395
- Contributions welcome! Please submit a Pull Request.
396
-
397
- ## Issues
398
-
399
- Report issues at: https://github.com/Murali1889/react-feedback-widget/issues
582
+ MIT
400
583
 
401
584
  ## Author
402
585
 
@@ -405,4 +588,4 @@ Email: murali.g@hyperverge.co
405
588
 
406
589
  ---
407
590
 
408
- Made with ❤️ for better user feedback collection
591
+ Made with care for better user feedback collection