feat(web): TODO-002 feedback form in settings page via @bytelyst/feedback-client

This commit is contained in:
saravanakumardb1 2026-04-18 17:58:06 -07:00
parent 8bddbec43c
commit 0242ca85ff

View File

@ -2,7 +2,8 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import Link from 'next/link'; 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 { URGENCY_ORDER, getUrgencyConfig } from '@/lib/urgency';
import { previewSound } from '@/lib/sounds'; import { previewSound } from '@/lib/sounds';
import { getNotificationPermission, requestNotificationPermission } from '@/lib/notifications'; import { getNotificationPermission, requestNotificationPermission } from '@/lib/notifications';
@ -33,6 +34,10 @@ export default function SettingsPage() {
const [showChangePw, setShowChangePw] = useState(false); const [showChangePw, setShowChangePw] = useState(false);
const [showDeleteAccount, setShowDeleteAccount] = useState(false); const [showDeleteAccount, setShowDeleteAccount] = useState(false);
const [deleteConfirmPw, setDeleteConfirmPw] = useState(''); 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(() => { useEffect(() => {
setMounted(true); setMounted(true);
@ -489,6 +494,74 @@ export default function SettingsPage() {
</div> </div>
</section> </section>
{/* Feedback */}
<section className="mb-8">
<h2 className="flex items-center gap-2 text-base font-semibold mb-4" style={{ color: 'var(--cm-text-primary)' }}>
<MessageSquarePlus size={18} /> Feedback
</h2>
<div
className="rounded-xl border p-4"
style={{ backgroundColor: 'var(--cm-surface-card)', borderColor: 'var(--cm-border)' }}
>
{feedbackSuccess ? (
<p className="text-sm font-medium" style={{ color: 'var(--cm-gentle)' }}>
Thank you for your feedback!
</p>
) : (
<div className="space-y-3">
<div className="flex gap-2">
{(['feature', 'bug', 'praise'] as const).map((t) => (
<button
key={t}
onClick={() => 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'}
</button>
))}
</div>
<textarea
value={feedbackText}
onChange={(e) => 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)',
}}
/>
<button
onClick={async () => {
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'}
</button>
</div>
)}
</div>
</section>
{/* About */} {/* About */}
<section> <section>
<div <div