Add image carousel to expertise section
This commit is contained in:
10
frontend/website/package-lock.json
generated
10
frontend/website/package-lock.json
generated
@@ -16,6 +16,7 @@
|
|||||||
"@types/node": "^22.13.5",
|
"@types/node": "^22.13.5",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
"embla-carousel-autoplay": "^8.5.2",
|
||||||
"embla-carousel-react": "^8.5.2",
|
"embla-carousel-react": "^8.5.2",
|
||||||
"lucide-react": "^0.476.0",
|
"lucide-react": "^0.476.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
@@ -2602,6 +2603,15 @@
|
|||||||
"integrity": "sha512-xQ9oVLrun/eCG/7ru3R+I5bJ7shsD8fFwLEY7yPe27/+fDHCNj0OT5EoG5ZbFyOxOcG6yTwW8oTz/dWyFnyGpg==",
|
"integrity": "sha512-xQ9oVLrun/eCG/7ru3R+I5bJ7shsD8fFwLEY7yPe27/+fDHCNj0OT5EoG5ZbFyOxOcG6yTwW8oTz/dWyFnyGpg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/embla-carousel-autoplay": {
|
||||||
|
"version": "8.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/embla-carousel-autoplay/-/embla-carousel-autoplay-8.5.2.tgz",
|
||||||
|
"integrity": "sha512-27emJ0px3q/c0kCHCjwRrEbYcyYUPfGO3g5IBWF1i7714TTzE6L9P81V6PHLoSMAKJ1aHoT2e7YFOsuFKCbyag==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"embla-carousel": "8.5.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/embla-carousel-react": {
|
"node_modules/embla-carousel-react": {
|
||||||
"version": "8.5.2",
|
"version": "8.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/embla-carousel-react/-/embla-carousel-react-8.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/embla-carousel-react/-/embla-carousel-react-8.5.2.tgz",
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
"@types/node": "^22.13.5",
|
"@types/node": "^22.13.5",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
"embla-carousel-autoplay": "^8.5.2",
|
||||||
"embla-carousel-react": "^8.5.2",
|
"embla-carousel-react": "^8.5.2",
|
||||||
"lucide-react": "^0.476.0",
|
"lucide-react": "^0.476.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
|
|||||||
@@ -12,16 +12,20 @@ const About = () => {
|
|||||||
<ExpertiseSection
|
<ExpertiseSection
|
||||||
title="Computer Vision"
|
title="Computer Vision"
|
||||||
content="Expertise in developing and deploying computer vision models for various applications, including object detection, image classification, and more. Ask me about state-of-the-art real-time models for your business."
|
content="Expertise in developing and deploying computer vision models for various applications, including object detection, image classification, and more. Ask me about state-of-the-art real-time models for your business."
|
||||||
imageUrl="https://gifs.cc/congratulation/congrats-animation-smiley-2018.gif"
|
images={[
|
||||||
imageAlt="Computer Vision"
|
{ url: "https://gifs.cc/congratulation/congrats-animation-smiley-2018.gif", alt: "Computer Vision 1" },
|
||||||
|
{ url: "https://example.com/image2.jpg", alt: "Computer Vision 2" },
|
||||||
|
]}
|
||||||
viewMoreLink="/computer-vision"
|
viewMoreLink="/computer-vision"
|
||||||
imageOnLeft={true}
|
imageOnLeft={true}
|
||||||
/>
|
/>
|
||||||
<ExpertiseSection
|
<ExpertiseSection
|
||||||
title="Natural Language Processing"
|
title="Natural Language Processing"
|
||||||
content="Building NLP models for tasks such as sentiment analysis, text classification, and language generation. If you need a chatbot for your business and care about privacy, or a simple text classification algorithm to understand your data, I can help."
|
content="Building NLP models for tasks such as sentiment analysis, text classification, and language generation. If you need a chatbot for your business and care about privacy, or a simple text classification algorithm to understand your data, I can help."
|
||||||
imageUrl="/path/to/nlp-image.jpg"
|
images={[
|
||||||
imageAlt="Natural Language Processing"
|
{ url: "/path/to/nlp-image.jpg", alt: "NLP 1" },
|
||||||
|
{ url: "/path/to/nlp-image2.jpg", alt: "NLP 2" },
|
||||||
|
]}
|
||||||
viewMoreLink="/nlp"
|
viewMoreLink="/nlp"
|
||||||
imageOnLeft={false}
|
imageOnLeft={false}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import ImageCarousel from './ImageCarousel';
|
||||||
|
|
||||||
interface ExpertiseSectionProps {
|
interface ExpertiseSectionProps {
|
||||||
title: string;
|
title: string;
|
||||||
content: string;
|
content: string;
|
||||||
imageUrl: string;
|
images: { url: string; alt: string }[];
|
||||||
imageAlt: string;
|
|
||||||
viewMoreLink: string;
|
viewMoreLink: string;
|
||||||
imageOnLeft?: boolean;
|
imageOnLeft?: boolean;
|
||||||
}
|
}
|
||||||
@@ -13,18 +13,17 @@ interface ExpertiseSectionProps {
|
|||||||
const ExpertiseSection: React.FC<ExpertiseSectionProps> = ({
|
const ExpertiseSection: React.FC<ExpertiseSectionProps> = ({
|
||||||
title,
|
title,
|
||||||
content,
|
content,
|
||||||
imageUrl,
|
images,
|
||||||
imageAlt,
|
|
||||||
viewMoreLink,
|
viewMoreLink,
|
||||||
imageOnLeft = false,
|
imageOnLeft = false,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<section className="expertise-section py-20 bg-gray-100">
|
<section className="expertise-section py-20 bg-gray-100">
|
||||||
<div className={`container mx-auto flex flex-col md:flex-row items-center ${imageOnLeft ? 'md:flex-row-reverse' : ''}`}>
|
<div className={`container mx-auto flex flex-col md:flex-row items-center ${imageOnLeft ? 'md:flex-row-reverse' : ''}`}>
|
||||||
<div className="md:w-1/2 mb-8 md:mb-0">
|
<div className="md:w-1/3 mb-8 mx-10 md:mb-0">
|
||||||
<img src={imageUrl} alt={imageAlt} className="rounded-lg shadow-md" />
|
<ImageCarousel images={images} />
|
||||||
</div>
|
</div>
|
||||||
<div className="md:w-1/2 md:px-8">
|
<div className="md:w-2/3 md:px-8">
|
||||||
<h2 className="text-3xl font-bold mb-4">{title}</h2>
|
<h2 className="text-3xl font-bold mb-4">{title}</h2>
|
||||||
<p className="text-lg mb-4">{content}</p>
|
<p className="text-lg mb-4">{content}</p>
|
||||||
<Button asChild>
|
<Button asChild>
|
||||||
|
|||||||
40
frontend/website/src/components/ImageCarousel.tsx
Normal file
40
frontend/website/src/components/ImageCarousel.tsx
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Carousel,
|
||||||
|
CarouselContent,
|
||||||
|
CarouselItem,
|
||||||
|
CarouselNext,
|
||||||
|
CarouselPrevious,
|
||||||
|
} from "@/components/ui/carousel";
|
||||||
|
import Autoplay from "embla-carousel-autoplay";
|
||||||
|
|
||||||
|
interface ImageCarouselProps {
|
||||||
|
images: { url: string; alt: string }[];
|
||||||
|
delay?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImageCarousel: React.FC<ImageCarouselProps> = ({ images, delay = 8000 }) => {
|
||||||
|
return (
|
||||||
|
<div className="relative">
|
||||||
|
<Carousel
|
||||||
|
plugins={[
|
||||||
|
Autoplay({
|
||||||
|
delay,
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<CarouselContent>
|
||||||
|
{images.map((image, index) => (
|
||||||
|
<CarouselItem key={index}>
|
||||||
|
<img src={image.url} alt={image.alt} className="rounded-lg shadow-md" />
|
||||||
|
</CarouselItem>
|
||||||
|
))}
|
||||||
|
</CarouselContent>
|
||||||
|
<CarouselPrevious />
|
||||||
|
<CarouselNext />
|
||||||
|
</Carousel>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ImageCarousel;
|
||||||
Reference in New Issue
Block a user