ProjectsShowcase: add details modal

This commit is contained in:
Mohammed Al-yaseen
2025-07-28 10:40:33 +03:00
parent c9998ce4e0
commit b2896c379f
3 changed files with 593 additions and 11 deletions

View File

@@ -1,8 +1,8 @@
// File: src/components/home/ProjectsShowcase.tsx // File: src/components/home/ProjectsShowcase.tsx
import { EyeOutlined, LeftOutlined, RightOutlined } from "@ant-design/icons"; import { CloseOutlined, EyeOutlined, GithubOutlined, LeftOutlined, LinkOutlined, RightOutlined } from "@ant-design/icons";
import { Badge, Button, Card, Col, Row, Tag, Typography } from "antd"; import { Badge, Button, Card, Carousel, Col, Modal, Row, Tag, Typography } from "antd";
import { motion, useAnimation } from "framer-motion"; import { AnimatePresence, motion, useAnimation } from "framer-motion";
import Image from "next/image"; import Image from "next/image";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import styles from "./ProjectsShowcaseSelector.module.css"; import styles from "./ProjectsShowcaseSelector.module.css";
@@ -17,6 +17,13 @@ interface Project {
category: string; category: string;
technologies: string[]; technologies: string[];
featured: boolean; featured: boolean;
detailedDescription: string;
images: string[];
liveUrl?: string;
githubUrl?: string;
features: string[];
duration: string;
teamSize: string;
} }
// Sample project data // Sample project data
@@ -30,6 +37,24 @@ const projectsData: Project[] = [
category: "Web Application", category: "Web Application",
technologies: ["React", "Node.js", "TensorFlow", "AWS"], technologies: ["React", "Node.js", "TensorFlow", "AWS"],
featured: true, featured: true,
detailedDescription: "A comprehensive financial analytics platform that leverages artificial intelligence to provide real-time insights into market trends, portfolio performance, and risk assessment. The platform features advanced data visualization, predictive analytics, and automated reporting capabilities.",
images: [
"/api/placeholder/800/500",
"/api/placeholder/800/500",
"/api/placeholder/800/500",
"/api/placeholder/800/500"
],
liveUrl: "https://fintech-demo.com",
githubUrl: "https://github.com/company/fintech-dashboard",
features: [
"Real-time data visualization with interactive charts",
"AI-powered predictive analytics",
"Automated risk assessment",
"Multi-device responsive design",
"Advanced security and encryption"
],
duration: "6 months",
teamSize: "8 developers"
}, },
{ {
id: "p2", id: "p2",
@@ -40,6 +65,23 @@ const projectsData: Project[] = [
category: "Enterprise Software", category: "Enterprise Software",
technologies: ["Angular", ".NET Core", "SQL Server", "Azure"], technologies: ["Angular", ".NET Core", "SQL Server", "Azure"],
featured: true, featured: true,
detailedDescription: "A robust healthcare management system designed to streamline patient care workflows, manage electronic health records, and ensure compliance with healthcare regulations. The system includes appointment scheduling, billing management, and advanced reporting features.",
images: [
"/api/placeholder/800/500",
"/api/placeholder/800/500",
"/api/placeholder/800/500"
],
liveUrl: "https://healthcare-demo.com",
githubUrl: "https://github.com/company/healthcare-system",
features: [
"Electronic Health Records (EHR)",
"Appointment scheduling and management",
"Billing and insurance processing",
"HIPAA compliance features",
"Advanced reporting and analytics"
],
duration: "8 months",
teamSize: "12 developers"
}, },
{ {
id: "p3", id: "p3",
@@ -50,6 +92,25 @@ const projectsData: Project[] = [
category: "E-commerce", category: "E-commerce",
technologies: ["Next.js", "Stripe", "MongoDB", "GraphQL"], technologies: ["Next.js", "Stripe", "MongoDB", "GraphQL"],
featured: true, featured: true,
detailedDescription: "A modern e-commerce platform that connects multiple vendors with customers through a unified marketplace. Features include advanced search, personalized recommendations, secure payment processing, and comprehensive inventory management.",
images: [
"/api/placeholder/800/500",
"/api/placeholder/800/500",
"/api/placeholder/800/500",
"/api/placeholder/800/500",
"/api/placeholder/800/500"
],
liveUrl: "https://marketplace-demo.com",
githubUrl: "https://github.com/company/ecommerce-marketplace",
features: [
"Multi-vendor marketplace",
"Advanced search and filtering",
"Secure payment processing",
"Inventory management system",
"Real-time notifications"
],
duration: "7 months",
teamSize: "10 developers"
}, },
{ {
id: "p4", id: "p4",
@@ -60,6 +121,23 @@ const projectsData: Project[] = [
category: "IoT", category: "IoT",
technologies: ["Python", "MQTT", "Kubernetes", "TensorFlow"], technologies: ["Python", "MQTT", "Kubernetes", "TensorFlow"],
featured: false, featured: false,
detailedDescription: "An Internet of Things platform designed for smart city applications, enabling real-time monitoring of urban infrastructure, traffic management, and environmental data collection with advanced analytics capabilities.",
images: [
"/api/placeholder/800/500",
"/api/placeholder/800/500",
"/api/placeholder/800/500"
],
liveUrl: "https://smartcity-demo.com",
githubUrl: "https://github.com/company/smart-city-platform",
features: [
"Real-time sensor data collection",
"Traffic flow optimization",
"Environmental monitoring",
"Predictive maintenance alerts",
"Interactive city dashboard"
],
duration: "10 months",
teamSize: "15 developers"
}, },
{ {
id: "p5", id: "p5",
@@ -70,6 +148,23 @@ const projectsData: Project[] = [
category: "Mobile & Web", category: "Mobile & Web",
technologies: ["React Native", "Node.js", "PostgreSQL", "Google Maps API"], technologies: ["React Native", "Node.js", "PostgreSQL", "Google Maps API"],
featured: false, featured: false,
detailedDescription: "A comprehensive logistics and fleet management system that provides real-time tracking, route optimization, and delivery management. The platform includes mobile apps for drivers and web dashboards for managers.",
images: [
"/api/placeholder/800/500",
"/api/placeholder/800/500",
"/api/placeholder/800/500"
],
liveUrl: "https://logistics-demo.com",
githubUrl: "https://github.com/company/logistics-system",
features: [
"Real-time GPS tracking",
"Route optimization algorithms",
"Driver mobile application",
"Delivery status updates",
"Analytics and reporting"
],
duration: "5 months",
teamSize: "6 developers"
}, },
{ {
id: "p6", id: "p6",
@@ -80,12 +175,31 @@ const projectsData: Project[] = [
category: "Education", category: "Education",
technologies: ["Vue.js", "Django", "WebRTC", "Docker"], technologies: ["Vue.js", "Django", "WebRTC", "Docker"],
featured: false, featured: false,
detailedDescription: "A modern virtual learning environment that provides interactive online education with video conferencing, personalized learning paths, and comprehensive progress tracking for students and educators.",
images: [
"/api/placeholder/800/500",
"/api/placeholder/800/500",
"/api/placeholder/800/500"
],
liveUrl: "https://learning-demo.com",
githubUrl: "https://github.com/company/virtual-learning",
features: [
"Video conferencing and screen sharing",
"Personalized learning paths",
"Progress tracking and analytics",
"Interactive whiteboard",
"Assignment and grading system"
],
duration: "9 months",
teamSize: "11 developers"
}, },
]; ];
const ProjectsShowcase: React.FC = () => { const ProjectsShowcase: React.FC = () => {
const [currentIndex, setCurrentIndex] = useState(0); const [currentIndex, setCurrentIndex] = useState(0);
const [visibleProjects, setVisibleProjects] = useState<Project[]>([]); const [visibleProjects, setVisibleProjects] = useState<Project[]>([]);
const [selectedProject, setSelectedProject] = useState<Project | null>(null);
const [isModalVisible, setIsModalVisible] = useState(false);
const controls = useAnimation(); const controls = useAnimation();
// Determine number of projects to show based on screen width // Determine number of projects to show based on screen width
@@ -148,6 +262,18 @@ const ProjectsShowcase: React.FC = () => {
} }
}; };
const handleProjectClick = (project: Project) => {
setSelectedProject(project);
setIsModalVisible(true);
};
const handleModalClose = () => {
setIsModalVisible(false);
setTimeout(() => {
setSelectedProject(null);
}, 300);
};
return ( return (
<section className={styles.projectsSection}> <section className={styles.projectsSection}>
<div className={styles.container}> <div className={styles.container}>
@@ -181,6 +307,7 @@ const ProjectsShowcase: React.FC = () => {
> >
<Card <Card
hoverable hoverable
onClick={() => handleProjectClick(project)}
cover={ cover={
<div className={styles.projectImageContainer}> <div className={styles.projectImageContainer}>
<Image <Image
@@ -258,6 +385,142 @@ const ProjectsShowcase: React.FC = () => {
</Button> </Button>
</div> </div>
</div> </div>
{/* Project Details Modal */}
<AnimatePresence>
{isModalVisible && selectedProject && (
<Modal
open={isModalVisible}
onCancel={handleModalClose}
footer={null}
width={1000}
className={styles.projectModal}
closeIcon={<CloseOutlined className={styles.closeIcon} />}
>
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }}
transition={{ duration: 0.3 }}
className={styles.modalContent}
>
<div className={styles.modalHeader}>
<div className={styles.modalTitleSection}>
<Title level={2} className={styles.modalTitle}>
{selectedProject.title}
</Title>
<Tag color="blue" className={styles.modalCategory}>
{selectedProject.category}
</Tag>
</div>
<div className={styles.modalActions}>
{selectedProject.liveUrl && (
<Button
type="primary"
icon={<LinkOutlined />}
href={selectedProject.liveUrl}
target="_blank"
className={styles.actionButton}
>
Live Demo
</Button>
)}
{selectedProject.githubUrl && (
<Button
icon={<GithubOutlined />}
href={selectedProject.githubUrl}
target="_blank"
className={styles.actionButton}
>
View Code
</Button>
)}
</div>
</div>
<div className={styles.modalBody}>
<div className={styles.projectImages}>
<Carousel
autoplay
dots={{ className: styles.carouselDots }}
className={styles.imageCarousel}
>
{selectedProject.images.map((image, index) => (
<div key={index} className={styles.carouselItem}>
<Image
src={image}
alt={`${selectedProject.title} - Image ${index + 1}`}
width={800}
height={500}
className={styles.modalImage}
/>
</div>
))}
</Carousel>
</div>
<div className={styles.projectDetails}>
<div className={styles.detailSection}>
<Title level={4} className={styles.sectionTitle}>
Project Overview
</Title>
<Paragraph className={styles.projectDescription}>
{selectedProject.detailedDescription}
</Paragraph>
</div>
<div className={styles.detailGrid}>
<div className={styles.detailSection}>
<Title level={4} className={styles.sectionTitle}>
Key Features
</Title>
<ul className={styles.featuresList}>
{selectedProject.features.map((feature, index) => (
<motion.li
key={index}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: index * 0.1 }}
className={styles.featureItem}
>
{feature}
</motion.li>
))}
</ul>
</div>
<div className={styles.detailSection}>
<Title level={4} className={styles.sectionTitle}>
Project Details
</Title>
<div className={styles.projectInfo}>
<div className={styles.infoItem}>
<Text strong>Duration:</Text>
<Text>{selectedProject.duration}</Text>
</div>
<div className={styles.infoItem}>
<Text strong>Team Size:</Text>
<Text>{selectedProject.teamSize}</Text>
</div>
<div className={styles.infoItem}>
<Text strong>Technologies:</Text>
<div className={styles.modalTechnologies}>
{selectedProject.technologies.map((tech) => (
<Tag key={tech} className={styles.modalTechTag}>
{tech}
</Tag>
))}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</motion.div>
</Modal>
)}
</AnimatePresence>
</section> </section>
); );
}; };

View File

@@ -52,6 +52,12 @@
transition: all 0.3s ease; transition: all 0.3s ease;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
border: none; border: none;
cursor: pointer;
}
.projectCard:hover {
transform: translateY(-8px);
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
} }
.projectImageContainer { .projectImageContainer {
@@ -164,6 +170,263 @@
} }
/* Modal Styles */
.projectModal {
border-radius: 16px;
overflow: hidden;
}
.projectModal :global(.ant-modal-content) {
border-radius: 16px;
overflow: hidden;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
}
.projectModal :global(.ant-modal-header) {
display: none;
}
.projectModal :global(.ant-modal-body) {
padding: 0;
}
.closeIcon {
font-size: 18px;
color: #666;
transition: color 0.3s ease;
}
.closeIcon:hover {
color: #333;
}
.modalContent {
background: #fff;
border-radius: 16px;
overflow: hidden;
}
.modalHeader {
padding: 32px 32px 24px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
position: relative;
}
.modalHeader::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grain" width="100" height="100" patternUnits="userSpaceOnUse"><circle cx="50" cy="50" r="1" fill="white" opacity="0.1"/></pattern></defs><rect width="100" height="100" fill="url(%23grain)"/></svg>');
opacity: 0.3;
}
.modalTitleSection {
position: relative;
z-index: 1;
}
.modalTitle {
color: white !important;
font-size: 2.2rem !important;
font-weight: 700 !important;
margin-bottom: 12px !important;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.modalCategory {
background: rgba(255, 255, 255, 0.2) !important;
border: 1px solid rgba(255, 255, 255, 0.3) !important;
color: white !important;
font-weight: 500;
padding: 4px 12px;
border-radius: 20px;
}
.modalActions {
position: absolute;
top: 32px;
right: 32px;
display: flex;
gap: 12px;
z-index: 1;
}
.actionButton {
height: 40px;
border-radius: 20px;
font-weight: 500;
transition: all 0.3s ease;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.actionButton:hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
}
.modalBody {
padding: 0;
}
.projectImages {
position: relative;
background: #f8f9fa;
}
.imageCarousel {
border-radius: 0;
}
.carouselItem {
position: relative;
height: 400px;
overflow: hidden;
}
.modalImage {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s ease;
}
.carouselItem:hover .modalImage {
transform: scale(1.02);
}
.carouselDots {
bottom: 20px;
}
.carouselDots :global(.ant-carousel .slick-dots li button) {
background: rgba(255, 255, 255, 0.5);
border-radius: 50%;
width: 8px;
height: 8px;
}
.carouselDots :global(.ant-carousel .slick-dots li.slick-active button) {
background: #6e48aa;
}
.projectDetails {
padding: 32px;
background: white;
}
.detailSection {
margin-bottom: 32px;
}
.detailSection:last-child {
margin-bottom: 0;
}
.sectionTitle {
font-size: 1.4rem !important;
font-weight: 600 !important;
margin-bottom: 16px !important;
color: #333;
position: relative;
}
.sectionTitle::after {
content: '';
position: absolute;
bottom: -4px;
left: 0;
width: 40px;
height: 3px;
background: linear-gradient(90deg, #667eea, #764ba2);
border-radius: 2px;
}
.projectDescription {
font-size: 1.1rem;
line-height: 1.7;
color: #555;
margin-bottom: 0;
}
.detailGrid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 32px;
margin-top: 32px;
}
.featuresList {
list-style: none;
padding: 0;
margin: 0;
}
.featureItem {
position: relative;
padding: 12px 0 12px 24px;
font-size: 1rem;
color: #555;
line-height: 1.6;
border-bottom: 1px solid #f0f0f0;
}
.featureItem:last-child {
border-bottom: none;
}
.featureItem::before {
content: '✓';
position: absolute;
left: 0;
top: 12px;
color: #6e48aa;
font-weight: bold;
font-size: 14px;
}
.projectInfo {
display: flex;
flex-direction: column;
gap: 16px;
}
.infoItem {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 16px;
background: #f8f9fa;
border-radius: 8px;
border-left: 4px solid #6e48aa;
}
.infoItem:last-child {
flex-direction: column;
gap: 8px;
}
.modalTechnologies {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 8px;
}
.modalTechTag {
background: #e6f7ff;
border: 1px solid #91d5ff;
color: #1890ff;
font-weight: 500;
border-radius: 16px;
padding: 4px 12px;
font-size: 0.85rem;
}
/* Responsive Design */
@media (max-width: 768px) { @media (max-width: 768px) {
.projectsSection { .projectsSection {
padding: 60px 0; padding: 60px 0;
@@ -176,4 +439,59 @@
.projectImageContainer { .projectImageContainer {
height: 180px; height: 180px;
} }
.projectModal {
margin: 16px;
max-width: calc(100vw - 32px);
}
.modalHeader {
padding: 24px 20px 20px;
}
.modalTitle {
font-size: 1.8rem !important;
}
.modalActions {
position: static;
margin-top: 16px;
justify-content: flex-start;
}
.projectDetails {
padding: 24px 20px;
}
.detailGrid {
grid-template-columns: 1fr;
gap: 24px;
}
.carouselItem {
height: 250px;
}
.actionButton {
height: 36px;
font-size: 0.9rem;
}
}
@media (max-width: 480px) {
.modalHeader {
padding: 20px 16px 16px;
}
.modalTitle {
font-size: 1.5rem !important;
}
.projectDetails {
padding: 20px 16px;
}
.carouselItem {
height: 200px;
}
} }

View File

@@ -7,12 +7,14 @@ import "./globals.css";
import { themeConfig } from "./theme/themeConfig"; import { themeConfig } from "./theme/themeConfig";
// Modern font (Inter + Orbitron backup) // Modern font (Inter + Orbitron backup)
const inter = Inter({ subsets: ["latin"] }); const inter = Inter({
const orbitron = { subsets: ["latin"],
className: "font-orbitron", display: "swap",
style: fallback: ["system-ui", "arial"],
"@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap');", adjustFontFallback: true,
}; preload: true,
variable: "--font-inter",
});
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Tech Master | Dubai To Stars", title: "Tech Master | Dubai To Stars",
@@ -24,7 +26,7 @@ export default function RootLayout({
children: React.ReactNode; children: React.ReactNode;
}) { }) {
return ( return (
<html lang="en"> <html lang="en" className={`${inter.variable} font-sans`}>
<head> <head>
{/* ThreeJS CDN */} {/* ThreeJS CDN */}
<script <script
@@ -36,7 +38,6 @@ export default function RootLayout({
async async
></script> ></script>
{/* Orbitron Font */} {/* Orbitron Font */}
<style>{orbitron.style}</style>
</head> </head>
<body <body
className={`${inter.className} bg-gradient-to-br from-[#0F0525] to-[#2A0B45]`} className={`${inter.className} bg-gradient-to-br from-[#0F0525] to-[#2A0B45]`}