@pitangent/feedback-system 1.0.1 → 1.0.3

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,136 +1,163 @@
1
- # @wellnesspro/feedback
1
+ # @pitangent/feedback-system
2
2
 
3
- A drop-in React feedback widget with screenshot capture and rrweb session
3
+ A drop-in React feedback widget with rating, bug report, and feature request
4
+ flows — including screenshot capture, screen recording, and rrweb session
4
5
  recording/playback.
5
6
 
6
- ## Exports
7
+ ## Installation
7
8
 
8
- ```ts
9
- import {
10
- FeedbackWidget, // the full feedback widget (trigger + panel + modal)
11
- RRWebPlayerModal, // standalone session-recording preview player
12
- SessionRecordingPreview // friendly alias for RRWebPlayerModal
13
- } from '@wellnesspro/feedback';
14
- import type { RRWebPlayerModalProps } from '@wellnesspro/feedback';
9
+ ```bash
10
+ npm install @pitangent/feedback-system
15
11
  ```
16
12
 
17
13
  ### Peer dependencies
18
14
 
19
- Consumers must have these installed (they are **not** bundled):
15
+ These are **not** bundled your app must already have them installed:
20
16
 
21
17
  - `react` `^18 || ^19`
22
18
  - `react-dom` `^18 || ^19`
23
19
  - `lucide-react` `^0.525.0`
24
20
  - `sonner` `^2.0.0`
25
21
 
26
- ---
27
-
28
- ## Publishing to npm — step by step
29
-
30
- > This is a **scoped** package (`@wellnesspro/...`). On the public npm registry,
31
- > scoped packages are **private by default**, so you must pass `--access public`
32
- > the first time (or set `publishConfig.access` — see below).
33
-
34
- ### One-time setup
35
-
36
- 1. **Create / verify the npm org.** The `@wellnesspro` scope must be an npm
37
- organization (or user) you belong to. Create it at
38
- <https://www.npmjs.com/org/create> (name: `wellnesspro`), or change the
39
- package name in `package.json`.
40
-
41
- 2. **Recommended `package.json` additions** so publishes are safe and repeatable:
42
-
43
- ```jsonc
44
- {
45
- "files": ["dist"], // ship only the build output
46
- "publishConfig": { "access": "public" },
47
- "scripts": {
48
- "prepublishOnly": "npm run build" // always build fresh dist before publish
49
- }
50
- }
51
- ```
52
-
53
- ### Every release
54
-
55
22
  ```bash
56
- cd packages
57
-
58
- # 1. Log in (once per machine)
59
- npm login
60
- npm whoami # confirm you're logged in
61
-
62
- # 2. Bump the version (npm rejects re-publishing an existing version)
63
- npm version patch # bug fix: 1.0.0 -> 1.0.1
64
- # npm version minor # new feature: 1.0.0 -> 1.1.0
65
- # npm version major # breaking: 1.0.0 -> 2.0.0
23
+ npm install react react-dom lucide-react sonner
24
+ ```
66
25
 
67
- # 3. Build the distributable (skip if you added prepublishOnly above)
68
- npm run build
26
+ > `sonner` powers the success/error toasts. Make sure you render its
27
+ > `<Toaster />` once near the root of your app, otherwise toasts won't appear.
69
28
 
70
- # 4. Dry run — inspect exactly what will be shipped (should be dist/* + package.json)
71
- npm publish --dry-run --access public
29
+ ## Exports
72
30
 
73
- # 5. Publish
74
- npm publish --access public
31
+ ```ts
32
+ import {
33
+ FeedbackWidget, // the full widget: trigger button + panel + modals
34
+ RRWebPlayerModal, // standalone session-recording preview player
35
+ SessionRecordingPreview, // friendly alias for RRWebPlayerModal
36
+ } from '@pitangent/feedback-system';
75
37
 
76
- # 6. Verify
77
- npm view @wellnesspro/feedback
38
+ import type { RRWebPlayerModalProps } from '@pitangent/feedback-system';
78
39
  ```
79
40
 
80
- ---
41
+ ## Quick start
42
+
43
+ The recommended pattern is to create a small **wrapper component** that pulls
44
+ auth/tenant info from your app, then mount that wrapper once near the root of
45
+ your app (e.g. in your root layout). The widget renders a floating trigger
46
+ button and handles all panels/modals internally, and renders **nothing** when
47
+ `isAuthenticated` is `false`.
48
+
49
+ ### 1. Create a wrapper component
50
+
51
+ ```tsx
52
+ // FeedbackWidgetWrapper.tsx
53
+ 'use client';
54
+
55
+ import { FeedbackWidget } from '@pitangent/feedback-system';
56
+ import { useAuth } from '@/shared/hooks';
57
+ import { AuthService, tenantService } from '@/infrastructure/auth';
58
+
59
+ export const FeedbackWidgetWrapper = () => {
60
+ const { isAuthenticated, user } = useAuth();
61
+
62
+ // Resolve a fresh token on each request
63
+ const getAuthToken = async () => {
64
+ return await AuthService.getValidAccessToken();
65
+ };
66
+
67
+ // Resolve the current tenant (multi-tenant backends)
68
+ const getTenantId = () => {
69
+ return tenantService.getToken();
70
+ };
71
+
72
+ const feedbackApiUrl = process.env.NEXT_PUBLIC_FEEDBACK_API_URL;
73
+ const apiKey = process.env.NEXT_PUBLIC_FEEDBACK_API_KEY;
74
+
75
+ // Don't render until the widget is properly configured
76
+ if (!feedbackApiUrl || !apiKey) {
77
+ return null;
78
+ }
79
+
80
+ return (
81
+ <FeedbackWidget
82
+ isAuthenticated={isAuthenticated}
83
+ user={user} // { id, email_id } | null
84
+ apiUrl={feedbackApiUrl}
85
+ apiKey={apiKey}
86
+ getAuthToken={getAuthToken} // optional
87
+ getTenantId={getTenantId} // optional
88
+ />
89
+ );
90
+ };
91
+ ```
81
92
 
82
- ## Free account now, paid/private later what you can and can't do
93
+ ### 2. Mount it once in your root layout
94
+
95
+ ```tsx
96
+ // app/layout.tsx
97
+ import { FeedbackWidgetWrapper } from '@/shared/components/FeedbackWidgetWrapper';
98
+ import { Toaster } from 'sonner';
99
+
100
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
101
+ return (
102
+ <html lang="en">
103
+ <body>
104
+ {children}
105
+
106
+ {/* render once, near the root */}
107
+ <FeedbackWidgetWrapper />
108
+ <Toaster />
109
+ </body>
110
+ </html>
111
+ );
112
+ }
113
+ ```
83
114
 
84
- **You can publish this on a free npm account today**, and move to a paid/private
85
- plan later **without renaming the package**.
115
+ ### 3. Set the environment variables
86
116
 
87
- | Plan | What you can publish |
88
- | --- | --- |
89
- | **Free account** | Unlimited **public** packages, including scoped public ones (`@wellnesspro/feedback` with `--access public`). |
90
- | **Paid plan** (paid user or paid org) | Everything above **plus private packages**. |
117
+ ```ini
118
+ NEXT_PUBLIC_FEEDBACK_API_URL=https://your-feedback-backend.example.com
119
+ NEXT_PUBLIC_FEEDBACK_API_KEY=your-api-key
120
+ ```
91
121
 
92
- ### Now (free): publish as public
122
+ ## `<FeedbackWidget />` props
93
123
 
94
- ```bash
95
- npm publish --access public
96
- ```
124
+ | Prop | Type | Required | Description |
125
+ | --- | --- | --- | --- |
126
+ | `isAuthenticated` | `boolean` | ✅ | When `false`, the widget renders nothing. |
127
+ | `user` | `{ id: string; email_id: string } \| null` | ✅ | Current user; `id` and `email_id` are sent with every submission. |
128
+ | `apiUrl` | `string` | ✅ | Base URL of the feedback backend. |
129
+ | `apiKey` | `string` | ✅ | API key used when fetching ticket priorities. |
130
+ | `getAuthToken` | `() => Promise<string \| null> \| string \| null` | — | Resolves a fresh auth token per request (sent as a bearer token). |
131
+ | `getTenantId` | `() => string \| null` | — | Resolves the tenant ID for multi-tenant backends. |
97
132
 
98
- ### Later (paid): switch the same package to private
133
+ `getAuthToken` / `getTenantId` are called lazily on each request, so tokens are
134
+ always current — you don't need to re-render the widget when they change.
99
135
 
100
- Once you upgrade to a paid plan, flip the existing package's visibility — no
101
- rename, no republish needed:
136
+ ### What the widget submits
102
137
 
103
- ```bash
104
- npm access restricted @wellnesspro/feedback # public -> private
105
- # npm access public @wellnesspro/feedback # private -> public (reverse)
106
- ```
138
+ - **General feedback** — star rating + comment
139
+ - **Bug report** — title, description, priority, optional screenshot / recording / attachment
140
+ - **Feature request** title, description, optional attachment
107
141
 
108
- After that, only members of the org (or people you grant access) can install it.
142
+ Submissions are sent as `multipart/form-data` to your `apiUrl`. Screenshots and
143
+ rrweb recordings are attached automatically when captured.
109
144
 
110
- > ⚠️ **Caveat:** any version you published while it was public was already
111
- > downloadable, so making the package private only restricts **future** installs
112
- > — it does not retract copies people already have. If the code must never be
113
- > public, start on a paid/private plan from the first publish instead.
145
+ ## Standalone session player
114
146
 
115
- ### Private install requires auth
147
+ Use `RRWebPlayerModal` (aka `SessionRecordingPreview`) to replay a captured
148
+ rrweb event stream on its own, outside the widget:
116
149
 
117
- Once private, consumers (and CI) need an auth token in their `.npmrc`:
150
+ ```tsx
151
+ import { RRWebPlayerModal } from '@pitangent/feedback-system';
118
152
 
119
- ```ini
120
- //registry.npmjs.org/:_authToken=${NPM_TOKEN}
153
+ <RRWebPlayerModal
154
+ isOpen={open}
155
+ onClose={() => setOpen(false)}
156
+ events={recordingEvents}
157
+ />;
121
158
  ```
122
159
 
123
160
  ---
124
161
 
125
- ## Using a private GitLab registry instead
126
-
127
- Your repo lives on GitLab (`gitlab.pitangent.net`). If you'd rather host this in
128
- GitLab's package registry instead of npmjs.com, add a project-level `.npmrc`:
129
-
130
- ```ini
131
- @wellnesspro:registry=https://gitlab.pitangent.net/api/v4/projects/<PROJECT_ID>/packages/npm/
132
- //gitlab.pitangent.net/api/v4/projects/<PROJECT_ID>/packages/npm/:_authToken=${CI_JOB_TOKEN}
133
- ```
134
-
135
- Then `npm publish` (no `--access` flag needed). This keeps the package private to
136
- your GitLab group at no extra npm cost.
162
+ 📦 Maintaining this package? See [PUBLISHING.md](./PUBLISHING.md) for how to
163
+ publish and ship updates to npm.
package/dist/index.js CHANGED
@@ -54,4 +54,4 @@
54
54
  transform: ${a} !important;
55
55
  }
56
56
  `)});let A=document.createElement("style");A.id="temp-fixed-style",A.innerHTML=V.join(`
57
- `),document.head.appendChild(A);try{let s=document.documentElement;N=await(0,$e.toPng)(s,{width:window.innerWidth,height:window.innerHeight,canvasWidth:window.innerWidth,canvasHeight:window.innerHeight,pixelRatio:window.devicePixelRatio,filter:U=>{if(U instanceof HTMLElement||U&&"classList"in U){let z=U;if(z.classList&&z.classList.contains("feedback-no-capture"))return!1}return!0},style:{transform:`translate(-${u}px, -${D}px)`,transformOrigin:"top left",width:`${s.scrollWidth}px`,height:`${s.scrollHeight}px`}})}catch(s){console.warn("html2canvas direct capture failed, falling back to display media stream:",s);let U;try{U=await navigator.mediaDevices.getDisplayMedia({video:{displaySurface:"browser"},audio:!1,preferCurrentTab:!0,selfBrowserSurface:"include"})}catch(d){let a=d;if(a?.name==="NotAllowedError"||a?.name==="AbortError")throw a;U=await navigator.mediaDevices.getDisplayMedia({video:!0})}let z=document.createElement("video");z.srcObject=U,z.muted=!0,z.playsInline=!0,await new Promise(d=>{z.onloadedmetadata=()=>{z.play().then(()=>d()).catch(()=>d())},setTimeout(d,1e3)}),await new Promise(d=>setTimeout(d,150));let Z=document.createElement("canvas");Z.width=z.videoWidth||window.innerWidth,Z.height=z.videoHeight||window.innerHeight;let ie=Z.getContext("2d");ie&&(ie.drawImage(z,0,0,Z.width,Z.height),N=Z.toDataURL("image/png")),U.getTracks().forEach(d=>d.stop())}finally{A.remove(),W.forEach(s=>{s.removeAttribute("data-fixed-id")})}if(N)M(N),v(!0);else throw new Error("Could not generate screen capture data.")}catch(N){console.error("Failed to capture screenshot:",N);let W=N;if(W?.name==="NotAllowedError"||W?.name==="AbortError"){t(C.current||"general");return}be.toast.error("Capture Failed",{description:"Could not capture screenshot. Please try again."}),t(C.current||"general")}},F=async()=>{C.current=e,t(null),l(!1),Q(!0),O.current=[],await new Promise(N=>setTimeout(N,300));try{I.current=(0,He.record)({emit(N){O.current.push(N)},sampling:{mousemove:!0},blockClass:"feedback-no-capture",recordCanvas:!0}),console.log("[RRWeb Recorder] Recording started. Viewport:",{innerWidth:window.innerWidth,innerHeight:window.innerHeight,devicePixelRatio:window.devicePixelRatio})}catch(N){console.error("Failed to start rrweb recording:",N),be.toast.error("Failed to start session recording."),Q(!1),t(C.current||"general")}},X=()=>{if(I.current){try{I.current()}catch(N){console.error("Error stopping rrweb recording:",N)}I.current=void 0}if(Q(!1),O.current.length>0){let N=new Blob([JSON.stringify(O.current)],{type:"application/json"});b(N),m(URL.createObjectURL(N)),be.toast.success("Session recorded successfully!")}else be.toast.error("No events recorded during session.");t(C.current||"general")},S=()=>{r(null),b(null),H&&URL.revokeObjectURL(H),m(null),re(null),M(null),v(!1)};return{screenshotData:g,setScreenshotData:r,recordingBlob:y,setRecordingBlob:b,recordingUrl:H,setRecordingUrl:m,externalFile:w,setExternalFile:re,capturedImg:G,setCapturedImg:M,editorOpen:j,setEditorOpen:v,isRecording:$,prevActiveModal:C,recordingEvents:O.current,handleFileChange:R,captureScreenshot:ee,startRecording:F,stopRecording:X,clearAllMedia:S}};var Ce=Ke(require("axios")),Oe=async(e,t,l)=>{try{let g=l?.apiUrl,r="feedback";t==="bug"?r="bug-report":t==="feature"&&(r="feature-request");let y=`${g}/${r}`,b={};return l?.authToken&&(b.Authorization=`Bearer ${l.authToken}`),l?.tenantId&&(b["X-Tenant-ID"]=l.tenantId),l?.apiKey&&(b["x-api-key"]=l?.apiKey),(await Ce.default.post(y,e,{headers:b})).data}catch(g){throw console.error(`Error creating ${t} feedback:`,g),g}},Be=async e=>{try{let l=`${e?.apiUrl}/tickets/priorities`;return(await Ce.default.get(l)).data}catch(t){throw console.error("Error fetching ticket priorities:",t),t}};var pe=require("react/jsx-runtime"),at=(e,t)=>{let l=e.split(","),g=l[0].match(/:(.*?);/)?.[1]||"image/png",r=atob(l[1]),y=r.length,b=new Uint8Array(y);for(;y--;)b[y]=r.charCodeAt(y);return new File([b],t,{type:g})},Ue=({isAuthenticated:e,user:t,apiUrl:l,apiKey:g,getAuthToken:r,getTenantId:y})=>{let[b,H]=(0,ne.useState)(!1),[m,w]=(0,ne.useState)(null),[re,G]=(0,ne.useState)(!1),[M,j]=(0,ne.useState)(!1),[v,C]=(0,ne.useState)(0),[I,O]=(0,ne.useState)(0),[$,Q]=(0,ne.useState)(""),[R,ee]=(0,ne.useState)(""),[F,X]=(0,ne.useState)({}),[S,N]=(0,ne.useState)([]),[W,V]=(0,ne.useState)(""),u=(0,ne.useRef)(null),D=(0,ne.useRef)(null),A=(0,ne.useRef)(null),s=We({activeModal:m,setActiveModal:w,setPanelOpen:H});(0,ne.useEffect)(()=>{m==="bug"&&(async()=>{try{let f=r?await r():null,k=y?y():null,L=await Be({apiUrl:l,apiKey:g,authToken:f,tenantId:k});if(L&&L.data){N(L.data);let o=L.data.find(i=>i.name.toLowerCase()==="low");o?V(o.id):L.data.length>0&&V(L.data[0].id)}}catch(f){console.error("Failed to fetch priorities:",f)}})()},[m,l,r,y]),(0,ne.useEffect)(()=>{let a=f=>{u.current&&!u.current.contains(f.target)&&D.current&&!D.current.contains(f.target)&&H(!1)};return b&&document.addEventListener("mousedown",a),()=>{document.removeEventListener("mousedown",a)}},[b]);let U=a=>{w(a),H(!1),C(0),O(0),Q(""),ee(""),V(""),s.clearAllMedia(),A.current&&(A.current.value=""),X({})},z=()=>{w(null),C(0),O(0),Q(""),ee(""),V(""),s.clearAllMedia(),A.current&&(A.current.value=""),X({})},Z=()=>{let a={};return m==="general"?(v===0&&(a.rating="Please select a rating"),R.trim()?R.trim().length<5&&(a.description="Please enter at least 5 characters"):a.description="Please enter your feedback"):(m==="bug"||m==="feature")&&($.trim()?$.length>100&&(a.title="Title must be 100 characters or less"):a.title="Please enter a title",R.trim()?R.trim().length<10&&(a.description="Please enter at least 10 characters"):a.description="Please enter details",m==="bug"&&!W&&(a.priority="Please select a priority")),X(a),Object.keys(a).length===0},ie=async a=>{if(a.preventDefault(),!(!Z()||!m)){G(!0);try{let f=r?await r():null,k=y?y():null,L=new FormData;if(L.append("user_id",String(t?.id??"")),L.append("user_email",t?.email_id||""),m==="general"&&v>0&&L.append("ratting",String(v)),$&&L.append("title",$),R&&L.append("description",R),m==="bug"&&W&&L.append("ticketPriorityId",W),s.screenshotData)try{let c=at(s.screenshotData,"screenshot.png");L.append("screenshot",c)}catch(c){console.error("Failed to convert screenshot to file",c)}s.recordingEvents&&s.recordingEvents.length>0&&L.append("events",JSON.stringify(s.recordingEvents)),s.externalFile&&L.append("attachement",s.externalFile);let o=await Oe(L,m,{apiUrl:l,authToken:f,tenantId:k});console.log("res",o);let i="Thank you for your feedback!";m==="bug"?i="Thank you! The bug report has been submitted.":m==="feature"&&(i="Thank you! The feature request has been submitted."),ke.toast.success("Success",{description:i}),z()}catch(f){console.error("API submission error:",f),ke.toast.error("Submission Failed",{description:"An error occurred while submitting your feedback. Please try again."})}finally{G(!1)}}},d=a=>{F[a]&&X(f=>({...f,[a]:void 0}))};return e?(0,pe.jsxs)(pe.Fragment,{children:[(0,pe.jsx)(Ee,{buttonRef:D,onClick:()=>H(!b)}),(0,pe.jsx)(Me,{panelRef:u,isOpen:b,onClose:()=>H(!1),onOpenModal:U}),(0,pe.jsx)(Pe,{activeModal:m,onClose:z,onSubmit:ie,userEmail:t?.email_id||"",submitting:re,rating:v,setRating:C,hoverRating:I,setHoverRating:O,title:$,setTitle:Q,description:R,setDescription:ee,screenshotData:s.screenshotData,setScreenshotData:s.setScreenshotData,recordingBlob:s.recordingBlob,onRemoveRecording:()=>{s.setRecordingBlob(null),s.setRecordingUrl(null)},onPreviewRecording:()=>setTimeout(()=>j(!0),100),externalFile:s.externalFile,setExternalFile:s.setExternalFile,fileInputRef:A,handleFileChange:s.handleFileChange,onCaptureScreenshot:s.captureScreenshot,onStartRecording:s.startRecording,errors:F,onClearError:d,priorities:S,selectedPriorityId:W,setSelectedPriorityId:V}),(0,pe.jsx)(Ae,{isRecording:s.isRecording,onStopRecording:s.stopRecording}),(0,pe.jsx)(Le,{isOpen:s.editorOpen,capturedImg:s.capturedImg||"",onClose:()=>{s.setEditorOpen(!1),w(s.prevActiveModal.current||"general")},onSave:a=>{s.setScreenshotData(a),s.setEditorOpen(!1),w(s.prevActiveModal.current||"general"),ke.toast.success("Screenshot attached successfully!")}}),(0,pe.jsx)(ve,{isOpen:M,onClose:()=>j(!1),events:s.recordingEvents})]}):null};0&&(module.exports={FeedbackWidget,RRWebPlayerModal,SessionRecordingPreview});
57
+ `),document.head.appendChild(A);try{let s=document.documentElement;N=await(0,$e.toPng)(s,{width:window.innerWidth,height:window.innerHeight,canvasWidth:window.innerWidth,canvasHeight:window.innerHeight,pixelRatio:window.devicePixelRatio,filter:U=>{if(U instanceof HTMLElement||U&&"classList"in U){let z=U;if(z.classList&&z.classList.contains("feedback-no-capture"))return!1}return!0},style:{transform:`translate(-${u}px, -${D}px)`,transformOrigin:"top left",width:`${s.scrollWidth}px`,height:`${s.scrollHeight}px`}})}catch(s){console.warn("html2canvas direct capture failed, falling back to display media stream:",s);let U;try{U=await navigator.mediaDevices.getDisplayMedia({video:{displaySurface:"browser"},audio:!1,preferCurrentTab:!0,selfBrowserSurface:"include"})}catch(d){let a=d;if(a?.name==="NotAllowedError"||a?.name==="AbortError")throw a;U=await navigator.mediaDevices.getDisplayMedia({video:!0})}let z=document.createElement("video");z.srcObject=U,z.muted=!0,z.playsInline=!0,await new Promise(d=>{z.onloadedmetadata=()=>{z.play().then(()=>d()).catch(()=>d())},setTimeout(d,1e3)}),await new Promise(d=>setTimeout(d,150));let Z=document.createElement("canvas");Z.width=z.videoWidth||window.innerWidth,Z.height=z.videoHeight||window.innerHeight;let ie=Z.getContext("2d");ie&&(ie.drawImage(z,0,0,Z.width,Z.height),N=Z.toDataURL("image/png")),U.getTracks().forEach(d=>d.stop())}finally{A.remove(),W.forEach(s=>{s.removeAttribute("data-fixed-id")})}if(N)M(N),v(!0);else throw new Error("Could not generate screen capture data.")}catch(N){console.error("Failed to capture screenshot:",N);let W=N;if(W?.name==="NotAllowedError"||W?.name==="AbortError"){t(C.current||"general");return}be.toast.error("Capture Failed",{description:"Could not capture screenshot. Please try again."}),t(C.current||"general")}},F=async()=>{C.current=e,t(null),l(!1),Q(!0),O.current=[],await new Promise(N=>setTimeout(N,300));try{I.current=(0,He.record)({emit(N){O.current.push(N)},sampling:{mousemove:!0},blockClass:"feedback-no-capture",recordCanvas:!0}),console.log("[RRWeb Recorder] Recording started. Viewport:",{innerWidth:window.innerWidth,innerHeight:window.innerHeight,devicePixelRatio:window.devicePixelRatio})}catch(N){console.error("Failed to start rrweb recording:",N),be.toast.error("Failed to start session recording."),Q(!1),t(C.current||"general")}},X=()=>{if(I.current){try{I.current()}catch(N){console.error("Error stopping rrweb recording:",N)}I.current=void 0}if(Q(!1),O.current.length>0){let N=new Blob([JSON.stringify(O.current)],{type:"application/json"});b(N),m(URL.createObjectURL(N)),be.toast.success("Session recorded successfully!")}else be.toast.error("No events recorded during session.");t(C.current||"general")},S=()=>{r(null),b(null),H&&URL.revokeObjectURL(H),m(null),re(null),M(null),v(!1)};return{screenshotData:g,setScreenshotData:r,recordingBlob:y,setRecordingBlob:b,recordingUrl:H,setRecordingUrl:m,externalFile:w,setExternalFile:re,capturedImg:G,setCapturedImg:M,editorOpen:j,setEditorOpen:v,isRecording:$,prevActiveModal:C,recordingEvents:O.current,handleFileChange:R,captureScreenshot:ee,startRecording:F,stopRecording:X,clearAllMedia:S}};var Ce=Ke(require("axios")),Oe=async(e,t,l)=>{try{let g=l?.apiUrl,r="feedback";t==="bug"?r="bug-report":t==="feature"&&(r="feature-request");let y=`${g}/${r}`,b={};return l?.authToken&&(b.Authorization=`Bearer ${l.authToken}`),l?.tenantId&&(b["X-Tenant-ID"]=l.tenantId),l?.apiKey&&(b["x-api-key"]=l?.apiKey),(await Ce.default.post(y,e,{headers:b})).data}catch(g){throw console.error(`Error creating ${t} feedback:`,g),g}},Be=async e=>{try{let l=`${e?.apiUrl}/tickets/priorities`;return(await Ce.default.get(l)).data}catch(t){throw console.error("Error fetching ticket priorities:",t),t}};var pe=require("react/jsx-runtime"),at=(e,t)=>{let l=e.split(","),g=l[0].match(/:(.*?);/)?.[1]||"image/png",r=atob(l[1]),y=r.length,b=new Uint8Array(y);for(;y--;)b[y]=r.charCodeAt(y);return new File([b],t,{type:g})},Ue=({isAuthenticated:e,user:t,apiUrl:l,apiKey:g,getAuthToken:r,getTenantId:y})=>{let[b,H]=(0,ne.useState)(!1),[m,w]=(0,ne.useState)(null),[re,G]=(0,ne.useState)(!1),[M,j]=(0,ne.useState)(!1),[v,C]=(0,ne.useState)(0),[I,O]=(0,ne.useState)(0),[$,Q]=(0,ne.useState)(""),[R,ee]=(0,ne.useState)(""),[F,X]=(0,ne.useState)({}),[S,N]=(0,ne.useState)([]),[W,V]=(0,ne.useState)(""),u=(0,ne.useRef)(null),D=(0,ne.useRef)(null),A=(0,ne.useRef)(null),s=We({activeModal:m,setActiveModal:w,setPanelOpen:H});(0,ne.useEffect)(()=>{m==="bug"&&(async()=>{try{let f=r?await r():null,k=y?y():null,L=await Be({apiUrl:l,authToken:f,tenantId:k});if(L&&L.data){N(L.data);let o=L.data.find(i=>i.name.toLowerCase()==="low");o?V(o.id):L.data.length>0&&V(L.data[0].id)}}catch(f){console.error("Failed to fetch priorities:",f)}})()},[m,l,r,y]),(0,ne.useEffect)(()=>{let a=f=>{u.current&&!u.current.contains(f.target)&&D.current&&!D.current.contains(f.target)&&H(!1)};return b&&document.addEventListener("mousedown",a),()=>{document.removeEventListener("mousedown",a)}},[b]);let U=a=>{w(a),H(!1),C(0),O(0),Q(""),ee(""),V(""),s.clearAllMedia(),A.current&&(A.current.value=""),X({})},z=()=>{w(null),C(0),O(0),Q(""),ee(""),V(""),s.clearAllMedia(),A.current&&(A.current.value=""),X({})},Z=()=>{let a={};return m==="general"?(v===0&&(a.rating="Please select a rating"),R.trim()?R.trim().length<5&&(a.description="Please enter at least 5 characters"):a.description="Please enter your feedback"):(m==="bug"||m==="feature")&&($.trim()?$.length>100&&(a.title="Title must be 100 characters or less"):a.title="Please enter a title",R.trim()?R.trim().length<10&&(a.description="Please enter at least 10 characters"):a.description="Please enter details",m==="bug"&&!W&&(a.priority="Please select a priority")),X(a),Object.keys(a).length===0},ie=async a=>{if(a.preventDefault(),!(!Z()||!m)){G(!0);try{let f=r?await r():null,k=y?y():null,L=new FormData;if(L.append("user_id",String(t?.id??"")),L.append("user_email",t?.email_id||""),m==="general"&&v>0&&L.append("ratting",String(v)),$&&L.append("title",$),R&&L.append("description",R),m==="bug"&&W&&L.append("ticketPriorityId",W),s.screenshotData)try{let c=at(s.screenshotData,"screenshot.png");L.append("screenshot",c)}catch(c){console.error("Failed to convert screenshot to file",c)}s.recordingEvents&&s.recordingEvents.length>0&&L.append("events",JSON.stringify(s.recordingEvents)),s.externalFile&&L.append("attachement",s.externalFile);let o=await Oe(L,m,{apiUrl:l,apiKey:g,authToken:f,tenantId:k});console.log("res",o);let i="Thank you for your feedback!";m==="bug"?i="Thank you! The bug report has been submitted.":m==="feature"&&(i="Thank you! The feature request has been submitted."),ke.toast.success("Success",{description:i}),z()}catch(f){console.error("API submission error:",f),ke.toast.error("Submission Failed",{description:"An error occurred while submitting your feedback. Please try again."})}finally{G(!1)}}},d=a=>{F[a]&&X(f=>({...f,[a]:void 0}))};return e?(0,pe.jsxs)(pe.Fragment,{children:[(0,pe.jsx)(Ee,{buttonRef:D,onClick:()=>H(!b)}),(0,pe.jsx)(Me,{panelRef:u,isOpen:b,onClose:()=>H(!1),onOpenModal:U}),(0,pe.jsx)(Pe,{activeModal:m,onClose:z,onSubmit:ie,userEmail:t?.email_id||"",submitting:re,rating:v,setRating:C,hoverRating:I,setHoverRating:O,title:$,setTitle:Q,description:R,setDescription:ee,screenshotData:s.screenshotData,setScreenshotData:s.setScreenshotData,recordingBlob:s.recordingBlob,onRemoveRecording:()=>{s.setRecordingBlob(null),s.setRecordingUrl(null)},onPreviewRecording:()=>setTimeout(()=>j(!0),100),externalFile:s.externalFile,setExternalFile:s.setExternalFile,fileInputRef:A,handleFileChange:s.handleFileChange,onCaptureScreenshot:s.captureScreenshot,onStartRecording:s.startRecording,errors:F,onClearError:d,priorities:S,selectedPriorityId:W,setSelectedPriorityId:V}),(0,pe.jsx)(Ae,{isRecording:s.isRecording,onStopRecording:s.stopRecording}),(0,pe.jsx)(Le,{isOpen:s.editorOpen,capturedImg:s.capturedImg||"",onClose:()=>{s.setEditorOpen(!1),w(s.prevActiveModal.current||"general")},onSave:a=>{s.setScreenshotData(a),s.setEditorOpen(!1),w(s.prevActiveModal.current||"general"),ke.toast.success("Screenshot attached successfully!")}}),(0,pe.jsx)(ve,{isOpen:M,onClose:()=>j(!1),events:s.recordingEvents})]}):null};0&&(module.exports={FeedbackWidget,RRWebPlayerModal,SessionRecordingPreview});
package/dist/index.mjs CHANGED
@@ -54,4 +54,4 @@ import{useEffect as Ze,useRef as Fe,useState as ie}from"react";import{MessageSqu
54
54
  transform: ${r} !important;
55
55
  }
56
56
  `)});let F=document.createElement("style");F.id="temp-fixed-style",F.innerHTML=Y.join(`
57
- `),document.head.appendChild(F);try{let o=document.documentElement;N=await jt(o,{width:window.innerWidth,height:window.innerHeight,canvasWidth:window.innerWidth,canvasHeight:window.innerHeight,pixelRatio:window.devicePixelRatio,filter:B=>{if(B instanceof HTMLElement||B&&"classList"in B){let z=B;if(z.classList&&z.classList.contains("feedback-no-capture"))return!1}return!0},style:{transform:`translate(-${d}px, -${E}px)`,transformOrigin:"top left",width:`${o.scrollWidth}px`,height:`${o.scrollHeight}px`}})}catch(o){console.warn("html2canvas direct capture failed, falling back to display media stream:",o);let B;try{B=await navigator.mediaDevices.getDisplayMedia({video:{displaySurface:"browser"},audio:!1,preferCurrentTab:!0,selfBrowserSurface:"include"})}catch(l){let r=l;if(r?.name==="NotAllowedError"||r?.name==="AbortError")throw r;B=await navigator.mediaDevices.getDisplayMedia({video:!0})}let z=document.createElement("video");z.srcObject=B,z.muted=!0,z.playsInline=!0,await new Promise(l=>{z.onloadedmetadata=()=>{z.play().then(()=>l()).catch(()=>l())},setTimeout(l,1e3)}),await new Promise(l=>setTimeout(l,150));let V=document.createElement("canvas");V.width=z.videoWidth||window.innerWidth,V.height=z.videoHeight||window.innerHeight;let ne=V.getContext("2d");ne&&(ne.drawImage(z,0,0,V.width,V.height),N=V.toDataURL("image/png")),B.getTracks().forEach(l=>l.stop())}finally{F.remove(),H.forEach(o=>{o.removeAttribute("data-fixed-id")})}if(N)P(N),y(!0);else throw new Error("Could not generate screen capture data.")}catch(N){console.error("Failed to capture screenshot:",N);let H=N;if(H?.name==="NotAllowedError"||H?.name==="AbortError"){t(C.current||"general");return}be.error("Capture Failed",{description:"Could not capture screenshot. Please try again."}),t(C.current||"general")}},T=async()=>{C.current=e,t(null),c(!1),K(!0),W.current=[],await new Promise(N=>setTimeout(N,300));try{D.current=zt({emit(N){W.current.push(N)},sampling:{mousemove:!0},blockClass:"feedback-no-capture",recordCanvas:!0}),console.log("[RRWeb Recorder] Recording started. Viewport:",{innerWidth:window.innerWidth,innerHeight:window.innerHeight,devicePixelRatio:window.devicePixelRatio})}catch(N){console.error("Failed to start rrweb recording:",N),be.error("Failed to start session recording."),K(!1),t(C.current||"general")}},X=()=>{if(D.current){try{D.current()}catch(N){console.error("Error stopping rrweb recording:",N)}D.current=void 0}if(K(!1),W.current.length>0){let N=new Blob([JSON.stringify(W.current)],{type:"application/json"});b(N),m(URL.createObjectURL(N)),be.success("Session recorded successfully!")}else be.error("No events recorded during session.");t(C.current||"general")},S=()=>{n(null),b(null),$&&URL.revokeObjectURL($),m(null),G(null),P(null),y(!1)};return{screenshotData:g,setScreenshotData:n,recordingBlob:x,setRecordingBlob:b,recordingUrl:$,setRecordingUrl:m,externalFile:v,setExternalFile:G,capturedImg:q,setCapturedImg:P,editorOpen:j,setEditorOpen:y,isRecording:L,prevActiveModal:C,recordingEvents:W.current,handleFileChange:R,captureScreenshot:J,startRecording:T,stopRecording:X,clearAllMedia:S}};import Je from"axios";var Ge=async(e,t,c)=>{try{let g=c?.apiUrl,n="feedback";t==="bug"?n="bug-report":t==="feature"&&(n="feature-request");let x=`${g}/${n}`,b={};return c?.authToken&&(b.Authorization=`Bearer ${c.authToken}`),c?.tenantId&&(b["X-Tenant-ID"]=c.tenantId),c?.apiKey&&(b["x-api-key"]=c?.apiKey),(await Je.post(x,e,{headers:b})).data}catch(g){throw console.error(`Error creating ${t} feedback:`,g),g}},Qe=async e=>{try{let c=`${e?.apiUrl}/tickets/priorities`;return(await Je.get(c)).data}catch(t){throw console.error("Error fetching ticket priorities:",t),t}};import{Fragment as _t,jsx as xe,jsxs as qt}from"react/jsx-runtime";var Xt=(e,t)=>{let c=e.split(","),g=c[0].match(/:(.*?);/)?.[1]||"image/png",n=atob(c[1]),x=n.length,b=new Uint8Array(x);for(;x--;)b[x]=n.charCodeAt(x);return new File([b],t,{type:g})},Yt=({isAuthenticated:e,user:t,apiUrl:c,apiKey:g,getAuthToken:n,getTenantId:x})=>{let[b,$]=ie(!1),[m,v]=ie(null),[G,q]=ie(!1),[P,j]=ie(!1),[y,C]=ie(0),[D,W]=ie(0),[L,K]=ie(""),[R,J]=ie(""),[T,X]=ie({}),[S,N]=ie([]),[H,Y]=ie(""),d=Fe(null),E=Fe(null),F=Fe(null),o=Ve({activeModal:m,setActiveModal:v,setPanelOpen:$});Ze(()=>{m==="bug"&&(async()=>{try{let f=n?await n():null,w=x?x():null,A=await Qe({apiUrl:c,apiKey:g,authToken:f,tenantId:w});if(A&&A.data){N(A.data);let a=A.data.find(s=>s.name.toLowerCase()==="low");a?Y(a.id):A.data.length>0&&Y(A.data[0].id)}}catch(f){console.error("Failed to fetch priorities:",f)}})()},[m,c,n,x]),Ze(()=>{let r=f=>{d.current&&!d.current.contains(f.target)&&E.current&&!E.current.contains(f.target)&&$(!1)};return b&&document.addEventListener("mousedown",r),()=>{document.removeEventListener("mousedown",r)}},[b]);let B=r=>{v(r),$(!1),C(0),W(0),K(""),J(""),Y(""),o.clearAllMedia(),F.current&&(F.current.value=""),X({})},z=()=>{v(null),C(0),W(0),K(""),J(""),Y(""),o.clearAllMedia(),F.current&&(F.current.value=""),X({})},V=()=>{let r={};return m==="general"?(y===0&&(r.rating="Please select a rating"),R.trim()?R.trim().length<5&&(r.description="Please enter at least 5 characters"):r.description="Please enter your feedback"):(m==="bug"||m==="feature")&&(L.trim()?L.length>100&&(r.title="Title must be 100 characters or less"):r.title="Please enter a title",R.trim()?R.trim().length<10&&(r.description="Please enter at least 10 characters"):r.description="Please enter details",m==="bug"&&!H&&(r.priority="Please select a priority")),X(r),Object.keys(r).length===0},ne=async r=>{if(r.preventDefault(),!(!V()||!m)){q(!0);try{let f=n?await n():null,w=x?x():null,A=new FormData;if(A.append("user_id",String(t?.id??"")),A.append("user_email",t?.email_id||""),m==="general"&&y>0&&A.append("ratting",String(y)),L&&A.append("title",L),R&&A.append("description",R),m==="bug"&&H&&A.append("ticketPriorityId",H),o.screenshotData)try{let i=Xt(o.screenshotData,"screenshot.png");A.append("screenshot",i)}catch(i){console.error("Failed to convert screenshot to file",i)}o.recordingEvents&&o.recordingEvents.length>0&&A.append("events",JSON.stringify(o.recordingEvents)),o.externalFile&&A.append("attachement",o.externalFile);let a=await Ge(A,m,{apiUrl:c,authToken:f,tenantId:w});console.log("res",a);let s="Thank you for your feedback!";m==="bug"?s="Thank you! The bug report has been submitted.":m==="feature"&&(s="Thank you! The feature request has been submitted."),De.success("Success",{description:s}),z()}catch(f){console.error("API submission error:",f),De.error("Submission Failed",{description:"An error occurred while submitting your feedback. Please try again."})}finally{q(!1)}}},l=r=>{T[r]&&X(f=>({...f,[r]:void 0}))};return e?qt(_t,{children:[xe(Oe,{buttonRef:E,onClick:()=>$(!b)}),xe(We,{panelRef:d,isOpen:b,onClose:()=>$(!1),onOpenModal:B}),xe(He,{activeModal:m,onClose:z,onSubmit:ne,userEmail:t?.email_id||"",submitting:G,rating:y,setRating:C,hoverRating:D,setHoverRating:W,title:L,setTitle:K,description:R,setDescription:J,screenshotData:o.screenshotData,setScreenshotData:o.setScreenshotData,recordingBlob:o.recordingBlob,onRemoveRecording:()=>{o.setRecordingBlob(null),o.setRecordingUrl(null)},onPreviewRecording:()=>setTimeout(()=>j(!0),100),externalFile:o.externalFile,setExternalFile:o.setExternalFile,fileInputRef:F,handleFileChange:o.handleFileChange,onCaptureScreenshot:o.captureScreenshot,onStartRecording:o.startRecording,errors:T,onClearError:l,priorities:S,selectedPriorityId:H,setSelectedPriorityId:Y}),xe(Ye,{isRecording:o.isRecording,onStopRecording:o.stopRecording}),xe(Ke,{isOpen:o.editorOpen,capturedImg:o.capturedImg||"",onClose:()=>{o.setEditorOpen(!1),v(o.prevActiveModal.current||"general")},onSave:r=>{o.setScreenshotData(r),o.setEditorOpen(!1),v(o.prevActiveModal.current||"general"),De.success("Screenshot attached successfully!")}}),xe(Ne,{isOpen:P,onClose:()=>j(!1),events:o.recordingEvents})]}):null};export{Yt as FeedbackWidget,Ne as RRWebPlayerModal,Ne as SessionRecordingPreview};
57
+ `),document.head.appendChild(F);try{let o=document.documentElement;N=await jt(o,{width:window.innerWidth,height:window.innerHeight,canvasWidth:window.innerWidth,canvasHeight:window.innerHeight,pixelRatio:window.devicePixelRatio,filter:B=>{if(B instanceof HTMLElement||B&&"classList"in B){let z=B;if(z.classList&&z.classList.contains("feedback-no-capture"))return!1}return!0},style:{transform:`translate(-${d}px, -${E}px)`,transformOrigin:"top left",width:`${o.scrollWidth}px`,height:`${o.scrollHeight}px`}})}catch(o){console.warn("html2canvas direct capture failed, falling back to display media stream:",o);let B;try{B=await navigator.mediaDevices.getDisplayMedia({video:{displaySurface:"browser"},audio:!1,preferCurrentTab:!0,selfBrowserSurface:"include"})}catch(l){let r=l;if(r?.name==="NotAllowedError"||r?.name==="AbortError")throw r;B=await navigator.mediaDevices.getDisplayMedia({video:!0})}let z=document.createElement("video");z.srcObject=B,z.muted=!0,z.playsInline=!0,await new Promise(l=>{z.onloadedmetadata=()=>{z.play().then(()=>l()).catch(()=>l())},setTimeout(l,1e3)}),await new Promise(l=>setTimeout(l,150));let V=document.createElement("canvas");V.width=z.videoWidth||window.innerWidth,V.height=z.videoHeight||window.innerHeight;let ne=V.getContext("2d");ne&&(ne.drawImage(z,0,0,V.width,V.height),N=V.toDataURL("image/png")),B.getTracks().forEach(l=>l.stop())}finally{F.remove(),H.forEach(o=>{o.removeAttribute("data-fixed-id")})}if(N)P(N),y(!0);else throw new Error("Could not generate screen capture data.")}catch(N){console.error("Failed to capture screenshot:",N);let H=N;if(H?.name==="NotAllowedError"||H?.name==="AbortError"){t(C.current||"general");return}be.error("Capture Failed",{description:"Could not capture screenshot. Please try again."}),t(C.current||"general")}},T=async()=>{C.current=e,t(null),c(!1),K(!0),W.current=[],await new Promise(N=>setTimeout(N,300));try{D.current=zt({emit(N){W.current.push(N)},sampling:{mousemove:!0},blockClass:"feedback-no-capture",recordCanvas:!0}),console.log("[RRWeb Recorder] Recording started. Viewport:",{innerWidth:window.innerWidth,innerHeight:window.innerHeight,devicePixelRatio:window.devicePixelRatio})}catch(N){console.error("Failed to start rrweb recording:",N),be.error("Failed to start session recording."),K(!1),t(C.current||"general")}},X=()=>{if(D.current){try{D.current()}catch(N){console.error("Error stopping rrweb recording:",N)}D.current=void 0}if(K(!1),W.current.length>0){let N=new Blob([JSON.stringify(W.current)],{type:"application/json"});b(N),m(URL.createObjectURL(N)),be.success("Session recorded successfully!")}else be.error("No events recorded during session.");t(C.current||"general")},S=()=>{n(null),b(null),$&&URL.revokeObjectURL($),m(null),G(null),P(null),y(!1)};return{screenshotData:g,setScreenshotData:n,recordingBlob:x,setRecordingBlob:b,recordingUrl:$,setRecordingUrl:m,externalFile:v,setExternalFile:G,capturedImg:q,setCapturedImg:P,editorOpen:j,setEditorOpen:y,isRecording:L,prevActiveModal:C,recordingEvents:W.current,handleFileChange:R,captureScreenshot:J,startRecording:T,stopRecording:X,clearAllMedia:S}};import Je from"axios";var Ge=async(e,t,c)=>{try{let g=c?.apiUrl,n="feedback";t==="bug"?n="bug-report":t==="feature"&&(n="feature-request");let x=`${g}/${n}`,b={};return c?.authToken&&(b.Authorization=`Bearer ${c.authToken}`),c?.tenantId&&(b["X-Tenant-ID"]=c.tenantId),c?.apiKey&&(b["x-api-key"]=c?.apiKey),(await Je.post(x,e,{headers:b})).data}catch(g){throw console.error(`Error creating ${t} feedback:`,g),g}},Qe=async e=>{try{let c=`${e?.apiUrl}/tickets/priorities`;return(await Je.get(c)).data}catch(t){throw console.error("Error fetching ticket priorities:",t),t}};import{Fragment as _t,jsx as xe,jsxs as qt}from"react/jsx-runtime";var Xt=(e,t)=>{let c=e.split(","),g=c[0].match(/:(.*?);/)?.[1]||"image/png",n=atob(c[1]),x=n.length,b=new Uint8Array(x);for(;x--;)b[x]=n.charCodeAt(x);return new File([b],t,{type:g})},Yt=({isAuthenticated:e,user:t,apiUrl:c,apiKey:g,getAuthToken:n,getTenantId:x})=>{let[b,$]=ie(!1),[m,v]=ie(null),[G,q]=ie(!1),[P,j]=ie(!1),[y,C]=ie(0),[D,W]=ie(0),[L,K]=ie(""),[R,J]=ie(""),[T,X]=ie({}),[S,N]=ie([]),[H,Y]=ie(""),d=Fe(null),E=Fe(null),F=Fe(null),o=Ve({activeModal:m,setActiveModal:v,setPanelOpen:$});Ze(()=>{m==="bug"&&(async()=>{try{let f=n?await n():null,w=x?x():null,A=await Qe({apiUrl:c,authToken:f,tenantId:w});if(A&&A.data){N(A.data);let a=A.data.find(s=>s.name.toLowerCase()==="low");a?Y(a.id):A.data.length>0&&Y(A.data[0].id)}}catch(f){console.error("Failed to fetch priorities:",f)}})()},[m,c,n,x]),Ze(()=>{let r=f=>{d.current&&!d.current.contains(f.target)&&E.current&&!E.current.contains(f.target)&&$(!1)};return b&&document.addEventListener("mousedown",r),()=>{document.removeEventListener("mousedown",r)}},[b]);let B=r=>{v(r),$(!1),C(0),W(0),K(""),J(""),Y(""),o.clearAllMedia(),F.current&&(F.current.value=""),X({})},z=()=>{v(null),C(0),W(0),K(""),J(""),Y(""),o.clearAllMedia(),F.current&&(F.current.value=""),X({})},V=()=>{let r={};return m==="general"?(y===0&&(r.rating="Please select a rating"),R.trim()?R.trim().length<5&&(r.description="Please enter at least 5 characters"):r.description="Please enter your feedback"):(m==="bug"||m==="feature")&&(L.trim()?L.length>100&&(r.title="Title must be 100 characters or less"):r.title="Please enter a title",R.trim()?R.trim().length<10&&(r.description="Please enter at least 10 characters"):r.description="Please enter details",m==="bug"&&!H&&(r.priority="Please select a priority")),X(r),Object.keys(r).length===0},ne=async r=>{if(r.preventDefault(),!(!V()||!m)){q(!0);try{let f=n?await n():null,w=x?x():null,A=new FormData;if(A.append("user_id",String(t?.id??"")),A.append("user_email",t?.email_id||""),m==="general"&&y>0&&A.append("ratting",String(y)),L&&A.append("title",L),R&&A.append("description",R),m==="bug"&&H&&A.append("ticketPriorityId",H),o.screenshotData)try{let i=Xt(o.screenshotData,"screenshot.png");A.append("screenshot",i)}catch(i){console.error("Failed to convert screenshot to file",i)}o.recordingEvents&&o.recordingEvents.length>0&&A.append("events",JSON.stringify(o.recordingEvents)),o.externalFile&&A.append("attachement",o.externalFile);let a=await Ge(A,m,{apiUrl:c,apiKey:g,authToken:f,tenantId:w});console.log("res",a);let s="Thank you for your feedback!";m==="bug"?s="Thank you! The bug report has been submitted.":m==="feature"&&(s="Thank you! The feature request has been submitted."),De.success("Success",{description:s}),z()}catch(f){console.error("API submission error:",f),De.error("Submission Failed",{description:"An error occurred while submitting your feedback. Please try again."})}finally{q(!1)}}},l=r=>{T[r]&&X(f=>({...f,[r]:void 0}))};return e?qt(_t,{children:[xe(Oe,{buttonRef:E,onClick:()=>$(!b)}),xe(We,{panelRef:d,isOpen:b,onClose:()=>$(!1),onOpenModal:B}),xe(He,{activeModal:m,onClose:z,onSubmit:ne,userEmail:t?.email_id||"",submitting:G,rating:y,setRating:C,hoverRating:D,setHoverRating:W,title:L,setTitle:K,description:R,setDescription:J,screenshotData:o.screenshotData,setScreenshotData:o.setScreenshotData,recordingBlob:o.recordingBlob,onRemoveRecording:()=>{o.setRecordingBlob(null),o.setRecordingUrl(null)},onPreviewRecording:()=>setTimeout(()=>j(!0),100),externalFile:o.externalFile,setExternalFile:o.setExternalFile,fileInputRef:F,handleFileChange:o.handleFileChange,onCaptureScreenshot:o.captureScreenshot,onStartRecording:o.startRecording,errors:T,onClearError:l,priorities:S,selectedPriorityId:H,setSelectedPriorityId:Y}),xe(Ye,{isRecording:o.isRecording,onStopRecording:o.stopRecording}),xe(Ke,{isOpen:o.editorOpen,capturedImg:o.capturedImg||"",onClose:()=>{o.setEditorOpen(!1),v(o.prevActiveModal.current||"general")},onSave:r=>{o.setScreenshotData(r),o.setEditorOpen(!1),v(o.prevActiveModal.current||"general"),De.success("Screenshot attached successfully!")}}),xe(Ne,{isOpen:P,onClose:()=>j(!1),events:o.recordingEvents})]}):null};export{Yt as FeedbackWidget,Ne as RRWebPlayerModal,Ne as SessionRecordingPreview};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pitangent/feedback-system",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.ts",