diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index cd768a6e..9210c217 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,4 +1,4 @@ -# These are supported funding model platforms - -github: Jonak-Adipta-Kalita -ko_fi: xxJonakAdiptaxx +# These are supported funding model platforms + +github: Jonak-Adipta-Kalita +ko_fi: xxJonakAdiptaxx diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 62ce150e..ef221abb 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,16 +1,16 @@ -version: 2 - -updates: - - package-ecosystem: "pip" - directory: "/backend" - schedule: - interval: "weekly" - day: "saturday" - open-pull-requests-limit: 3 - - - package-ecosystem: "npm" - directory: "/frontend" - schedule: - interval: "weekly" - day: "saturday" - open-pull-requests-limit: 2 +version: 2 + +updates: + - package-ecosystem: "pip" + directory: "/backend" + schedule: + interval: "weekly" + day: "saturday" + open-pull-requests-limit: 3 + + - package-ecosystem: "npm" + directory: "/frontend" + schedule: + interval: "weekly" + day: "saturday" + open-pull-requests-limit: 2 diff --git a/.prettierrc b/.prettierrc index 15569698..837f9cd2 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,7 +1,7 @@ -{ - "trailingComma": "es5", - "tabWidth": 4, - "semi": true, - "singleQuote": false, - "plugins": ["prettier-plugin-tailwindcss"] -} +{ + "trailingComma": "es5", + "tabWidth": 4, + "semi": true, + "singleQuote": false, + "plugins": ["prettier-plugin-tailwindcss"] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 4c646c32..f7a2ff56 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ -{ - "prettier.requireConfig": true -} +{ + "prettier.requireConfig": true +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c556eba2..b29d90a8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,123 +1,123 @@ -- Note: [Formatting](#format-code) the Code before Pushing is Important!! - -## Steps - -### Clone the Repository - -To Clone this Repository, open a terminal in a empty folder and type - -```bash -git clone https://github.com/Jonak-Adipta-Kalita/JAK-Website.git -``` - -### Installing The Required Modules - -To install the required modules, just open a terminal in the directory where this project is cloned. - -For the Backend: - -```bash -cd backend -pip install virtualenv -virtualenv venv -.\venv\Scripts\activate -pip install -r .\requirements.txt -``` - -For the Frontend - -```bash -cd frontend -npm i - -# or - -cd frontend -yarn -``` - -### Getting Django Secret Key - -`python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"` -type this in your Terminal. Copy the Output. This is Your DJANGO_SECRET_KEY. - -### Getting JWT Secret - -Use the [passwordgenerator](https://passwordsgenerator.net/) website to generate your secret. - -### Gettigng hCaptcha Keys - -### Getting PostgreSQL Credentials - -Create a Postgres Database using a online provider or you can create a local one, then you will get your Postgres credentials. - -### Passing the Required Credentials - -Create a new file `.env` in the `backend` folder and new file `.env.local` in the `frontend` folder. In the -`.env` file, paste the following: - -```env -PRODUCTION=False -SECRET_KEY= -JWT_SECRET= - -# Postgres -POSTGRESS_HOST= -POSTGRESS_DATABASE= -POSTGRESS_USER= -POSTGRESS_PORT= -POSTGRESS_PASSWORD= -``` - -and in the `.env.local` file, paste the following: - -```env -NEXT_PUBLIC_BACKEND_URL=http://127.0.0.1:8000 -NEXT_PUBLIC_HCAPTCHA_SITE_KEY= -HCAPTCHA_SECRET_KEY= -RAPIDAPI_KEY= -``` - -### Running the Backend: - -To run the backend, open a terminal in the directory. Now type - -```bash -cd backend -python manage.py makemigrations -python manage.py migrate -python manage.py runserver -``` - -to run the backtend. After you did that, go to [http://127.0.0.1:8000/](http://127.0.0.1:8000/) or -[http://localhost:8000/](http://localhost:8000/). - -### Running the Frontend - -To run the frontend, open a terminal in the directory. Now type - -```bash -cd frontend -npm run dev - -# or - -cd frontend -yarn dev -``` - -to run the frontend. After you did that, go to [http://127.0.0.1:3000/](http://127.0.0.1:3000/) or -[http://localhost:3000/](http://localhost:3000/). - -## Format Code - -In a terminal, type - -```bash -cd backend -.\venv\Scripts\activate -black . -deactivate -``` - -and press Enter. +- Note: [Formatting](#format-code) the Code before Pushing is Important!! + +## Steps + +### Clone the Repository + +To Clone this Repository, open a terminal in a empty folder and type + +```bash +git clone https://github.com/Jonak-Adipta-Kalita/JAK-Website.git +``` + +### Installing The Required Modules + +To install the required modules, just open a terminal in the directory where this project is cloned. + +For the Backend: + +```bash +cd backend +pip install virtualenv +virtualenv venv +.\venv\Scripts\activate +pip install -r .\requirements.txt +``` + +For the Frontend + +```bash +cd frontend +npm i + +# or + +cd frontend +yarn +``` + +### Getting Django Secret Key + +`python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"` +type this in your Terminal. Copy the Output. This is Your DJANGO_SECRET_KEY. + +### Getting JWT Secret + +Use the [passwordgenerator](https://passwordsgenerator.net/) website to generate your secret. + +### Gettigng hCaptcha Keys + +### Getting PostgreSQL Credentials + +Create a Postgres Database using a online provider or you can create a local one, then you will get your Postgres credentials. + +### Passing the Required Credentials + +Create a new file `.env` in the `backend` folder and new file `.env.local` in the `frontend` folder. In the +`.env` file, paste the following: + +```env +PRODUCTION=False +SECRET_KEY= +JWT_SECRET= + +# Postgres +POSTGRESS_HOST= +POSTGRESS_DATABASE= +POSTGRESS_USER= +POSTGRESS_PORT= +POSTGRESS_PASSWORD= +``` + +and in the `.env.local` file, paste the following: + +```env +NEXT_PUBLIC_BACKEND_URL=http://127.0.0.1:8000 +NEXT_PUBLIC_HCAPTCHA_SITE_KEY= +HCAPTCHA_SECRET_KEY= +RAPIDAPI_KEY= +``` + +### Running the Backend: + +To run the backend, open a terminal in the directory. Now type + +```bash +cd backend +python manage.py makemigrations +python manage.py migrate +python manage.py runserver +``` + +to run the backtend. After you did that, go to [http://127.0.0.1:8000/](http://127.0.0.1:8000/) or +[http://localhost:8000/](http://localhost:8000/). + +### Running the Frontend + +To run the frontend, open a terminal in the directory. Now type + +```bash +cd frontend +npm run dev + +# or + +cd frontend +yarn dev +``` + +to run the frontend. After you did that, go to [http://127.0.0.1:3000/](http://127.0.0.1:3000/) or +[http://localhost:3000/](http://localhost:3000/). + +## Format Code + +In a terminal, type + +```bash +cd backend +.\venv\Scripts\activate +black . +deactivate +``` + +and press Enter. diff --git a/README.md b/README.md index 16753f4d..00c603c6 100644 --- a/README.md +++ b/README.md @@ -1,61 +1,61 @@ -
-
- -[![Title](https://raw.githubusercontent.com/Jonak-Adipta-Kalita/JAK-Website/main/assets/title.png)](https://jonakadiptakalita.tk) - -
- -
- -
- -Welcome to the repository for My Website! This is the central hub where I store all the code, assets, and files that power my personal website. My Website is more than just a collection of web pages; it serves as a dynamic and interactive platform that encapsulates my identity, achievements, and passions. - -With meticulous attention to detail, My Website offers visitors a comprehensive glimpse into my professional journey, showcasing a curated selection of my projects, skills, and interests. Through an engaging and intuitive user interface, users can effortlessly navigate and explore the various sections, unlocking a rich tapestry of information and experiences. - -
- -
- -
- -![Website](https://img.shields.io/website?down_color=red&down_message=Offline&style=for-the-badge&up_color=green&up_message=Online&url=https%3A%2F%2Fjonakadiptakalita.vercel.app) -![License](https://img.shields.io/github/license/Jonak-Adipta-Kalita/JAK-Website?style=for-the-badge) -![GitHub Repo Stars](https://img.shields.io/github/stars/Jonak-Adipta-Kalita/JAK-Website?style=for-the-badge) -![GitHub Forks](https://img.shields.io/github/forks/Jonak-Adipta-Kalita/JAK-Website?style=for-the-badge) -![GitHub Watchers](https://img.shields.io/github/watchers/Jonak-Adipta-Kalita/JAK-Website?style=for-the-badge) -![Made by JAK](https://img.shields.io/badge/BeastNight%20TV-Made%20by%20JAK-blue?style=for-the-badge) - -
- -- [UI Design - Figma](https://www.figma.com/file/dWBap5xX9LiD8rB7uukptm/JAK-Website?type=design&node-id=100%3A2&mode=design&t=pjD43mpfHIj1ky4U-1) - -## Community support - -For additional help, you can use one of these channels to ask a question: - -- [Github Discussions](https://github.com/Jonak-Adipta-Kalita/JAK-Website/discussions) - Discussions with the community and the team. -- [Github Issues](https://github.com/Jonak-Adipta-Kalita/JAK-Website/issues) - For bug reports and feature requests. -- [Twitter](https://twitter.com/AdiptaJonak) - Get the product updates easily. - -## Roadmap - -Check out our [roadmap](https://github.com/users/Jonak-Adipta-Kalita/projects/4) to get informed of the latest features released and the upcoming ones. - -## Contributing - -Kindly read our [Contributing Guide](CONTRIBUTING.md) to learn and understand about our development process, how to propose bug fixes and improvements, and how to build and test your changes to **JAK Website**. - -## Technology(s) Used - -- Language: [Python](https://www.python.org/), [TypeScript](https://www.typescriptlang.org/) -- Language Framework: [Django](https://www.djangoproject.com/), [NextJS](https://nextjs.org/) -- Database: [PostgreSQL](https://www.postgresql.org/) -- Database Manager: [ElephantSQL](https://www.elephantsql.com/) -- Hosted: [Vercel](https://vercel.com/) - -## Contributors - - - - +
+
+ +[![Title](https://raw.githubusercontent.com/Jonak-Adipta-Kalita/JAK-Website/main/assets/title.png)](https://jonakadiptakalita.tk) + +
+ +
+ +
+ +Welcome to the repository for My Website! This is the central hub where I store all the code, assets, and files that power my personal website. My Website is more than just a collection of web pages; it serves as a dynamic and interactive platform that encapsulates my identity, achievements, and passions. + +With meticulous attention to detail, My Website offers visitors a comprehensive glimpse into my professional journey, showcasing a curated selection of my projects, skills, and interests. Through an engaging and intuitive user interface, users can effortlessly navigate and explore the various sections, unlocking a rich tapestry of information and experiences. + +
+ +
+ +
+ +![Website](https://img.shields.io/website?down_color=red&down_message=Offline&style=for-the-badge&up_color=green&up_message=Online&url=https%3A%2F%2Fjonakadiptakalita.vercel.app) +![License](https://img.shields.io/github/license/Jonak-Adipta-Kalita/JAK-Website?style=for-the-badge) +![GitHub Repo Stars](https://img.shields.io/github/stars/Jonak-Adipta-Kalita/JAK-Website?style=for-the-badge) +![GitHub Forks](https://img.shields.io/github/forks/Jonak-Adipta-Kalita/JAK-Website?style=for-the-badge) +![GitHub Watchers](https://img.shields.io/github/watchers/Jonak-Adipta-Kalita/JAK-Website?style=for-the-badge) +![Made by JAK](https://img.shields.io/badge/BeastNight%20TV-Made%20by%20JAK-blue?style=for-the-badge) + +
+ +- [UI Design - Figma](https://www.figma.com/file/dWBap5xX9LiD8rB7uukptm/JAK-Website?type=design&node-id=100%3A2&mode=design&t=pjD43mpfHIj1ky4U-1) + +## Community support + +For additional help, you can use one of these channels to ask a question: + +- [Github Discussions](https://github.com/Jonak-Adipta-Kalita/JAK-Website/discussions) - Discussions with the community and the team. +- [Github Issues](https://github.com/Jonak-Adipta-Kalita/JAK-Website/issues) - For bug reports and feature requests. +- [Twitter](https://twitter.com/AdiptaJonak) - Get the product updates easily. + +## Roadmap + +Check out our [roadmap](https://github.com/users/Jonak-Adipta-Kalita/projects/4) to get informed of the latest features released and the upcoming ones. + +## Contributing + +Kindly read our [Contributing Guide](CONTRIBUTING.md) to learn and understand about our development process, how to propose bug fixes and improvements, and how to build and test your changes to **JAK Website**. + +## Technology(s) Used + +- Language: [Python](https://www.python.org/), [TypeScript](https://www.typescriptlang.org/) +- Language Framework: [Django](https://www.djangoproject.com/), [NextJS](https://nextjs.org/) +- Database: [PostgreSQL](https://www.postgresql.org/) +- Database Manager: [ElephantSQL](https://www.elephantsql.com/) +- Hosted: [Vercel](https://vercel.com/) + +## Contributors + + + + diff --git a/SECURITY.md b/SECURITY.md index 11874c21..3380ed2b 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,5 +1,5 @@ -# Security Policy - -## Reporting a Vulnerability - -If you notice a security vulnerability, please open up an [Issue](https://github.com/Jonak-Adipta-Kalita/JAK-Website/issues)! +# Security Policy + +## Reporting a Vulnerability + +If you notice a security vulnerability, please open up an [Issue](https://github.com/Jonak-Adipta-Kalita/JAK-Website/issues)! diff --git a/components.json b/components.json index aaba37d4..78d60f7a 100644 --- a/components.json +++ b/components.json @@ -1,22 +1,22 @@ -{ - "$schema": "https://ui.shadcn.com/schema.json", - "style": "new-york", - "rsc": true, - "tsx": true, - "tailwind": { - "config": "", - "css": "src/app/globals.css", - "baseColor": "neutral", - "cssVariables": true, - "prefix": "" - }, - "iconLibrary": "lucide", - "aliases": { - "components": "@/components", - "utils": "@/lib/utils", - "ui": "@/components/ui", - "lib": "@/lib", - "hooks": "@/hooks" - }, - "registries": {} -} +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "registries": {} +} diff --git a/eslint.config.mjs b/eslint.config.mjs index b751c14f..cda2a7d9 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,16 +1,16 @@ -import { dirname } from "path"; -import { fileURLToPath } from "url"; -import { FlatCompat } from "@eslint/eslintrc"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -const compat = new FlatCompat({ - baseDirectory: __dirname, -}); - -const eslintConfig = [ - ...compat.extends("next/core-web-vitals", "next/typescript", "prettier"), -]; - -export default eslintConfig; +import { dirname } from "path"; +import { fileURLToPath } from "url"; +import { FlatCompat } from "@eslint/eslintrc"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const compat = new FlatCompat({ + baseDirectory: __dirname, +}); + +const eslintConfig = [ + ...compat.extends("next/core-web-vitals", "next/typescript", "prettier"), +]; + +export default eslintConfig; diff --git a/next.config.ts b/next.config.ts index f20f687e..c32f48d1 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,7 @@ -import type { NextConfig } from "next"; - -const nextConfig: NextConfig = { - /* config options here */ -}; - -export default nextConfig; +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + /* config options here */ +}; + +export default nextConfig; diff --git a/package.json b/package.json index d622ba27..53e68736 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "react": "19.1.0", "react-dom": "19.1.0", "tailwind-merge": "^3.3.1", - "zustand": "^5.0.8" + "zustand": "^5.0.11" }, "devDependencies": { "@eslint/eslintrc": "^3", diff --git a/postcss.config.mjs b/postcss.config.mjs index 904895e9..c4964caf 100644 --- a/postcss.config.mjs +++ b/postcss.config.mjs @@ -1,5 +1,5 @@ -const config = { - plugins: ["@tailwindcss/postcss"], -}; - -export default config; +const config = { + plugins: ["@tailwindcss/postcss"], +}; + +export default config; diff --git a/public/My Pic.png b/public/My Pic.png new file mode 100644 index 00000000..06df0b6c Binary files /dev/null and b/public/My Pic.png differ diff --git a/src/app/globals.css b/src/app/globals.css index 4c48a7a9..6751eacf 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -11,12 +11,13 @@ --color-fg-lobby-dark: #a0b5e2; --color-fg-lobby-extradark: #96adde; - --color-fg-programming-main: #c5d3ed; + --color-fg-programming-text: #c5d3ed; + --color-fg-programming-primary: #3faff5; + --color-fg-programming-secondary: #9dabdc; --color-bg-lobby: #121212; - --color-bg-programming: #0E0E0E; --color-bg-highlight: #131821; - --color-bg-curtain: #6082B6; + --color-bg-curtain: #6082b6; --color-selection: #2a3242; --color-selection-fg: #b5c5e9; @@ -33,8 +34,7 @@ @layer base { body { - /* TODO: Add Custom Scrollbar */ - @apply cursor-default overflow-x-hidden; + @apply cursor-default overflow-x-hidden overflow-y-auto; } ::selection { @@ -44,6 +44,15 @@ p { cursor: text; } + + .aboutText { + @apply text-fg-programming-text cursor-text text-xl font-bold; + font-size: clamp(0.75rem, 0.6rem + 0.4vw, 1rem); + } + + .aboutText-highlight { + @apply text-fg-programming-primary cursor-pointer decoration-dashed underline-offset-4 hover:underline; + } } @layer components { diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 8627e987..9b9fa92c 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -33,7 +33,7 @@ const RootLayout = ({ return ( {children} diff --git a/src/app/music/layout.tsx b/src/app/music/layout.tsx index 84dcf916..10ba22ab 100644 --- a/src/app/music/layout.tsx +++ b/src/app/music/layout.tsx @@ -11,11 +11,7 @@ const MusicLayout = ({ }: Readonly<{ children: React.ReactNode; }>) => { - return ( - - {children} - - ) -} + return {children}; +}; export default MusicLayout; diff --git a/src/app/music/page.tsx b/src/app/music/page.tsx index 5126a5a9..f27b89d4 100644 --- a/src/app/music/page.tsx +++ b/src/app/music/page.tsx @@ -1,5 +1,5 @@ const MusicPage = () => { - return
-} + return
; +}; export default MusicPage; diff --git a/src/app/polyglot/layout.tsx b/src/app/polyglot/layout.tsx index db947fb2..13c87a20 100644 --- a/src/app/polyglot/layout.tsx +++ b/src/app/polyglot/layout.tsx @@ -1,4 +1,4 @@ -import PageReveal from "@/components/Curtain/PageReveal" +import PageReveal from "@/components/Curtain/PageReveal"; import { Metadata } from "next"; export const metadata: Metadata = { @@ -9,13 +9,9 @@ export const metadata: Metadata = { const PolyglotLayout = ({ children, }: Readonly<{ - children: React.ReactNode + children: React.ReactNode; }>) => { - return ( - - {children} - - ) -} + return {children}; +}; export default PolyglotLayout; diff --git a/src/app/polyglot/page.tsx b/src/app/polyglot/page.tsx index 527f1759..45ff48dc 100644 --- a/src/app/polyglot/page.tsx +++ b/src/app/polyglot/page.tsx @@ -1,5 +1,5 @@ const PolyglotPage = () => { - return
-} + return
; +}; export default PolyglotPage; diff --git a/src/app/productivity/layout.tsx b/src/app/productivity/layout.tsx index 7e839baf..d4d25670 100644 --- a/src/app/productivity/layout.tsx +++ b/src/app/productivity/layout.tsx @@ -1,4 +1,4 @@ -import PageReveal from "@/components/Curtain/PageReveal" +import PageReveal from "@/components/Curtain/PageReveal"; import { Metadata } from "next"; export const metadata: Metadata = { @@ -9,13 +9,9 @@ export const metadata: Metadata = { const ProductivityLayout = ({ children, }: Readonly<{ - children: React.ReactNode + children: React.ReactNode; }>) => { - return ( - - {children} - - ) -} + return {children}; +}; export default ProductivityLayout; diff --git a/src/app/productivity/page.tsx b/src/app/productivity/page.tsx index 751b2d03..9c96898e 100644 --- a/src/app/productivity/page.tsx +++ b/src/app/productivity/page.tsx @@ -1,5 +1,5 @@ const ProductivityPage = () => { - return
-} + return
; +}; export default ProductivityPage; diff --git a/src/app/programming/AboutMe.tsx b/src/app/programming/AboutMe.tsx new file mode 100644 index 00000000..39b8bf56 --- /dev/null +++ b/src/app/programming/AboutMe.tsx @@ -0,0 +1,181 @@ +"use client"; + +import img from "@/../public/My Pic.png"; + +import Image from "next/image"; +import { useState } from "react"; +import { motion } from "framer-motion"; +import Typewriter from "@/components/Typewriter"; +import { useCurtain } from "@/lib/CurtainContext"; +import ScrollSnapSection from "@/components/ScrollSnapSection"; + +const AboutMe = () => { + const [typingDone, setTypingDone] = useState(false); + const { navigateTo } = useCurtain(); + + const listItems = [ + <> + I’m passionate about{" "} + Coding, exploring + various areas such as Web Development, Game Development, Machine + Learning, Computer Vision, IoT, ... + , + <> + navigateTo("/music")} + className="aboutText-highlight" + > + Music + {" "} + is a big part of my life; while I love playing different kinds of + instruments,{" "} + navigateTo("/music")} + className="aboutText-highlight" + > + Guitar + {" "} + holds a special place in my heart. + , + <> + Astronomy fascinates + me, and I’m always eager to learn more about the universe. + , + <> + I’m also a{" "} + navigateTo("/productivity")} + className="aboutText-highlight" + > + Book + {" "} + nerd who loves getting lost in captivating fiction and exploring new + worlds through stories. + , + <> + I have a huge love for{" "} + alert("some day fam!")} + className="aboutText-highlight" + > + Gaming + + , finding enjoyment in both playing and developing games. + , + <> + I am literate in{" "} + অসমীয়া (native),{" "} + English as well as{" "} + हिंदी and in the + process of learning{" "} + 日本語 - Polymathy + includes{" "} + navigateTo("/polyglot")} + className="aboutText-highlight text-fg-programming-text" + > + Polyglotism + {" "} + right? + , + ]; + + return ( + +
+ +
+ My Pic +
+
+
+
+ + +
+

+ +

+ + + {listItems.map((item, i) => ( + + {item} + + ))} + +
+
+ + ); +}; + +export default AboutMe; diff --git a/src/app/programming/ContactMe.tsx b/src/app/programming/ContactMe.tsx new file mode 100644 index 00000000..b18604f8 --- /dev/null +++ b/src/app/programming/ContactMe.tsx @@ -0,0 +1,11 @@ +import ScrollSnapSection from "@/components/ScrollSnapSection"; + +const ContactMe = () => { + return ( + +
{/* Use https://ui.shadcn.com/docs/forms */}
+
+ ); +}; + +export default ContactMe; diff --git a/src/app/programming/DynamicIsland.tsx b/src/app/programming/DynamicIsland.tsx index f1398b45..85b375ad 100644 --- a/src/app/programming/DynamicIsland.tsx +++ b/src/app/programming/DynamicIsland.tsx @@ -1,56 +1,96 @@ "use client"; -import { useState } from "react"; +import { useEffect } from "react"; import { motion } from "framer-motion"; -import { useRouter } from "next/navigation"; +import { usePathname, useRouter } from "next/navigation"; +import { + NAV_ITEMS, + HASH_ITEMS, + type NavItem, + useNavStore, +} from "@/lib/hooks/useNavStore"; + +function getActiveFromPathname(pathname: string): NavItem | null { + if (pathname.startsWith("/programming/journal")) return "Journal"; + if (pathname.startsWith("/programming/work")) return "Work"; + if (pathname === "/programming" || pathname === "/programming/") + return "About"; + + return null; +} const DynamicIsland = () => { - const [activeHeader, setActiveHeader] = useState("About"); const router = useRouter(); + const pathname = usePathname(); + + const { activeHeader, setActiveHeader, setIsScrolling } = useNavStore(); + + useEffect(() => { + const derived = getActiveFromPathname(pathname); + + const hash = window.location.hash; + + if (hash === "#testimonials") setActiveHeader("Testimonials"); + else if (hash === "#contact") setActiveHeader("Contact"); + else if (derived) setActiveHeader(derived); + }, [pathname]); + + useEffect(() => { + router.prefetch("/programming/journal"); + router.prefetch("/programming/work"); + }, []); return ( -
- {["About", "Work", "Journal", "Testimonials", "Contact"].map( - (name) => ( - { - e.preventDefault(); - - setActiveHeader(name); - - if (name === "Work" || name === "Journal") { - router.push(`/programming/${name.toLowerCase()}`); - } else { - let scrollHash = ""; - - if (name === "About") scrollHash = "" - else if (name === "Testimonials" || name === "Contact") - scrollHash = `#${name.toLowerCase()}` - - router.push(`/programming${scrollHash}`); - } - }} - className={`${name === "Contact" ? "hidden sm:inline" : ""} z-50 cursor-pointer rounded-full p-[clamp(0.75rem,1vw+0.5rem,1.75rem)] py-[clamp(0.5rem,0.5vw+0.25rem,0.75rem)]`} - animate={{ - backgroundColor: - activeHeader === name - ? "#1B1B1C" - : "rgba(0,0,0,0)", - }} - transition={{ - backgroundColor: { - duration: 0.2, - ease: "easeInOut", - }, - }} - > -

- {name} -

-
- ) - )} +
+ {NAV_ITEMS.map((name, index) => ( + { + e.preventDefault(); + + setIsScrolling(true); + setActiveHeader(name); + + if (name === "Work" || name === "Journal") { + router.push(`/programming/${name.toLowerCase()}`); + } else if (HASH_ITEMS.has(name)) { + router.push(`/programming#${name.toLowerCase()}`); + } + + setIsScrolling(false); + }} + className={`${name === "Contact" ? "hidden sm:inline" : ""} z-50 cursor-pointer rounded-full p-[clamp(0.75rem,1vw+0.5rem,1.75rem)] py-[clamp(0.5rem,0.5vw+0.25rem,0.75rem)]`} + initial={{ opacity: 0, y: -24 }} + animate={{ + opacity: 1, + y: 0, + backgroundColor: + activeHeader === name + ? "rgba(255,255,255,0.08)" + : "rgba(0,0,0,0)", + }} + transition={{ + opacity: { + duration: 0.4, + delay: index * 0.08, + ease: "easeOut", + }, + y: { + duration: 0.4, + delay: index * 0.08, + ease: "easeOut", + }, + backgroundColor: { + duration: 0.2, + ease: "easeInOut", + }, + }} + > +

+ {name} +

+
+ ))}
); }; diff --git a/src/app/programming/ProgrammingSkills.tsx b/src/app/programming/ProgrammingSkills.tsx new file mode 100644 index 00000000..fb8f06bf --- /dev/null +++ b/src/app/programming/ProgrammingSkills.tsx @@ -0,0 +1,32 @@ +import ScrollSnapSection from "@/components/ScrollSnapSection"; + +type Skill = { + pic: string; + name: string; + message: string; + link?: string; +} + +const skills: { + languages: Skill[], + frameworks: Skill[], + tools: Skill[] +} = { + languages: [], + frameworks: [], + tools: [], +}; + +const ProgrammingSkills = () => { + return ( + +
+ {/* Graph View! */} +
+
+ ); +}; + +export default ProgrammingSkills; diff --git a/src/app/programming/Testimonials.tsx b/src/app/programming/Testimonials.tsx new file mode 100644 index 00000000..0e6528a1 --- /dev/null +++ b/src/app/programming/Testimonials.tsx @@ -0,0 +1,19 @@ +import ScrollSnapSection from "@/components/ScrollSnapSection"; + +interface Testimonial { + name: string; + message: React.ReactNode; + href: string; +} + +const testimonials: Testimonial[] = []; + +const Testimonials = () => { + return ( + +
+
+ ); +}; + +export default Testimonials; diff --git a/src/app/programming/journal/page.tsx b/src/app/programming/journal/page.tsx index 5d986231..3ed062dc 100644 --- a/src/app/programming/journal/page.tsx +++ b/src/app/programming/journal/page.tsx @@ -1,7 +1,5 @@ const ProgrammingJournalPage = () => { - return ( -
- ) + return
{/* Layer on top of another ; Using Markdown to write the entries! */}
; }; export default ProgrammingJournalPage; diff --git a/src/app/programming/layout.tsx b/src/app/programming/layout.tsx index 7d558e80..dd034601 100644 --- a/src/app/programming/layout.tsx +++ b/src/app/programming/layout.tsx @@ -14,11 +14,11 @@ const ProgrammingLayout = ({ }>) => { return ( -
- -
- {children} -
+
+
+ +
+ {children}
); diff --git a/src/app/programming/page.tsx b/src/app/programming/page.tsx index c715f6ac..ac88b05d 100644 --- a/src/app/programming/page.tsx +++ b/src/app/programming/page.tsx @@ -1,3 +1,42 @@ -export default function ProgrammingPage() { - return
; -} +"use client"; + +import { HASH_ITEMS, useNavStore } from "@/lib/hooks/useNavStore"; +import AboutMe from "./AboutMe"; +import ContactMe from "./ContactMe"; +import ProgrammingSkills from "./ProgrammingSkills"; +import Testimonials from "./Testimonials"; +import { useEffect, useRef } from "react"; + +export default function ProgrammingPage() { + const activeHeader = useNavStore((s) => s.activeHeader); + const mainRef = useRef(null); + + useEffect(() => { + if (!activeHeader) return; + + const container = mainRef.current; + if (!container) return; + + if (HASH_ITEMS.has(activeHeader)) { + const target = document.getElementById(activeHeader.toLowerCase()); + if (!target) return; + + container.scrollTo({ top: target.offsetTop, behavior: "smooth" }); + } + }, [activeHeader]); + + return ( +
+ + + + + + + +
+ ); +} diff --git a/src/app/programming/work/page.tsx b/src/app/programming/work/page.tsx index 29938382..40fa8584 100644 --- a/src/app/programming/work/page.tsx +++ b/src/app/programming/work/page.tsx @@ -1,7 +1,5 @@ const ProgrammingWorkPage = () => { - return ( -
- ) + return
{/* Showcasing each project like done in Brittany Chang's Website */}
; }; export default ProgrammingWorkPage; diff --git a/src/components/Curtain/CurtainProvider.tsx b/src/components/Curtain/CurtainProvider.tsx index 5a25c1e1..398eb212 100644 --- a/src/components/Curtain/CurtainProvider.tsx +++ b/src/components/Curtain/CurtainProvider.tsx @@ -6,10 +6,18 @@ import { useCurtainTransition } from "@/lib/hooks/usePageTransition"; import { CurtainContext } from "@/lib/CurtainContext"; import { LeftCurtain, RightCurtain } from "./CurtainUI"; -const transition = { duration: 0.8, ease: [0.76, 0, 0.24, 1] as [number, number, number, number] }; +const transition = { + duration: 0.8, + ease: [0.76, 0, 0.24, 1] as [number, number, number, number], +}; -export const CurtainProvider = ({ children }: { children: React.ReactNode }) => { - const { curtainState, navigateTo, openOnMount, onClosed, onOpened } = useCurtainTransition(); +export const CurtainProvider = ({ + children, +}: { + children: React.ReactNode; +}) => { + const { curtainState, navigateTo, openOnMount, onClosed, onOpened } = + useCurtainTransition(); const leftControls = useAnimation(); const rightControls = useAnimation(); @@ -19,7 +27,10 @@ export const CurtainProvider = ({ children }: { children: React.ReactNode }) => if (curtainState === "closing") { await Promise.all([ leftControls.start({ x: "0%", transition }), - rightControls.start({ x: "0%", transition: { ...transition, delay: 0.5 } }), + rightControls.start({ + x: "0%", + transition: { ...transition, delay: 0.5 }, + }), ]); onClosed(); } else if (curtainState === "opening") { @@ -40,7 +51,7 @@ export const CurtainProvider = ({ children }: { children: React.ReactNode }) => @@ -48,7 +59,7 @@ export const CurtainProvider = ({ children }: { children: React.ReactNode }) => diff --git a/src/components/Curtain/CurtainUI.tsx b/src/components/Curtain/CurtainUI.tsx index ac6887e2..e3b98f26 100644 --- a/src/components/Curtain/CurtainUI.tsx +++ b/src/components/Curtain/CurtainUI.tsx @@ -1,9 +1,11 @@ +// TODO: After all the Pages! + const LeftCurtain = () => { - return
; + return
; }; const RightCurtain = () => { - return
; + return
; }; export { LeftCurtain, RightCurtain }; diff --git a/src/components/Curtain/PageReveal.tsx b/src/components/Curtain/PageReveal.tsx index 783f891f..c95b80c2 100644 --- a/src/components/Curtain/PageReveal.tsx +++ b/src/components/Curtain/PageReveal.tsx @@ -11,6 +11,6 @@ const PageReveal = ({ children }: { children: React.ReactNode }) => { }, [openOnMount]); return <>{children}; -} +}; export default PageReveal; diff --git a/src/components/GotoLobbyButton.tsx b/src/components/GotoLobbyButton.tsx index 160ea2b2..b15c9ff0 100644 --- a/src/components/GotoLobbyButton.tsx +++ b/src/components/GotoLobbyButton.tsx @@ -9,11 +9,7 @@ const GotoLobbyButton = () => { if (pathname === "/") return <>; - return ( - <> - {/* TODO */} - - ) -} + return <>{/* TODO */}; +}; export default GotoLobbyButton; diff --git a/src/components/ScrollSnapSection.tsx b/src/components/ScrollSnapSection.tsx new file mode 100644 index 00000000..0c47eb76 --- /dev/null +++ b/src/components/ScrollSnapSection.tsx @@ -0,0 +1,54 @@ +"use client"; + +import { NavItem, useNavStore } from "@/lib/hooks/useNavStore"; +import { cn } from "@/lib/utils"; +import { useRouter } from "next/navigation"; +import { useEffect, useRef } from "react"; + +const ScrollSnapSection = ({ + children, + nav, + id, + className, +}: { + children: React.ReactNode; + nav?: NavItem; + id: string; + className?: string; +}) => { + const { setActiveHeader, isScrolling } = useNavStore(); + const sectionRef = useRef(null); + const router = useRouter(); + + // TODO: Change Active Header & Link as per focus on section + // useEffect(() => { + // const observer = new IntersectionObserver( + // ([entry]) => { + // if (entry.isIntersecting && !isScrolling) { + // // if (nav) setActiveHeader(nav); + // // router.replace(`/programming#${id}`) + // } + // }, + // { threshold: 0.5 } + // ); + // + // if (sectionRef.current) observer.observe(sectionRef.current); + // + // return () => observer.disconnect(); + // }, []); + + return ( +
+ {children} +
+ ); +}; + +export default ScrollSnapSection; diff --git a/src/components/Typewriter.tsx b/src/components/Typewriter.tsx new file mode 100644 index 00000000..2652056d --- /dev/null +++ b/src/components/Typewriter.tsx @@ -0,0 +1,88 @@ +"use client"; + +import { useEffect, useRef, useState } from "react"; + +import type { Dispatch, SetStateAction } from "react"; + +interface TypewriterProps { + textParts: { + text: string; + highlight: boolean; + }[]; + + typingDone: boolean; + setTypingDone: Dispatch>; + + highlightTailwind: string; + + strokeTimeout: number; +} + +const Typewriter = ({ + typingDone, + setTypingDone, + textParts, + highlightTailwind, + strokeTimeout, +}: TypewriterProps) => { + const [displayedCount, setDisplayedCount] = useState(0); + const intervalRef = useRef(null); + + const FULL_STRING = textParts.map((part) => part.text).join(""); + + useEffect(() => { + intervalRef.current = setInterval(() => { + setDisplayedCount((prev) => { + if (prev >= FULL_STRING.length) { + clearInterval(intervalRef.current!); + return prev; + } + return prev + 1; + }); + }, strokeTimeout); + + return () => clearInterval(intervalRef.current!); + }, []); + + useEffect(() => { + if (displayedCount >= FULL_STRING.length && FULL_STRING.length > 0) { + setTypingDone(true); + } + }, [displayedCount]); + + const renderTypewriter = () => { + let remaining = displayedCount; + return textParts.map((part, i) => { + if (remaining <= 0) return null; + + const slice = part.text.slice(0, remaining); + remaining -= part.text.length; + + if (!slice) return null; + + return part.highlight ? ( + + {slice} + + ) : ( + {slice} + ); + }); + }; + + return ( + <> + {renderTypewriter()} + {!typingDone && ( + + )} + + ); +}; + +export default Typewriter; diff --git a/src/lib/CurtainContext.ts b/src/lib/CurtainContext.ts index 1eead542..d7e48b01 100644 --- a/src/lib/CurtainContext.ts +++ b/src/lib/CurtainContext.ts @@ -8,10 +8,10 @@ interface CurtainContextValue { } export const CurtainContext = createContext({ - navigateTo: () => { }, - openOnMount: () => { }, + navigateTo: () => {}, + openOnMount: () => {}, }); export const useCurtain = () => { return useContext(CurtainContext); -} +}; diff --git a/src/lib/hooks/useNavStore.ts b/src/lib/hooks/useNavStore.ts new file mode 100644 index 00000000..663ac986 --- /dev/null +++ b/src/lib/hooks/useNavStore.ts @@ -0,0 +1,33 @@ +import { create } from "zustand"; + +export const NAV_ITEMS = [ + "About", + "Work", + "Journal", + "Testimonials", + "Contact", +] as const; +export const HASH_ITEMS = new Set([ + "About", + "Skills", + "Testimonials", + "Contact", +]); + +export type NavItem = (typeof NAV_ITEMS)[number]; + +interface NavStore { + activeHeader: NavItem | null; + setActiveHeader: (item: NavItem | null) => void; + + isScrolling: boolean; + setIsScrolling: (val: boolean) => void; +} + +export const useNavStore = create((set) => ({ + activeHeader: null, + setActiveHeader: (item) => set({ activeHeader: item }), + + isScrolling: false, + setIsScrolling: (val) => set({ isScrolling: val }), +})); diff --git a/src/lib/utils.ts b/src/lib/utils.ts index aa65f25e..e6a8be07 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,6 +1,6 @@ -import { clsx, type ClassValue } from "clsx" -import { twMerge } from "tailwind-merge" +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) + return twMerge(clsx(inputs)); } diff --git a/tsconfig.json b/tsconfig.json index d39a4576..06ee0dbc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,27 +1,27 @@ -{ - "compilerOptions": { - "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "incremental": true, - "plugins": [ - { - "name": "next" - } - ], - "paths": { - "@/*": ["./src/*"] - } - }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] -} +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/yarn.lock b/yarn.lock index 73543575..4460d740 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3109,7 +3109,7 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== -zustand@^5.0.8: - version "5.0.8" - resolved "https://registry.yarnpkg.com/zustand/-/zustand-5.0.8.tgz#b998a0c088c7027a20f2709141a91cb07ac57f8a" - integrity sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw== +zustand@^5.0.11: + version "5.0.11" + resolved "https://registry.yarnpkg.com/zustand/-/zustand-5.0.11.tgz#99f912e590de1ca9ce6c6d1cab6cdb1f034ab494" + integrity sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==