[
  {
    "path": ".gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n.yarn/install-state.gz\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# local env files\n.env*.local\n\n# vercel\n.vercel\n\n.env.local\n\n\n# typescript\n*.tsbuildinfo\nnext-env.d.ts\n"
  },
  {
    "path": ".nvmrc",
    "content": "20.19.1"
  },
  {
    "path": "README.md",
    "content": "hello world"
  },
  {
    "path": "app/about/page.tsx",
    "content": "import AboutSection from \"@/components/AboutSection\";\nimport { Metadata } from \"next\";\nimport React from \"react\";\n\nexport const metadata: Metadata = {\n  title: \"About | evrea\",\n  description:\n    \"Frontendwith 1+ years of expertise. Junior Software Engineer. Specializing web apps, UX, and JavaScript technologies.\",\n  keywords: [\n    \"Developer\",\n    \"Portfolio\",\n    \"Developer Portflio\",\n    \"Muhamad Alfin Pratama\",\n    \"Next.js\",\n    \"React\",\n    \"ReactNative\",\n    \"Android\",\n    \"nodejs\",\n    \"alfin\",\n    \"alfin pratama\",\n    \"muhamad alfin\",\n    \"muhamad alfin pratama\",\n    \"muhammad alfin pratama\",\n    \"frontend web\",\n    \"Frontend Developer\",\n    \"Front-end Developer\",\n    \"Front End Developer\",\n    \"Frontend Engineer\",\n    \"Front-end Engineer\",\n    \"Front End Engineer\",\n    \"Muhamad Alfin\",\n    \"Alfin Pratama\",\n    \"Muhamad\",\n    \"Alfin\",\n    \"Pratama\",\n    \"evrea\",\n  ],\n};\n\nconst AboutPage = () => {\n  return (\n    <main className=\"w-full min-h-screen bg-transparent pt-28 pb-12 px-4 md:px-8\">\n      <AboutSection />\n    </main>\n  );\n};\n\nexport default AboutPage;\n"
  },
  {
    "path": "app/api/contact/route.ts",
    "content": "export const runtime = \"edge\";\n\nexport const POST = async (req: Request) => {\n  try {\n    const { name, email, subject, message } = await req.json();\n\n    if (!name || !email || !subject || !message) {\n      return Response.json(\n        { error: \"Please fill in all fields\" },\n        { status: 400 }\n      );\n    }\n\n    const res = await fetch(\n      `https://api.mailgun.net/v3/${process.env.MAILGUN_DOMAIN}/messages`,\n      {\n        method: \"POST\",\n        headers: {\n          Authorization: `Basic ${btoa(`api:${process.env.MAILGUN_PASSWORD}`)}`,\n          \"Content-Type\": \"application/x-www-form-urlencoded\",\n        },\n        body: new URLSearchParams({\n          from: process.env.MAILGUN_EMAIL!,\n          to: \"muhamadalfinpratamaa@gmail.com\",\n          subject,\n          text: message,\n        }),\n      }\n    );\n\n    if (!res.ok) throw new Error(\"Failed to send email\");\n\n    return Response.json({ message: \"Email sent\" }, { status: 200 });\n  } catch (error) {\n    console.error(error);\n    return Response.json({ error: \"Error sending email\" }, { status: 500 });\n  }\n};\n"
  },
  {
    "path": "app/blogs/[slug]/PostContent.tsx",
    "content": "import Image from \"next/image\";\nimport { PortableText } from \"@portabletext/react\";\n\nexport default function PostContent({ post, ptComponents, urlFor }: any) {\n  return (\n    <section className=\"min-h-screen py-24 px-4 text-white\">\n      <div className=\"max-w-5xl mx-auto grid gap-6 md:grid-cols-3\">\n        <div className=\"col-span-full bg-neutral-900/50 border border-neutral-700 rounded-xl p-8\">\n          <h1 className=\"text-4xl md:text-5xl font-bold mb-6 text-white\">\n            {post.title}\n          </h1>\n          {post.mainImage && (\n            <div className=\"relative w-full h-72 md:h-96 rounded-xl overflow-hidden border border-neutral-700\">\n              <Image\n                src={urlFor(post.mainImage).url()}\n                alt={post.title}\n                fill\n                className=\"object-cover\"\n                priority\n              />\n            </div>\n          )}\n        </div>\n\n        <div className=\"col-span-full md:col-span-2 bg-neutral-900/50 border border-neutral-700 rounded-xl p-8 prose prose-invert max-w-none\">\n          <PortableText value={post.body} components={ptComponents} />\n        </div>\n      </div>\n    </section>\n  );\n}\n"
  },
  {
    "path": "app/blogs/[slug]/page.tsx",
    "content": "import { client } from \"@/libs/sanity.client\";\nimport imageUrlBuilder from \"@sanity/image-url\";\nimport { groq } from \"next-sanity\";\nimport Image from \"next/image\";\nimport { notFound } from \"next/navigation\";\nimport { PortableText } from \"@portabletext/react\";\nimport { Metadata } from \"next\";\n\nexport const runtime = \"edge\";\n\nconst builder = imageUrlBuilder(client);\nfunction urlFor(source: any) {\n  return builder.image(source);\n}\n\nconst postQuery = groq`*[_type == \"post\" && slug.current == $slug][0] {\n  ...,\n  author-> { \n    name,\n    image,\n    bio\n  },\n  \"categories\": categories[]->title \n}`;\n\nconst ptComponents = {\n  types: {\n    image: ({ value }: { value: any }) => {\n      if (!value?.asset?._ref) return null;\n      return (\n        <div className=\"relative w-full h-96 my-8\">\n          <Image\n            src={urlFor(value).url()}\n            alt={value.alt || \"Gambar dari artikel\"}\n            fill\n            priority\n            className=\"object-contain\"\n          />\n        </div>\n      );\n    },\n  },\n  block: {\n    h1: ({ children }: any) => (\n      <h1 className=\"text-4xl font-bold text-white my-6\">{children}</h1>\n    ),\n    h2: ({ children }: any) => (\n      <h2 className=\"text-3xl font-semibold text-white my-4\">{children}</h2>\n    ),\n    h3: ({ children }: any) => (\n      <h3 className=\"text-2xl font-semibold text-white my-3\">{children}</h3>\n    ),\n    normal: ({ children }: any) => <p className=\"leading-7 my-4\">{children}</p>,\n    blockquote: ({ children }: any) => (\n      <blockquote className=\"border-l-4 border-purple-400 pl-4 italic my-4\">\n        {children}\n      </blockquote>\n    ),\n  },\n  marks: {\n    strong: ({ children }: any) => (\n      <strong className=\"font-bold text-white\">{children}</strong>\n    ),\n    em: ({ children }: any) => (\n      <em className=\"italic text-white\">{children}</em>\n    ),\n    link: ({ value, children }: any) => {\n      const target = (value?.href || \"\").startsWith(\"http\")\n        ? \"_blank\"\n        : undefined;\n      return (\n        <a\n          href={value?.href}\n          target={target}\n          rel={target === \"_blank\" ? \"noopener noreferrer\" : undefined}\n          className=\"text-purple-400 hover:text-purple-300 underline\"\n        >\n          {children}\n        </a>\n      );\n    },\n  },\n  list: {\n    bullet: ({ children }: any) => (\n      <ul className=\"list-disc ml-6 space-y-2\">{children}</ul>\n    ),\n    number: ({ children }: any) => (\n      <ol className=\"list-decimal ml-6 space-y-2\">{children}</ol>\n    ),\n  },\n};\n\ntype PageProps = {\n  params: Promise<{ slug: string }>;\n};\n\n// Generate dynamic metadata\nexport async function generateMetadata({\n  params,\n}: PageProps): Promise<Metadata> {\n  const { slug } = await params;\n  const post = await client.fetch(postQuery, { slug });\n\n  if (!post) {\n    return {\n      title: \"Post Not Found\",\n      description: \"The blog post you're looking for doesn't exist.\",\n    };\n  }\n\n  const getPlainTextFromBody = (body: any[]): string => {\n    if (!body || !Array.isArray(body)) return \"\";\n\n    return body\n      .filter((block) => block._type === \"block\" && block.style === \"normal\")\n      .map((block) => block.children?.map((child: any) => child.text).join(\"\"))\n      .join(\" \")\n      .slice(0, 160);\n  };\n\n  const description =\n    post.excerpt || getPlainTextFromBody(post.body) || \"Read this blog post\";\n  const imageUrl = post.mainImage\n    ? urlFor(post.mainImage).width(1200).height(630).url()\n    : null;\n\n  return {\n    title: `${post.title} - Blog Alfin Pratama`,\n    description,\n    keywords: post.categories?.join(\", \"),\n    authors: [{ name: post.author?.name || \"Anonymous\" }],\n    openGraph: {\n      title: post.title,\n      description,\n      type: \"article\",\n      publishedTime: post.publishedAt,\n      authors: [post.author?.name || \"Anonymous\"],\n      images: imageUrl\n        ? [\n            {\n              url: imageUrl,\n              width: 1200,\n              height: 630,\n              alt: post.title,\n            },\n          ]\n        : [],\n    },\n    twitter: {\n      card: \"summary_large_image\",\n      title: post.title,\n      description,\n      images: imageUrl ? [imageUrl] : [],\n    },\n    alternates: {\n      canonical: `/blogs/${slug}`,\n    },\n  };\n}\n\nexport default async function PostPage({ params }: PageProps) {\n  const { slug } = await params;\n  const post = await client.fetch(postQuery, { slug });\n\n  if (!post) {\n    notFound();\n  }\n\n  return (\n    <article className=\"min-h-screen py-28  text-white\">\n      <div className=\"max-w-5xl rounded-xl mx-auto bg-neutral-900/50 border border-neutral-700 p-10\">\n        {/* Header Artikel */}\n        <div className=\"text-center mb-12\">\n          <h1 className=\"text-4xl md:text-5xl font-extrabold tracking-tight mb-4\">\n            {post.title}\n          </h1>\n          <div className=\"flex items-center justify-center space-x-4 text-gray-400\">\n            <div className=\"flex items-center space-x-2\">\n              {post.author?.image && (\n                <Image\n                  src={\n                    post.authorImage?.asset\n                      ? urlFor(post.authorImage).width(40).height(40).url()\n                      : \"/default-avatar.jpg\"\n                  }\n                  alt={`Foto ${post.authorName}`}\n                  width={40}\n                  height={40}\n                  className=\"rounded-full\"\n                />\n              )}\n              <span>{post.author?.name || \"Anonim\"}</span>\n            </div>\n            <span>&bull;</span>\n            <span>{new Date(post.publishedAt).toLocaleDateString()}</span>\n          </div>\n        </div>\n\n        <div className=\"relative w-full h-64 md:h-96 rounded-lg overflow-hidden mb-12 shadow-lg\">\n          <Image\n            src={urlFor(post.mainImage).url()}\n            alt={`Gambar utama untuk ${post.title}`}\n            layout=\"fill\"\n            objectFit=\"cover\"\n            priority\n          />\n        </div>\n\n        <div className=\"prose prose-invert prose-lg max-w-none prose-h1:text-purple-400 prose-a:text-purple-400 hover:prose-a:text-purple-300\">\n          <PortableText value={post.body} components={ptComponents} />\n        </div>\n      </div>\n    </article>\n  );\n}\n"
  },
  {
    "path": "app/blogs/page.tsx",
    "content": "import { client } from \"@/libs/sanity.client\";\nimport { groq } from \"next-sanity\";\nimport BlogClientPage from \"@/components/BlogClientPage\"; // Kita akan buat komponen ini\nimport { Metadata } from \"next\";\n\nexport const runtime = 'edge';\n\nexport const metadata: Metadata = {\n  title: \"Blog - Alfin Pratama\",\n  description:\n    \"Sharing my thoughts, experiences, and insights on web development, programming, and technology.\",\n  openGraph: {\n    title: \"Blog - Alfin Pratama\",\n    description:\n      \"Sharing my thoughts, experiences, and insights on web development, programming, and technology.\",\n    url: \"https://evrea.tech/blogs\",\n    siteName: \"Alfin Pratama\",\n    images: [\n      {\n        url: \"https://evrea.tech/avatar2_.webp\",\n        width: 1200,\n        height: 630,\n        alt: \"Blog - Alfin Pratama\",\n      },\n    ],\n    locale: \"id_ID\",\n    type: \"website\",\n  },\n  keywords: [\n    \"blog\",\n    \"web development\",\n    \"programming\",\n    \"technology\",\n    \"javascript\",\n    \"react\",\n    \"next.js\",\n    \"sanity.io\",\n    \"tailwindcss\",\n    \"personal blog\",\n    \"blog alfin\",\n    \"coding\",\n    \"software development\",\n    \"frontend\",\n    \"backend\",\n    \"fullstack\",\n    \"tutorials\",\n    \"tips and tricks\",\n    \"web design\",\n    \"developer insights\",\n    \"blog evrea\",\n  ],\n};\n\nexport interface Post {\n  _id: string;\n  title: string;\n  slug: { current: string };\n  mainImage: any;\n  publishedAt: string;\n  authorName: string;\n  authorImage: any;\n  categories: Category[];\n}\n\nexport interface Category {\n  _id: string;\n  title: string;\n  slug: { current: string };\n}\n\nconst postsQuery = groq`*[_type == \"post\"] | order(publishedAt desc) {\n  _id,\n  title,\n  slug,\n  mainImage,\n  publishedAt,\n  \"authorName\": author->name, \n  \"authorImage\": author->image,\n  \"categories\": categories[]->{ _id, title, slug }\n}`;\n\nconst categoriesQuery = groq`*[_type == \"category\"] | order(title asc) {\n  _id,\n  title,\n  slug\n}`;\n\nexport default async function BlogPage() {\n  const posts: Post[] = await client.fetch(\n    postsQuery,\n    {},\n    { cache: \"no-store\" }\n  );\n\n  const categories: Category[] = await client.fetch(\n    categoriesQuery,\n    {},\n    {\n      cache: \"no-store\",\n    }\n  );\n\n  return <BlogClientPage posts={posts} categories={categories} />;\n}\n"
  },
  {
    "path": "app/contact/page.tsx",
    "content": "import { type Metadata } from \"next\";\nimport ContactForm from \"@/components/contact\";\n\nexport const metadata: Metadata = {\n  title: \"Contact | evrea\",\n  description:\n    \"Contact me via email to start a conversation or ask me anything\",\n  keywords: [\n    \"Contact\",\n    \"Portfolio\",\n    \"Developer Portflio\",\n    \"Muhamad Alfin Pratama\",\n    \"Next.js\",\n    \"React\",\n    \"ReactNative\",\n    \"Android\",\n    \"nodejs\",\n    \"alfin\",\n    \"alfin pratama\",\n    \"muhamad alfin\",\n    \"muhamad alfin pratama\",\n    \"muhammad alfin pratama\",\n    \"frontend web\",\n    \"Frontend Developer\",\n    \"Front-end Developer\",\n    \"Front End Developer\",\n    \"Frontend Engineer\",\n    \"Front-end Engineer\",\n    \"Front End Engineer\",\n    \"Muhamad Alfin\",\n    \"Alfin Pratama\",\n    \"Muhamad\",\n    \"Alfin\",\n    \"Pratama\",\n    \"evrea\",\n  ],\n};\n\nconst ContactPage = () => {\n  return <ContactForm />;\n};\nexport default ContactPage;\n"
  },
  {
    "path": "app/globals.css",
    "content": "@import \"tailwindcss\";\n\n:root {\n  --foreground-rgb: 0, 0, 0;\n  --background-start-rgb: 214, 219, 220;\n  --background-end-rgb: 255, 255, 255;\n}\n\n@layer components {\n  .banner-heading {\n    @apply text-4xl font-bold text-white md:text-5xl;\n  }\n}\n\n@media (prefers-color-scheme: dark) {\n  :root {\n    --foreground-rgb: 255, 255, 255;\n    --background-start-rgb: 0, 0, 0;\n    --background-end-rgb: 0, 0, 0;\n  }\n}\n\nbody {\n  color: rgb(var(--foreground-rgb));\n  background: linear-gradient(\n      to bottom,\n      transparent,\n      rgb(var(--background-end-rgb))\n    )\n    rgb(var(--background-start-rgb));\n}\n\n@layer utilities {\n  .text-balance {\n    text-wrap: balance;\n  }\n}\n\n.custom-scrollbar {\n  scrollbar-width: thin; /* Firefox */\n  scrollbar-color: #6b21a8 #262626; /* thumb, track */\n}\n\n/* Chrome, Edge, Safari */\n.custom-scrollbar::-webkit-scrollbar {\n  width: 6px; /* lebar scrollbar */\n}\n\n.custom-scrollbar::-webkit-scrollbar-track {\n  background: #262626; /* warna track */\n  border-radius: 10px;\n}\n\n.custom-scrollbar::-webkit-scrollbar-thumb {\n  background-color: #6b21a8; /* warna thumb */\n  border-radius: 10px;\n}\n\n.custom-scrollbar::-webkit-scrollbar-thumb:hover {\n  background-color: #9333ea; /* hover state */\n}\n"
  },
  {
    "path": "app/layout.tsx",
    "content": "import type { Metadata } from \"next\";\nimport { Poppins } from \"next/font/google\";\nimport \"./globals.css\";\nimport Navbar from \"@/components/Navbar\";\nimport ClientEffects from \"@/components/ClientEffects\";\nimport { ClientProvider } from \"@/provider/ClientProvicer\";\nimport CursorBackground from \"@/components/CursorBacground\";\nconst poppins = Poppins({\n  subsets: [\"latin\"],\n  weight: [\"100\", \"200\", \"300\", \"400\", \"500\", \"600\", \"700\", \"800\", \"900\"],\n  variable: \"--font-poppins\",\n});\n\nexport const metadata: Metadata = {\n  metadataBase: new URL(\"https://alfinpratamaa.github.io/\"),\n  title: \"Muhamad Alfin Pratama\",\n  description:\n    \"Fullstack Developer & DevOps Engineer with 1+ years of expertise in JavaScript, TypeScript, Golang, and PHP. Experienced with Next.js, React.js, Laravel frameworks, Docker containerization, VPS hosting, and CI/CD pipelines.\",\n  keywords: [\n    \"Fullstack Developer\",\n    \"DevOps Engineer\",\n    \"Portfolio\",\n    \"Developer Portfolio\",\n    \"Muhamad Alfin Pratama\",\n    \"Next.js\",\n    \"React\",\n    \"ReactNative\",\n    \"Laravel\",\n    \"JavaScript\",\n    \"TypeScript\",\n    \"Golang\",\n    \"PHP\",\n    \"Docker\",\n    \"VPS Hosting\",\n    \"CI/CD\",\n    \"DevOps\",\n    \"Backend Developer\",\n    \"Frontend Developer\",\n    \"Fullstack\",\n    \"Web Developer\",\n    \"Software Engineer\",\n    \"Software Developer\",\n    \"Devops Engineer\",\n    \"web development\",\n    \"programming\",\n    \"alfin\",\n    \"alfin pratama\",\n    \"muhamad alfin\",\n    \"muhamad alfin pratama\",\n    \"muhammad alfin pratama\",\n    \"Muhamad Alfin\",\n    \"Alfin Pratama\",\n    \"Muhamad\",\n    \"Alfin\",\n    \"Pratama\",\n    \"evrea\",\n  ],\n  creator: \"Muhamad Alfin Pratama\",\n  applicationName: \"evrea\",\n  icons: \"/avatar_2.webp\",\n  openGraph: {\n    title: \"Muhamad Alfin Pratama\",\n    description:\n      \"Fullstack Developer & DevOps Engineer with 1+ years of expertise in JavaScript, TypeScript, Golang, and PHP. Specializing in Next.js, React.js, Laravel, Docker, VPS hosting, and CI/CD pipelines.\",\n    images: \"/Avatar_2.webp\",\n  },\n  alternates: {\n    canonical: \"https://evrea.tech\",\n  },\n};\n\nexport default function RootLayout({\n  children,\n}: {\n  children: React.ReactNode;\n}) {\n  return (\n    <html lang=\"en\" className=\"dark\">\n      <head>\n        <link\n          rel=\"preload\"\n          as=\"image\"\n          href=\"/avatar_2.webp\"\n          type=\"image/webp\"\n        />\n        <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\" />\n        <link rel=\"shortcut icon\" href=\"/avatar_2.jpeg\" />\n      </head>\n      <body\n        className={`${poppins.className} min-h-screen overflow-y-scroll overflow-x-hidden relative bg-black`}\n      >\n        <CursorBackground>\n          <ClientProvider />\n          <ClientEffects />\n          <div className=\"relative z-[2]\">\n            <div className=\"mb-[10px]\">\n              <Navbar />\n            </div>\n            {children}\n          </div>\n        </CursorBackground>\n      </body>\n    </html>\n  );\n}\n"
  },
  {
    "path": "app/page.tsx",
    "content": "import Banner from \"@/components/Banner\";\n\nexport default function Home() {\n  return (\n    <main>\n      <Banner />\n    </main>\n  );\n}\n"
  },
  {
    "path": "app/robots.ts",
    "content": "import { MetadataRoute } from \"next\";\n\nexport default function robots(): MetadataRoute.Robots {\n  return {\n    rules: {\n      userAgent: \"*\",\n      allow: \"/\",\n    },\n    sitemap: \"https://evrea.tech/sitemap.xml\",\n  };\n}\n"
  },
  {
    "path": "app/sitemap.ts",
    "content": "// app/sitemap.ts\n\nimport { MetadataRoute } from \"next\";\nimport { client } from \"@/libs/sanity.client\";\nimport { groq } from \"next-sanity\";\n\n// Definisikan tipe untuk data post yang diambil\ninterface Post {\n  slug: {\n    current: string;\n  };\n  _updatedAt: string; // Gunakan _updatedAt untuk lastModified\n}\n\nexport default async function sitemap(): Promise<MetadataRoute.Sitemap> {\n  const baseUrl = \"https://evrea.tech\";\n\n  // 1. Ambil semua slug post dari Sanity\n  const query = groq`*[_type == \"post\"]{ \"slug\": slug.current, _updatedAt }`;\n  const posts: Post[] = await client.fetch(query);\n\n  const postUrls = posts.map((post) => ({\n    url: `${baseUrl}/blogs/${post.slug}`,\n    lastModified: new Date(post._updatedAt),\n    changeFrequency: \"daily\" as \"daily\",\n    priority: 0.8,\n  }));\n\n  // 2. Gabungkan dengan URL statis Anda\n  return [\n    {\n      url: baseUrl,\n      lastModified: new Date(),\n      changeFrequency: \"daily\",\n      priority: 1,\n    },\n    {\n      url: `${baseUrl}/about`,\n      lastModified: new Date(),\n      changeFrequency: \"monthly\",\n      priority: 0.7,\n    },\n    {\n      url: `${baseUrl}/blogs`,\n      lastModified: new Date(),\n      changeFrequency: \"daily\",\n      priority: 0.9,\n    },\n    ...postUrls,\n  ];\n}\n"
  },
  {
    "path": "components/AboutSection.tsx",
    "content": "\"use client\";\n\nimport Image from \"next/image\";\nimport Link from \"next/link\";\nimport { motion } from \"framer-motion\";\nimport {\n  FaGithub,\n  FaInstagram,\n  FaLinkedin,\n  FaLocationDot,\n  FaTelegram,\n  FaWhatsapp,\n  FaCalendar,\n} from \"react-icons/fa6\";\nimport { MdOutlineWork } from \"react-icons/md\";\nimport Skills from \"./Skills\";\nimport { FaExternalLinkAlt } from \"react-icons/fa\";\nimport { ProjectsSection } from \"./ProjectSection\";\n\n// Data untuk media sosial\nconst socials = [\n  { icon: <FaInstagram />, href: \"https://www.instagram.com/visfiveor5\" },\n  { icon: <FaLinkedin />, href: \"https://www.linkedin.com/in/alfinpr\" },\n  { icon: <FaGithub />, href: \"https://www.github.com/Alfinpratamaa\" },\n  { icon: <FaWhatsapp />, href: \"https://wa.me/6285175369960\" },\n  { icon: <FaTelegram />, href: \"https://t.me/visfiveor5\" },\n];\n\nconst AboutSection = () => {\n  // Varian animasi untuk container utama (stagger effect)\n  const containerVariants = {\n    hidden: { opacity: 0 },\n    visible: {\n      opacity: 1,\n      transition: {\n        staggerChildren: 0.1, // Setiap anak akan muncul dengan jeda 0.1 detik\n      },\n    },\n  };\n\n  // Varian animasi untuk setiap item di dalam grid\n  const itemVariants = {\n    hidden: { opacity: 0, y: 20 },\n    visible: { opacity: 1, y: 0, transition: { duration: 0.5 } },\n  };\n\n  return (\n    <motion.div\n      className=\"grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 gap-4 max-w-5xl mx-auto\"\n      variants={containerVariants}\n      initial=\"hidden\"\n      animate=\"visible\"\n    >\n      {/* ======================================= */}\n      {/* KARTU 1: PROFIL UTAMA                 */}\n      {/* ======================================= */}\n      <motion.div\n        className=\"col-span-full md:col-span-2 row-span-2 bg-neutral-900/50 border border-neutral-700 rounded-xl p-6 flex flex-col md:flex-row items-center gap-6\"\n        variants={itemVariants}\n      >\n        <div className=\"flex-shrink-0\">\n          <Image\n            src=\"/Me.png\"\n            alt=\"Muhamad Alfin Pratama\"\n            width={120}\n            height={120}\n            className=\"rounded-full object-cover grayscale hover:grayscale-0 transition-all duration-300\"\n          />\n        </div>\n        <div className=\"text-center md:text-left\">\n          <h1 className=\"text-2xl font-bold text-white\">\n            Muhamad Alfin Pratama\n          </h1>\n          <div className=\"relative mx-auto md:mx-0 w-3/4 mt-2 mb-3\">\n            <div className=\"absolute inset-0 h-0.5 bg-gradient-to-r from-purple-500 to-orange-400\"></div>\n          </div>\n          <div className=\"flex items-center justify-center md:justify-start space-x-2 mt-4\">\n            <MdOutlineWork className=\"text-purple-400\" />\n            <p className=\"text-sm font-semibold text-white\">\n              Student | Frontend Web\n            </p>\n          </div>\n          <div className=\"flex items-center justify-center md:justify-start space-x-2 mt-2\">\n            <FaLocationDot className=\"text-orange-400\" />\n            <p className=\"text-xs text-gray-400\">\n              Bandung, West Java, Indonesia\n            </p>\n          </div>\n        </div>\n      </motion.div>\n\n      {/* ======================================= */}\n      {/* KARTU 2: DESKRIPSI DIRI               */}\n      {/* ======================================= */}\n      <motion.div\n        className=\"col-span-full md:col-span-1 lg:col-span-2 bg-neutral-900/50 border border-neutral-700 rounded-xl p-6\"\n        variants={itemVariants}\n      >\n        <p className=\"text-sm text-gray-300 leading-relaxed\">\n          I am a frontend developer with expertise in React, Next.js, and\n          Tailwind CSS. Passionate about creating user-friendly interfaces and\n          delivering high-quality code. Let's work together!\n        </p>\n      </motion.div>\n\n      {/* ======================================= */}\n      {/* KARTU 3: MEDIA SOSIAL                 */}\n      {/* ======================================= */}\n      <motion.div\n        className=\"col-span-full md:col-span-1 lg:col-span-2 row-span-1 bg-neutral-900/50 border border-neutral-700 rounded-xl p-6 flex items-center justify-center\"\n        variants={itemVariants}\n      >\n        <div className=\"flex items-center justify-center gap-4\">\n          {socials.map((social, index) => (\n            <Link\n              href={social.href}\n              key={index}\n              target=\"_blank\"\n              rel=\"noopener noreferrer\"\n              className=\"text-gray-400 hover:text-white transition-colors text-2xl\"\n            >\n              {social.icon}\n            </Link>\n          ))}\n        </div>\n      </motion.div>\n\n      {/* ======================================= */}\n      {/* KARTU 4: EXPERIENCE                   */}\n      {/* ======================================= */}\n      <motion.div\n        className=\"col-span-full lg:col-span-2 bg-neutral-900/50 border border-neutral-700 rounded-xl p-6\"\n        variants={itemVariants}\n      >\n        <div className=\"flex items-center gap-3 mb-4\">\n          <MdOutlineWork className=\"text-purple-400 text-xl\" />\n          <h3 className=\"text-lg font-bold text-white\">Experience</h3>\n        </div>\n        <div className=\"space-y-4\">\n          <div className=\"border-l-2 border-purple-400 pl-4\">\n            <div className=\"flex items-center gap-2 mb-2\">\n              <h4 className=\"text-white font-semibold\">\n                Frontend Developer Intern\n              </h4>\n            </div>\n            <div className=\"flex items-center gap-2 mb-2\">\n              <Image\n                src=\"https://pilihjurusan.id/favicon.ico\"\n                alt=\"Pilih Jurusan Logo\"\n                width={16}\n                height={16}\n                className=\"rounded\"\n              />\n              <Link\n                href=\"https://pilihjurusan.id\"\n                target=\"_blank\"\n                rel=\"noopener noreferrer\"\n                className=\"text-orange-400 text-sm font-medium hover:text-orange-300 transition-colors\"\n              >\n                Pilih Jurusan\n              </Link>\n            </div>\n            <div className=\"flex items-center gap-1 mb-3\">\n              <FaCalendar className=\"text-gray-400 text-xs\" />\n              <p className=\"text-gray-400 text-xs\">\n                September 2024 - December 2024\n              </p>\n            </div>\n            <ul className=\"text-gray-300 text-sm space-y-2\">\n              <li>\n                • Reorganized assets and components to improve code cleanliness\n                and reusability\n              </li>\n              <li>\n                • Adapted and implemented unit testing using Cypress according\n                to the latest code\n              </li>\n              <li>\n                • Developed new sections and pages based on project manager\n                requests\n              </li>\n              <li>\n                • Built CMS with Firebase integration and data interaction using\n                server actions in Next.js\n              </li>\n            </ul>\n          </div>\n        </div>\n      </motion.div>\n\n      {/* ======================================= */}\n      {/* KARTU 5: PROJECTS                     */}\n      {/* ======================================= */}\n      <ProjectsSection />\n\n      {/* ======================================= */}\n      {/* KARTU 6: SKILLS                       */}\n      {/* ======================================= */}\n      <motion.div\n        className=\"col-span-full row-span-2 bg-neutral-900/50 border border-neutral-700 rounded-xl p-6\"\n        variants={itemVariants}\n      >\n        <Skills />\n      </motion.div>\n    </motion.div>\n  );\n};\n\nexport default AboutSection;\n"
  },
  {
    "path": "components/BackgroundStar.tsx",
    "content": "\"use client\";\n\nimport React, { useMemo, useRef } from \"react\";\nimport { Canvas, useFrame } from \"@react-three/fiber\";\nimport * as THREE from \"three\";\nimport { Points, PointMaterial } from \"@react-three/drei\";\n// @ts-ignore\nimport * as random from \"maath/random/dist/maath-random.esm\";\n\nconst StarBackground = () => {\n  const ref = useRef<THREE.Points>(null!);\n\n  const positions = useMemo(\n    () => random.inSphere(new Float32Array(5000 * 3), { radius: 1.2 }),\n    []\n  );\n  const posRef = useRef<Float32Array>(positions.slice());\n\n  useFrame((state, delta) => {\n    if (!ref.current) return;\n\n    const { mouse } = state;\n    const pos = posRef.current;\n\n    for (let i = 0; i < pos.length; i += 3) {\n      const x = pos[i];\n      const y = pos[i + 1];\n\n      const mx = mouse.x * 1.2;\n      const my = mouse.y * 1.2;\n\n      const dx = x - mx;\n      const dy = y - my;\n      const dist = Math.sqrt(dx * dx + dy * dy);\n\n      if (dist < 0.25) {\n        const force = (0.25 - dist) * 0.03;\n        pos[i] += dx * force;\n        pos[i + 1] += dy * force;\n      }\n    }\n\n    // kasih tau three.js kalau posisi berubah\n    ref.current.geometry.attributes.position.needsUpdate = true;\n\n    // rotasi background biar tetap hidup\n    ref.current.rotation.x -= delta / 20;\n    ref.current.rotation.y -= delta / 30;\n  });\n\n  return (\n    <group rotation={[0, 0, Math.PI / 4]}>\n      <Points ref={ref} positions={posRef.current} stride={3} frustumCulled>\n        <PointMaterial\n          transparent\n          color=\"#ffffff\"\n          size={0.002}\n          sizeAttenuation\n          depthWrite={false}\n        />\n      </Points>\n    </group>\n  );\n};\n\nconst StarsCanvas: React.FC = () => (\n  <div className=\"w-full h-screen fixed inset-0 z-[1]\">\n    <Canvas camera={{ position: [0, 0, 1] }}>\n      <StarBackground />\n    </Canvas>\n  </div>\n);\n\nexport default StarsCanvas;\n"
  },
  {
    "path": "components/Banner.tsx",
    "content": "\"use client\";\n\nimport React from \"react\";\nimport Image from \"next/image\";\nimport Link from \"next/link\";\nimport dynamic from \"next/dynamic\";\nimport { motion } from \"framer-motion\";\n\nconst TypeAnimation = dynamic(\n  () => import(\"react-type-animation\").then((mod) => mod.TypeAnimation),\n  { ssr: false }\n);\nconst FaCloudDownloadAlt = dynamic(\n  () => import(\"react-icons/fa\").then((mod) => mod.FaCloudDownloadAlt),\n  { ssr: false }\n);\n\nconst Banner: React.FC = () => {\n  const containerVariants = {\n    hidden: { opacity: 0 },\n    visible: {\n      opacity: 1,\n      transition: {\n        delay: 0.2,\n        staggerChildren: 0.2,\n      },\n    },\n  };\n  const itemVariants = {\n    hidden: { opacity: 0, y: 30 },\n    visible: { opacity: 1, y: 0, transition: { duration: 0.6 } },\n  };\n  const cardVariants = {\n    hidden: { opacity: 0, scale: 0.9 },\n    visible: { opacity: 1, scale: 1, transition: { duration: 0.7 } },\n  };\n  return (\n    <motion.section\n      className=\"flex items-center justify-center min-h-screen px-3 sm:px-4 py-6 sm:py-10\"\n      initial=\"hidden\"\n      animate=\"visible\"\n      variants={containerVariants}\n    >\n      <motion.div\n        className=\"bg-neutral-900/40 backdrop-blur-sm border border-neutral-700/30 rounded-2xl sm:rounded-3xl p-4 sm:p-6 md:p-8 lg:p-12 max-w-4xl w-full shadow-2xl\"\n        variants={cardVariants}\n      >\n        <div className=\"flex flex-col lg:flex-row items-center gap-6 sm:gap-8 lg:gap-12\">\n          <motion.div className=\"flex-shrink-0\" variants={itemVariants}>\n            <div className=\"bg-neutral-700/50 p-3 sm:p-4 md:p-6 rounded-xl sm:rounded-2xl backdrop-blur-sm border border-neutral-600/30\">\n              <Image\n                priority\n                src=\"/avatar_2.webp\"\n                height={160}\n                width={160}\n                alt=\"Alfin\"\n                className=\"rounded-lg sm:rounded-xl hover:scale-105 transition-transform duration-300 w-32 h-32 sm:w-40 sm:h-40 md:w-[220px] md:h-[220px] object-cover\"\n              />\n            </div>\n          </motion.div>\n          <motion.div\n            className=\"flex-1 space-y-4 sm:space-y-6 text-center lg:text-left w-full\"\n            variants={itemVariants}\n          >\n            <motion.div\n              className=\"space-y-1 sm:space-y-2\"\n              variants={itemVariants}\n            >\n              <h1 className=\"text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-bold leading-tight\">\n                <span className=\"text-transparent bg-clip-text bg-gradient-to-r from-purple-400 to-orange-400\">\n                  Hi,\n                </span>\n                <br className=\"sm:hidden\" />\n                <span className=\"sm:ml-2\">I'm Alfin</span>\n              </h1>\n            </motion.div>\n\n            {/* Typing Effect Card */}\n            <motion.div\n              className=\"bg-neutral-700/30 rounded-lg sm:rounded-xl p-3 sm:p-4 border border-neutral-600/20\"\n              variants={itemVariants}\n            >\n              <div className=\"flex items-center justify-center lg:justify-start\">\n                <div className=\"text-lg sm:text-xl md:text-2xl font-medium text-gray-200 text-center lg:text-left\">\n                  <TypeAnimation\n                    sequence={[\n                      \"Fullstack Developer\",\n                      1000,\n                      \"DevOps Engineer\",\n                      1000,\n                    ]}\n                    wrapper=\"span\"\n                    speed={50}\n                    repeat={Infinity}\n                  />\n                </div>\n              </div>\n            </motion.div>\n\n            <motion.div\n              className=\"bg-neutral-700/20 rounded-lg sm:rounded-xl p-3 sm:p-4 md:p-5 border border-neutral-600/20\"\n              initial={{ opacity: 1, y: 0 }}\n              animate={{ opacity: 1, y: 0 }}\n            >\n              <h2 className=\"text-sm sm:text-base md:text-lg text-gray-200 leading-relaxed\">\n                Passionate Fullstack Developer with a focus on{\" \"}\n                <span className=\"text-transparent font-semibold bg-clip-text bg-gradient-to-r from-purple-400 to-orange-400\">\n                  React.js, php, golang, nodejs\n                </span>{\" \"}\n                development using{\" \"}\n                <span className=\"text-transparent font-semibold bg-clip-text bg-gradient-to-r from-purple-400 to-orange-400\">\n                  Next.js, laravel, gofiber, expressjs\n                </span>{\" \"}\n                framework, dedicated to crafting elegant and user-friendly web\n                applications.\n              </h2>\n            </motion.div>\n\n            {/* Action Buttons */}\n            <motion.div\n              className=\"flex flex-col sm:flex-row gap-3 sm:gap-4 pt-2 sm:pt-4 w-full\"\n              variants={itemVariants}\n            >\n              <motion.a\n                href={\"/cv-alfin.pdf\"}\n                target=\"_blank\"\n                rel=\"noopener noreferrer\"\n                className=\"group flex items-center justify-center gap-2 relative overflow-hidden bg-gradient-to-r from-purple-500 to-orange-400 text-white font-semibold px-4 sm:px-6 py-3 rounded-lg sm:rounded-xl transition-all duration-300 hover:shadow-lg hover:shadow-purple-500/25 min-h-[44px] sm:min-h-[48px] text-sm sm:text-base w-full sm:w-auto\"\n                variants={itemVariants}\n                whileHover={{ scale: 1.05 }}\n                whileTap={{ scale: 0.95 }}\n              >\n                Download CV{\" \"}\n                <FaCloudDownloadAlt className=\"text-sm sm:text-base\" />\n              </motion.a>\n\n              <motion.div\n                variants={itemVariants}\n                whileHover={{ scale: 1.05 }}\n                whileTap={{ scale: 0.95 }}\n                className=\"w-full sm:w-auto\"\n              >\n                <Link\n                  href={\"/blogs\"}\n                  className=\"flex items-center justify-center bg-neutral-700/50 backdrop-blur-sm border border-neutral-600/30 text-white font-semibold px-4 sm:px-6 py-3 rounded-lg sm:rounded-xl hover:bg-neutral-600/50 transition-all duration-300 min-h-[44px] sm:min-h-[48px] text-sm sm:text-base w-full\"\n                >\n                  See My blogs\n                </Link>\n              </motion.div>\n            </motion.div>\n          </motion.div>\n        </div>\n      </motion.div>\n    </motion.section>\n  );\n};\n\nexport default Banner;\n"
  },
  {
    "path": "components/BlogClientPage.tsx",
    "content": "\"use client\";\n\nimport Link from \"next/link\";\nimport Image from \"next/image\";\nimport { useState } from \"react\";\nimport { client } from \"@/libs/sanity.client\";\nimport imageUrlBuilder from \"@sanity/image-url\";\nimport { Post, Category } from \"@/app/blogs/page\";\n\nconst builder = imageUrlBuilder(client);\nfunction urlFor(source: any) {\n  return builder.image(source);\n}\n\ninterface BlogClientPageProps {\n  posts: Post[];\n  categories: Category[];\n}\n\nexport default function BlogClientPage({\n  posts,\n  categories,\n}: BlogClientPageProps) {\n  const [filteredPosts, setFilteredPosts] = useState<Post[]>(posts);\n  const [activeCategory, setActiveCategory] = useState<string>(\"all\");\n\n  const filterPosts = (categoryId: string) => {\n    setActiveCategory(categoryId);\n\n    if (categoryId === \"all\") {\n      setFilteredPosts(posts);\n    } else {\n      const filtered = posts.filter((post) =>\n        post.categories?.some((cat) => cat._id === categoryId)\n      );\n      setFilteredPosts(filtered);\n    }\n  };\n\n  return (\n    <div className=\"min-h-screen py-28 text-white\">\n      <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\n        {/* Header */}\n        <div className=\"text-center mb-8 bg-neutral-900/50 p-6 border border-neutral-700 rounded-xl shadow-lg\">\n          <h1 className=\"text-3xl md:text-5xl font-extrabold tracking-tight\">\n            My Blog\n          </h1>\n          <p className=\"mt-4 text-lg text-gray-400\">\n            Sharing my thoughts, experiences, and insights on web development,\n            programming, and technology.\n          </p>\n        </div>\n\n        {/* Category Tabs */}\n        <div className=\"mb-8\">\n          <div className=\"bg-neutral-900/50 border border-neutral-700 rounded-xl p-6 shadow-lg\">\n            <h3 className=\"text-lg font-semibold mb-4 text-gray-300\">\n              Filter by category :\n            </h3>\n            <div className=\"flex flex-wrap gap-3\">\n              {/* Tab Semua */}\n              <button\n                onClick={() => filterPosts(\"all\")}\n                className={`px-4 py-2 rounded-full text-sm font-medium transition-all duration-300 ${\n                  activeCategory === \"all\"\n                    ? \"bg-purple-600 text-white shadow-lg shadow-purple-500/30\"\n                    : \"bg-neutral-800 text-gray-300 hover:bg-neutral-700 hover:text-white\"\n                }`}\n              >\n                All Topics ({posts.length})\n              </button>\n\n              {/* Tab Categories */}\n              {categories.map((category) => {\n                const categoryPostCount = posts.filter((post) =>\n                  post.categories?.some((cat) => cat._id === category._id)\n                ).length;\n\n                return (\n                  <button\n                    key={category._id}\n                    onClick={() => filterPosts(category._id)}\n                    className={`px-4 py-2 rounded-full text-sm font-medium transition-all duration-300 ${\n                      activeCategory === category._id\n                        ? \"bg-purple-600 text-white shadow-lg shadow-purple-500/30\"\n                        : \"bg-neutral-800 text-gray-300 hover:bg-neutral-700 hover:text-white\"\n                    }`}\n                  >\n                    {category.title} ({categoryPostCount})\n                  </button>\n                );\n              })}\n            </div>\n          </div>\n        </div>\n\n        {/* Posts Grid */}\n        {filteredPosts.length === 0 ? (\n          <div className=\"text-center py-12\">\n            <div className=\"bg-neutral-900/50 border border-neutral-700 rounded-xl p-8\">\n              <p className=\"text-gray-400 text-lg\">\n                Tidak ada artikel untuk kategori ini.\n              </p>\n            </div>\n          </div>\n        ) : (\n          <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8\">\n            {filteredPosts.map((post) => (\n              <Link key={post._id} href={`/blogs/${post.slug.current}`}>\n                <div className=\"group block p-3 bg-neutral-900/50 border border-neutral-700 rounded-xl overflow-hidden shadow-lg hover:shadow-purple-500/30 transition-all duration-300 hover:scale-105\">\n                  <div className=\"relative w-full h-48 rounded-lg overflow-hidden\">\n                    <Image\n                      src={\n                        post.mainImage\n                          ? urlFor(post.mainImage).url()\n                          : \"https://via.placeholder.com/400x300?text=No+Image\"\n                      }\n                      alt={`Gambar untuk ${post.title}`}\n                      fill\n                      className=\"object-cover group-hover:scale-110 transition-transform duration-300\"\n                    />\n                  </div>\n\n                  <div className=\"p-6\">\n                    {/* Categories Badge */}\n                    {post.categories && post.categories.length > 0 && (\n                      <div className=\"flex flex-wrap gap-2 mb-3\">\n                        {post.categories.slice(0, 2).map((category) => (\n                          <span\n                            key={category._id}\n                            className=\"px-2 py-1 bg-purple-600/20 text-purple-300 text-xs rounded-full border border-purple-500/30\"\n                          >\n                            {category.title}\n                          </span>\n                        ))}\n                        {post.categories.length > 2 && (\n                          <span className=\"px-2 py-1 bg-gray-600/20 text-gray-400 text-xs rounded-full\">\n                            +{post.categories.length - 2}\n                          </span>\n                        )}\n                      </div>\n                    )}\n\n                    <h2 className=\"text-xl font-bold mb-2 line-clamp-2 group-hover:text-purple-400 transition-colors\">\n                      {post.title}\n                    </h2>\n\n                    <div className=\"flex items-center text-sm text-gray-400 mt-4\">\n                      <div className=\"relative w-8 h-8 rounded-full overflow-hidden mr-3\">\n                        {post.authorImage && (\n                          <Image\n                            src={\n                              post.authorImage?.asset\n                                ? urlFor(post.authorImage)\n                                    .width(40)\n                                    .height(40)\n                                    .url()\n                                : \"/default-avatar.jpg\"\n                            }\n                            alt={`Foto ${post.authorName}`}\n                            fill\n                            className=\"object-cover\"\n                          />\n                        )}\n                      </div>\n                      <span>{post.authorName}</span>\n                      <span className=\"mx-2\">&bull;</span>\n                      <span>\n                        {new Date(post.publishedAt).toLocaleDateString(\"id-ID\")}\n                      </span>\n                    </div>\n                  </div>\n                </div>\n              </Link>\n            ))}\n          </div>\n        )}\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/ClientEffects.tsx",
    "content": "\"use client\";\nimport dynamic from \"next/dynamic\";\n\nconst StarsCanvas = dynamic(() => import(\"./BackgroundStar\"), {\n  ssr: false,\n});\n\nconst NebulaEffect = dynamic(\n  () =>\n    import(\"./NebulaEffect\").then((mod) => ({\n      default: mod.NebulaEffect,\n    })),\n  {\n    ssr: false,\n  }\n);\n\nexport default function ClientEffects() {\n  return (\n    <>\n      <StarsCanvas />\n      <NebulaEffect />\n    </>\n  );\n}\n"
  },
  {
    "path": "components/CursorBacground.tsx",
    "content": "\"use client\";\n\nimport { useState } from \"react\";\n\nexport default function CursorBackground({\n  children,\n}: {\n  children: React.ReactNode;\n}) {\n  const [pos, setPos] = useState({ x: 0, y: 0 });\n\n  return (\n    <div\n      className=\"relative min-h-screen bg-neutral-900/50 verflow-hidden\"\n      onMouseMove={(e) => setPos({ x: e.clientX, y: e.clientY })}\n    >\n      {/* Layer gradasi mengikuti mouse */}\n      <div\n        className=\"pointer-events-none fixed inset-0 z-0\"\n        style={{\n          background: `radial-gradient(\n            600px at ${pos.x}px ${pos.y}px,\n            rgba(147, 51, 234, 0.35),   /* purple-600 */\n            rgba(251, 146, 60, 0.15),   /* orange-400 */\n            transparent 80%\n          )`,\n          transition: \"background 0.1s ease\",\n        }}\n      />\n\n      {/* Konten utama di atas */}\n      <div className=\"relative z-10\">{children}</div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/LinksCard.tsx",
    "content": "import Image from \"next/image\";\ninterface LinksCardProps {\n    name: string;\n    icon?: any;\n}\n\nconst LinksCard = ({ name, icon }: LinksCardProps) => {\n    return (\n        <div className=\" w-[500px] z-[1] bg-gray-700 rounded-lg cursor-pointer hover:bg-gray-800 hover:-translate-y-3 transition-all ease-in-out duration-300\">\n            <div className=\"flex justify-center h-[60px] z-[1] cursor-pointer items-center gap-3\">\n                <h3 className=\"text-white\">{name}</h3>\n                <Image src={icon} alt={name} width={30} height={30} />\n            </div>\n\n        </div>\n    )\n}\n\nexport default LinksCard"
  },
  {
    "path": "components/Navbar.tsx",
    "content": "\"use client\";\nimport { NavbarLinks } from \"@/libs/NavbarLinks\";\nimport Link from \"next/link\";\nimport React, { useState } from \"react\";\nimport { motion } from \"framer-motion\";\nimport { AiOutlineClose, AiOutlineMenu } from \"react-icons/ai\";\n\nconst Navbar: React.FC<{}> = () => {\n  const navItemVariants = {\n    hidden: { opacity: 0, y: -20 },\n    visible: { opacity: 1, y: 0, transition: { duration: 0.5 } },\n  };\n\n  const containerVariants = {\n    hidden: { opacity: 0 },\n    visible: {\n      opacity: 1,\n      transition: {\n        delayChildren: 0.2,\n        staggerChildren: 0.1,\n      },\n    },\n  };\n\n  const [nav, setNav] = useState(false);\n\n  const handleNav = () => {\n    setNav(!nav);\n  };\n\n  return (\n    <div className=\"w-full h-[5px] bg-['#111'] fixed backdrop-blur-sm py-12 z-50 px-10\">\n      <motion.div\n        variants={containerVariants}\n        className=\"w-full h-full flex flex-row items-center justify-between px-[10px]\"\n      >\n        <Link\n          title=\"Evrea logo\"\n          href=\"/\"\n          className=\"h-auto w-auto flex flex-row items-center\"\n        >\n          <motion.h1\n            variants={navItemVariants}\n            className=\" bg-clip-text bg-gradient-to-r from-purple-500 to-orange-500 text-transparent text-2xl font-bold\"\n          >\n            Evrea\n          </motion.h1>\n        </Link>\n\n        {/* Navbar for desktop (sudah benar) */}\n        <div className=\"md:flex hidden\">\n          <motion.ul\n            initial=\"hidden\"\n            animate=\"visible\"\n            variants={containerVariants}\n            className=\"flex gap-5 items-center\"\n          >\n            {NavbarLinks.map((nav) => (\n              <motion.li key={nav.href} variants={navItemVariants}>\n                <Link\n                  className=\"group text-white transition-all duration-300 ease-in-out\"\n                  href={nav.href}\n                >\n                  <span className=\"bg-left-bottom bg-gradient-to-r from-purple-500 to-orange-500 font-bold bg-[length:0%_2px] bg-no-repeat group-hover:bg-[length:100%_2px] transition-all duration-500 ease-out\">\n                    {nav.text}\n                  </span>\n                </Link>\n              </motion.li>\n            ))}\n          </motion.ul>\n        </div>\n\n        {/* Navbar for mobile (bagian yang diperbaiki) */}\n        <div className=\"block md:hidden\">\n          {/* PERBAIKAN 1: Mengubah div menjadi button untuk aksesibilitas */}\n          <button\n            onClick={handleNav}\n            className=\"cursor-pointer\"\n            aria-label={nav ? \"Close menu\" : \"Open menu\"} // Memberi label yang jelas\n            aria-expanded={nav} // Menandakan status menu (terbuka/tertutup)\n            aria-controls=\"mobile-menu\" // Menghubungkan tombol dengan menu\n          >\n            {nav ? (\n              <AiOutlineClose className=\"text-white\" size={20} />\n            ) : (\n              <AiOutlineMenu size={20} className=\"text-white\" />\n            )}\n          </button>\n\n          <div\n            className={\n              nav\n                ? \"fixed left-0 top-0 w-[60%] min-h-screen bg-gradient-to-r from-purple-500 to-orange-500 z-[1] ease-in-out duration-500\"\n                : \"fixed left-[-100%] top-0 ease-in-out duration-500\"\n            }\n          >\n            {/* PERBAIKAN 2: Judul dipindahkan ke luar <ul> */}\n            <h1 className=\"p-4 text-center text-2xl font-bold text-white\">\n              Evrea\n            </h1>\n\n            <ul id=\"mobile-menu\">\n              {\" \"}\n              {/* Menambahkan ID untuk dihubungkan dengan aria-controls */}\n              {/* PERBAIKAN 3: Struktur <li> -> <Link> dibenarkan */}\n              {NavbarLinks.map((item) => (\n                <li\n                  key={item.href}\n                  className=\"border-b border-b-white rounded-xl top-10 hover:bg-black duration-300 border-gray-600\"\n                >\n                  <Link\n                    href={item.href}\n                    onClick={() => setNav(false)}\n                    className=\"block p-4 text-black hover:text-white cursor-pointer\" // 'block' agar link mengisi seluruh area <li>\n                  >\n                    {item.text}\n                  </Link>\n                </li>\n              ))}\n            </ul>\n          </div>\n        </div>\n      </motion.div>\n    </div>\n  );\n};\n\nexport default Navbar;\n"
  },
  {
    "path": "components/NebulaEffect.tsx",
    "content": "export const NebulaEffect = () => {\n  return (\n    <>\n      <div className=\"fixed inset-0 z-[0]\">\n        <div className=\"w-full h-full bg-[radial-gradient(circle_at_25%_40%,rgba(59,130,246,0.6),transparent_70%)] blur-3xl\"></div>\n      </div>\n      <div className=\"fixed inset-0 z-[0]\">\n        <div className=\"w-full h-full bg-[radial-gradient(circle_at_65%_50%,rgba(175,170,224,0.6),transparent_70%)] blur-3xl\"></div>\n      </div>\n      <div className=\"fixed inset-0 z-[0]\">\n        <div className=\"w-full h-full bg-[radial-gradient(circle_at_50%_50%,rgba(255,255,255,0.35),transparent_60%)] blur-2xl\"></div>\n      </div>\n    </>\n  );\n};\n"
  },
  {
    "path": "components/ProjectCard.tsx",
    "content": "'use client';\nimport Image from 'next/image';\nimport Link from 'next/link';\nimport { FaGithub, FaLink } from 'react-icons/fa';\nimport { motion } from 'framer-motion';\n\ninterface ProjectCardProps {\n    srcImg: string;\n    demoUrl: string;\n    srcGithub: string;\n    title: string;\n    description: string;\n    date: string;\n}\n\nconst ProjectCard = ({\n    srcImg,\n    demoUrl,\n    srcGithub,\n    title,\n    description,\n    date\n}: ProjectCardProps) => {\n\n    // Variants for the container\n    const containerVariants = {\n        hidden: { opacity: 0 },\n        visible: {\n            opacity: 1,\n            transition: {\n                delayChildren: 0.3,\n                staggerChildren: 0.2,\n            },\n        },\n    };\n\n    // Variants for the image\n    const imageVariants = {\n        hidden: { opacity: 0, x: -50 },\n        visible: { opacity: 1, x: 0, transition: { duration: 0.5 } },\n    };\n\n    // Variants for the text\n    const textVariants = {\n        hidden: { opacity: 0, x: 50 },\n        visible: { opacity: 1, x: 0, transition: { duration: 0.5 } },\n    };\n\n    return (\n        <motion.div\n            className=\"bg-transparent border border-white p-6 rounded-lg shadow-lg  text-white max-w-md mx-auto flex flex-col justify-between h-full\"\n            variants={containerVariants}\n            initial=\"hidden\"\n            animate=\"visible\"\n        >\n            <motion.div className='mb-5' variants={containerVariants}>\n                < motion.div className=\"relative w-full h-56 z-[1]\" variants={imageVariants} >\n                    <Image src={srcImg} alt={title} layout=\"fill\" objectFit=\"cover\" className=\"rounded-lg cursor-pointer object-contain hover:scale-105 transition-all duration-500\" />\n                </ motion.div>\n                <motion.div className=\"flex justify-between items-center mt-4\" variants={textVariants}>\n                    <span className=\"text-sm text-gray-400\">{date}</span>\n                </motion.div>\n                <motion.h3 className=\"mt-2 text-xl font-semibold text-white\" variants={textVariants}>{title}</motion.h3>\n                <motion.p className=\"mt-2 text-gray-300\" variants={textVariants}>{description}</motion.p>\n            </motion.div >\n            <motion.div className=\"flex justify-center items-center z-[1] gap-4 mt-auto\" variants={textVariants}>\n                <Link href={srcGithub}>\n                    <div className=\"w-6 h-6 rounded-full\">\n                        <FaGithub className='w-full h-full' />\n                    </div>\n                </Link>\n                <Link href={demoUrl}>\n                    <div className=\"w-6 h-6 rounded-full\">\n                        <FaLink className='w-full h-full' />\n                    </div>\n                </Link>\n            </motion.div>\n        </motion.div >\n    );\n};\n\nexport default ProjectCard;\n"
  },
  {
    "path": "components/ProjectSection.tsx",
    "content": "\"use client\";\n\nimport { project_data } from \"@/libs/constant\";\nimport { motion } from \"framer-motion\";\nimport { useState } from \"react\";\nimport { FaGithub, FaExternalLinkAlt, FaCalendar } from \"react-icons/fa\";\nimport Image from \"next/image\";\nimport Link from \"next/link\";\n\nexport function ProjectsSection() {\n  const [activeIndex, setActiveIndex] = useState(0);\n\n  return (\n    <motion.div\n      className=\"col-span-full lg:col-span-2 bg-neutral-900/50 border border-neutral-700 rounded-xl p-6\"\n      initial={{ opacity: 0, y: 20 }}\n      animate={{ opacity: 1, y: 0 }}\n      transition={{ duration: 0.5 }}\n    >\n      {/* Header */}\n      <div className=\"flex items-center gap-3 mb-6\">\n        <FaGithub className=\"text-orange-400 text-xl\" />\n        <h3 className=\"text-lg font-bold text-white\">Projects</h3>\n      </div>\n\n      <div className=\"grid grid-cols-4 gap-6\">\n        {/* Sidebar Project List */}\n        <div className=\"col-span-1 flex flex-col space-y-3 text-sm\">\n          {project_data.map((proj, index) => (\n            <button\n              key={index}\n              onClick={() => setActiveIndex(index)}\n              className={`text-left transition-colors truncate ${\n                activeIndex === index\n                  ? \"text-orange-400 border-l-2 border-orange-400 pl-2\"\n                  : \"text-gray-400 hover:text-white pl-2\"\n              }`}\n            >\n              {proj.title}\n            </button>\n          ))}\n        </div>\n\n        {/* Project Details */}\n        <div className=\"col-span-3\">\n          <div className=\"flex items-center gap-3 mb-3\">\n            <Image\n              src={project_data[activeIndex].image}\n              alt={project_data[activeIndex].title}\n              width={60}\n              height={40}\n              className=\"rounded-md border border-neutral-600 object-cover\"\n            />\n            <h4 className=\"text-white font-semibold text-base\">\n              {project_data[activeIndex].title}\n            </h4>\n          </div>\n\n          {/* Date */}\n          <div className=\"flex items-center gap-2 text-gray-400 text-xs mb-3\">\n            <FaCalendar className=\"text-xs\" />\n            {project_data[activeIndex].date}\n          </div>\n\n          {/* Description */}\n          <p className=\"text-gray-300 text-sm leading-relaxed mb-4\">\n            {project_data[activeIndex].description}\n          </p>\n\n          {/* Links */}\n          <div className=\"flex gap-3\">\n            {project_data[activeIndex].linkDemo && (\n              <Link\n                href={project_data[activeIndex].linkDemo}\n                target=\"_blank\"\n                rel=\"noopener noreferrer\"\n                className=\"flex items-center gap-1 text-purple-400 hover:text-purple-300 text-sm\"\n              >\n                <FaExternalLinkAlt className=\"text-xs\" /> Demo\n              </Link>\n            )}\n            {project_data[activeIndex].linkGithub && (\n              <Link\n                href={project_data[activeIndex].linkGithub}\n                target=\"_blank\"\n                rel=\"noopener noreferrer\"\n                className=\"flex items-center gap-1 text-orange-400 hover:text-orange-300 text-sm\"\n              >\n                <FaGithub className=\"text-xs\" /> Code\n              </Link>\n            )}\n          </div>\n        </div>\n      </div>\n    </motion.div>\n  );\n}\n"
  },
  {
    "path": "components/Skills.tsx",
    "content": "\"use client\";\n\nimport { motion } from \"framer-motion\";\nimport Image from \"next/image\";\nimport { Frontend_skill, Backend_Skill, tools } from \"@/libs/constant\";\n\nconst categories = [\n  {\n    title: \"Frontend\",\n    data: Frontend_skill,\n    color: \"from-green-500/20 to-teal-500/20\",\n  },\n  {\n    title: \"Backend\",\n    data: Backend_Skill,\n    color: \"from-blue-500/20 to-purple-500/20\",\n  },\n  { title: \"Tools\", data: tools, color: \"from-orange-500/20 to-red-500/20\" },\n];\n\nconst Skills = () => {\n  return (\n    <div className=\"w-full max-w-7xl mx-auto px-4\">\n      <div className=\"text-center mb-12\">\n        <motion.h2\n          initial={{ opacity: 0, y: -20 }}\n          whileInView={{ opacity: 1, y: 0 }}\n          transition={{ duration: 0.6 }}\n          viewport={{ once: true }}\n          className=\"text-3xl md:text-4xl font-bold bg-gradient-to-r from-blue-400 to-purple-600 bg-clip-text text-transparent mb-4\"\n        >\n          Technical Skills\n        </motion.h2>\n        <motion.p\n          initial={{ opacity: 0, y: 20 }}\n          whileInView={{ opacity: 1, y: 0 }}\n          transition={{ duration: 0.6, delay: 0.2 }}\n          viewport={{ once: true }}\n          className=\"text-gray-400 text-lg max-w-2xl mx-auto\"\n        >\n          Technologies and tools I work with\n        </motion.p>\n      </div>\n\n      <div className=\"grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-8\">\n        {categories.map((cat, i) => (\n          <motion.div\n            key={i}\n            initial={{ opacity: 0, y: 30, scale: 0.9 }}\n            whileInView={{ opacity: 1, y: 0, scale: 1 }}\n            transition={{\n              duration: 0.6,\n              delay: i * 0.1,\n              type: \"spring\",\n              stiffness: 100,\n            }}\n            viewport={{ once: true }}\n            whileHover={{\n              scale: 1.02,\n              transition: { duration: 0.2 },\n            }}\n            className={`\n                            relative overflow-hidden\n                            bg-gradient-to-br ${cat.color}\n                            backdrop-blur-xl \n                            border border-white/10\n                            rounded-3xl p-8\n                            shadow-2xl shadow-black/20\n                            hover:shadow-3xl hover:shadow-black/30\n                            transition-all duration-300\n                        `}\n          >\n            {/* Background Pattern */}\n            <div className=\"absolute inset-0 opacity-5\">\n              <div className=\"absolute top-0 right-0 w-32 h-32 bg-white rounded-full -translate-y-16 translate-x-16\"></div>\n              <div className=\"absolute bottom-0 left-0 w-24 h-24 bg-white rounded-full translate-y-12 -translate-x-12\"></div>\n            </div>\n\n            {/* Content */}\n            <div className=\"relative z-10\">\n              {/* Category Header */}\n              <div className=\"flex items-center justify-between mb-6\">\n                <h3 className=\"text-xl font-bold text-white\">{cat.title}</h3>\n                <div className=\"w-8 h-1 bg-gradient-to-r from-white/50 to-transparent rounded-full\"></div>\n              </div>\n\n              {/* Skills Grid */}\n              <div className=\"grid grid-cols-3 sm:grid-cols-4 gap-6\">\n                {cat.data.map((skill, idx) => (\n                  <motion.div\n                    key={idx}\n                    initial={{ opacity: 0, scale: 0 }}\n                    whileInView={{ opacity: 1, scale: 1 }}\n                    transition={{\n                      duration: 0.4,\n                      delay: i * 0.1 + idx * 0.05,\n                      type: \"spring\",\n                      stiffness: 200,\n                    }}\n                    viewport={{ once: true }}\n                    whileHover={{\n                      scale: 1.1,\n                      y: -5,\n                      transition: { duration: 0.2 },\n                    }}\n                    className=\"group\"\n                  >\n                    <div\n                      className=\"\n                                            flex flex-col items-center text-center\n                                            p-3 rounded-2xl\n                                            \n                                            transition-all duration-300\n                                            cursor-pointer\n                                        \"\n                    >\n                      <div\n                        className=\"\n                                                relative w-12 h-12 mb-3\n                                                flex items-center justify-center\n                                                bg-white/10 rounded-xl\n                                                group-hover:bg-white/20\n                                                transition-all duration-300\n                                            \"\n                      >\n                        <Image\n                          src={skill.Image}\n                          width={32}\n                          height={32}\n                          alt={skill.skill_name}\n                          className=\"object-contain filter group-hover:scale-110 transition-transform duration-300\"\n                        />\n                      </div>\n                      <p\n                        className=\"\n                                                text-xs font-medium text-gray-300 \n                                                group-hover:text-white\n                                                transition-colors duration-300\n                                                leading-tight\n                                            \"\n                      >\n                        {skill.skill_name}\n                      </p>\n                    </div>\n                  </motion.div>\n                ))}\n              </div>\n\n              {/* Skills Count */}\n              <div className=\"mt-6 pt-4 border-t border-white/10\">\n                <p className=\"text-xs text-gray-400 text-center\">\n                  {cat.data.length} skills\n                </p>\n              </div>\n            </div>\n          </motion.div>\n        ))}\n      </div>\n    </div>\n  );\n};\n\nexport default Skills;\n"
  },
  {
    "path": "components/about.tsx",
    "content": "'use client'\nimport { Link as ScrollLink, Element } from 'react-scroll';\nimport Skills from '@/components/Skills';\nimport AboutSection from '@/components/AboutSection';\nimport { FaArrowDownLong } from \"react-icons/fa6\";\nexport const About = () => {\n    return (\n        <main className='bg-black'>\n            <AboutSection />\n            <div className='flex justify-center items-center h-5 z-[1] cursor-pointer'>\n                <ScrollLink to=\"skills\" smooth={true} duration={1000} className='z-[1] -mt-8'>\n                    <FaArrowDownLong className='animate-bounce text-4xl text-white' />\n                </ScrollLink>\n            </div>\n            <Element name=\"skills\" className='lg:min-h-screen h-screen'>\n                <Skills />\n            </Element>\n        </main>\n    );\n};"
  },
  {
    "path": "components/contact.tsx",
    "content": "\"use client\";\n\nimport { FaMapMarkerAlt, FaPhoneAlt, FaEnvelope } from \"react-icons/fa\";\nimport { motion } from \"framer-motion\";\nimport { useState } from \"react\";\n\nconst ContactForm = () => {\n  const [name, setName] = useState(\"\");\n  const [email, setEmail] = useState(\"\");\n  const [subject, setSubject] = useState(\"\");\n  const [message, setMessage] = useState(\"\");\n  const [status, setStatus] = useState(\"\");\n\n  const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {\n    event.preventDefault();\n    setStatus(\"Sending...\");\n\n    try {\n      const response = await fetch(\"/api/contact\", {\n        method: \"POST\",\n        headers: { \"Content-Type\": \"application/json\" },\n        body: JSON.stringify({ name, email, subject, message }),\n      });\n\n      const data = await response.json();\n      setStatus(data.message);\n    } catch (error) {\n      console.error(\"Error sending email:\", error);\n      setStatus(\"Error sending email\");\n    } finally {\n      setName(\"\");\n      setEmail(\"\");\n      setSubject(\"\");\n      setMessage(\"\");\n    }\n  };\n\n  return (\n    <main className=\"min-h-screen flex justify-center items-center py-24 px-6 md:px-20\">\n      <div className=\"w-full max-w-6xl\">\n        <motion.div\n          className=\"bg-neutral-900/80 backdrop-blur-sm rounded-2xl shadow-2xl border border-neutral-700/50 overflow-hidden\"\n          initial={{ opacity: 0, y: 50 }}\n          animate={{ opacity: 1, y: 0 }}\n          transition={{ duration: 0.8 }}\n        >\n          <div className=\"grid grid-cols-1 lg:grid-cols-2\">\n            {/* Left side - Contact Info */}\n            <motion.div\n              className=\"p-8 lg:p-12 bg-gradient-to-br from-neutral-800/50 to-neutral-900/50\"\n              initial={{ x: -50, opacity: 0 }}\n              animate={{ x: 0, opacity: 1 }}\n              transition={{ duration: 0.8, delay: 0.2 }}\n            >\n              <div className=\"h-full flex flex-col justify-center\">\n                <h1 className=\"text-4xl lg:text-5xl text-white font-extrabold tracking-tight mb-4\">\n                  Get in touch\n                </h1>\n                <p className=\"text-lg lg:text-xl font-medium text-gray-300 mb-8\">\n                  Fill in the form to start a conversation\n                </p>\n\n                <div className=\"space-y-6\">\n                  <div className=\"flex items-center text-gray-300\">\n                    <div className=\"w-12 h-12 bg-neutral-700/50 rounded-full flex items-center justify-center mr-4\">\n                      <FaMapMarkerAlt className=\"w-5 h-5 text-purple-400\" />\n                    </div>\n                    <p className=\"text-base font-medium\">\n                      Bandung City, West Java, Indonesia\n                    </p>\n                  </div>\n\n                  <div className=\"flex items-center text-gray-300\">\n                    <div className=\"w-12 h-12 bg-neutral-700/50 rounded-full flex items-center justify-center mr-4\">\n                      <FaPhoneAlt className=\"w-5 h-5 text-purple-400\" />\n                    </div>\n                    <p className=\"text-base font-medium\">+62 851 7536 9960</p>\n                  </div>\n\n                  <div className=\"flex items-center text-gray-300\">\n                    <div className=\"w-12 h-12 bg-neutral-700/50 rounded-full flex items-center justify-center mr-4\">\n                      <FaEnvelope className=\"w-5 h-5 text-purple-400\" />\n                    </div>\n                    <p className=\"text-base font-medium\">\n                      muhamadalfinpratamaa@gmail.com\n                    </p>\n                  </div>\n                </div>\n              </div>\n            </motion.div>\n\n            {/* Right side - Contact Form */}\n            <motion.div\n              className=\"p-8 lg:p-12\"\n              initial={{ x: 50, opacity: 0 }}\n              animate={{ x: 0, opacity: 1 }}\n              transition={{ duration: 0.8, delay: 0.4 }}\n            >\n              <form onSubmit={handleSubmit} className=\"space-y-6\">\n                <div>\n                  <label\n                    htmlFor=\"name\"\n                    className=\"block text-sm font-medium text-gray-300 mb-2\"\n                  >\n                    Full Name\n                  </label>\n                  <input\n                    type=\"text\"\n                    id=\"name\"\n                    value={name}\n                    onChange={(e) => setName(e.target.value)}\n                    placeholder=\"Enter your full name\"\n                    className=\"w-full py-3 px-4 rounded-lg bg-neutral-700/50 border border-neutral-600 text-white placeholder-gray-400 font-medium focus:border-purple-500 focus:outline-none focus:ring-2 focus:ring-purple-500/20 transition-all duration-300\"\n                    required\n                  />\n                </div>\n\n                <div>\n                  <label\n                    htmlFor=\"email\"\n                    className=\"block text-sm font-medium text-gray-300 mb-2\"\n                  >\n                    Email\n                  </label>\n                  <input\n                    type=\"email\"\n                    id=\"email\"\n                    value={email}\n                    onChange={(e) => setEmail(e.target.value)}\n                    placeholder=\"Enter your email\"\n                    className=\"w-full py-3 px-4 rounded-lg bg-neutral-700/50 border border-neutral-600 text-white placeholder-gray-400 font-medium focus:border-purple-500 focus:outline-none focus:ring-2 focus:ring-purple-500/20 transition-all duration-300\"\n                    required\n                  />\n                </div>\n\n                <div>\n                  <label\n                    htmlFor=\"subject\"\n                    className=\"block text-sm font-medium text-gray-300 mb-2\"\n                  >\n                    Subject\n                  </label>\n                  <input\n                    type=\"text\"\n                    id=\"subject\"\n                    value={subject}\n                    onChange={(e) => setSubject(e.target.value)}\n                    placeholder=\"Enter email subject\"\n                    className=\"w-full py-3 px-4 rounded-lg bg-neutral-700/50 border border-neutral-600 text-white placeholder-gray-400 font-medium focus:border-purple-500 focus:outline-none focus:ring-2 focus:ring-purple-500/20 transition-all duration-300\"\n                    required\n                  />\n                </div>\n\n                <div>\n                  <label\n                    htmlFor=\"message\"\n                    className=\"block text-sm font-medium text-gray-300 mb-2\"\n                  >\n                    Message\n                  </label>\n                  <textarea\n                    id=\"message\"\n                    value={message}\n                    onChange={(e) => setMessage(e.target.value)}\n                    placeholder=\"Enter your message\"\n                    rows={5}\n                    className=\"w-full py-3 px-4 rounded-lg bg-neutral-700/50 border border-neutral-600 text-white placeholder-gray-400 font-medium focus:border-purple-500 focus:outline-none focus:ring-2 focus:ring-purple-500/20 transition-all duration-300 resize-none\"\n                    required\n                  />\n                </div>\n\n                <motion.button\n                  type=\"submit\"\n                  className=\"w-full bg-gradient-to-r from-purple-500 to-pink-500 text-white font-bold py-3 px-6 rounded-lg hover:from-purple-600 hover:to-pink-600 transition-all duration-300 shadow-lg hover:shadow-purple-500/25\"\n                  whileHover={{ scale: 1.02 }}\n                  whileTap={{ scale: 0.98 }}\n                  disabled={status === \"Sending...\"}\n                >\n                  {status || \"Send Message\"}\n                </motion.button>\n\n                {status && status !== \"Sending...\" && (\n                  <motion.p\n                    initial={{ opacity: 0 }}\n                    animate={{ opacity: 1 }}\n                    className={`text-center text-sm font-medium ${\n                      status.includes(\"Error\")\n                        ? \"text-red-400\"\n                        : \"text-green-400\"\n                    }`}\n                  >\n                    {status}\n                  </motion.p>\n                )}\n              </form>\n            </motion.div>\n          </div>\n        </motion.div>\n      </div>\n    </main>\n  );\n};\n\nexport default ContactForm;\n"
  },
  {
    "path": "libs/NavbarLinks.ts",
    "content": "export const NavbarLinks = [\n  {\n    text: \"Home\",\n    href: \"/\",\n  },\n  {\n    text: \"About Me\",\n    href: \"/about\",\n  },\n  {\n    text: \"Blogs\",\n    href: \"/blogs\",\n  },\n  {\n    text: \"Contact Me\",\n    href: \"/contact\",\n  },\n];\n"
  },
  {
    "path": "libs/constant.ts",
    "content": "export const Social_Icons = [\n  {\n    link: \"https://github.com/alfinpratamaa\",\n    image: \"/Github.svg\",\n    alt: \"Github\",\n  },\n  {\n    link: \"https://www.instagram.com/visfiveor5\",\n    image: \"/Instagram.svg\",\n    alt: \"Instagram\",\n  },\n  {\n    link: \"https://www.linkedin.com/in/alfinpr/\",\n    image: \"/LinkedIn.svg\",\n    alt: \"LinkedIn\",\n  },\n  {\n    link: \"https://t.me/visfiveor5\",\n    image: \"/telegram.svg\",\n    alt: \"Telegram\",\n  },\n  {\n    link: \"https://wa.me/6285175369960\",\n    image: \"/whatsapp.svg\",\n    alt: \"Whatsapp\",\n  },\n];\n\nexport const Backend_Skill = [\n  {\n    skill_name: \"Node.js\",\n    Image:\n      \"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/nodejs/nodejs-original.svg\",\n    width: 80,\n    height: 80,\n  },\n  {\n    skill_name: \"Express.js\",\n    Image:\n      \"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/express/express-original.svg\",\n    width: 80,\n    height: 80,\n  },\n  {\n    skill_name: \"NestJS\",\n    Image: \"https://img.jsdelivr.com/github.com/nestjs.png\",\n    width: 80,\n    height: 80,\n  },\n  {\n    skill_name: \"Golang\",\n    Image:\n      \"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/go/go-original.svg\",\n    width: 80,\n    height: 80,\n  },\n  {\n    skill_name: \"PHP\",\n    Image:\n      \"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/php/php-original.svg\",\n    width: 80,\n    height: 80,\n  },\n  {\n    skill_name: \"Laravel\",\n    Image: \"https://www.svgrepo.com/show/353985/laravel.svg\",\n    width: 80,\n    height: 80,\n  },\n  {\n    skill_name: \"PostgreSQL\",\n    Image:\n      \"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/postgresql/postgresql-original.svg\",\n    width: 80,\n    height: 80,\n  },\n  {\n    skill_name: \"MySQL\",\n    Image:\n      \"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/mysql/mysql-original.svg\",\n    width: 80,\n    height: 80,\n  },\n  {\n    skill_name: \"MongoDB\",\n    Image:\n      \"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/mongodb/mongodb-original.svg\",\n    width: 80,\n    height: 80,\n  },\n];\n\nexport const Frontend_skill = [\n  {\n    skill_name: \"Html 5\",\n    Image: \"/html.png\",\n    width: 80,\n    height: 80,\n  },\n  {\n    skill_name: \"Css\",\n    Image: \"/css.png\",\n    width: 80,\n    height: 80,\n  },\n  {\n    skill_name: \"Java Script\",\n    Image: \"/js.png\",\n    width: 65,\n    height: 65,\n  },\n  {\n    skill_name: \"Tailwind Css\",\n    Image: \"/tailwind.png\",\n    width: 80,\n    height: 80,\n  },\n  {\n    skill_name: \"React\",\n    Image: \"/react.png\",\n    width: 80,\n    height: 80,\n  },\n  {\n    skill_name: \"React Query\",\n    Image: \"/reactquery.png\",\n    width: 80,\n    height: 80,\n  },\n  {\n    skill_name: \"Type Script\",\n    Image: \"/ts.png\",\n    width: 80,\n    height: 80,\n  },\n  {\n    skill_name: \"Next js\",\n    Image: \"/Next.js.png\",\n    width: 80,\n    height: 80,\n  },\n];\n\nexport const tools = [\n  {\n    skill_name: \"Figma\",\n    Image: \"/Figma.svg\",\n    width: 80,\n    height: 80,\n  },\n  {\n    skill_name: \"Git\",\n    Image: \"/git.png\",\n    width: 80,\n    height: 80,\n  },\n  {\n    skill_name: \"Github\",\n    Image: \"/Github.svg\",\n    width: 80,\n    height: 80,\n  },\n  {\n    skill_name: \"VS Code\",\n    Image: \"/vscode.png\",\n    width: 80,\n    height: 80,\n  },\n  {\n    skill_name: \"Postman\",\n    Image: \"/postman.png\",\n    width: 80,\n    height: 80,\n  },\n];\n\nexport const project_data = [\n  {\n    title: \"Mangaloomp (Comics Reader)\",\n    description:\n      \"Mangaloom is a web application for reading manga online. Built with Next.js and Tailwind CSS, it features a clean UI, search functionality, and a vast collection of manga titles.\",\n    image: \"/mangaloom.png\",\n    linkDemo: \"https://mangaloom.app\",\n    linkGithub: \"https://github.com/Mangaloom/web\",\n    date: \"March 2024 - Present\",\n  },\n  {\n    title: \"E-Commerce Book Store\",\n    description:\n      \"An e-commerce website for books built with Laravel + Livewire and Tailwind CSS. Features include product browsing, shopping cart, user authentication, and admin panel for managing products and orders.\",\n    image: \"/ngabaca.png\",\n    linkDemo: \"https://ngabaca.me\",\n    linkGithub: \"https://github.com/Alfinpratamaa/ngabaca\",\n    date: \"June 2025 - August 2025\",\n  },\n  {\n    title: \"Selfgalery Web Apps\",\n    description:\n      \"Photo gallery application built with Next.js and Tailwind CSS for storing photos. Features upload, delete, and download functionality.\",\n    image: \"/selfgalery.png\",\n    linkDemo: \"https://selfgalery.vercel.app/\",\n    linkGithub: \"https://github.com/Alfinpratamaa/selfgalery\",\n    date: \"January 2024 - February 2024\",\n  },\n  {\n    title: \"Portfolio\",\n    description: \"Personal portfolio built with Next.js and Tailwind CSS\",\n    image: \"/portfolio.png\",\n    linkDemo: \"https://muhamadalfinpratama.vercel.app/\",\n    linkGithub: \"https://github.com/Alfinpratamaa/portfolio\",\n    date: \"June 2024\",\n  },\n];\n"
  },
  {
    "path": "libs/sanity.client.ts",
    "content": "import { createClient } from 'next-sanity'\n\nconst projectId = process.env.NEXT_PUBLIC_SANITY_PROJECT_ID\nconst dataset = process.env.NEXT_PUBLIC_SANITY_DATASET\nconst apiVersion = process.env.NEXT_PUBLIC_SANITY_API_VERSION\n\nexport const client = createClient({\n  projectId,\n  dataset,\n  apiVersion,\n  useCdn: true, \n})"
  },
  {
    "path": "next.config.mjs",
    "content": "/** @type {import('next').NextConfig} */\nconst nextConfig = {\n  images: {\n    remotePatterns: [\n      {\n        protocol: \"https\",\n        hostname: \"**\",\n      },\n      {\n        protocol: \"http\",\n        hostname: \"**\",\n      },\n    ],\n  },\n  output: \"standalone\",\n};\n\nexport default nextConfig;\n  "
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"out\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"next dev --turbopack\",\n    \"build\": \"next build --turbopack\",\n    \"start\": \"next start\",\n    \"lint\": \"next lint\"\n  },\n  \"dependencies\": {\n    \"@portabletext/react\": \"^4.0.1\",\n    \"@sanity/image-url\": \"^1.1.0\",\n    \"@types/mailgun-js\": \"^0.22.18\",\n    \"@types/nodemailer\": \"^7.0.1\",\n    \"@types/nodemailer-mailgun-transport\": \"^1.4.6\",\n    \"@types/react-scroll\": \"^1.8.10\",\n    \"@types/react-typing-effect\": \"^2.0.7\",\n    \"@vercel/analytics\": \"^1.5.0\",\n    \"clsx\": \"^2.1.1\",\n    \"framer-motion\": \"^12.23.12\",\n    \"mailgun-js\": \"^0.22.0\",\n    \"next\": \"^15.5.2\",\n    \"next-sanity\": \"^10.0.15\",\n    \"next-sitemap\": \"^4.2.3\",\n    \"nextjs-toploader\": \"^3.8.16\",\n    \"nodemailer\": \"^7.0.5\",\n    \"nodemailer-mailgun-transport\": \"^2.1.5\",\n    \"react\": \"^19.1.1\",\n    \"react-dom\": \"^19.1.1\",\n    \"react-icons\": \"^5.5.0\",\n    \"react-scroll\": \"^1.9.3\",\n    \"react-tippy\": \"^1.4.0\",\n    \"react-type-animation\": \"^3.2.0\",\n    \"sharp\": \"^0.34.3\"\n  },\n  \"devDependencies\": {\n    \"@react-three/drei\": \"^10.7.4\",\n    \"@react-three/fiber\": \"^9.3.0\",\n    \"@tailwindcss/postcss\": \"^4.1.12\",\n    \"@types/node\": \"^24.3.0\",\n    \"@types/react\": \"^19.1.11\",\n    \"@types/react-dom\": \"^19.1.8\",\n    \"autoprefixer\": \"^10.4.21\",\n    \"eslint\": \"^9.34.0\",\n    \"eslint-config-next\": \"^15.5.2\",\n    \"postcss\": \"^8.5.6\",\n    \"react-intersection-observer\": \"^9.16.0\",\n    \"tailwindcss\": \"^4.1.12\",\n    \"three\": \"^0.179.1\",\n    \"typescript\": \"^5.9.2\"\n  },\n  \"engines\": {\n    \"node\": \">=20.x\"\n  }\n}\n"
  },
  {
    "path": "postcss.config.mjs",
    "content": "export default {\n  plugins: {\n    \"@tailwindcss/postcss\": {},\n  },\n};\n"
  },
  {
    "path": "provider/ClientProvicer.tsx",
    "content": "\"use client\";\n\nimport NextTopLoader from \"nextjs-toploader\";\n\nfunction ClientProvider() {\n  return (\n    <>\n      <NextTopLoader\n        showSpinner={false}\n        crawlSpeed={200}\n        height={3}\n        crawl={true}\n        color=\"transparent\"\n      />\n\n      <style jsx global>{`\n        #nprogress .bar {\n          background: linear-gradient(to right, #9333ea, #fb923c) !important;\n        }\n      `}</style>\n    </>\n  );\n}\n\nexport { ClientProvider };\n"
  },
  {
    "path": "provider/SkillData.tsx",
    "content": "'use client';\nimport { useInView } from 'react-intersection-observer';\nimport { motion } from 'framer-motion';\nimport Image from 'next/image';\ninterface Props {\n    src: string;\n    width: number;\n    height: number;\n    index: number;\n    name?: string;\n}\nconst SkillData = ({ src, width, height, index, name }: Props) => {\n    const [ref, inView] = useInView({\n        triggerOnce: true\n    })\n\n    const imageVariant = {\n        hidden: {\n            opacity: 0,\n        },\n        visible: {\n            opacity: 1\n        }\n    }\n\n    const animationDelay = 0.3\n    return (\n        <motion.div\n            ref={ref}\n            initial=\"hidden\"\n            variants={imageVariant}\n            animate={inView ? \"visible\" : \"hidden\"}\n            custom={index}\n            transition={{ delay: index * animationDelay }}\n\n        >\n            <Image src={src} width={width} height={height} alt={name || \"image err\"} />\n        </motion.div>\n    )\n}\n\nexport default SkillData"
  },
  {
    "path": "tailwind.config.js",
    "content": "/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n  darkMode: \"class\",\n  content: [\n    \"./app/**/*.{js,ts,jsx,tsx,mdx}\",\n    \"./pages/**/*.{js,ts,jsx,tsx,mdx}\",\n    \"./components/**/*.{js,ts,jsx,tsx,mdx}\",\n\n    // Or if using `src` directory:\n    \"./src/**/*.{js,ts,jsx,tsx,mdx}\",\n  ],\n  theme: {\n    container: {\n      padding: {\n        DEFAULT: \"1rem\",\n        sm: \"2rem\",\n        lg: \"4rem\",\n        xl: \"15rem\",\n        \"2xl\": \"22rem\",\n      },\n    },\n    extend: {\n      backgroundImage: {\n        \"gradient-radial\": \"radial-gradient(var(--tw-gradient-stops))\",\n        \"gradient-conic\":\n          \"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))\",\n      },\n      backgroundColor: {\n        primary: \"#DD7C7F\",\n      },\n    },\n  },\n  plugins: [],\n};\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"lib\": [\n      \"dom\",\n      \"dom.iterable\",\n      \"esnext\"\n    ],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"bundler\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"incremental\": true,\n    \"plugins\": [\n      {\n        \"name\": \"next\"\n      }\n    ],\n    \"paths\": {\n      \"@/*\": [\n        \"./*\"\n      ]\n    },\n    \"target\": \"ES2017\"\n  },\n  \"include\": [\n    \"next-env.d.ts\",\n    \"**/*.ts\",\n    \"**/*.tsx\",\n    \".next/types/**/*.ts\"\n  ],\n  \"exclude\": [\n    \"node_modules\"\n  ]\n}\n"
  },
  {
    "path": "types/index.ts",
    "content": "export type ProjectFrontmatter = {\n  slug?: string;\n  title: string;\n  publishedAt?: string;\n  lastUpdated?: string;\n  description: string;\n  category?: string;\n  techs?: string;\n  banner?: string;\n  link?: string;\n  github?: string;\n  youtube?: string;\n};\n"
  }
]