@uxbertlabs/reportly 1.0.16 → 1.0.18

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.
@@ -261,6 +261,9 @@ class Modal {
261
261
  <button type="button" class="uxbert-btn uxbert-btn-secondary" id="uxbert-cancel-btn">
262
262
  Cancel
263
263
  </button>
264
+ <button type="button" class="uxbert-btn uxbert-btn-success" id="uxbert-n8n-btn" style="display: none;">
265
+ 🚀 Send to n8n
266
+ </button>
264
267
  <button type="submit" class="uxbert-btn uxbert-btn-primary" id="uxbert-submit-btn">
265
268
  📥 Download JSON
266
269
  </button>
@@ -315,6 +318,33 @@ class Modal {
315
318
  this.callbacks.onRetake();
316
319
  }
317
320
  });
321
+ // n8n button
322
+ const n8nBtn = this.modal.querySelector('#uxbert-n8n-btn');
323
+ n8nBtn.addEventListener('click', () => {
324
+ this.handleN8nSubmit();
325
+ });
326
+ }
327
+ handleN8nSubmit() {
328
+ if (!this.modal)
329
+ return;
330
+ const titleInput = this.modal.querySelector('#uxbert-title');
331
+ const descriptionInput = this.modal.querySelector('#uxbert-description');
332
+ const prioritySelect = this.modal.querySelector('#uxbert-priority');
333
+ const title = titleInput.value;
334
+ const description = descriptionInput.value;
335
+ const priority = prioritySelect.value;
336
+ if (!title.trim()) {
337
+ alert('Please enter an issue title');
338
+ return;
339
+ }
340
+ const issueData = {
341
+ title: title.trim(),
342
+ description: description.trim(),
343
+ priority
344
+ };
345
+ if (this.callbacks.onSendToN8n) {
346
+ this.callbacks.onSendToN8n(issueData);
347
+ }
318
348
  }
319
349
  handleSubmit() {
320
350
  if (!this.modal)
@@ -373,6 +403,14 @@ class Modal {
373
403
  const viewportRadio = this.modal.querySelector('#uxbert-capture-viewport');
374
404
  return viewportRadio.checked ? 'viewport' : 'fullpage';
375
405
  }
406
+ setN8nButtonVisible(visible) {
407
+ if (!this.modal)
408
+ return;
409
+ const n8nBtn = this.modal.querySelector('#uxbert-n8n-btn');
410
+ if (n8nBtn) {
411
+ n8nBtn.style.display = visible ? 'inline-block' : 'none';
412
+ }
413
+ }
376
414
  reset() {
377
415
  if (!this.modal)
378
416
  return;
@@ -9452,8 +9490,6 @@ class Screenshot {
9452
9490
  }
9453
9491
  async capture(mode = "fullpage") {
9454
9492
  try {
9455
- // Show loading screen
9456
- this.showLoadingScreen();
9457
9493
  // Hide UXbert UI elements before capturing
9458
9494
  this.hideUXbertElements();
9459
9495
  const originalScrollY = window.scrollY;
@@ -9472,6 +9508,8 @@ class Screenshot {
9472
9508
  x: window.scrollX,
9473
9509
  y: window.scrollY,
9474
9510
  onclone: async () => {
9511
+ // Show loading screen after clone (won't appear in screenshot)
9512
+ this.showLoadingScreen();
9475
9513
  // Wait 1 second after cloning to let animations complete
9476
9514
  await this.wait(1000);
9477
9515
  },
@@ -9512,6 +9550,10 @@ class Screenshot {
9512
9550
  logging: false,
9513
9551
  width: fullPageWidth,
9514
9552
  height: fullPageHeight,
9553
+ onclone: async () => {
9554
+ // Show loading screen after clone (won't appear in screenshot)
9555
+ this.showLoadingScreen();
9556
+ },
9515
9557
  ignoreElements: (element) => {
9516
9558
  // Skip cross-origin images that might cause issues
9517
9559
  if (element.tagName === "IMG") {
@@ -10155,9 +10197,213 @@ class DeviceInfo {
10155
10197
  }
10156
10198
  }
10157
10199
 
10200
+ class N8nIntegration {
10201
+ constructor(config) {
10202
+ this.config = config || null;
10203
+ }
10204
+ /**
10205
+ * Configure n8n integration
10206
+ */
10207
+ configure(config) {
10208
+ this.config = config;
10209
+ }
10210
+ /**
10211
+ * Check if n8n integration is configured and enabled
10212
+ */
10213
+ isEnabled() {
10214
+ return !!(this.config && this.config.enabled && this.config.webhookUrl);
10215
+ }
10216
+ /**
10217
+ * Send issue data to n8n webhook
10218
+ */
10219
+ async sendToN8n(issueData) {
10220
+ if (!this.config || !this.config.webhookUrl) {
10221
+ return {
10222
+ success: false,
10223
+ message: 'n8n webhook URL is not configured',
10224
+ error: 'MISSING_CONFIG'
10225
+ };
10226
+ }
10227
+ if (!this.config.enabled) {
10228
+ return {
10229
+ success: false,
10230
+ message: 'n8n integration is disabled',
10231
+ error: 'DISABLED'
10232
+ };
10233
+ }
10234
+ try {
10235
+ // Prepare payload
10236
+ const payload = this.preparePayload(issueData);
10237
+ // Setup request headers
10238
+ const headers = {
10239
+ 'Content-Type': 'application/json',
10240
+ ...this.config.headers
10241
+ };
10242
+ // Setup timeout
10243
+ const timeout = this.config.timeout || 30000; // 30 seconds default
10244
+ const controller = new AbortController();
10245
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
10246
+ // Send request
10247
+ const response = await fetch(this.config.webhookUrl, {
10248
+ method: 'POST',
10249
+ headers,
10250
+ body: JSON.stringify(payload),
10251
+ signal: controller.signal
10252
+ });
10253
+ clearTimeout(timeoutId);
10254
+ // Check response
10255
+ if (!response.ok) {
10256
+ const errorText = await response.text().catch(() => 'Unknown error');
10257
+ return {
10258
+ success: false,
10259
+ message: `n8n webhook request failed: ${response.status} ${response.statusText}`,
10260
+ error: errorText
10261
+ };
10262
+ }
10263
+ // Parse response
10264
+ let responseData;
10265
+ try {
10266
+ responseData = await response.json();
10267
+ }
10268
+ catch (e) {
10269
+ // Some webhooks don't return JSON
10270
+ responseData = { message: 'Success' };
10271
+ }
10272
+ return {
10273
+ success: true,
10274
+ message: 'Issue sent to n8n successfully',
10275
+ workflowId: responseData.workflowId || responseData.executionId
10276
+ };
10277
+ }
10278
+ catch (error) {
10279
+ if (error instanceof Error) {
10280
+ if (error.name === 'AbortError') {
10281
+ return {
10282
+ success: false,
10283
+ message: 'Request to n8n webhook timed out',
10284
+ error: 'TIMEOUT'
10285
+ };
10286
+ }
10287
+ return {
10288
+ success: false,
10289
+ message: `Failed to send issue to n8n: ${error.message}`,
10290
+ error: error.message
10291
+ };
10292
+ }
10293
+ return {
10294
+ success: false,
10295
+ message: 'Unknown error occurred while sending to n8n',
10296
+ error: 'UNKNOWN'
10297
+ };
10298
+ }
10299
+ }
10300
+ /**
10301
+ * Prepare payload for n8n webhook
10302
+ * This formats the data in a structure that's easy to work with in n8n
10303
+ */
10304
+ preparePayload(issueData) {
10305
+ return {
10306
+ // Main issue data
10307
+ issue: {
10308
+ title: issueData.title,
10309
+ description: issueData.description,
10310
+ priority: issueData.priority,
10311
+ createdAt: issueData.createdAt
10312
+ },
10313
+ // Screenshot data (base64)
10314
+ screenshot: {
10315
+ data: issueData.screenshot,
10316
+ format: 'base64',
10317
+ mimeType: 'image/png'
10318
+ },
10319
+ // Device and browser information
10320
+ context: {
10321
+ browser: issueData.deviceInfo.browser,
10322
+ os: issueData.deviceInfo.os,
10323
+ screenResolution: issueData.deviceInfo.screenResolution,
10324
+ viewport: issueData.deviceInfo.viewport,
10325
+ url: issueData.deviceInfo.url,
10326
+ userAgent: issueData.deviceInfo.userAgent,
10327
+ language: issueData.deviceInfo.language,
10328
+ platform: issueData.deviceInfo.platform,
10329
+ cookiesEnabled: issueData.deviceInfo.cookiesEnabled,
10330
+ onLine: issueData.deviceInfo.onLine,
10331
+ timestamp: issueData.deviceInfo.timestamp
10332
+ },
10333
+ // Metadata for tracking
10334
+ metadata: {
10335
+ source: 'uxbert-reportly',
10336
+ version: '1.0.0',
10337
+ timestamp: new Date().toISOString()
10338
+ }
10339
+ };
10340
+ }
10341
+ /**
10342
+ * Test connection to n8n webhook
10343
+ */
10344
+ async testConnection() {
10345
+ if (!this.config || !this.config.webhookUrl) {
10346
+ return {
10347
+ success: false,
10348
+ message: 'n8n webhook URL is not configured',
10349
+ error: 'MISSING_CONFIG'
10350
+ };
10351
+ }
10352
+ try {
10353
+ const controller = new AbortController();
10354
+ const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout for test
10355
+ await fetch(this.config.webhookUrl, {
10356
+ method: 'HEAD',
10357
+ signal: controller.signal
10358
+ });
10359
+ clearTimeout(timeoutId);
10360
+ return {
10361
+ success: true,
10362
+ message: 'n8n webhook is reachable'
10363
+ };
10364
+ }
10365
+ catch (error) {
10366
+ return {
10367
+ success: false,
10368
+ message: 'Failed to reach n8n webhook',
10369
+ error: error instanceof Error ? error.message : 'UNKNOWN'
10370
+ };
10371
+ }
10372
+ }
10373
+ /**
10374
+ * Get current configuration
10375
+ */
10376
+ getConfig() {
10377
+ return this.config;
10378
+ }
10379
+ /**
10380
+ * Update webhook URL
10381
+ */
10382
+ setWebhookUrl(url) {
10383
+ if (!this.config) {
10384
+ this.config = {
10385
+ webhookUrl: url,
10386
+ enabled: true
10387
+ };
10388
+ }
10389
+ else {
10390
+ this.config.webhookUrl = url;
10391
+ }
10392
+ }
10393
+ /**
10394
+ * Enable/disable n8n integration
10395
+ */
10396
+ setEnabled(enabled) {
10397
+ if (this.config) {
10398
+ this.config.enabled = enabled;
10399
+ }
10400
+ }
10401
+ }
10402
+
10158
10403
  class Export {
10159
- constructor() {
10404
+ constructor(n8nConfig) {
10160
10405
  this.savedIssues = this.loadSavedIssues();
10406
+ this.n8nIntegration = new N8nIntegration(n8nConfig);
10161
10407
  }
10162
10408
  exportToJSON(issueData) {
10163
10409
  const json = JSON.stringify(issueData, null, 2);
@@ -10226,6 +10472,54 @@ class Export {
10226
10472
  document.body.removeChild(link);
10227
10473
  URL.revokeObjectURL(url);
10228
10474
  }
10475
+ // n8n Integration Methods
10476
+ /**
10477
+ * Send issue to n8n webhook
10478
+ */
10479
+ async sendToN8n(issueData) {
10480
+ const response = await this.n8nIntegration.sendToN8n(issueData);
10481
+ // Save to localStorage if n8n send was successful
10482
+ if (response.success) {
10483
+ this.saveIssue(issueData);
10484
+ }
10485
+ return response;
10486
+ }
10487
+ /**
10488
+ * Configure n8n integration
10489
+ */
10490
+ configureN8n(config) {
10491
+ this.n8nIntegration.configure(config);
10492
+ }
10493
+ /**
10494
+ * Check if n8n is enabled
10495
+ */
10496
+ isN8nEnabled() {
10497
+ return this.n8nIntegration.isEnabled();
10498
+ }
10499
+ /**
10500
+ * Test n8n connection
10501
+ */
10502
+ async testN8nConnection() {
10503
+ return this.n8nIntegration.testConnection();
10504
+ }
10505
+ /**
10506
+ * Get n8n configuration
10507
+ */
10508
+ getN8nConfig() {
10509
+ return this.n8nIntegration.getConfig();
10510
+ }
10511
+ /**
10512
+ * Enable/disable n8n integration
10513
+ */
10514
+ setN8nEnabled(enabled) {
10515
+ this.n8nIntegration.setEnabled(enabled);
10516
+ }
10517
+ /**
10518
+ * Set n8n webhook URL
10519
+ */
10520
+ setN8nWebhookUrl(url) {
10521
+ this.n8nIntegration.setWebhookUrl(url);
10522
+ }
10229
10523
  }
10230
10524
 
10231
10525
  function styleInject(css, ref) {
@@ -10255,7 +10549,7 @@ function styleInject(css, ref) {
10255
10549
  }
10256
10550
  }
10257
10551
 
10258
- var css_248z = ":root{--uxbert-primary:#4f46e5;--uxbert-primary-hover:#4338ca;--uxbert-danger:#ef4444;--uxbert-success:#10b981;--uxbert-bg:#fff;--uxbert-text:#1f2937;--uxbert-border:#e5e7eb;--uxbert-shadow:0 10px 25px rgba(0,0,0,.1);--uxbert-z-button:999999;--uxbert-z-modal:1000000;--uxbert-z-canvas:1000001;--uxbert-z-toolbar:1000002}[data-theme=dark]{--uxbert-bg:#1f2937;--uxbert-text:#f9fafb;--uxbert-border:#374151}.uxbert-fab{align-items:center;background:var(--uxbert-primary);border:none;border-radius:50%;box-shadow:var(--uxbert-shadow);color:#fff;cursor:pointer;display:flex;font-size:24px;height:56px;justify-content:center;position:fixed;transition:all .3s ease;width:56px;z-index:var(--uxbert-z-button)}.uxbert-fab:hover{background:var(--uxbert-primary-hover);transform:scale(1.1)}.uxbert-fab.bottom-right{bottom:24px;right:24px}.uxbert-fab.bottom-left{bottom:24px;left:24px}.uxbert-fab.top-right{right:24px;top:24px}.uxbert-fab.top-left{left:24px;top:24px}.uxbert-overlay{align-items:center;animation:fadeIn .3s ease;background:rgba(0,0,0,.5);display:none;height:100%;justify-content:center;left:0;position:fixed;top:0;width:100%;z-index:var(--uxbert-z-modal)}.uxbert-overlay.active{display:flex}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.uxbert-modal{animation:slideUp .3s ease;background:var(--uxbert-bg);border-radius:12px;box-shadow:var(--uxbert-shadow);color:var(--uxbert-text);max-height:90vh;max-width:600px;overflow-y:auto;padding:24px;width:90%}@keyframes slideUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}.uxbert-modal-header{align-items:center;display:flex;justify-content:space-between;margin-bottom:20px}.uxbert-modal-title{font-size:20px;font-weight:600;margin:0}.uxbert-modal-close{align-items:center;background:none;border:none;border-radius:4px;color:var(--uxbert-text);cursor:pointer;display:flex;font-size:24px;height:32px;justify-content:center;padding:0;width:32px}.uxbert-modal-close:hover{background:var(--uxbert-border)}.uxbert-form-group{margin-bottom:16px}.uxbert-form-label{display:block;font-size:14px;font-weight:500;margin-bottom:8px}.uxbert-capture-mode{display:flex;flex-wrap:wrap;gap:12px}.uxbert-radio-label{align-items:center;border:2px solid var(--uxbert-border);border-radius:8px;cursor:pointer;display:flex;flex:1;gap:8px;min-width:140px;padding:10px 16px;transition:all .2s ease}.uxbert-radio-label:hover{background:rgba(79,70,229,.05);border-color:var(--uxbert-primary)}.uxbert-radio-label input[type=radio]{cursor:pointer;height:18px;margin:0;width:18px}.uxbert-radio-label input[type=radio]:checked{accent-color:var(--uxbert-primary)}.uxbert-radio-label:has(input[type=radio]:checked){background:rgba(79,70,229,.1);border-color:var(--uxbert-primary)}.uxbert-radio-label span{flex:1;font-size:14px;font-weight:500}.uxbert-form-input,.uxbert-form-select,.uxbert-form-textarea{background:var(--uxbert-bg);border:1px solid var(--uxbert-border);border-radius:6px;box-sizing:border-box;color:var(--uxbert-text);font-family:inherit;font-size:14px;padding:10px;width:100%}.uxbert-form-textarea{min-height:100px;resize:vertical}.uxbert-form-input:focus,.uxbert-form-select:focus,.uxbert-form-textarea:focus{border-color:var(--uxbert-primary);outline:none}.uxbert-capture-action{margin:16px 0;text-align:center}.uxbert-capture-action .uxbert-btn{min-width:200px}.uxbert-screenshot-preview{border:1px solid var(--uxbert-border);border-radius:8px;margin:16px 0;overflow:hidden}.uxbert-screenshot-preview img{display:block;width:100%}.uxbert-screenshot-actions{background:var(--uxbert-border);display:flex;gap:8px;padding:12px}.uxbert-btn{align-items:center;border:none;border-radius:6px;cursor:pointer;display:inline-flex;font-size:14px;font-weight:500;gap:8px;padding:10px 20px;transition:all .2s ease}.uxbert-btn-primary{background:var(--uxbert-primary);color:#fff}.uxbert-btn-primary:hover{background:var(--uxbert-primary-hover)}.uxbert-btn-secondary{background:var(--uxbert-border);color:var(--uxbert-text)}.uxbert-btn-secondary:hover{background:#d1d5db}.uxbert-btn-danger{background:var(--uxbert-danger);color:#fff}.uxbert-btn-danger:hover{background:#dc2626}.uxbert-modal-actions{display:flex;gap:12px;justify-content:flex-end;margin-top:20px}.uxbert-toolbar-toggle{align-items:center;background:var(--uxbert-primary);border:none;border-radius:50%;bottom:24px;box-shadow:var(--uxbert-shadow);color:#fff;cursor:pointer;display:none;font-size:24px;height:56px;justify-content:center;position:fixed;right:24px;transition:all .3s ease;width:56px;z-index:var(--uxbert-z-toolbar)}.uxbert-toolbar-toggle.active{display:flex}.uxbert-toolbar-toggle.hidden{opacity:0;pointer-events:none;transform:scale(.8)}.uxbert-toolbar-toggle:hover{background:var(--uxbert-primary-hover);transform:scale(1.1)}.uxbert-toolbar{background:var(--uxbert-bg);border-radius:12px;bottom:24px;box-shadow:var(--uxbert-shadow);display:none;flex-direction:column;gap:8px;max-height:80vh;opacity:0;overflow-y:auto;padding:16px;pointer-events:none;position:fixed;right:24px;transform:translateY(20px);transition:all .3s ease;z-index:var(--uxbert-z-toolbar)}.uxbert-toolbar.active{display:flex}.uxbert-toolbar.expanded{opacity:1;pointer-events:all;transform:translateY(0)}.uxbert-toolbar-header{align-items:center;display:flex;gap:8px;justify-content:space-between;margin-bottom:8px}.uxbert-toolbar-title{flex:1;font-size:14px;font-weight:600}.uxbert-toolbar-exit,.uxbert-toolbar-minimize{align-items:center;background:var(--uxbert-border);border:none;border-radius:6px;color:var(--uxbert-text);cursor:pointer;display:flex;font-size:20px;font-weight:700;height:32px;justify-content:center;transition:all .2s ease;width:32px}.uxbert-toolbar-exit{background:var(--uxbert-danger);color:#fff}.uxbert-toolbar-minimize:hover{background:#d1d5db;transform:scale(1.1)}.uxbert-toolbar-exit:hover{background:#dc2626;transform:scale(1.1)}.uxbert-toolbar-tools{display:flex;flex-wrap:wrap;gap:8px}.uxbert-tool-btn{align-items:center;background:var(--uxbert-bg);border:2px solid var(--uxbert-border);border-radius:6px;cursor:pointer;display:flex;font-family:Arial,sans-serif;font-size:18px;font-weight:700;height:40px;justify-content:center;transition:all .2s ease;width:40px}.uxbert-tool-btn.active,.uxbert-tool-btn:hover{background:var(--uxbert-primary);border-color:var(--uxbert-primary);color:#fff}.uxbert-color-picker{display:flex;gap:6px;margin:8px 0}.uxbert-color-option{border:2px solid var(--uxbert-border);border-radius:50%;cursor:pointer;height:32px;transition:transform .2s ease;width:32px}.uxbert-color-option:hover{transform:scale(1.1)}.uxbert-color-option.active{border:3px solid var(--uxbert-text)}.uxbert-canvas-overlay{cursor:crosshair;display:none;left:0;position:absolute;top:0;z-index:var(--uxbert-z-canvas)}.uxbert-canvas-overlay.active{display:block}.uxbert-canvas-overlay.viewport-mode{left:0;position:fixed;top:0}.uxbert-text-input{background:hsla(0,0%,100%,.95);border:2px solid var(--uxbert-primary);border-radius:6px;box-shadow:0 4px 12px rgba(0,0,0,.15);color:var(--uxbert-text);font-family:Arial,sans-serif;font-size:16px;font-weight:700;min-width:200px;outline:none;padding:8px 12px;z-index:var(--uxbert-z-toolbar)}.uxbert-text-input:focus{border-color:var(--uxbert-primary-hover);box-shadow:0 4px 16px rgba(79,70,229,.3)}.uxbert-text-input::placeholder{color:#9ca3af;font-weight:400}@media (max-width:768px){.uxbert-modal{max-height:95vh;padding:16px;width:95%}.uxbert-toolbar{bottom:90px;left:16px;max-height:60vh;right:16px;width:auto}.uxbert-toolbar-toggle{bottom:16px;height:56px;right:16px;width:56px}.uxbert-fab{font-size:20px;height:48px;width:48px}.uxbert-color-picker,.uxbert-toolbar-tools{justify-content:center}}";
10552
+ var css_248z = ":root{--uxbert-primary:#4f46e5;--uxbert-primary-hover:#4338ca;--uxbert-danger:#ef4444;--uxbert-success:#10b981;--uxbert-bg:#fff;--uxbert-text:#1f2937;--uxbert-border:#e5e7eb;--uxbert-shadow:0 10px 25px rgba(0,0,0,.1);--uxbert-z-button:999999;--uxbert-z-modal:1000000;--uxbert-z-canvas:1000001;--uxbert-z-toolbar:1000002}[data-theme=dark]{--uxbert-bg:#1f2937;--uxbert-text:#f9fafb;--uxbert-border:#374151}.uxbert-fab{align-items:center;background:var(--uxbert-primary);border:none;border-radius:50%;box-shadow:var(--uxbert-shadow);color:#fff;cursor:pointer;display:flex;font-size:24px;height:56px;justify-content:center;position:fixed;transition:all .3s ease;width:56px;z-index:var(--uxbert-z-button)}.uxbert-fab:hover{background:var(--uxbert-primary-hover);transform:scale(1.1)}.uxbert-fab.bottom-right{bottom:24px;right:24px}.uxbert-fab.bottom-left{bottom:24px;left:24px}.uxbert-fab.top-right{right:24px;top:24px}.uxbert-fab.top-left{left:24px;top:24px}.uxbert-overlay{align-items:center;animation:fadeIn .3s ease;background:rgba(0,0,0,.5);display:none;height:100%;justify-content:center;left:0;position:fixed;top:0;width:100%;z-index:var(--uxbert-z-modal)}.uxbert-overlay.active{display:flex}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.uxbert-modal{animation:slideUp .3s ease;background:var(--uxbert-bg);border-radius:12px;box-shadow:var(--uxbert-shadow);color:var(--uxbert-text);max-height:90vh;max-width:600px;overflow-y:auto;padding:24px;width:90%}@keyframes slideUp{0%{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}.uxbert-modal-header{align-items:center;display:flex;justify-content:space-between;margin-bottom:20px}.uxbert-modal-title{font-size:20px;font-weight:600;margin:0}.uxbert-modal-close{align-items:center;background:none;border:none;border-radius:4px;color:var(--uxbert-text);cursor:pointer;display:flex;font-size:24px;height:32px;justify-content:center;padding:0;width:32px}.uxbert-modal-close:hover{background:var(--uxbert-border)}.uxbert-form-group{margin-bottom:16px}.uxbert-form-label{display:block;font-size:14px;font-weight:500;margin-bottom:8px}.uxbert-capture-mode{display:flex;flex-wrap:wrap;gap:12px}.uxbert-radio-label{align-items:center;border:2px solid var(--uxbert-border);border-radius:8px;cursor:pointer;display:flex;flex:1;gap:8px;min-width:140px;padding:10px 16px;transition:all .2s ease}.uxbert-radio-label:hover{background:rgba(79,70,229,.05);border-color:var(--uxbert-primary)}.uxbert-radio-label input[type=radio]{cursor:pointer;height:18px;margin:0;width:18px}.uxbert-radio-label input[type=radio]:checked{accent-color:var(--uxbert-primary)}.uxbert-radio-label:has(input[type=radio]:checked){background:rgba(79,70,229,.1);border-color:var(--uxbert-primary)}.uxbert-radio-label span{flex:1;font-size:14px;font-weight:500}.uxbert-form-input,.uxbert-form-select,.uxbert-form-textarea{background:var(--uxbert-bg);border:1px solid var(--uxbert-border);border-radius:6px;box-sizing:border-box;color:var(--uxbert-text);font-family:inherit;font-size:14px;padding:10px;width:100%}.uxbert-form-textarea{min-height:100px;resize:vertical}.uxbert-form-input:focus,.uxbert-form-select:focus,.uxbert-form-textarea:focus{border-color:var(--uxbert-primary);outline:none}.uxbert-capture-action{margin:16px 0;text-align:center}.uxbert-capture-action .uxbert-btn{min-width:200px}.uxbert-screenshot-preview{border:1px solid var(--uxbert-border);border-radius:8px;margin:16px 0;overflow:hidden}.uxbert-screenshot-preview img{display:block;width:100%}.uxbert-screenshot-actions{background:var(--uxbert-border);display:flex;gap:8px;padding:12px}.uxbert-btn{align-items:center;border:none;border-radius:6px;cursor:pointer;display:inline-flex;font-size:14px;font-weight:500;gap:8px;padding:10px 20px;transition:all .2s ease}.uxbert-btn-primary{background:var(--uxbert-primary);color:#fff}.uxbert-btn-primary:hover{background:var(--uxbert-primary-hover)}.uxbert-btn-secondary{background:var(--uxbert-border);color:var(--uxbert-text)}.uxbert-btn-secondary:hover{background:#d1d5db}.uxbert-btn-success{background:var(--uxbert-success);color:#fff}.uxbert-btn-success:hover{background:#059669}.uxbert-btn-danger{background:var(--uxbert-danger);color:#fff}.uxbert-btn-danger:hover{background:#dc2626}.uxbert-modal-actions{display:flex;gap:12px;justify-content:flex-end;margin-top:20px}.uxbert-toolbar-toggle{align-items:center;background:var(--uxbert-primary);border:none;border-radius:50%;bottom:24px;box-shadow:var(--uxbert-shadow);color:#fff;cursor:pointer;display:none;font-size:24px;height:56px;justify-content:center;position:fixed;right:24px;transition:all .3s ease;width:56px;z-index:var(--uxbert-z-toolbar)}.uxbert-toolbar-toggle.active{display:flex}.uxbert-toolbar-toggle.hidden{opacity:0;pointer-events:none;transform:scale(.8)}.uxbert-toolbar-toggle:hover{background:var(--uxbert-primary-hover);transform:scale(1.1)}.uxbert-toolbar{background:var(--uxbert-bg);border-radius:12px;bottom:24px;box-shadow:var(--uxbert-shadow);display:none;flex-direction:column;gap:8px;max-height:80vh;opacity:0;overflow-y:auto;padding:16px;pointer-events:none;position:fixed;right:24px;transform:translateY(20px);transition:all .3s ease;z-index:var(--uxbert-z-toolbar)}.uxbert-toolbar.active{display:flex}.uxbert-toolbar.expanded{opacity:1;pointer-events:all;transform:translateY(0)}.uxbert-toolbar-header{align-items:center;display:flex;gap:8px;justify-content:space-between;margin-bottom:8px}.uxbert-toolbar-title{flex:1;font-size:14px;font-weight:600}.uxbert-toolbar-exit,.uxbert-toolbar-minimize{align-items:center;background:var(--uxbert-border);border:none;border-radius:6px;color:var(--uxbert-text);cursor:pointer;display:flex;font-size:20px;font-weight:700;height:32px;justify-content:center;transition:all .2s ease;width:32px}.uxbert-toolbar-exit{background:var(--uxbert-danger);color:#fff}.uxbert-toolbar-minimize:hover{background:#d1d5db;transform:scale(1.1)}.uxbert-toolbar-exit:hover{background:#dc2626;transform:scale(1.1)}.uxbert-toolbar-tools{display:flex;flex-wrap:wrap;gap:8px}.uxbert-tool-btn{align-items:center;background:var(--uxbert-bg);border:2px solid var(--uxbert-border);border-radius:6px;cursor:pointer;display:flex;font-family:Arial,sans-serif;font-size:18px;font-weight:700;height:40px;justify-content:center;transition:all .2s ease;width:40px}.uxbert-tool-btn.active,.uxbert-tool-btn:hover{background:var(--uxbert-primary);border-color:var(--uxbert-primary);color:#fff}.uxbert-color-picker{display:flex;gap:6px;margin:8px 0}.uxbert-color-option{border:2px solid var(--uxbert-border);border-radius:50%;cursor:pointer;height:32px;transition:transform .2s ease;width:32px}.uxbert-color-option:hover{transform:scale(1.1)}.uxbert-color-option.active{border:3px solid var(--uxbert-text)}.uxbert-canvas-overlay{cursor:crosshair;display:none;left:0;position:absolute;top:0;z-index:var(--uxbert-z-canvas)}.uxbert-canvas-overlay.active{display:block}.uxbert-canvas-overlay.viewport-mode{left:0;position:fixed;top:0}.uxbert-text-input{background:hsla(0,0%,100%,.95);border:2px solid var(--uxbert-primary);border-radius:6px;box-shadow:0 4px 12px rgba(0,0,0,.15);color:var(--uxbert-text);font-family:Arial,sans-serif;font-size:16px;font-weight:700;min-width:200px;outline:none;padding:8px 12px;z-index:var(--uxbert-z-toolbar)}.uxbert-text-input:focus{border-color:var(--uxbert-primary-hover);box-shadow:0 4px 16px rgba(79,70,229,.3)}.uxbert-text-input::placeholder{color:#9ca3af;font-weight:400}@media (max-width:768px){.uxbert-modal{max-height:95vh;padding:16px;width:95%}.uxbert-toolbar{bottom:90px;left:16px;max-height:60vh;right:16px;width:auto}.uxbert-toolbar-toggle{bottom:16px;height:56px;right:16px;width:56px}.uxbert-fab{font-size:20px;height:48px;width:48px}.uxbert-color-picker,.uxbert-toolbar-tools{justify-content:center}}";
10259
10553
  styleInject(css_248z);
10260
10554
 
10261
10555
  // Main initialization file
@@ -10287,7 +10581,9 @@ class Reportly {
10287
10581
  this.state = new State();
10288
10582
  this.deviceInfo = new DeviceInfo();
10289
10583
  this.screenshot = new Screenshot();
10290
- this.export = new Export();
10584
+ // Initialize export with n8n config if provided
10585
+ const n8nConfig = userConfig.integrations?.n8n;
10586
+ this.export = new Export(n8nConfig);
10291
10587
  // Initialize UI components
10292
10588
  this.button = new Button(this.config, {
10293
10589
  onClick: () => this.handleButtonClick(),
@@ -10298,6 +10594,7 @@ class Reportly {
10298
10594
  onAnnotate: () => this.startAnnotation(),
10299
10595
  onRetake: () => this.retakeScreenshot(),
10300
10596
  onCapture: () => this.captureWithMode(),
10597
+ onSendToN8n: (issueData) => this.handleN8nSubmit(issueData),
10301
10598
  });
10302
10599
  this.toolbar = new Toolbar({
10303
10600
  onToolChange: (tool) => this.annotation?.setTool(tool),
@@ -10313,6 +10610,10 @@ class Reportly {
10313
10610
  this.modal.create();
10314
10611
  this.toolbar.create();
10315
10612
  this.annotation.createCanvas();
10613
+ // Show/hide n8n button based on configuration
10614
+ if (this.export.isN8nEnabled()) {
10615
+ this.modal.setN8nButtonVisible(true);
10616
+ }
10316
10617
  // Setup keyboard shortcuts
10317
10618
  this.setupKeyboardShortcuts();
10318
10619
  this.isInitialized = true;
@@ -10440,6 +10741,54 @@ class Reportly {
10440
10741
  alert("Failed to export issue. Please try again.");
10441
10742
  }
10442
10743
  }
10744
+ async handleN8nSubmit(issueData) {
10745
+ try {
10746
+ // Create complete issue package
10747
+ const completeIssue = {
10748
+ ...issueData,
10749
+ screenshot: this.state?.getScreenshot() || "",
10750
+ deviceInfo: this.deviceInfo.get(),
10751
+ createdAt: new Date().toISOString(),
10752
+ };
10753
+ // Show loading state
10754
+ const n8nBtn = document.querySelector('#uxbert-n8n-btn');
10755
+ if (n8nBtn) {
10756
+ n8nBtn.disabled = true;
10757
+ n8nBtn.textContent = '⏳ Sending...';
10758
+ }
10759
+ // Send to n8n
10760
+ const response = await this.export?.sendToN8n(completeIssue);
10761
+ if (response?.success) {
10762
+ // Emit event
10763
+ this.state?.emit("issue:sent-to-n8n", completeIssue);
10764
+ // Reset state
10765
+ this.state?.reset();
10766
+ this.annotation?.clear();
10767
+ this.modal?.close();
10768
+ alert("Issue sent to n8n successfully!");
10769
+ console.log("Issue sent to n8n successfully");
10770
+ }
10771
+ else {
10772
+ alert(`Failed to send to n8n: ${response?.message || 'Unknown error'}`);
10773
+ console.error("Failed to send to n8n:", response?.error);
10774
+ }
10775
+ // Reset button state
10776
+ if (n8nBtn) {
10777
+ n8nBtn.disabled = false;
10778
+ n8nBtn.textContent = '🚀 Send to n8n';
10779
+ }
10780
+ }
10781
+ catch (error) {
10782
+ console.error("Failed to send to n8n:", error);
10783
+ alert("Failed to send to n8n. Please try again.");
10784
+ // Reset button state
10785
+ const n8nBtn = document.querySelector('#uxbert-n8n-btn');
10786
+ if (n8nBtn) {
10787
+ n8nBtn.disabled = false;
10788
+ n8nBtn.textContent = '🚀 Send to n8n';
10789
+ }
10790
+ }
10791
+ }
10443
10792
  handleModalClose() {
10444
10793
  this.annotation?.hide();
10445
10794
  this.toolbar?.hide();
@@ -10468,6 +10817,24 @@ class Reportly {
10468
10817
  exportAllIssues() {
10469
10818
  this.export?.exportAllIssues();
10470
10819
  }
10820
+ // n8n Integration API methods
10821
+ configureN8n(webhookUrl, enabled = true, options) {
10822
+ this.export?.configureN8n({
10823
+ webhookUrl,
10824
+ enabled,
10825
+ headers: options?.headers,
10826
+ timeout: options?.timeout
10827
+ });
10828
+ // Update button visibility
10829
+ this.modal?.setN8nButtonVisible(enabled);
10830
+ }
10831
+ enableN8n(enabled) {
10832
+ this.export?.setN8nEnabled(enabled);
10833
+ this.modal?.setN8nButtonVisible(enabled);
10834
+ }
10835
+ testN8nConnection() {
10836
+ return this.export?.testN8nConnection() || Promise.resolve({ success: false, message: 'Not initialized' });
10837
+ }
10471
10838
  on(event, callback) {
10472
10839
  this.state?.on(event, callback);
10473
10840
  }