"use client" import NewProjectModal from "@/components/dashboard/newProject" import ProjectCard from "@/components/dashboard/projectCard/" import { Button } from "@/components/ui/button" import { Card, CardContent, CardDescription, CardTitle, } from "@/components/ui/card" import { HoverCard, HoverCardContent, HoverCardTrigger, } from "@/components/ui/hover-card" import { Label } from "@/components/ui/label" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { deleteSandbox, updateSandbox, updateUser } from "@/lib/actions" import { TIERS } from "@/lib/tiers" import { SandboxWithLiked, User } from "@/lib/types" import { useUser } from "@clerk/nextjs" import { Edit, Heart, Info, Loader2, LucideIcon, Package2, PlusCircle, Sparkles, X, } from "lucide-react" import { useRouter } from "next/navigation" import { Fragment, useCallback, useEffect, useMemo, useState } from "react" import { useFormState, useFormStatus } from "react-dom" import { toast } from "sonner" import Avatar from "../ui/avatar" import { Badge } from "../ui/badge" import { Input } from "../ui/input" import { Progress } from "../ui/progress" // #region Profile Page export default function ProfilePage({ publicSandboxes, privateSandboxes, profileOwner, loggedInUser, }: { publicSandboxes: SandboxWithLiked[] privateSandboxes: SandboxWithLiked[] profileOwner: User loggedInUser: User | null }) { const isOwnProfile = profileOwner.id === loggedInUser?.id const sandboxes = useMemo(() => { const allSandboxes = isOwnProfile ? [...publicSandboxes, ...privateSandboxes] : publicSandboxes return allSandboxes }, [isOwnProfile, publicSandboxes, privateSandboxes]) return ( <>
) } // #endregion // #region Profile Card function ProfileCard({ name, username, avatarUrl, sandboxes, joinedDate, generations, isOwnProfile, tier, }: { name: string username: string avatarUrl: string | null sandboxes: SandboxWithLiked[] joinedDate: Date generations?: number isOwnProfile: boolean tier: string }) { const { user } = useUser() const router = useRouter() const [isEditing, setIsEditing] = useState(false) const [formState, formAction] = useFormState(updateUser, {}) const joinedAt = useMemo(() => { const date = new Date(joinedDate).toLocaleDateString("en-US", { month: "long", year: "numeric", }) return `Joined ${date}` }, [joinedDate]) const toggleEdit = useCallback(() => { setIsEditing((s) => !s) }, []) const stats = useMemo(() => { const totalSandboxes = sandboxes.length const totalLikes = sandboxes.reduce( (sum, sandbox) => sum + sandbox.likeCount, 0 ) return { sandboxes: totalSandboxes === 1 ? "1 sandbox" : `${totalSandboxes} sandboxes`, likes: totalLikes === 1 ? "1 like" : `${totalLikes} likes`, } }, [sandboxes]) useEffect(() => { if ("message" in formState) { toast.success(formState.message as String) toggleEdit() if ("newRoute" in formState && typeof formState.newRoute === "string") { router.replace(formState.newRoute) } } if ("error" in formState) { const error = formState.error if (typeof error === "string") { toast.error(error) } else { toast.error("An Error Occured") } } }, [formState]) return ( {isOwnProfile && ( )} {!isEditing ? (
{name} {`@${username}`}
) : (
@
)} {!isEditing && ( <>

{joinedAt}

{typeof generations === "number" && ( )}
)}
) } function SubmitButton() { const { pending } = useFormStatus() return ( ) } // #endregion // #region Sandboxes Panel function SandboxesPanel({ publicSandboxes, privateSandboxes, isOwnProfile, }: { publicSandboxes: SandboxWithLiked[] privateSandboxes: SandboxWithLiked[] isOwnProfile: boolean }) { const [deletingId, setDeletingId] = useState("") const hasPublicSandboxes = publicSandboxes.length > 0 const hasPrivateSandboxes = privateSandboxes.length > 0 const onVisibilityChange = useMemo( () => async (sandbox: Pick) => { const newVisibility = sandbox.visibility === "public" ? "private" : "public" toast(`Project ${sandbox.name} is now ${newVisibility}.`) await updateSandbox({ id: sandbox.id, visibility: newVisibility, }) }, [] ) const onDelete = useMemo( () => async (sandbox: Pick) => { setDeletingId(sandbox.id) toast(`Project ${sandbox.name} deleted.`) await deleteSandbox(sandbox.id) setDeletingId("") }, [] ) if (!isOwnProfile) { return (
{hasPublicSandboxes ? ( <>

Sandboxes

{publicSandboxes.map((sandbox) => { return ( {isOwnProfile ? ( ) : ( )} ) })}
) : ( )}
) } return ( Public Private {hasPublicSandboxes ? (
{publicSandboxes.map((sandbox) => { return ( {isOwnProfile ? ( ) : ( )} ) })}
) : ( )}
{hasPrivateSandboxes ? (
{privateSandboxes.map((sandbox) => ( ))}
) : ( )}
) } // #endregion // #region Empty State function EmptyState({ type, isOwnProfile, }: { type: "public" | "private" isOwnProfile: boolean }) { const [newProjectModalOpen, setNewProjectModalOpen] = useState(false) const text = useMemo(() => { let title: string let description: string switch (type) { case "public": title = "No public sandboxes yet" description = isOwnProfile ? "Create your first public sandbox to share your work with the world!" : "user has no public sandboxes" case "private": title = "No private sandboxes yet" description = isOwnProfile ? "Create your first private sandbox to start working on your personal projects!" : "user has no private sandboxes" } return { title, description, } }, [type, isOwnProfile]) const openModal = useCallback(() => setNewProjectModalOpen(true), []) return ( <> {text.title} {text.description} {isOwnProfile && ( )} ) } // #endregion // #region StatsItem interface StatsItemProps { icon: LucideIcon label: string } const StatsItem = ({ icon: Icon, label }: StatsItemProps) => (
{label}
) // #endregion // #region Sub Badge const SubscriptionBadge = ({ generations, tier = "FREE" }: { generations: number, tier?: keyof typeof TIERS }) => { return (
{tier}
AI Generations {`${generations} / ${TIERS[tier].generations}`}
) } // #endregion