diff --git a/web/src/app/settings/page.tsx b/web/src/app/settings/page.tsx index 14670a5..1a52f98 100644 --- a/web/src/app/settings/page.tsx +++ b/web/src/app/settings/page.tsx @@ -2,7 +2,8 @@ import { useState, useEffect } from 'react'; import Link from 'next/link'; -import { ArrowLeft, Volume2, Bell, Palette, Trash2, Minimize2, User, LogOut } from 'lucide-react'; +import { ArrowLeft, Volume2, Bell, Palette, Trash2, Minimize2, User, LogOut, MessageSquarePlus } from 'lucide-react'; +import { feedbackClient } from '@/lib/feedback'; import { URGENCY_ORDER, getUrgencyConfig } from '@/lib/urgency'; import { previewSound } from '@/lib/sounds'; import { getNotificationPermission, requestNotificationPermission } from '@/lib/notifications'; @@ -33,6 +34,10 @@ export default function SettingsPage() { const [showChangePw, setShowChangePw] = useState(false); const [showDeleteAccount, setShowDeleteAccount] = useState(false); const [deleteConfirmPw, setDeleteConfirmPw] = useState(''); + const [feedbackType, setFeedbackType] = useState<'bug' | 'feature' | 'praise'>('feature'); + const [feedbackText, setFeedbackText] = useState(''); + const [feedbackSubmitting, setFeedbackSubmitting] = useState(false); + const [feedbackSuccess, setFeedbackSuccess] = useState(false); useEffect(() => { setMounted(true); @@ -489,6 +494,74 @@ export default function SettingsPage() { + {/* Feedback */} + + + Feedback + + + {feedbackSuccess ? ( + + Thank you for your feedback! + + ) : ( + + + {(['feature', 'bug', 'praise'] as const).map((t) => ( + setFeedbackType(t)} + aria-label={`Feedback type: ${t}`} + className="px-3 py-1.5 rounded-lg text-xs font-medium cursor-pointer" + style={{ + backgroundColor: feedbackType === t ? 'var(--cm-accent)' : 'var(--cm-surface-muted)', + color: feedbackType === t ? 'var(--cm-white)' : 'var(--cm-text-secondary)', + }} + > + {t === 'feature' ? 'Feature Request' : t === 'bug' ? 'Bug Report' : 'Praise'} + + ))} + + setFeedbackText(e.target.value)} + placeholder="Tell us what you think..." + rows={3} + className="w-full px-3 py-2 rounded-lg text-sm outline-none resize-none" + style={{ + backgroundColor: 'var(--cm-surface-muted)', + color: 'var(--cm-text-primary)', + border: '1px solid var(--cm-border)', + }} + /> + { + if (!feedbackText.trim()) return; + setFeedbackSubmitting(true); + try { + await feedbackClient.submitWithScreenshot({ type: feedbackType, title: feedbackText.trim().slice(0, 100), body: feedbackText.trim(), platform: 'web' }); + setFeedbackSuccess(true); + setFeedbackText(''); + setTimeout(() => setFeedbackSuccess(false), 4000); + } catch { + // fail silently — feedback is best-effort + } + setFeedbackSubmitting(false); + }} + disabled={feedbackSubmitting || !feedbackText.trim()} + className="w-full px-4 py-2 rounded-lg text-sm font-medium cursor-pointer disabled:opacity-40" + style={{ backgroundColor: 'var(--cm-accent)', color: 'var(--cm-white)' }} + > + {feedbackSubmitting ? 'Sending...' : 'Send Feedback'} + + + )} + + + {/* About */}
+ Thank you for your feedback! +