@uxbertlabs/reportly 1.0.26 → 1.0.28

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.
@@ -1 +1 @@
1
- {"version":3,"file":"IssueModal.d.ts","sourceRoot":"","sources":["../../src/components/IssueModal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AAOrD,UAAU,eAAe;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,UAAU,CAAC,EAAE,SAAc,EAAE,EAAE,eAAe,4BA+V7D;AAED,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"IssueModal.d.ts","sourceRoot":"","sources":["../../src/components/IssueModal.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AAOrD,UAAU,eAAe;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,UAAU,CAAC,EAAE,SAAc,EAAE,EAAE,eAAe,4BA8W7D;AAED,eAAe,UAAU,CAAC"}
@@ -1,13 +1,13 @@
1
- import React, { ReactNode } from 'react';
2
- import type { ReportlyConfig, IssueData, CompleteIssue, AnnotationTool } from '../types';
3
- import type { N8nResponse } from '../features/n8n-integration';
4
- import AnnotationManager from '../features/annotation';
1
+ import React, { ReactNode } from "react";
2
+ import type { ReportlyConfig, IssueData, CompleteIssue, AnnotationTool } from "../types";
3
+ import type { N8nResponse } from "../features/n8n-integration";
4
+ import AnnotationManager from "../features/annotation";
5
5
  export interface ReportlyState {
6
6
  isOpen: boolean;
7
7
  isAnnotating: boolean;
8
8
  isCapturing: boolean;
9
9
  screenshot: string | null;
10
- captureMode: 'viewport' | 'fullpage';
10
+ captureMode: "viewport" | "fullpage";
11
11
  currentTool: string;
12
12
  currentColor: string;
13
13
  config: ReportlyConfig;
@@ -20,7 +20,7 @@ interface ReportlyContextType {
20
20
  closeModal: () => void;
21
21
  startAnnotation: () => void;
22
22
  endAnnotation: () => void;
23
- captureScreenshot: (mode?: 'viewport' | 'fullpage') => Promise<void>;
23
+ captureScreenshot: (mode?: "viewport" | "fullpage") => Promise<void>;
24
24
  retakeScreenshot: () => Promise<void>;
25
25
  setTool: (tool: AnnotationTool) => void;
26
26
  setColor: (color: string) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"ReportlyProvider.d.ts","sourceRoot":"","sources":["../../src/components/ReportlyProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAyC,SAAS,EAAwB,MAAM,OAAO,CAAC;AACtG,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AACzF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAI/D,OAAO,iBAAiB,MAAM,wBAAwB,CAAC;AAEvD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,UAAU,GAAG,UAAU,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,cAAc,CAAC;CACxB;AAmED,UAAU,mBAAmB;IAC3B,KAAK,EAAE,aAAa,CAAC;IACrB,iBAAiB,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC5C,oBAAoB,EAAE,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,KAAK,IAAI,CAAC;IAClE,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,iBAAiB,EAAE,CAAC,IAAI,CAAC,EAAE,UAAU,GAAG,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,gBAAgB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,OAAO,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACxC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,WAAW,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,IAAI,CAAC;IAC5C,WAAW,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IAC5D,YAAY,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC7C,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,cAAc,CAAC,KAAK,IAAI,CAAC;IACxD,gBAAgB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAID,UAAU,qBAAqB;IAC7B,QAAQ,EAAE,SAAS,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;CAClC;AAED,wBAAgB,gBAAgB,CAAC,EAAE,QAAQ,EAAE,MAAW,EAAE,EAAE,qBAAqB,qBA0KhF;AAED,wBAAgB,WAAW,IAAI,mBAAmB,CAMjD"}
1
+ {"version":3,"file":"ReportlyProvider.d.ts","sourceRoot":"","sources":["../../src/components/ReportlyProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAyC,SAAS,EAAwB,MAAM,OAAO,CAAC;AACtG,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AACzF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAI/D,OAAO,iBAAiB,MAAM,wBAAwB,CAAC;AAEvD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,UAAU,GAAG,UAAU,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,cAAc,CAAC;CACxB;AAmED,UAAU,mBAAmB;IAC3B,KAAK,EAAE,aAAa,CAAC;IACrB,iBAAiB,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC5C,oBAAoB,EAAE,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,KAAK,IAAI,CAAC;IAClE,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,iBAAiB,EAAE,CAAC,IAAI,CAAC,EAAE,UAAU,GAAG,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,gBAAgB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,OAAO,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACxC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,WAAW,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,IAAI,CAAC;IAC5C,WAAW,EAAE,CAAC,SAAS,EAAE,SAAS,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IAC5D,YAAY,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAC7C,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,cAAc,CAAC,KAAK,IAAI,CAAC;IACxD,gBAAgB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAID,UAAU,qBAAqB;IAC7B,QAAQ,EAAE,SAAS,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;CAClC;AAED,wBAAgB,gBAAgB,CAAC,EAAE,QAAQ,EAAE,MAAW,EAAE,EAAE,qBAAqB,qBAwLhF;AAED,wBAAgB,WAAW,IAAI,mBAAmB,CAMjD"}
package/dist/index.cjs.js CHANGED
@@ -9632,13 +9632,13 @@ const initialState = {
9632
9632
  isAnnotating: false,
9633
9633
  isCapturing: false,
9634
9634
  screenshot: null,
9635
- captureMode: 'viewport',
9636
- currentTool: 'pen',
9637
- currentColor: '#ff0000',
9635
+ captureMode: "viewport",
9636
+ currentTool: "pen",
9637
+ currentColor: "#ff0000",
9638
9638
  config: {
9639
9639
  ui: {
9640
- position: 'bottom-right',
9641
- theme: 'light'
9640
+ position: "bottom-right",
9641
+ theme: "light"
9642
9642
  },
9643
9643
  features: {
9644
9644
  annotation: true,
@@ -9648,67 +9648,67 @@ const initialState = {
9648
9648
  };
9649
9649
  function reportlyReducer(state, action) {
9650
9650
  switch (action.type) {
9651
- case 'OPEN_MODAL':
9651
+ case "OPEN_MODAL":
9652
9652
  return {
9653
9653
  ...state,
9654
9654
  isOpen: true
9655
9655
  };
9656
- case 'CLOSE_MODAL':
9656
+ case "CLOSE_MODAL":
9657
9657
  return {
9658
9658
  ...state,
9659
9659
  isOpen: false,
9660
9660
  isAnnotating: false
9661
9661
  };
9662
- case 'START_ANNOTATION':
9662
+ case "START_ANNOTATION":
9663
9663
  return {
9664
9664
  ...state,
9665
9665
  isAnnotating: true,
9666
9666
  isOpen: false
9667
9667
  };
9668
- case 'END_ANNOTATION':
9668
+ case "END_ANNOTATION":
9669
9669
  return {
9670
9670
  ...state,
9671
9671
  isAnnotating: false,
9672
9672
  isOpen: true
9673
9673
  };
9674
- case 'START_CAPTURE':
9674
+ case "START_CAPTURE":
9675
9675
  return {
9676
9676
  ...state,
9677
9677
  isCapturing: true
9678
9678
  };
9679
- case 'END_CAPTURE':
9679
+ case "END_CAPTURE":
9680
9680
  return {
9681
9681
  ...state,
9682
9682
  isCapturing: false
9683
9683
  };
9684
- case 'SET_SCREENSHOT':
9684
+ case "SET_SCREENSHOT":
9685
9685
  return {
9686
9686
  ...state,
9687
9687
  screenshot: action.payload
9688
9688
  };
9689
- case 'SET_CAPTURE_MODE':
9689
+ case "SET_CAPTURE_MODE":
9690
9690
  return {
9691
9691
  ...state,
9692
9692
  captureMode: action.payload
9693
9693
  };
9694
- case 'SET_TOOL':
9694
+ case "SET_TOOL":
9695
9695
  return {
9696
9696
  ...state,
9697
9697
  currentTool: action.payload
9698
9698
  };
9699
- case 'SET_COLOR':
9699
+ case "SET_COLOR":
9700
9700
  return {
9701
9701
  ...state,
9702
9702
  currentColor: action.payload
9703
9703
  };
9704
- case 'RESET':
9704
+ case "RESET":
9705
9705
  return {
9706
9706
  ...state,
9707
9707
  isOpen: false,
9708
9708
  isAnnotating: false,
9709
9709
  screenshot: null
9710
9710
  };
9711
- case 'UPDATE_CONFIG':
9711
+ case "UPDATE_CONFIG":
9712
9712
  return {
9713
9713
  ...state,
9714
9714
  config: {
@@ -9740,58 +9740,58 @@ function ReportlyProvider({
9740
9740
  const screenshot = React.useMemo(() => new Screenshot(), []);
9741
9741
  const openModal = React.useCallback(() => {
9742
9742
  dispatch({
9743
- type: 'OPEN_MODAL'
9743
+ type: "OPEN_MODAL"
9744
9744
  });
9745
9745
  }, []);
9746
9746
  const closeModal = React.useCallback(() => {
9747
9747
  dispatch({
9748
- type: 'CLOSE_MODAL'
9748
+ type: "CLOSE_MODAL"
9749
9749
  });
9750
9750
  }, []);
9751
9751
  const startAnnotation = React.useCallback(() => {
9752
9752
  dispatch({
9753
- type: 'START_ANNOTATION'
9753
+ type: "START_ANNOTATION"
9754
9754
  });
9755
9755
  }, []);
9756
9756
  const endAnnotation = React.useCallback(() => {
9757
9757
  dispatch({
9758
- type: 'END_ANNOTATION'
9758
+ type: "END_ANNOTATION"
9759
9759
  });
9760
9760
  }, []);
9761
- const captureScreenshot = React.useCallback(async (mode = 'viewport') => {
9761
+ const captureScreenshot = React.useCallback(async (mode = "viewport") => {
9762
9762
  try {
9763
9763
  dispatch({
9764
- type: 'START_CAPTURE'
9764
+ type: "START_CAPTURE"
9765
9765
  });
9766
9766
  dispatch({
9767
- type: 'SET_CAPTURE_MODE',
9767
+ type: "SET_CAPTURE_MODE",
9768
9768
  payload: mode
9769
9769
  });
9770
9770
  // Close modal before capturing
9771
9771
  dispatch({
9772
- type: 'CLOSE_MODAL'
9772
+ type: "CLOSE_MODAL"
9773
9773
  });
9774
9774
  // Wait a bit for the modal to close (for animation)
9775
9775
  await new Promise(resolve => setTimeout(resolve, 100));
9776
9776
  const screenshotData = await screenshot.capture(mode);
9777
9777
  dispatch({
9778
- type: 'SET_SCREENSHOT',
9778
+ type: "SET_SCREENSHOT",
9779
9779
  payload: screenshotData
9780
9780
  });
9781
9781
  // Reopen modal after capture
9782
9782
  dispatch({
9783
- type: 'OPEN_MODAL'
9783
+ type: "OPEN_MODAL"
9784
9784
  });
9785
9785
  dispatch({
9786
- type: 'END_CAPTURE'
9786
+ type: "END_CAPTURE"
9787
9787
  });
9788
9788
  } catch (error) {
9789
- console.error('Failed to capture screenshot:', error);
9789
+ console.error("Failed to capture screenshot:", error);
9790
9790
  dispatch({
9791
- type: 'OPEN_MODAL'
9791
+ type: "OPEN_MODAL"
9792
9792
  });
9793
9793
  dispatch({
9794
- type: 'END_CAPTURE'
9794
+ type: "END_CAPTURE"
9795
9795
  });
9796
9796
  throw error;
9797
9797
  }
@@ -9803,13 +9803,13 @@ function ReportlyProvider({
9803
9803
  }
9804
9804
  await captureScreenshot(state.captureMode);
9805
9805
  } catch (error) {
9806
- console.error('Failed to retake screenshot:', error);
9806
+ console.error("Failed to retake screenshot:", error);
9807
9807
  throw error;
9808
9808
  }
9809
9809
  }, [annotationManager, captureScreenshot, state.captureMode]);
9810
9810
  const setTool = React.useCallback(tool => {
9811
9811
  dispatch({
9812
- type: 'SET_TOOL',
9812
+ type: "SET_TOOL",
9813
9813
  payload: tool
9814
9814
  });
9815
9815
  if (annotationManager) {
@@ -9818,7 +9818,7 @@ function ReportlyProvider({
9818
9818
  }, [annotationManager]);
9819
9819
  const setColor = React.useCallback(color => {
9820
9820
  dispatch({
9821
- type: 'SET_COLOR',
9821
+ type: "SET_COLOR",
9822
9822
  payload: color
9823
9823
  });
9824
9824
  if (annotationManager) {
@@ -9829,19 +9829,19 @@ function ReportlyProvider({
9829
9829
  try {
9830
9830
  const completeIssue = {
9831
9831
  ...issueData,
9832
- screenshot: state.screenshot || '',
9832
+ screenshot: state.screenshot || "",
9833
9833
  deviceInfo: deviceInfo.get(),
9834
9834
  createdAt: new Date().toISOString()
9835
9835
  };
9836
9836
  exportService.exportToJSON(completeIssue);
9837
9837
  dispatch({
9838
- type: 'RESET'
9838
+ type: "RESET"
9839
9839
  });
9840
9840
  if (annotationManager) {
9841
9841
  annotationManager.clear();
9842
9842
  }
9843
9843
  } catch (error) {
9844
- console.error('Failed to submit issue:', error);
9844
+ console.error("Failed to submit issue:", error);
9845
9845
  throw error;
9846
9846
  }
9847
9847
  }, [state.screenshot, deviceInfo, exportService, annotationManager]);
@@ -9849,14 +9849,14 @@ function ReportlyProvider({
9849
9849
  try {
9850
9850
  const completeIssue = {
9851
9851
  ...issueData,
9852
- screenshot: state.screenshot || '',
9852
+ screenshot: state.screenshot || "",
9853
9853
  deviceInfo: deviceInfo.get(),
9854
9854
  createdAt: new Date().toISOString()
9855
9855
  };
9856
9856
  const response = await exportService.sendToN8n(completeIssue);
9857
9857
  if (response?.success) {
9858
9858
  dispatch({
9859
- type: 'RESET'
9859
+ type: "RESET"
9860
9860
  });
9861
9861
  if (annotationManager) {
9862
9862
  annotationManager.clear();
@@ -9864,7 +9864,7 @@ function ReportlyProvider({
9864
9864
  }
9865
9865
  return response;
9866
9866
  } catch (error) {
9867
- console.error('Failed to send to n8n:', error);
9867
+ console.error("Failed to create jira ticket:", error);
9868
9868
  throw error;
9869
9869
  }
9870
9870
  }, [state.screenshot, deviceInfo, exportService, annotationManager]);
@@ -9873,19 +9873,19 @@ function ReportlyProvider({
9873
9873
  }, [exportService]);
9874
9874
  const updateConfig = React.useCallback(newConfig => {
9875
9875
  dispatch({
9876
- type: 'UPDATE_CONFIG',
9876
+ type: "UPDATE_CONFIG",
9877
9877
  payload: newConfig
9878
9878
  });
9879
9879
  }, []);
9880
9880
  const updateScreenshot = React.useCallback(screenshot => {
9881
9881
  dispatch({
9882
- type: 'SET_SCREENSHOT',
9882
+ type: "SET_SCREENSHOT",
9883
9883
  payload: screenshot
9884
9884
  });
9885
9885
  }, []);
9886
9886
  const reset = React.useCallback(() => {
9887
9887
  dispatch({
9888
- type: 'RESET'
9888
+ type: "RESET"
9889
9889
  });
9890
9890
  if (annotationManager) {
9891
9891
  annotationManager.clear();
@@ -9917,7 +9917,7 @@ function ReportlyProvider({
9917
9917
  function useReportly() {
9918
9918
  const context = React.useContext(ReportlyContext);
9919
9919
  if (!context) {
9920
- throw new Error('useReportly must be used within a ReportlyProvider');
9920
+ throw new Error("useReportly must be used within a ReportlyProvider");
9921
9921
  }
9922
9922
  return context;
9923
9923
  }
@@ -10352,12 +10352,20 @@ function IssueModal({
10352
10352
  const [captureMode, setCaptureMode] = React.useState("viewport");
10353
10353
  const [isSubmitting, setIsSubmitting] = React.useState(false);
10354
10354
  const [isSubmittingN8n, setIsSubmittingN8n] = React.useState(false);
10355
+ const [errors, setErrors] = React.useState({});
10355
10356
  const handleInputChange = React.useCallback((field, value) => {
10356
10357
  setFormData(prev => ({
10357
10358
  ...prev,
10358
10359
  [field]: value
10359
10360
  }));
10360
- }, []);
10361
+ // Clear error for the field when user starts typing
10362
+ if (field === "title" && errors.title) {
10363
+ setErrors(prev => ({
10364
+ ...prev,
10365
+ title: undefined
10366
+ }));
10367
+ }
10368
+ }, [errors.title]);
10361
10369
  const handleClose = React.useCallback(() => {
10362
10370
  const hasData = state.screenshot !== null || formData.title.trim() !== "" || formData.description.trim() !== "";
10363
10371
  if (hasData) {
@@ -10378,28 +10386,40 @@ function IssueModal({
10378
10386
  }, [state.screenshot, formData.title, formData.description, reset, closeModal]);
10379
10387
  const handleCapture = React.useCallback(async () => {
10380
10388
  try {
10389
+ setErrors({});
10381
10390
  await captureScreenshot(captureMode);
10382
10391
  } catch (error) {
10383
10392
  console.error("Failed to capture screenshot:", error);
10384
- alert("Failed to capture screenshot. Please try again.");
10393
+ setErrors({
10394
+ general: "Failed to capture screenshot. Please try again."
10395
+ });
10385
10396
  }
10386
10397
  }, [captureScreenshot, captureMode]);
10387
10398
  const handleRetake = React.useCallback(async () => {
10388
10399
  try {
10400
+ setErrors({});
10389
10401
  await retakeScreenshot();
10390
10402
  } catch (error) {
10391
10403
  console.error("Failed to retake screenshot:", error);
10392
- alert("Failed to retake screenshot. Please try again.");
10404
+ setErrors({
10405
+ general: "Failed to retake screenshot. Please try again."
10406
+ });
10393
10407
  }
10394
10408
  }, [retakeScreenshot]);
10395
10409
  const handleSubmit = React.useCallback(async e => {
10396
10410
  e.preventDefault();
10411
+ // Clear previous errors
10412
+ setErrors({});
10413
+ // Validation
10414
+ const newErrors = {};
10397
10415
  if (!formData.title.trim()) {
10398
- alert("Please enter an issue title");
10399
- return;
10416
+ newErrors.title = "Please enter an issue title";
10400
10417
  }
10401
10418
  if (!state.screenshot) {
10402
- alert("Please capture a screenshot first");
10419
+ newErrors.screenshot = "Please capture a screenshot first";
10420
+ }
10421
+ if (Object.keys(newErrors).length > 0) {
10422
+ setErrors(newErrors);
10403
10423
  return;
10404
10424
  }
10405
10425
  try {
@@ -10413,18 +10433,26 @@ function IssueModal({
10413
10433
  });
10414
10434
  } catch (error) {
10415
10435
  console.error("Failed to submit issue:", error);
10416
- alert("Failed to submit issue. Please try again.");
10436
+ setErrors({
10437
+ general: "Failed to submit issue. Please try again."
10438
+ });
10417
10439
  } finally {
10418
10440
  setIsSubmitting(false);
10419
10441
  }
10420
10442
  }, [formData, state.screenshot, submitIssue]);
10421
10443
  const handleN8nSubmit = React.useCallback(async () => {
10444
+ // Clear previous errors
10445
+ setErrors({});
10446
+ // Validation
10447
+ const newErrors = {};
10422
10448
  if (!formData.title.trim()) {
10423
- alert("Please enter an issue title");
10424
- return;
10449
+ newErrors.title = "Please enter an issue title";
10425
10450
  }
10426
10451
  if (!state.screenshot) {
10427
- alert("Please capture a screenshot first");
10452
+ newErrors.screenshot = "Please capture a screenshot first";
10453
+ }
10454
+ if (Object.keys(newErrors).length > 0) {
10455
+ setErrors(newErrors);
10428
10456
  return;
10429
10457
  }
10430
10458
  try {
@@ -10437,13 +10465,19 @@ function IssueModal({
10437
10465
  priority: "Medium",
10438
10466
  deviceType: "desktop"
10439
10467
  });
10440
- alert("Issue sent to n8n successfully!");
10468
+ setErrors({
10469
+ general: "Issue sent to n8n successfully!"
10470
+ });
10441
10471
  } else {
10442
- alert(`Failed to send to n8n: ${response.message}`);
10472
+ setErrors({
10473
+ general: `Failed to send to n8n: ${response.message}`
10474
+ });
10443
10475
  }
10444
10476
  } catch (error) {
10445
10477
  console.error("Failed to send to n8n:", error);
10446
- alert("Failed to send to n8n. Please try again.");
10478
+ setErrors({
10479
+ general: "Failed to send to n8n. Please try again."
10480
+ });
10447
10481
  } finally {
10448
10482
  setIsSubmittingN8n(false);
10449
10483
  }
@@ -10495,7 +10529,12 @@ function IssueModal({
10495
10529
  className: "uxbert-capture-title"
10496
10530
  }, "Capture Screenshot"), /*#__PURE__*/React.createElement("p", {
10497
10531
  className: "uxbert-capture-description"
10498
- }, "Choose what to capture before reporting your issue"), /*#__PURE__*/React.createElement("div", {
10532
+ }, "Choose what to capture before reporting your issue"), errors.general && (/*#__PURE__*/React.createElement("div", {
10533
+ className: "uxbert-error-message",
10534
+ style: {
10535
+ marginBottom: "1rem"
10536
+ }
10537
+ }, errors.general)), /*#__PURE__*/React.createElement("div", {
10499
10538
  className: "uxbert-capture-modes"
10500
10539
  }, /*#__PURE__*/React.createElement("label", {
10501
10540
  className: cn("uxbert-capture-mode-option", captureMode === "viewport" && "active")
@@ -10548,7 +10587,17 @@ function IssueModal({
10548
10587
  src: state.screenshot,
10549
10588
  alt: "Screenshot preview",
10550
10589
  className: "uxbert-screenshot-image"
10551
- })), /*#__PURE__*/React.createElement("div", {
10590
+ })), errors.screenshot && (/*#__PURE__*/React.createElement("div", {
10591
+ className: "uxbert-error-message",
10592
+ style: {
10593
+ marginTop: "0.5rem"
10594
+ }
10595
+ }, errors.screenshot)), errors.general && (/*#__PURE__*/React.createElement("div", {
10596
+ className: "uxbert-error-message",
10597
+ style: {
10598
+ marginTop: "0.5rem"
10599
+ }
10600
+ }, errors.general)), /*#__PURE__*/React.createElement("div", {
10552
10601
  className: "uxbert-screenshot-actions"
10553
10602
  }, /*#__PURE__*/React.createElement("button", {
10554
10603
  onClick: handleRetake,
@@ -10583,8 +10632,10 @@ function IssueModal({
10583
10632
  onChange: e => handleInputChange("title", e.target.value),
10584
10633
  placeholder: "Brief description of the issue",
10585
10634
  required: true,
10586
- className: "uxbert-form-input"
10587
- })), /*#__PURE__*/React.createElement("div", {
10635
+ className: cn("uxbert-form-input", errors.title && "uxbert-form-input-error")
10636
+ }), errors.title && /*#__PURE__*/React.createElement("div", {
10637
+ className: "uxbert-error-message"
10638
+ }, errors.title)), /*#__PURE__*/React.createElement("div", {
10588
10639
  className: "uxbert-form-group"
10589
10640
  }, /*#__PURE__*/React.createElement("label", {
10590
10641
  htmlFor: "issue-description",
@@ -10661,7 +10712,7 @@ function IssueModal({
10661
10712
  style: {
10662
10713
  color: "currentColor"
10663
10714
  }
10664
- }), "Send to n8n")))))))))));
10715
+ }), "Create Ticket")))))))))));
10665
10716
  }
10666
10717
 
10667
10718
  function AnnotationToolbar({