1+ import Link from "next/link"
2+ import { Container } from "@/components/ui/container"
3+ import { Card , CardContent , CardDescription , CardHeader , CardTitle } from "@/components/ui/card"
4+ import { Button } from "@/components/ui/button"
5+ import { Badge } from "@/components/ui/badge"
6+ import { Separator } from "@/components/ui/separator"
7+ import { GitHubLogoIcon , ExternalLinkIcon , StarIcon , CopyIcon , CalendarIcon , PersonIcon } from "@radix-ui/react-icons"
8+ import { getAllProjects , getProjectBySlug } from "@/lib/project-data"
9+ import { notFound } from "next/navigation"
10+ import ReactMarkdown from "react-markdown"
11+
12+ interface ProjectPageProps {
13+ params : {
14+ slug : string
15+ }
16+ }
17+
18+ export async function generateStaticParams ( ) {
19+ const projects = getAllProjects ( )
20+ return projects . map ( ( project ) => ( {
21+ slug : project . slug ,
22+ } ) )
23+ }
24+
25+ export async function generateMetadata ( { params } : ProjectPageProps ) {
26+ const project = getProjectBySlug ( params . slug )
27+
28+ if ( ! project ) {
29+ return {
30+ title : "Project Not Found" ,
31+ }
32+ }
33+
34+ return {
35+ title : `${ project . name } | CodeStorm Hub Projects` ,
36+ description : project . overview || project . description || `Learn more about ${ project . name } - an innovative project by CodeStorm Hub.` ,
37+ openGraph : {
38+ title : project . name ,
39+ description : project . overview || project . description ,
40+ type : "website" ,
41+ } ,
42+ }
43+ }
44+
45+ export default function ProjectPage ( { params } : ProjectPageProps ) {
46+ const project = getProjectBySlug ( params . slug )
47+
48+ if ( ! project ) {
49+ notFound ( )
50+ }
51+
52+ const getStatusBadge = ( status : string | undefined ) => {
53+ const statusConfig = {
54+ 'Active' : { emoji : '✅' , color : 'bg-green-100 text-green-800 border-green-200' } ,
55+ 'Archived' : { emoji : '📦' , color : 'bg-gray-100 text-gray-800 border-gray-200' } ,
56+ 'Maintenance' : { emoji : '🚧' , color : 'bg-yellow-100 text-yellow-800 border-yellow-200' } ,
57+ 'In Development' : { emoji : '🔄' , color : 'bg-blue-100 text-blue-800 border-blue-200' } ,
58+ 'Under Construction' : { emoji : '🏗️' , color : 'bg-orange-100 text-orange-800 border-orange-200' } ,
59+ }
60+ const config = statusConfig [ status as keyof typeof statusConfig ] || statusConfig [ 'Active' ]
61+ return (
62+ < Badge variant = "outline" className = { `${ config . color } text-sm font-medium` } >
63+ < span className = "mr-2" > { config . emoji } </ span >
64+ { status }
65+ </ Badge >
66+ )
67+ }
68+
69+ const getTechBadgeColor = ( tech : string ) => {
70+ const colors : Record < string , string > = {
71+ 'JavaScript' : 'bg-yellow-100 text-yellow-800 border-yellow-200' ,
72+ 'TypeScript' : 'bg-blue-100 text-blue-800 border-blue-200' ,
73+ 'Python' : 'bg-green-100 text-green-800 border-green-200' ,
74+ 'PHP' : 'bg-purple-100 text-purple-800 border-purple-200' ,
75+ 'Laravel' : 'bg-red-100 text-red-800 border-red-200' ,
76+ 'Vue.js' : 'bg-emerald-100 text-emerald-800 border-emerald-200' ,
77+ 'React' : 'bg-cyan-100 text-cyan-800 border-cyan-200' ,
78+ 'Next.js' : 'bg-gray-100 text-gray-800 border-gray-200' ,
79+ 'HTML' : 'bg-orange-100 text-orange-800 border-orange-200' ,
80+ 'CSS' : 'bg-pink-100 text-pink-800 border-pink-200' ,
81+ }
82+ return colors [ tech ] || 'bg-secondary text-secondary-foreground border-border'
83+ }
84+
85+ return (
86+ < div className = "py-12" >
87+ < Container >
88+ < div className = "space-y-8" >
89+ { /* Breadcrumb */ }
90+ < nav className = "flex items-center space-x-2 text-sm text-muted-foreground" >
91+ < Link href = "/projects" className = "hover:text-foreground transition-colors" > Projects</ Link >
92+ < span > /</ span >
93+ < span className = "text-foreground font-medium" > { project . name } </ span >
94+ </ nav >
95+
96+ { /* Project Header */ }
97+ < div className = "space-y-6" >
98+ < div className = "flex flex-col lg:flex-row lg:items-start lg:justify-between gap-6" >
99+ < div className = "space-y-4" >
100+ < div className = "flex items-center gap-3" >
101+ < h1 className = "text-4xl font-bold tracking-tight" > { project . name } </ h1 >
102+ { project . primaryLanguage && (
103+ < Badge variant = "outline" className = { `${ getTechBadgeColor ( project . primaryLanguage ) } text-sm` } >
104+ { project . primaryLanguage }
105+ </ Badge >
106+ ) }
107+ </ div >
108+
109+ < div className = "flex flex-wrap items-center gap-3" >
110+ { getStatusBadge ( project . status ) }
111+ { project . category && (
112+ < Badge variant = "secondary" className = "text-sm" >
113+ { project . category }
114+ </ Badge >
115+ ) }
116+ { project . visibility && (
117+ < Badge variant = "outline" className = { `text-sm ${
118+ project . visibility === 'Public'
119+ ? 'bg-green-50 text-green-700 border-green-200'
120+ : 'bg-gray-50 text-gray-700 border-gray-200'
121+ } `} >
122+ { project . visibility === 'Public' ? '🌐' : '🔐' } { project . visibility }
123+ </ Badge >
124+ ) }
125+ </ div >
126+
127+ < p className = "text-xl text-muted-foreground max-w-3xl" >
128+ { project . overview || project . description || 'An innovative project demonstrating modern development practices and solving real-world challenges.' }
129+ </ p >
130+ </ div >
131+
132+ { /* Action Buttons */ }
133+ < div className = "flex flex-col sm:flex-row gap-3 lg:flex-col" >
134+ { project . liveDemo && (
135+ < Button size = "lg" asChild >
136+ < a href = { project . liveDemo } target = "_blank" rel = "noopener noreferrer" >
137+ < ExternalLinkIcon className = "mr-2 h-5 w-5" />
138+ Live Demo
139+ </ a >
140+ </ Button >
141+ ) }
142+ { project . github && (
143+ < Button variant = "outline" size = "lg" asChild >
144+ < a href = { project . github } target = "_blank" rel = "noopener noreferrer" >
145+ < GitHubLogoIcon className = "mr-2 h-5 w-5" />
146+ View on GitHub
147+ </ a >
148+ </ Button >
149+ ) }
150+ </ div >
151+ </ div >
152+
153+ { /* Project Stats */ }
154+ { project . github && (
155+ < div className = "flex items-center gap-6 text-muted-foreground" >
156+ { project . stars !== undefined && (
157+ < div className = "flex items-center gap-2" >
158+ < StarIcon className = "h-5 w-5" />
159+ < span className = "text-lg font-medium" > { project . stars . toLocaleString ( ) } </ span >
160+ < span className = "text-sm" > stars</ span >
161+ </ div >
162+ ) }
163+ { project . forks !== undefined && (
164+ < div className = "flex items-center gap-2" >
165+ < CopyIcon className = "h-5 w-5" />
166+ < span className = "text-lg font-medium" > { project . forks } </ span >
167+ < span className = "text-sm" > forks</ span >
168+ </ div >
169+ ) }
170+ { project . lastUpdated && (
171+ < div className = "flex items-center gap-2" >
172+ < CalendarIcon className = "h-5 w-5" />
173+ < span className = "text-sm" > Updated { project . lastUpdated } </ span >
174+ </ div >
175+ ) }
176+ </ div >
177+ ) }
178+ </ div >
179+
180+ < Separator />
181+
182+ < div className = "grid grid-cols-1 lg:grid-cols-3 gap-8" >
183+ { /* Main Content */ }
184+ < div className = "lg:col-span-2 space-y-8" >
185+ { /* Key Features */ }
186+ { project . keyFeatures && project . keyFeatures . length > 0 && (
187+ < Card >
188+ < CardHeader >
189+ < CardTitle className = "text-xl" > ✨ Key Features</ CardTitle >
190+ </ CardHeader >
191+ < CardContent >
192+ < ul className = "space-y-3" >
193+ { project . keyFeatures . map ( ( feature , idx ) => {
194+ const [ title , description ] = feature . split ( ': ' )
195+ return (
196+ < li key = { idx } className = "flex gap-3" >
197+ < span className = "text-primary font-medium" > •</ span >
198+ < div >
199+ < span className = "font-medium" > { title } :</ span >
200+ < span className = "text-muted-foreground ml-1" > { description } </ span >
201+ </ div >
202+ </ li >
203+ )
204+ } ) }
205+ </ ul >
206+ </ CardContent >
207+ </ Card >
208+ ) }
209+
210+ { /* Full Documentation */ }
211+ < Card >
212+ < CardHeader >
213+ < CardTitle className = "text-xl" > 📋 Complete Documentation</ CardTitle >
214+ < CardDescription >
215+ Comprehensive project documentation including setup, usage, and contribution guidelines.
216+ </ CardDescription >
217+ </ CardHeader >
218+ < CardContent >
219+ < div className = "prose prose-gray max-w-none dark:prose-invert" >
220+ < ReactMarkdown > { project . content } </ ReactMarkdown >
221+ </ div >
222+ </ CardContent >
223+ </ Card >
224+ </ div >
225+
226+ { /* Sidebar */ }
227+ < div className = "space-y-6" >
228+ { /* Technology Stack */ }
229+ { project . techStack && project . techStack . length > 0 && (
230+ < Card >
231+ < CardHeader >
232+ < CardTitle className = "text-lg" > 🛠️ Technology Stack</ CardTitle >
233+ </ CardHeader >
234+ < CardContent >
235+ < div className = "flex flex-wrap gap-2" >
236+ { project . techStack . map ( ( tech , idx ) => (
237+ < Badge key = { idx } variant = "outline" className = { getTechBadgeColor ( tech ) } >
238+ { tech }
239+ </ Badge >
240+ ) ) }
241+ </ div >
242+ </ CardContent >
243+ </ Card >
244+ ) }
245+
246+ { /* Project Details */ }
247+ < Card >
248+ < CardHeader >
249+ < CardTitle className = "text-lg" > 📋 Project Details</ CardTitle >
250+ </ CardHeader >
251+ < CardContent className = "space-y-3" >
252+ { project . architecture && (
253+ < div >
254+ < span className = "text-sm font-medium text-muted-foreground" > Architecture:</ span >
255+ < p className = "text-sm" > { project . architecture } </ p >
256+ </ div >
257+ ) }
258+ { project . repositorySize && (
259+ < div >
260+ < span className = "text-sm font-medium text-muted-foreground" > Repository Size:</ span >
261+ < p className = "text-sm" > { project . repositorySize } </ p >
262+ </ div >
263+ ) }
264+ { project . languages && project . languages . length > 0 && (
265+ < div >
266+ < span className = "text-sm font-medium text-muted-foreground" > Languages:</ span >
267+ < p className = "text-sm" > { project . languages . join ( ', ' ) } </ p >
268+ </ div >
269+ ) }
270+ </ CardContent >
271+ </ Card >
272+
273+ { /* Contributors */ }
274+ { project . contributors && project . contributors . length > 0 && (
275+ < Card >
276+ < CardHeader >
277+ < CardTitle className = "text-lg" > 👥 Contributors</ CardTitle >
278+ </ CardHeader >
279+ < CardContent >
280+ < div className = "space-y-2" >
281+ { project . contributors . map ( ( contributor , idx ) => (
282+ < div key = { idx } className = "flex items-center gap-2" >
283+ < PersonIcon className = "h-4 w-4" />
284+ < span className = "text-sm" > { contributor } </ span >
285+ </ div >
286+ ) ) }
287+ </ div >
288+ </ CardContent >
289+ </ Card >
290+ ) }
291+
292+ { /* Tags */ }
293+ { project . tags && project . tags . length > 0 && (
294+ < Card >
295+ < CardHeader >
296+ < CardTitle className = "text-lg" > 🏷️ Tags</ CardTitle >
297+ </ CardHeader >
298+ < CardContent >
299+ < div className = "flex flex-wrap gap-2" >
300+ { project . tags . map ( ( tag , idx ) => (
301+ < Badge key = { idx } variant = "secondary" className = "text-xs" >
302+ #{ tag }
303+ </ Badge >
304+ ) ) }
305+ </ div >
306+ </ CardContent >
307+ </ Card >
308+ ) }
309+ </ div >
310+ </ div >
311+ </ div >
312+ </ Container >
313+ </ div >
314+ )
315+ }
0 commit comments