import { useCallback, useEffect, useRef, useState } from 'react'; import { Bug, X } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { useFocusTrap } from '../hooks/use-focus-trap'; import { __, sprintf } from '../lib/i18n'; import { trackFeedbackClosed, trackFeedbackSubmitFailed, trackFeedbackSubmitted, } from '../lib/mixpanel'; interface FeedbackModalProps { open: boolean; onClose: () => void; } export function FeedbackModal({ open, onClose }: FeedbackModalProps) { const [subject, setSubject] = useState(''); const [message, setMessage] = useState(''); const [includeLogs, setIncludeLogs] = useState(false); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); const [success, setSuccess] = useState(false); const dialogRef = useRef(null); const dismiss = useCallback( (reason: 'backdrop' | 'x' | 'cancel' | 'escape') => { trackFeedbackClosed(reason); onClose(); }, [onClose], ); useFocusTrap(dialogRef, { active: open, onEscape: () => dismiss('escape') }); useEffect(() => { if (open) { setError(null); setSuccess(false); setIncludeLogs(false); } }, [open]); const handleSubmit = useCallback(async () => { const s = subject.trim(); const m = message.trim(); if (!s || !m) { setError(__('Please fill in both subject and description.', 'llmagnet-llm-txt-generator')); return; } const root = window.wpApiSettings?.root?.replace(/\/$/, '') || ''; const nonce = window.wpApiSettings?.nonce || ''; if (!root || !nonce) { setError(__('Could not send feedback (missing API settings). Please reload the page.', 'llmagnet-llm-txt-generator')); return; } setSubmitting(true); setError(null); try { const res = await fetch(`${root}/llm-analytics/v1/feedback`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': nonce, }, credentials: 'same-origin', body: JSON.stringify({ subject: s, message: m, include_logs: includeLogs }), }); const data = (await res.json().catch(() => ({}))) as { code?: string; message?: string; }; if (!res.ok) { const msg = typeof data.message === 'string' && data.message ? data.message : sprintf( /* translators: %d: HTTP status code. */ __('Request failed (%d)', 'llmagnet-llm-txt-generator'), res.status ); trackFeedbackSubmitFailed(res.status, typeof data.code === 'string' ? data.code : undefined); setError(msg); return; } trackFeedbackSubmitted(s.length, m.length); setSuccess(true); setSubject(''); setMessage(''); window.setTimeout(() => { setSuccess(false); onClose(); }, 1600); } catch { trackFeedbackSubmitFailed(0, 'network'); setError(__('Network error. Please try again.', 'llmagnet-llm-txt-generator')); } finally { setSubmitting(false); } }, [subject, message, includeLogs, onClose]); if (!open) { return null; } return (
{ if (e.target === e.currentTarget) { dismiss('backdrop'); } }} >
e.stopPropagation()} >

{__('Feedback / bugs', 'llmagnet-llm-txt-generator')}

{success ? (

{__('Thanks — your feedback was sent.', 'llmagnet-llm-txt-generator')}

) : ( <> setSubject(e.target.value)} placeholder={__('Have something to say?', 'llmagnet-llm-txt-generator')} disabled={submitting} style={{ width: '100%', boxSizing: 'border-box', padding: '10px 12px', borderRadius: 10, border: '1px solid #d8b4e2', fontSize: 14, outline: 'none', fontFamily: 'inherit', }} />