Add new features: product favorites, hero slider, and product improvements
This commit is contained in:
parent
9f0d3aba35
commit
01e10367ba
43
components/CookieNotification.tsx
Normal file
43
components/CookieNotification.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
|
||||
export default function CookieNotification() {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
// Проверяем, было ли уже показано уведомление
|
||||
useEffect(() => {
|
||||
const cookieAccepted = localStorage.getItem('cookieAccepted');
|
||||
if (!cookieAccepted) {
|
||||
// Показываем уведомление с небольшой задержкой
|
||||
const timer = setTimeout(() => {
|
||||
setIsVisible(true);
|
||||
}, 2000);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const acceptCookies = () => {
|
||||
localStorage.setItem('cookieAccepted', 'true');
|
||||
setIsVisible(false);
|
||||
};
|
||||
|
||||
if (!isVisible) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed bottom-4 right-4 bg-white p-4 shadow-lg rounded-lg max-w-sm z-50 animate-fade-in">
|
||||
<h3 className="font-bold mb-2">Уведомление о Cookies</h3>
|
||||
<p className="text-sm mb-4">
|
||||
Наш сайт использует файлы cookie. Продолжая пользоваться сайтом, вы соглашаетесь на использование наших файлов
|
||||
cookie.
|
||||
</p>
|
||||
<button
|
||||
onClick={acceptCookies}
|
||||
className="bg-black text-white px-4 py-2 rounded hover:bg-gray-800 transition-colors"
|
||||
>
|
||||
Хорошо, спасибо
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
75
components/Header.tsx
Normal file
75
components/Header.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
import Link from "next/link";
|
||||
import { Search, Heart, User, ShoppingCart } from "lucide-react";
|
||||
import { useState, useEffect } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import Image from "next/image";
|
||||
|
||||
export default function Header() {
|
||||
// Состояние для отслеживания прокрутки страницы
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
|
||||
// Эффект для отслеживания прокрутки
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
const isScrolled = window.scrollY > 50;
|
||||
if (isScrolled !== scrolled) {
|
||||
setScrolled(isScrolled);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', handleScroll);
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handleScroll);
|
||||
};
|
||||
}, [scrolled]);
|
||||
|
||||
return (
|
||||
<header className={`fixed w-full z-50 transition-all duration-300 ${scrolled ? 'bg-white shadow-sm' : 'bg-transparent'}`}>
|
||||
<nav className={`py-4 transition-all duration-300 ${scrolled ? 'text-black' : 'text-white'}`}>
|
||||
<div className="container mx-auto px-4 flex items-center justify-between">
|
||||
<div className="flex items-center space-x-6">
|
||||
<Link href="/catalog" className="text-sm font-medium hover:opacity-70 transition-opacity">
|
||||
Каталог
|
||||
</Link>
|
||||
<Link href="/new" className="text-sm font-medium hover:opacity-70 transition-opacity">
|
||||
Новинки
|
||||
</Link>
|
||||
<Link href="/stores" className="text-sm font-medium hover:opacity-70 transition-opacity">
|
||||
Магазины
|
||||
</Link>
|
||||
</div>
|
||||
<Link href="/" className="text-2xl font-bold">
|
||||
<div className="relative h-10 w-32">
|
||||
<Image
|
||||
src="/logo.png"
|
||||
alt="Brand Logo"
|
||||
layout="fill"
|
||||
objectFit="contain"
|
||||
/>
|
||||
</div>
|
||||
</Link>
|
||||
<div className="flex items-center space-x-5">
|
||||
<button className="hover:opacity-70 transition-opacity">
|
||||
<Search className="w-5 h-5" />
|
||||
</button>
|
||||
<Link href="/favorites" className="relative hover:opacity-70 transition-opacity">
|
||||
<Heart className="w-5 h-5" />
|
||||
<span className={`absolute -top-2 -right-2 ${scrolled ? 'bg-black text-white' : 'bg-white text-black'} text-xs rounded-full w-4 h-4 flex items-center justify-center`}>
|
||||
0
|
||||
</span>
|
||||
</Link>
|
||||
<Link href="/account" className="hover:opacity-70 transition-opacity">
|
||||
<User className="w-5 h-5" />
|
||||
</Link>
|
||||
<Link href="/cart" className="relative hover:opacity-70 transition-opacity">
|
||||
<ShoppingCart className="w-5 h-5" />
|
||||
<span className={`absolute -top-2 -right-2 ${scrolled ? 'bg-black text-white' : 'bg-white text-black'} text-xs rounded-full w-4 h-4 flex items-center justify-center`}>
|
||||
0
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
135
components/Hero.tsx
Normal file
135
components/Hero.tsx
Normal file
@ -0,0 +1,135 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import Image from "next/image";
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
|
||||
// Типы для свойств компонента
|
||||
interface HeroProps {
|
||||
images?: string[];
|
||||
}
|
||||
|
||||
export default function Hero({ images = [] }: HeroProps) {
|
||||
// Состояние для текущего индекса слайда
|
||||
const [currentIndex, setCurrentIndex] = useState(0);
|
||||
// Состояние для автоматического воспроизведения
|
||||
const [autoplay, setAutoplay] = useState(true);
|
||||
// Состояние для направления анимации: 1 - следующий слайд, -1 - предыдущий слайд
|
||||
const [direction, setDirection] = useState(0);
|
||||
|
||||
// Если изображения не переданы или массив пуст, используем стандартное изображение
|
||||
const heroImages = images.length > 0
|
||||
? images
|
||||
: ['/photos/head_photo.png'];
|
||||
|
||||
// Функция для перехода к следующему слайду
|
||||
const nextSlide = () => {
|
||||
setDirection(1);
|
||||
setCurrentIndex((prevIndex) =>
|
||||
prevIndex === heroImages.length - 1 ? 0 : prevIndex + 1
|
||||
);
|
||||
};
|
||||
|
||||
// Функция для перехода к предыдущему слайду
|
||||
const prevSlide = () => {
|
||||
setDirection(-1);
|
||||
setCurrentIndex((prevIndex) =>
|
||||
prevIndex === 0 ? heroImages.length - 1 : prevIndex - 1
|
||||
);
|
||||
};
|
||||
|
||||
// Эффект для автоматического воспроизведения слайдера
|
||||
useEffect(() => {
|
||||
let interval: NodeJS.Timeout;
|
||||
|
||||
if (autoplay) {
|
||||
interval = setInterval(() => {
|
||||
nextSlide();
|
||||
}, 5000); // Смена слайда каждые 5 секунд
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (interval) clearInterval(interval);
|
||||
};
|
||||
}, [autoplay, currentIndex]);
|
||||
|
||||
// Определяем варианты анимации для эффекта "скольжения"
|
||||
const variants = {
|
||||
enter: (direction: number) => ({
|
||||
x: direction > 0 ? "100%" : "-100%",
|
||||
opacity: 0
|
||||
}),
|
||||
center: {
|
||||
x: 0,
|
||||
opacity: 1
|
||||
},
|
||||
exit: (direction: number) => ({
|
||||
x: direction > 0 ? "-100%" : "100%",
|
||||
opacity: 0
|
||||
}),
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative h-screen overflow-hidden">
|
||||
<AnimatePresence custom={direction} mode="wait">
|
||||
<motion.div
|
||||
key={currentIndex}
|
||||
custom={direction}
|
||||
variants={variants}
|
||||
initial="enter"
|
||||
animate="center"
|
||||
exit="exit"
|
||||
transition={{ duration: 0.7 }}
|
||||
className="absolute inset-0"
|
||||
>
|
||||
<Image
|
||||
src={heroImages[currentIndex]}
|
||||
alt="New Arrivals"
|
||||
layout="fill"
|
||||
objectFit="cover"
|
||||
quality={100}
|
||||
priority
|
||||
/>
|
||||
{/* Убрали затемнение изображения */}
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
|
||||
<button
|
||||
className="absolute left-4 top-1/2 transform -translate-y-1/2 bg-white bg-opacity-50 p-2 rounded-full hover:bg-opacity-70 transition-all"
|
||||
onClick={() => {
|
||||
prevSlide();
|
||||
setAutoplay(false);
|
||||
}}
|
||||
>
|
||||
<ChevronLeft className="w-6 h-6 text-black" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="absolute right-4 top-1/2 transform -translate-y-1/2 bg-white bg-opacity-50 p-2 rounded-full hover:bg-opacity-70 transition-all"
|
||||
onClick={() => {
|
||||
nextSlide();
|
||||
setAutoplay(false);
|
||||
}}
|
||||
>
|
||||
<ChevronRight className="w-6 h-6 text-black" />
|
||||
</button>
|
||||
|
||||
{/* Индикаторы слайдов */}
|
||||
<div className="absolute bottom-8 left-0 right-0 flex justify-center space-x-2">
|
||||
{heroImages.map((_, index) => (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => {
|
||||
// Определяем направление в зависимости от выбранного индекса
|
||||
setDirection(index > currentIndex ? 1 : -1);
|
||||
setCurrentIndex(index);
|
||||
setAutoplay(false);
|
||||
}}
|
||||
className={`w-3 h-3 rounded-full ${
|
||||
index === currentIndex ? 'bg-white' : 'bg-white/50'
|
||||
}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
82
package-lock.json
generated
82
package-lock.json
generated
@ -9,7 +9,9 @@
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@heroicons/react": "^2.0.18",
|
||||
"@tailwindcss/line-clamp": "^0.4.4",
|
||||
"framer-motion": "^10.16.4",
|
||||
"lucide-react": "^0.476.0",
|
||||
"next": "^13.4.19",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
@ -203,7 +205,6 @@
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nodelib/fs.stat": "2.0.5",
|
||||
@ -217,7 +218,6 @@
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
|
||||
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
@ -227,7 +227,6 @@
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
|
||||
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nodelib/fs.scandir": "2.1.5",
|
||||
@ -246,6 +245,15 @@
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/line-clamp": {
|
||||
"version": "0.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/line-clamp/-/line-clamp-0.4.4.tgz",
|
||||
"integrity": "sha512-5U6SY5z8N42VtrCrKlsTAA35gy2VSyYtHWCsg1H87NU1SXnEfekTVlrga9fzUDrrHcGi2Lb5KenUWb4lRQT5/g==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"tailwindcss": ">=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.17.19",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.19.tgz",
|
||||
@ -278,7 +286,6 @@
|
||||
"version": "7.4.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
|
||||
"integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
@ -291,7 +298,6 @@
|
||||
"version": "1.8.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz",
|
||||
"integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"acorn": "^7.0.0",
|
||||
@ -303,7 +309,6 @@
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz",
|
||||
"integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
@ -313,7 +318,6 @@
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"normalize-path": "^3.0.0",
|
||||
@ -327,7 +331,6 @@
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
||||
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/autoprefixer": {
|
||||
@ -368,7 +371,6 @@
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@ -381,7 +383,6 @@
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fill-range": "^7.1.1"
|
||||
@ -438,7 +439,6 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
|
||||
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
@ -468,7 +468,6 @@
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"anymatch": "~3.1.2",
|
||||
@ -493,7 +492,6 @@
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
@ -512,14 +510,12 @@
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cssesc": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"cssesc": "bin/cssesc"
|
||||
@ -539,7 +535,6 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/defined/-/defined-1.0.1.tgz",
|
||||
"integrity": "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
@ -549,7 +544,6 @@
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/detective/-/detective-5.2.1.tgz",
|
||||
"integrity": "sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"acorn-node": "^1.8.2",
|
||||
@ -567,14 +561,12 @@
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
||||
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/dlv": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
|
||||
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
@ -598,7 +590,6 @@
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
||||
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nodelib/fs.stat": "^2.0.2",
|
||||
@ -615,7 +606,6 @@
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
@ -628,7 +618,6 @@
|
||||
"version": "1.19.0",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz",
|
||||
"integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"reusify": "^1.0.4"
|
||||
@ -638,7 +627,6 @@
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
@ -689,7 +677,6 @@
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
@ -704,7 +691,6 @@
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
@ -714,7 +700,6 @@
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
||||
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.3"
|
||||
@ -739,7 +724,6 @@
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
@ -752,7 +736,6 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"binary-extensions": "^2.0.0"
|
||||
@ -765,7 +748,6 @@
|
||||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
|
||||
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hasown": "^2.0.2"
|
||||
@ -781,7 +763,6 @@
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@ -791,7 +772,6 @@
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-extglob": "^2.1.1"
|
||||
@ -804,7 +784,6 @@
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
@ -820,7 +799,6 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
|
||||
"integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
@ -838,11 +816,19 @@
|
||||
"loose-envify": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/lucide-react": {
|
||||
"version": "0.476.0",
|
||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.476.0.tgz",
|
||||
"integrity": "sha512-x6cLTk8gahdUPje0hSgLN1/MgiJH+Xl90Xoxy9bkPAsMPOUiyRSKR4JCDPGVCEpyqnZXH3exFWNItcvra9WzUQ==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/merge2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
@ -852,7 +838,6 @@
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"braces": "^3.0.3",
|
||||
@ -866,7 +851,6 @@
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
@ -975,7 +959,6 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@ -995,7 +978,6 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
|
||||
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
@ -1005,7 +987,6 @@
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
@ -1018,7 +999,6 @@
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
@ -1031,7 +1011,6 @@
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@ -1041,7 +1020,6 @@
|
||||
"version": "8.4.20",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.20.tgz",
|
||||
"integrity": "sha512-6Q04AXR1212bXr5fh03u8aAwbLxAQNGQ/Q1LNa0VfOI06ZAlhPHtQvE4OIdpj4kLThXilalPnmDSOD65DcHt+g==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@ -1066,7 +1044,6 @@
|
||||
"version": "14.1.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz",
|
||||
"integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"postcss-value-parser": "^4.0.0",
|
||||
@ -1084,7 +1061,6 @@
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz",
|
||||
"integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lilconfig": "^2.0.5",
|
||||
@ -1114,7 +1090,6 @@
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz",
|
||||
"integrity": "sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"postcss-selector-parser": "^6.0.10"
|
||||
@ -1134,7 +1109,6 @@
|
||||
"version": "6.1.2",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
|
||||
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cssesc": "^3.0.0",
|
||||
@ -1148,14 +1122,12 @@
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/queue-microtask": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@ -1176,7 +1148,6 @@
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
|
||||
"integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
@ -1214,7 +1185,6 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pify": "^2.3.0"
|
||||
@ -1224,7 +1194,6 @@
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"picomatch": "^2.2.1"
|
||||
@ -1237,7 +1206,6 @@
|
||||
"version": "1.22.10",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
||||
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-core-module": "^2.16.0",
|
||||
@ -1258,7 +1226,6 @@
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
||||
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"iojs": ">=1.0.0",
|
||||
@ -1269,7 +1236,6 @@
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@ -1342,7 +1308,6 @@
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@ -1355,7 +1320,6 @@
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.2.4.tgz",
|
||||
"integrity": "sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"arg": "^5.0.2",
|
||||
@ -1397,7 +1361,6 @@
|
||||
"version": "8.5.3",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
|
||||
"integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@ -1426,7 +1389,6 @@
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
|
||||
"integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"camelcase-css": "^2.0.1"
|
||||
@ -1446,7 +1408,6 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
@ -1517,7 +1478,6 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/watchpack": {
|
||||
@ -1537,7 +1497,6 @@
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4"
|
||||
@ -1547,7 +1506,6 @@
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
||||
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
|
||||
@ -9,7 +9,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@heroicons/react": "^2.0.18",
|
||||
"@tailwindcss/line-clamp": "^0.4.4",
|
||||
"framer-motion": "^10.16.4",
|
||||
"lucide-react": "^0.476.0",
|
||||
"next": "^13.4.19",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
|
||||
252
pages/index.tsx
252
pages/index.tsx
@ -2,35 +2,34 @@ import { motion } from 'framer-motion';
|
||||
import Head from 'next/head';
|
||||
import Image from 'next/image';
|
||||
import { useState, useEffect } from 'react';
|
||||
import Header from '../components/Header';
|
||||
import Hero from '../components/Hero';
|
||||
import CookieNotification from '../components/CookieNotification';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { Heart } from 'lucide-react';
|
||||
|
||||
export default function Home() {
|
||||
// Данные о товарах
|
||||
const products = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Пальто',
|
||||
price: 15000,
|
||||
images: ['/wear/palto1.jpg', '/wear/palto2.jpg'],
|
||||
description: 'Элегантное пальто высокого качества'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Пиджак',
|
||||
price: 12000,
|
||||
images: ['/wear/pidzak1.jpg', '/wear/pidzak2.jpg'],
|
||||
description: 'Стильный пиджак для особых случаев'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Сорочка',
|
||||
price: 5000,
|
||||
images: ['/wear/sorochka1.jpg', '/wear/sorochka2.jpg'],
|
||||
description: 'Классическая сорочка из премиальных материалов'
|
||||
}
|
||||
];
|
||||
// Типы для свойств компонента
|
||||
interface HomeProps {
|
||||
heroImages: string[];
|
||||
products: Product[];
|
||||
}
|
||||
|
||||
// Тип для товара
|
||||
interface Product {
|
||||
id: number;
|
||||
name: string;
|
||||
price: number;
|
||||
images: string[];
|
||||
description: string;
|
||||
isNew?: boolean;
|
||||
}
|
||||
|
||||
export default function Home({ heroImages, products }: HomeProps) {
|
||||
// Состояние для отслеживания наведения на карточки товаров
|
||||
const [hoveredProduct, setHoveredProduct] = useState<number | null>(null);
|
||||
// Состояние для отслеживания избранных товаров
|
||||
const [favorites, setFavorites] = useState<number[]>([]);
|
||||
|
||||
// Состояние для отслеживания прокрутки страницы
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
@ -50,109 +49,90 @@ export default function Home() {
|
||||
};
|
||||
}, [scrolled]);
|
||||
|
||||
// Функция для добавления/удаления товара из избранного
|
||||
const toggleFavorite = (id: number, e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
setFavorites(prev =>
|
||||
prev.includes(id)
|
||||
? prev.filter(itemId => itemId !== id)
|
||||
: [...prev, id]
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-white font-['Arimo']">
|
||||
<Head>
|
||||
<title>Brand Store | Элегантная мода</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Arimo:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
||||
</Head>
|
||||
|
||||
<header className={`fixed w-full z-50 transition-all duration-300 ${scrolled ? 'bg-white/80 backdrop-blur-md shadow-sm' : 'bg-transparent'}`}>
|
||||
<nav className="container mx-auto px-6 py-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
className="flex items-center"
|
||||
>
|
||||
<div className="relative h-12 w-12">
|
||||
<Image
|
||||
src="/logo.png"
|
||||
alt="Brand Logo"
|
||||
layout="fill"
|
||||
objectFit="contain"
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
<div className="hidden md:flex space-x-8">
|
||||
<a href="#" className={`transition-colors ${scrolled ? 'text-primary hover:text-accent' : 'text-white hover:text-gray-200'}`}>Коллекция</a>
|
||||
<a href="#" className={`transition-colors ${scrolled ? 'text-primary hover:text-accent' : 'text-white hover:text-gray-200'}`}>О нас</a>
|
||||
<a href="#" className={`transition-colors ${scrolled ? 'text-primary hover:text-accent' : 'text-white hover:text-gray-200'}`}>Контакты</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<Header />
|
||||
|
||||
<main>
|
||||
{/* Секция с фоновым изображением */}
|
||||
<section className="relative h-screen">
|
||||
<div className="absolute inset-0 w-full h-full">
|
||||
<Image
|
||||
src="/photos/head_photo.png"
|
||||
alt="Главное фото"
|
||||
layout="fill"
|
||||
objectFit="cover"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
{/* Секция с HERO элементом */}
|
||||
<Hero images={heroImages} />
|
||||
|
||||
{/* Секция с приветственным текстом */}
|
||||
<section className="py-20 bg-white">
|
||||
{/* Секция с товарами */}
|
||||
<section className="py-16 bg-white">
|
||||
<div className="container mx-auto px-6">
|
||||
<motion.div
|
||||
<motion.h2
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.2 }}
|
||||
className="text-center max-w-3xl mx-auto"
|
||||
className="text-3xl text-center font-medium mb-12"
|
||||
>
|
||||
<h1 className="text-5xl md:text-6xl text-primary mb-6">
|
||||
Элегантность в каждой детали
|
||||
</h1>
|
||||
<p className="text-lg text-gray-600 mb-8">
|
||||
Откройте для себя новую коллекцию 2024
|
||||
</p>
|
||||
<button className="bg-primary text-white px-8 py-3 rounded-full hover:bg-accent transition-colors">
|
||||
Смотреть коллекцию
|
||||
</button>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Секция с товарами */}
|
||||
<section className="py-20 bg-gray-50">
|
||||
<div className="container mx-auto px-6">
|
||||
{/* <h2 className="text-3xl text-primary text-center mb-16">Наши товары</h2> */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-16">
|
||||
Новинки
|
||||
</motion.h2>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-x-6 gap-y-12">
|
||||
{products.map((product, index) => (
|
||||
<motion.div
|
||||
key={product.id}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: index * 0.2 }}
|
||||
className="group"
|
||||
transition={{ delay: index * 0.1 }}
|
||||
className="group relative"
|
||||
onMouseEnter={() => setHoveredProduct(product.id)}
|
||||
onMouseLeave={() => setHoveredProduct(null)}
|
||||
>
|
||||
<div className="relative h-[600px] w-full overflow-hidden mb-6">
|
||||
{/* Метка "New" */}
|
||||
{product.isNew && (
|
||||
<div className="absolute top-4 left-4 z-10 bg-gray-100 text-gray-700 text-xs px-3 py-1 rounded-full">
|
||||
New
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Кнопка избранного */}
|
||||
<button
|
||||
onClick={(e) => toggleFavorite(product.id, e)}
|
||||
className="absolute top-4 right-4 z-10 p-1"
|
||||
>
|
||||
<Heart
|
||||
className={`w-6 h-6 transition-colors ${
|
||||
favorites.includes(product.id)
|
||||
? 'fill-black text-black'
|
||||
: 'text-gray-400 hover:text-black'
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
|
||||
{/* Изображение товара */}
|
||||
<div className="relative aspect-[3/4] w-full overflow-hidden mb-4">
|
||||
<Image
|
||||
src={hoveredProduct === product.id ? product.images[1] : product.images[0]}
|
||||
src={hoveredProduct === product.id && product.images.length > 1 ? product.images[1] : product.images[0]}
|
||||
alt={product.name}
|
||||
layout="fill"
|
||||
objectFit="contain"
|
||||
className="transition-opacity duration-300"
|
||||
objectFit="cover"
|
||||
className="transition-all duration-500 hover:scale-105"
|
||||
/>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<h3 className="text-2xl text-primary mb-3">{product.name}</h3>
|
||||
<p className="text-gray-600 mb-5 text-lg">{product.description}</p>
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-xl font-semibold text-primary">{product.price.toLocaleString()} ₽</span>
|
||||
<button className="bg-primary text-white px-6 py-3 rounded-full hover:bg-accent transition-colors">
|
||||
Подробнее
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Информация о товаре */}
|
||||
<div className="px-1">
|
||||
<h3 className="text-sm text-gray-700 mb-2 line-clamp-2">{product.name}</h3>
|
||||
<p className="text-base font-medium">{product.price.toLocaleString()} ₽</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
@ -193,6 +173,80 @@ export default function Home() {
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<CookieNotification />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Функция для получения данных на стороне сервера
|
||||
export async function getStaticProps() {
|
||||
// Данные о товарах
|
||||
const products = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Пальто оверсайз',
|
||||
price: 43800,
|
||||
images: ['/wear/palto1.jpg', '/wear/palto2.jpg'],
|
||||
description: 'Элегантное пальто оверсайз высокого качества',
|
||||
isNew: true
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Костюм хлопок',
|
||||
price: 12800,
|
||||
images: ['/wear/pidzak1.jpg', '/wear/pidzak2.jpg'],
|
||||
description: 'Стильный костюм для особых случаев',
|
||||
isNew: true
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Блузка',
|
||||
price: 3500,
|
||||
images: ['/wear/sorochka1.jpg', '/wear/sorochka2.jpg'],
|
||||
description: 'Классическая блузка в коричневом цвете',
|
||||
isNew: true
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'Платье со сборкой',
|
||||
price: 28800,
|
||||
images: ['/wear/jumpsuit_1.jpg', '/wear/jumpsuit_2.jpg'],
|
||||
description: 'Элегантное платье высокого качества',
|
||||
isNew: true
|
||||
}
|
||||
];
|
||||
|
||||
// Получение изображений для слайдера из папки hero_photos
|
||||
const heroImagesDirectory = path.join(process.cwd(), 'public/hero_photos');
|
||||
let heroImages = [];
|
||||
|
||||
try {
|
||||
const fileNames = fs.readdirSync(heroImagesDirectory);
|
||||
// Фильтруем только изображения
|
||||
const imageExtensions = ['.jpg', '.jpeg', '.png', '.webp', '.gif'];
|
||||
const imageFiles = fileNames.filter(file =>
|
||||
imageExtensions.some(ext => file.toLowerCase().endsWith(ext))
|
||||
);
|
||||
|
||||
if (imageFiles.length > 0) {
|
||||
heroImages = imageFiles.map(fileName => `/hero_photos/${fileName}`);
|
||||
} else {
|
||||
// Если нет изображений, используем изображения из папки photos
|
||||
heroImages = ['/photos/head_photo.png'];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error reading hero_photos directory:', error);
|
||||
// Если папка не существует или пуста, используем изображение из папки photos
|
||||
heroImages = ['/photos/head_photo.png'];
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
heroImages,
|
||||
products,
|
||||
},
|
||||
// Перегенерация страницы каждые 10 минут
|
||||
revalidate: 600,
|
||||
};
|
||||
}
|
||||
BIN
public/hero_photos/hero1.png
Normal file
BIN
public/hero_photos/hero1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.0 MiB |
BIN
public/hero_photos/photo_main_main_1.png
Normal file
BIN
public/hero_photos/photo_main_main_1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 312 KiB |
BIN
public/hero_photos/photo_main_main_2.png
Normal file
BIN
public/hero_photos/photo_main_main_2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 230 KiB |
BIN
public/hero_photos/photo_main_main_3.png
Normal file
BIN
public/hero_photos/photo_main_main_3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 441 KiB |
BIN
public/wear/jumpsuit_1.jpg
Normal file
BIN
public/wear/jumpsuit_1.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
BIN
public/wear/jumpsuit_2.jpg
Normal file
BIN
public/wear/jumpsuit_2.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
@ -16,6 +16,15 @@ module.exports = {
|
||||
sans: ['Inter', 'sans-serif'],
|
||||
serif: ['Playfair Display', 'serif'],
|
||||
},
|
||||
animation: {
|
||||
'fade-in': 'fadeIn 0.5s ease-in-out',
|
||||
},
|
||||
keyframes: {
|
||||
fadeIn: {
|
||||
'0%': { opacity: 0 },
|
||||
'100%': { opacity: 1 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
|
||||
Loading…
Reference in New Issue
Block a user