feat(web): TODO-002 feedback form in settings page via @bytelyst/feedback-client
This commit is contained in:
parent
8bddbec43c
commit
0242ca85ff
@ -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
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user