Compare commits
No commits in common. "master" and "version-13" have entirely different histories.
master
...
version-13
|
|
@ -1,4 +1,3 @@
|
||||||
{
|
{
|
||||||
"extends": "next/core-web-vitals"
|
"extends": "next/core-web-vitals"
|
||||||
}
|
}
|
||||||
// TODO: FIX ESLINT ERRORS
|
|
||||||
1
.vscode/settings.json
vendored
|
|
@ -11,7 +11,6 @@
|
||||||
"editor.tabSize": 2
|
"editor.tabSize": 2
|
||||||
},
|
},
|
||||||
"prettier.singleAttributePerLine": false,
|
"prettier.singleAttributePerLine": false,
|
||||||
"npm-scripts.showStartNotification": false,
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
5
LICENSE
|
|
@ -1,7 +1,4 @@
|
||||||
korobtsev-NextJS01 - A Next.js Portfolio Project
|
GNU GENERAL PUBLIC LICENSE
|
||||||
Copyright (C) 2025 Alvalen Bilyunazra <alvalen.shafel04@gmail.com>
|
|
||||||
|
|
||||||
GNU GENERAL PUBLIC LICENSE
|
|
||||||
Version 3, 29 June 2007
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
|
|
||||||
59
README.md
|
|
@ -1,29 +1,35 @@
|
||||||
# Мой личный сайт на основе Korobtsev Porto 2
|
# Alvalens Porto 2
|
||||||
|
|
||||||
Проект построен на базе шаблона Korobtsev Porto 2. Изменения будут перечислены в соответствующем разделе.
|

|
||||||
|
|
||||||
## Фишки
|
This is my debut project utilizing Next.js, a portfolio website featuring four primary sections: Home, About, Projects, and Contact. The site is brought to life with animations powered by Framer Motion, enhanced page transitions with Fullpage.js, and styled using elements of Tailwind CSS. This project also read list of project data from a JSON file. It serves as a showcase of my web development skills and represents my initial foray into web development with Next.js.
|
||||||
|
|
||||||
* **Плавная анимация:** Сквозная прокрутка страниц и прокрутка анимации.
|
  
|
||||||
* **Плавный переход страниц:** Плавный переход страницы с предварительной выборкой функции NEXTJS.
|
|
||||||
* **Динамические данные:** Получает данные проекта из файла JSON.
|
|
||||||
|
|
||||||
## Установка
|
## Features
|
||||||
|
|
||||||
|
* **Smooth Animation:** Smooth page scroll and scroll trigger animation.
|
||||||
|
* **Smooth Page Transition:** Smooth page transition with prefetching feature of next js.
|
||||||
|
* **Dynamic Data:** Read the project data from JSON file instead of directly implemented the data.
|
||||||
|
* **Intelliticks chat widget:** Chat with owner of the web realtime.
|
||||||
|
* **Spotify realtime widget:** Show what song youre playing in this web.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Ensure you have [pnpm](https://pnpm.io/) installed. If not, you can install it using:
|
||||||
|
|
||||||
1. Убедитесь, что у вас установлен [PNPM](https://pnpm.io/). Если нет, вы можете установить его, используя:
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
npm install -g pnpm
|
npm install -g pnpm
|
||||||
```
|
```
|
||||||
2. Склонируйте репозиторий:
|
2. Clone the repository:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
git clone https://dev.gossys.dev/v.korobtsev/korobtsev-NextJS01.git
|
git clone https://github.com/Alvalens/Alvalens-porto-2-nextJs.git
|
||||||
```
|
```
|
||||||
3. Navigate to the project directory:
|
3. Navigate to the project directory:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cd korobtsev-NextJS01
|
cd Alvalens-porto-2-nextJs
|
||||||
```
|
```
|
||||||
4. Install dependencies:
|
4. Install dependencies:
|
||||||
|
|
||||||
|
|
@ -35,6 +41,33 @@
|
||||||
```shell
|
```shell
|
||||||
pnpm next dev
|
pnpm next dev
|
||||||
```
|
```
|
||||||
|
6. Update the Intelliticks chat widget script in components/Chat.jsx, with your own
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
"use client"
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
// Component for Intelliticks chat widget
|
||||||
|
const Chat = () => {
|
||||||
|
useEffect(() => {
|
||||||
|
// Replace the Intelliticks script here
|
||||||
|
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Chat;
|
||||||
|
```
|
||||||
|
7. Set env.local by copying env.example and fill the variable
|
||||||
|
|
||||||
|
```
|
||||||
|
NEXT_PUBLIC_SPOTIFY_CLIENT_ID=
|
||||||
|
NEXT_PUBLIC_SPOTIFY_CLIENT_SECRET=
|
||||||
|
NEXT_PUBLIC_SPOTIFY_REFRESH_TOKEN=
|
||||||
|
```
|
||||||
|
|
||||||
|
for further reference on how to setup spotify api you can check this [link ](https://leerob.io/blog/spotify-api-nextjs "leerob blog")
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|
@ -79,5 +112,3 @@ Contributions are welcome! If you find any issues or have suggestions, feel free
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is licensed under the GPL-3.0 License see the [LICENSE](LICENSE) file for details.
|
This project is licensed under the GPL-3.0 License see the [LICENSE](LICENSE) file for details.
|
||||||
|
|
||||||
Copyright (C) 2025 Alvalen Shafelbilyunazra
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
export default function Loading() {
|
export default function Loading() {
|
||||||
return (
|
return (
|
||||||
<div className="fixed top-0 left-0 flex justify-center items-center h-screen w-screen bg-gray-300 z-[999]">
|
<>
|
||||||
<h1 className="text-4xl text-gray-900">Загрузка...</h1>
|
(
|
||||||
</div>
|
<div className="fixed top-0 left-0 flex justify-center items-center h-screen w-screen bg-gray-300 z-[999]">
|
||||||
|
<h1 className="text-4xl text-gray-900">Loading...</h1>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,19 +5,20 @@ import Image from "next/legacy/image";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
// Компоненты
|
// components
|
||||||
import Button from "@/components/Button";
|
import Button from "@/components/Button";
|
||||||
import Me from "@/public/image/me.jpeg";
|
import Me from "@/public/image/me.jpg";
|
||||||
import MeAbout from "@/public/image/me7.jpg";
|
import MeAbout from "@/public/image/me2.jpg";
|
||||||
import Setup from "@/public/image/setup.jpg";
|
import Setup from "@/public/image/setup.jpg";
|
||||||
import ProjectAll from "@/public/image/projects.png";
|
import ProjectAll from "@/public/image/projects.png";
|
||||||
import Hr from "@/components/Hr";
|
import Hr from "@/components/Hr";
|
||||||
// Значки
|
// icons
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { faGithub } from "@fortawesome/free-brands-svg-icons";
|
import { faGithub } from "@fortawesome/free-brands-svg-icons";
|
||||||
|
import { faInstagram } from "@fortawesome/free-brands-svg-icons";
|
||||||
|
import { faLinkedin } from "@fortawesome/free-brands-svg-icons";
|
||||||
|
import { faDiscord } from "@fortawesome/free-brands-svg-icons";
|
||||||
import { faEnvelope } from "@fortawesome/free-solid-svg-icons";
|
import { faEnvelope } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { faVk } from "@fortawesome/free-brands-svg-icons";
|
|
||||||
import { faTelegram } from "@fortawesome/free-brands-svg-icons";
|
|
||||||
|
|
||||||
const MyPage = () => {
|
const MyPage = () => {
|
||||||
const fullpageOptions = {
|
const fullpageOptions = {
|
||||||
|
|
@ -26,6 +27,7 @@ const MyPage = () => {
|
||||||
licenseKey: "gplv3-license",
|
licenseKey: "gplv3-license",
|
||||||
menu: "#sidebar",
|
menu: "#sidebar",
|
||||||
lockAnchors: false,
|
lockAnchors: false,
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -42,14 +44,14 @@ const MyPage = () => {
|
||||||
transition={{
|
transition={{
|
||||||
type: "spring",
|
type: "spring",
|
||||||
}}>
|
}}>
|
||||||
<div className="block md:hidden col-span-1 mx-auto my-5">
|
<div className="block md:hidden col-span-1 mx-auto my-10">
|
||||||
<div className="bg-slate-500 rounded h-40 w-40 grayscale hover:grayscale-0 transition-all ease duration-300">
|
<div className="bg-slate-500 rounded-full h-60 w-60 grayscale hover:grayscale-0 transition-all ease duration-300">
|
||||||
<Image
|
<Image
|
||||||
src={Me}
|
src={Me}
|
||||||
width={500}
|
width={500}
|
||||||
height={500}
|
height={500}
|
||||||
className="rounded w-half h-half object-cover "
|
className="rounded-full w-full h-full object-cover "
|
||||||
alt="V.Korobtsev"
|
alt="Alvalens"
|
||||||
placeholder="blur"
|
placeholder="blur"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -62,7 +64,7 @@ const MyPage = () => {
|
||||||
delay: 0.2,
|
delay: 0.2,
|
||||||
type: "spring",
|
type: "spring",
|
||||||
}}>
|
}}>
|
||||||
Виталий Коробцев
|
Alvalen Shafel
|
||||||
</motion.h3>
|
</motion.h3>
|
||||||
<motion.h1
|
<motion.h1
|
||||||
className="text-black text-5xl md:text-6xl lg:text-7xl 2xl:text-8xl font-bold my-2 md:my-5"
|
className="text-black text-5xl md:text-6xl lg:text-7xl 2xl:text-8xl font-bold my-2 md:my-5"
|
||||||
|
|
@ -72,7 +74,7 @@ const MyPage = () => {
|
||||||
delay: 0.3,
|
delay: 0.3,
|
||||||
type: "spring",
|
type: "spring",
|
||||||
}}>
|
}}>
|
||||||
Главное
|
Full Stack Developer
|
||||||
</motion.h1>
|
</motion.h1>
|
||||||
<motion.p
|
<motion.p
|
||||||
className="title text-md 2xl:text-xl mt-4 tracking-wider text-gray-500 leading-[1.7rem]"
|
className="title text-md 2xl:text-xl mt-4 tracking-wider text-gray-500 leading-[1.7rem]"
|
||||||
|
|
@ -82,8 +84,12 @@ const MyPage = () => {
|
||||||
delay: 0.4,
|
delay: 0.4,
|
||||||
type: "spring",
|
type: "spring",
|
||||||
}}>
|
}}>
|
||||||
Привет! Я Виталий Коробцев. Создатель "Гибридной Офисной Системы". Под моим руководством (или с моим участием) создано множество крупных проектов. На данном сайте я решил собрать некоторые из них и рассказать о себе и своих проектах - завершенных, текущих, планируемых.
|
A junior fullstack developer who
|
||||||
|
currently focused on Web Development.
|
||||||
|
Other than that, I also interested in
|
||||||
|
UX/UI Design, Mobile and AI Development.
|
||||||
|
I love to learn new things and always
|
||||||
|
open to new opportunities.
|
||||||
</motion.p>
|
</motion.p>
|
||||||
<motion.div
|
<motion.div
|
||||||
className="buttons flex flex-row justify-center items-center space-x-4 mt-10"
|
className="buttons flex flex-row justify-center items-center space-x-4 mt-10"
|
||||||
|
|
@ -94,10 +100,17 @@ const MyPage = () => {
|
||||||
type: "spring",
|
type: "spring",
|
||||||
}}>
|
}}>
|
||||||
<Button variation="primary">
|
<Button variation="primary">
|
||||||
<a href="#projects">Проекты</a>
|
<Link
|
||||||
|
href={"/docs/cv.pdf"}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
locale={false}
|
||||||
|
download>
|
||||||
|
Download CV
|
||||||
|
</Link>
|
||||||
</Button>
|
</Button>
|
||||||
<Button variation="secondary">
|
<Button variation="secondary">
|
||||||
<a href="#contact">Связаться</a>
|
<a href="#contact">Contact Me</a>
|
||||||
</Button>
|
</Button>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
@ -115,7 +128,7 @@ const MyPage = () => {
|
||||||
width={400}
|
width={400}
|
||||||
height={550}
|
height={550}
|
||||||
placeholder="blur"
|
placeholder="blur"
|
||||||
alt="Korobtsev"
|
alt="Alvalens"
|
||||||
className="rounded-full w-full h-full object-cover"
|
className="rounded-full w-full h-full object-cover"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -147,7 +160,7 @@ const MyPage = () => {
|
||||||
src={MeAbout}
|
src={MeAbout}
|
||||||
layout="fill"
|
layout="fill"
|
||||||
className="object-cover"
|
className="object-cover"
|
||||||
alt="Korobtsev"
|
alt="Alvalens"
|
||||||
placeholder="blur"
|
placeholder="blur"
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
@ -161,7 +174,7 @@ const MyPage = () => {
|
||||||
delay: 0.1,
|
delay: 0.1,
|
||||||
type: "spring",
|
type: "spring",
|
||||||
}}>
|
}}>
|
||||||
Обо мне
|
About Me
|
||||||
</motion.h1>
|
</motion.h1>
|
||||||
<Hr />
|
<Hr />
|
||||||
<motion.p
|
<motion.p
|
||||||
|
|
@ -172,7 +185,8 @@ const MyPage = () => {
|
||||||
delay: 0.2,
|
delay: 0.2,
|
||||||
type: "spring",
|
type: "spring",
|
||||||
}}>
|
}}>
|
||||||
Немного обо мне и моих интересах.
|
A brief introduction about me and my
|
||||||
|
interest.
|
||||||
</motion.p>
|
</motion.p>
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ y: 40, opacity: 0 }}
|
initial={{ y: 40, opacity: 0 }}
|
||||||
|
|
@ -182,7 +196,9 @@ const MyPage = () => {
|
||||||
type: "spring",
|
type: "spring",
|
||||||
}}>
|
}}>
|
||||||
<Button variation="primary">
|
<Button variation="primary">
|
||||||
<Link href="/about">Читать</Link>
|
<Link href="/about">
|
||||||
|
Learn More
|
||||||
|
</Link>
|
||||||
</Button>
|
</Button>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -213,7 +229,7 @@ const MyPage = () => {
|
||||||
src={ProjectAll}
|
src={ProjectAll}
|
||||||
layout="fill"
|
layout="fill"
|
||||||
className="object-cover"
|
className="object-cover"
|
||||||
alt="Рабочее место"
|
alt="Alvalens Setup"
|
||||||
placeholder="blur"
|
placeholder="blur"
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
@ -227,7 +243,7 @@ const MyPage = () => {
|
||||||
delay: 0.1,
|
delay: 0.1,
|
||||||
type: "spring",
|
type: "spring",
|
||||||
}}>
|
}}>
|
||||||
Проекты
|
My Projects
|
||||||
</motion.h1>
|
</motion.h1>
|
||||||
<Hr />
|
<Hr />
|
||||||
<motion.p
|
<motion.p
|
||||||
|
|
@ -238,10 +254,11 @@ const MyPage = () => {
|
||||||
delay: 0.2,
|
delay: 0.2,
|
||||||
type: "spring",
|
type: "spring",
|
||||||
}}>
|
}}>
|
||||||
Некоторые из проектов с моим учаcтием {" "}
|
This is some of my projects that I have
|
||||||
|
done{" "}
|
||||||
<span className="bg-transparent md:bg-gray-100 bg-opacity-50 xl:bg-transparent">
|
<span className="bg-transparent md:bg-gray-100 bg-opacity-50 xl:bg-transparent">
|
||||||
{" "}
|
{" "}
|
||||||
или под моим руководством.
|
and currently working on.
|
||||||
</span>
|
</span>
|
||||||
</motion.p>
|
</motion.p>
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|
@ -252,7 +269,9 @@ const MyPage = () => {
|
||||||
type: "spring",
|
type: "spring",
|
||||||
}}>
|
}}>
|
||||||
<Button variation="primary">
|
<Button variation="primary">
|
||||||
<Link href="/projects">Просмотреть</Link>
|
<Link href="/projects">
|
||||||
|
Learn More
|
||||||
|
</Link>
|
||||||
</Button>
|
</Button>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -283,7 +302,7 @@ const MyPage = () => {
|
||||||
src={Setup}
|
src={Setup}
|
||||||
layout="fill"
|
layout="fill"
|
||||||
className="object-cover"
|
className="object-cover"
|
||||||
alt="Рабочее место"
|
alt="Alvalens Setup"
|
||||||
placeholder="blur"
|
placeholder="blur"
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
@ -297,7 +316,7 @@ const MyPage = () => {
|
||||||
delay: 0.1,
|
delay: 0.1,
|
||||||
type: "spring",
|
type: "spring",
|
||||||
}}>
|
}}>
|
||||||
Связаться
|
Get In Touch
|
||||||
</motion.h1>
|
</motion.h1>
|
||||||
<Hr />
|
<Hr />
|
||||||
<motion.p
|
<motion.p
|
||||||
|
|
@ -308,9 +327,9 @@ const MyPage = () => {
|
||||||
delay: 0.2,
|
delay: 0.2,
|
||||||
type: "spring",
|
type: "spring",
|
||||||
}}>
|
}}>
|
||||||
Не стесняйтесь связаться со мной,{" "}
|
Feel free to contact me if you have any{" "}
|
||||||
<span className="bg-transparent md:bg-gray-100 bg-opacity-50 xl:bg-transparent">
|
<span className="bg-transparent md:bg-gray-100 bg-opacity-50 xl:bg-transparent">
|
||||||
можем посотрудничать и реализовать Ваш проект.
|
questions or just want to say hi.
|
||||||
</span>
|
</span>
|
||||||
</motion.p>
|
</motion.p>
|
||||||
<motion.p
|
<motion.p
|
||||||
|
|
@ -321,14 +340,14 @@ const MyPage = () => {
|
||||||
delay: 0.3,
|
delay: 0.3,
|
||||||
type: "spring",
|
type: "spring",
|
||||||
}}>
|
}}>
|
||||||
<a href="mailto:vk@gossys.dev?subject=Я с сайта Korobtsev.ru&body=Приветствую Виталий!">
|
<a href="mailto:Alvalen.shafel04@gmail.com?subject=Hello&body=Hello Alvalens,">
|
||||||
vk@gossys.dev
|
alvalen.shafel04@gmail.com
|
||||||
</a>
|
</a>
|
||||||
</motion.p>
|
</motion.p>
|
||||||
{/* icons */}
|
{/* icons */}
|
||||||
<div className="flex justify-center items-center space-x-4">
|
<div className="flex justify-center items-center space-x-4">
|
||||||
<motion.a
|
<motion.a
|
||||||
href="mailto:vk@gossys.dev?subject=Я с сайта Korobtsev.ru&body=Приветствую Виталий!"
|
href="mailto:Alvalen.shafel04@gmail.com?subject=Hello&body=Hello Alvalens,"
|
||||||
className="flex justify-center items-center bg-gray-700 w-14 h-14 rounded-full text-gray-100 hover:bg-gray-400 transition-all ease-in-out duration-300"
|
className="flex justify-center items-center bg-gray-700 w-14 h-14 rounded-full text-gray-100 hover:bg-gray-400 transition-all ease-in-out duration-300"
|
||||||
initial={{ y: 40, opacity: 0 }}
|
initial={{ y: 40, opacity: 0 }}
|
||||||
whileInView={{ y: 0, opacity: 1 }}
|
whileInView={{ y: 0, opacity: 1 }}
|
||||||
|
|
@ -336,11 +355,14 @@ const MyPage = () => {
|
||||||
y: { delay: 0.1 },
|
y: { delay: 0.1 },
|
||||||
opacity: { delay: 0.2 },
|
opacity: { delay: 0.2 },
|
||||||
}}>
|
}}>
|
||||||
<FontAwesomeIcon icon={faEnvelope} className="text-3xl" />
|
<FontAwesomeIcon
|
||||||
|
icon={faEnvelope}
|
||||||
|
className="text-3xl"
|
||||||
|
/>
|
||||||
</motion.a>
|
</motion.a>
|
||||||
|
|
||||||
<motion.a
|
<motion.a
|
||||||
href="https://dev.gossys.dev/v.korobtsev/"
|
href="https://github.com/Alvalens"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="flex justify-center items-center bg-gray-700 w-14 h-14 rounded-full text-gray-100 hover:bg-gray-400 transition-all ease-in-out duration-300"
|
className="flex justify-center items-center bg-gray-700 w-14 h-14 rounded-full text-gray-100 hover:bg-gray-400 transition-all ease-in-out duration-300"
|
||||||
|
|
@ -350,10 +372,13 @@ const MyPage = () => {
|
||||||
y: { delay: 0.2 },
|
y: { delay: 0.2 },
|
||||||
opacity: { delay: 0.3 },
|
opacity: { delay: 0.3 },
|
||||||
}}>
|
}}>
|
||||||
<FontAwesomeIcon icon={faGithub} className="text-3xl" />
|
<FontAwesomeIcon
|
||||||
|
icon={faGithub}
|
||||||
|
className="text-3xl"
|
||||||
|
/>
|
||||||
</motion.a>
|
</motion.a>
|
||||||
<motion.a
|
<motion.a
|
||||||
href="https://vk.ru/Miguelle74/"
|
href="https://www.instagram.com/alvalens_/"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="flex justify-center items-center bg-gray-700 w-14 h-14 rounded-full text-gray-100 hover:bg-gray-400 transition-all ease-in-out duration-300"
|
className="flex justify-center items-center bg-gray-700 w-14 h-14 rounded-full text-gray-100 hover:bg-gray-400 transition-all ease-in-out duration-300"
|
||||||
|
|
@ -364,12 +389,12 @@ const MyPage = () => {
|
||||||
opacity: { delay: 0.4 },
|
opacity: { delay: 0.4 },
|
||||||
}}>
|
}}>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={faVk}
|
icon={faInstagram}
|
||||||
className="text-3xl"
|
className="text-3xl"
|
||||||
/>
|
/>
|
||||||
</motion.a>
|
</motion.a>
|
||||||
<motion.a
|
<motion.a
|
||||||
href="https://t.me/Gosys_Maestro/"
|
href="https://www.linkedin.com/in/alvalen-shafel-8a081a254/"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="flex justify-center items-center bg-gray-700 w-14 h-14 rounded-full text-gray-100 hover:bg-gray-400 transition-all ease-in-out duration-300"
|
className="flex justify-center items-center bg-gray-700 w-14 h-14 rounded-full text-gray-100 hover:bg-gray-400 transition-all ease-in-out duration-300"
|
||||||
|
|
@ -379,8 +404,26 @@ const MyPage = () => {
|
||||||
y: { delay: 0.4 },
|
y: { delay: 0.4 },
|
||||||
opacity: { delay: 0.5 },
|
opacity: { delay: 0.5 },
|
||||||
}}>
|
}}>
|
||||||
<FontAwesomeIcon icon={faTelegram}
|
<FontAwesomeIcon
|
||||||
className="text-3xl" />
|
icon={faLinkedin}
|
||||||
|
className="text-3xl"
|
||||||
|
/>
|
||||||
|
</motion.a>
|
||||||
|
<motion.a
|
||||||
|
href="https://discordapp.com/users/bloody#6118"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="flex justify-center items-center bg-gray-700 w-14 h-14 rounded-full text-gray-100 hover:bg-gray-400 transition-all ease-in-out duration-300"
|
||||||
|
initial={{ opacity: 0, y: 40 }}
|
||||||
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{
|
||||||
|
y: { delay: 0.5 },
|
||||||
|
opacity: { delay: 0.6 },
|
||||||
|
}}>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faDiscord}
|
||||||
|
className="text-3xl"
|
||||||
|
/>
|
||||||
</motion.a>
|
</motion.a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
import Card from "./spotify/card";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import Me1 from "@/public/image/me1.jpeg";
|
import Me1 from "@/public/image/me1.jpg";
|
||||||
import Me2 from "@/public/image/me2.jpeg";
|
import Me2 from "@/public/image/me2.jpg";
|
||||||
import Me3 from "@/public/image/me4.jpg";
|
import Me3 from "@/public/image/me3.jpg";
|
||||||
import Hr from "@/components/Hr";
|
import Hr from "@/components/Hr";
|
||||||
|
|
||||||
function Title() {
|
function Title() {
|
||||||
|
|
@ -10,7 +11,7 @@ function Title() {
|
||||||
<div className="mt-10 flex flex-col justify-start items-center w-full pl-10 md:pl-32">
|
<div className="mt-10 flex flex-col justify-start items-center w-full pl-10 md:pl-32">
|
||||||
<div className="flex justify-center items-center flex-col my-5 self-start ">
|
<div className="flex justify-center items-center flex-col my-5 self-start ">
|
||||||
<Hr variant="long"></Hr>
|
<Hr variant="long"></Hr>
|
||||||
<h1 className="text-3xl font-bold mt-3">Обо мне</h1>
|
<h1 className="text-3xl font-bold mt-3">Who Am I?</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -34,7 +35,7 @@ export default function About() {
|
||||||
className="w-full h-full">
|
className="w-full h-full">
|
||||||
<Image
|
<Image
|
||||||
src={Me1}
|
src={Me1}
|
||||||
alt="Korobtsev.ru"
|
alt="Alvalens"
|
||||||
layout="fill"
|
layout="fill"
|
||||||
objectFit="cover"
|
objectFit="cover"
|
||||||
placeholder="blur"
|
placeholder="blur"
|
||||||
|
|
@ -57,7 +58,7 @@ export default function About() {
|
||||||
className="w-full h-full">
|
className="w-full h-full">
|
||||||
<Image
|
<Image
|
||||||
src={Me2}
|
src={Me2}
|
||||||
alt="Korobtsev"
|
alt="Alvalens"
|
||||||
layout="fill"
|
layout="fill"
|
||||||
objectFit="cover"
|
objectFit="cover"
|
||||||
placeholder="blur"
|
placeholder="blur"
|
||||||
|
|
@ -82,7 +83,7 @@ export default function About() {
|
||||||
className="w-full h-full">
|
className="w-full h-full">
|
||||||
<Image
|
<Image
|
||||||
src={Me3}
|
src={Me3}
|
||||||
alt="Korobtsev"
|
alt="Alvalens"
|
||||||
layout="fill"
|
layout="fill"
|
||||||
objectFit="cover"
|
objectFit="cover"
|
||||||
placeholder="blur"
|
placeholder="blur"
|
||||||
|
|
@ -107,37 +108,43 @@ export default function About() {
|
||||||
type: "spring",
|
type: "spring",
|
||||||
}}>
|
}}>
|
||||||
<h2 className="text-2xl font-bold tracking-wider mb-3">
|
<h2 className="text-2xl font-bold tracking-wider mb-3">
|
||||||
Виталий Коробцев
|
Alvalen Shafelbilyunazra
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-gray-600 text-justify title text-lg">
|
<p className="text-gray-600 text-justify title text-lg">
|
||||||
Привет! Я Виталий Коробцев,
|
Hey there, I’m Alvalen Shafelbilyunazra, a
|
||||||
<span className="text-black font-medium">
|
<span className="text-black font-medium">
|
||||||
{" "}
|
{" "}
|
||||||
разработчик, архитектор,
|
tech enthusiast
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
with a growing expertise in
|
and
|
||||||
<span className="text-black font-medium">
|
<span className="text-black font-medium">
|
||||||
{" "}
|
{" "}
|
||||||
Artificial Intelligence.
|
aspiring Web Developer.
|
||||||
</span>{" "}
|
|
||||||
Hailing from Pasuruan, East Java, Indonesia, I’m currently
|
|
||||||
pursuing my degree in{" "}
|
|
||||||
<span className="text-black font-medium">
|
|
||||||
Computer Science
|
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
|
Originating from Pasuruan, East Java, Indonesia,
|
||||||
|
I’m currently embarking on a fascinating journey
|
||||||
at{" "}
|
at{" "}
|
||||||
<span className="text-black font-medium">
|
<span className="text-black font-medium">
|
||||||
Universitas Negeri Malang.
|
Universitas Negeri Malang,
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
My work bridges modern web technologies and intelligent systems—from
|
where I’m pursuing my degree in{" "}
|
||||||
building responsive, scalable websites to exploring generative AI
|
<span className="text-black font-medium">
|
||||||
and LLM-based solutions. Beyond development, I stay curious about
|
Informatics Engineering.
|
||||||
design and emerging technologies. In today’s ever-changing
|
</span>{" "}
|
||||||
digital landscape, I believe being a
|
My passion for technology and coding knows no bounds.
|
||||||
<span className="text-black font-medium"> lifelong learner</span> is
|
Beyond the world of coding, I find myself deeply
|
||||||
essential. Let’s connect and explore the evolving intersection
|
immersed in the realms of design, Game Development, and
|
||||||
of web and AI together!
|
the fascinating universe of AI. I believe that in
|
||||||
|
today’s fast-paced digital landscape, being a
|
||||||
|
<span className="text-black font-medium">
|
||||||
|
{" "}
|
||||||
|
lifelong learner
|
||||||
|
</span>{" "}
|
||||||
|
is not just a choice, but a necessity. Let’s
|
||||||
|
connect and explore this ever-evolving world of tech
|
||||||
|
together!
|
||||||
</p>
|
</p>
|
||||||
|
<Card />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
25
app/about/components/about/spotify/animation.jsx
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
|
||||||
|
const PlayingAnimation = () => {
|
||||||
|
return (
|
||||||
|
<div className="relative flex justify-between w-4 h-8 space-x-1">
|
||||||
|
<motion.span
|
||||||
|
className="w-1 h-full bg-gray-500 rounded-full"
|
||||||
|
animate={{ scaleY: [0.3, 1, 0.5, 0.75, 0.5] }}
|
||||||
|
transition={{ duration: 2.2, repeat: Infinity }}
|
||||||
|
/>
|
||||||
|
<motion.span
|
||||||
|
className="w-1 h-full bg-gray-500 rounded-full"
|
||||||
|
animate={{ scaleY: [0.3, 1, 0.5, 0.75, 0.5] }}
|
||||||
|
transition={{ duration: 2.2, repeat: Infinity, delay: -2 }}
|
||||||
|
/>
|
||||||
|
<motion.span
|
||||||
|
className="w-1 h-full bg-gray-500 rounded-full"
|
||||||
|
animate={{ scaleY: [0.3, 1, 0.5, 0.75, 0.5] }}
|
||||||
|
transition={{ duration: 2.2, repeat: Infinity, delay: -3.7 }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PlayingAnimation;
|
||||||
80
app/about/components/about/spotify/card.jsx
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import getNowPlayingItem from "./fetch";
|
||||||
|
import PlayingAnimation from "./animation";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
import { faSpotify } from "@fortawesome/free-brands-svg-icons";
|
||||||
|
import Image from "next/image";
|
||||||
|
|
||||||
|
const Card = () => {
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [result, setResult] = useState({});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchData = async () => {
|
||||||
|
const results = await Promise.all([getNowPlayingItem()]);
|
||||||
|
console.log(results);
|
||||||
|
setResult(results[0]);
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
fetchData(); // Fetch data immediately on component mount
|
||||||
|
|
||||||
|
const intervalId = setInterval(fetchData, 60 * 1000); // ! Fetch data every minute: temporary
|
||||||
|
|
||||||
|
return () => clearInterval(intervalId);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mt-3 flex justify-center w-full">
|
||||||
|
{loading ? (
|
||||||
|
<div className="flex justify-center mb-8">
|
||||||
|
{/* Replace this with your own loading spinner */}
|
||||||
|
<div className="loader ease-linear rounded-full border-4 border-t-4 border-black h-12 w-12 mb-4"></div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="relative w-full mb-8 grid grid-cols-2 gap-2 border-2 border-gray-500 p-4 rounded-lg backdrop-filter backdrop-blur-lg bg-white bg-opacity-30">
|
||||||
|
{result.isPlaying && (
|
||||||
|
<Image
|
||||||
|
src={result.albumImageUrl}
|
||||||
|
alt="backgroundImage"
|
||||||
|
layout="fill"
|
||||||
|
objectFit="cover"
|
||||||
|
className="z-0 opacity-20 absolute"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<div className="z-10 flex items-center">
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faSpotify}
|
||||||
|
className="text-black text-5xl me-5 md:me-1 xl:me-8"
|
||||||
|
/>
|
||||||
|
<p className="font-semibold me-5 md:me-1 xl:me-7">
|
||||||
|
{result.isPlaying
|
||||||
|
? "Now playing"
|
||||||
|
: "Currently offline"}
|
||||||
|
</p>
|
||||||
|
{result.isPlaying && <PlayingAnimation />}
|
||||||
|
</div>
|
||||||
|
{result.isPlaying && (
|
||||||
|
<div className="z-10 rounded-lg ms-5">
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<div className="overflow-hidden">
|
||||||
|
<a
|
||||||
|
href={result.songUrl}
|
||||||
|
target="_blank"
|
||||||
|
className="block font-semibold w-full truncate text-blue-500">
|
||||||
|
{result.title}
|
||||||
|
</a>
|
||||||
|
<p className="truncate text-gray-500">
|
||||||
|
{result.artist}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Card;
|
||||||
|
|
||||||
53
app/about/components/about/spotify/fetch.js
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
import querystring from "querystring";
|
||||||
|
const TOKEN_ENDPOINT = `https://accounts.spotify.com/api/token`;
|
||||||
|
const NOW_PLAYING_ENDPOINT = `https://api.spotify.com/v1/me/player/currently-playing`;
|
||||||
|
const client_id = process.env.NEXT_PUBLIC_SPOTIFY_CLIENT_ID;
|
||||||
|
const client_secret = process.env.NEXT_PUBLIC_SPOTIFY_CLIENT_SECRET;
|
||||||
|
const refresh_token = process.env.NEXT_PUBLIC_SPOTIFY_REFRESH_TOKEN;
|
||||||
|
|
||||||
|
const basic = Buffer.from(`${client_id}:${client_secret}`).toString("base64");
|
||||||
|
const getAccessToken = async () => {
|
||||||
|
const response = await fetch(TOKEN_ENDPOINT, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Basic ${basic}`,
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
},
|
||||||
|
body: querystring.stringify({
|
||||||
|
grant_type: "refresh_token",
|
||||||
|
refresh_token,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.json();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getNowPlaying = async () => {
|
||||||
|
const { access_token } = await getAccessToken();
|
||||||
|
return fetch(NOW_PLAYING_ENDPOINT, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${access_token}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default async function getNowPlayingItem() {
|
||||||
|
const response = await getNowPlaying();
|
||||||
|
if (response.status === 204 || response.status > 400) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const song = await response.json();
|
||||||
|
const albumImageUrl = song.item.album.images[0].url;
|
||||||
|
const artist = song.item.artists.map((_artist) => _artist.name).join(", ");
|
||||||
|
const isPlaying = song.is_playing;
|
||||||
|
const songUrl = song.item.external_urls.spotify;
|
||||||
|
const title = song.item.name;
|
||||||
|
|
||||||
|
return {
|
||||||
|
albumImageUrl,
|
||||||
|
artist,
|
||||||
|
isPlaying,
|
||||||
|
songUrl,
|
||||||
|
title,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1,37 +1,26 @@
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import {
|
import { faMedal } from "@fortawesome/free-solid-svg-icons";
|
||||||
faMedal,
|
import { motion } from "framer-motion";
|
||||||
faGraduationCap,
|
import Me4 from "@/public/image/me4.jpeg";
|
||||||
faTrophy,
|
|
||||||
faAward,
|
|
||||||
faChevronDown,
|
|
||||||
faChevronUp,
|
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { motion, AnimatePresence } from "framer-motion";
|
|
||||||
import { useState } from "react";
|
|
||||||
import Me4 from "@/public/image/me4.jpg";
|
|
||||||
import Me5 from "@/public/image/me5.jpg";
|
|
||||||
import Me6 from "@/public/image/me6.jpg";
|
|
||||||
|
|
||||||
function Wrapper({ children }) {
|
function Wrapper({ children }) {
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto container gap-10 p-10 grid grid-cols-1 my-10">
|
<div className="mx-auto container gap-10 p-10 grid grid-cols-1 my-10 ">
|
||||||
<motion.div
|
<motion.div
|
||||||
className="flex justify-center items-start flex-col mb-5"
|
className="flex justify-center items-start flex-col mb-5 "
|
||||||
initial={{
|
initial={{
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
y: 50,
|
x: -200,
|
||||||
}}
|
}}
|
||||||
whileInView={{
|
whileInView={{
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
y: 0,
|
x: 0,
|
||||||
}}
|
}}
|
||||||
transition={{
|
transition={{
|
||||||
delay: 0.3,
|
delay: 0.5,
|
||||||
duration: 0.8,
|
|
||||||
type: "spring",
|
type: "spring",
|
||||||
stiffness: 100,
|
|
||||||
}}>
|
}}>
|
||||||
{children}
|
{children}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
@ -40,313 +29,170 @@ function Wrapper({ children }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Education() {
|
export default function Education() {
|
||||||
const [isExpanded, setIsExpanded] = useState(false);
|
|
||||||
|
|
||||||
const achievementsByYear = {
|
|
||||||
2025: [
|
|
||||||
{
|
|
||||||
icon: faMedal,
|
|
||||||
title: "1st place (Gold Medal)",
|
|
||||||
subtitle: "Fesmaro IT Business Competition",
|
|
||||||
date: "Mar 2025",
|
|
||||||
color: "from-yellow-400 to-orange-500",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: faAward,
|
|
||||||
title: "Finalist",
|
|
||||||
subtitle: "Hackfest Build to Billion 2025",
|
|
||||||
date: "Apr 2025",
|
|
||||||
color: "from-blue-500 to-purple-600",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: faMedal,
|
|
||||||
title: "3rd place (Bronze Medal)",
|
|
||||||
subtitle: "Faculty of Engineering Most Outstanding Student",
|
|
||||||
date: "Feb 2025",
|
|
||||||
color: "from-amber-600 to-yellow-600",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
2024: [
|
|
||||||
{
|
|
||||||
icon: faTrophy,
|
|
||||||
title: "Special Award | Gold Medal | Incubation Opportunity",
|
|
||||||
subtitle: "Indonesia Inventor Day 2024 (IID)",
|
|
||||||
date: "Aug 2024",
|
|
||||||
color: "from-yellow-400 to-orange-500",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: faMedal,
|
|
||||||
title: "1st place (Gold Medal)",
|
|
||||||
subtitle: "Tech & Trade Expo 2024",
|
|
||||||
date: "Jul 2024",
|
|
||||||
color: "from-yellow-400 to-orange-500",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: faMedal,
|
|
||||||
title: "2nd place (Silver Medal)",
|
|
||||||
subtitle: "IdeaFest 2024",
|
|
||||||
date: "Jul 2024",
|
|
||||||
color: "from-slate-400 to-slate-500",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: faAward,
|
|
||||||
title: "Finalist",
|
|
||||||
subtitle: "37th National Student Science Week (PIMNAS)",
|
|
||||||
date: "Oct 2024",
|
|
||||||
color: "from-blue-500 to-purple-600",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: faMedal,
|
|
||||||
title: "3rd place (Bronze Medal)",
|
|
||||||
subtitle: "Student Digital Innovation Competition (LIDM)",
|
|
||||||
date: "Jun 2024",
|
|
||||||
color: "from-amber-600 to-yellow-600",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: faMedal,
|
|
||||||
title: "3rd place (Bronze Medal)",
|
|
||||||
subtitle: "Fesmaro UI/UX Design",
|
|
||||||
date: "Jun 2024",
|
|
||||||
color: "from-amber-600 to-yellow-600",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
2023: [
|
|
||||||
{
|
|
||||||
icon: faAward,
|
|
||||||
title: "Finalist",
|
|
||||||
subtitle: "36th National Student Science Week (PIMNAS)",
|
|
||||||
date: "Nov 2023",
|
|
||||||
color: "from-blue-500 to-purple-600",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: faMedal,
|
|
||||||
title: "3rd place (Bronze Medal)",
|
|
||||||
subtitle: "FORKAFEST 3.0 Web Development competition",
|
|
||||||
date: "Jan 2023",
|
|
||||||
color: "from-amber-600 to-yellow-600",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
// Flatten all achievements into a single array for easier limiting
|
|
||||||
const allAchievements = Object.entries(achievementsByYear)
|
|
||||||
.sort(([a], [b]) => parseInt(b) - parseInt(a))
|
|
||||||
.flatMap(([year, achievements]) =>
|
|
||||||
achievements.map((achievement) => ({ ...achievement, year }))
|
|
||||||
);
|
|
||||||
|
|
||||||
const visibleAchievements = isExpanded
|
|
||||||
? allAchievements
|
|
||||||
: allAchievements.slice(0, 6);
|
|
||||||
const hasMoreAchievements = allAchievements.length > 6;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<section className="grid gap-8 md:gap-12">
|
<section className="grid gap-8 md:gap-12">
|
||||||
{" "}
|
<div className="text-center space-y-2">
|
||||||
{/* Header */}
|
|
||||||
<motion.div
|
|
||||||
className="text-center space-y-2"
|
|
||||||
initial={{ opacity: 0, y: 30 }}
|
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
|
||||||
transition={{ duration: 0.6 }}>
|
|
||||||
<h1 className="text-3xl md:text-4xl font-bold tracking-tighter">
|
<h1 className="text-3xl md:text-4xl font-bold tracking-tighter">
|
||||||
Education
|
Education
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-muted-foreground max-w-[800px] mx-auto">
|
<p className="text-muted-foreground max-w-[800px] mx-auto">
|
||||||
Get to know more about my educational background.
|
Get to know more about my educational background.
|
||||||
</p>
|
</p>
|
||||||
</motion.div>
|
</div>
|
||||||
{/* Main Content */}
|
<div className="grid gap-8 md:gap-12">
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
{/* Education Section - Left */}
|
<div className="px-5">
|
||||||
<motion.div
|
<div className="font-medium text-lg">
|
||||||
className="px-5"
|
2022 - Present
|
||||||
initial={{ opacity: 0, x: -50 }}
|
|
||||||
whileInView={{ opacity: 1, x: 0 }}
|
|
||||||
transition={{ duration: 0.8, delay: 0.2 }}>
|
|
||||||
<div className="font-medium text-lg mb-4">2022 - Present</div>
|
|
||||||
<div>
|
|
||||||
<h2 className="font-semibold text-xl">
|
|
||||||
Universitas Negeri Malang
|
|
||||||
</h2>
|
|
||||||
<h3 className="text-md font-normal mb-3">
|
|
||||||
S1 Teknik Informatika | Computer Science
|
|
||||||
</h3>
|
|
||||||
<div className="gap-4 mb-4 flex items-stretch md:h-[300px] xl:h-[400px]">
|
|
||||||
<div className="flex-[1] transition-all duration-300 ease-in-out hover:flex-[3] group">
|
|
||||||
<Image
|
|
||||||
src={Me5}
|
|
||||||
width={400}
|
|
||||||
height={225}
|
|
||||||
alt="University"
|
|
||||||
className="rounded-lg w-full h-full object-cover grayscale group-hover:grayscale-0 transition-all duration-300 ease-in-out"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex-[1] transition-all duration-300 ease-in-out hover:flex-[3] group">
|
|
||||||
<Image
|
|
||||||
src={Me4}
|
|
||||||
width={400}
|
|
||||||
height={225}
|
|
||||||
alt="University"
|
|
||||||
className="rounded-lg w-full h-full object-cover grayscale group-hover:grayscale-0 transition-all duration-300 ease-in-out"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex-[1] transition-all duration-300 ease-in-out hover:flex-[3] group">
|
|
||||||
<Image
|
|
||||||
src={Me6}
|
|
||||||
width={400}
|
|
||||||
height={225}
|
|
||||||
alt="University"
|
|
||||||
className="rounded-lg w-full h-full object-cover grayscale group-hover:grayscale-0 transition-all duration-300 ease-in-out"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div>
|
||||||
<p className="text-gray-600 text-justify title text-lg">
|
<h2 className="font-semibold text-xl">
|
||||||
Aspiring to become a professional Software Engineer, I’m
|
Universitas Negeri Malang
|
||||||
currently pursuing my Bachelor’s degree in{" "}
|
</h2>
|
||||||
<span className="text-black font-medium">
|
<h3 className="text-md font-normal mb-3">
|
||||||
Computer Science
|
S1 Teknik Informatika | Computer Science
|
||||||
</span>{" "}
|
</h3>
|
||||||
at{" "}
|
<div className="gap-4 mb-4 flex items-stretch md:h-[300px] xl:h-[400px]">
|
||||||
<span className="text-black font-medium">
|
<div className="flex-[1] transition-all duration-300 ease-in-out hover:flex-[3] group">
|
||||||
Universitas Negeri Malang
|
<Image
|
||||||
</span>
|
src={Me4}
|
||||||
. My journey in tech is driven by curiosity and
|
width={400}
|
||||||
creativity—combining web development with cutting-edge AI
|
height={225}
|
||||||
research and implementation.
|
alt="University"
|
||||||
<br />
|
className="rounded-lg w-full h-full object-cover grayscale group-hover:grayscale-0 transition-all duration-300 ease-in-out"
|
||||||
<br />
|
|
||||||
As the{" "}
|
|
||||||
<span className="text-black font-medium">
|
|
||||||
Co-Lead of Google Developer Group on Campus (GDGoC)
|
|
||||||
</span>{" "}
|
|
||||||
at my university, I’ve led technical initiatives,
|
|
||||||
mentored peers, and helped build impactful digital solutions.
|
|
||||||
I’ve actively participated in various{" "}
|
|
||||||
<span className="text-black font-medium">
|
|
||||||
national or international technology competitions
|
|
||||||
</span>
|
|
||||||
, earning recognition through awards such as{" "}
|
|
||||||
<span className="text-black font-medium">
|
|
||||||
PIMNAS Finalist, Indonesia Inventor Day Gold Medalist, Top 3
|
|
||||||
at LIDM 2024
|
|
||||||
</span>
|
|
||||||
, and more.
|
|
||||||
<br />
|
|
||||||
<br />I am passionate about solving real-world problems
|
|
||||||
through code, especially at the intersection of{" "}
|
|
||||||
<span className="text-black font-medium">
|
|
||||||
Web Development
|
|
||||||
</span>{" "}
|
|
||||||
and{" "}
|
|
||||||
<span className="text-black font-medium">
|
|
||||||
Artificial Intelligence
|
|
||||||
</span>
|
|
||||||
. Constantly learning, building, and collaborating—I’m
|
|
||||||
excited to keep growing and contributing to the evolving tech
|
|
||||||
landscape.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-wrap gap-2 mt-4 text-sm">
|
|
||||||
<div className="bg-gray-300 text-black px-2 py-1 rounded-2xl">
|
|
||||||
GPA: 3.9 out of 4
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</motion.div>{" "}
|
|
||||||
{/* Achievements Section - Right */}
|
|
||||||
<motion.div
|
|
||||||
className="flex flex-col justify-start px-5 md:px-0"
|
|
||||||
initial={{ opacity: 0, x: 50 }}
|
|
||||||
whileInView={{ opacity: 1, x: 0 }}
|
|
||||||
transition={{ duration: 0.8, delay: 0.4 }}>
|
|
||||||
<h2 className="font-semibold text-xl mt-7">Achievements</h2>
|
|
||||||
<p className="text-md font-normal mb-3 md:mb-6">
|
|
||||||
Some of my achievements during my study.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{/* Achievements Container with transparent bottom effect */}
|
|
||||||
<div className="relative">
|
|
||||||
<div className="space-y-4">
|
|
||||||
{/* Show visible achievements */}
|
|
||||||
<AnimatePresence>
|
|
||||||
{visibleAchievements.map((achievement, index) => (
|
|
||||||
<motion.div
|
|
||||||
key={`${achievement.year}-${index}`}
|
|
||||||
className="group"
|
|
||||||
initial={{ opacity: 0, y: 20 }}
|
|
||||||
animate={{ opacity: 1, y: 0 }}
|
|
||||||
exit={{ opacity: 0, y: -20 }}
|
|
||||||
transition={{ duration: 0.5, delay: index * 0.05 }}>
|
|
||||||
{/* Year indicator for first achievement of each year */}
|
|
||||||
{index === 0 ||
|
|
||||||
visibleAchievements[index - 1]?.year !==
|
|
||||||
achievement.year ? (
|
|
||||||
<div className="flex items-center gap-3 mb-3 mt-2">
|
|
||||||
<div className="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center">
|
|
||||||
<span className="text-xs font-bold text-gray-600">
|
|
||||||
{achievement.year}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex-1 h-px bg-gradient-to-r from-gray-300 to-transparent"></div>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{/* Glassmorphism achievement card with monochrome to color effect */}
|
|
||||||
<div className="bg-white/20 backdrop-blur-md border border-white/30 rounded-2xl p-4 shadow-lg hover:bg-white/30 transition-all duration-300 hover:shadow-xl grayscale hover:grayscale-0">
|
|
||||||
<div className="flex items-center gap-4">
|
|
||||||
<div
|
|
||||||
className={`aspect-square w-10 rounded-full bg-gradient-to-r ${achievement.color} flex items-center justify-center text-primary-foreground transition-all duration-300`}>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon={achievement.icon}
|
|
||||||
className="text-white h-5 w-5"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h3 className="font-medium">{achievement.title}</h3>
|
|
||||||
<p className="text-sm">{achievement.subtitle}</p>
|
|
||||||
<div className="text-xs text-gray-500 mt-1">
|
|
||||||
{achievement.date}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</motion.div>
|
|
||||||
))}
|
|
||||||
</AnimatePresence>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Transparent bottom overlay when not expanded */}
|
|
||||||
{!isExpanded && hasMoreAchievements && (
|
|
||||||
<div className="absolute bottom-0 left-0 right-0 h-20 bg-gradient-to-t from-stale-300 via-stale/70 to-transparent pointer-events-none"></div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Expand/Collapse Button */}
|
|
||||||
{hasMoreAchievements && (
|
|
||||||
<motion.div
|
|
||||||
className="flex justify-center mt-6"
|
|
||||||
initial={{ opacity: 0 }}
|
|
||||||
animate={{ opacity: 1 }}
|
|
||||||
transition={{ delay: 0.5 }}>
|
|
||||||
<button
|
|
||||||
onClick={() => setIsExpanded(!isExpanded)}
|
|
||||||
className="flex items-center gap-2 px-6 py-3 bg-white/30 backdrop-blur-md border border-white/40 rounded-full hover:bg-white/40 transition-all duration-300 text-sm font-medium shadow-lg hover:shadow-xl">
|
|
||||||
<span>
|
|
||||||
{isExpanded
|
|
||||||
? `Show Less`
|
|
||||||
: `Show ${allAchievements.length - 4} More`}
|
|
||||||
</span>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon={isExpanded ? faChevronUp : faChevronDown}
|
|
||||||
className="h-3 w-3 transition-transform duration-300"
|
|
||||||
/>
|
/>
|
||||||
</button>
|
</div>
|
||||||
</motion.div>
|
<div className="flex-[1] transition-all duration-300 ease-in-out hover:flex-[3] group">
|
||||||
)}
|
<Image
|
||||||
|
src={Me4}
|
||||||
|
width={400}
|
||||||
|
height={225}
|
||||||
|
alt="University"
|
||||||
|
className="rounded-lg w-full h-full object-cover grayscale group-hover:grayscale-0 transition-all duration-300 ease-in-out"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex-[1] transition-all duration-300 ease-in-out hover:flex-[3] group">
|
||||||
|
<Image
|
||||||
|
src={Me4}
|
||||||
|
width={400}
|
||||||
|
height={225}
|
||||||
|
alt="University"
|
||||||
|
className="rounded-lg w-full h-full object-cover grayscale group-hover:grayscale-0 transition-all duration-300 ease-in-out"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<p className="text-justify">
|
||||||
|
Aspiring to be a software engineer, I am
|
||||||
|
currently pursuing a bachelor’s
|
||||||
|
degree in computer science at
|
||||||
|
Universitas Negeri Malang. Fascinated by
|
||||||
|
the world of technology, I am eager to
|
||||||
|
learn and explore new things in the
|
||||||
|
field of computer science.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{/* add gpa in a style */}
|
||||||
|
<div className="flex flex-wrap gap-2 mt-4 text-sm">
|
||||||
|
<div className="bg-gray-300 text-black px-2 py-1 rounded-2xl">
|
||||||
|
GPA: 3.9 out of 4
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
<div className="flex flex-col justify-start px-5 md:px-0">
|
||||||
|
<h2 className="font-semibold text-xl mt-7">
|
||||||
|
Achievements
|
||||||
|
</h2>
|
||||||
|
<p className="text-md font-normal mb-3 md:mb-12">
|
||||||
|
Some of my achievements during my study.
|
||||||
|
</p>
|
||||||
|
<div className="flex items-center gap-4 mb-4">
|
||||||
|
<div className="aspect-square w-10 rounded-full bg-black flex items-center justify-center text-primary-foreground">
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faMedal}
|
||||||
|
className="text-white h-5 w-5"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="font-medium">
|
||||||
|
Special Award | Gold Medal | Incubation
|
||||||
|
Opportunity
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm">
|
||||||
|
Indonesia Inventor Day 2024 (IID) | Aug
|
||||||
|
2024
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-4 mb-4">
|
||||||
|
<div className="aspect-square w-10 rounded-full bg-black flex items-center justify-center text-primary-foreground">
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faMedal}
|
||||||
|
className="text-white h-5 w-5"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="font-medium">
|
||||||
|
3rd place (Bronze Medal)
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm">
|
||||||
|
Student Digital Innovation Competition
|
||||||
|
(LIDM) | Jun 2024
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-4 mb-4">
|
||||||
|
<div className="aspect-square w-10 rounded-full bg-black flex items-center justify-center text-primary-foreground">
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faMedal}
|
||||||
|
className="text-white h-5 w-5"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="font-medium">
|
||||||
|
3rd place (Bronze Medal)
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm">
|
||||||
|
Fesmaro UI/UX Design | Jun 2024
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-4 mb-5">
|
||||||
|
<div className="aspect-square w-10 rounded-full bg-black flex items-center justify-center text-primary-foreground">
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faMedal}
|
||||||
|
className="text-white h-5 w-5"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="font-medium">Finalist</h3>
|
||||||
|
<p className="text-sm">
|
||||||
|
36th National Student Science Week
|
||||||
|
(PIMNAS) | Nov 2023
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-4 mb-5">
|
||||||
|
<div className="aspect-square w-10 rounded-full bg-black flex items-center justify-center text-primary-foreground">
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faMedal}
|
||||||
|
className="text-white h-5 w-5"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="font-medium">
|
||||||
|
3rd place (Bronze Medal)
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm">
|
||||||
|
FORKAFEST 3.0 Web Development
|
||||||
|
competition | Jan 2023
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
|
|
|
||||||
|
|
@ -1,124 +1,5 @@
|
||||||
"use client";
|
|
||||||
import Hr from "@/components/Hr";
|
import Hr from "@/components/Hr";
|
||||||
import { motion, AnimatePresence } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
const experiences = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
startDate: "Sep 2023",
|
|
||||||
endDate: "Jan 2024",
|
|
||||||
company: "Universitas Negeri Malang",
|
|
||||||
position: "Assistant Lecturer",
|
|
||||||
type: "Seasonal",
|
|
||||||
location: "Malang, Indonesia",
|
|
||||||
description:
|
|
||||||
"Assisted in teaching and mentoring students in algorithms and data structures, focusing on practical applications and problem-solving techniques. Developed course materials and provided support in lab sessions.",
|
|
||||||
skills: ["Python", "C", "Algorithms", "Data Structures", "Mentoring"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
startDate: "Jul 2024",
|
|
||||||
endDate: "Jan 2025",
|
|
||||||
company: "Outlier AI",
|
|
||||||
position: "AI Trainer",
|
|
||||||
type: "Freelance (Remote)",
|
|
||||||
location: "Oakland, California",
|
|
||||||
description:
|
|
||||||
"Trained LLM models using RLFH, focusing on enhancing their understanding of human language and improving their response accuracy. Collaborated with a team of AI specialists to refine model performance and ensure high-quality outputs.",
|
|
||||||
skills: ["Generative AI", "RLFH", "LLM", "Prompt Engineering", "Teamwork"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
startDate: "Jun 2023",
|
|
||||||
endDate: "Feb 2025",
|
|
||||||
company: "PUI-PT DLI",
|
|
||||||
position: "Fullstack Developer",
|
|
||||||
type: "Freelance",
|
|
||||||
location: "Malang, Indonesia",
|
|
||||||
description:
|
|
||||||
"Crafted and maintained web applications using Laravel, ensuring high performance and responsiveness. Collaborated with designers and other developers to create seamless user experiences.",
|
|
||||||
skills: ["Laravel", "MySQL", "PHP", "JavaScript", "Teamwork"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
startDate: "Apr 2025",
|
|
||||||
endDate: "Jul 2025",
|
|
||||||
company: "Vektorian Labophase",
|
|
||||||
position: "Front-end Developer",
|
|
||||||
type: "Freelance",
|
|
||||||
location: "Malang, Indonesia",
|
|
||||||
description:
|
|
||||||
"Developed and maintained web applications using Next.js. Focused on creating responsive and user-friendly interfaces while ensuring backend functionality. Collaborated with backend and designers to implement modern UI/UX principles.",
|
|
||||||
skills: ["Next.js", "Typescript", "Teamwork"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
startDate: "Apr 2023",
|
|
||||||
endDate: "Present",
|
|
||||||
company: "Self-Employed",
|
|
||||||
position: "Web Developer & AI Consultant",
|
|
||||||
type: "Freelance",
|
|
||||||
location: "Malang, Indonesia",
|
|
||||||
description:
|
|
||||||
"Developed 15+ web applications using Next.js, React, and Laravel. Provided AI consulting services, including creating custom LLMs. Focused on delivering high-quality, user-friendly applications and AI solutions.",
|
|
||||||
skills: [
|
|
||||||
"Next.js",
|
|
||||||
"React",
|
|
||||||
"Laravel",
|
|
||||||
"MySQL",
|
|
||||||
"PostgreSQL",
|
|
||||||
"MongoDB",
|
|
||||||
"JavaScript",
|
|
||||||
"TypeScript",
|
|
||||||
"Gemini AI",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 6,
|
|
||||||
startDate: "Aug 2023",
|
|
||||||
endDate: "Present",
|
|
||||||
company: "PT Hafdzamedia Teknologi Aplikasi",
|
|
||||||
position: "Fullstack Web Developer",
|
|
||||||
type: "Part-time",
|
|
||||||
location: "Malang, Indonesia",
|
|
||||||
description:
|
|
||||||
"Developed and maintained web applications using React, Next.js, and Laravel. Focused on creating responsive and user-friendly interfaces while ensuring backend functionality.",
|
|
||||||
skills: [
|
|
||||||
"React",
|
|
||||||
"Next.js",
|
|
||||||
"Laravel",
|
|
||||||
"MySQL",
|
|
||||||
"JavaScript",
|
|
||||||
"TypeScript",
|
|
||||||
"Teamwork",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 7,
|
|
||||||
startDate: "Feb 2025",
|
|
||||||
endDate: "Present",
|
|
||||||
company: "Joki Proyek",
|
|
||||||
position: "IT Implementation Specialist",
|
|
||||||
type: "Internship (Remote)",
|
|
||||||
location: "Malang, Indonesia",
|
|
||||||
description:
|
|
||||||
"Developed and maintained current internal web applications using Next.js & and Laravel. Fixed bugs and implemented new features to enhance user experience. Collaborated with the team to ensure smooth deployment and functionality of applications.",
|
|
||||||
skills: [
|
|
||||||
"Next.js",
|
|
||||||
"Laravel",
|
|
||||||
"MySQL",
|
|
||||||
"PostgreSQL",
|
|
||||||
"JavaScript",
|
|
||||||
"TypeScript",
|
|
||||||
"MongoDB",
|
|
||||||
"Docker",
|
|
||||||
"Teamwork",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
experiences.reverse();
|
|
||||||
|
|
||||||
function Title() {
|
function Title() {
|
||||||
return (
|
return (
|
||||||
|
|
@ -137,6 +18,7 @@ function Title() {
|
||||||
}}
|
}}
|
||||||
transition={{
|
transition={{
|
||||||
delay: 0.7,
|
delay: 0.7,
|
||||||
|
|
||||||
type: "spring",
|
type: "spring",
|
||||||
}}>
|
}}>
|
||||||
Profesional Experience
|
Profesional Experience
|
||||||
|
|
@ -146,193 +28,135 @@ function Title() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function TimelineCard({ experience, index, isEven }) {
|
|
||||||
return (
|
|
||||||
<motion.div
|
|
||||||
initial={{ opacity: 0, y: -20 }}
|
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
|
||||||
transition={{ delay: index * 0.15, duration: 0.5 }}
|
|
||||||
className={`flex ps-10 md:ps-0 ${
|
|
||||||
isEven
|
|
||||||
? "md:justify-center md:translate-x-68"
|
|
||||||
: "md:justify-center md:-translate-x-68"
|
|
||||||
} justify-center mb-4`}>
|
|
||||||
<div className="bg-gradient-to-r from-black to-gray-800 text-white px-12 py-3 rounded-xl shadow-lg border border-gray-600 min-w-max">
|
|
||||||
<div className="flex items-center justify-center gap-6">
|
|
||||||
<div className="text-center">
|
|
||||||
<div className="text-sm font-bold">{experience.startDate}</div>
|
|
||||||
<div className="text-xs text-gray-300">Start</div>
|
|
||||||
</div>
|
|
||||||
<div className="w-px h-8 bg-gray-500"></div>
|
|
||||||
<div className="text-center">
|
|
||||||
<div className="text-sm font-bold">{experience.endDate}</div>
|
|
||||||
<div className="text-xs text-gray-300">End</div>
|
|
||||||
</div> <div className="w-px h-8 bg-gray-500"></div>
|
|
||||||
<div className="text-center">
|
|
||||||
<div className="text-sm font-medium text-gray-400">
|
|
||||||
{experience.location}
|
|
||||||
</div>
|
|
||||||
<div className="text-xs text-gray-300">Location</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</motion.div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ExperienceCard({ experience, index, isEven }) {
|
|
||||||
return (
|
|
||||||
<motion.div
|
|
||||||
initial={{ opacity: 0, y: 50 }}
|
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
|
||||||
transition={{ delay: index * 0.2, duration: 0.6 }}
|
|
||||||
className={`relative group ${
|
|
||||||
isEven ? "md:ml-auto md:pl-12" : "md:mr-auto md:pr-12"
|
|
||||||
} md:w-1/2`}>
|
|
||||||
{" "}
|
|
||||||
{/* Card */}
|
|
||||||
<div
|
|
||||||
className={`bg-white/20 backdrop-blur-sm border border-gray-300/30 rounded-2xl p-6 shadow-lg
|
|
||||||
hover:shadow-xl hover:bg-white/30 transition-all duration-300 ml-12 md:ml-0`}>
|
|
||||||
{/* Company & Position */}
|
|
||||||
<div className="mb-4">
|
|
||||||
<h3 className="font-bold text-xl text-black mb-1">
|
|
||||||
{experience.company}
|
|
||||||
</h3>
|
|
||||||
<h4 className="font-medium text-lg text-gray-700">
|
|
||||||
{experience.position}
|
|
||||||
<span className="text-sm font-normal text-gray-500 ml-2">
|
|
||||||
• {experience.type}
|
|
||||||
</span>
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Description */}
|
|
||||||
<p className="text-gray-600 text-justify leading-relaxed mb-4">
|
|
||||||
{experience.description}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{/* Skills */}
|
|
||||||
<div className="flex flex-wrap gap-2">
|
|
||||||
{experience.skills.map((skill, idx) => (
|
|
||||||
<span
|
|
||||||
key={idx}
|
|
||||||
className="bg-gray-200/60 hover:bg-gray-300/60 border border-gray-400/40 text-black px-3 py-1 rounded-full text-sm font-medium transition-all duration-300 backdrop-blur-sm hover:scale-105">
|
|
||||||
{skill}
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</motion.div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Wrapper({ children }) {
|
function Wrapper({ children }) {
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto container px-6 py-10">
|
<div className="mx-auto container gap-10 p-10 grid grid-cols-1 my-10 ">
|
||||||
<div
|
<motion.div
|
||||||
className="flex justify-center items-center flex-col">
|
className="flex justify-center items-start flex-col mb-5 "
|
||||||
{children}
|
initial={{
|
||||||
</div>
|
opacity: 0,
|
||||||
|
x: -200,
|
||||||
|
}}
|
||||||
|
whileInView={{
|
||||||
|
opacity: 1,
|
||||||
|
x: 0,
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
delay: 0.5,
|
||||||
|
|
||||||
|
type: "spring",
|
||||||
|
}}>
|
||||||
|
<section className="grid gap-8 md:gap-12">
|
||||||
|
<div className="relative after:absolute after:inset-y-2 after:w-1 after:bg-gray-700 after:left-[-4px] md:after:left-1 lg:after:left-2 xl:after:left-3 grid gap-8 md:gap-12 pl-6 md:pl-8">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Experience() {
|
export default function Experience() {
|
||||||
const [showAll, setShowAll] = useState(false);
|
|
||||||
const displayedExperiences = showAll ? experiences : experiences.slice(0, 3);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Title />
|
<Title/>
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
{" "}
|
<div className="grid gap-4 relative">
|
||||||
<div className="relative w-full max-w-6xl mx-auto">
|
<div className="aspect-square w-5 bg-black rounded-full absolute left-0 translate-x-[-29.5px] z-10" />
|
||||||
{" "}
|
<div className="font-medium text-lg">2023 - Present</div>
|
||||||
{/* Timeline line - hidden on mobile, visible on md+ */}
|
<div>
|
||||||
<div className="hidden md:block absolute left-1/2 transform -translate-x-1/2 w-1 bg-gradient-to-b from-black via-gray-400 to-transparent h-full"></div>
|
<h3 className="font-semibold text-xl text-black">
|
||||||
{/* Mobile timeline line */}
|
PT Hafdzamedia Teknologi Aplikasi
|
||||||
<div className="md:hidden absolute left-0 w-1 bg-gradient-to-b from-black via-gray-400 to-transparent h-full"></div>{" "}
|
</h3>
|
||||||
{/* Experience cards */}
|
<h4 className=" font-light text-md mb-4">
|
||||||
<div className="space-y-12 md:space-y-16 relative">
|
Fullstack Web Developer | Part-time
|
||||||
<AnimatePresence>
|
</h4>
|
||||||
{displayedExperiences.map((experience, index) => (
|
<p className="text-justify">
|
||||||
<div key={experience.id} className="relative">
|
Responsible for building and maintaining scalable
|
||||||
{/* Timeline period card - flows naturally above content */}
|
web applications using the latest technologies.
|
||||||
<TimelineCard
|
Collaborated with cross-functional teams to deliver
|
||||||
experience={experience}
|
high-quality solutions that meet business
|
||||||
index={index}
|
requirements.
|
||||||
isEven={index % 2 === 1}
|
</p>
|
||||||
/>
|
<div className="flex flex-wrap gap-2 mt-4 text-sm">
|
||||||
|
<div className="bg-gray-300 text-black px-2 py-1 rounded-2xl">
|
||||||
{/* Timeline dot - positioned at the start of the experience card */}
|
React
|
||||||
<div
|
</div>
|
||||||
className={`absolute w-6 h-6 bg-black rounded-full border-4 border-white shadow-lg z-30
|
<div className="bg-gray-300 text-black px-2 py-1 rounded-2xl">
|
||||||
md:left-1/2 md:-translate-x-1/2 md:top-4
|
Next.js
|
||||||
left-0 -translate-x-1/2 top-5`}
|
</div>
|
||||||
/>
|
<div className="bg-gray-300 text-black px-2 py-1 rounded-2xl">
|
||||||
|
Laravel
|
||||||
{/* Experience content card */}
|
</div>
|
||||||
<ExperienceCard
|
<div className="bg-gray-300 text-black px-2 py-1 rounded-2xl">
|
||||||
experience={experience}
|
MySql
|
||||||
index={index}
|
</div>
|
||||||
isEven={index % 2 === 1}
|
</div>
|
||||||
/>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
<div className="grid gap-4 relative">
|
||||||
</AnimatePresence>
|
<div className="aspect-square w-5 bg-black rounded-full absolute left-0 translate-x-[-29.5px] z-10" />
|
||||||
|
<div className="font-medium text-lg">2024 - Present</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="font-semibold text-xl text-black">
|
||||||
|
Outlier
|
||||||
|
</h3>
|
||||||
|
<h4 className=" font-light text-md mb-4">
|
||||||
|
AI Trainer | Freelance
|
||||||
|
</h4>
|
||||||
|
<p className="text-justify">
|
||||||
|
Crafted and refined chatbot prompts using prompt
|
||||||
|
engineering and RLHF techniques to improve the AI
|
||||||
|
responses in English and Indonesian, leading to more
|
||||||
|
accurate and contextually relevant interactions.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="flex flex-wrap gap-2 mt-4 text-sm">
|
||||||
|
<div className="bg-gray-300 text-black px-2 py-1 rounded-2xl">
|
||||||
|
Generative AI
|
||||||
|
</div>
|
||||||
|
<div className="bg-gray-300 text-black px-2 py-1 rounded-2xl">
|
||||||
|
Prompt Engineering
|
||||||
|
</div>
|
||||||
|
<div className="bg-gray-300 text-black px-2 py-1 rounded-2xl">
|
||||||
|
Problem Solving
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="grid gap-4 relative">
|
||||||
|
<div className="aspect-square w-5 bg-black rounded-full absolute left-0 translate-x-[-29.5px] z-10" />
|
||||||
|
<div className="font-medium text-lg">2023 - Present</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="font-semibold text-xl text-black">
|
||||||
|
Self-Employed
|
||||||
|
</h3>
|
||||||
|
<h4 className=" font-light text-md mb-4">
|
||||||
|
Web Developer | Freelance
|
||||||
|
</h4>
|
||||||
|
<p className="text-justify">
|
||||||
|
Specialized in crafting innovative solutions using
|
||||||
|
Laravel and Next.js, enhancing client satisfaction
|
||||||
|
and project efficiency. Contributed expertise in
|
||||||
|
both frontend and backend development during
|
||||||
|
collaborative team projects, leading to successful
|
||||||
|
project completions
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-wrap gap-2 mt-4 text-sm">
|
||||||
|
<div className="bg-gray-300 text-black px-2 py-1 rounded-2xl">
|
||||||
|
React
|
||||||
|
</div>
|
||||||
|
<div className="bg-gray-300 text-black px-2 py-1 rounded-2xl">
|
||||||
|
Next.js
|
||||||
|
</div>
|
||||||
|
<div className="bg-gray-300 text-black px-2 py-1 rounded-2xl">
|
||||||
|
Laravel
|
||||||
|
</div>
|
||||||
|
<div className="bg-gray-300 text-black px-2 py-1 rounded-2xl">
|
||||||
|
MySql
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* Expand/Collapse button */}
|
|
||||||
{experiences.length > 3 && (
|
|
||||||
<motion.div
|
|
||||||
className="flex justify-center mt-12"
|
|
||||||
initial={{ opacity: 0 }}
|
|
||||||
whileInView={{ opacity: 1 }}
|
|
||||||
transition={{ delay: 0.5 }}>
|
|
||||||
<button
|
|
||||||
onClick={() => setShowAll(!showAll)}
|
|
||||||
className="bg-black hover:bg-gray-800 text-white px-8 py-3 rounded-full font-medium
|
|
||||||
transition-all duration-300 hover:scale-105 shadow-lg flex items-center gap-2">
|
|
||||||
{showAll ? (
|
|
||||||
<>
|
|
||||||
Show Less
|
|
||||||
<svg
|
|
||||||
className="w-4 h-4 transform rotate-180"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24">
|
|
||||||
<path
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
strokeWidth={2}
|
|
||||||
d="M19 9l-7 7-7-7"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
View More Experience
|
|
||||||
<svg
|
|
||||||
className="w-4 h-4"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24">
|
|
||||||
<path
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
strokeWidth={2}
|
|
||||||
d="M19 9l-7 7-7-7"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</motion.div>
|
|
||||||
)}{" "}
|
|
||||||
{/* Gradient fade effect at bottom */}
|
|
||||||
{!showAll && (
|
|
||||||
<div className="absolute bottom-0 left-0 right-0 h-32 bg-gradient-to-t from-stale-300 to-transparent pointer-events-none"></div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -17,15 +17,13 @@ export function useIntersectionObserver() {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const currentRef = ref.current; // Store the ref value in a variable
|
if (ref.current) {
|
||||||
|
observer.observe(ref.current);
|
||||||
if (currentRef) {
|
|
||||||
observer.observe(currentRef);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (currentRef) {
|
if (ref.current) {
|
||||||
observer.unobserve(currentRef);
|
observer.unobserve(ref.current);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
function ActivityIcon(props) {
|
|
||||||
return (
|
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="2"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round">
|
|
||||||
<path d="M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
function CodepenIcon(props) {
|
|
||||||
return (
|
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="2"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round">
|
|
||||||
<polygon points="12 2 22 8.5 22 15.5 12 22 2 15.5 2 8.5 12 2" />
|
|
||||||
<line x1="12" x2="12" y1="22" y2="15.5" />
|
|
||||||
<polyline points="22 8.5 12 15.5 2 8.5" />
|
|
||||||
<polyline points="2 15.5 12 8.5 22 15.5" />
|
|
||||||
<line x1="12" x2="12" y1="2" y2="8.5" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function WebhookIcon(props) {
|
|
||||||
return (
|
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="2"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round">
|
|
||||||
<path d="M18 16.98h-5.99c-1.1 0-1.95.94-2.48 1.9A4 4 0 0 1 2 17c.01-.7.2-1.4.57-2" />
|
|
||||||
<path d="m6 17 3.13-5.78c.53-.97.1-2.18-.5-3.1a4 4 0 1 1 6.89-4.06" />
|
|
||||||
<path d="m12 6 3.13 5.73C15.66 12.7 16.9 13 18 13a4 4 0 0 1 0 8" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function MobileIcon(props) {
|
|
||||||
return (
|
|
||||||
<svg
|
|
||||||
{...props}
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="2"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round">
|
|
||||||
<rect x="5" y="2" width="14" height="20" rx="2" ry="2" />
|
|
||||||
<line x1="12" x2="12" y1="18" y2="18" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
ActivityIcon,
|
|
||||||
CodepenIcon,
|
|
||||||
WebhookIcon,
|
|
||||||
MobileIcon,
|
|
||||||
};
|
|
||||||
|
|
@ -1,249 +1,164 @@
|
||||||
"use client";
|
import { motion } from "framer-motion";
|
||||||
import { motion, AnimatePresence } from "framer-motion";
|
import Stack from "./stack";
|
||||||
import { useState } from "react";
|
import Tools from "./tools";
|
||||||
import { CodepenIcon, WebhookIcon, ActivityIcon, MobileIcon } from "./icons"
|
|
||||||
|
|
||||||
const skillCategories = {
|
|
||||||
web: {
|
|
||||||
title: "Web Development",
|
|
||||||
icon: CodepenIcon,
|
|
||||||
description: "Building modern, responsive web applications",
|
|
||||||
languages: [
|
|
||||||
"HTML",
|
|
||||||
"CSS",
|
|
||||||
"JavaScript",
|
|
||||||
"TypeScript",
|
|
||||||
"PHP",
|
|
||||||
"Python",
|
|
||||||
"React",
|
|
||||||
"NextJS",
|
|
||||||
"TailwindCSS",
|
|
||||||
"Bootstrap",
|
|
||||||
"NodeJS",
|
|
||||||
"ExpressJS",
|
|
||||||
"Laravel",
|
|
||||||
"Flask",
|
|
||||||
"Django",
|
|
||||||
"Firebase"
|
|
||||||
],
|
|
||||||
tools: ["Visual Studio Code", "Git", "Github", "Figma", "Vite", "Docker", "Kubernetes", "Google Cloud", "Postman"],
|
|
||||||
},
|
|
||||||
api: {
|
|
||||||
title: "REST API",
|
|
||||||
icon: WebhookIcon,
|
|
||||||
description: "Creating robust and scalable backend services",
|
|
||||||
languages: [
|
|
||||||
"NodeJS",
|
|
||||||
"ExpressJS",
|
|
||||||
"PHP",
|
|
||||||
"Laravel",
|
|
||||||
"Python",
|
|
||||||
"FastAPI",
|
|
||||||
"Flask",
|
|
||||||
"Django",
|
|
||||||
"MySQL",
|
|
||||||
"PostgreSQL",
|
|
||||||
"MongoDB",
|
|
||||||
"Firebase",
|
|
||||||
],
|
|
||||||
tools: ["Postman", "Docker", "Kubernetes", "Swagger", "Git", "Github", "Google Cloud", "IBM Cloud"],
|
|
||||||
},
|
|
||||||
ai: {
|
|
||||||
title: "AI & Machine Learning",
|
|
||||||
icon: ActivityIcon,
|
|
||||||
description: "Developing intelligent solutions with ML/AI",
|
|
||||||
languages: [
|
|
||||||
"Python",
|
|
||||||
"TensorFlow",
|
|
||||||
"PyTorch",
|
|
||||||
"Scikit-learn",
|
|
||||||
"Pandas",
|
|
||||||
"NumPy",
|
|
||||||
"Jupyter",
|
|
||||||
"OpenAI API",
|
|
||||||
"Gemini API",
|
|
||||||
"LangChain",
|
|
||||||
],
|
|
||||||
tools: [
|
|
||||||
"Jupyter Notebook",
|
|
||||||
"Google Colab",
|
|
||||||
"Google Cloud AI",
|
|
||||||
"AWS SageMaker",
|
|
||||||
"IBM Watson",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
mobile: {
|
|
||||||
title: "Mobile Development",
|
|
||||||
icon: MobileIcon,
|
|
||||||
description: "Cross-platform mobile app development",
|
|
||||||
languages: [
|
|
||||||
"React Native",
|
|
||||||
"JavaScript",
|
|
||||||
"TypeScript",
|
|
||||||
"Dart",
|
|
||||||
"Flutter",
|
|
||||||
],
|
|
||||||
tools: [
|
|
||||||
"Android Studio",
|
|
||||||
"React Native CLI",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
function SkillCard({ skill, isSelected, onClick }) {
|
|
||||||
const Icon = skill.icon;
|
|
||||||
|
|
||||||
|
function Wrapper({ children }) {
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<div className="mx-auto container gap-4 p-10 grid grid-cols-1 mt-10">
|
||||||
onClick={onClick}
|
|
||||||
className={`relative cursor-pointer group p-6 rounded-2xl backdrop-blur-lg border transition-all duration-300 ${
|
|
||||||
isSelected
|
|
||||||
? "bg-white/20 border-black border-2 shadow-lg"
|
|
||||||
: "bg-white/10 border-gray-300/20 hover:bg-white/20 hover:border-gray-300/30"
|
|
||||||
}`}
|
|
||||||
whileHover={{
|
|
||||||
scale: 1.05,
|
|
||||||
rotateY: 5,
|
|
||||||
}}
|
|
||||||
whileTap={{ scale: 0.95 }}
|
|
||||||
initial={{ opacity: 0, y: 20 }}
|
|
||||||
animate={{ opacity: 1, y: 0 }}
|
|
||||||
transition={{
|
|
||||||
type: "spring",
|
|
||||||
stiffness: 300,
|
|
||||||
damping: 20,
|
|
||||||
}}>
|
|
||||||
{/* Glow effect - removed for selected state */}
|
|
||||||
{!isSelected && (
|
|
||||||
<div className="absolute inset-0 rounded-2xl transition-opacity duration-300 opacity-0 group-hover:opacity-50 bg-gradient-to-r from-gray-400/20 to-gray-600/20 blur-xl" />
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="relative z-10 flex flex-col items-center text-center space-y-4">
|
|
||||||
<div
|
|
||||||
className={`p-4 rounded-xl transition-all duration-300 ${
|
|
||||||
isSelected ? "bg-white/30" : "bg-white/10 group-hover:bg-white/20"
|
|
||||||
}`}>
|
|
||||||
<Icon className="w-8 h-8 text-black" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h3 className="font-semibold text-black text-lg mb-2">
|
|
||||||
{skill.title}
|
|
||||||
</h3>
|
|
||||||
<p className="text-gray-600 text-sm leading-relaxed">
|
|
||||||
{skill.description}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</motion.div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function SkillDetails({ selectedSkill }) {
|
|
||||||
if (!selectedSkill) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<motion.div
|
|
||||||
initial={{ opacity: 0, y: 20 }}
|
|
||||||
animate={{ opacity: 1, y: 0 }}
|
|
||||||
exit={{ opacity: 0, y: -20 }}
|
|
||||||
transition={{ duration: 0.5 }}
|
|
||||||
className="mt-12 space-y-8">
|
|
||||||
{/* Languages & Frameworks */}
|
|
||||||
<motion.div
|
<motion.div
|
||||||
className="backdrop-blur-lg bg-white/20 border border-gray-300/30 rounded-2xl p-8"
|
className="flex justify-center items-center flex-col mb-5 "
|
||||||
initial={{ opacity: 0, x: -50 }}
|
initial={{
|
||||||
animate={{ opacity: 1, x: 0 }}
|
opacity: 0,
|
||||||
transition={{ delay: 0.2 }}>
|
x: -200,
|
||||||
<h3 className="text-2xl font-semibold text-black mb-6 text-center">
|
}}
|
||||||
Languages & Frameworks
|
whileInView={{
|
||||||
</h3>
|
opacity: 1,
|
||||||
<div className="flex flex-wrap justify-center gap-3">
|
x: 0,
|
||||||
{selectedSkill.languages.map((lang, index) => (
|
}}
|
||||||
<motion.span
|
transition={{
|
||||||
key={lang}
|
delay: 0.5,
|
||||||
initial={{ opacity: 0, scale: 0.8 }}
|
type: "spring",
|
||||||
animate={{ opacity: 1, scale: 1 }}
|
}}>
|
||||||
transition={{ delay: 0.3 + index * 0.1 }}
|
{children}
|
||||||
className="px-4 py-2 bg-gradient-to-r from-gray-200/60 to-white/40
|
|
||||||
border border-gray-400/40 rounded-full text-black font-medium
|
|
||||||
backdrop-blur-sm hover:scale-105 transition-transform cursor-default
|
|
||||||
hover:bg-gradient-to-r hover:from-gray-300/60 hover:to-white/50">
|
|
||||||
{lang}
|
|
||||||
</motion.span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
</div>
|
||||||
{/* Tools */}
|
|
||||||
<motion.div
|
|
||||||
className="backdrop-blur-lg bg-white/20 border border-gray-300/30 rounded-2xl p-8"
|
|
||||||
initial={{ opacity: 0, x: 50 }}
|
|
||||||
animate={{ opacity: 1, x: 0 }}
|
|
||||||
transition={{ delay: 0.4 }}>
|
|
||||||
<h3 className="text-2xl font-semibold text-black mb-6 text-center">
|
|
||||||
Tools & Technologies
|
|
||||||
</h3>
|
|
||||||
<div className="flex flex-wrap justify-center gap-3">
|
|
||||||
{selectedSkill.tools.map((tool, index) => (
|
|
||||||
<motion.span
|
|
||||||
key={tool}
|
|
||||||
initial={{ opacity: 0, scale: 0.8 }}
|
|
||||||
animate={{ opacity: 1, scale: 1 }}
|
|
||||||
transition={{ delay: 0.5 + index * 0.1 }}
|
|
||||||
className="px-4 py-2 bg-gradient-to-r from-gray-300/60 to-gray-100/40
|
|
||||||
border border-gray-500/40 rounded-full text-black font-medium
|
|
||||||
backdrop-blur-sm hover:scale-105 transition-transform cursor-default
|
|
||||||
hover:bg-gradient-to-r hover:from-gray-400/60 hover:to-gray-200/50">
|
|
||||||
{tool}
|
|
||||||
</motion.span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</motion.div>
|
|
||||||
</motion.div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Skills() {
|
export default function Skills() {
|
||||||
const [selectedCategory, setSelectedCategory] = useState("web");
|
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<>
|
||||||
<div className="mx-auto container px-6 py-20">
|
<Wrapper>
|
||||||
<motion.div
|
<section className="grid gap-8 md:gap-12">
|
||||||
initial={{ opacity: 0, y: -20 }}
|
<div className="text-center space-y-2">
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
<h2 className="text-3xl font-bold mt-3 text-black">
|
||||||
transition={{ duration: 0.6 }}
|
Skills and Expertise
|
||||||
className="text-center space-y-4 mb-16">
|
</h2>
|
||||||
<h2 className="text-5xl font-bold bg-gradient-to-r from-black to-gray-600 bg-clip-text text-transparent">
|
<p className="text-muted-foreground max-w-[800px] mx-auto">
|
||||||
Skills & Expertise
|
Explore some skills I'm proficient in to
|
||||||
</h2>
|
deliver high-quality solutions.
|
||||||
<p className="text-gray-600 max-w-2xl mx-auto text-lg leading-relaxed">
|
</p>
|
||||||
Explore my technical skills across different domains. Click on any
|
</div>
|
||||||
category to see the specific technologies and tools I work with.
|
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6 md:gap-8">
|
||||||
</p>
|
<div className="flex flex-col items-center gap-2">
|
||||||
</motion.div>
|
<CodepenIcon className="w-12 h-12" />
|
||||||
|
<div className="font-medium">Web Development</div>
|
||||||
{/* Skill Categories Grid */}
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
<div className="flex flex-col items-center gap-2">
|
||||||
{Object.entries(skillCategories).map(([key, skill], index) => (
|
<WebhookIcon className="w-12 h-12" />
|
||||||
<motion.div
|
<div className="font-medium">REST API</div>
|
||||||
key={key}
|
</div>
|
||||||
initial={{ opacity: 0, y: 20 }}
|
<div className="flex flex-col items-center gap-2">
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
<ActivityIcon className="w-12 h-12" />
|
||||||
transition={{ delay: index * 0.1 }}>
|
<div className="font-medium">Machine Learning</div>
|
||||||
<SkillCard
|
</div>
|
||||||
skill={skill}
|
<div className="flex flex-col items-center gap-2">
|
||||||
isSelected={selectedCategory === key}
|
<MobileIcon className="w-12 h-12" />
|
||||||
onClick={() => setSelectedCategory(key)}
|
<div className="font-medium">
|
||||||
/>
|
Mobile Development
|
||||||
</motion.div>
|
</div>
|
||||||
))}
|
</div>
|
||||||
|
{/* <div className="flex flex-col items-center gap-2">
|
||||||
|
<CloudIcon className="w-12 h-12" />
|
||||||
|
<div className="font-medium">Cloud Computing</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-col items-center gap-2">
|
||||||
{/* Skill Details */}
|
<BarChartIcon className="w-12 h-12" />
|
||||||
<AnimatePresence mode="wait">
|
<div className="font-medium">Data Analysis</div>
|
||||||
<SkillDetails selectedSkill={skillCategories[selectedCategory]} />
|
</div>
|
||||||
</AnimatePresence>
|
<div className="flex flex-col items-center gap-2">
|
||||||
|
<ComputerIcon className="w-12 h-12" />
|
||||||
|
<div className="font-medium">Cybersecurity</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col items-center gap-2">
|
||||||
|
<PackageIcon className="w-12 h-12" />
|
||||||
|
<div className="font-medium">Product Management</div>
|
||||||
|
</div> */}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</Wrapper>
|
||||||
|
<div className=" mx-auto container gap-4 p-10 grid grid-cols-1 md:grid-cols-2 mt-10 mb-24">
|
||||||
|
<Stack />
|
||||||
|
<Tools />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ActivityIcon(props) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
{...props}
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round">
|
||||||
|
<path d="M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
function CodepenIcon(props) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
{...props}
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round">
|
||||||
|
<polygon points="12 2 22 8.5 22 15.5 12 22 2 15.5 2 8.5 12 2" />
|
||||||
|
<line x1="12" x2="12" y1="22" y2="15.5" />
|
||||||
|
<polyline points="22 8.5 12 15.5 2 8.5" />
|
||||||
|
<polyline points="2 15.5 12 8.5 22 15.5" />
|
||||||
|
<line x1="12" x2="12" y1="2" y2="8.5" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function WebhookIcon(props) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
{...props}
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round">
|
||||||
|
<path d="M18 16.98h-5.99c-1.1 0-1.95.94-2.48 1.9A4 4 0 0 1 2 17c.01-.7.2-1.4.57-2" />
|
||||||
|
<path d="m6 17 3.13-5.78c.53-.97.1-2.18-.5-3.1a4 4 0 1 1 6.89-4.06" />
|
||||||
|
<path d="m12 6 3.13 5.73C15.66 12.7 16.9 13 18 13a4 4 0 0 1 0 8" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MobileIcon(props) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
{...props}
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round">
|
||||||
|
<rect x="5" y="2" width="14" height="20" rx="2" ry="2" />
|
||||||
|
<line x1="12" x2="12" y1="18" y2="18" />
|
||||||
|
</svg>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
44
app/about/components/skills/stack.jsx
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
|
||||||
|
export default function Stack() {
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
className="flex justify-center items-center flex-col mb-5 "
|
||||||
|
initial={{
|
||||||
|
opacity: 0,
|
||||||
|
x: -200,
|
||||||
|
}}
|
||||||
|
whileInView={{
|
||||||
|
opacity: 1,
|
||||||
|
x: 0,
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
delay: 0.5,
|
||||||
|
|
||||||
|
type: "spring",
|
||||||
|
}}>
|
||||||
|
<h2 className="text-2xl md:text-xl font-normal mb-3 md:tracking-[.3rem] lg:tracking-[.5rem] uppercase ">
|
||||||
|
Language & Framework
|
||||||
|
</h2>
|
||||||
|
<p className="text-gray-500 text-center tracking-widest md:px-5">
|
||||||
|
<span className="text-black font-bold">HTML</span> |{" "}
|
||||||
|
<span className="text-black font-bold">CSS</span> |{" "}
|
||||||
|
<span className="text-black font-bold">Javascript</span> |{" "}
|
||||||
|
<span className="text-black font-bold">Typescript</span> |{" "}
|
||||||
|
<span className="text-black font-bold">React</span> |{" "}
|
||||||
|
<span className="text-black font-bold">NextJS</span> |{" "}
|
||||||
|
<span className="text-black font-bold">TailwindCSS</span> |{" "}
|
||||||
|
<span className="text-black font-bold">Bootstrap</span> |{" "}
|
||||||
|
<span className="text-black font-medium">NodeJS</span> |{" "}
|
||||||
|
<span className="text-black font-medium">ExpressJS</span> |{" "}
|
||||||
|
<span className="text-black font-bold">MySQL</span> |{" "}
|
||||||
|
<span className="text-black font-bold">PostgreSQL</span> |{" "}
|
||||||
|
<span className="text-black font-medium">MongoDB</span> |{" "}
|
||||||
|
<span className="text-black font-medium">Firebase</span> |{" "}
|
||||||
|
<span className="text-black font-medium">PHP</span> |{" "}
|
||||||
|
<span className="text-black font-bold">Laravel</span> |{" "}
|
||||||
|
<span className="text-black font-medium">Python</span>
|
||||||
|
</p>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
42
app/about/components/skills/tools.jsx
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
|
||||||
|
export default function Tools() {
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
className="flex justify-start items-center flex-col mb-5 "
|
||||||
|
initial={{
|
||||||
|
opacity: 0,
|
||||||
|
x: 200,
|
||||||
|
}}
|
||||||
|
whileInView={{
|
||||||
|
opacity: 1,
|
||||||
|
x: 0,
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
delay: 0.6,
|
||||||
|
|
||||||
|
type: "spring",
|
||||||
|
}}>
|
||||||
|
<h2 className="text-2xl md:text-xl font-normal mb-3 md:tracking-[.3rem] lg:tracking-[.5rem] uppercase">
|
||||||
|
Tools
|
||||||
|
</h2>
|
||||||
|
<p className="text-gray-500 text-center tracking-wider md:px-">
|
||||||
|
<span className="text-black font-bold">Visual Studio Code</span>{" "}
|
||||||
|
| <span className="text-black font-medium">Jet Brains</span> |{" "}
|
||||||
|
<span className="text-black font-bold">Git</span> |{" "}
|
||||||
|
<span className="text-black font-bold">Github</span> |{" "}
|
||||||
|
<span className="text-black font-medium">Figma</span> |{" "}
|
||||||
|
<span className="text-black font-medium">Adobe Photoshop</span>{" "}
|
||||||
|
|{" "}
|
||||||
|
<span className="text-black font-medium">
|
||||||
|
Adobe Premiere Pro
|
||||||
|
</span>{" "}
|
||||||
|
|{" "}
|
||||||
|
<span className="text-black font-medium">
|
||||||
|
Adobe After Effect
|
||||||
|
</span>{" "}
|
||||||
|
|{" "}
|
||||||
|
</p>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import Footer from "@/components/Footer";
|
import Footer from "@/components/Footer";
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: "Korobtsev.ru | Обо мне"
|
title: "Alvalens | About",
|
||||||
};
|
};
|
||||||
export default function Layout({ children }) {
|
export default function Layout({ children }) {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
export default function Loading() {
|
export default function Loading() {
|
||||||
return (
|
return (
|
||||||
<div className="fixed top-0 left-0 flex justify-center items-center h-screen w-screen bg-gray-300 z-[999]">
|
<>
|
||||||
<h1 className="text-4xl text-gray-900">Загрузка...</h1>
|
(
|
||||||
</div>
|
<div className="fixed top-0 left-0 flex justify-center items-center h-screen w-screen bg-gray-300 z-[999]">
|
||||||
|
<h1 className="text-4xl text-gray-900">Loading...</h1>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import Experience from "./components/experience.jsx";
|
||||||
import Education from "./components/education.jsx";
|
import Education from "./components/education.jsx";
|
||||||
|
|
||||||
// images
|
// images
|
||||||
import Hero from "@/public/image/me2.jpeg";
|
import Hero from "@/public/image/me2.jpg";
|
||||||
|
|
||||||
import Hr from "@/components/Hr";
|
import Hr from "@/components/Hr";
|
||||||
import About from "./components/about/about.jsx";
|
import About from "./components/about/about.jsx";
|
||||||
|
|
@ -42,7 +42,7 @@ export default function Page() {
|
||||||
className="bg-slate-300 rounded-sm h-[400px] md:h-[600px] w-[80vw] md:w-[30vw] grayscale hover:grayscale-0 ">
|
className="bg-slate-300 rounded-sm h-[400px] md:h-[600px] w-[80vw] md:w-[30vw] grayscale hover:grayscale-0 ">
|
||||||
<Image
|
<Image
|
||||||
src={Hero}
|
src={Hero}
|
||||||
alt="Виталий Коробцев"
|
alt="Alvalen Shafel"
|
||||||
layout="fill"
|
layout="fill"
|
||||||
objectFit="cover"
|
objectFit="cover"
|
||||||
placeholder="blur"
|
placeholder="blur"
|
||||||
|
|
@ -55,10 +55,10 @@ export default function Page() {
|
||||||
</h1>
|
</h1>
|
||||||
<Hr />
|
<Hr />
|
||||||
<p className="title text-xl mt-4 tracking-wider text-gray-900 leading-[1.7rem] mb-5 ">
|
<p className="title text-xl mt-4 tracking-wider text-gray-900 leading-[1.7rem] mb-5 ">
|
||||||
Немного обо мне и{" "}
|
A brief introduction about me and{" "}
|
||||||
<span className="bg-transparent md:bg-gray-100 bg-opacity-50 xl:bg-transparent">
|
<span className="bg-transparent md:bg-gray-100 bg-opacity-50 xl:bg-transparent">
|
||||||
{" "}
|
{" "}
|
||||||
моих интересах.
|
my interest.
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|
@ -72,7 +72,7 @@ export default function Page() {
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
className="mb-3">
|
className="mb-3">
|
||||||
<Button variation="primary">Прокрути</Button>
|
<Button variation="primary">Scroll Down</Button>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,9 @@
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;500;600;700&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;500;600;700&display=swap');
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,300;0,400;0,500;0,600;0,700;1,400&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,300;0,400;0,500;0,600;0,700;1,400&display=swap');
|
||||||
|
|
||||||
@import "tailwindcss";
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
html {
|
html {
|
||||||
font-size: 17px !important;
|
font-size: 17px !important;
|
||||||
|
|
@ -51,9 +53,9 @@ h1, h2, h3, h4, h5, h6 {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* h2, h3, h4, h5, h6 {
|
h2, h3, h4, h5, h6 {
|
||||||
@apply text-gray-600;
|
@apply text-gray-600;
|
||||||
} */
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
font-family: "Jost", sans-serif;
|
font-family: "Jost", sans-serif;
|
||||||
|
|
@ -62,7 +64,6 @@ span {
|
||||||
li {
|
li {
|
||||||
@apply list-none relative;
|
@apply list-none relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
li.active:after {
|
li.active:after {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
@ -71,7 +72,7 @@ li.active:after {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 2px;
|
height: 2px;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
import Navbar from "@/components/Navbar";
|
import Navbar from "@/components/Navbar";
|
||||||
import { config } from "@fortawesome/fontawesome-svg-core";
|
import { config } from "@fortawesome/fontawesome-svg-core";
|
||||||
|
|
@ -5,55 +6,60 @@ import "@fortawesome/fontawesome-svg-core/styles.css";
|
||||||
config.autoAddCss = false;
|
config.autoAddCss = false;
|
||||||
import "./nprogress.css";
|
import "./nprogress.css";
|
||||||
import { Analytics } from "@vercel/analytics/react";
|
import { Analytics } from "@vercel/analytics/react";
|
||||||
import ClientTopProgressBar from "@/components/ClientTopProgressBar";
|
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: "Korobtsev.ru | Личный сайт",
|
title: "Alvalens | Portofolio",
|
||||||
|
description:
|
||||||
description:
|
"My name is Alvalens, I'm a web developer and I'm passionate about it. I'm currently studying at Universitas Negeri Malang.",
|
||||||
"Всем привет! Я Виталий Коробцев и это мой личный сайт! Я хочу рассказать Вам о себе и своих проектах.",
|
author: "Alvalen Shafelbilyunazra",
|
||||||
|
siteUrl: "https://www.alvalens.my.id",
|
||||||
author: "Виталий Коробцев",
|
applicationName: "Alvalens",
|
||||||
siteUrl: "https://www.korobtsev.ru",
|
keywords: [
|
||||||
applicationName: "Korobtsev.ru",
|
"alvalens",
|
||||||
|
"alvalen",
|
||||||
keywords: [
|
"alvalen shafel",
|
||||||
"Korobtsev",
|
"shafel",
|
||||||
"Коробцев",
|
"alvalen shafelbilyunazra",
|
||||||
"Виталий Коробцев",
|
"alvalen shafel bilyunazra",
|
||||||
"Miguelle74",
|
"bloodfallen",
|
||||||
"Госис",
|
"alvalen porto",
|
||||||
"Гибридная Офисная Система",
|
"alvalen um",
|
||||||
"ОСГОС",
|
|
||||||
"Новый Крым",
|
|
||||||
"Технико",
|
|
||||||
],
|
],
|
||||||
|
openGraph: {
|
||||||
openGraph: {
|
|
||||||
type: "website",
|
type: "website",
|
||||||
url: "https://www.korobtsev.ru",
|
url: "https://www.alvalens.my.id",
|
||||||
title: "Korobtsev.ru | Личный сайт",
|
title: "Alvalens | Portofolio",
|
||||||
site_name: "Korobtsev.ru | Личный сайт",
|
site_name: "Alvalens | Portofolio",
|
||||||
description: "My name is Vitaliy Korobtsev, This is my website.",
|
description: "My name is Alvalens, This is my portofolio website.",
|
||||||
width: 1200,
|
width: 1200,
|
||||||
height: 630,
|
height: 630,
|
||||||
images: [
|
images: [
|
||||||
{
|
{
|
||||||
url: "/og-image-rev.png",
|
url: "/og-image-rev.png",
|
||||||
alt: "Korobtsev ",
|
alt: "Alvalens Portofolio",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
site_name: "Korobtsev.ru | Личный сайт",
|
site_name: "Alvalens | Portofolio",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
import Chat from "@/components/Chat";
|
||||||
|
|
||||||
|
const TopProgressBar = dynamic(() => import("@/components/TopProgressbar"), {
|
||||||
|
ssr: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default function RootLayout({ children }) {
|
export default function RootLayout({ children }) {
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<body>
|
<body>
|
||||||
<ClientTopProgressBar />
|
<TopProgressBar />
|
||||||
<Navbar />
|
<Navbar />
|
||||||
{children}
|
{children}
|
||||||
|
<Chat />
|
||||||
<Analytics />
|
<Analytics />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ const NotFound = () => (
|
||||||
<div className="flex justify-center items-center flex-col mb-5 space-y-10">
|
<div className="flex justify-center items-center flex-col mb-5 space-y-10">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="uppercase font-normal text-lg tracking-[8px] text-neutral-400 text-center">
|
<h2 className="uppercase font-normal text-lg tracking-[8px] text-neutral-400 text-center">
|
||||||
А тут совсем ничего нет...
|
Page not found
|
||||||
</h2>
|
</h2>
|
||||||
<h1 className="text-4xl font-medium text-neutral-900 text-center">
|
<h1 className="text-4xl font-medium text-neutral-900 text-center">
|
||||||
404
|
404
|
||||||
|
|
@ -20,7 +20,7 @@ const NotFound = () => (
|
||||||
<button
|
<button
|
||||||
onClick={() => window.history.back()}
|
onClick={() => window.history.back()}
|
||||||
className="text-lg text-neutral-900 hover:text-neutral-800 text-center border-2 border-gray-800 rounded-xl p-3 py-2 mt-2">
|
className="text-lg text-neutral-900 hover:text-neutral-800 text-center border-2 border-gray-800 rounded-xl p-3 py-2 mt-2">
|
||||||
Назад
|
Go Back
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Image from "next/image";
|
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import PropTypes from "prop-types";
|
import Image from "next/image";
|
||||||
import BlurImage from "@/public/image/placeholder/blur.jpg";
|
export default function projectCard({project, index, activeCategory}){
|
||||||
|
console.log(project.category[0], activeCategory);
|
||||||
export default function ProjectCard({ project, index, activeCategory }) {
|
console.log(project.category.includes(activeCategory));
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{project.category.includes(parseInt(activeCategory)) && (
|
{project.category.includes(parseInt(activeCategory)) && (
|
||||||
<Link href={"projects/" + project.slug} key={index}>
|
<Link href={"projects/" + project.slug} key={index}>
|
||||||
|
|
@ -24,12 +23,12 @@ export default function ProjectCard({ project, index, activeCategory }) {
|
||||||
}}>
|
}}>
|
||||||
<Image
|
<Image
|
||||||
src={project.thumbnail}
|
src={project.thumbnail}
|
||||||
alt="Korobtsev"
|
alt="Alvalens"
|
||||||
layout="fill"
|
layout="fill"
|
||||||
objectFit="cover"
|
objectFit="cover"
|
||||||
placeholder="blur"
|
placeholder="blur"
|
||||||
className="bg-slate-950 opacity-10 group-hover/tes:opacity-100 transition-all ease duration-500"
|
className="bg-slate-950 opacity-10 group-hover/tes:opacity-100 transition-all ease duration-500"
|
||||||
blurDataURL={BlurImage.src}
|
blurDataURL={project.thumbnail}
|
||||||
/>
|
/>
|
||||||
<div className="absolute top-0 left-0 bg-gray-600 px-4 py-2">
|
<div className="absolute top-0 left-0 bg-gray-600 px-4 py-2">
|
||||||
<h4 className="text-white">{project.year}</h4>
|
<h4 className="text-white">{project.year}</h4>
|
||||||
|
|
@ -57,10 +56,3 @@ export default function ProjectCard({ project, index, activeCategory }) {
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectCard.propTypes = {
|
|
||||||
project: PropTypes.object.isRequired,
|
|
||||||
index: PropTypes.number.isRequired,
|
|
||||||
activeCategory: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
|
|
||||||
.isRequired,
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
"use client";
|
"use client";
|
||||||
import { useState, useEffect, use } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import jsonData from "@/json/data.json";
|
import jsonData from "@/json/data.json";
|
||||||
|
|
||||||
|
|
@ -8,7 +8,6 @@ import { faGithub } from "@fortawesome/free-brands-svg-icons";
|
||||||
import { faArrowUpRightFromSquare } from "@fortawesome/free-solid-svg-icons";
|
import { faArrowUpRightFromSquare } from "@fortawesome/free-solid-svg-icons";
|
||||||
import NotFound from "@/app/not-found";
|
import NotFound from "@/app/not-found";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import BlurImage from "@/public/image/placeholder/blur.jpg";
|
|
||||||
import FixedButon from "@/components/FixedButton";
|
import FixedButon from "@/components/FixedButton";
|
||||||
import { faChevronLeft } from "@fortawesome/free-solid-svg-icons";
|
import { faChevronLeft } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { faChevronDown } from "@fortawesome/free-solid-svg-icons";
|
import { faChevronDown } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
@ -54,10 +53,9 @@ function ScrollDownButton() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function Page(props) {
|
function Page({ params }) {
|
||||||
const params = use(props.params);
|
const [data, setData] = useState(null);
|
||||||
const [data, setData] = useState(null);
|
useEffect(() => {
|
||||||
useEffect(() => {
|
|
||||||
const selectedData = jsonData.Projects.find(
|
const selectedData = jsonData.Projects.find(
|
||||||
(item) => item.slug === params.slug
|
(item) => item.slug === params.slug
|
||||||
);
|
);
|
||||||
|
|
@ -68,7 +66,7 @@ function Page(props) {
|
||||||
}
|
}
|
||||||
}, [params.slug]);
|
}, [params.slug]);
|
||||||
|
|
||||||
if (data === "404") {
|
if (data === "404") {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<NotFound />
|
<NotFound />
|
||||||
|
|
@ -100,7 +98,7 @@ function Page(props) {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="relative min-h-screen w-full gap-4 p-10 flex justify-center items-center flex-col mb-10 ">
|
<div className="relative min-h-screen w-full gap-4 p-10 flex justify-center items-center flex-col mb-10 ">
|
||||||
<FixedButon href="/projects">
|
<FixedButon href="/projects">
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
|
|
@ -114,7 +112,7 @@ function Page(props) {
|
||||||
<div className="min-h-screen sm:min-h-0 flex justify-center items-start flex-col mb-5 space-y-10 mx-auto">
|
<div className="min-h-screen sm:min-h-0 flex justify-center items-start flex-col mb-5 space-y-10 mx-auto">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="uppercase font-normal text-lg tracking-[8px] text-neutral-400">
|
<h2 className="uppercase font-normal text-lg tracking-[8px] text-neutral-400">
|
||||||
Проект
|
Project
|
||||||
</h2>
|
</h2>
|
||||||
<h1 className="text-4xl font-medium text-neutral-900">
|
<h1 className="text-4xl font-medium text-neutral-900">
|
||||||
{data.title}
|
{data.title}
|
||||||
|
|
@ -122,7 +120,7 @@ function Page(props) {
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2 className="uppercase font-normal text-lg tracking-[8px] text-neutral-400">
|
<h2 className="uppercase font-normal text-lg tracking-[8px] text-neutral-400">
|
||||||
Технологии
|
Technology
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-2xl font-normal text-neutral-900">
|
<p className="text-2xl font-normal text-neutral-900">
|
||||||
{data.tech.join(", ")}
|
{data.tech.join(", ")}
|
||||||
|
|
@ -130,7 +128,7 @@ function Page(props) {
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2 className="uppercase font-normal text-lg tracking-[8px] text-neutral-400">
|
<h2 className="uppercase font-normal text-lg tracking-[8px] text-neutral-400">
|
||||||
Год(ы)
|
Year
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-2xl font-normal text-neutral-900">
|
<p className="text-2xl font-normal text-neutral-900">
|
||||||
{data.year}
|
{data.year}
|
||||||
|
|
@ -139,14 +137,14 @@ function Page(props) {
|
||||||
{data.preview && (
|
{data.preview && (
|
||||||
<div>
|
<div>
|
||||||
<h2 className="uppercase font-normal text-lg tracking-[8px] text-neutral-400">
|
<h2 className="uppercase font-normal text-lg tracking-[8px] text-neutral-400">
|
||||||
Посмотреть
|
Preview
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-2xl font-normal text-neutral-900">
|
<p className="text-2xl font-normal text-neutral-900">
|
||||||
<a
|
<a
|
||||||
href={data.preview}
|
href={data.preview}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer">
|
rel="noopener noreferrer">
|
||||||
Перейти{" "}
|
Preview{" "}
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={faArrowUpRightFromSquare}
|
icon={faArrowUpRightFromSquare}
|
||||||
className="ml-3"
|
className="ml-3"
|
||||||
|
|
@ -158,7 +156,7 @@ function Page(props) {
|
||||||
{data.code && (
|
{data.code && (
|
||||||
<div>
|
<div>
|
||||||
<h2 className="uppercase font-normal text-lg tracking-[8px] text-neutral-400">
|
<h2 className="uppercase font-normal text-lg tracking-[8px] text-neutral-400">
|
||||||
Код (для Open Source)
|
Source Code
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-2xl font-normal text-neutral-900">
|
<p className="text-2xl font-normal text-neutral-900">
|
||||||
<a
|
<a
|
||||||
|
|
@ -177,7 +175,7 @@ function Page(props) {
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-start items-start flex-col mb-5 ">
|
<div className="flex justify-start items-start flex-col mb-5 ">
|
||||||
<h2 className="uppercase font-normal text-lg tracking-[8px] text-neutral-400">
|
<h2 className="uppercase font-normal text-lg tracking-[8px] text-neutral-400">
|
||||||
Описание
|
Description
|
||||||
</h2>
|
</h2>
|
||||||
{data.desc.map((desc, index) => (
|
{data.desc.map((desc, index) => (
|
||||||
<p
|
<p
|
||||||
|
|
@ -200,7 +198,7 @@ function Page(props) {
|
||||||
className="mb-5 h-auto max-h-screen max-w-7xl mx-auto"
|
className="mb-5 h-auto max-h-screen max-w-7xl mx-auto"
|
||||||
width={1920}
|
width={1920}
|
||||||
height={1080}
|
height={1080}
|
||||||
blurDataURL={BlurImage.src}
|
blurDataURL={image}
|
||||||
layout="responsive"
|
layout="responsive"
|
||||||
objectFit="contain"
|
objectFit="contain"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
"use client";
|
"use client"
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
@ -6,16 +6,18 @@ import { faGithub } from "@fortawesome/free-brands-svg-icons";
|
||||||
import { faArrowUpRightFromSquare } from "@fortawesome/free-solid-svg-icons";
|
import { faArrowUpRightFromSquare } from "@fortawesome/free-solid-svg-icons";
|
||||||
import FixedButon from "@/components/FixedButton";
|
import FixedButon from "@/components/FixedButton";
|
||||||
import { faChevronLeft } from "@fortawesome/free-solid-svg-icons";
|
import { faChevronLeft } from "@fortawesome/free-solid-svg-icons";
|
||||||
import Projects from "@/json/data.json";
|
import Projects from "@/json/data.json"
|
||||||
import Link from "next/link";
|
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page () {
|
||||||
const projects = Projects.Projects;
|
const projects = Projects.Projects
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<main className="overflow-hidden">
|
<main className="overflow-hidden">
|
||||||
<FixedButon href="/projects">
|
<FixedButon href="/projects">
|
||||||
<FontAwesomeIcon icon={faChevronLeft} className="text-black pr-10" />
|
<FontAwesomeIcon
|
||||||
|
icon={faChevronLeft}
|
||||||
|
className="text-black pr-10"
|
||||||
|
/>
|
||||||
</FixedButon>
|
</FixedButon>
|
||||||
<div className="min-h-screen w-screen mt-10 md:mt-0 p-10 flex justify-center items-center flex-col mb-10">
|
<div className="min-h-screen w-screen mt-10 md:mt-0 p-10 flex justify-center items-center flex-col mb-10">
|
||||||
<div className="flex justify-center items-center flex-col my-5 self-start ">
|
<div className="flex justify-center items-center flex-col my-5 self-start ">
|
||||||
|
|
@ -73,10 +75,10 @@ export default function Page() {
|
||||||
<table className="space-y-3">
|
<table className="space-y-3">
|
||||||
<thead>
|
<thead>
|
||||||
<tr className=" hover:shadow-md transition-all ease duration-500">
|
<tr className=" hover:shadow-md transition-all ease duration-500">
|
||||||
<th className="text-start">Год создания</th>
|
<th className="text-start">Year</th>
|
||||||
<th className="text-start">Название</th>
|
<th className="text-start">Title</th>
|
||||||
<th className="text-start">Технологии</th>
|
<th className="text-start">Technology</th>
|
||||||
<th className="text-start">Ссылки</th>
|
<th className="text-start">Link</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
@ -85,16 +87,16 @@ export default function Page() {
|
||||||
key={index}
|
key={index}
|
||||||
className="hover:shadow-md transition-all ease duration-500">
|
className="hover:shadow-md transition-all ease duration-500">
|
||||||
<td>{project.year}</td>
|
<td>{project.year}</td>
|
||||||
|
<td>{project.title}</td>
|
||||||
<td>
|
<td>
|
||||||
<Link href={`/projects/${project.slug}`}>
|
{project.tech.map((t) => `${t}, `)}
|
||||||
{project.title}
|
|
||||||
</Link>
|
|
||||||
</td>
|
</td>
|
||||||
<td>{project.tech.map((t) => `${t}, `)}</td>
|
|
||||||
<td>
|
<td>
|
||||||
<div className="flex flex-row justify-center items-center">
|
<div className="flex flex-row justify-center items-center">
|
||||||
{project.code && (
|
{project.code && (
|
||||||
<a href={project.code} title="Link to GitHub">
|
<a
|
||||||
|
href={project.code}
|
||||||
|
title="Link to GitHub">
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={faGithub}
|
icon={faGithub}
|
||||||
className="text-xl mr-2"
|
className="text-xl mr-2"
|
||||||
|
|
@ -106,7 +108,9 @@ export default function Page() {
|
||||||
href={project.preview}
|
href={project.preview}
|
||||||
title="Link to project preview">
|
title="Link to project preview">
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={faArrowUpRightFromSquare}
|
icon={
|
||||||
|
faArrowUpRightFromSquare
|
||||||
|
}
|
||||||
className="text-xl"
|
className="text-xl"
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -121,5 +125,5 @@ export default function Page() {
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
import Link from "next/link";
|
|
||||||
import Image from "next/image";
|
|
||||||
import { motion } from "framer-motion";
|
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import BlurImage from "@/public/image/placeholder/blur.jpg";
|
|
||||||
|
|
||||||
export default function ProjectCard({ project, index, activeCategory }) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{project.category.includes(parseInt(activeCategory)) && (
|
|
||||||
<Link href={"projects/" + project.slug} key={index}>
|
|
||||||
<motion.div
|
|
||||||
className="z-10 relative flex justify-center items-start flex-col mb-5 md:px-10 w-full h-auto bg-gray-400 group/tes py-20 px-5 md:py-2 aspect-video "
|
|
||||||
initial={{
|
|
||||||
opacity: 0,
|
|
||||||
x: -200,
|
|
||||||
}}
|
|
||||||
whileInView={{
|
|
||||||
opacity: 1,
|
|
||||||
x: 0,
|
|
||||||
}}
|
|
||||||
transition={{
|
|
||||||
type: "spring",
|
|
||||||
}}>
|
|
||||||
<Image
|
|
||||||
src={project.thumbnail}
|
|
||||||
alt="Korobtsev"
|
|
||||||
layout="fill"
|
|
||||||
objectFit="cover"
|
|
||||||
placeholder="blur"
|
|
||||||
className="bg-slate-950 opacity-10 group-hover/tes:opacity-100 transition-all ease duration-500"
|
|
||||||
blurDataURL={BlurImage.src}
|
|
||||||
/>
|
|
||||||
<div className="absolute top-0 left-0 bg-gray-600 px-4 py-2">
|
|
||||||
<h4 className="text-white">{project.year}</h4>
|
|
||||||
</div>
|
|
||||||
<div className="transition-all ease duration-500 opacity-100 content text-center group-hover/tes:opacity-0 z-10">
|
|
||||||
<h1 className="text-3xl font-bold mb-3">{project.title}</h1>
|
|
||||||
<p>
|
|
||||||
{project.desc[0].length > 125
|
|
||||||
? `${project.desc[0].slice(0, 125)}...`
|
|
||||||
: project.desc[0]}
|
|
||||||
</p>
|
|
||||||
<div className="flex justify-center items-center flex-row mt-5 flex-wrap">
|
|
||||||
{project.tech.map((t, index) => (
|
|
||||||
<span
|
|
||||||
key={index}
|
|
||||||
className="m-1 px-4 py-2 bg-gray-600 text-white ">
|
|
||||||
{t}
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</motion.div>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ProjectCard.propTypes = {
|
|
||||||
project: PropTypes.object.isRequired,
|
|
||||||
index: PropTypes.number.isRequired,
|
|
||||||
activeCategory: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
|
|
||||||
.isRequired,
|
|
||||||
};
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import Footer from "@/components/Footer";
|
import Footer from "@/components/Footer";
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: "Korobtsev.ru | Проекты"
|
title: "Alvalens | Projects",
|
||||||
};
|
};
|
||||||
export default function Layout({ children }) {
|
export default function Layout({ children }) {
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
export default function Loading() {
|
export default function Loading() {
|
||||||
return (
|
return (
|
||||||
<div className="fixed top-0 left-0 flex justify-center items-center h-screen w-screen bg-gray-300 z-[999]">
|
<>
|
||||||
<h1 className="text-4xl text-gray-900">Загрузка...</h1>
|
(
|
||||||
</div>
|
<div className="fixed top-0 left-0 flex justify-center items-center h-screen w-screen bg-gray-300 z-[999]">
|
||||||
|
<h1 className="text-4xl text-gray-900">Loading...</h1>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,27 +6,28 @@ import Button from "@/components/Button";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
// images
|
// images
|
||||||
import Intervyou1 from "@/public/image/projects/web/intervyou/intervyou-1.png";
|
import ReactChat1 from "@/public/image/projects/web/trivia/trivia1.png";
|
||||||
import Intervyou2 from "@/public/image/projects/web/intervyou/intervyou-2.png";
|
import ReactChat2 from "@/public/image/projects/web/trivia/trivia2.png";
|
||||||
import Intervyou3 from "@/public/image/projects/web/intervyou/intervyou-3.png";
|
import ReactChat3 from "@/public/image/projects/web/trivia/trivia3.png";
|
||||||
import ProjectAll from "@/public/image/projects.png";
|
import ProjectAll from "@/public/image/projects.png";
|
||||||
|
|
||||||
import Hr from "@/components/Hr";
|
import Hr from "@/components/Hr";
|
||||||
import ProjectCard from "./components/ProjectCard";
|
import ProjectCard from "./(project-card)";
|
||||||
import Projects from "@/json/data.json";
|
import Projects from "@/json/data.json";
|
||||||
import FixedButon from "@/components/FixedButton";
|
import FixedButon from "@/components/FixedButton";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { faChevronLeft } from "@fortawesome/free-solid-svg-icons";
|
import { faChevronLeft } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
const category = {
|
const category = {
|
||||||
1: "WEB разработка",
|
1: "Web Development",
|
||||||
2: "Linux и ПО",
|
2: "AI & Machine Learning",
|
||||||
9: "Другие",
|
9: "Other",
|
||||||
};
|
}
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const [activeCategory, setActiveCategory] = useState(1);
|
const [activeCategory, setActiveCategory] = useState(1);
|
||||||
const projects = Projects.Projects.filter((item) => item.show === true);
|
const projects = Projects.Projects.filter ((item) => item.show === true
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
|
|
@ -35,7 +36,10 @@ export default function Page() {
|
||||||
<>
|
<>
|
||||||
<main className="overflow-hidden">
|
<main className="overflow-hidden">
|
||||||
<FixedButon href="/#projects">
|
<FixedButon href="/#projects">
|
||||||
<FontAwesomeIcon icon={faChevronLeft} className="text-black pr-10" />
|
<FontAwesomeIcon
|
||||||
|
icon={faChevronLeft}
|
||||||
|
className="text-black pr-10"
|
||||||
|
/>
|
||||||
</FixedButon>
|
</FixedButon>
|
||||||
<div className="relative h-screen w-screen gap-4 p-10 flex justify-center items-center flex-col mb-10 overflow-hidden">
|
<div className="relative h-screen w-screen gap-4 p-10 flex justify-center items-center flex-col mb-10 overflow-hidden">
|
||||||
<div className="z-0 mb-48 md:mb-0 md:absolute top-1/4 md:right-[10%] md:-translate-y-16 ">
|
<div className="z-0 mb-48 md:mb-0 md:absolute top-1/4 md:right-[10%] md:-translate-y-16 ">
|
||||||
|
|
@ -46,7 +50,7 @@ export default function Page() {
|
||||||
className="bg-slate-300 rounded-sm h-[400px] md:h-[600px] w-[80vw] md:w-[30vw] grayscale hover:grayscale-0 ">
|
className="bg-slate-300 rounded-sm h-[400px] md:h-[600px] w-[80vw] md:w-[30vw] grayscale hover:grayscale-0 ">
|
||||||
<Image
|
<Image
|
||||||
src={ProjectAll}
|
src={ProjectAll}
|
||||||
alt="Korobtsev"
|
alt="Alvalens"
|
||||||
layout="fill"
|
layout="fill"
|
||||||
objectFit="cover"
|
objectFit="cover"
|
||||||
placeholder="blur"
|
placeholder="blur"
|
||||||
|
|
@ -55,14 +59,14 @@ export default function Page() {
|
||||||
</div>
|
</div>
|
||||||
<div className="z-10 w-full absolute md:w-auto md:left-[10%] top-[60%] md:top-1/3 col-span-2 flex flex-col justify-center items-start md:items-start text-start px-10 pt-4 backdrop-filter backdrop-blur-sm md:backdrop-blur-none md:backdrop-filter-none bg-gray-100 bg-opacity-50 md:bg-transparent md:pt-0">
|
<div className="z-10 w-full absolute md:w-auto md:left-[10%] top-[60%] md:top-1/3 col-span-2 flex flex-col justify-center items-start md:items-start text-start px-10 pt-4 backdrop-filter backdrop-blur-sm md:backdrop-blur-none md:backdrop-filter-none bg-gray-100 bg-opacity-50 md:bg-transparent md:pt-0">
|
||||||
<h1 className="md:bg-white bg-transparent lg:bg-transparent bg-opacity-50 md-px-0 text-black text-5xl md:text-8xl font-bold">
|
<h1 className="md:bg-white bg-transparent lg:bg-transparent bg-opacity-50 md-px-0 text-black text-5xl md:text-8xl font-bold">
|
||||||
Проекты
|
My Projects
|
||||||
</h1>
|
</h1>
|
||||||
<Hr />
|
<Hr />
|
||||||
<p className="title text-xl mt-4 tracking-wider text-gray-900 leading-[1.7rem] mb-5">
|
<p className="title text-xl mt-4 tracking-wider text-gray-900 leading-[1.7rem] mb-5">
|
||||||
Некоторые из проектов с моим учаcтием{" "}
|
List of my projects that I have done and{" "}
|
||||||
<span className="bg-transparent md:bg-gray-100 bg-opacity-50 xl:bg-transparent">
|
<span className="bg-transparent md:bg-gray-100 bg-opacity-50 xl:bg-transparent">
|
||||||
{" "}
|
{" "}
|
||||||
или под моим руководством.
|
currently working on.
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|
@ -76,14 +80,14 @@ export default function Page() {
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
className="mb-3">
|
className="mb-3">
|
||||||
<Button variation="primary">Прокрутить</Button>
|
<Button variation="primary">Scroll Down</Button>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-10 flex flex-col justify-start items-center w-full pl-10 md:pl-32">
|
<div className="mt-10 flex flex-col justify-start items-center w-full pl-10 md:pl-32">
|
||||||
<div className="flex justify-center items-center flex-col my-5 self-start ">
|
<div className="flex justify-center items-center flex-col my-5 self-start ">
|
||||||
<Hr variant="long"></Hr>
|
<Hr variant="long"></Hr>
|
||||||
<h1 className="text-3xl font-bold mt-3">Основное</h1>
|
<h1 className="text-3xl font-bold mt-3">Hightlight</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative w-screen mx-auto container gap-4 px-10 grid grid-cols-1 md:grid-cols-2 mb-10">
|
<div className="relative w-screen mx-auto container gap-4 px-10 grid grid-cols-1 md:grid-cols-2 mb-10">
|
||||||
|
|
@ -99,8 +103,8 @@ export default function Page() {
|
||||||
}}
|
}}
|
||||||
className="w-full h-full shadow-lg">
|
className="w-full h-full shadow-lg">
|
||||||
<Image
|
<Image
|
||||||
src={Intervyou1}
|
src={ReactChat1}
|
||||||
alt="Korobtsev"
|
alt="Alvalens"
|
||||||
layout="fill"
|
layout="fill"
|
||||||
objectFit="cover"
|
objectFit="cover"
|
||||||
placeholder="blur"
|
placeholder="blur"
|
||||||
|
|
@ -108,7 +112,7 @@ export default function Page() {
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute top-10 right-28 h-[30%] aspect-video grayscale hover:grayscale-0 transition-all ease duration-300 hover:scale-150">
|
<div className="absolute top-16 right-28 h-[30%] aspect-video grayscale hover:grayscale-0 transition-all ease duration-300 hover:scale-150">
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{
|
initial={{
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
|
|
@ -123,8 +127,8 @@ export default function Page() {
|
||||||
transition={{ delay: 0.3 }}
|
transition={{ delay: 0.3 }}
|
||||||
className="w-full h-full shadow-lg ">
|
className="w-full h-full shadow-lg ">
|
||||||
<Image
|
<Image
|
||||||
src={Intervyou3}
|
src={ReactChat3}
|
||||||
alt="Korobtsev"
|
alt="Alvalens"
|
||||||
layout="fill"
|
layout="fill"
|
||||||
objectFit="cover"
|
objectFit="cover"
|
||||||
placeholder="blur"
|
placeholder="blur"
|
||||||
|
|
@ -132,7 +136,7 @@ export default function Page() {
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute bottom-10 md:bottom-26 right-20 h-[35%] aspect-video grayscale hover:grayscale-0 transition-all ease duration-300 hover:scale-150">
|
<div className="absolute bottom-16 right-20 h-[35%] aspect-video grayscale hover:grayscale-0 transition-all ease duration-300 hover:scale-150">
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{
|
initial={{
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
|
|
@ -149,8 +153,8 @@ export default function Page() {
|
||||||
}}
|
}}
|
||||||
className="w-full h-full shadow-lg">
|
className="w-full h-full shadow-lg">
|
||||||
<Image
|
<Image
|
||||||
src={Intervyou2}
|
src={ReactChat2}
|
||||||
alt="Korobtsev"
|
alt="Alvalens"
|
||||||
layout="fill"
|
layout="fill"
|
||||||
objectFit="cover"
|
objectFit="cover"
|
||||||
placeholder="blur"
|
placeholder="blur"
|
||||||
|
|
@ -174,28 +178,29 @@ export default function Page() {
|
||||||
type: "spring",
|
type: "spring",
|
||||||
}}>
|
}}>
|
||||||
<h2 className="text-2xl font-bold tracking-wider mb-3">
|
<h2 className="text-2xl font-bold tracking-wider mb-3">
|
||||||
ОС ОСГОС
|
Generative Trivia
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-gray-600 text-justify title text-lg">
|
<p className="text-gray-600 text-justify title text-lg">
|
||||||
Операционная система на ядре Debian linux актуальной версии.
|
A generative trivia website built with Next.js,
|
||||||
Изначально разрабатывалась для одного из Государственных заказчиков.
|
Firebase, NextAuth, Google OAuth, and TypeScript.
|
||||||
На данный момент проект построен на Debian 13, имеет собственные репозитории
|
Users can generate trivia questions for predefined
|
||||||
и магазин приложений, инфраструктуру технической поддержки. Проект поддерживается
|
school subjects and answer them with a unique
|
||||||
и активно развивается, обрастая собственными сервисами и ПО, несмотря на
|
interactive input style. The application supports
|
||||||
высокую конкуренцию в данном серменте.
|
user authentication with Google and stores data in
|
||||||
Разработано несколько вариантов дистрибутива, в том числе и специализированные версии.
|
Firestore. You can see the source code of this
|
||||||
Серверное направление выделено в отдельный продукт.
|
project on my github account or preview the project
|
||||||
|
by clicking the button below.
|
||||||
</p>{" "}
|
</p>{" "}
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<Button variation="primary">
|
<Button variation="primary">
|
||||||
<Link href="projects/intervyou">Больше</Link>
|
<Link href="projects/trivia">More</Link>
|
||||||
</Button>
|
</Button>
|
||||||
<Button variation="secondary">
|
<Button variation="secondary">
|
||||||
<a
|
<a
|
||||||
href="https://www.osgos.ru"
|
href="https://generative-trivia.vercel.app/"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer">
|
rel="noopener noreferrer">
|
||||||
Просмотреть
|
Preview
|
||||||
</a>
|
</a>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -218,7 +223,7 @@ export default function Page() {
|
||||||
delay: 0.7,
|
delay: 0.7,
|
||||||
type: "spring",
|
type: "spring",
|
||||||
}}>
|
}}>
|
||||||
Другие ключевые проекты
|
Other Note Worthy Projects
|
||||||
</motion.h1>
|
</motion.h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -272,7 +277,7 @@ export default function Page() {
|
||||||
}}
|
}}
|
||||||
className="flex justify-center items-center flex-col my-5 self-start ">
|
className="flex justify-center items-center flex-col my-5 self-start ">
|
||||||
<Button variation="primary">
|
<Button variation="primary">
|
||||||
<Link href="projects/archive">Посмотреть все</Link>
|
<Link href="projects/archive">View In Archive</Link>
|
||||||
</Button>
|
</Button>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</main>
|
</main>
|
||||||
|
|
|
||||||
35
components/Chat.jsx
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
// components/Intelliticks.js
|
||||||
|
"use client"
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
// Component for Intelliticks chat widget
|
||||||
|
const Chat = () => {
|
||||||
|
useEffect(() => {
|
||||||
|
// Paste the Intelliticks script here
|
||||||
|
(function (I, L, T, i, c, k, s) {
|
||||||
|
if (I.iticks) return;
|
||||||
|
I.iticks = { host: c, settings: s, clientId: k, cdn: L, queue: [] };
|
||||||
|
var h = T.head || T.documentElement;
|
||||||
|
var e = T.createElement(i);
|
||||||
|
var l = I.location;
|
||||||
|
e.async = true;
|
||||||
|
e.src = (L || c) + "/client/inject-v2.min.js";
|
||||||
|
h.insertBefore(e, h.firstChild);
|
||||||
|
I.iticks.call = function (a, b) {
|
||||||
|
I.iticks.queue.push([a, b]);
|
||||||
|
};
|
||||||
|
})(
|
||||||
|
window,
|
||||||
|
"https://cdn-v1.intelliticks.com/prod/common",
|
||||||
|
document,
|
||||||
|
"script",
|
||||||
|
"https://app.intelliticks.com",
|
||||||
|
"u3cWw3sDpdKexqXCY_c",
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Chat;
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import dynamic from "next/dynamic";
|
|
||||||
|
|
||||||
const TopProgressBar = dynamic(() => import("@/components/TopProgressbar"), {
|
|
||||||
ssr: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default function ClientTopProgressBar() {
|
|
||||||
return <TopProgressBar />;
|
|
||||||
}
|
|
||||||
|
|
@ -23,7 +23,7 @@ export default function Footer() {
|
||||||
transition={{
|
transition={{
|
||||||
delay: 0.2,
|
delay: 0.2,
|
||||||
}}>
|
}}>
|
||||||
Что-то заинтересовало?
|
Want something like this?
|
||||||
</motion.h2>
|
</motion.h2>
|
||||||
<motion.h1
|
<motion.h1
|
||||||
className="text-5xl md:text-7xl font-medium mt-3 hover:underline whitespace-nowrap leading-none"
|
className="text-5xl md:text-7xl font-medium mt-3 hover:underline whitespace-nowrap leading-none"
|
||||||
|
|
@ -38,7 +38,7 @@ export default function Footer() {
|
||||||
transition={{
|
transition={{
|
||||||
delay: 0.5,
|
delay: 0.5,
|
||||||
}}>
|
}}>
|
||||||
Связаться{" "}
|
Get In Touch{" "}
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={faArrowAltCircleRight}
|
icon={faArrowAltCircleRight}
|
||||||
className="text-5xl ml-2 "
|
className="text-5xl ml-2 "
|
||||||
|
|
@ -49,7 +49,7 @@ export default function Footer() {
|
||||||
<footer className="flex justify-center items-center flex-col my-5 self-start]">
|
<footer className="flex justify-center items-center flex-col my-5 self-start]">
|
||||||
<p className="text-gray-800 text-sm">
|
<p className="text-gray-800 text-sm">
|
||||||
©{new Date().getFullYear()} -{" "}
|
©{new Date().getFullYear()} -{" "}
|
||||||
<span className="text-gray-700 text-lg">Korobtsev.ru</span>
|
<span className="text-gray-700 text-lg">Alvalens</span>
|
||||||
</p>
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ const NavItems = ({ isNavOpen, setIsNavOpen }) => {
|
||||||
type: "spring",
|
type: "spring",
|
||||||
stiffness: 400,
|
stiffness: 400,
|
||||||
damping: 40,
|
damping: 40,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -111,15 +111,15 @@ const NavItems = ({ isNavOpen, setIsNavOpen }) => {
|
||||||
animate={isNavOpen ? "open" : "closed"}
|
animate={isNavOpen ? "open" : "closed"}
|
||||||
initial={false}>
|
initial={false}>
|
||||||
<div className="relative backdrop-blur-sm opacity-95 flex flex-col items-center space-x-8 min-h-[100vh] bg-gray-700 min-w-[100vw] ">
|
<div className="relative backdrop-blur-sm opacity-95 flex flex-col items-center space-x-8 min-h-[100vh] bg-gray-700 min-w-[100vw] ">
|
||||||
<div className="flex flex-col items-center space-y-8 my-auto mx-0 z-50">
|
<div className="flex flex-col items-center space-y-8 my-auto mx-0">
|
||||||
{/* title */}
|
{/* title */}
|
||||||
<motion.h1
|
<motion.h1
|
||||||
variants={itemVariants}
|
variants={itemVariants}
|
||||||
animate={isNavOpen ? "open" : "closed"}
|
animate={isNavOpen ? "open" : "closed"}
|
||||||
className="text-6xl font-bold text-white ">
|
className="text-6xl font-bold text-white ">
|
||||||
Меню
|
Menu
|
||||||
</motion.h1>
|
</motion.h1>
|
||||||
<Link href="/#home">
|
<a href="/#home">
|
||||||
<div
|
<div
|
||||||
className="text-2xl font-bold text-white"
|
className="text-2xl font-bold text-white"
|
||||||
onClick={handleItemClick}>
|
onClick={handleItemClick}>
|
||||||
|
|
@ -128,10 +128,10 @@ const NavItems = ({ isNavOpen, setIsNavOpen }) => {
|
||||||
variants={itemVariants}
|
variants={itemVariants}
|
||||||
animate={isNavOpen ? "open" : "closed"}
|
animate={isNavOpen ? "open" : "closed"}
|
||||||
custom={0.1}>
|
custom={0.1}>
|
||||||
Главная
|
Home
|
||||||
</motion.h2>
|
</motion.h2>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</a>
|
||||||
<Link href="/about">
|
<Link href="/about">
|
||||||
<div
|
<div
|
||||||
onClick={handleItemClick}
|
onClick={handleItemClick}
|
||||||
|
|
@ -141,7 +141,7 @@ const NavItems = ({ isNavOpen, setIsNavOpen }) => {
|
||||||
variants={itemVariants}
|
variants={itemVariants}
|
||||||
animate={isNavOpen ? "open" : "closed"}
|
animate={isNavOpen ? "open" : "closed"}
|
||||||
custom={0.2}>
|
custom={0.2}>
|
||||||
Обо мне
|
About
|
||||||
</motion.h2>
|
</motion.h2>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
@ -154,11 +154,11 @@ const NavItems = ({ isNavOpen, setIsNavOpen }) => {
|
||||||
variants={itemVariants}
|
variants={itemVariants}
|
||||||
animate={isNavOpen ? "open" : "closed"}
|
animate={isNavOpen ? "open" : "closed"}
|
||||||
custom={0.3}>
|
custom={0.3}>
|
||||||
Проекты
|
Projects
|
||||||
</motion.h2>
|
</motion.h2>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="/#contact">
|
<a href="/#contact">
|
||||||
<div
|
<div
|
||||||
onClick={handleItemClick}
|
onClick={handleItemClick}
|
||||||
className="text-2xl font-bold text-white">
|
className="text-2xl font-bold text-white">
|
||||||
|
|
@ -167,10 +167,10 @@ const NavItems = ({ isNavOpen, setIsNavOpen }) => {
|
||||||
variants={itemVariants}
|
variants={itemVariants}
|
||||||
animate={isNavOpen ? "open" : "closed"}
|
animate={isNavOpen ? "open" : "closed"}
|
||||||
custom={0.4}>
|
custom={0.4}>
|
||||||
Контакты
|
Contact
|
||||||
</motion.h2>
|
</motion.h2>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
@ -200,7 +200,7 @@ const Navbar = () => {
|
||||||
className={`text-2xl ml-2 md:ml-0 transition-colors ease duration-500 ${
|
className={`text-2xl ml-2 md:ml-0 transition-colors ease duration-500 ${
|
||||||
isNavOpen ? "text-white" : ""
|
isNavOpen ? "text-white" : ""
|
||||||
}`}>
|
}`}>
|
||||||
Korobtsev.ru
|
Alvalens
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row items-center">
|
<div className="flex flex-row items-center">
|
||||||
|
|
@ -209,11 +209,15 @@ const Navbar = () => {
|
||||||
onClick={toggleNav}>
|
onClick={toggleNav}>
|
||||||
<div
|
<div
|
||||||
className={`w-10 h-1 bg-black rounded-full transition-all ease duration-300 ${
|
className={`w-10 h-1 bg-black rounded-full transition-all ease duration-300 ${
|
||||||
isNavOpen ? "rotate-45 bg-white translate-y-[2px]" : ""
|
isNavOpen
|
||||||
|
? "rotate-45 bg-white translate-y-[2px]"
|
||||||
|
: ""
|
||||||
}`}></div>
|
}`}></div>
|
||||||
<div
|
<div
|
||||||
className={`w-10 h-1 bg-black rounded-full transition-all ease duration-300 ${
|
className={`w-10 h-1 bg-black rounded-full transition-all ease duration-300 ${
|
||||||
isNavOpen ? "-rotate-45 -translate-y-2 bg-white" : ""
|
isNavOpen
|
||||||
|
? "-rotate-45 -translate-y-2 bg-white"
|
||||||
|
: ""
|
||||||
}`}></div>
|
}`}></div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ const fs = require("fs");
|
||||||
|
|
||||||
async function generateSitemap() {
|
async function generateSitemap() {
|
||||||
const sitemap = new SitemapStream({
|
const sitemap = new SitemapStream({
|
||||||
hostname: "https://korobtsev.ru/",
|
hostname: "https://alvalens.my.id/",
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add URLs to your sitemap
|
// Add URLs to your sitemap
|
||||||
|
|
|
||||||
|
|
@ -2,41 +2,6 @@
|
||||||
"Projects": [
|
"Projects": [
|
||||||
{
|
{
|
||||||
"show": true,
|
"show": true,
|
||||||
"title": "ОС ОСГОС",
|
|
||||||
"desc": [
|
|
||||||
"Операционная система Основная Составляющая Гибридной Офисной Системы - ОС ОСГОС, построена на ядре Debian linux актуальной версии. Является десктопной операционной системой общего назначения для использования до уровня секретности КС-4. Имеет свидетельство о Государственной регистрации. Изначально ОС разрабатывалась для одного из Государственных заказчиков.",
|
|
||||||
"ОС имеет собственные репозитории и магазин приложений, инфраструктуру технической поддержки. Проект поддерживается и активно развивается, обрастая собственными сервисами и ПО. Разработано несколько вариантов дистрибутива, в том числе и специализированные версии.",
|
|
||||||
"Основной упор при разработке был сделан на легкую интеграцию в существующую инфраструктуру, построенную на технологиях Microsoft. В результате ОС может бесшовно интегрироваться в гибридную экосистему без потери предоставляемого ей функционала. ОС может работать как в открытом, так и в полностью замкнутом стетевом периметре, обращаясь к локальным серверам обновления. Для поддержки ОС созданы все необходимые сервисы и дополнительное ПО, как на базе Open-Sourse продуктов, так и разработанное с нуля.",
|
|
||||||
"Второй целью являлась простота перехода Пользователя на ОС ОСГОС с других операционных систем и нетребовательность к железу. По результатам тестирования - все поставленные цели были достигнуты."
|
|
||||||
],
|
|
||||||
"year": "2022-Текущий",
|
|
||||||
"preview": "https://osgos.ru",
|
|
||||||
"code": "",
|
|
||||||
"thumbnail": "/image/projects/web/intervyou/intervyou-thumbnail.jpg",
|
|
||||||
"images": [
|
|
||||||
"/image/projects/web/intervyou/intervyou-1.png",
|
|
||||||
"/image/projects/web/intervyou/intervyou-2.png",
|
|
||||||
"/image/projects/web/intervyou/intervyou-3.png",
|
|
||||||
"/image/projects/web/intervyou/intervyou-4.png",
|
|
||||||
"/image/projects/web/intervyou/intervyou-5.png"
|
|
||||||
],
|
|
||||||
"tech": [
|
|
||||||
"C",
|
|
||||||
"C#",
|
|
||||||
"Bash",
|
|
||||||
"Go",
|
|
||||||
"Python",
|
|
||||||
"Web Dev",
|
|
||||||
"Database",
|
|
||||||
"Руководство",
|
|
||||||
"Архитектура",
|
|
||||||
"Инфраструктура"
|
|
||||||
],
|
|
||||||
"slug": "intervyou",
|
|
||||||
"category": [1, 2]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"show": false,
|
|
||||||
"title": "Client Personal Protfolio Website",
|
"title": "Client Personal Protfolio Website",
|
||||||
"desc": [
|
"desc": [
|
||||||
"A personal portfolio website that I created for a client. The website is built using two main tech, native (HTML, CSSS, JS) and react based (ReactJs, TailwindCSS, Typescript). The website also created based on the client design, and improved and sliced by me. The website has a few features such as a about page, a project page, resume page, and a contact page.",
|
"A personal portfolio website that I created for a client. The website is built using two main tech, native (HTML, CSSS, JS) and react based (ReactJs, TailwindCSS, Typescript). The website also created based on the client design, and improved and sliced by me. The website has a few features such as a about page, a project page, resume page, and a contact page.",
|
||||||
|
|
@ -101,7 +66,7 @@
|
||||||
],
|
],
|
||||||
"year": "2024",
|
"year": "2024",
|
||||||
"preview": "",
|
"preview": "",
|
||||||
"code": "https://github.com/Korobtsev/web-javanese-translation",
|
"code": "https://github.com/Alvalens/web-javanese-translation",
|
||||||
"thumbnail": "/image/projects/ai/javanese-translation/javanese-translation-thumbnail.jpg",
|
"thumbnail": "/image/projects/ai/javanese-translation/javanese-translation-thumbnail.jpg",
|
||||||
"images": [
|
"images": [
|
||||||
"/image/projects/ai/javanese-translation/javanese-translation1.png",
|
"/image/projects/ai/javanese-translation/javanese-translation1.png",
|
||||||
|
|
@ -127,7 +92,7 @@
|
||||||
],
|
],
|
||||||
"year": "2024",
|
"year": "2024",
|
||||||
"preview": "",
|
"preview": "",
|
||||||
"code": "https://github.com/Korobtsev/heart-attack-prediction",
|
"code": "https://github.com/Alvalens/heart-attack-prediction",
|
||||||
"thumbnail": "/image/projects/ai/heart-attack-prediction/heart-attack-prediction-thumbnail.jpg",
|
"thumbnail": "/image/projects/ai/heart-attack-prediction/heart-attack-prediction-thumbnail.jpg",
|
||||||
"images": [
|
"images": [
|
||||||
"/image/projects/ai/heart-attack-prediction/heart-attack1.png",
|
"/image/projects/ai/heart-attack-prediction/heart-attack1.png",
|
||||||
|
|
@ -156,7 +121,7 @@
|
||||||
],
|
],
|
||||||
"year": "2024",
|
"year": "2024",
|
||||||
"preview": "https://generative-trivia.vercel.app/",
|
"preview": "https://generative-trivia.vercel.app/",
|
||||||
"code": "https://github.com/Korobtsev/generative-trivia",
|
"code": "https://github.com/Alvalens/generative-trivia",
|
||||||
"thumbnail": "/image/projects/web/trivia/trivia-thumbnail.png",
|
"thumbnail": "/image/projects/web/trivia/trivia-thumbnail.png",
|
||||||
"images": [
|
"images": [
|
||||||
"/image/projects/web/trivia/trivia1.png",
|
"/image/projects/web/trivia/trivia1.png",
|
||||||
|
|
@ -236,8 +201,8 @@
|
||||||
"RandomZone is a feature that allows the player to get a random number of steps. The 3D dice is a feature that allows the player to roll a 3D dice. The game also has a few other features menuy screen and also loading screen.", "Beside that the game also has a audio theme that will play when the game is started. The game also has a few sound effects such as dice roll sound, and a sound when the player win the game."
|
"RandomZone is a feature that allows the player to get a random number of steps. The 3D dice is a feature that allows the player to roll a 3D dice. The game also has a few other features menuy screen and also loading screen.", "Beside that the game also has a audio theme that will play when the game is started. The game also has a few sound effects such as dice roll sound, and a sound when the player win the game."
|
||||||
],
|
],
|
||||||
"year": "2023",
|
"year": "2023",
|
||||||
"preview": "https://github.com/Korobtsev/ludo-game/releases/tag/pre-release",
|
"preview": "https://github.com/Alvalens/ludo-game/releases/tag/pre-release",
|
||||||
"code": "https://github.com/Korobtsev/ludo-game",
|
"code": "https://github.com/Alvalens/ludo-game",
|
||||||
"thumbnail": "/image/projects/other/LUDOang/ludo-thumbnail.png",
|
"thumbnail": "/image/projects/other/LUDOang/ludo-thumbnail.png",
|
||||||
"images": [
|
"images": [
|
||||||
"/image/projects/other/LUDOang/ludo1.png",
|
"/image/projects/other/LUDOang/ludo1.png",
|
||||||
|
|
@ -283,7 +248,7 @@
|
||||||
],
|
],
|
||||||
"year": "2023",
|
"year": "2023",
|
||||||
"preview": "https://react-chat-rouge.vercel.app",
|
"preview": "https://react-chat-rouge.vercel.app",
|
||||||
"code": "https://github.com/Korobtsev/react-realtime-chat",
|
"code": "https://github.com/Alvalens/react-realtime-chat",
|
||||||
"thumbnail": "/image/projects/web/react-chat/chat-thumbnail.png",
|
"thumbnail": "/image/projects/web/react-chat/chat-thumbnail.png",
|
||||||
"images": [
|
"images": [
|
||||||
"/image/projects/web/react-chat/chat1.png",
|
"/image/projects/web/react-chat/chat1.png",
|
||||||
|
|
@ -313,7 +278,7 @@
|
||||||
],
|
],
|
||||||
"year": "2023",
|
"year": "2023",
|
||||||
"preview": "https://react-tools-flax.vercel.app",
|
"preview": "https://react-tools-flax.vercel.app",
|
||||||
"code": "https://github.com/Korobtsev/react-tools",
|
"code": "https://github.com/Alvalens/react-tools",
|
||||||
"thumbnail": "/image/projects/web/tools/tools-thumbnail.png",
|
"thumbnail": "/image/projects/web/tools/tools-thumbnail.png",
|
||||||
"images": [
|
"images": [
|
||||||
"/image/projects/web/tools/tools.png",
|
"/image/projects/web/tools/tools.png",
|
||||||
|
|
@ -337,7 +302,7 @@
|
||||||
"Aleph is a Discord bot that I created for my dircord server to use. The bot is built using Python and the discord.py library. The bot has image grabber feauture that takes argument (keywords) and returns an image from the internet.", "Other than that this bot has a few other features such as utility command to delete a messages, and a command to get the server's ping. This bot is still in development and I plan to add more features in the future."],
|
"Aleph is a Discord bot that I created for my dircord server to use. The bot is built using Python and the discord.py library. The bot has image grabber feauture that takes argument (keywords) and returns an image from the internet.", "Other than that this bot has a few other features such as utility command to delete a messages, and a command to get the server's ping. This bot is still in development and I plan to add more features in the future."],
|
||||||
"year": "2023",
|
"year": "2023",
|
||||||
"preview": "",
|
"preview": "",
|
||||||
"code": "https://github.com/Korobtsev/aleph-discord-bot",
|
"code": "https://github.com/Alvalens/aleph-discord-bot",
|
||||||
"thumbnail": "/image/projects/other/bot/bot-thumbnail.png",
|
"thumbnail": "/image/projects/other/bot/bot-thumbnail.png",
|
||||||
"images": [
|
"images": [
|
||||||
"/image/projects/other/bot/bot1.png",
|
"/image/projects/other/bot/bot1.png",
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,7 @@ const withBundleAnalyzer = require("@next/bundle-analyzer")({
|
||||||
module.exports = withBundleAnalyzer({
|
module.exports = withBundleAnalyzer({
|
||||||
// your Next.js configuration
|
// your Next.js configuration
|
||||||
images: {
|
images: {
|
||||||
remotePatterns: [
|
domains: ["i.scdn.co"],
|
||||||
{
|
|
||||||
protocol: "https",
|
|
||||||
hostname: "i.scdn.co",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
// Add image optimization settings
|
|
||||||
formats: ["image/avif", "image/webp"],
|
|
||||||
minimumCacheTTL: 60,
|
|
||||||
},
|
},
|
||||||
webpack: (config, options) => {
|
webpack: (config, options) => {
|
||||||
config.module.rules.push({
|
config.module.rules.push({
|
||||||
|
|
@ -31,39 +23,8 @@ module.exports = withBundleAnalyzer({
|
||||||
key: "Content-Type",
|
key: "Content-Type",
|
||||||
value: "application/gzip",
|
value: "application/gzip",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
key: "Cache-Control",
|
|
||||||
value: "public, max-age=3600", // Cache for 1 hour
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
source: "/:path*",
|
|
||||||
headers: [
|
|
||||||
{
|
|
||||||
key: "X-Content-Type-Options",
|
|
||||||
value: "nosniff",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "X-Frame-Options",
|
|
||||||
value: "DENY",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "X-XSS-Protection",
|
|
||||||
value: "1; mode=block",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
// Add performance optimizations
|
|
||||||
reactStrictMode: true,
|
|
||||||
compiler: {
|
|
||||||
removeConsole:
|
|
||||||
process.env.NODE_ENV === "production"
|
|
||||||
? {
|
|
||||||
exclude: ["error", "warn"],
|
|
||||||
}
|
|
||||||
: false,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
57
package.json
|
|
@ -1,47 +1,38 @@
|
||||||
{
|
{
|
||||||
"name": "alvalen-portfolio-website",
|
"name": "my-porto-2",
|
||||||
"author": {
|
|
||||||
"name": "Alvalen Shafelbilyunazra",
|
|
||||||
"email": "alvalen.shafel04@gmail.com",
|
|
||||||
"url": "https://korobtsev.ru"
|
|
||||||
},
|
|
||||||
"license": "GPL-3.0",
|
|
||||||
"copyright": "Copyright (C) 2025 Alvalen Shafelbilyunazra",
|
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --turbopack",
|
"dev": "next dev --turbo",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"generate-sitemap": "node generate-sitemap.js"
|
"generate-sitemap": "node generate-sitemap.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-svg-core": "^7.0.1",
|
"@fortawesome/fontawesome-svg-core": "^6.4.2",
|
||||||
"@fortawesome/free-brands-svg-icons": "^7.0.1",
|
"@fortawesome/free-brands-svg-icons": "^6.4.2",
|
||||||
"@fortawesome/free-regular-svg-icons": "^7.0.1",
|
"@fortawesome/free-regular-svg-icons": "^6.4.2",
|
||||||
"@fortawesome/free-solid-svg-icons": "^7.0.1",
|
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
||||||
"@fortawesome/react-fontawesome": "^0.2.6",
|
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||||
"@fullpage/react-fullpage": "^0.1.49",
|
"@fullpage/react-fullpage": "^0.1.40",
|
||||||
"@tailwindcss/postcss": "^4.1.13",
|
"@vercel/analytics": "^1.0.2",
|
||||||
"@vercel/analytics": "^1.5.0",
|
"autoprefixer": "10.4.15",
|
||||||
"eslint": "9.33.0",
|
"eslint": "8.48.0",
|
||||||
"eslint-config-next": "15.4.6",
|
"eslint-config-next": "13.4.19",
|
||||||
"framer-motion": "^12.23.12",
|
"framer-motion": "^10.16.2",
|
||||||
"next": "15.4.6",
|
"next": "13.4.19",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "8.4.28",
|
||||||
"prop-types": "^15.8.1",
|
"react": "18.2.0",
|
||||||
"react": "19.1.1",
|
"react-dom": "18.2.0",
|
||||||
"react-dom": "19.1.1",
|
"sharp": "^0.32.5",
|
||||||
"sharp": "^0.34.3",
|
"sitemap": "^7.1.1",
|
||||||
"sitemap": "^8.0.0",
|
"swr": "^2.2.2",
|
||||||
"swr": "^2.3.6",
|
"tailwindcss": "3.3.3"
|
||||||
"tailwindcss": "^4.1.11"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@next/bundle-analyzer": "15.4.6",
|
"@next/bundle-analyzer": "^13.4.19",
|
||||||
"@types/react": "19.1.10",
|
"cross-env": "^7.0.3"
|
||||||
"cross-env": "^10.0.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
5861
pnpm-lock.yaml
generated
|
|
@ -1,6 +0,0 @@
|
||||||
packages:
|
|
||||||
- .
|
|
||||||
|
|
||||||
onlyBuiltDependencies:
|
|
||||||
- core-js
|
|
||||||
- sharp
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: {
|
plugins: {
|
||||||
"@tailwindcss/postcss": {},
|
tailwindcss: {},
|
||||||
},
|
autoprefixer: {},
|
||||||
};
|
},
|
||||||
|
}
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 110 KiB |
BIN
public/image/me.jpg
Normal file
|
After Width: | Height: | Size: 791 KiB |
|
Before Width: | Height: | Size: 103 KiB |
BIN
public/image/me1.jpg
Normal file
|
After Width: | Height: | Size: 984 KiB |
|
Before Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 170 KiB |
BIN
public/image/me3.jpg
Normal file
|
After Width: | Height: | Size: 662 KiB |
BIN
public/image/me4.jpeg
Normal file
|
After Width: | Height: | Size: 378 KiB |
|
Before Width: | Height: | Size: 259 KiB |
|
Before Width: | Height: | Size: 441 KiB |
|
Before Width: | Height: | Size: 155 KiB |
|
Before Width: | Height: | Size: 222 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 133 KiB After Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 253 KiB After Width: | Height: | Size: 515 KiB |
|
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 190 KiB |
|
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 249 KiB |
|
Before Width: | Height: | Size: 268 KiB After Width: | Height: | Size: 323 KiB |
|
Before Width: | Height: | Size: 186 KiB After Width: | Height: | Size: 214 KiB |
|
Before Width: | Height: | Size: 251 KiB |
|
Before Width: | Height: | Size: 171 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 158 KiB |
|
Before Width: | Height: | Size: 182 KiB |
|
Before Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 121 KiB |
|
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 126 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 135 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 160 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 125 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 798 KiB After Width: | Height: | Size: 918 KiB |
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 162 KiB |
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 153 KiB |
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 171 KiB |
|
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 208 KiB |
|
Before Width: | Height: | Size: 178 KiB After Width: | Height: | Size: 192 KiB |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 2 MiB |
|
Before Width: | Height: | Size: 507 KiB After Width: | Height: | Size: 588 KiB |
|
Before Width: | Height: | Size: 1 MiB After Width: | Height: | Size: 1.2 MiB |
|
|
@ -1,4 +1,4 @@
|
||||||
User-agent: *
|
User-agent: *
|
||||||
Disallow:
|
Disallow:
|
||||||
|
|
||||||
Sitemap: https://www.korobtsev.ru/sitemap2.xml
|
Sitemap: https://www.alvalens.my.id/sitemap2.xml
|
||||||
|
|
|
||||||
|
|
@ -8,57 +8,57 @@
|
||||||
|
|
||||||
|
|
||||||
<url>
|
<url>
|
||||||
<loc>https://www.korobtsev.ru/</loc>
|
<loc>https://www.alvalens.my.id/</loc>
|
||||||
<lastmod>2023-09-11T14:41:56+00:00</lastmod>
|
<lastmod>2023-09-11T14:41:56+00:00</lastmod>
|
||||||
<priority>1.00</priority>
|
<priority>1.00</priority>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://www.korobtsev.ru/about</loc>
|
<loc>https://www.alvalens.my.id/about</loc>
|
||||||
<lastmod>2023-09-11T14:41:56+00:00</lastmod>
|
<lastmod>2023-09-11T14:41:56+00:00</lastmod>
|
||||||
<priority>0.80</priority>
|
<priority>0.80</priority>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://www.korobtsev.ru/projects</loc>
|
<loc>https://www.alvalens.my.id/projects</loc>
|
||||||
<lastmod>2023-09-11T14:41:56+00:00</lastmod>
|
<lastmod>2023-09-11T14:41:56+00:00</lastmod>
|
||||||
<priority>0.80</priority>
|
<priority>0.80</priority>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://www.korobtsev.ru/projects/react-chat</loc>
|
<loc>https://www.alvalens.my.id/projects/react-chat</loc>
|
||||||
<lastmod>2023-09-11T14:41:56+00:00</lastmod>
|
<lastmod>2023-09-11T14:41:56+00:00</lastmod>
|
||||||
<priority>0.64</priority>
|
<priority>0.64</priority>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://www.korobtsev.ru/projects/react-tools</loc>
|
<loc>https://www.alvalens.my.id/projects/react-tools</loc>
|
||||||
<lastmod>2023-09-11T14:41:56+00:00</lastmod>
|
<lastmod>2023-09-11T14:41:56+00:00</lastmod>
|
||||||
<priority>0.64</priority>
|
<priority>0.64</priority>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://www.korobtsev.ru/projects/aleph-discord-bot</loc>
|
<loc>https://www.alvalens.my.id/projects/aleph-discord-bot</loc>
|
||||||
<lastmod>2023-09-11T14:41:56+00:00</lastmod>
|
<lastmod>2023-09-11T14:41:56+00:00</lastmod>
|
||||||
<priority>0.64</priority>
|
<priority>0.64</priority>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://www.korobtsev.ru/projects/lptk-cup-2023</loc>
|
<loc>https://www.alvalens.my.id/projects/lptk-cup-2023</loc>
|
||||||
<lastmod>2023-09-11T14:41:56+00:00</lastmod>
|
<lastmod>2023-09-11T14:41:56+00:00</lastmod>
|
||||||
<priority>0.64</priority>
|
<priority>0.64</priority>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://www.korobtsev.ru/projects/nu-ino-website</loc>
|
<loc>https://www.alvalens.my.id/projects/nu-ino-website</loc>
|
||||||
<lastmod>2023-09-11T14:41:56+00:00</lastmod>
|
<lastmod>2023-09-11T14:41:56+00:00</lastmod>
|
||||||
<priority>0.64</priority>
|
<priority>0.64</priority>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://www.korobtsev.ru/projects/unmuh-jember-landing-page</loc>
|
<loc>https://www.alvalens.my.id/projects/unmuh-jember-landing-page</loc>
|
||||||
<lastmod>2023-09-11T14:41:56+00:00</lastmod>
|
<lastmod>2023-09-11T14:41:56+00:00</lastmod>
|
||||||
<priority>0.64</priority>
|
<priority>0.64</priority>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://www.korobtsev.ru/projects/my-first-portofolio-website</loc>
|
<loc>https://www.alvalens.my.id/projects/my-first-portofolio-website</loc>
|
||||||
<lastmod>2023-09-11T14:41:56+00:00</lastmod>
|
<lastmod>2023-09-11T14:41:56+00:00</lastmod>
|
||||||
<priority>0.64</priority>
|
<priority>0.64</priority>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://www.korobtsev.ru/projects/archive</loc>
|
<loc>https://www.alvalens.my.id/projects/archive</loc>
|
||||||
<lastmod>2023-09-11T14:41:56+00:00</lastmod>
|
<lastmod>2023-09-11T14:41:56+00:00</lastmod>
|
||||||
<priority>0.64</priority>
|
<priority>0.64</priority>
|
||||||
</url>
|
</url>
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,12 @@
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
content: [
|
important: true,
|
||||||
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
content: [
|
||||||
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
|
||||||
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
'./components/**/*.{js,ts,jsx,tsx,mdx}',
|
||||||
],
|
'./app/**/*.{js,ts,jsx,tsx,mdx}',
|
||||||
theme: {
|
],
|
||||||
extend: {
|
theme: {
|
||||||
},
|
},
|
||||||
},
|
plugins: [],
|
||||||
plugins: [],
|
}
|
||||||
// Add this to reduce CSS file size in production
|
|
||||||
future: {
|
|
||||||
removeDeprecatedGapUtilities: true,
|
|
||||||
purgeLayersByDefault: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
|
||||||
24
vite-to_VS/.gitignore
vendored
|
|
@ -1,24 +0,0 @@
|
||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
pnpm-debug.log*
|
|
||||||
lerna-debug.log*
|
|
||||||
|
|
||||||
node_modules
|
|
||||||
dist
|
|
||||||
dist-ssr
|
|
||||||
*.local
|
|
||||||
|
|
||||||
# Editor directories and files
|
|
||||||
.vscode/*
|
|
||||||
!.vscode/extensions.json
|
|
||||||
.idea
|
|
||||||
.DS_Store
|
|
||||||
*.suo
|
|
||||||
*.ntvs*
|
|
||||||
*.njsproj
|
|
||||||
*.sln
|
|
||||||
*.sw?
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
import js from '@eslint/js'
|
|
||||||
import globals from 'globals'
|
|
||||||
import reactHooks from 'eslint-plugin-react-hooks'
|
|
||||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
|
||||||
import tseslint from 'typescript-eslint'
|
|
||||||
import { globalIgnores } from 'eslint/config'
|
|
||||||
|
|
||||||
export default tseslint.config([
|
|
||||||
globalIgnores(['dist']),
|
|
||||||
{
|
|
||||||
files: ['**/*.{ts,tsx}'],
|
|
||||||
extends: [
|
|
||||||
js.configs.recommended,
|
|
||||||
tseslint.configs.recommended,
|
|
||||||
reactHooks.configs['recommended-latest'],
|
|
||||||
reactRefresh.configs.vite,
|
|
||||||
],
|
|
||||||
languageOptions: {
|
|
||||||
ecmaVersion: 2020,
|
|
||||||
globals: globals.browser,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Vite + React + TS</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="root"></div>
|
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
{
|
|
||||||
"name": "repkorepo_test",
|
|
||||||
"private": true,
|
|
||||||
"version": "0.0.0",
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
|
||||||
"dev": "vite",
|
|
||||||
"build": "tsc -b && vite build",
|
|
||||||
"lint": "eslint .",
|
|
||||||
"preview": "vite preview"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"react": "^19.1.1",
|
|
||||||
"react-dom": "^19.1.1"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@eslint/js": "^9.33.0",
|
|
||||||
"@types/react": "^19.1.10",
|
|
||||||
"@types/react-dom": "^19.1.7",
|
|
||||||
"@vitejs/plugin-react-swc": "^4.0.0",
|
|
||||||
"eslint": "^9.33.0",
|
|
||||||
"eslint-plugin-react-hooks": "^5.2.0",
|
|
||||||
"eslint-plugin-react-refresh": "^0.4.20",
|
|
||||||
"globals": "^16.3.0",
|
|
||||||
"typescript": "~5.8.3",
|
|
||||||
"typescript-eslint": "^8.39.1",
|
|
||||||
"vite": "^7.1.2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB |