diff --git a/package-lock.json b/package-lock.json
index 38d9679..14a66ae 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,10 @@
"version": "0.1.0",
"dependencies": {
"cookies-next": "^4.3.0",
+ "framer-motion": "^12.42.0",
+ "lucide-react": "^1.21.0",
"next": "16.2.9",
+ "next-themes": "^0.4.6",
"nextjs-toploader": "^3.7.15",
"react": "19.2.7",
"react-dom": "19.2.7",
@@ -37,6 +40,10 @@
"typescript": "^5",
"vitest": "^4.1.9",
"webpack-bundle-analyzer": "^4.10.2"
+ },
+ "optionalDependencies": {
+ "@next/swc-linux-x64-gnu": "16.2.9",
+ "@next/swc-linux-x64-musl": "16.2.9"
}
},
"node_modules/@adobe/css-tools": {
@@ -5260,6 +5267,33 @@
"node": ">= 6"
}
},
+ "node_modules/framer-motion": {
+ "version": "12.42.0",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.42.0.tgz",
+ "integrity": "sha512-wp7EJnfWaaEScVygKv3e20udoRz+LbtxScsuTkakAxfXmt+ReC6WyPW2nINRAGvd+hG9odwcjBLyOTPjH5pBRA==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-dom": "^12.42.0",
+ "motion-utils": "^12.39.0",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "@emotion/is-prop-valid": "*",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/is-prop-valid": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/fs-extra": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
@@ -7345,6 +7379,15 @@
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"license": "ISC"
},
+ "node_modules/lucide-react": {
+ "version": "1.21.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.21.0.tgz",
+ "integrity": "sha512-reEZMXq8Qdd5jg5XYkQ5TR1fB/GiQ7ih4vcrthYDtgjSDwh0i6/YLiGjsWsIwgN49gpAnd4J2elSNzncMEEUUQ==",
+ "license": "ISC",
+ "peerDependencies": {
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/lz-string": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
@@ -7500,6 +7543,21 @@
"node": ">=16 || 14 >=14.17"
}
},
+ "node_modules/motion-dom": {
+ "version": "12.42.0",
+ "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.42.0.tgz",
+ "integrity": "sha512-M63h4n8R+quJdNhBwuLlgxM+OLYa9+I/T2pzDRboB9fLXRdbou+Gw7Zury+SkpaCyACP1JHSjHgZ1EgTkBr30w==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-utils": "^12.39.0"
+ }
+ },
+ "node_modules/motion-utils": {
+ "version": "12.39.0",
+ "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.39.0.tgz",
+ "integrity": "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==",
+ "license": "MIT"
+ },
"node_modules/mrmime": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz",
@@ -7606,6 +7664,16 @@
}
}
},
+ "node_modules/next-themes": {
+ "version": "0.4.6",
+ "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz",
+ "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc"
+ }
+ },
"node_modules/next/node_modules/@img/sharp-darwin-arm64": {
"version": "0.34.5",
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
diff --git a/package.json b/package.json
index 7749911..5d70c64 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,10 @@
},
"dependencies": {
"cookies-next": "^4.3.0",
+ "framer-motion": "^12.42.0",
+ "lucide-react": "^1.21.0",
"next": "16.2.9",
+ "next-themes": "^0.4.6",
"nextjs-toploader": "^3.7.15",
"react": "19.2.7",
"react-dom": "19.2.7",
@@ -25,23 +28,23 @@
},
"devDependencies": {
"@next/bundle-analyzer": "16.2.9",
+ "@testing-library/jest-dom": "^6.9.1",
+ "@testing-library/react": "^16.3.2",
"@types/negotiator": "^0.6.3",
"@types/node": "^20",
"@types/react": "19.2.17",
"@types/react-dom": "19.2.3",
+ "@vitejs/plugin-react": "^6.0.3",
"cross-env": "^7.0.3",
"cypress": "^15.17.0",
"eslint": "^9",
- "jsdom": "^29.1.1",
- "@testing-library/jest-dom": "^6.9.1",
- "@testing-library/react": "^16.3.2",
- "@vitejs/plugin-react": "^6.0.3",
- "vitest": "^4.1.9",
"eslint-config-next": "16.2.9",
+ "jsdom": "^29.1.1",
"postcss": "^8",
"prettier": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.6",
"typescript": "^5",
+ "vitest": "^4.1.9",
"webpack-bundle-analyzer": "^4.10.2"
},
"optionalDependencies": {
diff --git a/src/app/(routes)/(website)/(home)/page.tsx b/src/app/(routes)/(website)/(home)/page.tsx
index 1618269..80c472e 100644
--- a/src/app/(routes)/(website)/(home)/page.tsx
+++ b/src/app/(routes)/(website)/(home)/page.tsx
@@ -1,9 +1,9 @@
-import Image from "next/image";
import Testimonials from "../components/testimonials/testimonials";
import Contact from "../components/contact/contact";
import AboutMe from "../components/aboutMe/aboutMe";
import Services from "../components/services/services";
import MyProjects from "../components/projects/projects";
+import Hero from "../components/hero/hero";
import { getDictionary } from "@/app/lib/functions/dictionaries";
import { getDefaultLocale } from "@/app/lib/functions/utils";
import { Metadata } from "next";
@@ -23,79 +23,16 @@ export const metadata: Metadata = {
export default async function Home() {
const dict = await getDictionary(await getDefaultLocale());
- const socialLinks = [
- { href: "#", src: "/images/icons/facebook.svg", alt: "facebook" },
- { href: "#", src: "/images/icons/twitter.svg", alt: "twitter" },
- { href: "#", src: "/images/icons/instagram.svg", alt: "instagram" },
- { href: "#", src: "/images/icons/linkedin.svg", alt: "linkedin" },
- ];
return (
-
- {/* Home */}
-
- {/* start */}
-
-
-
-
- {/* socials */}
-
-
- {socialLinks.map(({ href, src, alt }) => (
-
-
-
- ))}
-
-
-
- {/* end */}
-
-
- {dict.website.homePage.top.greeting}
-
-
- {dict.website.homePage.top.name}
-
-
- {dict.website.homePage.top.profession}
-
-
- {dict.website.homePage.top.roll}
-
-
- {dict.website.homePage.top.descriptionAboutMe}
-
- {/*
*/}
-
+
);
}
diff --git a/src/app/(routes)/(website)/components/aboutMe/AboutMeContent.tsx b/src/app/(routes)/(website)/components/aboutMe/AboutMeContent.tsx
new file mode 100644
index 0000000..71a72c9
--- /dev/null
+++ b/src/app/(routes)/(website)/components/aboutMe/AboutMeContent.tsx
@@ -0,0 +1,113 @@
+"use client";
+
+import React from "react";
+import Image from "next/image";
+import { motion } from "framer-motion";
+import { AboutMeDataType } from "@/app/types/types";
+
+interface AboutMeContentProps {
+ data: AboutMeDataType;
+}
+
+const CATEGORIES = {
+ "Core Tech": ["React", "Next.js", "TypeScript", "JavaScript"],
+ "Backend & Tools": ["Laravel", "PHP", "MySQL", "Node.js", "Docker", "Git"],
+ "Architecture & Others": ["REST API", "TDD", "UI/UX Design", "Clean Architecture"],
+};
+
+export default function AboutMeContent({ data }: AboutMeContentProps) {
+ const categorizedSkills = {
+ "Core Tech": [],
+ "Backend & Tools": [],
+ "Architecture & Others": [],
+ "Other": [],
+ };
+
+ data.skills.forEach((skill) => {
+ let matched = false;
+ for (const [cat, skills] of Object.entries(CATEGORIES)) {
+ if (skills.some((s) => skill.title.toLowerCase().includes(s.toLowerCase()))) {
+ categorizedSkills[cat].push(skill.title);
+ matched = true;
+ break;
+ }
+ }
+ if (!matched) categorizedSkills["Other"].push(skill.title);
+ });
+
+ const isRTL = typeof document !== "undefined" && document.dir === "rtl";
+
+ return (
+
+ {/* Text Section */}
+
+
+ {data.title}
+
+
+ {data.description}
+
+
+
+ {Object.entries(categorizedSkills).map(([category, skills]) => (
+ skills.length > 0 && (
+
+
+ {category}
+
+
+ {skills.map((skill, idx) => (
+
+ {skill}
+
+ ))}
+
+
+ )
+ ))}
+
+
+
+ {/* Image Section */}
+
+
+
+
+ );
+}
diff --git a/src/app/(routes)/(website)/components/aboutMe/aboutMe.tsx b/src/app/(routes)/(website)/components/aboutMe/aboutMe.tsx
index 48d0f6d..cdfa056 100644
--- a/src/app/(routes)/(website)/components/aboutMe/aboutMe.tsx
+++ b/src/app/(routes)/(website)/components/aboutMe/aboutMe.tsx
@@ -3,47 +3,26 @@ import { cookies } from "next/headers";
import React from "react";
import Skeleton from "react-loading-skeleton";
import "react-loading-skeleton/dist/skeleton.css";
+import AboutMeContent from "./AboutMeContent";
export default async function AboutMe() {
const aboutMeData: AboutMeDataType = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/website/about-me/1/translation/${(await cookies()).get("selectedLocale")?.value ? (await cookies()).get("selectedLocale")?.value : (await cookies()).get("defaultLocale")?.value}`,
).then((res) => res.json());
- return (
-
-
-
- {aboutMeData?.title || }
-
-
- {aboutMeData?.description || }
-
-
- {aboutMeData.skills.map((skill, index) => (
- -
-
{skill.title}
-
-
-
-
- ))}
-
+ if (!aboutMeData) {
+ return (
+
-
-

-
-
- );
+ );
+ }
+
+ return
;
}
diff --git a/src/app/(routes)/(website)/components/contact/contact.tsx b/src/app/(routes)/(website)/components/contact/contact.tsx
index 6069056..e79d795 100644
--- a/src/app/(routes)/(website)/components/contact/contact.tsx
+++ b/src/app/(routes)/(website)/components/contact/contact.tsx
@@ -1,4 +1,8 @@
-import React from "react";
+"use client";
+
+import React, { useState } from "react";
+import { motion } from "framer-motion";
+import { Send } from "lucide-react";
interface ContactProps {
dict: {
@@ -16,32 +20,85 @@ interface ContactProps {
}
export default function Contact({ dict }: ContactProps) {
+ const [isSubmitting, setIsSubmitting] = useState(false);
+ const [submitted, setSubmitted] = useState(false);
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ setIsSubmitting(true);
+ // Simulate API call
+ await new Promise((resolve) => setTimeout(resolve, 2000));
+ setIsSubmitting(false);
+ setSubmitted(true);
+ setTimeout(() => setSubmitted(false), 5000);
+ };
+
return (
-
+
+
+
+
+
);
}
diff --git a/src/app/(routes)/(website)/components/footer/footer.tsx b/src/app/(routes)/(website)/components/footer/footer.tsx
index a6d8bed..9c896a5 100644
--- a/src/app/(routes)/(website)/components/footer/footer.tsx
+++ b/src/app/(routes)/(website)/components/footer/footer.tsx
@@ -6,104 +6,76 @@ import Link from "next/link";
export default async function Footer() {
const dict = await getDictionary(await getDefaultLocale());
+
+ const footerLinks = [
+ { href: "/#home", label: dict.website.footer.Home },
+ { href: "/#about-me", label: dict.website.footer.AboutMe },
+ { href: "/#services", label: dict.website.footer.Services },
+ { href: "/#projects", label: dict.website.footer.Projects },
+ { href: "/#testimonials", label: dict.website.footer.Testimonials },
+ { href: "/#contact", label: dict.website.footer.Contact },
+ { href: "/blog", label: dict.website.footer.Blog },
+ ];
+
+ const socialLinks = [
+ { src: "/images/icons/facebook.svg", alt: "facebook" },
+ { src: "/images/icons/twitter.svg", alt: "twitter" },
+ { src: "/images/icons/instagram.svg", alt: "instagram" },
+ { src: "/images/icons/linkedin.svg", alt: "linkedin" },
+ ];
+
return (
-
-
-
-
-
+