Refactor and update project components for Next.js 15 compatibility (#6)
* refactor: update all modules to latest version * refactor: update layout to match nextjs 15 convention * fix: implement TopProgressBar as Client * fix: remove unnecessary locale prop * fix: update postcss.config.js export * fix: comment out styles * fix: ref usage in useIntersectionObserver and update Navbar * chore: performance optimizations * fix: simplify Loading component structure * refactor: move ProjectCard component and add prop types validation
This commit is contained in:
parent
481bc8324e
commit
7f0c142dda
20 changed files with 3422 additions and 2569 deletions
|
|
@ -1,11 +1,7 @@
|
||||||
export default function Loading() {
|
export default function Loading() {
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
(
|
|
||||||
<div className="fixed top-0 left-0 flex justify-center items-center h-screen w-screen bg-gray-300 z-[999]">
|
<div className="fixed top-0 left-0 flex justify-center items-center h-screen w-screen bg-gray-300 z-[999]">
|
||||||
<h1 className="text-4xl text-gray-900">Loading...</h1>
|
<h1 className="text-4xl text-gray-900">Loading...</h1>
|
||||||
</div>
|
</div>
|
||||||
)
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,6 @@ const MyPage = () => {
|
||||||
href={"/docs/cv.pdf"}
|
href={"/docs/cv.pdf"}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
locale={false}
|
|
||||||
download>
|
download>
|
||||||
Download CV
|
Download CV
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,15 @@ export function useIntersectionObserver() {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (ref.current) {
|
const currentRef = ref.current; // Store the ref value in a variable
|
||||||
observer.observe(ref.current);
|
|
||||||
|
if (currentRef) {
|
||||||
|
observer.observe(currentRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (ref.current) {
|
if (currentRef) {
|
||||||
observer.unobserve(ref.current);
|
observer.unobserve(currentRef);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import Footer from "@/components/Footer";
|
import Footer from "@/components/Footer";
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: "Alvalens | About",
|
title: "Alvalens | About"
|
||||||
};
|
};
|
||||||
export default function Layout({ children }) {
|
export default function Layout({ children }) {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,7 @@
|
||||||
export default function Loading() {
|
export default function Loading() {
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
(
|
|
||||||
<div className="fixed top-0 left-0 flex justify-center items-center h-screen w-screen bg-gray-300 z-[999]">
|
<div className="fixed top-0 left-0 flex justify-center items-center h-screen w-screen bg-gray-300 z-[999]">
|
||||||
<h1 className="text-4xl text-gray-900">Loading...</h1>
|
<h1 className="text-4xl text-gray-900">Loading...</h1>
|
||||||
</div>
|
</div>
|
||||||
)
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,7 @@
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;500;600;700&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;500;600;700&display=swap');
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,300;0,400;0,500;0,600;0,700;1,400&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,300;0,400;0,500;0,600;0,700;1,400&display=swap');
|
||||||
|
|
||||||
@tailwind base;
|
@import "tailwindcss";
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
|
||||||
|
|
||||||
html {
|
html {
|
||||||
font-size: 17px !important;
|
font-size: 17px !important;
|
||||||
|
|
@ -53,9 +51,9 @@ h1, h2, h3, h4, h5, h6 {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
h2, h3, h4, h5, h6 {
|
/* h2, h3, h4, h5, h6 {
|
||||||
@apply text-gray-600;
|
@apply text-gray-600;
|
||||||
}
|
} */
|
||||||
|
|
||||||
span {
|
span {
|
||||||
font-family: "Jost", sans-serif;
|
font-family: "Jost", sans-serif;
|
||||||
|
|
@ -64,6 +62,7 @@ span {
|
||||||
li {
|
li {
|
||||||
@apply list-none relative;
|
@apply list-none relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
li.active:after {
|
li.active:after {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import dynamic from "next/dynamic";
|
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
import Navbar from "@/components/Navbar";
|
import Navbar from "@/components/Navbar";
|
||||||
import { config } from "@fortawesome/fontawesome-svg-core";
|
import { config } from "@fortawesome/fontawesome-svg-core";
|
||||||
|
|
@ -6,14 +5,19 @@ import "@fortawesome/fontawesome-svg-core/styles.css";
|
||||||
config.autoAddCss = false;
|
config.autoAddCss = false;
|
||||||
import "./nprogress.css";
|
import "./nprogress.css";
|
||||||
import { Analytics } from "@vercel/analytics/react";
|
import { Analytics } from "@vercel/analytics/react";
|
||||||
|
import Chat from "@/components/Chat";
|
||||||
|
import ClientTopProgressBar from "@/components/ClientTopProgressBar";
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: "Alvalens | Portofolio",
|
title: "Alvalens | Portofolio",
|
||||||
|
|
||||||
description:
|
description:
|
||||||
"My name is Alvalens, I'm a web developer and I'm passionate about it. I'm currently studying at Universitas Negeri Malang.",
|
"My name is Alvalens, I'm a web developer and I'm passionate about it. I'm currently studying at Universitas Negeri Malang.",
|
||||||
|
|
||||||
author: "Alvalen Shafelbilyunazra",
|
author: "Alvalen Shafelbilyunazra",
|
||||||
siteUrl: "https://www.alvalens.my.id",
|
siteUrl: "https://www.alvalens.my.id",
|
||||||
applicationName: "Alvalens",
|
applicationName: "Alvalens",
|
||||||
|
|
||||||
keywords: [
|
keywords: [
|
||||||
"alvalens",
|
"alvalens",
|
||||||
"alvalen",
|
"alvalen",
|
||||||
|
|
@ -25,6 +29,7 @@ export const metadata = {
|
||||||
"alvalen porto",
|
"alvalen porto",
|
||||||
"alvalen um",
|
"alvalen um",
|
||||||
],
|
],
|
||||||
|
|
||||||
openGraph: {
|
openGraph: {
|
||||||
type: "website",
|
type: "website",
|
||||||
url: "https://www.alvalens.my.id",
|
url: "https://www.alvalens.my.id",
|
||||||
|
|
@ -40,23 +45,14 @@ export const metadata = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
site_name: "Alvalens | Portofolio",
|
site_name: "Alvalens | Portofolio",
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
import Chat from "@/components/Chat";
|
|
||||||
|
|
||||||
const TopProgressBar = dynamic(() => import("@/components/TopProgressbar"), {
|
|
||||||
ssr: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default function RootLayout({ children }) {
|
export default function RootLayout({ children }) {
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<body>
|
<body>
|
||||||
<TopProgressBar />
|
<ClientTopProgressBar />
|
||||||
<Navbar />
|
<Navbar />
|
||||||
{children}
|
{children}
|
||||||
<Chat />
|
<Chat />
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { motion } from "framer-motion";
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
export default function projectCard({project, index, activeCategory}){
|
import { motion } from "framer-motion";
|
||||||
console.log(project.category[0], activeCategory);
|
import PropTypes from "prop-types";
|
||||||
console.log(project.category.includes(activeCategory));
|
|
||||||
|
export default function ProjectCard({ project, index, activeCategory }) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{project.category.includes(parseInt(activeCategory)) && (
|
{project.category.includes(parseInt(activeCategory)) && (
|
||||||
|
|
@ -56,3 +56,10 @@ export default function projectCard({project, index, activeCategory}){
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProjectCard.propTypes = {
|
||||||
|
project: PropTypes.object.isRequired,
|
||||||
|
index: PropTypes.number.isRequired,
|
||||||
|
activeCategory: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
|
||||||
|
.isRequired,
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
"use client";
|
"use client";
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect, use } from "react";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import jsonData from "@/json/data.json";
|
import jsonData from "@/json/data.json";
|
||||||
|
|
||||||
|
|
@ -53,7 +53,8 @@ function ScrollDownButton() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function Page({ params }) {
|
function Page(props) {
|
||||||
|
const params = use(props.params);
|
||||||
const [data, setData] = useState(null);
|
const [data, setData] = useState(null);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const selectedData = jsonData.Projects.find(
|
const selectedData = jsonData.Projects.find(
|
||||||
|
|
|
||||||
65
app/projects/components/ProjectCard.jsx
Normal file
65
app/projects/components/ProjectCard.jsx
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import Link from "next/link";
|
||||||
|
import Image from "next/image";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import PropTypes from "prop-types";
|
||||||
|
|
||||||
|
export default function ProjectCard({ project, index, activeCategory }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{project.category.includes(parseInt(activeCategory)) && (
|
||||||
|
<Link href={"projects/" + project.slug} key={index}>
|
||||||
|
<motion.div
|
||||||
|
className="z-10 relative flex justify-center items-start flex-col mb-5 md:px-10 w-full h-auto bg-gray-400 group/tes py-20 px-5 md:py-2 aspect-video "
|
||||||
|
initial={{
|
||||||
|
opacity: 0,
|
||||||
|
x: -200,
|
||||||
|
}}
|
||||||
|
whileInView={{
|
||||||
|
opacity: 1,
|
||||||
|
x: 0,
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
type: "spring",
|
||||||
|
}}>
|
||||||
|
<Image
|
||||||
|
src={project.thumbnail}
|
||||||
|
alt="Alvalens"
|
||||||
|
layout="fill"
|
||||||
|
objectFit="cover"
|
||||||
|
placeholder="blur"
|
||||||
|
className="bg-slate-950 opacity-10 group-hover/tes:opacity-100 transition-all ease duration-500"
|
||||||
|
blurDataURL={project.thumbnail}
|
||||||
|
/>
|
||||||
|
<div className="absolute top-0 left-0 bg-gray-600 px-4 py-2">
|
||||||
|
<h4 className="text-white">{project.year}</h4>
|
||||||
|
</div>
|
||||||
|
<div className="transition-all ease duration-500 opacity-100 content text-center group-hover/tes:opacity-0 z-10">
|
||||||
|
<h1 className="text-3xl font-bold mb-3">{project.title}</h1>
|
||||||
|
<p>
|
||||||
|
{project.desc[0].length > 125
|
||||||
|
? `${project.desc[0].slice(0, 125)}...`
|
||||||
|
: project.desc[0]}
|
||||||
|
</p>
|
||||||
|
<div className="flex justify-center items-center flex-row mt-5 flex-wrap">
|
||||||
|
{project.tech.map((t, index) => (
|
||||||
|
<span
|
||||||
|
key={index}
|
||||||
|
className="m-1 px-4 py-2 bg-gray-600 text-white ">
|
||||||
|
{t}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProjectCard.propTypes = {
|
||||||
|
project: PropTypes.object.isRequired,
|
||||||
|
index: PropTypes.number.isRequired,
|
||||||
|
activeCategory: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
|
||||||
|
.isRequired,
|
||||||
|
};
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import Footer from "@/components/Footer";
|
import Footer from "@/components/Footer";
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: "Alvalens | Projects",
|
title: "Alvalens | Projects"
|
||||||
};
|
};
|
||||||
export default function Layout({ children }) {
|
export default function Layout({ children }) {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,7 @@
|
||||||
export default function Loading() {
|
export default function Loading() {
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
(
|
|
||||||
<div className="fixed top-0 left-0 flex justify-center items-center h-screen w-screen bg-gray-300 z-[999]">
|
<div className="fixed top-0 left-0 flex justify-center items-center h-screen w-screen bg-gray-300 z-[999]">
|
||||||
<h1 className="text-4xl text-gray-900">Loading...</h1>
|
<h1 className="text-4xl text-gray-900">Loading...</h1>
|
||||||
</div>
|
</div>
|
||||||
)
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import ReactChat3 from "@/public/image/projects/web/trivia/trivia3.png";
|
||||||
import ProjectAll from "@/public/image/projects.png";
|
import ProjectAll from "@/public/image/projects.png";
|
||||||
|
|
||||||
import Hr from "@/components/Hr";
|
import Hr from "@/components/Hr";
|
||||||
import ProjectCard from "./(project-card)";
|
import ProjectCard from "./components/ProjectCard";
|
||||||
import Projects from "@/json/data.json";
|
import Projects from "@/json/data.json";
|
||||||
import FixedButon from "@/components/FixedButton";
|
import FixedButon from "@/components/FixedButton";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
@ -22,12 +22,11 @@ const category = {
|
||||||
1: "Web Development",
|
1: "Web Development",
|
||||||
2: "AI & Machine Learning",
|
2: "AI & Machine Learning",
|
||||||
9: "Other",
|
9: "Other",
|
||||||
}
|
};
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const [activeCategory, setActiveCategory] = useState(1);
|
const [activeCategory, setActiveCategory] = useState(1);
|
||||||
const projects = Projects.Projects.filter ((item) => item.show === true
|
const projects = Projects.Projects.filter((item) => item.show === true);
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
|
|
@ -36,10 +35,7 @@ export default function Page() {
|
||||||
<>
|
<>
|
||||||
<main className="overflow-hidden">
|
<main className="overflow-hidden">
|
||||||
<FixedButon href="/#projects">
|
<FixedButon href="/#projects">
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon icon={faChevronLeft} className="text-black pr-10" />
|
||||||
icon={faChevronLeft}
|
|
||||||
className="text-black pr-10"
|
|
||||||
/>
|
|
||||||
</FixedButon>
|
</FixedButon>
|
||||||
<div className="relative h-screen w-screen gap-4 p-10 flex justify-center items-center flex-col mb-10 overflow-hidden">
|
<div className="relative h-screen w-screen gap-4 p-10 flex justify-center items-center flex-col mb-10 overflow-hidden">
|
||||||
<div className="z-0 mb-48 md:mb-0 md:absolute top-1/4 md:right-[10%] md:-translate-y-16 ">
|
<div className="z-0 mb-48 md:mb-0 md:absolute top-1/4 md:right-[10%] md:-translate-y-16 ">
|
||||||
|
|
@ -181,15 +177,13 @@ export default function Page() {
|
||||||
Generative Trivia
|
Generative Trivia
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-gray-600 text-justify title text-lg">
|
<p className="text-gray-600 text-justify title text-lg">
|
||||||
A generative trivia website built with Next.js,
|
A generative trivia website built with Next.js, Firebase,
|
||||||
Firebase, NextAuth, Google OAuth, and TypeScript.
|
NextAuth, Google OAuth, and TypeScript. Users can generate trivia
|
||||||
Users can generate trivia questions for predefined
|
questions for predefined school subjects and answer them with a
|
||||||
school subjects and answer them with a unique
|
unique interactive input style. The application supports user
|
||||||
interactive input style. The application supports
|
authentication with Google and stores data in Firestore. You can
|
||||||
user authentication with Google and stores data in
|
see the source code of this project on my github account or
|
||||||
Firestore. You can see the source code of this
|
preview the project by clicking the button below.
|
||||||
project on my github account or preview the project
|
|
||||||
by clicking the button below.
|
|
||||||
</p>{" "}
|
</p>{" "}
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<Button variation="primary">
|
<Button variation="primary">
|
||||||
|
|
|
||||||
11
components/ClientTopProgressBar.jsx
Normal file
11
components/ClientTopProgressBar.jsx
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
|
||||||
|
const TopProgressBar = dynamic(() => import("@/components/TopProgressbar"), {
|
||||||
|
ssr: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function ClientTopProgressBar() {
|
||||||
|
return <TopProgressBar />;
|
||||||
|
}
|
||||||
|
|
@ -111,7 +111,7 @@ const NavItems = ({ isNavOpen, setIsNavOpen }) => {
|
||||||
animate={isNavOpen ? "open" : "closed"}
|
animate={isNavOpen ? "open" : "closed"}
|
||||||
initial={false}>
|
initial={false}>
|
||||||
<div className="relative backdrop-blur-sm opacity-95 flex flex-col items-center space-x-8 min-h-[100vh] bg-gray-700 min-w-[100vw] ">
|
<div className="relative backdrop-blur-sm opacity-95 flex flex-col items-center space-x-8 min-h-[100vh] bg-gray-700 min-w-[100vw] ">
|
||||||
<div className="flex flex-col items-center space-y-8 my-auto mx-0">
|
<div className="flex flex-col items-center space-y-8 my-auto mx-0 z-50">
|
||||||
{/* title */}
|
{/* title */}
|
||||||
<motion.h1
|
<motion.h1
|
||||||
variants={itemVariants}
|
variants={itemVariants}
|
||||||
|
|
@ -119,7 +119,7 @@ const NavItems = ({ isNavOpen, setIsNavOpen }) => {
|
||||||
className="text-6xl font-bold text-white ">
|
className="text-6xl font-bold text-white ">
|
||||||
Menu
|
Menu
|
||||||
</motion.h1>
|
</motion.h1>
|
||||||
<a href="/#home">
|
<Link href="/#home">
|
||||||
<div
|
<div
|
||||||
className="text-2xl font-bold text-white"
|
className="text-2xl font-bold text-white"
|
||||||
onClick={handleItemClick}>
|
onClick={handleItemClick}>
|
||||||
|
|
@ -131,7 +131,7 @@ const NavItems = ({ isNavOpen, setIsNavOpen }) => {
|
||||||
Home
|
Home
|
||||||
</motion.h2>
|
</motion.h2>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</Link>
|
||||||
<Link href="/about">
|
<Link href="/about">
|
||||||
<div
|
<div
|
||||||
onClick={handleItemClick}
|
onClick={handleItemClick}
|
||||||
|
|
@ -158,7 +158,7 @@ const NavItems = ({ isNavOpen, setIsNavOpen }) => {
|
||||||
</motion.h2>
|
</motion.h2>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
<a href="/#contact">
|
<Link href="/#contact">
|
||||||
<div
|
<div
|
||||||
onClick={handleItemClick}
|
onClick={handleItemClick}
|
||||||
className="text-2xl font-bold text-white">
|
className="text-2xl font-bold text-white">
|
||||||
|
|
@ -170,7 +170,7 @@ const NavItems = ({ isNavOpen, setIsNavOpen }) => {
|
||||||
Contact
|
Contact
|
||||||
</motion.h2>
|
</motion.h2>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
@ -209,15 +209,11 @@ const Navbar = () => {
|
||||||
onClick={toggleNav}>
|
onClick={toggleNav}>
|
||||||
<div
|
<div
|
||||||
className={`w-10 h-1 bg-black rounded-full transition-all ease duration-300 ${
|
className={`w-10 h-1 bg-black rounded-full transition-all ease duration-300 ${
|
||||||
isNavOpen
|
isNavOpen ? "rotate-45 bg-white translate-y-[2px]" : ""
|
||||||
? "rotate-45 bg-white translate-y-[2px]"
|
|
||||||
: ""
|
|
||||||
}`}></div>
|
}`}></div>
|
||||||
<div
|
<div
|
||||||
className={`w-10 h-1 bg-black rounded-full transition-all ease duration-300 ${
|
className={`w-10 h-1 bg-black rounded-full transition-all ease duration-300 ${
|
||||||
isNavOpen
|
isNavOpen ? "-rotate-45 -translate-y-2 bg-white" : ""
|
||||||
? "-rotate-45 -translate-y-2 bg-white"
|
|
||||||
: ""
|
|
||||||
}`}></div>
|
}`}></div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@ module.exports = withBundleAnalyzer({
|
||||||
// your Next.js configuration
|
// your Next.js configuration
|
||||||
images: {
|
images: {
|
||||||
domains: ["i.scdn.co"],
|
domains: ["i.scdn.co"],
|
||||||
|
// Add image optimization settings
|
||||||
|
formats: ['image/avif', 'image/webp'],
|
||||||
|
minimumCacheTTL: 60,
|
||||||
},
|
},
|
||||||
webpack: (config, options) => {
|
webpack: (config, options) => {
|
||||||
config.module.rules.push({
|
config.module.rules.push({
|
||||||
|
|
@ -23,8 +26,37 @@ module.exports = withBundleAnalyzer({
|
||||||
key: "Content-Type",
|
key: "Content-Type",
|
||||||
value: "application/gzip",
|
value: "application/gzip",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "Cache-Control",
|
||||||
|
value: "public, max-age=3600", // Cache for 1 hour
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: '/:path*',
|
||||||
|
headers: [
|
||||||
|
{
|
||||||
|
key: 'X-Content-Type-Options',
|
||||||
|
value: 'nosniff',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'X-Frame-Options',
|
||||||
|
value: 'DENY',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'X-XSS-Protection',
|
||||||
|
value: '1; mode=block',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
|
// Add performance optimizations
|
||||||
|
swcMinify: true,
|
||||||
|
reactStrictMode: true,
|
||||||
|
compiler: {
|
||||||
|
removeConsole: process.env.NODE_ENV === 'production' ? {
|
||||||
|
exclude: ['error', 'warn'],
|
||||||
|
} : false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
43
package.json
43
package.json
|
|
@ -3,36 +3,37 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --turbo",
|
"dev": "next dev --turbopack",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"generate-sitemap": "node generate-sitemap.js"
|
"generate-sitemap": "node generate-sitemap.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-svg-core": "^6.4.2",
|
"@fortawesome/fontawesome-svg-core": "^6.7.2",
|
||||||
"@fortawesome/free-brands-svg-icons": "^6.4.2",
|
"@fortawesome/free-brands-svg-icons": "^6.7.2",
|
||||||
"@fortawesome/free-regular-svg-icons": "^6.4.2",
|
"@fortawesome/free-regular-svg-icons": "^6.7.2",
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
||||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||||
"@fullpage/react-fullpage": "^0.1.40",
|
"@fullpage/react-fullpage": "^0.1.48",
|
||||||
"@vercel/analytics": "^1.0.2",
|
"@tailwindcss/postcss": "^4.0.14",
|
||||||
"autoprefixer": "10.4.15",
|
"@vercel/analytics": "^1.5.0",
|
||||||
"eslint": "8.48.0",
|
"eslint": "9.22.0",
|
||||||
"eslint-config-next": "13.4.19",
|
"eslint-config-next": "15.2.3",
|
||||||
"framer-motion": "^10.16.2",
|
"framer-motion": "^12.5.0",
|
||||||
"next": "13.4.19",
|
"next": "15.2.3",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"postcss": "8.4.28",
|
"postcss": "^8.5.3",
|
||||||
"react": "18.2.0",
|
"prop-types": "^15.8.1",
|
||||||
"react-dom": "18.2.0",
|
"react": "19.0.0",
|
||||||
"sharp": "^0.32.5",
|
"react-dom": "19.0.0",
|
||||||
"sitemap": "^7.1.1",
|
"sharp": "^0.33.5",
|
||||||
"swr": "^2.2.2",
|
"sitemap": "^8.0.0",
|
||||||
"tailwindcss": "3.3.3"
|
"swr": "^2.3.3",
|
||||||
|
"tailwindcss": "^4.0.14"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@next/bundle-analyzer": "^13.4.19",
|
"@next/bundle-analyzer": "15.2.3",
|
||||||
"cross-env": "^7.0.3"
|
"cross-env": "^7.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
5589
pnpm-lock.yaml
generated
5589
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,5 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: {
|
plugins: {
|
||||||
tailwindcss: {},
|
"@tailwindcss/postcss": {},
|
||||||
autoprefixer: {},
|
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,18 @@
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
important: true,
|
|
||||||
content: [
|
content: [
|
||||||
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
|
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
'./components/**/*.{js,ts,jsx,tsx,mdx}',
|
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
'./app/**/*.{js,ts,jsx,tsx,mdx}',
|
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
],
|
],
|
||||||
theme: {
|
theme: {
|
||||||
|
extend: {
|
||||||
|
},
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: [],
|
||||||
}
|
// Add this to reduce CSS file size in production
|
||||||
|
future: {
|
||||||
|
removeDeprecatedGapUtilities: true,
|
||||||
|
purgeLayersByDefault: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue