diwali banner, portfolio content changes, and some hero text changes and slider add on homepage
This commit is contained in:
127
components/portfolio/PortfolioChallengeSolution.tsx
Normal file
127
components/portfolio/PortfolioChallengeSolution.tsx
Normal file
@@ -0,0 +1,127 @@
|
||||
import React from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import { CheckCircle } from "lucide-react";
|
||||
|
||||
interface Challenge {
|
||||
icon: React.ReactNode;
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
interface Technology {
|
||||
icon: React.ReactNode;
|
||||
label: string;
|
||||
}
|
||||
|
||||
interface Highlight {
|
||||
text: string;
|
||||
}
|
||||
|
||||
interface PortfolioChallengeSolutionProps {
|
||||
challengesTitle?: string;
|
||||
challenges: Challenge[];
|
||||
solutionTitle?: string;
|
||||
technologyStack: Technology[];
|
||||
highlights: Highlight[];
|
||||
}
|
||||
|
||||
const PortfolioChallengeSolution: React.FC<PortfolioChallengeSolutionProps> = ({
|
||||
challengesTitle = "Challenges & Constraints",
|
||||
challenges,
|
||||
solutionTitle = "Solution Architecture",
|
||||
technologyStack,
|
||||
highlights,
|
||||
}) => {
|
||||
return (
|
||||
<section className="py-24 bg-background">
|
||||
<div className="container mx-auto px-4 lg:px-6">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<div className="grid lg:grid-cols-2 gap-20">
|
||||
{/* Challenges */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -30 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<h2 className="text-3xl lg:text-4xl font-semibold text-foreground mb-12">
|
||||
{challengesTitle}
|
||||
</h2>
|
||||
<div className="space-y-6">
|
||||
{challenges.map((challenge, index) => (
|
||||
<motion.div
|
||||
key={challenge.title}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: index * 0.1 }}
|
||||
viewport={{ once: true }}
|
||||
className="bg-background/50 rounded-xl p-6 border border-border/50 hover:border-accent/20 transition-colors duration-300"
|
||||
>
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="text-accent mt-1">{challenge.icon}</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-foreground mb-2">
|
||||
{challenge.title}
|
||||
</h3>
|
||||
<p className="text-muted-foreground leading-relaxed">
|
||||
{challenge.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Solution Architecture */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: 30 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<h2 className="text-3xl lg:text-4xl font-semibold text-foreground mb-12">
|
||||
{solutionTitle}
|
||||
</h2>
|
||||
<div className="bg-background/50 rounded-2xl p-8 border border-border/50">
|
||||
<div className="space-y-8">
|
||||
<div>
|
||||
<h3 className="text-xl font-semibold text-foreground mb-6">
|
||||
Technology Stack
|
||||
</h3>
|
||||
<div className="space-y-4">
|
||||
{technologyStack.map((tech, idx) => (
|
||||
<div key={idx} className="flex items-center gap-3">
|
||||
{tech.icon}
|
||||
<span className="text-muted-foreground">{tech.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-xl font-semibold text-foreground mb-6">
|
||||
Key Highlights
|
||||
</h3>
|
||||
<div className="space-y-3">
|
||||
{highlights.map((item, idx) => (
|
||||
<div key={idx} className="flex items-start gap-3">
|
||||
<CheckCircle className="w-4 h-4 text-accent mt-0.5 flex-shrink-0" />
|
||||
<span className="text-muted-foreground text-base">
|
||||
{item.text}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default PortfolioChallengeSolution;
|
||||
73
components/portfolio/PortfolioCoreFeatures.tsx
Normal file
73
components/portfolio/PortfolioCoreFeatures.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
// components/PortfolioCoreFeatures.tsx
|
||||
import { motion } from "framer-motion";
|
||||
import { ReactNode } from "react";
|
||||
import { Card, CardContent } from "@/components/ui/card"; // adjust path as per your project
|
||||
|
||||
interface Feature {
|
||||
icon: ReactNode;
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
interface PortfolioCoreFeaturesProps {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
features: Feature[];
|
||||
}
|
||||
|
||||
export function PortfolioCoreFeatures({
|
||||
title,
|
||||
subtitle,
|
||||
features,
|
||||
}: PortfolioCoreFeaturesProps) {
|
||||
return (
|
||||
<section className="py-24 bg-card/30">
|
||||
<div className="container mx-auto px-4 lg:px-6">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
className="max-w-6xl mx-auto"
|
||||
>
|
||||
{/* Section Heading */}
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-3xl lg:text-5xl font-semibold text-foreground mb-6">
|
||||
{title}
|
||||
</h2>
|
||||
<p className="text-xl text-muted-foreground max-w-3xl mx-auto">
|
||||
{subtitle}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Features Grid */}
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
{features.map((feature, index) => (
|
||||
<motion.div
|
||||
key={feature.title}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: index * 0.1 }}
|
||||
viewport={{ once: true }}
|
||||
>
|
||||
<Card className="h-full bg-card/50 border-border/50 hover:border-accent/30 transition-all duration-300 group">
|
||||
<CardContent className="p-8">
|
||||
<div className="text-accent mb-4 group-hover:scale-110 transition-transform duration-300">
|
||||
{feature.icon}
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-foreground mb-4">
|
||||
{feature.title}
|
||||
</h3>
|
||||
<p className="text-muted-foreground leading-relaxed">
|
||||
{feature.description}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
78
components/portfolio/PortfolioDevelopmentProcess.tsx
Normal file
78
components/portfolio/PortfolioDevelopmentProcess.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
// components/PortfolioDevelopmentProcess.tsx
|
||||
import { motion } from "framer-motion";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
interface Phase {
|
||||
icon: ReactNode;
|
||||
phase: string;
|
||||
duration: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
interface PortfolioDevelopmentProcessProps {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
phases: Phase[];
|
||||
}
|
||||
|
||||
export function PortfolioDevelopmentProcess({
|
||||
title,
|
||||
subtitle,
|
||||
phases,
|
||||
}: PortfolioDevelopmentProcessProps) {
|
||||
return (
|
||||
<section className="py-24 bg-card/30">
|
||||
<div className="container mx-auto px-4 lg:px-6">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
className="max-w-6xl mx-auto"
|
||||
>
|
||||
{/* Section Heading */}
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-3xl lg:text-5xl font-semibold text-foreground mb-6">
|
||||
{title}
|
||||
</h2>
|
||||
<p className="text-xl text-muted-foreground max-w-3xl mx-auto">
|
||||
{subtitle}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Phases Grid */}
|
||||
<div className="grid lg:grid-cols-5 gap-8">
|
||||
{phases.map((phase, index) => (
|
||||
<motion.div
|
||||
key={phase.phase}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: index * 0.1 }}
|
||||
viewport={{ once: true }}
|
||||
className="relative"
|
||||
>
|
||||
<div className="bg-card/50 rounded-xl p-6 border border-border/50 hover:border-accent/30 transition-all duration-300 h-full">
|
||||
<div className="text-accent mb-4">{phase.icon}</div>
|
||||
<h3 className="text-lg font-semibold text-foreground mb-2">
|
||||
{phase.phase}
|
||||
</h3>
|
||||
<div className="text-accent font-medium mb-3">
|
||||
{phase.duration}
|
||||
</div>
|
||||
<p className="text-muted-foreground text-sm leading-relaxed">
|
||||
{phase.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Connector Line */}
|
||||
{index < phases.length - 1 && (
|
||||
<div className="hidden lg:block absolute top-1/2 -right-4 w-8 h-0.5 bg-border transform -translate-y-1/2" />
|
||||
)}
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
35
components/portfolio/PortfolioExecutiveSummary.tsx
Normal file
35
components/portfolio/PortfolioExecutiveSummary.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
// components/portfolio/PortfolioExecutiveSummary.tsx
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
interface PortfolioExecutiveSummaryProps {
|
||||
title?: string;
|
||||
content: string;
|
||||
backgroundColor?: string;
|
||||
}
|
||||
|
||||
export const PortfolioExecutiveSummary = ({
|
||||
title = "Executive Summary",
|
||||
content,
|
||||
backgroundColor = "bg-card/30"
|
||||
}: PortfolioExecutiveSummaryProps) => {
|
||||
return (
|
||||
<section className={`py-24 ${backgroundColor}`}>
|
||||
<div className="container mx-auto px-4 lg:px-6">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
className="max-w-4xl mx-auto text-center"
|
||||
>
|
||||
<h2 className="text-3xl lg:text-5xl font-semibold text-foreground mb-8">
|
||||
{title}
|
||||
</h2>
|
||||
<p className="text-xl text-muted-foreground leading-relaxed">
|
||||
{content}
|
||||
</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
96
components/portfolio/PortfolioHero.tsx
Normal file
96
components/portfolio/PortfolioHero.tsx
Normal file
@@ -0,0 +1,96 @@
|
||||
// components/portfolio/PortfolioHero.tsx
|
||||
import { motion } from "framer-motion";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ArrowLeft } from "lucide-react";
|
||||
import { GridPattern } from "../GridPattern";
|
||||
import { ImageWithFallback } from "../figma/ImageWithFallback";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
interface PortfolioHeroProps {
|
||||
badgeText?: string;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
subtitleTwo?: string;
|
||||
imageUrl: string;
|
||||
imageAlt: string;
|
||||
}
|
||||
|
||||
export const PortfolioHero = ({
|
||||
badgeText = "Portfolio",
|
||||
title,
|
||||
subtitle,
|
||||
subtitleTwo,
|
||||
imageUrl,
|
||||
imageAlt,
|
||||
}: PortfolioHeroProps) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<section className="relative pt-32 pb-24 bg-background overflow-hidden">
|
||||
<GridPattern strokeDasharray="4 2" />
|
||||
|
||||
<div className="relative z-10 container mx-auto px-4 lg:px-6">
|
||||
{/* Back Button */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
className="mb-12"
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => navigate("/case-studies")}
|
||||
className="text-muted-foreground hover:text-foreground flex items-center gap-2 px-0 hover:bg-transparent"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4" />
|
||||
Back to Portfolio
|
||||
</Button>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid lg:grid-cols-12 gap-16 items-center">
|
||||
{/* Content - Left Aligned */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
className="lg:col-span-7"
|
||||
>
|
||||
<div className="mb-6">
|
||||
<Badge variant="secondary" className="text-accent border-accent/20 bg-accent/10">
|
||||
{badgeText}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<h1 className="text-4xl lg:text-6xl font-semibold text-foreground mb-8 leading-tight">
|
||||
{title}
|
||||
</h1>
|
||||
|
||||
<p className="text-xl text-muted-foreground mb-6 leading-relaxed max-w-2xl">
|
||||
{subtitle}
|
||||
</p>
|
||||
<p className="text-xl text-muted-foreground mb-10 leading-relaxed max-w-2xl">
|
||||
{subtitleTwo}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* Project Image */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, delay: 0.2 }}
|
||||
className="lg:col-span-5"
|
||||
>
|
||||
<div className="relative aspect-[4/3] overflow-hidden bg-card/30 rounded-2xl border border-border/50 p-4">
|
||||
<ImageWithFallback
|
||||
src={imageUrl}
|
||||
alt={imageAlt}
|
||||
className="w-full h-full object-contain object-center rounded-xl"
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
119
components/portfolio/PortfolioLessonsSection.tsx
Normal file
119
components/portfolio/PortfolioLessonsSection.tsx
Normal file
@@ -0,0 +1,119 @@
|
||||
import React from "react";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
interface LessonSectionProps {
|
||||
title: string;
|
||||
description: string;
|
||||
workedTitle: string;
|
||||
workedIcon: React.ReactNode;
|
||||
workedColor: string;
|
||||
workedLessons: string[];
|
||||
improveTitle: string;
|
||||
improveIcon: React.ReactNode;
|
||||
improveColor: string;
|
||||
improveLessons: string[];
|
||||
}
|
||||
|
||||
const PortfolioLessonsSection: React.FC<LessonSectionProps> = ({
|
||||
title,
|
||||
description,
|
||||
workedTitle,
|
||||
workedIcon,
|
||||
workedColor,
|
||||
workedLessons,
|
||||
improveTitle,
|
||||
improveIcon,
|
||||
improveColor,
|
||||
improveLessons,
|
||||
}) => {
|
||||
return (
|
||||
<section className="py-24 bg-card/30">
|
||||
<div className="container mx-auto px-4 lg:px-6">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
{/* Section Header */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
className="text-center mb-16"
|
||||
>
|
||||
<h2 className="text-3xl lg:text-5xl font-semibold text-foreground mb-6">
|
||||
{title}
|
||||
</h2>
|
||||
<p className="text-xl text-muted-foreground max-w-3xl mx-auto">
|
||||
{description}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* Lessons Grid */}
|
||||
<div className="grid lg:grid-cols-2 gap-12">
|
||||
{/* Worked Well */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -30 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
className={`rounded-2xl p-8 border`}
|
||||
style={{
|
||||
backgroundColor: `${workedColor}0D`, // light transparent bg
|
||||
borderColor: `${workedColor}33`, // faint border
|
||||
}}
|
||||
>
|
||||
<h3 className="text-2xl font-semibold text-foreground mb-8 flex items-center gap-3">
|
||||
<span className="text-xl" style={{ color: workedColor }}>
|
||||
{workedIcon}
|
||||
</span>
|
||||
{workedTitle}
|
||||
</h3>
|
||||
<div className="space-y-4">
|
||||
{workedLessons.map((lesson, index) => (
|
||||
<div key={index} className="flex items-start gap-3">
|
||||
<div
|
||||
className="w-2 h-2 rounded-full mt-2 flex-shrink-0"
|
||||
style={{ backgroundColor: workedColor }}
|
||||
/>
|
||||
<span className="text-muted-foreground">{lesson}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Areas for Improvement */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: 30 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
className={`rounded-2xl p-8 border`}
|
||||
style={{
|
||||
backgroundColor: `${improveColor}0D`,
|
||||
borderColor: `${improveColor}33`,
|
||||
}}
|
||||
>
|
||||
<h3 className="text-2xl font-semibold text-foreground mb-8 flex items-center gap-3">
|
||||
<span className="text-xl" style={{ color: improveColor }}>
|
||||
{improveIcon}
|
||||
</span>
|
||||
{improveTitle}
|
||||
</h3>
|
||||
<div className="space-y-4">
|
||||
{improveLessons.map((lesson, index) => (
|
||||
<div key={index} className="flex items-start gap-3">
|
||||
<div
|
||||
className="w-2 h-2 rounded-full mt-2 flex-shrink-0"
|
||||
style={{ backgroundColor: improveColor }}
|
||||
/>
|
||||
<span className="text-muted-foreground">{lesson}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default PortfolioLessonsSection;
|
||||
233
components/portfolio/PortfolioProjectDetails.tsx
Normal file
233
components/portfolio/PortfolioProjectDetails.tsx
Normal file
@@ -0,0 +1,233 @@
|
||||
// components/portfolio/PortfolioProjectDetails.tsx
|
||||
import { motion } from "framer-motion";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import {
|
||||
Code,
|
||||
ShoppingCart,
|
||||
Calendar,
|
||||
Users,
|
||||
Smartphone
|
||||
} from "lucide-react";
|
||||
|
||||
interface Technology {
|
||||
name: string;
|
||||
icon: React.ReactNode;
|
||||
}
|
||||
|
||||
interface ProjectDetails {
|
||||
technologies: Technology[];
|
||||
industries: string[];
|
||||
duration: string;
|
||||
teamSize: string;
|
||||
platforms: string[];
|
||||
}
|
||||
|
||||
interface PortfolioProjectDetailsProps {
|
||||
title?: string;
|
||||
description?: string;
|
||||
details: ProjectDetails;
|
||||
achievements?: Array<{
|
||||
label: string;
|
||||
value: string;
|
||||
description: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export const PortfolioProjectDetails = ({
|
||||
title = "Project Details",
|
||||
description = "Detailed overview of the project including technologies, timeline, and team composition.",
|
||||
details,
|
||||
achievements = []
|
||||
}: PortfolioProjectDetailsProps) => {
|
||||
return (
|
||||
<section className="py-24 bg-card/30 relative overflow-hidden">
|
||||
{/* Background Elements */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-accent/5 via-transparent to-green-500/5" />
|
||||
<div className="absolute top-20 right-20 w-64 h-64 bg-accent/10 rounded-full blur-3xl opacity-20" />
|
||||
<div className="absolute bottom-20 left-20 w-48 h-48 bg-green-500/10 rounded-full blur-3xl opacity-20" />
|
||||
|
||||
<div className="container mx-auto px-4 lg:px-6 relative z-10">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
{/* Section Header */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
className="text-center mb-16"
|
||||
>
|
||||
<h2 className="text-3xl lg:text-5xl font-semibold text-foreground mb-6">
|
||||
{title}
|
||||
</h2>
|
||||
<p className="text-xl text-muted-foreground max-w-3xl mx-auto">
|
||||
{description}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* Project Meta Information Grid */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, delay: 0.2 }}
|
||||
className="grid lg:grid-cols-2 gap-8 mb-20"
|
||||
>
|
||||
{/* Technologies & Industries Card */}
|
||||
<div className="bg-background/40 backdrop-blur-xl rounded-2xl p-8 border border-border/30 hover:border-accent/20 transition-all duration-500 group">
|
||||
<div className="space-y-8">
|
||||
{/* Technologies */}
|
||||
<div>
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-12 h-12 bg-accent/10 backdrop-blur-sm rounded-xl border border-accent/20 flex items-center justify-center group-hover:bg-accent/20 transition-all duration-300">
|
||||
<Code className="w-6 h-6 text-accent" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-foreground">Technologies</h3>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-3">
|
||||
{details.technologies.map((tech) => (
|
||||
<Badge
|
||||
key={tech.name}
|
||||
variant="outline"
|
||||
className="text-base border-border/40 bg-background/30 hover:bg-accent/10 hover:border-accent/40 flex items-center gap-2 px-4 py-2 transition-all duration-300"
|
||||
>
|
||||
<span className="text-accent">{tech.icon}</span>
|
||||
{tech.name}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Industries */}
|
||||
<div>
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-12 h-12 bg-green-500/10 backdrop-blur-sm rounded-xl border border-green-500/20 flex items-center justify-center group-hover:bg-green-500/20 transition-all duration-300">
|
||||
<ShoppingCart className="w-6 h-6 text-green-400" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-foreground">Industries</h3>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-3">
|
||||
{details.industries.map((industry) => (
|
||||
<Badge
|
||||
key={industry}
|
||||
variant="secondary"
|
||||
className="text-base bg-green-500/10 border-green-500/20 text-green-100 hover:bg-green-500/20 px-4 py-2 transition-all duration-300"
|
||||
>
|
||||
{industry}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Timeline & Team Card */}
|
||||
<div className="bg-background/40 backdrop-blur-xl rounded-2xl p-8 border border-border/30 hover:border-blue-400/20 transition-all duration-500 group">
|
||||
<div className="space-y-8">
|
||||
{/* Duration */}
|
||||
<div>
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-12 h-12 bg-blue-500/10 backdrop-blur-sm rounded-xl border border-blue-500/20 flex items-center justify-center group-hover:bg-blue-500/20 transition-all duration-300">
|
||||
<Calendar className="w-6 h-6 text-blue-400" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-foreground">Project Timeline</h3>
|
||||
</div>
|
||||
<p className="text-lg text-muted-foreground pl-15">{details.duration}</p>
|
||||
</div>
|
||||
|
||||
{/* Team */}
|
||||
<div>
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-12 h-12 bg-purple-500/10 backdrop-blur-sm rounded-xl border border-purple-500/20 flex items-center justify-center group-hover:bg-purple-500/20 transition-all duration-300">
|
||||
<Users className="w-6 h-6 text-purple-400" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-foreground">Team Composition</h3>
|
||||
</div>
|
||||
<p className="text-lg text-muted-foreground pl-15">{details.teamSize}</p>
|
||||
</div>
|
||||
|
||||
{/* Platforms */}
|
||||
<div>
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-12 h-12 bg-orange-500/10 backdrop-blur-sm rounded-xl border border-orange-500/20 flex items-center justify-center group-hover:bg-orange-500/20 transition-all duration-300">
|
||||
<Smartphone className="w-6 h-6 text-orange-400" />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold text-foreground">Target Platforms</h3>
|
||||
</div>
|
||||
<div className="flex gap-3 pl-15">
|
||||
{details.platforms.map((platform) => (
|
||||
<Badge
|
||||
key={platform}
|
||||
variant="outline"
|
||||
className="text-base border-orange-400/40 bg-orange-500/10 text-orange-100 hover:bg-orange-500/20 px-3 py-1"
|
||||
>
|
||||
{platform}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Key Achievements Section */}
|
||||
{achievements.length > 0 && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, delay: 0.4 }}
|
||||
className="mb-16"
|
||||
>
|
||||
<div className="text-center mb-12">
|
||||
<h2 className="text-3xl lg:text-4xl font-semibold text-foreground mb-6">
|
||||
Key Impact & Results
|
||||
</h2>
|
||||
<p className="text-xl text-muted-foreground max-w-3xl mx-auto">
|
||||
Measurable outcomes that demonstrate the project's success
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
{achievements.map((achievement, index) => (
|
||||
<motion.div
|
||||
key={achievement.label}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: 0.6 + index * 0.1 }}
|
||||
whileHover={{
|
||||
scale: 1.02,
|
||||
y: -4,
|
||||
transition: { duration: 0.3, ease: "easeOut" }
|
||||
}}
|
||||
className="bg-background/50 backdrop-blur-xl rounded-2xl p-8 border border-border/40 hover:border-accent/30 hover:bg-background/60 transition-all duration-500 group cursor-pointer relative overflow-hidden"
|
||||
>
|
||||
{/* Card Background Gradient */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-accent/5 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
|
||||
|
||||
{/* Content */}
|
||||
<div className="relative z-10 text-center">
|
||||
{/* Value */}
|
||||
<div className="text-3xl lg:text-4xl font-bold text-accent mb-4 group-hover:text-accent transition-colors duration-300">
|
||||
{achievement.value}
|
||||
</div>
|
||||
|
||||
{/* Label */}
|
||||
<div className="text-xl font-semibold text-foreground mb-3 group-hover:text-foreground transition-colors duration-300">
|
||||
{achievement.label}
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
<div className="text-base text-muted-foreground leading-relaxed group-hover:text-muted-foreground transition-colors duration-300">
|
||||
{achievement.description}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Hover Effect Line */}
|
||||
<div className="absolute bottom-0 left-0 w-full h-1 bg-gradient-to-r from-accent to-accent/50 transform scale-x-0 group-hover:scale-x-100 transition-transform duration-500 origin-left" />
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
75
components/portfolio/PortfolioProjectOverview.tsx
Normal file
75
components/portfolio/PortfolioProjectOverview.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
// components/PortfolioProjectOverview.tsx
|
||||
import { motion } from "framer-motion";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
interface PortfolioProjectOverviewProps {
|
||||
icon: ReactNode;
|
||||
title: string;
|
||||
description?: string;
|
||||
points?: string[];
|
||||
borderColor?: string;
|
||||
hoverColor?: string;
|
||||
}
|
||||
|
||||
export function PortfolioProjectOverview({
|
||||
icon,
|
||||
title,
|
||||
description,
|
||||
points,
|
||||
borderColor = "border-accent/20",
|
||||
hoverColor = "accent",
|
||||
}: PortfolioProjectOverviewProps) {
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
whileHover={{
|
||||
scale: 1.05,
|
||||
y: -4,
|
||||
transition: { duration: 0.3, ease: "easeOut" },
|
||||
}}
|
||||
transition={{ duration: 0.6 }}
|
||||
viewport={{ once: true }}
|
||||
className={`bg-card/30 rounded-2xl p-8 border-2 ${borderColor} cursor-pointer group
|
||||
hover:border-${hoverColor}/40 hover:bg-card/40
|
||||
hover:shadow-lg hover:shadow-${hoverColor}/10
|
||||
transition-all duration-300 ease-out`}
|
||||
>
|
||||
{/* Icon */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
whileInView={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
viewport={{ once: true }}
|
||||
className="flex justify-start mb-6"
|
||||
>
|
||||
<div
|
||||
className={`w-16 h-16 bg-background/20 backdrop-blur-sm rounded-full
|
||||
border border-border/30 flex items-center justify-center
|
||||
group-hover:border-${hoverColor}/50 group-hover:bg-${hoverColor}/10
|
||||
transition-all duration-300 ease-out`}
|
||||
>
|
||||
{icon}
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* Title */}
|
||||
<h3 className="text-2xl font-semibold text-foreground mb-6">{title}</h3>
|
||||
|
||||
{/* Content */}
|
||||
{description && (
|
||||
<p className="text-muted-foreground leading-relaxed">{description}</p>
|
||||
)}
|
||||
{points && (
|
||||
<div className="space-y-3">
|
||||
{points.map((point, i) => (
|
||||
<div key={i} className="flex items-start gap-3">
|
||||
<span className="w-5 h-5 text-accent mt-0.5 flex-shrink-0">✔</span>
|
||||
<span className="text-muted-foreground text-base">{point}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
92
components/portfolio/PortfolioResultsImpact.tsx
Normal file
92
components/portfolio/PortfolioResultsImpact.tsx
Normal file
@@ -0,0 +1,92 @@
|
||||
// components/PortfolioResultsImpact.tsx
|
||||
import { motion } from "framer-motion";
|
||||
import { CheckCircle } from "lucide-react";
|
||||
|
||||
interface Metric {
|
||||
value: string;
|
||||
label: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
interface PortfolioResultsImpactProps {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
metrics: Metric[];
|
||||
achievements: string[];
|
||||
}
|
||||
|
||||
export function PortfolioResultsImpact({
|
||||
title,
|
||||
subtitle,
|
||||
metrics,
|
||||
achievements,
|
||||
}: PortfolioResultsImpactProps) {
|
||||
return (
|
||||
<section className="py-24 bg-background">
|
||||
<div className="container mx-auto px-4 lg:px-6">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
className="max-w-6xl mx-auto"
|
||||
>
|
||||
{/* Heading */}
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-3xl lg:text-5xl font-semibold text-foreground mb-6">
|
||||
{title}
|
||||
</h2>
|
||||
<p className="text-xl text-muted-foreground max-w-3xl mx-auto">
|
||||
{subtitle}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Metrics */}
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8 mb-16">
|
||||
{metrics.map((metric, index) => (
|
||||
<motion.div
|
||||
key={metric.label}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: index * 0.1 }}
|
||||
viewport={{ once: true }}
|
||||
className="bg-background/50 rounded-xl p-6 border border-border/50 hover:border-accent/30 transition-all duration-300 text-center"
|
||||
>
|
||||
<div className="text-3xl font-bold text-accent mb-2">
|
||||
{metric.value}
|
||||
</div>
|
||||
<div className="text-lg font-semibold text-foreground mb-2">
|
||||
{metric.label}
|
||||
</div>
|
||||
<div className="text-muted-foreground text-sm">
|
||||
{metric.description}
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Achievements */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8, delay: 0.3 }}
|
||||
viewport={{ once: true }}
|
||||
className="bg-background/50 rounded-2xl p-8 border border-border/50"
|
||||
>
|
||||
<h3 className="text-2xl font-semibold text-foreground mb-6">
|
||||
Technical Achievements
|
||||
</h3>
|
||||
<div className="grid md:grid-cols-2 gap-4">
|
||||
{achievements.map((achievement, index) => (
|
||||
<div key={index} className="flex items-start gap-3">
|
||||
<CheckCircle className="w-5 h-5 text-accent mt-0.5 flex-shrink-0" />
|
||||
<span className="text-muted-foreground">{achievement}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
75
components/portfolio/PortfolioRoadmapSection.tsx
Normal file
75
components/portfolio/PortfolioRoadmapSection.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import React from "react";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
interface RoadmapItem {
|
||||
phase: string;
|
||||
features: string[];
|
||||
}
|
||||
|
||||
interface PortfolioRoadmapSectionProps {
|
||||
title: string;
|
||||
description: string;
|
||||
roadmapItems: RoadmapItem[];
|
||||
icon?: React.ReactNode; // optional (default ArrowRight)
|
||||
}
|
||||
|
||||
const PortfolioRoadmapSection: React.FC<PortfolioRoadmapSectionProps> = ({
|
||||
title,
|
||||
description,
|
||||
roadmapItems,
|
||||
icon,
|
||||
}) => {
|
||||
return (
|
||||
<section className="py-24 bg-background">
|
||||
<div className="container mx-auto px-4 lg:px-6">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
className="max-w-6xl mx-auto"
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-3xl lg:text-5xl font-semibold text-foreground mb-6">
|
||||
{title}
|
||||
</h2>
|
||||
<p className="text-xl text-muted-foreground max-w-3xl mx-auto">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Roadmap Grid */}
|
||||
<div className="grid lg:grid-cols-2 gap-12">
|
||||
{roadmapItems.map((roadmap, index) => (
|
||||
<motion.div
|
||||
key={roadmap.phase}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, delay: index * 0.2 }}
|
||||
viewport={{ once: true }}
|
||||
className="bg-background/50 rounded-2xl p-8 border border-border/50 hover:border-accent/30 transition-all duration-300"
|
||||
>
|
||||
<h3 className="text-2xl font-semibold text-foreground mb-6">
|
||||
{roadmap.phase}
|
||||
</h3>
|
||||
<div className="space-y-4">
|
||||
{roadmap.features.map((feature, featureIndex) => (
|
||||
<div key={featureIndex} className="flex items-start gap-3">
|
||||
<span className="w-5 h-5 text-accent mt-0.5 flex-shrink-0">
|
||||
{icon}
|
||||
</span>
|
||||
<span className="text-muted-foreground">{feature}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default PortfolioRoadmapSection;
|
||||
70
components/portfolio/PortfolioTestimonial.tsx
Normal file
70
components/portfolio/PortfolioTestimonial.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import React from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import { Star } from "lucide-react";
|
||||
|
||||
interface PortfolioTestimonialProps {
|
||||
logo: string | React.ReactNode; // can be an image URL or React component
|
||||
alt?: string;
|
||||
rating?: number; // default 5
|
||||
testimonial: string;
|
||||
clientName: string;
|
||||
clientRole: string;
|
||||
maxWidth?: string; // optional, e.g., "max-w-4xl"
|
||||
}
|
||||
|
||||
const PortfolioTestimonial: React.FC<PortfolioTestimonialProps> = ({
|
||||
logo,
|
||||
alt = "Client Logo",
|
||||
rating = 5,
|
||||
testimonial,
|
||||
clientName,
|
||||
clientRole,
|
||||
maxWidth = "max-w-4xl",
|
||||
}) => {
|
||||
return (
|
||||
<section className="py-24 bg-card/30">
|
||||
<div className="container mx-auto px-4 lg:px-6">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
viewport={{ once: true }}
|
||||
className={`mx-auto text-center ${maxWidth}`}
|
||||
>
|
||||
<div className="bg-card/50 rounded-2xl p-12 border border-border/50">
|
||||
{/* Logo */}
|
||||
{typeof logo === "string" ? (
|
||||
<div className="flex justify-center mb-8">
|
||||
<img src={logo} alt={alt} className="h-12 w-auto object-contain" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex justify-center mb-8">{logo}</div>
|
||||
)}
|
||||
|
||||
{/* Rating */}
|
||||
<div className="flex justify-center mb-8">
|
||||
<div className="flex text-yellow-400">
|
||||
{[...Array(rating)].map((_, i) => (
|
||||
<Star key={i} className="w-6 h-6 fill-current" />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Testimonial */}
|
||||
<blockquote className="text-2xl lg:text-3xl text-foreground mb-8 leading-relaxed italic">
|
||||
{testimonial}
|
||||
</blockquote>
|
||||
|
||||
{/* Client Info */}
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="text-lg font-semibold text-foreground">{clientName}</div>
|
||||
<div className="text-muted-foreground">{clientRole}</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default PortfolioTestimonial;
|
||||
Reference in New Issue
Block a user