init: init repo with existing code

This commit is contained in:
2026-03-30 20:20:21 +06:00
commit d71b4ab17e
100 changed files with 19389 additions and 0 deletions
+200
View File
@@ -0,0 +1,200 @@
import { useState } from 'react';
import { motion } from 'framer-motion';
import { ArrowLeft, Calendar, Clock, User, Search } from 'lucide-react';
import { Link } from 'react-router-dom';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import Navbar from '@/components/Navbar';
import Footer from '@/components/Footer';
import PageTransition from '@/components/PageTransition';
import { blogPosts } from '@/data/blogData';
const categories = ['All', 'AI & Machine Learning', 'Cloud Solutions', 'Web Development', 'Mobile Development'];
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: { staggerChildren: 0.1 },
},
};
const itemVariants = {
hidden: { opacity: 0, y: 30 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.5, ease: 'easeOut' as const },
},
};
export default function Blog() {
const [activeCategory, setActiveCategory] = useState('All');
const [searchQuery, setSearchQuery] = useState('');
const filteredPosts = blogPosts.filter(post => {
const matchesCategory = activeCategory === 'All' || post.category === activeCategory;
const matchesSearch = post.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
post.excerpt.toLowerCase().includes(searchQuery.toLowerCase());
return matchesCategory && matchesSearch;
});
return (
<PageTransition>
<div className="min-h-screen bg-background overflow-x-hidden">
<Navbar />
{/* Hero Section */}
<section className="pt-32 pb-16 relative overflow-hidden">
<div className="absolute inset-0">
<div className="absolute top-1/4 left-1/4 w-96 h-96 bg-primary/20 rounded-full blur-3xl" />
<div className="absolute bottom-0 right-1/4 w-64 h-64 bg-neon-purple/20 rounded-full blur-3xl" />
</div>
<div className="container mx-auto px-4 relative z-10">
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5 }}
>
<Link to="/">
<Button variant="ghost" className="mb-8 group">
<ArrowLeft className="w-4 h-4 mr-2 transition-transform group-hover:-translate-x-1" />
Back to Home
</Button>
</Link>
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
className="text-center mb-12"
>
<h1 className="text-5xl md:text-6xl font-bold mb-6">
Our <span className="text-primary neon-text-glow">Blog</span>
</h1>
<p className="text-muted-foreground max-w-2xl mx-auto text-lg">
Insights, tutorials, and thought leadership from our team of experts.
</p>
</motion.div>
{/* Search */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.1 }}
className="max-w-md mx-auto mb-8"
>
<div className="relative">
<Search className="absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-muted-foreground" />
<Input
type="text"
placeholder="Search articles..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-12 py-6 rounded-full glass border-primary/30 focus:border-primary"
/>
</div>
</motion.div>
{/* Category Filter */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
className="flex flex-wrap justify-center gap-3 mb-16"
>
{categories.map((category) => (
<Button
key={category}
variant={activeCategory === category ? 'default' : 'outline'}
onClick={() => setActiveCategory(category)}
className={`rounded-full px-6 transition-all duration-300 ${
activeCategory === category
? 'neon-glow bg-primary text-primary-foreground'
: 'glass border-primary/30 hover:border-primary'
}`}
>
{category}
</Button>
))}
</motion.div>
</div>
</section>
{/* Blog Posts Grid */}
<section className="pb-24">
<div className="container mx-auto px-4">
<motion.div
variants={containerVariants}
initial="hidden"
animate="visible"
className="grid md:grid-cols-2 lg:grid-cols-3 gap-8"
>
{filteredPosts.map((post) => (
<motion.article
key={post.id}
variants={itemVariants}
className="group glass rounded-2xl overflow-hidden hover:neon-glow transition-all duration-500"
>
<Link to={`/blog/${post.slug}`}>
<div className="relative h-48 overflow-hidden">
<img
src={post.image}
alt={post.title}
className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110"
/>
<div className="absolute inset-0 bg-gradient-to-t from-background/80 to-transparent" />
<span className="absolute bottom-4 left-4 px-3 py-1 rounded-full bg-primary/20 text-primary text-xs font-medium backdrop-blur-sm">
{post.category}
</span>
</div>
<div className="p-6">
<h3 className="text-xl font-bold mb-3 group-hover:text-primary transition-colors line-clamp-2">
{post.title}
</h3>
<p className="text-muted-foreground text-sm mb-4 line-clamp-2">
{post.excerpt}
</p>
<div className="flex items-center gap-4 text-xs text-muted-foreground">
<span className="flex items-center gap-1">
<User className="w-3 h-3" />
{post.author.name}
</span>
<span className="flex items-center gap-1">
<Calendar className="w-3 h-3" />
{new Date(post.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}
</span>
<span className="flex items-center gap-1">
<Clock className="w-3 h-3" />
{post.readTime}
</span>
</div>
</div>
</Link>
</motion.article>
))}
</motion.div>
{filteredPosts.length === 0 && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className="text-center py-20"
>
<p className="text-muted-foreground text-lg">
No articles found matching your criteria.
</p>
</motion.div>
)}
</div>
</section>
<Footer />
</div>
</PageTransition>
);
}
+346
View File
@@ -0,0 +1,346 @@
import { useParams, Link, useNavigate } from 'react-router-dom';
import { motion } from 'framer-motion';
import { ArrowLeft, Calendar, Clock, Twitter, Linkedin, Facebook, Link2 } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { toast } from 'sonner';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import Navbar from '@/components/Navbar';
import Footer from '@/components/Footer';
import PageTransition from '@/components/PageTransition';
import { getPostBySlug, getRelatedPosts } from '@/data/blogData';
export default function BlogArticle() {
const { slug } = useParams<{ slug: string }>();
const navigate = useNavigate();
const post = getPostBySlug(slug || '');
if (!post) {
return (
<PageTransition>
<div className="min-h-screen bg-background flex items-center justify-center">
<div className="text-center">
<h1 className="text-4xl font-bold mb-4">Article Not Found</h1>
<p className="text-muted-foreground mb-8">The article you're looking for doesn't exist.</p>
<Button onClick={() => navigate('/blog')}>Back to Blog</Button>
</div>
</div>
</PageTransition>
);
}
const relatedPosts = getRelatedPosts(post);
const shareUrl = window.location.href;
const handleShare = (platform: string) => {
const urls: Record<string, string> = {
twitter: `https://twitter.com/intent/tweet?url=${encodeURIComponent(shareUrl)}&text=${encodeURIComponent(post.title)}`,
linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(shareUrl)}`,
facebook: `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(shareUrl)}`,
};
if (platform === 'copy') {
navigator.clipboard.writeText(shareUrl);
toast.success('Link copied to clipboard!');
} else {
window.open(urls[platform], '_blank', 'width=600,height=400');
}
};
return (
<PageTransition>
<div className="min-h-screen bg-background overflow-x-hidden">
<Navbar />
{/* Hero */}
<section className="pt-32 pb-16 relative overflow-hidden">
<div className="absolute inset-0">
<div className="absolute top-1/4 left-1/4 w-96 h-96 bg-primary/20 rounded-full blur-3xl" />
</div>
<div className="container mx-auto px-4 relative z-10 max-w-4xl">
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5 }}
>
<Link to="/blog">
<Button variant="ghost" className="mb-8 group">
<ArrowLeft className="w-4 h-4 mr-2 transition-transform group-hover:-translate-x-1" />
Back to Blog
</Button>
</Link>
</motion.div>
{/* Category */}
<motion.span
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
className="inline-block px-4 py-1.5 rounded-full bg-primary/20 text-primary text-sm font-medium mb-6"
>
{post.category}
</motion.span>
{/* Title */}
<motion.h1
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.1 }}
className="text-4xl md:text-5xl font-bold mb-6 leading-tight"
>
{post.title}
</motion.h1>
{/* Meta */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
className="flex flex-wrap items-center gap-6 text-muted-foreground mb-8"
>
<div className="flex items-center gap-3">
<img
src={post.author.avatar}
alt={post.author.name}
className="w-10 h-10 rounded-full object-cover border-2 border-primary/50"
/>
<div>
<p className="font-medium text-foreground">{post.author.name}</p>
<p className="text-sm">{post.author.role}</p>
</div>
</div>
<span className="flex items-center gap-2">
<Calendar className="w-4 h-4" />
{new Date(post.date).toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' })}
</span>
<span className="flex items-center gap-2">
<Clock className="w-4 h-4" />
{post.readTime}
</span>
</motion.div>
{/* Featured Image */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3 }}
className="rounded-2xl overflow-hidden mb-12"
>
<img
src={post.image}
alt={post.title}
className="w-full h-[400px] object-cover"
/>
</motion.div>
</div>
</section>
{/* Content */}
<section className="pb-16">
<div className="container mx-auto px-4 max-w-4xl">
<div className="grid lg:grid-cols-[1fr_200px] gap-12">
{/* Article Content */}
<motion.article
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.4 }}
className="max-w-none prose-content"
>
<ReactMarkdown
remarkPlugins={[remarkGfm]}
components={{
h1: ({ children }) => <h1 className="text-3xl font-bold text-foreground mt-8 mb-4">{children}</h1>,
h2: ({ children }) => <h2 className="text-2xl font-bold text-foreground mt-8 mb-4">{children}</h2>,
h3: ({ children }) => <h3 className="text-xl font-bold text-foreground mt-6 mb-3">{children}</h3>,
h4: ({ children }) => <h4 className="text-lg font-bold text-foreground mt-4 mb-2">{children}</h4>,
p: ({ children }) => <p className="text-muted-foreground leading-relaxed mb-4">{children}</p>,
ul: ({ children }) => <ul className="list-disc list-inside text-muted-foreground space-y-2 mb-4 ml-4">{children}</ul>,
ol: ({ children }) => <ol className="list-decimal list-inside text-muted-foreground space-y-2 mb-4 ml-4">{children}</ol>,
li: ({ children }) => <li className="text-muted-foreground">{children}</li>,
a: ({ href, children }) => <a href={href} className="text-primary hover:underline">{children}</a>,
strong: ({ children }) => <strong className="font-bold text-foreground">{children}</strong>,
em: ({ children }) => <em className="italic">{children}</em>,
blockquote: ({ children }) => (
<blockquote className="border-l-4 border-primary pl-4 italic text-muted-foreground my-4">
{children}
</blockquote>
),
code: ({ className, children }) => {
const isInline = !className;
if (isInline) {
return <code className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">{children}</code>;
}
return (
<code className="block bg-muted border border-border rounded-lg p-4 overflow-x-auto text-sm font-mono my-4">
{children}
</code>
);
},
pre: ({ children }) => <pre className="bg-muted border border-border rounded-lg p-4 overflow-x-auto my-4">{children}</pre>,
hr: () => <hr className="border-border my-8" />,
}}
>
{post.content}
</ReactMarkdown>
</motion.article>
{/* Sidebar */}
<aside className="hidden lg:block">
<div className="sticky top-32 space-y-8">
{/* Share */}
<div className="glass rounded-2xl p-6">
<h4 className="font-bold mb-4 text-sm uppercase tracking-wide text-muted-foreground">Share</h4>
<div className="flex flex-col gap-3">
<Button
variant="outline"
size="sm"
onClick={() => handleShare('twitter')}
className="justify-start gap-2 glass border-primary/30"
>
<Twitter className="w-4 h-4" /> Twitter
</Button>
<Button
variant="outline"
size="sm"
onClick={() => handleShare('linkedin')}
className="justify-start gap-2 glass border-primary/30"
>
<Linkedin className="w-4 h-4" /> LinkedIn
</Button>
<Button
variant="outline"
size="sm"
onClick={() => handleShare('facebook')}
className="justify-start gap-2 glass border-primary/30"
>
<Facebook className="w-4 h-4" /> Facebook
</Button>
<Button
variant="outline"
size="sm"
onClick={() => handleShare('copy')}
className="justify-start gap-2 glass border-primary/30"
>
<Link2 className="w-4 h-4" /> Copy Link
</Button>
</div>
</div>
{/* Tags */}
<div className="glass rounded-2xl p-6">
<h4 className="font-bold mb-4 text-sm uppercase tracking-wide text-muted-foreground">Tags</h4>
<div className="flex flex-wrap gap-2">
{post.tags.map((tag) => (
<span
key={tag}
className="px-3 py-1 rounded-full bg-muted text-muted-foreground text-xs"
>
{tag}
</span>
))}
</div>
</div>
</div>
</aside>
</div>
</div>
</section>
{/* Author Bio */}
<section className="py-16 bg-secondary/30">
<div className="container mx-auto px-4 max-w-4xl">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="glass rounded-3xl p-8 flex flex-col md:flex-row gap-6 items-center md:items-start"
>
<img
src={post.author.avatar}
alt={post.author.name}
className="w-24 h-24 rounded-full object-cover border-4 border-primary/50"
/>
<div className="text-center md:text-left">
<h3 className="text-2xl font-bold mb-2">{post.author.name}</h3>
<p className="text-primary font-medium mb-4">{post.author.role}</p>
<p className="text-muted-foreground mb-4">{post.author.bio}</p>
<div className="flex gap-3 justify-center md:justify-start">
{post.author.twitter && (
<a
href={`https://twitter.com/${post.author.twitter}`}
target="_blank"
rel="noopener noreferrer"
className="p-2 rounded-full glass hover:neon-glow transition-all"
>
<Twitter className="w-5 h-5" />
</a>
)}
{post.author.linkedin && (
<a
href={`https://linkedin.com/in/${post.author.linkedin}`}
target="_blank"
rel="noopener noreferrer"
className="p-2 rounded-full glass hover:neon-glow transition-all"
>
<Linkedin className="w-5 h-5" />
</a>
)}
</div>
</div>
</motion.div>
</div>
</section>
{/* Related Articles */}
{relatedPosts.length > 0 && (
<section className="py-24">
<div className="container mx-auto px-4">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="text-center mb-12"
>
<h2 className="text-3xl font-bold">Related Articles</h2>
</motion.div>
<div className="grid md:grid-cols-3 gap-8 max-w-5xl mx-auto">
{relatedPosts.map((relatedPost, index) => (
<motion.article
key={relatedPost.id}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: index * 0.1 }}
className="group glass rounded-2xl overflow-hidden hover:neon-glow transition-all duration-500"
>
<Link to={`/blog/${relatedPost.slug}`}>
<div className="relative h-40 overflow-hidden">
<img
src={relatedPost.image}
alt={relatedPost.title}
className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110"
/>
</div>
<div className="p-5">
<h3 className="font-bold mb-2 group-hover:text-primary transition-colors line-clamp-2">
{relatedPost.title}
</h3>
<p className="text-sm text-muted-foreground flex items-center gap-2">
<Clock className="w-3 h-3" /> {relatedPost.readTime}
</p>
</div>
</Link>
</motion.article>
))}
</div>
</div>
</section>
)}
<Footer />
</div>
</PageTransition>
);
}
+34
View File
@@ -0,0 +1,34 @@
import Navbar from '@/components/Navbar';
import HeroSection from '@/components/HeroSection';
import ServicesSection from '@/components/ServicesSection';
import ProjectsSection from '@/components/ProjectsSection';
import TeamSection from '@/components/TeamSection';
import AboutSection from '@/components/AboutSection';
import TestimonialsSection from '@/components/TestimonialsSection';
import BlogSection from '@/components/BlogSection';
import FAQSection from '@/components/FAQSection';
import ContactSection from '@/components/ContactSection';
import Footer from '@/components/Footer';
import PageTransition from '@/components/PageTransition';
const Index = () => {
return (
<PageTransition>
<div className="min-h-screen bg-background overflow-x-hidden">
<Navbar />
<HeroSection />
<ServicesSection />
<ProjectsSection />
<TeamSection />
<AboutSection />
<TestimonialsSection />
<BlogSection />
<FAQSection />
<ContactSection />
<Footer />
</div>
</PageTransition>
);
};
export default Index;
+24
View File
@@ -0,0 +1,24 @@
import { useLocation } from "react-router-dom";
import { useEffect } from "react";
const NotFound = () => {
const location = useLocation();
useEffect(() => {
console.error("404 Error: User attempted to access non-existent route:", location.pathname);
}, [location.pathname]);
return (
<div className="flex min-h-screen items-center justify-center bg-muted">
<div className="text-center">
<h1 className="mb-4 text-4xl font-bold">404</h1>
<p className="mb-4 text-xl text-muted-foreground">Oops! Page not found</p>
<a href="/" className="text-primary underline hover:text-primary/90">
Return to Home
</a>
</div>
</div>
);
};
export default NotFound;
+372
View File
@@ -0,0 +1,372 @@
import { useParams, Link, useNavigate } from 'react-router-dom';
import { motion } from 'framer-motion';
import { ArrowLeft, ExternalLink, Github, Calendar, Clock, Users, ChevronRight } from 'lucide-react';
import { Button } from '@/components/ui/button';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import Navbar from '@/components/Navbar';
import Footer from '@/components/Footer';
import PageTransition from '@/components/PageTransition';
import { getProjectBySlug, getRelatedProjects } from '@/data/projectData';
export default function ProjectDetails() {
const { slug } = useParams<{ slug: string }>();
const navigate = useNavigate();
const project = getProjectBySlug(slug || '');
if (!project) {
return (
<PageTransition>
<div className="min-h-screen bg-background flex items-center justify-center">
<div className="text-center">
<h1 className="text-4xl font-bold mb-4">Project Not Found</h1>
<p className="text-muted-foreground mb-8">The project you're looking for doesn't exist.</p>
<Button onClick={() => navigate('/projects')}>Back to Projects</Button>
</div>
</div>
</PageTransition>
);
}
const relatedProjects = getRelatedProjects(project);
const categoryLabels: Record<string, string> = {
web: 'Web Development',
mobile: 'Mobile App',
ai: 'AI & Machine Learning',
cloud: 'Cloud Solutions',
};
return (
<PageTransition>
<div className="min-h-screen bg-background overflow-x-hidden">
<Navbar />
{/* Hero */}
<section className="pt-32 pb-16 relative overflow-hidden">
<div className="absolute inset-0">
<div className="absolute top-1/4 left-1/4 w-96 h-96 bg-primary/20 rounded-full blur-3xl" />
<div className="absolute bottom-0 right-1/4 w-64 h-64 bg-neon-purple/20 rounded-full blur-3xl" />
</div>
<div className="container mx-auto px-4 relative z-10">
{/* Breadcrumb */}
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5 }}
className="flex items-center gap-2 text-sm text-muted-foreground mb-8"
>
<Link to="/" className="hover:text-primary transition-colors">Home</Link>
<ChevronRight className="w-4 h-4" />
<Link to="/projects" className="hover:text-primary transition-colors">Projects</Link>
<ChevronRight className="w-4 h-4" />
<span className="text-foreground">{project.title}</span>
</motion.div>
<div className="grid lg:grid-cols-2 gap-12 items-center">
{/* Content */}
<div>
<motion.span
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
className="inline-block px-4 py-1.5 rounded-full bg-primary/20 text-primary text-sm font-medium mb-6"
>
{categoryLabels[project.category]}
</motion.span>
<motion.h1
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.1 }}
className="text-4xl md:text-5xl font-bold mb-6"
>
{project.title}
</motion.h1>
<motion.p
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
className="text-lg text-muted-foreground mb-8"
>
{project.description}
</motion.p>
{/* Meta */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3 }}
className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8"
>
<div className="glass rounded-xl p-4 text-center">
<Calendar className="w-5 h-5 mx-auto mb-2 text-primary" />
<p className="text-sm text-muted-foreground">Year</p>
<p className="font-bold">{project.year}</p>
</div>
<div className="glass rounded-xl p-4 text-center">
<Clock className="w-5 h-5 mx-auto mb-2 text-primary" />
<p className="text-sm text-muted-foreground">Duration</p>
<p className="font-bold">{project.duration}</p>
</div>
<div className="glass rounded-xl p-4 text-center">
<Users className="w-5 h-5 mx-auto mb-2 text-primary" />
<p className="text-sm text-muted-foreground">Team</p>
<p className="font-bold">{project.team}</p>
</div>
<div className="glass rounded-xl p-4 text-center">
<p className="text-sm text-muted-foreground mb-1">Client</p>
<p className="font-bold text-sm">{project.client}</p>
</div>
</motion.div>
{/* Actions */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.4 }}
className="flex gap-4"
>
<Button className="rounded-full neon-glow">
<ExternalLink className="w-4 h-4 mr-2" />
View Live
</Button>
<Button variant="outline" className="rounded-full glass border-primary/30">
<Github className="w-4 h-4 mr-2" />
View Code
</Button>
</motion.div>
</div>
{/* Image */}
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.6, delay: 0.3 }}
className="rounded-3xl overflow-hidden neon-glow"
>
<img
src={project.image}
alt={project.title}
className="w-full h-[400px] object-cover"
/>
</motion.div>
</div>
</div>
</section>
{/* Results */}
<section className="py-16 bg-secondary/30">
<div className="container mx-auto px-4">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="text-center mb-12"
>
<h2 className="text-3xl font-bold">Key Results</h2>
</motion.div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-6 max-w-4xl mx-auto">
{project.results.map((result, index) => (
<motion.div
key={result.label}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: index * 0.1 }}
className="glass-strong rounded-2xl p-6 text-center"
>
<p className="text-3xl md:text-4xl font-bold text-primary neon-text-glow mb-2">
{result.value}
</p>
<p className="text-muted-foreground text-sm">{result.label}</p>
</motion.div>
))}
</div>
</div>
</section>
{/* Details */}
<section className="py-24">
<div className="container mx-auto px-4">
<div className="grid lg:grid-cols-2 gap-16 max-w-6xl mx-auto">
{/* Full Description */}
<motion.div
initial={{ opacity: 0, x: -30 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
>
<h3 className="text-2xl font-bold mb-6">About the Project</h3>
<div className="prose-content">
<ReactMarkdown
remarkPlugins={[remarkGfm]}
components={{
p: ({ children }) => <p className="text-muted-foreground leading-relaxed mb-4">{children}</p>,
strong: ({ children }) => <strong className="font-bold text-foreground">{children}</strong>,
ul: ({ children }) => <ul className="list-disc list-inside text-muted-foreground space-y-2 mb-4 ml-4">{children}</ul>,
li: ({ children }) => <li className="text-muted-foreground">{children}</li>,
}}
>
{project.fullDescription}
</ReactMarkdown>
</div>
</motion.div>
{/* Features & Tech */}
<div className="space-y-12">
<motion.div
initial={{ opacity: 0, x: 30 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
>
<h3 className="text-2xl font-bold mb-6">Key Features</h3>
<ul className="space-y-3">
{project.features.map((feature, index) => (
<li key={index} className="flex items-start gap-3">
<span className="w-2 h-2 rounded-full bg-primary mt-2 flex-shrink-0" />
<span className="text-muted-foreground">{feature}</span>
</li>
))}
</ul>
</motion.div>
<motion.div
initial={{ opacity: 0, x: 30 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
transition={{ delay: 0.1 }}
>
<h3 className="text-2xl font-bold mb-6">Technologies Used</h3>
<div className="flex flex-wrap gap-3">
{project.technologies.map((tech) => (
<span
key={tech}
className="px-4 py-2 rounded-full glass text-sm font-medium border border-primary/30"
>
{tech}
</span>
))}
</div>
</motion.div>
<motion.div
initial={{ opacity: 0, x: 30 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
transition={{ delay: 0.2 }}
>
<h3 className="text-2xl font-bold mb-6">Challenges Solved</h3>
<ul className="space-y-3">
{project.challenges.map((challenge, index) => (
<li key={index} className="flex items-start gap-3">
<span className="w-2 h-2 rounded-full bg-neon-purple mt-2 flex-shrink-0" />
<span className="text-muted-foreground">{challenge}</span>
</li>
))}
</ul>
</motion.div>
</div>
</div>
</div>
</section>
{/* Gallery */}
<section className="py-16 bg-secondary/30">
<div className="container mx-auto px-4">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="text-center mb-12"
>
<h2 className="text-3xl font-bold">Project Gallery</h2>
</motion.div>
<div className="grid md:grid-cols-3 gap-6 max-w-5xl mx-auto">
{project.gallery.map((image, index) => (
<motion.div
key={index}
initial={{ opacity: 0, scale: 0.9 }}
whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true }}
transition={{ delay: index * 0.1 }}
className="rounded-2xl overflow-hidden hover:neon-glow transition-all duration-500"
>
<img
src={image}
alt={`${project.title} screenshot ${index + 1}`}
className="w-full h-48 object-cover hover:scale-105 transition-transform duration-500"
/>
</motion.div>
))}
</div>
</div>
</section>
{/* Related Projects */}
{relatedProjects.length > 0 && (
<section className="py-24">
<div className="container mx-auto px-4">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="text-center mb-12"
>
<h2 className="text-3xl font-bold">Related Projects</h2>
</motion.div>
<div className="grid md:grid-cols-3 gap-8 max-w-5xl mx-auto">
{relatedProjects.map((relatedProject, index) => (
<motion.article
key={relatedProject.id}
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: index * 0.1 }}
className="group glass rounded-2xl overflow-hidden hover:neon-glow transition-all duration-500"
>
<Link to={`/projects/${relatedProject.slug}`}>
<div className="relative h-40 overflow-hidden">
<img
src={relatedProject.image}
alt={relatedProject.title}
className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110"
/>
</div>
<div className="p-5">
<h3 className="font-bold mb-2 group-hover:text-primary transition-colors">
{relatedProject.title}
</h3>
<p className="text-sm text-muted-foreground line-clamp-2">
{relatedProject.description}
</p>
</div>
</Link>
</motion.article>
))}
</div>
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
className="text-center mt-12"
>
<Link to="/projects">
<Button variant="outline" className="rounded-full px-8 glass border-primary/30 hover:neon-glow">
View All Projects
</Button>
</Link>
</motion.div>
</div>
</section>
)}
<Footer />
</div>
</PageTransition>
);
}
+225
View File
@@ -0,0 +1,225 @@
import { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { ArrowLeft, Globe, Smartphone, Brain, Cloud, ExternalLink, Github } from 'lucide-react';
import { Link } from 'react-router-dom';
import { Button } from '@/components/ui/button';
import Navbar from '@/components/Navbar';
import Footer from '@/components/Footer';
import PageTransition from '@/components/PageTransition';
import { projects } from '@/data/projectData';
const categories = [
{ id: 'all', name: 'All Projects', icon: null },
{ id: 'web', name: 'Web', icon: Globe },
{ id: 'mobile', name: 'Mobile', icon: Smartphone },
{ id: 'ai', name: 'AI', icon: Brain },
{ id: 'cloud', name: 'Cloud', icon: Cloud },
];
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1,
},
},
};
const itemVariants = {
hidden: { opacity: 0, y: 30 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.5, ease: 'easeOut' as const },
},
};
export default function Projects() {
const [activeCategory, setActiveCategory] = useState('all');
const filteredProjects = activeCategory === 'all'
? projects
: projects.filter(project => project.category === activeCategory);
return (
<PageTransition>
<div className="min-h-screen bg-background overflow-x-hidden">
<Navbar />
{/* Hero Section */}
<section className="pt-32 pb-16 relative overflow-hidden">
<div className="absolute inset-0">
<div className="absolute top-1/4 left-1/4 w-96 h-96 bg-primary/20 rounded-full blur-3xl" />
<div className="absolute bottom-0 right-1/4 w-64 h-64 bg-neon-purple/20 rounded-full blur-3xl" />
</div>
<div className="container mx-auto px-4 relative z-10">
{/* Back Button */}
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5 }}
>
<Link to="/">
<Button variant="ghost" className="mb-8 group">
<ArrowLeft className="w-4 h-4 mr-2 transition-transform group-hover:-translate-x-1" />
Back to Home
</Button>
</Link>
</motion.div>
{/* Header */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
className="text-center mb-12"
>
<h1 className="text-5xl md:text-6xl font-bold mb-6">
Our <span className="text-primary neon-text-glow">Projects</span>
</h1>
<p className="text-muted-foreground max-w-2xl mx-auto text-lg">
Explore our portfolio of innovative solutions that have transformed businesses across industries.
</p>
</motion.div>
{/* Filter Tabs */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
className="flex flex-wrap justify-center gap-3 mb-16"
>
{categories.map((category) => {
const Icon = category.icon;
return (
<Button
key={category.id}
variant={activeCategory === category.id ? 'default' : 'outline'}
onClick={() => setActiveCategory(category.id)}
className={`rounded-full px-6 transition-all duration-300 ${
activeCategory === category.id
? 'neon-glow bg-primary text-primary-foreground'
: 'glass border-primary/30 hover:border-primary'
}`}
>
{Icon && <Icon className="w-4 h-4 mr-2" />}
{category.name}
</Button>
);
})}
</motion.div>
</div>
</section>
{/* Projects Grid */}
<section className="pb-24">
<div className="container mx-auto px-4">
<AnimatePresence mode="wait">
<motion.div
key={activeCategory}
variants={containerVariants}
initial="hidden"
animate="visible"
exit="hidden"
className="grid md:grid-cols-2 gap-8"
>
{filteredProjects.map((project) => (
<motion.article
key={project.id}
variants={itemVariants}
layout
className="group glass rounded-3xl overflow-hidden hover:neon-glow transition-all duration-500"
>
{/* Image */}
<div className="relative h-64 overflow-hidden">
<img
src={project.image}
alt={project.title}
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-110"
/>
<div className="absolute inset-0 bg-gradient-to-t from-background via-background/50 to-transparent opacity-60" />
{/* Category Badge */}
<div className="absolute top-4 left-4">
<span className="px-3 py-1 rounded-full bg-primary/20 text-primary text-sm font-medium backdrop-blur-sm border border-primary/30">
{categories.find(c => c.id === project.category)?.name}
</span>
</div>
{/* Quick Links */}
<div className="absolute top-4 right-4 flex gap-2 opacity-0 group-hover:opacity-100 transition-opacity duration-300">
<a
href={project.liveUrl}
className="p-2 rounded-full glass hover:bg-primary/20 transition-colors"
aria-label="View live project"
>
<ExternalLink className="w-4 h-4" />
</a>
<a
href={project.githubUrl}
className="p-2 rounded-full glass hover:bg-primary/20 transition-colors"
aria-label="View on GitHub"
>
<Github className="w-4 h-4" />
</a>
</div>
</div>
{/* Content */}
<div className="p-8">
{/* Meta */}
<div className="flex items-center gap-4 text-sm text-muted-foreground mb-4">
<span>{project.client}</span>
<span className="w-1 h-1 rounded-full bg-muted-foreground" />
<span>{project.year}</span>
</div>
{/* Title */}
<h3 className="text-2xl font-bold mb-4 group-hover:text-primary transition-colors">
{project.title}
</h3>
{/* Description */}
<p className="text-muted-foreground mb-6 leading-relaxed">
{project.description}
</p>
{/* Technologies */}
<div className="flex flex-wrap gap-2">
{project.technologies.map((tech) => (
<span
key={tech}
className="px-3 py-1 rounded-full bg-muted text-muted-foreground text-xs font-medium"
>
{tech}
</span>
))}
</div>
</div>
</motion.article>
))}
</motion.div>
</AnimatePresence>
{/* Empty State */}
{filteredProjects.length === 0 && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className="text-center py-20"
>
<p className="text-muted-foreground text-lg">
No projects found in this category.
</p>
</motion.div>
)}
</div>
</section>
<Footer />
</div>
</PageTransition>
);
}