From 5ad56c59515e135ed2a2d104c3849097a4f9dbfd Mon Sep 17 00:00:00 2001 From: Claude Code Date: Thu, 14 May 2026 16:33:20 -0500 Subject: [PATCH 1/7] chore(feedback): remove mailto feedback widget The widget routed to feedback@mrbaseballdynasty.com, which is not a mailbox we own or monitor. Removing the dead destination is cleaner than wiring a fallback in v1. - Delete apps/web/src/features/feedback/ (button, form, mailto helper, tests) - Pull FeedbackButton import + render from SettingsPage About section - Drop the matching SettingsPage feedback test Co-Authored-By: Claude Opus 4.7 --- .../src/features/feedback/FeedbackButton.tsx | 22 -- .../src/features/feedback/FeedbackForm.tsx | 250 ------------------ .../feedback/__tests__/FeedbackForm.test.tsx | 190 ------------- .../src/features/feedback/feedbackSubmit.ts | 56 ---- apps/web/src/features/feedback/index.ts | 14 - .../settings/routes/SettingsPage.test.tsx | 22 -- .../features/settings/routes/SettingsPage.tsx | 2 - 7 files changed, 556 deletions(-) delete mode 100644 apps/web/src/features/feedback/FeedbackButton.tsx delete mode 100644 apps/web/src/features/feedback/FeedbackForm.tsx delete mode 100644 apps/web/src/features/feedback/__tests__/FeedbackForm.test.tsx delete mode 100644 apps/web/src/features/feedback/feedbackSubmit.ts delete mode 100644 apps/web/src/features/feedback/index.ts diff --git a/apps/web/src/features/feedback/FeedbackButton.tsx b/apps/web/src/features/feedback/FeedbackButton.tsx deleted file mode 100644 index 044ee44..0000000 --- a/apps/web/src/features/feedback/FeedbackButton.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { useState } from 'react'; -import { MessageSquare } from 'lucide-react'; -import { FeedbackForm } from './FeedbackForm'; - -export function FeedbackButton() { - const [open, setOpen] = useState(false); - - return ( - <> - - {open ? setOpen(false)} /> : null} - - ); -} diff --git a/apps/web/src/features/feedback/FeedbackForm.tsx b/apps/web/src/features/feedback/FeedbackForm.tsx deleted file mode 100644 index 7672970..0000000 --- a/apps/web/src/features/feedback/FeedbackForm.tsx +++ /dev/null @@ -1,250 +0,0 @@ -import { useEffect, useMemo, useState } from 'react'; -import { Send, X } from 'lucide-react'; -import { useFocusTrap } from '@/shared/hooks/useFocusTrap'; -import { - FEEDBACK_BODY_MAX_LENGTH, - FEEDBACK_BODY_MIN_LENGTH, - formatFeedbackType, - submitFeedbackWithFallback, - type FeedbackPayload, - type FeedbackSubmitter, - type FeedbackType, -} from './feedbackSubmit'; - -const FEEDBACK_TYPES: FeedbackType[] = ['bug', 'suggestion', 'question']; - -interface FeedbackFormProps { - onClose: () => void; - submitFeedback?: FeedbackSubmitter; - autoDismissMs?: number; -} - -interface FeedbackErrors { - type?: string; - body?: string; - submit?: string; -} - -function validateFeedback(type: string, body: string): FeedbackErrors { - const errors: FeedbackErrors = {}; - const trimmedBody = body.trim(); - - if (!FEEDBACK_TYPES.includes(type as FeedbackType)) { - errors.type = 'Choose a feedback type.'; - } - - if ( - trimmedBody.length < FEEDBACK_BODY_MIN_LENGTH - || trimmedBody.length > FEEDBACK_BODY_MAX_LENGTH - ) { - errors.body = 'Write 200-500 characters so the report is actionable.'; - } - - return errors; -} - -export function FeedbackForm({ - onClose, - submitFeedback = submitFeedbackWithFallback, - autoDismissMs = 3000, -}: FeedbackFormProps) { - const [type, setType] = useState(''); - const [body, setBody] = useState(''); - const [contact, setContact] = useState(''); - const [errors, setErrors] = useState({}); - const [status, setStatus] = useState<'idle' | 'submitting' | 'sent'>('idle'); - const trapRef = useFocusTrap(true); - - const bodyLength = body.trim().length; - const canSubmit = status !== 'submitting' && status !== 'sent'; - - const payload = useMemo(() => { - if (!FEEDBACK_TYPES.includes(type as FeedbackType)) { - return null; - } - - return { - type: type as FeedbackType, - body: body.trim(), - contact: contact.trim() || undefined, - }; - }, [body, contact, type]); - - useEffect(() => { - if (status !== 'sent') { - return; - } - - const timeout = window.setTimeout(onClose, autoDismissMs); - return () => window.clearTimeout(timeout); - }, [autoDismissMs, onClose, status]); - - useEffect(() => { - const handleKeyDown = (event: KeyboardEvent) => { - if (event.key === 'Escape' && canSubmit) { - event.preventDefault(); - onClose(); - } - }; - - document.addEventListener('keydown', handleKeyDown); - return () => document.removeEventListener('keydown', handleKeyDown); - }, [canSubmit, onClose]); - - async function handleSubmit() { - if (!canSubmit) { - return; - } - - const nextErrors = validateFeedback(type, body); - if (Object.keys(nextErrors).length > 0 || !payload) { - setErrors(nextErrors); - return; - } - - setStatus('submitting'); - setErrors({}); - try { - await submitFeedback(payload); - setStatus('sent'); - } catch { - setStatus('idle'); - setErrors({ submit: 'Feedback could not be sent. Try again or use your mail client directly.' }); - } - } - - return ( -
-
-
-
-
- Player Feedback -
-

- Send feedback -

-

- Report bugs, questions, or friction from the exact thing you are seeing. Only the fields you type are sent. -

-
- -
- - {status === 'sent' ? ( -
- Thanks — sent. -
- ) : ( -
- - -