1- import clsx from ' clsx' ;
2- import { useLocation } from ' @docusaurus/router' ;
3- import React , { useEffect , useMemo , useState } from ' react' ;
1+ import clsx from " clsx" ;
2+ import { useLocation } from " @docusaurus/router" ;
3+ import React , { useEffect , useMemo , useState } from " react" ;
44
5- import styles from ' ./styles.module.css' ;
5+ import styles from " ./styles.module.css" ;
66
7- const CSAT_STORAGE_PREFIX = ' devvit_docs_csat_submitted:' ;
7+ const CSAT_STORAGE_PREFIX = " devvit_docs_csat_submitted:" ;
88
99declare global {
1010 interface Window {
1111 sendV2Event ?: ( event : unknown ) => void ;
1212 }
1313}
1414
15+ type CsatReaction = "positive" | "negative" ;
16+ type CsatScore = 1 | 5 ;
17+
1518function getStorageKey ( pathname : string ) : string {
1619 return `${ CSAT_STORAGE_PREFIX } ${ pathname } ` ;
1720}
@@ -36,59 +39,71 @@ function getReferrerInfo(): { url?: string; domain?: string } {
3639
3740type ScoreOption = {
3841 label : string ;
39- value : number ;
42+ value : CsatScore ;
4043 emoji : string ;
41- reaction : 'positive' | 'negative' ;
44+ reaction : CsatReaction ;
4245} ;
4346
4447const SCORE_OPTIONS : ScoreOption [ ] = [
4548 {
46- label : ' Thumbs down' ,
49+ label : " Thumbs down" ,
4750 value : 1 ,
48- emoji : '👎' ,
49- reaction : ' negative' ,
51+ emoji : "👎" ,
52+ reaction : " negative" ,
5053 } ,
5154 {
52- label : ' Thumbs up' ,
55+ label : " Thumbs up" ,
5356 value : 5 ,
54- emoji : '👍' ,
55- reaction : ' positive' ,
57+ emoji : "👍" ,
58+ reaction : " positive" ,
5659 } ,
5760] ;
5861
5962const NEGATIVE_REASON_OPTIONS = [
60- ' The page is unclear' ,
61- ' Important details are missing' ,
62- ' The instructions did not work' ,
63+ " The page is unclear" ,
64+ " Important details are missing" ,
65+ " The instructions did not work" ,
6366] ;
6467
6568export default function CsatWidget ( ) : React . ReactElement | null {
6669 const { pathname } = useLocation ( ) ;
6770 const [ selectedScore , setSelectedScore ] = useState < number | null > ( null ) ;
68- const [ feedback , setFeedback ] = useState ( '' ) ;
71+ const [ feedback , setFeedback ] = useState ( "" ) ;
6972 const [ selectedReasons , setSelectedReasons ] = useState < string [ ] > ( [ ] ) ;
7073 const [ isVisible , setIsVisible ] = useState ( false ) ;
7174 const [ isSubmitted , setIsSubmitted ] = useState ( false ) ;
7275
7376 const storageKey = useMemo ( ( ) => getStorageKey ( pathname ) , [ pathname ] ) ;
7477 const shouldShowForPath = useMemo ( ( ) => {
75- const normalizedPath = pathname . replace ( / \/ + $ / , '' ) || '/' ;
76- return normalizedPath !== '/' && normalizedPath !== ' /docs' ;
78+ const normalizedPath = pathname . replace ( / \/ + $ / , "" ) || "/" ;
79+ return normalizedPath !== "/" && normalizedPath !== " /docs" ;
7780 } , [ pathname ] ) ;
7881
79- const sendEvent = ( action : string , details : Record < string , unknown > ) => {
80- if ( typeof window === 'undefined' || typeof window . sendV2Event !== 'function' ) {
82+ const sendCsatSubmitEvent = (
83+ score : CsatScore ,
84+ reaction : CsatReaction ,
85+ feedbackValue : string ,
86+ reasons : string [ ] ,
87+ ) => {
88+ if (
89+ typeof window === "undefined" ||
90+ typeof window . sendV2Event !== "function"
91+ ) {
8192 return ;
8293 }
8394
8495 window . sendV2Event ( {
85- source : ' docs_csat_widget' ,
86- action,
87- noun : ' csat' ,
96+ source : " docs_csat_widget" ,
97+ action : "submit" ,
98+ noun : " csat" ,
8899 action_info : {
89- page_type : ' dev_portal_docs' ,
100+ page_type : " dev_portal_docs" ,
90101 page_path : pathname ,
91- ...details ,
102+ widget : "docs_csat" ,
103+ score,
104+ reaction,
105+ feedback : feedbackValue ,
106+ reasons,
92107 } ,
93108 request : {
94109 base_url : window . location . href ,
@@ -98,48 +113,44 @@ export default function CsatWidget(): React.ReactElement | null {
98113 } ;
99114
100115 useEffect ( ( ) => {
101- if ( typeof window === ' undefined' ) {
116+ if ( typeof window === " undefined" ) {
102117 return ;
103118 }
104119
105120 if ( ! shouldShowForPath ) {
106121 setSelectedScore ( null ) ;
107- setFeedback ( '' ) ;
122+ setFeedback ( "" ) ;
108123 setSelectedReasons ( [ ] ) ;
109124 setIsSubmitted ( false ) ;
110125 setIsVisible ( false ) ;
111126 return ;
112127 }
113128
114- const hasSubmitted = window . sessionStorage . getItem ( storageKey ) === '1' ;
129+ const hasSubmitted = window . sessionStorage . getItem ( storageKey ) === "1" ;
115130 setSelectedScore ( null ) ;
116- setFeedback ( '' ) ;
131+ setFeedback ( "" ) ;
117132 setSelectedReasons ( [ ] ) ;
118133 setIsSubmitted ( false ) ;
119134 setIsVisible ( ! hasSubmitted ) ;
120-
121- if ( ! hasSubmitted ) {
122- sendEvent ( 'view' , { widget : 'docs_csat' } ) ;
123- }
124135 } , [ storageKey , shouldShowForPath ] ) ;
125136
126137 const dismiss = ( ) => {
127- sendEvent ( 'dismiss' , { widget : 'docs_csat' } ) ;
128138 setIsVisible ( false ) ;
129139 } ;
130140
131- const submit = ( score : number , extraDetails : Record < string , unknown > = { } ) => {
132- if ( typeof window === 'undefined' ) {
141+ const submit = (
142+ score : CsatScore ,
143+ reaction : CsatReaction ,
144+ feedbackValue : string ,
145+ reasons : string [ ] ,
146+ ) => {
147+ if ( typeof window === "undefined" ) {
133148 return ;
134149 }
135150
136- sendEvent ( 'submit' , {
137- widget : 'docs_csat' ,
138- score,
139- ...extraDetails ,
140- } ) ;
151+ sendCsatSubmitEvent ( score , reaction , feedbackValue , reasons ) ;
141152
142- window . sessionStorage . setItem ( storageKey , '1' ) ;
153+ window . sessionStorage . setItem ( storageKey , "1" ) ;
143154 setIsSubmitted ( true ) ;
144155 window . setTimeout ( ( ) => {
145156 setIsVisible ( false ) ;
@@ -149,29 +160,21 @@ export default function CsatWidget(): React.ReactElement | null {
149160 const onScoreSelect = ( option : ScoreOption ) => {
150161 const score = option . value ;
151162 setSelectedScore ( score ) ;
152- if ( option . reaction === ' positive' ) {
163+ if ( option . reaction === " positive" ) {
153164 setSelectedReasons ( [ ] ) ;
154- setFeedback ( '' ) ;
165+ setFeedback ( "" ) ;
155166 }
156167
157- sendEvent ( 'select_score' , {
158- widget : 'docs_csat' ,
159- score,
160- reaction : option . reaction ,
161- } ) ;
162-
163- if ( option . reaction === 'positive' ) {
164- submit ( score , {
165- reaction : option . reaction ,
166- feedback : '' ,
167- reasons : [ ] ,
168- } ) ;
168+ if ( option . reaction === "positive" ) {
169+ submit ( score , option . reaction , "" , [ ] ) ;
169170 }
170171 } ;
171172
172173 const toggleReason = ( reason : string ) => {
173174 setSelectedReasons ( ( current ) =>
174- current . includes ( reason ) ? current . filter ( ( item ) => item !== reason ) : [ ...current , reason ]
175+ current . includes ( reason )
176+ ? current . filter ( ( item ) => item !== reason )
177+ : [ ...current , reason ] ,
175178 ) ;
176179 } ;
177180
@@ -181,11 +184,7 @@ export default function CsatWidget(): React.ReactElement | null {
181184 }
182185
183186 const trimmedFeedback = feedback . trim ( ) ;
184- submit ( selectedScore , {
185- reaction : 'negative' ,
186- feedback : trimmedFeedback ,
187- reasons : selectedReasons ,
188- } ) ;
187+ submit ( selectedScore , "negative" , trimmedFeedback , selectedReasons ) ;
189188 } ;
190189
191190 if ( ! isVisible ) {
@@ -243,12 +242,18 @@ export default function CsatWidget(): React.ReactElement | null {
243242 </ label >
244243 ) ) }
245244 </ div >
246- < button className = { styles . submitButton } type = "button" onClick = { submitNegativeFeedback } >
245+ < button
246+ className = { styles . submitButton }
247+ type = "button"
248+ onClick = { submitNegativeFeedback }
249+ >
247250 Send feedback
248251 </ button >
249252 </ div >
250253 ) }
251- { isSubmitted && < div className = { styles . thanks } > Thanks for the feedback!</ div > }
254+ { isSubmitted && (
255+ < div className = { styles . thanks } > Thanks for the feedback!</ div >
256+ ) }
252257 </ div >
253258 ) ;
254259}
0 commit comments