From d450a1d8d8822dc865e5c1d84d38f9e0d4ac9efd Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Fri, 15 May 2026 20:01:00 +0200 Subject: [PATCH] refactor(frontend): EmptyState + LoadingSkeleton primitives; unified ThoughtForm replaces PostThoughtForm and ReplyForm --- thoughts-frontend/app/page.tsx | 9 +- thoughts-frontend/app/search/page.tsx | 9 +- thoughts-frontend/app/tags/[tagName]/page.tsx | 5 +- .../app/users/[username]/page.tsx | 10 +- thoughts-frontend/components/empty-state.tsx | 12 ++ .../components/loading-skeleton.tsx | 34 +++++ .../components/post-thought-form.tsx | 130 ---------------- thoughts-frontend/components/reply-form.tsx | 97 ------------ thoughts-frontend/components/thought-card.tsx | 8 +- thoughts-frontend/components/thought-form.tsx | 142 ++++++++++++++++++ 10 files changed, 203 insertions(+), 253 deletions(-) create mode 100644 thoughts-frontend/components/empty-state.tsx create mode 100644 thoughts-frontend/components/loading-skeleton.tsx delete mode 100644 thoughts-frontend/components/post-thought-form.tsx delete mode 100644 thoughts-frontend/components/reply-form.tsx create mode 100644 thoughts-frontend/components/thought-form.tsx diff --git a/thoughts-frontend/app/page.tsx b/thoughts-frontend/app/page.tsx index c95f232..ebac652 100644 --- a/thoughts-frontend/app/page.tsx +++ b/thoughts-frontend/app/page.tsx @@ -7,7 +7,8 @@ import { getTopFriends, Me, } from "@/lib/api"; -import { PostThoughtForm } from "@/components/post-thought-form"; +import { ThoughtForm } from "@/components/thought-form"; +import { EmptyState } from "@/components/empty-state"; import { Button } from "@/components/ui/button"; import Link from "next/link"; import { PopularTags } from "@/components/popular-tags"; @@ -80,7 +81,7 @@ async function FeedPage({

Your Feed

- +
@@ -102,9 +103,7 @@ async function FeedPage({ /> ))} {thoughtThreads.length === 0 && ( -

- Your feed is empty. Follow some users to see their thoughts! -

+ )}
) : ( -

- No user found at {query} -

+ ) ) : results ? ( @@ -92,9 +91,7 @@ export default async function SearchPage({ searchParams }: SearchPageProps) { ) : ( -

- No results found or an error occurred. -

+ )} diff --git a/thoughts-frontend/app/tags/[tagName]/page.tsx b/thoughts-frontend/app/tags/[tagName]/page.tsx index 94293e9..953df10 100644 --- a/thoughts-frontend/app/tags/[tagName]/page.tsx +++ b/thoughts-frontend/app/tags/[tagName]/page.tsx @@ -23,6 +23,7 @@ export async function generateMetadata({ }, }; } +import { EmptyState } from "@/components/empty-state"; import { buildThoughtThreads } from "@/lib/utils"; import { ThoughtThread } from "@/components/thought-thread"; import { notFound } from "next/navigation"; @@ -66,9 +67,7 @@ export default async function TagPage({ params }: TagPageProps) { /> ))} {thoughtThreads.length === 0 && ( -

- No thoughts found for this tag. -

+ )} diff --git a/thoughts-frontend/app/users/[username]/page.tsx b/thoughts-frontend/app/users/[username]/page.tsx index 1d2a745..bbb4ef1 100644 --- a/thoughts-frontend/app/users/[username]/page.tsx +++ b/thoughts-frontend/app/users/[username]/page.tsx @@ -44,6 +44,7 @@ export async function generateMetadata({ }, }; } +import { EmptyState } from "@/components/empty-state"; import { UserAvatar } from "@/components/user-avatar"; import { Calendar, Settings } from "lucide-react"; import { Card } from "@/components/ui/card"; @@ -278,14 +279,7 @@ export default async function ProfilePage({ params }: ProfilePageProps) { /> ))} {thoughtThreads.length === 0 && ( - -

- This user hasn't posted any public thoughts yet. -

-
+ )} {isOwnProfile && ( diff --git a/thoughts-frontend/components/empty-state.tsx b/thoughts-frontend/components/empty-state.tsx new file mode 100644 index 0000000..5dcc01d --- /dev/null +++ b/thoughts-frontend/components/empty-state.tsx @@ -0,0 +1,12 @@ +interface EmptyStateProps { + message: string + className?: string +} + +export function EmptyState({ message, className }: EmptyStateProps) { + return ( +

+ {message} +

+ ) +} diff --git a/thoughts-frontend/components/loading-skeleton.tsx b/thoughts-frontend/components/loading-skeleton.tsx new file mode 100644 index 0000000..8b7cbbe --- /dev/null +++ b/thoughts-frontend/components/loading-skeleton.tsx @@ -0,0 +1,34 @@ +import { Card, CardContent, CardHeader } from "@/components/ui/card" +import { Skeleton } from "@/components/ui/skeleton" + +export function ThoughtSkeleton() { + return ( + + + +
+ + +
+
+ + + + +
+ ) +} + +export function ProfileSkeleton() { + return ( + + + +
+ + +
+
+
+ ) +} diff --git a/thoughts-frontend/components/post-thought-form.tsx b/thoughts-frontend/components/post-thought-form.tsx deleted file mode 100644 index 124fa63..0000000 --- a/thoughts-frontend/components/post-thought-form.tsx +++ /dev/null @@ -1,130 +0,0 @@ -"use client"; - -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { z } from "zod"; -import { useRouter } from "next/navigation"; -import { Button } from "@/components/ui/button"; -import { Card, CardContent } from "@/components/ui/card"; -import { - Form, - FormField, - FormItem, - FormControl, - FormMessage, -} from "@/components/ui/form"; -import { Textarea } from "@/components/ui/textarea"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { CreateThoughtSchema, createThought } from "@/lib/api"; -import { useAuth } from "@/hooks/use-auth"; -import { toast } from "sonner"; -import { Globe, Lock, Users } from "lucide-react"; -import { useState } from "react"; -import { Confetti } from "./confetti"; - -export function PostThoughtForm() { - const router = useRouter(); - const { token } = useAuth(); - const [showConfetti, setShowConfetti] = useState(false); - - const form = useForm>({ - resolver: zodResolver(CreateThoughtSchema), - defaultValues: { content: "", visibility: "public" }, - }); - - async function onSubmit(values: z.infer) { - if (!token) { - toast.error("You must be logged in to post."); - return; - } - - try { - await createThought(values, token); - toast.success("Your thought has been posted!"); - setShowConfetti(true); - form.reset(); - router.refresh(); // This is the key to updating the feed - } catch { - toast.error("Failed to post thought. Please try again."); - } - } - - return ( - <> - setShowConfetti(false)} /> - - -
- - ( - - -