From 5672d9062d8d11b8547974977a49b43e1e1355c9 Mon Sep 17 00:00:00 2001 From: chihlasm Date: Wed, 18 Feb 2026 17:58:42 -0500 Subject: [PATCH] feat: add FeedbackPage with custom feedback type selector Co-Authored-By: Claude Opus 4.6 --- frontend/src/pages/FeedbackPage.tsx | 198 ++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 frontend/src/pages/FeedbackPage.tsx diff --git a/frontend/src/pages/FeedbackPage.tsx b/frontend/src/pages/FeedbackPage.tsx new file mode 100644 index 00000000..118399da --- /dev/null +++ b/frontend/src/pages/FeedbackPage.tsx @@ -0,0 +1,198 @@ +import { useState } from 'react' +import { MessageSquareText, Send, CheckCircle2, ChevronDown } from 'lucide-react' +import { useAuthStore } from '@/store/authStore' +import { feedbackApi } from '@/api' +import { cn } from '@/lib/utils' +import { toast } from '@/lib/toast' + +// TODO: Post-session contextual feedback prompt — after completing a troubleshooting +// session, show a subtle inline prompt like "How was this flow? Quick feedback →" +// that opens a lightweight version of this form pre-tagged with tree/session context. + +const FEEDBACK_TYPES = [ + { value: 'Bug Report', description: 'Something is broken or not working as expected' }, + { value: 'Feature Request', description: "An idea for something new you'd like to see" }, + { value: 'Usability Issue', description: 'Something works but is confusing or hard to use' }, + { value: 'Documentation', description: 'Feedback on help docs, tooltips, or in-app guidance' }, + { value: 'General Feedback', description: 'Anything else — thoughts, impressions, suggestions' }, +] as const + +export function FeedbackPage() { + const user = useAuthStore(s => s.user) + + const [email, setEmail] = useState(user?.email ?? '') + const [feedbackType, setFeedbackType] = useState('') + const [message, setMessage] = useState('') + const [isSubmitting, setIsSubmitting] = useState(false) + const [submitted, setSubmitted] = useState(false) + const [typeDropdownOpen, setTypeDropdownOpen] = useState(false) + + const canSubmit = email.trim() && feedbackType && message.trim().length >= 10 + + const selectedType = FEEDBACK_TYPES.find(t => t.value === feedbackType) + + const handleSelectType = (value: string) => { + setFeedbackType(value) + setTypeDropdownOpen(false) + } + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + if (!canSubmit || isSubmitting) return + + setIsSubmitting(true) + try { + const response = await feedbackApi.submit({ + email: email.trim(), + feedback_type: feedbackType, + message: message.trim(), + }) + if (response.success) { + setSubmitted(true) + setFeedbackType('') + setMessage('') + } + } catch (err: unknown) { + const error = err as { response?: { data?: { detail?: string } } } + toast.error(error.response?.data?.detail || 'Failed to submit feedback. Please try again.') + } finally { + setIsSubmitting(false) + } + } + + const handleNewFeedback = () => { + setSubmitted(false) + setEmail(user?.email ?? '') + } + + return ( +
+ {/* Page header */} +
+
+ +

Send Feedback

+
+

+ Help us improve ResolutionFlow. Report bugs, request features, or share your thoughts. +

+
+ +
+ {submitted ? ( +
+ +

Thank you for your feedback!

+

+ We've received your submission and will review it shortly. Check your email for a confirmation. +

+ +
+ ) : ( +
+ {/* Email */} +
+ + setEmail(e.target.value)} + placeholder="your@email.com" + required + className="w-full rounded-lg border border-border bg-card px-3 py-2 text-foreground placeholder:text-muted-foreground focus:border-primary focus:ring-1 focus:ring-primary/20 focus:outline-none" + /> +

We'll reply to this address if we need more details.

+
+ + {/* Feedback Type — custom selector with descriptions */} +
+ +
+ + {typeDropdownOpen && ( +
+ {FEEDBACK_TYPES.map(type => ( + + ))} +
+ )} +
+
+ + {/* Message */} +
+ +