'use client' import { Eye, Heart, Settings } from 'lucide-react' import Link from 'next/link' import { useEffect, useState } from 'react' import type { Project } from '../lib/projects/types' import type { LocalProject } from '../lib/local-storage/project-store' import { ProjectSettingsDialog } from './project-settings-dialog' import { getUserProjectLikes, toggleProjectLike } from '../lib/projects/actions' import { useAuth } from '../lib/auth/hooks' interface ProjectGridProps { projects: (Project | LocalProject)[] onProjectClick: (id: string) => void onViewClick?: (id: string) => void onSaveToCloud?: (project: LocalProject) => void showOwner: boolean isLocal?: boolean canEdit?: boolean onUpdate?: () => void } function isLocalProject(prop: Project | LocalProject): prop is LocalProject { return 'is_local' in prop && prop.is_local === true } export function ProjectGrid({ projects, onProjectClick, onViewClick, onSaveToCloud, showOwner, isLocal = false, canEdit = false, onUpdate, }: ProjectGridProps) { const { isAuthenticated } = useAuth() const [settingsProject, setSettingsProject] = useState(null) const [userLikes, setUserLikes] = useState>({}) const [likeCounts, setLikeCounts] = useState>({}) // Initialize like counts from projects useEffect(() => { const counts: Record = {} projects.forEach((proj) => { if (!isLocalProject(proj)) { counts[proj.id] = proj.likes } }) setLikeCounts(counts) }, [projects]) // Fetch which projects the user has liked useEffect(() => { if (!isAuthenticated) { setUserLikes({}) return } const projectIds = projects .filter((p) => !isLocalProject(p)) .map((p) => p.id) if (projectIds.length === 0) return getUserProjectLikes(projectIds).then((result) => { if (result.success && result.data) { setUserLikes(result.data) } }) }, [projects, isAuthenticated]) const handleSettingsClick = (e: React.MouseEvent, project: Project | LocalProject) => { e.stopPropagation() if (!isLocalProject(project)) { setSettingsProject(project) } } const handleViewClick = (e: React.MouseEvent, projectId: string) => { e.stopPropagation() onViewClick?.(projectId) } const handleLikeClick = async (e: React.MouseEvent, projectId: string) => { e.stopPropagation() if (!isAuthenticated) { // Could show a sign-in prompt here return } // Optimistic update const wasLiked = userLikes[projectId] || false const currentCount = likeCounts[projectId] || 0 setUserLikes((prev) => ({ ...prev, [projectId]: !wasLiked })) setLikeCounts((prev) => ({ ...prev, [projectId]: wasLiked ? currentCount - 1 : currentCount + 1 })) // Call server action const result = await toggleProjectLike(projectId) if (result.success && result.data) { // Update with actual values from server const data = result.data setUserLikes((prev) => ({ ...prev, [projectId]: data.liked })) setLikeCounts((prev) => ({ ...prev, [projectId]: data.likes })) } else { // Revert on error setUserLikes((prev) => ({ ...prev, [projectId]: wasLiked })) setLikeCounts((prev) => ({ ...prev, [projectId]: currentCount })) } } return ( <>
{projects.map((project) => { const owner = !isLocalProject(project) ? project.owner : null return (
onProjectClick(project.id)} className="group text-left cursor-pointer" > {/* Thumbnail card */}
{!isLocalProject(project) && project.thumbnail_url ? ( {project.name} ) : (
No preview
)} {isLocalProject(project) && (
{isAuthenticated && onSaveToCloud ? ( ) : (
Local
)}
)} {canEdit && !isLocalProject(project) && (
{onViewClick && ( )}
)}
{/* Info row below the card */}
{/* Avatar */} {showOwner && owner ? ( e.stopPropagation()} className="shrink-0" > {owner.image ? ( {owner.name} ) : (
{owner.name?.[0]?.toUpperCase() || '?'}
)} ) : null} {/* Name + stats */}

{project.name}

{showOwner && owner && ( <> e.stopPropagation()} className="hover:text-foreground transition-colors truncate" > {owner.username || owner.name} {!isLocalProject(project) && ·} )} {!isLocalProject(project) && ( <>
{project.views}
· )} {isLocalProject(project) && ( {new Date(project.updated_at).toLocaleDateString()} )}
) })}
{/* Settings Dialog */} {settingsProject && ( !open && setSettingsProject(null)} onUpdate={onUpdate} onDelete={() => { setSettingsProject(null) onUpdate?.() }} /> )} ) }