diff --git a/components/ChangelogTabs.tsx b/components/ChangelogTabs.tsx
index 87ee996..55119b3 100644
--- a/components/ChangelogTabs.tsx
+++ b/components/ChangelogTabs.tsx
@@ -3,6 +3,7 @@
import { useEffect, useRef, useState } from 'react'
import { PortableTextRenderer } from '@/components/PortableTextRenderer'
import { RoadmapItem } from '@/components/RoadmapItem'
+import { FeedbackForm } from '@/components/FeedbackForm'
import type { ChangelogEntry, RoadmapItemData, Tab, VoteState } from '@/types/changelog'
import { useAuth } from '@/context/AuthContext'
import Link from 'next/link'
@@ -297,9 +298,17 @@ export function ChangelogTabs({
)}
- {/* ── Give Feedback — stub, filled in Task 6 ── */}
+ {/* ── Give Feedback ── */}
{activeTab === 'give-feedback' && (
-
Feedback form coming soon.
+
+
+
Tell us what you think.
+
+ Bug, feature idea, or general thought — we read everything.
+
+
+
+
)}
)
diff --git a/components/FeedbackForm.tsx b/components/FeedbackForm.tsx
new file mode 100644
index 0000000..0e21862
--- /dev/null
+++ b/components/FeedbackForm.tsx
@@ -0,0 +1,129 @@
+'use client'
+
+import { useState } from 'react'
+
+type Status = 'idle' | 'loading' | 'done' | 'error'
+
+const CATEGORIES = [
+ { value: 'BUG', label: 'Bug report' },
+ { value: 'FEATURE_REQUEST', label: 'Feature request' },
+ { value: 'GENERAL', label: 'General feedback' },
+]
+
+export function FeedbackForm({ onSuccess }: { onSuccess?: () => void }) {
+ const [category, setCategory] = useState('BUG')
+ const [message, setMessage] = useState('')
+ const [email, setEmail] = useState('')
+ const [status, setStatus] = useState('idle')
+
+ async function handleSubmit(e: React.FormEvent) {
+ e.preventDefault()
+ if (!message.trim()) return
+ setStatus('loading')
+ try {
+ const res = await fetch('/api/feedback', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ category,
+ message: message.trim(),
+ email: email.trim() || undefined,
+ }),
+ })
+ if (res.ok) {
+ setStatus('done')
+ onSuccess?.()
+ } else {
+ setStatus('error')
+ }
+ } catch {
+ setStatus('error')
+ }
+ }
+
+ if (status === 'done') {
+ return (
+
+
Thanks — we got it.
+
+ We read everything. If you left an email we'll follow up.
+
+
+ )
+ }
+
+ return (
+
+ )
+}
diff --git a/components/FeedbackModal.tsx b/components/FeedbackModal.tsx
new file mode 100644
index 0000000..f55d5eb
--- /dev/null
+++ b/components/FeedbackModal.tsx
@@ -0,0 +1,51 @@
+'use client'
+
+import { useEffect } from 'react'
+import { XMarkIcon } from '@heroicons/react/20/solid'
+import { FeedbackForm } from '@/components/FeedbackForm'
+
+export function FeedbackModal({
+ open,
+ onClose,
+}: {
+ open: boolean
+ onClose: () => void
+}) {
+ useEffect(() => {
+ if (!open) return
+ function onKey(e: KeyboardEvent) {
+ if (e.key === 'Escape') onClose()
+ }
+ document.addEventListener('keydown', onKey)
+ return () => document.removeEventListener('keydown', onKey)
+ }, [open, onClose])
+
+ if (!open) return null
+
+ return (
+
+
+
+
+
Send Feedback
+
+
+
+
+
+ )
+}