diff --git a/src/pages/Feedback.tsx b/src/pages/Feedback.tsx new file mode 100644 index 0000000..507cb7d --- /dev/null +++ b/src/pages/Feedback.tsx @@ -0,0 +1,281 @@ +import { useState } from "react"; +import { Card, CardContent } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Avatar, AvatarFallback } from "@/components/ui/avatar"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; +import { Label } from "@/components/ui/label"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { + Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, DialogFooter +} from "@/components/ui/dialog"; +import { + Plus, ThumbsUp, MessageSquare, TrendingUp, Clock, Bug, Lightbulb, Wrench, ArrowUpDown, LogIn, LogOut +} from "lucide-react"; +import { toast } from "sonner"; +import { useAuth, useSaasMutation } from "@/hooks/use-tygarun"; +import Sidebar from "@/components/Sidebar"; +import Header from "@/components/Header"; + +interface FeedbackItem { + id: number; + user: string; + userInitials: string; + userColor: string; + text: string; + category: string; + categoryColor: string; + categoryIcon: string; + votes: number; + status: string; + statusColor: string; + date: string; + comments: number; +} + +const feedbackData: FeedbackItem[] = [ + { id: 1, user: "Elena Martinez", userInitials: "EM", userColor: "bg-blue-500", text: "The roadmap board needs a timeline view option. It would be so much easier to visualize dependencies and deadlines in a Gantt-style layout.", category: "Feature", categoryColor: "bg-blue-100 text-blue-700", categoryIcon: "lightbulb", votes: 34, status: "Under Review", statusColor: "bg-amber-100 text-amber-700", date: "Apr 10, 2026", comments: 8 }, + { id: 2, user: "James Chen", userInitials: "JC", userColor: "bg-emerald-500", text: "Exporting reports to PDF cuts off long feature descriptions. The page margins seem too wide and text wraps incorrectly in the generated document.", category: "Bug", categoryColor: "bg-red-100 text-red-700", categoryIcon: "bug", votes: 28, status: "In Progress", statusColor: "bg-blue-100 text-blue-700", date: "Apr 9, 2026", comments: 5 }, + { id: 3, user: "Taylor Pham", userInitials: "TP", userColor: "bg-violet-500", text: "Would love the ability to link related feedback items together. Sometimes multiple users report the same issue in different ways.", category: "Feature", categoryColor: "bg-blue-100 text-blue-700", categoryIcon: "lightbulb", votes: 45, status: "Planned", statusColor: "bg-slate-100 text-slate-700", date: "Apr 8, 2026", comments: 12 }, + { id: 4, user: "Lisa Wang", userInitials: "LW", userColor: "bg-sky-500", text: "The notification emails could include more context. Right now they just say a feature was updated but not what changed specifically.", category: "Improvement", categoryColor: "bg-amber-100 text-amber-700", categoryIcon: "wrench", votes: 19, status: "Under Review", statusColor: "bg-amber-100 text-amber-700", date: "Apr 7, 2026", comments: 3 }, + { id: 5, user: "David Park", userInitials: "DP", userColor: "bg-indigo-500", text: "Dashboard widgets sometimes show stale data after switching between views. A manual refresh button or auto-refresh interval would fix this.", category: "Bug", categoryColor: "bg-red-100 text-red-700", categoryIcon: "bug", votes: 22, status: "In Progress", statusColor: "bg-blue-100 text-blue-700", date: "Apr 6, 2026", comments: 6 }, + { id: 6, user: "Amy Foster", userInitials: "AF", userColor: "bg-pink-500", text: "It would be great if the feature voting page had a search and filter option. With hundreds of features, scrolling through all of them is tedious.", category: "Improvement", categoryColor: "bg-amber-100 text-amber-700", categoryIcon: "wrench", votes: 37, status: "Planned", statusColor: "bg-slate-100 text-slate-700", date: "Apr 5, 2026", comments: 9 }, +]; + +const categoryIcons: Record = { + bug: Bug, + lightbulb: Lightbulb, + wrench: Wrench, +}; + +export default function FeedbackPage() { + const [sortBy, setSortBy] = useState<"votes" | "newest" | "trending">("votes"); + const [dialogOpen, setDialogOpen] = useState(false); + const [items, setItems] = useState(feedbackData); + const [fbTitle, setFbTitle] = useState(""); + const [fbDesc, setFbDesc] = useState(""); + const [fbCategory, setFbCategory] = useState("feature"); + + // Login is LIVE via tyga.run auth; voting + feedback submission use surveys (seed until live) + const { user, isSignedIn, signin, signout } = useAuth(); + const { mutate: submitFeedback } = useSaasMutation("surveys", "/responses"); + const { mutate: castVote } = useSaasMutation("surveys", "/responses"); + + const [authOpen, setAuthOpen] = useState(false); + const [authEmail, setAuthEmail] = useState(""); + const [authPassword, setAuthPassword] = useState(""); + const [authBusy, setAuthBusy] = useState(false); + + const handleSignin = async (e: React.FormEvent) => { + e.preventDefault(); + setAuthBusy(true); + try { + await signin(authEmail, authPassword); + toast.success("Signed in! Your votes now count."); + setAuthOpen(false); + setAuthEmail(""); + setAuthPassword(""); + } catch (err) { + toast.error("Sign in failed. Check your credentials."); + } finally { + setAuthBusy(false); + } + }; + + const handleVote = async (id: number) => { + if (!isSignedIn) { setAuthOpen(true); toast("Sign in to vote"); return; } + setItems(prev => prev.map(item => item.id === id ? { ...item, votes: item.votes + 1 } : item)); + try { + await castVote({ feedbackId: id, type: "upvote" }); + toast("Vote recorded!"); + } catch (err) { + toast.error("Could not record vote."); + } + }; + + const handleSubmit = async () => { + if (!isSignedIn) { setAuthOpen(true); toast("Sign in to submit feedback"); return; } + try { + await submitFeedback({ title: fbTitle, description: fbDesc, category: fbCategory }); + toast.success("Feedback submitted successfully!"); + setDialogOpen(false); + setFbTitle(""); + setFbDesc(""); + setFbCategory("feature"); + } catch (err) { + toast.error("Could not submit feedback."); + } + }; + + const sorted = [...items].sort((a, b) => { + if (sortBy === "votes") return b.votes - a.votes; + if (sortBy === "newest") return new Date(b.date).getTime() - new Date(a.date).getTime(); + return (b.votes + b.comments * 2) - (a.votes + a.comments * 2); + }); + + return ( +
+ +
+
+
+ {/* ── Page Header ── */} +
+
+

Feedback

+

Collect and prioritize user feedback

+
+
+ {/* Auth control — login is LIVE via tyga.run */} + {isSignedIn ? ( + + ) : ( + + + + + + + Sign in to vote + +
+
+ + setAuthEmail(e.target.value)} placeholder="you@company.com" required /> +
+
+ + setAuthPassword(e.target.value)} placeholder="Password" required /> +
+ +
+
+
+ )} + + {/* Sort Options */} +
+ {(["votes", "newest", "trending"] as const).map((option) => ( + + ))} +
+ + {/* Submit Feedback Dialog */} + + + + + + + Submit Feedback + +
+
+ + setFbTitle(e.target.value)} placeholder="Brief summary of your feedback" /> +
+
+ +