@uxbertlabs/reportly 1.0.18 → 1.0.19
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/dist/features/n8n-integration.d.ts +8 -4
- package/dist/features/n8n-integration.d.ts.map +1 -1
- package/dist/reportly.cjs.js +140 -123
- package/dist/reportly.cjs.js.map +1 -1
- package/dist/reportly.esm.js +140 -123
- package/dist/reportly.esm.js.map +1 -1
- package/dist/reportly.js +140 -123
- package/dist/reportly.js.map +1 -1
- package/dist/reportly.min.js +2 -2
- package/dist/reportly.min.js.map +1 -1
- package/dist/ui/modal.d.ts +3 -3
- package/dist/ui/modal.d.ts.map +1 -1
- package/package.json +1 -1
@@ -1,4 +1,4 @@
|
|
1
|
-
import type { CompleteIssue } from
|
1
|
+
import type { CompleteIssue } from "../types";
|
2
2
|
export interface N8nConfig {
|
3
3
|
webhookUrl: string;
|
4
4
|
enabled: boolean;
|
@@ -27,10 +27,14 @@ declare class N8nIntegration {
|
|
27
27
|
*/
|
28
28
|
sendToN8n(issueData: CompleteIssue): Promise<N8nResponse>;
|
29
29
|
/**
|
30
|
-
*
|
31
|
-
* This formats the data in a structure that's easy to work with in n8n
|
30
|
+
* Convert base64 data URL to Blob
|
32
31
|
*/
|
33
|
-
private
|
32
|
+
private base64ToBlob;
|
33
|
+
/**
|
34
|
+
* Prepare FormData payload for n8n webhook
|
35
|
+
* This sends the screenshot as a binary file instead of base64 JSON
|
36
|
+
*/
|
37
|
+
private prepareFormData;
|
34
38
|
/**
|
35
39
|
* Test connection to n8n webhook
|
36
40
|
*/
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"n8n-integration.d.ts","sourceRoot":"","sources":["../../src/features/n8n-integration.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,cAAM,cAAc;IAClB,OAAO,CAAC,MAAM,CAAmB;gBAErB,MAAM,CAAC,EAAE,SAAS;IAI9B;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAIlC;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACG,SAAS,CAAC,SAAS,EAAE,aAAa,GAAG,OAAO,CAAC,WAAW,CAAC;
|
1
|
+
{"version":3,"file":"n8n-integration.d.ts","sourceRoot":"","sources":["../../src/features/n8n-integration.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,cAAM,cAAc;IAClB,OAAO,CAAC,MAAM,CAAmB;gBAErB,MAAM,CAAC,EAAE,SAAS;IAI9B;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAIlC;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACG,SAAS,CAAC,SAAS,EAAE,aAAa,GAAG,OAAO,CAAC,WAAW,CAAC;IA0F/D;;OAEG;IACH,OAAO,CAAC,YAAY;IAsBpB;;;OAGG;YACW,eAAe;IAkC7B;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,WAAW,CAAC;IAiC5C;;OAEG;IACH,SAAS,IAAI,SAAS,GAAG,IAAI;IAI7B;;OAEG;IACH,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAWhC;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;CAKnC;AAED,eAAe,cAAc,CAAC"}
|
package/dist/reportly.cjs.js
CHANGED
@@ -184,11 +184,11 @@ class Modal {
|
|
184
184
|
}
|
185
185
|
create() {
|
186
186
|
// Create overlay
|
187
|
-
this.overlay = document.createElement(
|
188
|
-
this.overlay.className =
|
187
|
+
this.overlay = document.createElement("div");
|
188
|
+
this.overlay.className = "uxbert-overlay";
|
189
189
|
// Create modal
|
190
|
-
this.modal = document.createElement(
|
191
|
-
this.modal.className =
|
190
|
+
this.modal = document.createElement("div");
|
191
|
+
this.modal.className = "uxbert-modal";
|
192
192
|
// Modal HTML
|
193
193
|
this.modal.innerHTML = `
|
194
194
|
<div class="uxbert-modal-header">
|
@@ -233,12 +233,13 @@ class Modal {
|
|
233
233
|
|
234
234
|
<div class="uxbert-form-group">
|
235
235
|
<label class="uxbert-form-label" for="uxbert-priority">Priority</label>
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
</
|
236
|
+
<select id="uxbert-priority" class="uxbert-form-select">
|
237
|
+
<option value="5" selected>Lowest</option>
|
238
|
+
<option value="4">Low</option>
|
239
|
+
<option value="3" selected>Medium</option>
|
240
|
+
<option value="2">High</option>
|
241
|
+
<option value="1">Highest</option>
|
242
|
+
</select>
|
242
243
|
</div>
|
243
244
|
|
244
245
|
<div class="uxbert-capture-action" id="uxbert-capture-action">
|
@@ -282,67 +283,67 @@ class Modal {
|
|
282
283
|
if (!this.modal)
|
283
284
|
return;
|
284
285
|
// Close button
|
285
|
-
const closeBtn = this.modal.querySelector(
|
286
|
-
closeBtn.addEventListener(
|
286
|
+
const closeBtn = this.modal.querySelector(".uxbert-modal-close");
|
287
|
+
closeBtn.addEventListener("click", () => this.close());
|
287
288
|
// Cancel button
|
288
|
-
const cancelBtn = this.modal.querySelector(
|
289
|
-
cancelBtn.addEventListener(
|
289
|
+
const cancelBtn = this.modal.querySelector("#uxbert-cancel-btn");
|
290
|
+
cancelBtn.addEventListener("click", () => this.close());
|
290
291
|
// Overlay click to close
|
291
|
-
this.overlay?.addEventListener(
|
292
|
+
this.overlay?.addEventListener("click", (e) => {
|
292
293
|
if (e.target === this.overlay) {
|
293
294
|
this.close();
|
294
295
|
}
|
295
296
|
});
|
296
297
|
// Form submit
|
297
|
-
const form = this.modal.querySelector(
|
298
|
-
form.addEventListener(
|
298
|
+
const form = this.modal.querySelector("#uxbert-issue-form");
|
299
|
+
form.addEventListener("submit", (e) => {
|
299
300
|
e.preventDefault();
|
300
301
|
this.handleSubmit();
|
301
302
|
});
|
302
303
|
// Capture button
|
303
|
-
const captureBtn = this.modal.querySelector(
|
304
|
-
captureBtn.addEventListener(
|
304
|
+
const captureBtn = this.modal.querySelector("#uxbert-capture-btn");
|
305
|
+
captureBtn.addEventListener("click", () => {
|
305
306
|
if (this.callbacks.onCapture) {
|
306
307
|
this.callbacks.onCapture();
|
307
308
|
}
|
308
309
|
});
|
309
310
|
// Annotate button
|
310
|
-
const annotateBtn = this.modal.querySelector(
|
311
|
-
annotateBtn.addEventListener(
|
311
|
+
const annotateBtn = this.modal.querySelector("#uxbert-annotate-btn");
|
312
|
+
annotateBtn.addEventListener("click", () => {
|
312
313
|
if (this.callbacks.onAnnotate) {
|
313
314
|
this.callbacks.onAnnotate();
|
314
315
|
}
|
315
316
|
});
|
316
317
|
// Retake button
|
317
|
-
const retakeBtn = this.modal.querySelector(
|
318
|
-
retakeBtn.addEventListener(
|
318
|
+
const retakeBtn = this.modal.querySelector("#uxbert-retake-btn");
|
319
|
+
retakeBtn.addEventListener("click", () => {
|
319
320
|
if (this.callbacks.onRetake) {
|
320
321
|
this.callbacks.onRetake();
|
321
322
|
}
|
322
323
|
});
|
323
324
|
// n8n button
|
324
|
-
const n8nBtn = this.modal.querySelector(
|
325
|
-
n8nBtn.addEventListener(
|
325
|
+
const n8nBtn = this.modal.querySelector("#uxbert-n8n-btn");
|
326
|
+
n8nBtn.addEventListener("click", () => {
|
326
327
|
this.handleN8nSubmit();
|
327
328
|
});
|
328
329
|
}
|
329
330
|
handleN8nSubmit() {
|
330
331
|
if (!this.modal)
|
331
332
|
return;
|
332
|
-
const titleInput = this.modal.querySelector(
|
333
|
-
const descriptionInput = this.modal.querySelector(
|
334
|
-
const prioritySelect = this.modal.querySelector(
|
333
|
+
const titleInput = this.modal.querySelector("#uxbert-title");
|
334
|
+
const descriptionInput = this.modal.querySelector("#uxbert-description");
|
335
|
+
const prioritySelect = this.modal.querySelector("#uxbert-priority");
|
335
336
|
const title = titleInput.value;
|
336
337
|
const description = descriptionInput.value;
|
337
338
|
const priority = prioritySelect.value;
|
338
339
|
if (!title.trim()) {
|
339
|
-
alert(
|
340
|
+
alert("Please enter an issue title");
|
340
341
|
return;
|
341
342
|
}
|
342
343
|
const issueData = {
|
343
344
|
title: title.trim(),
|
344
345
|
description: description.trim(),
|
345
|
-
priority
|
346
|
+
priority,
|
346
347
|
};
|
347
348
|
if (this.callbacks.onSendToN8n) {
|
348
349
|
this.callbacks.onSendToN8n(issueData);
|
@@ -351,30 +352,34 @@ class Modal {
|
|
351
352
|
handleSubmit() {
|
352
353
|
if (!this.modal)
|
353
354
|
return;
|
354
|
-
const titleInput = this.modal.querySelector(
|
355
|
-
const descriptionInput = this.modal.querySelector(
|
356
|
-
const prioritySelect = this.modal.querySelector(
|
355
|
+
const titleInput = this.modal.querySelector("#uxbert-title");
|
356
|
+
const descriptionInput = this.modal.querySelector("#uxbert-description");
|
357
|
+
const prioritySelect = this.modal.querySelector("#uxbert-priority");
|
357
358
|
const title = titleInput.value;
|
358
359
|
const description = descriptionInput.value;
|
359
360
|
const priority = prioritySelect.value;
|
360
361
|
if (!title.trim()) {
|
361
|
-
alert(
|
362
|
+
alert("Please enter an issue title");
|
362
363
|
return;
|
363
364
|
}
|
364
365
|
const issueData = {
|
365
366
|
title: title.trim(),
|
366
367
|
description: description.trim(),
|
367
|
-
priority
|
368
|
+
priority,
|
368
369
|
};
|
369
370
|
if (this.callbacks.onSubmit) {
|
370
371
|
this.callbacks.onSubmit(issueData);
|
371
372
|
}
|
372
373
|
}
|
373
374
|
open() {
|
374
|
-
this.overlay?.classList.add(
|
375
|
+
this.overlay?.classList.add("active");
|
376
|
+
// Prevent body scroll when modal is open
|
377
|
+
document.body.style.overflow = "hidden";
|
375
378
|
}
|
376
379
|
close() {
|
377
|
-
this.overlay?.classList.remove(
|
380
|
+
this.overlay?.classList.remove("active");
|
381
|
+
// Restore body scroll when modal is closed
|
382
|
+
document.body.style.overflow = "";
|
378
383
|
this.reset();
|
379
384
|
if (this.callbacks.onClose) {
|
380
385
|
this.callbacks.onClose();
|
@@ -383,47 +388,49 @@ class Modal {
|
|
383
388
|
setScreenshot(screenshot) {
|
384
389
|
if (!this.modal)
|
385
390
|
return;
|
386
|
-
const container = this.modal.querySelector(
|
387
|
-
const img = this.modal.querySelector(
|
388
|
-
const captureAction = this.modal.querySelector(
|
389
|
-
const submitBtn = this.modal.querySelector(
|
391
|
+
const container = this.modal.querySelector("#uxbert-screenshot-container");
|
392
|
+
const img = this.modal.querySelector("#uxbert-screenshot-img");
|
393
|
+
const captureAction = this.modal.querySelector("#uxbert-capture-action");
|
394
|
+
const submitBtn = this.modal.querySelector("#uxbert-submit-btn");
|
390
395
|
if (screenshot) {
|
391
396
|
img.src = screenshot;
|
392
|
-
container.style.display =
|
393
|
-
captureAction.style.display =
|
397
|
+
container.style.display = "block";
|
398
|
+
captureAction.style.display = "none";
|
394
399
|
submitBtn.disabled = false;
|
395
400
|
}
|
396
401
|
else {
|
397
|
-
container.style.display =
|
398
|
-
captureAction.style.display =
|
402
|
+
container.style.display = "none";
|
403
|
+
captureAction.style.display = "block";
|
399
404
|
submitBtn.disabled = true;
|
400
405
|
}
|
401
406
|
}
|
402
407
|
getCaptureMode() {
|
403
408
|
if (!this.modal)
|
404
|
-
return
|
405
|
-
const viewportRadio = this.modal.querySelector(
|
406
|
-
return viewportRadio.checked ?
|
409
|
+
return "viewport";
|
410
|
+
const viewportRadio = this.modal.querySelector("#uxbert-capture-viewport");
|
411
|
+
return viewportRadio.checked ? "viewport" : "fullpage";
|
407
412
|
}
|
408
413
|
setN8nButtonVisible(visible) {
|
409
414
|
if (!this.modal)
|
410
415
|
return;
|
411
|
-
const n8nBtn = this.modal.querySelector(
|
416
|
+
const n8nBtn = this.modal.querySelector("#uxbert-n8n-btn");
|
412
417
|
if (n8nBtn) {
|
413
|
-
n8nBtn.style.display = visible ?
|
418
|
+
n8nBtn.style.display = visible ? "inline-block" : "none";
|
414
419
|
}
|
415
420
|
}
|
416
421
|
reset() {
|
417
422
|
if (!this.modal)
|
418
423
|
return;
|
419
|
-
this.modal.querySelector(
|
420
|
-
this.modal.querySelector(
|
421
|
-
this.modal.querySelector(
|
424
|
+
this.modal.querySelector("#uxbert-title").value = "";
|
425
|
+
this.modal.querySelector("#uxbert-description").value = "";
|
426
|
+
this.modal.querySelector("#uxbert-priority").value = "Medium";
|
422
427
|
this.setScreenshot(null);
|
423
428
|
}
|
424
429
|
destroy() {
|
425
430
|
if (this.overlay && this.overlay.parentNode) {
|
426
431
|
this.overlay.parentNode.removeChild(this.overlay);
|
432
|
+
// Restore body scroll when destroying modal
|
433
|
+
document.body.style.overflow = "";
|
427
434
|
this.overlay = null;
|
428
435
|
this.modal = null;
|
429
436
|
}
|
@@ -10222,24 +10229,23 @@ class N8nIntegration {
|
|
10222
10229
|
if (!this.config || !this.config.webhookUrl) {
|
10223
10230
|
return {
|
10224
10231
|
success: false,
|
10225
|
-
message:
|
10226
|
-
error:
|
10232
|
+
message: "n8n webhook URL is not configured",
|
10233
|
+
error: "MISSING_CONFIG",
|
10227
10234
|
};
|
10228
10235
|
}
|
10229
10236
|
if (!this.config.enabled) {
|
10230
10237
|
return {
|
10231
10238
|
success: false,
|
10232
|
-
message:
|
10233
|
-
error:
|
10239
|
+
message: "n8n integration is disabled",
|
10240
|
+
error: "DISABLED",
|
10234
10241
|
};
|
10235
10242
|
}
|
10236
10243
|
try {
|
10237
|
-
// Prepare
|
10238
|
-
const
|
10239
|
-
// Setup request headers
|
10244
|
+
// Prepare FormData with binary screenshot
|
10245
|
+
const formData = await this.prepareFormData(issueData);
|
10246
|
+
// Setup request headers (don't set Content-Type - browser will set it with boundary for FormData)
|
10240
10247
|
const headers = {
|
10241
|
-
|
10242
|
-
...this.config.headers
|
10248
|
+
...this.config.headers,
|
10243
10249
|
};
|
10244
10250
|
// Setup timeout
|
10245
10251
|
const timeout = this.config.timeout || 30000; // 30 seconds default
|
@@ -10247,19 +10253,19 @@ class N8nIntegration {
|
|
10247
10253
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
10248
10254
|
// Send request
|
10249
10255
|
const response = await fetch(this.config.webhookUrl, {
|
10250
|
-
method:
|
10256
|
+
method: "POST",
|
10251
10257
|
headers,
|
10252
|
-
body:
|
10253
|
-
signal: controller.signal
|
10258
|
+
body: formData,
|
10259
|
+
signal: controller.signal,
|
10254
10260
|
});
|
10255
10261
|
clearTimeout(timeoutId);
|
10256
10262
|
// Check response
|
10257
10263
|
if (!response.ok) {
|
10258
|
-
const errorText = await response.text().catch(() =>
|
10264
|
+
const errorText = await response.text().catch(() => "Unknown error");
|
10259
10265
|
return {
|
10260
10266
|
success: false,
|
10261
10267
|
message: `n8n webhook request failed: ${response.status} ${response.statusText}`,
|
10262
|
-
error: errorText
|
10268
|
+
error: errorText,
|
10263
10269
|
};
|
10264
10270
|
}
|
10265
10271
|
// Parse response
|
@@ -10269,76 +10275,87 @@ class N8nIntegration {
|
|
10269
10275
|
}
|
10270
10276
|
catch (e) {
|
10271
10277
|
// Some webhooks don't return JSON
|
10272
|
-
responseData = { message:
|
10278
|
+
responseData = { message: "Success" };
|
10273
10279
|
}
|
10274
10280
|
return {
|
10275
10281
|
success: true,
|
10276
|
-
message:
|
10277
|
-
workflowId: responseData.workflowId || responseData.executionId
|
10282
|
+
message: "Issue sent to n8n successfully",
|
10283
|
+
workflowId: responseData.workflowId || responseData.executionId,
|
10278
10284
|
};
|
10279
10285
|
}
|
10280
10286
|
catch (error) {
|
10281
10287
|
if (error instanceof Error) {
|
10282
|
-
if (error.name ===
|
10288
|
+
if (error.name === "AbortError") {
|
10283
10289
|
return {
|
10284
10290
|
success: false,
|
10285
|
-
message:
|
10286
|
-
error:
|
10291
|
+
message: "Request to n8n webhook timed out",
|
10292
|
+
error: "TIMEOUT",
|
10287
10293
|
};
|
10288
10294
|
}
|
10289
10295
|
return {
|
10290
10296
|
success: false,
|
10291
10297
|
message: `Failed to send issue to n8n: ${error.message}`,
|
10292
|
-
error: error.message
|
10298
|
+
error: error.message,
|
10293
10299
|
};
|
10294
10300
|
}
|
10295
10301
|
return {
|
10296
10302
|
success: false,
|
10297
|
-
message:
|
10298
|
-
error:
|
10303
|
+
message: "Unknown error occurred while sending to n8n",
|
10304
|
+
error: "UNKNOWN",
|
10299
10305
|
};
|
10300
10306
|
}
|
10301
10307
|
}
|
10302
10308
|
/**
|
10303
|
-
*
|
10304
|
-
* This formats the data in a structure that's easy to work with in n8n
|
10309
|
+
* Convert base64 data URL to Blob
|
10305
10310
|
*/
|
10306
|
-
|
10307
|
-
|
10308
|
-
|
10309
|
-
|
10310
|
-
|
10311
|
-
|
10312
|
-
|
10313
|
-
|
10314
|
-
|
10315
|
-
|
10316
|
-
|
10317
|
-
|
10318
|
-
|
10319
|
-
|
10320
|
-
|
10321
|
-
|
10322
|
-
|
10323
|
-
|
10324
|
-
|
10325
|
-
|
10326
|
-
|
10327
|
-
|
10328
|
-
|
10329
|
-
|
10330
|
-
|
10331
|
-
|
10332
|
-
|
10333
|
-
|
10334
|
-
|
10335
|
-
|
10336
|
-
|
10337
|
-
|
10338
|
-
|
10339
|
-
|
10340
|
-
|
10341
|
-
|
10311
|
+
base64ToBlob(base64Data) {
|
10312
|
+
// Extract the base64 string and mime type
|
10313
|
+
const matches = base64Data.match(/^data:([^;]+);base64,(.+)$/);
|
10314
|
+
if (!matches) {
|
10315
|
+
throw new Error("Invalid base64 data URL");
|
10316
|
+
}
|
10317
|
+
const mimeType = matches[1];
|
10318
|
+
const base64String = matches[2];
|
10319
|
+
// Decode base64
|
10320
|
+
const byteCharacters = atob(base64String);
|
10321
|
+
const byteNumbers = new Array(byteCharacters.length);
|
10322
|
+
for (let i = 0; i < byteCharacters.length; i++) {
|
10323
|
+
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
10324
|
+
}
|
10325
|
+
const byteArray = new Uint8Array(byteNumbers);
|
10326
|
+
return new Blob([byteArray], { type: mimeType });
|
10327
|
+
}
|
10328
|
+
/**
|
10329
|
+
* Prepare FormData payload for n8n webhook
|
10330
|
+
* This sends the screenshot as a binary file instead of base64 JSON
|
10331
|
+
*/
|
10332
|
+
async prepareFormData(issueData) {
|
10333
|
+
const formData = new FormData();
|
10334
|
+
// Add issue data as JSON string
|
10335
|
+
formData.append("title", issueData.title);
|
10336
|
+
formData.append("description", issueData.description);
|
10337
|
+
formData.append("priority", issueData.priority);
|
10338
|
+
formData.append("createdAt", issueData.createdAt);
|
10339
|
+
// Convert base64 screenshot to binary Blob and add as file
|
10340
|
+
const screenshotBlob = this.base64ToBlob(issueData.screenshot);
|
10341
|
+
const filename = `screenshot-${Date.now()}.png`;
|
10342
|
+
formData.append("screenshot", screenshotBlob, filename);
|
10343
|
+
// Add device info as individual fields
|
10344
|
+
formData.append("browser", issueData.deviceInfo.browser);
|
10345
|
+
formData.append("os", issueData.deviceInfo.os);
|
10346
|
+
formData.append("screenResolution", issueData.deviceInfo.screenResolution);
|
10347
|
+
formData.append("viewport", issueData.deviceInfo.viewport);
|
10348
|
+
formData.append("url", issueData.deviceInfo.url);
|
10349
|
+
formData.append("userAgent", issueData.deviceInfo.userAgent);
|
10350
|
+
formData.append("language", issueData.deviceInfo.language || "");
|
10351
|
+
formData.append("platform", issueData.deviceInfo.platform);
|
10352
|
+
formData.append("cookiesEnabled", String(issueData.deviceInfo.cookiesEnabled));
|
10353
|
+
formData.append("onLine", String(issueData.deviceInfo.onLine));
|
10354
|
+
formData.append("timestamp", issueData.deviceInfo.timestamp);
|
10355
|
+
// Add metadata
|
10356
|
+
formData.append("source", "uxbert-reportly");
|
10357
|
+
formData.append("version", "1.0.0");
|
10358
|
+
return formData;
|
10342
10359
|
}
|
10343
10360
|
/**
|
10344
10361
|
* Test connection to n8n webhook
|
@@ -10347,28 +10364,28 @@ class N8nIntegration {
|
|
10347
10364
|
if (!this.config || !this.config.webhookUrl) {
|
10348
10365
|
return {
|
10349
10366
|
success: false,
|
10350
|
-
message:
|
10351
|
-
error:
|
10367
|
+
message: "n8n webhook URL is not configured",
|
10368
|
+
error: "MISSING_CONFIG",
|
10352
10369
|
};
|
10353
10370
|
}
|
10354
10371
|
try {
|
10355
10372
|
const controller = new AbortController();
|
10356
10373
|
const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout for test
|
10357
10374
|
await fetch(this.config.webhookUrl, {
|
10358
|
-
method:
|
10359
|
-
signal: controller.signal
|
10375
|
+
method: "HEAD",
|
10376
|
+
signal: controller.signal,
|
10360
10377
|
});
|
10361
10378
|
clearTimeout(timeoutId);
|
10362
10379
|
return {
|
10363
10380
|
success: true,
|
10364
|
-
message:
|
10381
|
+
message: "n8n webhook is reachable",
|
10365
10382
|
};
|
10366
10383
|
}
|
10367
10384
|
catch (error) {
|
10368
10385
|
return {
|
10369
10386
|
success: false,
|
10370
|
-
message:
|
10371
|
-
error: error instanceof Error ? error.message :
|
10387
|
+
message: "Failed to reach n8n webhook",
|
10388
|
+
error: error instanceof Error ? error.message : "UNKNOWN",
|
10372
10389
|
};
|
10373
10390
|
}
|
10374
10391
|
}
|
@@ -10385,7 +10402,7 @@ class N8nIntegration {
|
|
10385
10402
|
if (!this.config) {
|
10386
10403
|
this.config = {
|
10387
10404
|
webhookUrl: url,
|
10388
|
-
enabled: true
|
10405
|
+
enabled: true,
|
10389
10406
|
};
|
10390
10407
|
}
|
10391
10408
|
else {
|
@@ -10551,7 +10568,7 @@ function styleInject(css, ref) {
|
|
10551
10568
|
}
|
10552
10569
|
}
|
10553
10570
|
|
10554
|
-
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}}";
|
10571
|
+
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;overflow:hidden;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}}";
|
10555
10572
|
styleInject(css_248z);
|
10556
10573
|
|
10557
10574
|
// Main initialization file
|