protofolio / frontend /src /components /modals /ProjectModal.tsx
moustafa7's picture
Redesign: Project modal with modern gradient style
a55b87b
Raw History Blame Contribute Delete
6.39 kB
import { Dialog, DialogContent } from "@/components/ui/dialog";
import type { Project } from "@/types";
import { useTranslation } from "react-i18next";
import { ExternalLink, X, Code2, Tag, Star } from "lucide-react";
import { motion } from "framer-motion";
const GithubIcon = ({ className }: { className?: string }) => (
<svg className={className} viewBox="0 0 24 24" fill="currentColor"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
);
interface ProjectModalProps {
project: Project | null;
open: boolean;
onClose: () => void;
}
export function ProjectModal({ project, open, onClose }: ProjectModalProps) {
const { i18n } = useTranslation();
const isArabic = i18n.language === "ar";
if (!project) return null;
const title = isArabic ? project.titleAr : project.titleEn;
const fullDesc = isArabic ? project.fullDescAr : project.fullDescEn;
const category = isArabic ? project.categoryAr : project.categoryEn;
const technologies = project.technologies.split(",").map((t) => t.trim());
return (
<Dialog open={open} onOpenChange={onClose}>
<DialogContent showCloseButton={false} className="!max-w-4xl !w-[calc(100%-2rem)] max-h-[90vh] overflow-y-auto !p-0 bg-white dark:bg-slate-900 !border-0 !rounded-2xl !shadow-2xl">
{/* Close Button */}
<button
onClick={onClose}
className="absolute top-4 right-4 z-50 p-2 rounded-full bg-black/20 backdrop-blur-sm text-white hover:bg-black/40 transition-colors"
>
<X className="w-5 h-5" />
</button>
{/* Cover Image */}
{project.coverImage && (
<div className="relative h-72 md:h-80 overflow-hidden rounded-t-2xl">
<img src={project.coverImage} alt={title} className="w-full h-full object-cover" />
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent" />
<div className="absolute bottom-0 left-0 right-0 p-6 md:p-8">
<div className="flex items-center gap-2 mb-3">
<span className="px-3 py-1 text-xs font-semibold bg-blue-600 text-white rounded-full">
{category}
</span>
{project.featured && (
<span className="px-3 py-1 text-xs font-semibold bg-amber-500 text-white rounded-full flex items-center gap-1">
<Star className="w-3 h-3" fill="currentColor" /> Featured
</span>
)}
</div>
<h2 className="text-2xl md:text-3xl font-bold text-white">
{title}
</h2>
</div>
</div>
)}
{/* Gallery */}
{project.images.length > 0 && (
<div className="px-6 pt-6">
<div className="grid grid-cols-2 md:grid-cols-3 gap-3">
{project.images.map((img) => (
<div key={img.id} className="relative h-36 md:h-44 rounded-xl overflow-hidden group">
<img src={img.url} alt="" className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-500" />
<div className="absolute inset-0 bg-black/0 group-hover:bg-black/20 transition-colors" />
</div>
))}
</div>
</div>
)}
{/* Content */}
<div className="p-6 md:p-8 space-y-6">
{/* Description */}
<div>
<p className="text-slate-600 dark:text-slate-300 leading-relaxed text-base">
{fullDesc}
</p>
</div>
{/* Technologies */}
<div>
<div className="flex items-center gap-2 mb-3">
<Code2 className="w-4 h-4 text-blue-600 dark:text-blue-400" />
<h4 className="text-sm font-bold text-slate-900 dark:text-white uppercase tracking-wider">
{isArabic ? "التقنيات" : "Technologies"}
</h4>
</div>
<div className="flex flex-wrap gap-2">
{technologies.map((tech, i) => (
<span
key={i}
className="px-4 py-1.5 text-sm font-medium bg-gradient-to-r from-blue-50 to-cyan-50 dark:from-blue-950/50 dark:to-cyan-950/50 text-blue-700 dark:text-blue-300 rounded-full border border-blue-100 dark:border-blue-900/50"
>
{tech}
</span>
))}
</div>
</div>
{/* Action Buttons */}
<div className="flex flex-wrap gap-3 pt-2">
{project.previewUrl && (
<a
href={project.previewUrl}
target="_blank"
rel="noreferrer"
className="flex items-center gap-2 px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white rounded-xl text-sm font-semibold transition-all shadow-lg shadow-blue-600/25 hover:shadow-blue-600/40 hover:-translate-y-0.5"
>
<ExternalLink className="w-4 h-4" />
{isArabic ? "معاينة" : "Live Preview"}
</a>
)}
{project.githubUrl && (
<a
href={project.githubUrl}
target="_blank"
rel="noreferrer"
className="flex items-center gap-2 px-6 py-3 bg-slate-800 dark:bg-slate-700 hover:bg-slate-900 dark:hover:bg-slate-600 text-white rounded-xl text-sm font-semibold transition-all shadow-lg hover:-translate-y-0.5"
>
<GithubIcon className="w-4 h-4" />
GitHub
</a>
)}
</div>
</div>
</DialogContent>
</Dialog>
);
}