diff --git a/dongle/app/discover/page.tsx b/dongle/app/discover/page.tsx index d7bf011..3f39b2e 100644 --- a/dongle/app/discover/page.tsx +++ b/dongle/app/discover/page.tsx @@ -4,11 +4,13 @@ import { useState, useMemo } from "react"; import { mockProjects } from "@/data/mockProjects"; import { ProjectCard } from "@/components/projects/ProjectCard"; import { Button } from "@/components/ui/Button"; -import { Search, Filter } from "lucide-react"; +import { Spinner } from "@/components/ui/Spinner"; +import { Search, Filter, Package } from "lucide-react"; const ITEMS_PER_PAGE = 9; export default function DiscoverPage() { + const [isInitialLoading, setIsInitialLoading] = useState(true); const [searchQuery, setSearchQuery] = useState(""); const [selectedCategory, setSelectedCategory] = useState("All"); const [sortBy, setSortBy] = useState<"rating" | "newest" | "popular">("rating"); @@ -16,6 +18,14 @@ export default function DiscoverPage() { const [page, setPage] = useState(1); const [isLoadingMore, setIsLoadingMore] = useState(false); + // Simulate initial data loading + useEffect(() => { + const timer = setTimeout(() => { + setIsInitialLoading(false); + }, 800); + return () => clearTimeout(timer); + }, []); + const categories = [ "All", ...Array.from(new Set(mockProjects.map((p) => p.category))), @@ -109,7 +119,8 @@ export default function DiscoverPage() { onChange={(e) => handleSortChange(e.target.value as "rating" | "newest" | "popular") } - className="px-4 py-2 bg-zinc-100 dark:bg-zinc-800 border border-transparent rounded-xl text-sm font-medium focus:outline-none focus:ring-2 focus:ring-blue-500/20" + disabled={isInitialLoading} + className="px-4 py-2 bg-zinc-100 dark:bg-zinc-800 border border-transparent rounded-xl text-sm font-medium focus:outline-none focus:ring-2 focus:ring-blue-500/20 disabled:opacity-50 disabled:cursor-not-allowed" > @@ -119,12 +130,11 @@ export default function DiscoverPage() { - {/* Grid */} - {visibleProjects.length > 0 ? ( -
- {visibleProjects.map((project) => ( - - ))} + {/* Loading State */} + {isInitialLoading ? ( +
+ +

Loading projects...

) : (
@@ -161,11 +171,6 @@ export default function DiscoverPage() {
)} - -
- Showing {visibleProjects.length} of{" "} - {filteredAndSortedProjects.length} projects -
); diff --git a/dongle/app/projects/[id]/page.tsx b/dongle/app/projects/[id]/page.tsx new file mode 100644 index 0000000..a6ab2f0 --- /dev/null +++ b/dongle/app/projects/[id]/page.tsx @@ -0,0 +1,268 @@ +"use client"; + +import React, { useState, useEffect } from "react"; +import { useParams, useRouter } from "next/navigation"; +import LayoutWrapper from "@/components/layout/LayoutWrapper"; +import { mockProjects } from "@/data/mockProjects"; +import { Badge } from "@/components/ui/Badge"; +import { Button } from "@/components/ui/Button"; +import { Spinner } from "@/components/ui/Spinner"; +import VerificationStatus from "@/components/verify/VerificationStatus"; +import ReviewList from "@/components/reviews/ReviewList"; +import { Review } from "@/types/review"; +import { + ArrowLeft, + ExternalLink, + Github, + Globe, + Star, + MessageSquare, + Calendar, + AlertCircle, +} from "lucide-react"; + +export default function ProjectDetailPage() { + const params = useParams(); + const router = useRouter(); + const projectId = params.id as string; + + const [isLoading, setIsLoading] = useState(true); + const [project, setProject] = useState(null); + const [reviews, setReviews] = useState([]); + const [currentUserAddress] = useState(null); + + useEffect(() => { + // Simulate data loading + const timer = setTimeout(() => { + const foundProject = mockProjects.find((p) => p.id === projectId); + setProject(foundProject || null); + + // Mock reviews data + if (foundProject) { + setReviews([ + { + id: "rev-1", + projectId: foundProject.id, + projectName: foundProject.name, + userAddress: "GABC...XYZ1", + rating: 5, + comment: "Excellent project! The team is very responsive and the product works flawlessly.", + createdAt: new Date(Date.now() - 86400000 * 2).toISOString(), + }, + { + id: "rev-2", + projectId: foundProject.id, + projectName: foundProject.name, + userAddress: "GDEF...XYZ2", + rating: 4, + comment: "Great concept and execution. Looking forward to future updates.", + createdAt: new Date(Date.now() - 86400000 * 5).toISOString(), + }, + ]); + } + + setIsLoading(false); + }, 600); + + return () => clearTimeout(timer); + }, [projectId]); + + const handleEdit = (review: Review) => { + console.log("Edit review:", review); + }; + + const handleDelete = (id: string) => { + console.log("Delete review:", id); + }; + + if (isLoading) { + return ( + +
+
+
+ +

Loading project details...

+
+
+
+
+ ); + } + + if (!project) { + return ( + +
+
+
+ +

Project Not Found

+

+ The project you're looking for doesn't exist or has been removed. +

+ +
+
+
+
+ ); + } + + return ( + +
+
+ {/* Back Button */} + + +
+ {/* Main Content */} +
+ {/* Project Header */} +
+
+
+ + {project.category} + +

{project.name}

+
+
+ + + {project.rating} + + ({project.reviews} reviews) +
+
+ + + {new Date(project.createdAt).toLocaleDateString("en-US", { + year: "numeric", + month: "long", + day: "numeric", + })} + +
+
+
+
+ + {/* Project Image Placeholder */} +
+
+ {project.name[0]} +
+
+ + {/* Description */} +
+

About

+

+ {project.description} +

+
+ + {/* Links */} +
+ + +
+
+ + {/* Reviews Section */} +
+
+

+ + Reviews +

+ +
+ +
+
+ + {/* Sidebar */} +
+ {/* Verification Status */} +
+

Verification Status

+ +
+ + {/* Quick Stats */} +
+

Quick Stats

+
+
+ Rating + {project.rating} / 5.0 +
+
+ Total Reviews + {project.reviews} +
+
+ Category + {project.category} +
+
+
+ + {/* Actions */} +
+

Actions

+
+ + +
+
+
+
+
+
+
+ ); +} diff --git a/dongle/components/projects/ProjectCard.tsx b/dongle/components/projects/ProjectCard.tsx index d8b3002..5c4734c 100644 --- a/dongle/components/projects/ProjectCard.tsx +++ b/dongle/components/projects/ProjectCard.tsx @@ -1,4 +1,5 @@ import React from "react"; +import Link from "next/link"; import { Project } from "@/data/mockProjects"; interface ProjectCardProps { @@ -7,7 +8,7 @@ interface ProjectCardProps { export const ProjectCard = ({ project }: ProjectCardProps) => { return ( -
+
{project.name[0]} @@ -33,6 +34,6 @@ export const ProjectCard = ({ project }: ProjectCardProps) => {
{project.reviews} reviews
-
+ ); };