"use client" import { useState, useEffect } from "react" import Link from "next/link" import { usePathname } from "next/navigation" import { Button } from "@/components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Badge } from "@/components/ui/badge" import { Input } from "@/components/ui/input" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { Cpu, Heart, MessageCircle, Share, Search, Monitor, Users, Eye, BarChart3, Calendar} from "lucide-react" import { buildService, buildComponentService } from "@/lib/database" import { formatCurrency, PRICE_RANGES } from "@/lib/currency" import { useAuth } from "@/contexts/supabase-auth-context" import { supabase } from "@/lib/supabase" import { swal } from "@/lib/sweetalert" interface BuildWithDetails { build_id: number build_name: string date_created: string user_id: number build_type_id: number users: { user_id: number user_name: string email: string avatar_url: string } build_types: { build_type_id: number type_name: string } components?: any[] totalPrice?: number likes?: number comments?: number views?: number description: string } export default function BuildsPage() { const { user } = useAuth() const pathname = usePathname() const [builds, setBuilds] = useState([]) const [statistics, setStatistics] = useState({ totalBuilders: 0, totalBuilds: 0, totalLikes: 0 }) const [searchTerm, setSearchTerm] = useState("") const [sortBy, setSortBy] = useState("popular") const [priceFilter, setPriceFilter] = useState("all") const [likedBuilds, setLikedBuilds] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [sharePopupOpen, setSharePopupOpen] = useState(false) const [shareUrl, setShareUrl] = useState("") // Fetch builds and statistics useEffect(() => { fetchBuilds() fetchStatistics() const buildsChannel = supabase .channel('builds-changes') .on('postgres_changes', { event: '*', schema: 'public', table: 'builds' }, () => { fetchBuilds() fetchStatistics() }) .subscribe() return () => { supabase.removeChannel(buildsChannel) } }, [pathname, user]) // Refetch when pathname changes (navigation) or user changes // Fetch user's liked builds useEffect(() => { if (!user) return; const fetchLiked = async () => { const { data, error } = await supabase .from("build_likes") .select("build_id") .eq("user_id", user.user_id) if (!error && data) setLikedBuilds(data.map((r) => r.build_id)) } fetchLiked() }, [user]) const fetchBuilds = async () => { if (!user) return try { setLoading(true) setError(null) // 1️⃣ Builds the user liked const { data: likedData, error: likedError } = await supabase .from("build_likes") .select("build_id") .eq("user_id", user.user_id) if (likedError) throw likedError const likedIds = likedData?.map(r => r.build_id) || [] if (likedIds.length === 0) { setBuilds([]) return } // 2️⃣ Fetch builds const { data: buildsData, error: buildsError } = await supabase .from("builds") .select(` *, users:user_id ( user_id, user_name, email, avatar_url ), build_types:build_type_id ( build_type_id, type_name ) `) .in("build_id", likedIds) if (buildsError) throw buildsError // 3️⃣ Enrich builds const buildsWithDetails = await Promise.all( buildsData.map(async (build: any) => { const components = await buildComponentService.getBuildComponents(build.build_id) // ✅ Use total_price from the builds table const totalPrice = Number(build.total_price) || 0 // Likes count const { count: likesCount } = await supabase .from("build_likes") .select("*", { count: "exact", head: true }) .eq("build_id", build.build_id) // Comments count const { count: commentsCount } = await supabase .from("build_comments") .select("*", { count: "exact", head: true }) .eq("build_id", build.build_id) // Views count const { count: viewsCount } = await supabase .from("build_views") .select("*", { count: "exact", head: true }) .eq("build_id", build.build_id) return { ...build, components, totalPrice, likes: likesCount ?? 0, comments: commentsCount ?? 0, views: viewsCount ?? 0, } }) ) setBuilds(buildsWithDetails) } catch (err: any) { console.error(err) setError(err.message || "Failed to load builds") } finally { setLoading(false) } } const fetchStatistics = async () => { try { if (!user) return; // 1️⃣ Get build IDs the user liked const { data: likedData, error: likedError } = await supabase .from("build_likes") .select("build_id") .eq("user_id", user.user_id); if (likedError) throw likedError; const likedBuildIds = likedData?.map(r => r.build_id) || []; if (likedBuildIds.length === 0) { setStatistics({ totalBuilders: 0, totalBuilds: 0, totalLikes: 0 }); return; } // 2️⃣ Fetch build owners const { data: buildsData, error: buildsError } = await supabase .from("builds") .select("build_id, user_id") .in("build_id", likedBuildIds); if (buildsError) throw buildsError; // 3️⃣ Count total likes from build_likes const { count: totalLikes, error: likesCountError } = await supabase .from("build_likes") .select("*", { count: "exact", head: true }) .in("build_id", likedBuildIds); if (likesCountError) throw likesCountError; // 4️⃣ Unique builders const uniqueBuilders = new Set(buildsData.map(b => b.user_id)); setStatistics({ totalBuilders: uniqueBuilders.size, totalBuilds: likedBuildIds.length, totalLikes: totalLikes || 0 }); } catch (err) { console.error("Error fetching statistics:", err); setStatistics({ totalBuilders: 0, totalBuilds: 0, totalLikes: 0 }); } }; const handleLike = async (build: BuildWithDetails) => { if (!user) return const isLiked = likedBuilds.includes(build.build_id) if (build.users?.user_id === user.user_id) { swal.warning("Cannot like", "You cannot like your own build."); return } if (isLiked) { const { error } = await supabase .from("build_likes") .delete() .eq("build_id", build.build_id) .eq("user_id", user.user_id) if (!error) { setLikedBuilds(prev => prev.filter(id => id !== build.build_id)) setBuilds(prev => prev.map(b => b.build_id === build.build_id ? { ...b, likes: Math.max((b.likes || 1) - 1, 0) } : b ) ) } } else { const { error } = await supabase .from("build_likes") .insert({ build_id: build.build_id, user_id: user.user_id }) if (!error) { setLikedBuilds(prev => [...prev, build.build_id]) setBuilds(prev => prev.map(b => b.build_id === build.build_id ? { ...b, likes: (b.likes || 0) + 1 } : b ) ) // Send push notification to build owner (non-blocking) try { const payload = { userId: build.users?.user_id, notificationType: 'buildLike', title: `${user.user_name} liked your build`, body: `${user.user_name} liked ${build.build_name}`, url: `/builds/${build.build_id}`, }; console.log('Sending push notification payload (likedbuilds):', payload); fetch('/api/notifications/send', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }) .then(res => res.json().then(r => console.log('Push send result (likedbuilds):', r)).catch(() => {})) .catch(err => console.error('Error sending push (likedbuilds):', err)); } catch (err) { console.error('Error preparing push send (likedbuilds):', err); } } } } const getFilteredBuilds = (): BuildWithDetails[] => { let filtered = builds.filter( b => b.build_name.toLowerCase().includes(searchTerm.toLowerCase()) || b.build_types?.type_name.toLowerCase().includes(searchTerm.toLowerCase()) ) if (priceFilter !== "all") { filtered = filtered.filter(build => { const price = build.totalPrice || 0 switch (priceFilter) { case "budget": return price < PRICE_RANGES.budget.max case "mid": return price >= PRICE_RANGES.mid.min && price < PRICE_RANGES.mid.max case "high": return price >= PRICE_RANGES.high.min default: return true } }) } switch (sortBy) { case "recent": return filtered.sort((a, b) => new Date(b.date_created).getTime() - new Date(a.date_created).getTime()) case "price-low": return filtered.sort((a, b) => (a.totalPrice || 0) - (b.totalPrice || 0)) case "price-high": return filtered.sort((a, b) => (b.totalPrice || 0) - (a.totalPrice || 0)) case "likes": return filtered.sort((a, b) => (b.likes || 0) - (a.likes || 0)) default: return filtered.sort((a, b) => (b.likes || 0) - (a.likes || 0)) } } const filteredBuilds = getFilteredBuilds() const formatNumber = (num: number) => num.toLocaleString() if (loading && builds.length === 0) { return (

Loading builds...

) } return (
{/* Page Header */}

Liked Builds

These are the PC builds you've liked from the community

{error && (

{error}

)} {/* Stats */}
{formatNumber(statistics.totalBuilders)} builders
{formatNumber(statistics.totalBuilds)} builds
{formatNumber(statistics.totalLikes)} likes
setSearchTerm(e.target.value)} />
{filteredBuilds.map((build) => { const creator = build.users const isLiked = likedBuilds.includes(build.build_id) const cpuComponent = build.components?.find((bc: any) => bc.components?.component_categories?.category_name === 'CPU' ) const gpuComponent = build.components?.find((bc: any) => bc.components?.component_categories?.category_name === 'GPU' ) return (
{build.build_name}
{creator?.avatar_url ? ( ) : ( {creator?.user_name ? creator?.user_name?.charAt(0).toUpperCase() : "?"} )} {creator?.user_name || "Unknown"}
{formatCurrency(build.totalPrice || 0)}
{build.date_created ? new Date(build.date_created).toLocaleDateString() : "-"}
{(build.views || 0).toLocaleString()} views

{build?.description || 'PC Build'}

{/* Build tags */}
{build.build_types?.type_name || 'build'}
{/* Key components */}
{cpuComponent && (
{cpuComponent.components?.component_name}
)} {gpuComponent && (
{gpuComponent.components?.component_name}
)}
{/* Actions */}
) })}
{filteredBuilds.length === 0 && !loading && (

No liked builds found

Try adjusting your search terms or filters

)}
{/* Share Popup */} {sharePopupOpen && (

Share Build

Copy the link below to share this build with others.

)}
) }