| import { useState } from 'react' |
|
|
| const STAR_HINTS = ['', 'Poor π', 'Fair π', 'Good π', 'Great π', 'Excellent π€©'] |
|
|
| export default function FeedbackModal({ onSubmit, onClose }) { |
| const [rating, setRating] = useState(0) |
| const [hover, setHover] = useState(0) |
| const [cat, setCat] = useState('general') |
| const [text, setText] = useState('') |
| const [err, setErr] = useState('') |
| const [done, setDone] = useState(false) |
| const [loading, setLoading] = useState(false) |
|
|
| const handleSubmit = async () => { |
| if (!rating) return setErr('Please select a star rating.') |
| if (!text.trim()) return setErr('Please write a short message.') |
| |
| setLoading(true) |
| try { |
| await onSubmit({ rating, category: cat, message: text.trim() }) |
| setDone(true) |
| } catch (e) { |
| setErr(e.message || 'Failed to submit feedback') |
| } finally { |
| setLoading(false) |
| } |
| } |
|
|
| if (done) { |
| return ( |
| <div className="modal-overlay" onClick={e => e.target === e.currentTarget && onClose()}> |
| <div className="modal-card" style={{ maxWidth: 440 }}> |
| <div className="modal-header"> |
| <span className="modal-title">Send Feedback</span> |
| <button className="modal-close" onClick={onClose}> |
| <svg viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg> |
| </button> |
| </div> |
| <div className="feedback-success"> |
| <div className="feedback-success-icon">π</div> |
| <div className="feedback-success-title">Thank you!</div> |
| <div className="feedback-success-sub">Your feedback has been saved. We read every single one.</div> |
| <button className="btn btn-primary" onClick={onClose} style={{ marginTop: 10 }}>Close</button> |
| </div> |
| </div> |
| </div> |
| ) |
| } |
|
|
| return ( |
| <div className="modal-overlay" onClick={e => e.target === e.currentTarget && onClose()}> |
| <div className="modal-card" style={{ maxWidth: 460 }}> |
| <div className="modal-header"> |
| <span className="modal-title">Send Feedback</span> |
| <button className="modal-close" onClick={onClose}> |
| <svg viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg> |
| </button> |
| </div> |
| |
| <div className="modal-body"> |
| <div className="field-label">How would you rate OwnGPT?</div> |
| <div className="star-row"> |
| {[1,2,3,4,5].map(v => ( |
| <button |
| key={v} |
| className={`star-btn ${rating >= v ? 'lit' : ''} ${hover >= v ? 'hover' : ''}`} |
| onMouseEnter={() => setHover(v)} |
| onMouseLeave={() => setHover(0)} |
| onClick={() => setRating(v)} |
| > |
| β
|
| </button> |
| ))} |
| </div> |
| <div className="star-hint">{STAR_HINTS[hover || rating]}</div> |
| |
| <div className="field-label" style={{ marginTop: 16 }}>Category</div> |
| <div className="fcat-row"> |
| <button className={`fcat-btn ${cat === 'general' ? 'active' : ''}`} onClick={() => setCat('general')}>π¬ General</button> |
| <button className={`fcat-btn ${cat === 'bug' ? 'active' : ''}`} onClick={() => setCat('bug')}>π Bug</button> |
| <button className={`fcat-btn ${cat === 'feature' ? 'active' : ''}`} onClick={() => setCat('feature')}>β¨ Feature</button> |
| <button className={`fcat-btn ${cat === 'other' ? 'active' : ''}`} onClick={() => setCat('other')}>π Other</button> |
| </div> |
| |
| <div className="field-label">Your message</div> |
| <textarea |
| className="field-textarea" |
| placeholder="Tell us what you think..." |
| value={text} |
| onChange={e => setText(e.target.value)} |
| maxLength={2000} |
| /> |
| <div className="char-count">{text.length} / 2000</div> |
| |
| {err && <div className="field-error" style={{ marginTop: 8 }}>{err}</div>} |
| </div> |
| |
| <div className="modal-footer"> |
| <button className="btn" onClick={onClose}>Cancel</button> |
| <button className="btn btn-primary" onClick={handleSubmit} disabled={loading}> |
| {loading ? 'Sending...' : 'Submit Feedback'} |
| </button> |
| </div> |
| </div> |
| </div> |
| ) |
| } |
|
|