@@ -4,6 +4,22 @@ import styles from './styles.module.css'
44type Rating = 'yes' | 'no' | null
55type Phase = 'initial' | 'comment' | 'submitted' | 'error'
66
7+ function stripHtml ( text : string ) : string {
8+ let prev = text
9+ for ( ; ; ) {
10+ const next = prev . replace ( / < [ ^ > ] * > / g, '' )
11+ if ( next === prev ) return next
12+ prev = next
13+ }
14+ }
15+
16+ function sanitizeReason ( text : string ) : string {
17+ return stripHtml ( text )
18+ . replace ( / ! \[ ( [ ^ \] ] * ) \] \( [ ^ ) ] * \) / g, '$1' )
19+ . replace ( / \[ ( [ ^ \] ] * ) \] \( [ ^ ) ] * \) / g, '$1' )
20+ . trim ( )
21+ }
22+
723declare global {
824 interface Window {
925 dataLayer ?: Record < string , unknown > [ ]
@@ -14,15 +30,18 @@ export default function FeedbackWidget(): React.ReactNode {
1430 const [ rating , setRating ] = useState < Rating > ( null )
1531 const [ phase , setPhase ] = useState < Phase > ( 'initial' )
1632 const [ reason , setReason ] = useState ( '' )
33+ const [ errorMsg , setErrorMsg ] = useState ( '' )
1734 const [ submitting , setSubmitting ] = useState ( false )
1835
36+ const reasonIsEmpty = ! sanitizeReason ( reason )
37+
1938 const handleRating = ( selected : Rating ) => {
2039 setRating ( selected )
2140 setPhase ( 'comment' )
2241 }
2342
2443 const handleSubmit = async ( ) => {
25- if ( rating === 'no' && ! reason . trim ( ) ) return
44+ if ( rating === 'no' && reasonIsEmpty ) return
2645 setSubmitting ( true )
2746
2847 window . dataLayer ?. push ( {
@@ -42,8 +61,12 @@ export default function FeedbackWidget(): React.ReactNode {
4261 reason : reason . trim ( ) ,
4362 } ) ,
4463 } )
45- if ( ! res . ok ) throw new Error ( String ( res . status ) )
46- } catch {
64+ if ( ! res . ok ) {
65+ const body = await res . json ( ) . catch ( ( ) => null )
66+ throw new Error ( body ?. error || `Request failed (${ res . status } )` )
67+ }
68+ } catch ( err ) {
69+ setErrorMsg ( err instanceof Error ? err . message : 'Something went wrong. Please try again later.' )
4770 setPhase ( 'error' )
4871 setSubmitting ( false )
4972 return
@@ -64,7 +87,7 @@ export default function FeedbackWidget(): React.ReactNode {
6487 if ( phase === 'error' ) {
6588 return (
6689 < div className = { styles . widget } >
67- < p className = { styles . error } > Something went wrong. Please try again later. </ p >
90+ < p className = { styles . error } > { errorMsg } </ p >
6891 </ div >
6992 )
7093 }
@@ -107,7 +130,7 @@ export default function FeedbackWidget(): React.ReactNode {
107130 type = "button"
108131 className = { styles . submitBtn }
109132 onClick = { handleSubmit }
110- disabled = { submitting || ( rating === 'no' && ! reason . trim ( ) ) } >
133+ disabled = { submitting || ( rating === 'no' && reasonIsEmpty ) } >
111134 { submitting ? 'Sending...' : 'Submit' }
112135 </ button >
113136 </ div >
0 commit comments