"use client" import { useEffect, useState, useRef } from "react" import { useRouter, usePathname } from "next/navigation" import Link from "next/link" import { Button } from "@/components/ui/button" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { Badge } from "@/components/ui/badge" import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" import { Input } from "@/components/ui/input" import { Dialog, DialogContent, DialogHeader, DialogTitle, } from "@/components/ui/dialog" import { Cpu, Plus, Settings, LogOut, Wrench, Users, BookOpen, Star, Heart, MessageCircle, Share, Edit, Copy, Eye, Calendar, Trophy, Trash2, } from "lucide-react" import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog" import { useAuth } from "@/contexts/supabase-auth-context" import { mockBuilds } from "@/lib/mock-data" import { supabase } from "@/lib/supabase" import { swal } from "@/lib/sweetalert" import { time } from "console" import { formatCurrency } from "@/lib/currency" export default function DashboardPage() { const { user, logout, isLoading } = useAuth() const router = useRouter() const pathname = usePathname() const [activeTab, setActiveTab] = useState("overview") const [userBuilds, setUserBuilds] = useState([]) const [likedBuilds, setLikedBuilds] = useState([]) const [totalLikes, setTotalLikes] = useState(0) const [loadingBuilds, setLoadingBuilds] = useState(false) const [loadingLikes, setLoadingLikes] = useState(false) const [recentActivity, setRecentActivity] = useState([]); const [followers, setFollowers] = useState([]); const [following, setFollowing] = useState([]); const [deleteBuildId, setDeleteBuildId] = useState(null) const [sharePopupOpen, setSharePopupOpen] = useState(false) const [shareUrl, setShareUrl] = useState("") const [pageLoading, setPageLoading] = useState(true) const [topBuilds, setTopBuilds] = useState([]); const [followModalOpen, setFollowModalOpen] = useState(false); const [followTab, setFollowTab] = useState<"followers" | "following">("following"); const [likedTopBuildIds, setLikedTopBuildIds] = useState([]); const [deletePopupOpen, setDeletePopupOpen] = useState(false) const [likeOwnPopupOpen, setLikeOwnPopupOpen] = useState(false); const topUserBuildsScrollRef = useRef(null); const topCommunityBuildsScrollRef = useRef(null); const [pauseAutoScroll, setPauseAutoScroll] = useState(false); // Auto-scroll for "Your Top Builds" and "Top Community Builds" (pauses on hover) useEffect(() => { if (pauseAutoScroll) return; const scrollStep = 1; const scrollInterval = 55; const run = (el: HTMLDivElement | null) => { if (!el || el.scrollHeight <= el.clientHeight) return; el.scrollTop += scrollStep; if (el.scrollTop >= el.scrollHeight - el.clientHeight - 1) { el.scrollTop = 0; } }; const t = setInterval(() => { run(topUserBuildsScrollRef.current); run(topCommunityBuildsScrollRef.current); }, scrollInterval); return () => clearInterval(t); }, [userBuilds.length, topBuilds.length, pauseAutoScroll]); function timeAgo(dateString: string | null) { if (!dateString) return "-"; const now = new Date(); const date = new Date(dateString); const seconds = Math.floor((now.getTime() - date.getTime()) / 1000); if (seconds < 5) return "just now"; const intervals: { [key: string]: number } = { year: 31536000, month: 2592000, week: 604800, day: 86400, hour: 3600, minute: 60, second: 1, }; for (const key in intervals) { const interval = Math.floor(seconds / intervals[key]); if (interval >= 1) { return `${interval} ${key}${interval !== 1 ? "s" : ""} ago`; } } return "just now"; } useEffect(() => { if (isLoading) return if (!user) router.push("/login") }, [isLoading, user, router]) useEffect(() => { if (isLoading || !user) return loadDashboard() loadTopBuilds().then(async (res) => { setTopBuilds(res); if (!user || !res.length) return; const buildIds = res.map((b: any) => b.build_id); const { data } = await supabase .from("build_likes") .select("build_id") .eq("user_id", user.user_id) .in("build_id", buildIds); if (data) { setLikedTopBuildIds(data.map(d => d.build_id)); } }); }, [isLoading, user]) const loadDashboard = async () => { setPageLoading(true) try { const userId = user.user_id const [ buildsRes, totalLikesRes, likedRes, followersRes, followingRes, commentsRes, ] = await Promise.all([ supabase .from("builds") .select("*, build_types(type_name)") .eq("user_id", userId) .order("date_created", { ascending: false }), supabase .from("build_likes") .select("builds!inner(build_id)", { count: "exact", head: true }) .eq("builds.user_id", userId), supabase .from("build_likes") .select("*, builds(*, build_types(type_name))") .eq("user_id", userId) .order("created_at", { ascending: false }), supabase .from("followers") .select(` follower_user_id, users!fk_follower ( user_id, user_name, avatar_url ) `) .eq("user_id", userId), supabase .from("followers") .select("*") .eq("follower_user_id", userId), supabase .from("build_comments") .select("*, builds(build_name)") .eq("user_id", userId), ]) // BUILDS + STATS const buildsWithStats = await fetchBuildsWithStats(buildsRes.data || []) setUserBuilds(buildsWithStats) // TOTAL LIKES RECEIVED setTotalLikes(totalLikesRes.count ?? 0) // LIKED BUILDS if (likedRes.data) { const likedBuildsOnly = likedRes.data.map(r => r.builds) const likedWithStats = await fetchBuildsWithStats(likedBuildsOnly) setLikedBuilds( likedRes.data.map((row, i) => ({ build: likedWithStats[i], liked_at: row.created_at, })) ) } // FOLLOWERS setFollowers( (followersRes.data || []).map((f: any) => ({ user_id: f.users.user_id, username: f.users.user_name, avatar: f.users.avatar_url, })) ) // FOLLOWING (with user info) if (followingRes.data?.length) { const ids = followingRes.data.map(f => f.user_id) const { data: users } = await supabase .from("users") .select("user_id, user_name, avatar_url") .in("user_id", ids) setFollowing( followingRes.data.map(f => { const u = users?.find(x => x.user_id === f.user_id) return { ...f, username: u?.user_name || "Unknown", avatar: u?.avatar_url || null, } }) ) } else { setFollowing([]) } // RECENT ACTIVITY (built once, correctly) const activity: any[] = [] buildsWithStats.forEach(b => activity.push({ id: `build_${b.build_id}`, description: `You Created '${b.build_name}'`, rawTime: b.date_created, time: timeAgo(b.date_created), icon: Plus, }) ) likedRes.data?.forEach(l => activity.push({ id: `like_${l.build_id}`, description: `You Liked '${l.builds.build_name}'`, rawTime: l.created_at, time: timeAgo(l.created_at), icon: Heart, }) ) commentsRes.data?.forEach(c => activity.push({ id: `comment_${c.comment_id}`, description: `You Commented on '${c.builds?.build_name}'`, rawTime: c.created_at, time: timeAgo(c.created_at), icon: MessageCircle, }) ) activity.sort( (a, b) => new Date(b.rawTime).getTime() - new Date(a.rawTime).getTime() ) setRecentActivity(activity) } catch (err) { console.error("Dashboard load failed:", err) } finally { setPageLoading(false) } } async function loadTopBuilds() { const { data, error } = await supabase .rpc("get_top_builds", { limit_count: 3 }); // argument matches the function if (error) { console.error("RPC Error:", error); return []; } return data; } const fetchBuildsWithStats = async (builds: any[]) => { if (!builds || builds.length === 0) return builds; const buildIds = builds.map(b => b.build_id); const userIds = builds.map(b => b.user_id); // Likes const { data: likesData } = await supabase .from("build_likes") .select("build_id") .in("build_id", buildIds); // Views const { data: viewsData } = await supabase .from("build_views") .select("build_id") .in("build_id", buildIds); // Comments const { data: commentsData } = await supabase .from("build_comments") .select("build_id") .in("build_id", buildIds); // Users info const { data: usersData } = await supabase .from("users") .select("user_id, user_name, avatar_url") .in("user_id", userIds); // Map counts const likeCountMap: Record = {}; const viewCountMap: Record = {}; const commentCountMap: Record = {}; buildIds.forEach(id => { likeCountMap[id] = 0; viewCountMap[id] = 0; commentCountMap[id] = 0; }); likesData?.forEach(like => (likeCountMap[like.build_id]++)); viewsData?.forEach(view => (viewCountMap[view.build_id]++)); commentsData?.forEach(comment => (commentCountMap[comment.build_id]++)); return builds.map(build => { const user = usersData?.find(u => u.user_id === build.user_id); return { ...build, likes: likeCountMap[build.build_id] || 0, views: viewCountMap[build.build_id] || 0, comments: commentCountMap[build.build_id] || 0, user_name: user?.user_name || "Unknown", avatar_url: user?.avatar_url || null, }; }); }; const toggleTopBuildLike = async (buildId: number) => { if (!user) return; const build = topBuilds.find(b => b.build_id === buildId); if (!build) return; // 🚨 Prevent liking own build if (build.user_id === user.user_id) { setLikeOwnPopupOpen(true); return; } const isLiked = likedTopBuildIds.includes(buildId); if (isLiked) { await supabase .from("build_likes") .delete() .eq("build_id", buildId) .eq("user_id", user.user_id); setLikedTopBuildIds(prev => prev.filter(id => id !== buildId)); setTopBuilds(prev => prev.map(b => b.build_id === buildId ? { ...b, likes: Math.max((b.likes || 1) - 1, 0) } : b ) ); } else { await supabase .from("build_likes") .insert({ build_id: buildId, user_id: user.user_id, }); setLikedTopBuildIds(prev => [...prev, buildId]); setTopBuilds(prev => prev.map(b => b.build_id === buildId ? { ...b, likes: (b.likes || 0) + 1 } : b ) ); // Send push notification to the build owner (non-blocking) try { const payload = { userId: build.user_id, notificationType: 'buildLike', title: `${user.user_name} liked your build`, body: `${user.user_name} liked ${build.build_name}`, url: `/builds/${buildId}`, }; console.log('Sending push notification payload:', payload); const res = await fetch('/api/notifications/send', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); const sendResult = await res.json(); console.log('Push send result:', sendResult); } catch (err) { console.error('Error sending push notification after like:', err); } } }; const handleUnlikeBuild = async (buildId: number) => { if (!user) return; const { error } = await supabase .from("build_likes") .delete() .eq("build_id", buildId) .eq("user_id", user.user_id); if (error) { console.error("Error unliking build:", error); return; } // Remove from liked list immediately (optimistic UI) setLikedBuilds(prev => prev.filter(row => row.build.build_id !== buildId)); // Update total likes stat setTotalLikes(prev => Math.max(prev - 1, 0)); }; const topUserBuilds = [...userBuilds] .sort((a, b) => b.likes - a.likes) .slice(0, 3); if (isLoading || pageLoading) { return (

Loading...

) } if (!user) { return null } const handleDeleteBuild = async () => { if (!deleteBuildId) return const { error } = await supabase .from("builds") .delete() .eq("build_id", deleteBuildId) .eq("user_id", user.user_id) if (error) { console.error("Error deleting build:", error) swal.error("Failed to delete build", "Please try again.") return } setUserBuilds((prev) => prev.filter((b) => b.build_id !== deleteBuildId)) setDeleteBuildId(null) } // Dynamic achievements based on Supabase data const achievements = [ { id: 1, name: "First Build", description: "Created your first PC build", earned: userBuilds.length >= 1, icon: Wrench, }, { id: 2, name: "Community Favorite", description: "Received 100+ likes", earned: totalLikes >= 100, icon: Heart, }, { id: 3, name: "Build Master", description: "Created 10+ builds", earned: userBuilds.length >= 10, icon: Trophy, }, { id: 4, name: "Guide Follower", description: "Completed a build guide", earned: true, // still static icon: BookOpen, }, ] return (

Dashboard

Manage your builds, track your progress, and connect with the community.

{/* User Stats Cards */}

Total Builds

{userBuilds.length}

Total Likes

{totalLikes}

{ setFollowTab("following") setFollowModalOpen(true) }} >

Following

{following.length}

{ setFollowTab("followers") setFollowModalOpen(true) }} >

Followers

{followers.length}

Achievements

{achievements.filter((a) => a.earned).length}/{achievements.length}

Overview My Builds Liked Builds Achievements Activity
{topUserBuilds.length > 0 && ( Your Top Builds Your most liked builds
setPauseAutoScroll(true)} onMouseLeave={() => setPauseAutoScroll(false)} >
{topUserBuilds.map((build, index) => (
{build.build_name} {build.description || "No description"}
{build.avatar_url ? ( ) : ( {build.user_name?.[0] || "U"} )} {build.user_name || "Unknown"}
{formatCurrency(build.total_price || 0)}
{build.date_created ? new Date(build.date_created).toLocaleDateString() : "-"}
{(build.views || 0).toLocaleString()} views
{build.likes}
))}
)} {/* Top Community Builds Card */} Top Community Builds Most liked builds in the community {topBuilds.length === 0 ? (

No builds available yet.

) : (
setPauseAutoScroll(true)} onMouseLeave={() => setPauseAutoScroll(false)} >
{topBuilds.map((build: any) => { const isTopCommunityBuildLiked = likedTopBuildIds.includes(build.build_id); return (
{build.build_name} {build.description || "No description"}
{build.avatar_url ? ( ) : ( {build.user_name?.[0] || "U"} )} {build.user_name || "Unknown"}
{formatCurrency(build.total_price || 0)}
{build.date_created ? new Date(build.date_created).toLocaleDateString() : "-"}
{(build.views || 0).toLocaleString()} views
) })}
)}
Recent Activity Your recent activities
{recentActivity.slice(0, 5).map((activity) => (

{activity.description}

{activity.time}

))}
My Builds ({userBuilds.length}) Your saved PC configurations
{userBuilds.length === 0 ? (

No builds yet

Start building your first PC configuration

) : (
{userBuilds.slice(0, 4).map((build) => (
{build.build_name} {build.description || "No description"}
{build.avatar_url ? ( ) : ( {build.user_name?.[0] || "U"} )} {build.user_name || "Unknown"}
{formatCurrency(build.total_price || 0)}
{build.date_created ? new Date(build.date_created).toLocaleDateString() : "-"}
{(build.views || 0).toLocaleString()} views
{build.build_types?.type_name && ( {build.build_types.type_name} )}
{build.likes}
))}
)} {userBuilds.length > 4 && (
)}
Liked Builds ({likedBuilds.length}) Builds you've liked from the community {likedBuilds.length === 0 ? (

No liked builds yet

Explore the community and like builds that inspire you

) : (
{likedBuilds.slice(0, 4).map((row: any) => { const build = row.build; return (
{build.build_name} {build.description || "No description"}
{build.avatar_url ? ( ) : ( {build.user_name?.[0] || "U"} )} {build.user_name || "Unknown"}
{formatCurrency(build.total_price || 0)}
{build.date_created ? new Date(build.date_created).toLocaleDateString() : "-"}
{(build.views || 0).toLocaleString()} views
{build.build_types?.type_name && ( {build.build_types.type_name} )}
) })}
)} {likedBuilds.length > 4 && (
)}
Achievements Track your progress and unlock new badges
{achievements.map((achievement) => (

{achievement.name}

{achievement.description}

{achievement.earned && Earned}
))}
Activity Timeline
{recentActivity.map((activity, index) => (
{index < recentActivity.length - 1 && (
)}

{activity.description}

{activity.time}

))}
setDeleteBuildId(null)}> Delete build? This action cannot be undone. This will permanently delete your build. Cancel Delete {/* Share Popup */} {sharePopupOpen && (

Share Build

Copy the link below to share this build with others.

)} {/* Followers / Following Modal */} Connections {/* Tabs triggers stay at top */} Following Followers {/* Scrollable area */}
{followTab === "following" && (
{following.length === 0 ? (

Not following anyone yet.

) : ( following.map((f) => ( setFollowModalOpen(false)} > {f.avatar ? : {f.username[0]}}

{f.username}

)) )}
)} {followTab === "followers" && (
{followers.length === 0 ? (

No followers yet.

) : ( followers.map((f) => ( setFollowModalOpen(false)} > {f.avatar ? : {f.username[0]}}

{f.username}

)) )}
)}
{/* Popup for trying to like own build */} {likeOwnPopupOpen && (

Oops!

You cannot like your own build.

)} {deletePopupOpen && buildToDelete && (

Confirm Delete

Are you sure you want to delete "{buildToDelete.build_name}"? This action cannot be undone.

)}
) }