Skip to content

Commit 8a51a51

Browse files
committed
add files
1 parent 5b9faee commit 8a51a51

File tree

3 files changed

+259
-11
lines changed

3 files changed

+259
-11
lines changed

apps/dashboard/src/lib/project-showcase-constants.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export const PROJECT_SHOWCASE_DATA = [
77
slug: "artifact",
88
description:
99
"Blockchain-based platform for creating and trading digital art NFTs.",
10-
image: "/placeholder.svg?height=200&width=300",
10+
// image: "ipfs://QmQJrpMvLApCnoQaoXVd8Rb5Wyjn8u6c8Ei2rG6KawCwWS",
1111
industries: ["Art", "Games"],
1212
link: "https://www.gotartifact.com/",
1313
case_study: "No case study available",
@@ -472,16 +472,6 @@ export const PROJECT_SHOWCASE_DATA = [
472472
link: "https://xaigames.io",
473473
case_study: "No case study available",
474474
},
475-
{
476-
id: 42,
477-
title: "nan",
478-
slug: "nan",
479-
description: "No description available",
480-
image: "/placeholder.svg?height=200&width=300",
481-
industries: ["nan"],
482-
link: "No link available",
483-
case_study: "No case study available",
484-
},
485475
];
486476

487477
export const PROJECT_SHOWCASE_INDUSTRIES = [
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { Badge } from "@/components/ui/badge";
2+
import { Button } from "@/components/ui/button";
3+
import {
4+
Card,
5+
CardContent,
6+
CardFooter,
7+
CardHeader,
8+
CardTitle,
9+
} from "@/components/ui/card";
10+
import { ExternalLink, FileText } from "lucide-react";
11+
import Image from "next/image";
12+
import Link from "next/link";
13+
import { useRouter } from "next/router";
14+
import { PROJECT_SHOWCASE_DATA } from "../../lib/project-showcase-constants";
15+
16+
export default function DetailPage() {
17+
const router = useRouter();
18+
const { slug } = router.query;
19+
20+
const project = PROJECT_SHOWCASE_DATA.find((p) => p.slug === slug);
21+
22+
if (!project) {
23+
return <div>Project not found</div>;
24+
}
25+
26+
return (
27+
<div className="container mx-auto px-4 py-8">
28+
<Card className="overflow-hidden">
29+
<div className="md:flex">
30+
<div className="md:w-1/2 md:pr-6">
31+
<CardHeader className="border-b md:border-b-0">
32+
<CardTitle className="font-bold text-2xl md:text-3xl">
33+
{project.title}
34+
</CardTitle>
35+
</CardHeader>
36+
<CardContent className="pt-6">
37+
<p className="mb-4 text-lg">{project.description}</p>
38+
<div className="mb-6 flex flex-wrap gap-2">
39+
{project.industries.map((industry) => (
40+
<Badge key={industry} variant="secondary">
41+
{industry}
42+
</Badge>
43+
))}
44+
</div>
45+
</CardContent>
46+
<CardFooter className="flex flex-col gap-4 sm:flex-row">
47+
<Button asChild className="w-full sm:w-auto">
48+
<Link
49+
href={project.link}
50+
target="_blank"
51+
rel="noopener noreferrer"
52+
>
53+
<ExternalLink className="mr-2 h-4 w-4" />
54+
Visit Project Website
55+
</Link>
56+
</Button>
57+
{project.case_study && (
58+
<Button asChild variant="outline" className="w-full sm:w-auto">
59+
<Link
60+
href={project.case_study}
61+
target="_blank"
62+
rel="noopener noreferrer"
63+
>
64+
<FileText className="mr-2 h-4 w-4" />
65+
Link to Case Study
66+
</Link>
67+
</Button>
68+
)}
69+
</CardFooter>
70+
</div>
71+
<div className="md:w-1/2">
72+
<div className="relative aspect-video md:aspect-square w-full h-full">
73+
<Image
74+
src={project.image}
75+
alt={`${project.title} Thumbnail`}
76+
layout="fill"
77+
objectFit="cover"
78+
className="rounded-b-lg md:rounded-r-lg md:rounded-bl-none"
79+
/>
80+
</div>
81+
</div>
82+
</div>
83+
</Card>
84+
</div>
85+
);
86+
}
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
"use client";
2+
3+
import { Badge } from "@/components/ui/badge";
4+
import { Button } from "@/components/ui/button";
5+
import {
6+
Card,
7+
CardContent,
8+
CardDescription,
9+
CardHeader,
10+
CardTitle,
11+
} from "@/components/ui/card";
12+
import {
13+
Pagination,
14+
PaginationContent,
15+
PaginationItem,
16+
PaginationLink,
17+
PaginationNext,
18+
PaginationPrevious,
19+
} from "@/components/ui/pagination";
20+
import { ArrowRight } from "lucide-react";
21+
import Image from "next/image";
22+
import Link from "next/link";
23+
import { useState } from "react";
24+
import {
25+
PROJECT_SHOWCASE_DATA,
26+
PROJECT_SHOWCASE_INDUSTRIES,
27+
PROJECT_SHOWCASE_ITEMS_PER_PAGE,
28+
} from "../../lib/project-showcase-constants";
29+
30+
export default function ProjectShowcase() {
31+
const [selectedIndustry, setSelectedIndustry] = useState("All");
32+
const [currentPage, setCurrentPage] = useState(1);
33+
34+
const filteredProjects =
35+
selectedIndustry === "All"
36+
? PROJECT_SHOWCASE_DATA
37+
: PROJECT_SHOWCASE_DATA.filter((project) =>
38+
project.industries?.includes(selectedIndustry),
39+
);
40+
41+
const totalPages = Math.ceil(
42+
filteredProjects.length / PROJECT_SHOWCASE_ITEMS_PER_PAGE,
43+
);
44+
const paginatedProjects = filteredProjects.slice(
45+
(currentPage - 1) * PROJECT_SHOWCASE_ITEMS_PER_PAGE,
46+
currentPage * PROJECT_SHOWCASE_ITEMS_PER_PAGE,
47+
);
48+
49+
return (
50+
<div className="min-h-screen bg-background">
51+
<header className="px-4 py-12 text-center md:px-6">
52+
<h1 className="mb-4 font-bold text-4xl">built on thirdweb</h1>
53+
<p className="mx-auto mb-8 max-w-2xl text-muted-foreground text-xl">
54+
Discover the latest web3 apps and games built on thirdweb.
55+
</p>
56+
<div className="flex justify-center gap-4">
57+
<Button size="lg" asChild>
58+
<Link href="https://portal.thirdweb.com/">
59+
Start building
60+
<ArrowRight className="ml-2 h-4 w-4" />
61+
</Link>
62+
</Button>
63+
<Button size="lg" variant="outline" asChild>
64+
<Link href="https://thirdweb.com/contact-us">Contact Us</Link>
65+
</Button>
66+
</div>
67+
</header>
68+
<main className="container mx-auto px-4 py-12 md:px-6">
69+
<section>
70+
<div className="mb-8">
71+
<div className="mb-8 flex flex-wrap justify-center gap-2">
72+
{PROJECT_SHOWCASE_INDUSTRIES.map((industry) => (
73+
<Button
74+
key={industry}
75+
variant={
76+
selectedIndustry === industry ? "default" : "outline"
77+
}
78+
onClick={() => {
79+
setSelectedIndustry(industry);
80+
setCurrentPage(1);
81+
}}
82+
>
83+
{industry}
84+
</Button>
85+
))}
86+
</div>
87+
</div>
88+
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3">
89+
{paginatedProjects.map((project) => (
90+
<Link
91+
key={project.id}
92+
href={`/project-showcase/${project.slug}`}
93+
className="block"
94+
>
95+
<Card className="flex h-full cursor-pointer flex-col overflow-hidden transition-shadow hover:shadow-lg">
96+
<Image
97+
src={project.image}
98+
alt={project.title}
99+
width={300}
100+
height={200}
101+
className="h-48 w-full object-cover"
102+
/>
103+
<CardHeader>
104+
<CardTitle>{project.title}</CardTitle>
105+
<CardDescription>{project.description}</CardDescription>
106+
</CardHeader>
107+
<CardContent className="flex-grow">
108+
<div className="flex flex-wrap gap-2">
109+
{project.industries?.map((industry) => (
110+
<Badge key={industry} variant="secondary">
111+
{industry}
112+
</Badge>
113+
))}
114+
</div>
115+
</CardContent>
116+
</Card>
117+
</Link>
118+
))}
119+
</div>
120+
<div className="mt-8 flex flex-col items-center">
121+
<div className="mb-4 text-muted-foreground text-sm">
122+
Showing {paginatedProjects.length} of {filteredProjects.length}{" "}
123+
projects in{" "}
124+
{selectedIndustry === "All" ? "all categories" : selectedIndustry}
125+
</div>
126+
<Pagination>
127+
<PaginationContent>
128+
<PaginationItem>
129+
<PaginationPrevious
130+
onClick={(e) => {
131+
e.preventDefault();
132+
setCurrentPage((prev) => Math.max(prev - 1, 1));
133+
}}
134+
className={
135+
currentPage === 1 ? "pointer-events-none opacity-50" : ""
136+
}
137+
/>
138+
</PaginationItem>
139+
{[...Array(totalPages)].map((_, i) => (
140+
<PaginationItem key={i}>
141+
<PaginationLink
142+
onClick={(e) => {
143+
e.preventDefault();
144+
setCurrentPage(i + 1);
145+
}}
146+
isActive={currentPage === i + 1}
147+
>
148+
{i + 1}
149+
</PaginationLink>
150+
</PaginationItem>
151+
))}
152+
<PaginationItem>
153+
<PaginationNext
154+
onClick={(e) => {
155+
e.preventDefault();
156+
setCurrentPage((prev) => Math.min(prev + 1, totalPages));
157+
}}
158+
className={
159+
currentPage === totalPages
160+
? "pointer-events-none opacity-50"
161+
: ""
162+
}
163+
/>
164+
</PaginationItem>
165+
</PaginationContent>
166+
</Pagination>
167+
</div>
168+
</section>
169+
</main>
170+
</div>
171+
);
172+
}

0 commit comments

Comments
 (0)