Skip to content

A statically-compiled web app built using Next.js 15, It utilizes the GSAP and Framer Motion, and employs components from Aceternity UI, Namer UI, HextaUI, and React Bits.

License

Notifications You must be signed in to change notification settings

Northstrix/my-portfolio

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

35 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

My Portfolio

A statically-compiled web app built using Next.js 15, It utilizes the GSAP and Framer Motion, and employs components from Aceternity UI, Namer UI, HextaUI, and React Bits.

Check it out at https://maxim-bortnikov.netlify.app/

Features:

  • Like button

  • Link click count

  • Playground section with draggable cards (desktop only)

Playground Events:

  • Change the active card

  • Change the background of the wildcard

  • Change the inscriptions on the penumbra card

  • Show an animated rectangle notification

  • Show a full-viewport notification with Balatro

  • Change the inscription in the playground area

  • Change the style of the inscription in the playground area

  • Display a notification with a random intro quote from the "Losing Alice" TV show

Firestore Rules:

If you decide to use this as a template for your own portfolio and want the link/like counter to work, please follow these steps:

  • Run Firestore in test mode.

  • Create the necessary fields by modifying the relevant functions:

    Before:

     const handleProjectClick = async (link: string) => {
       incrementLinkCount(i18n.language, link);
     };
    
     async function incrementLinkCount(language: string, link: string) {
       const cleanedLink = sanitizeLink(link);
       const safeLink = encodeURIComponent(cleanedLink);
       const fieldName = `${language}:${safeLink}`;
       const docRef = doc(db, "data", "linkCounts");
       try {
         await updateDoc(docRef, { [fieldName]: increment(1) });
       } catch (error: any) {
         if (error.code === "not-found") {
           await setDoc(docRef, { [fieldName]: 1 }, { merge: true });
         } else {
           //console.error("Error incrementing link count:", error);
         }
       }
       return safeLink;
     }
    

    After:

    const handleProjectClick = async (link: string) => {
       for (const lang of languages) {
         incrementLinkCount(lang.code, link);
       }
     };
    
    async function incrementLinkCount(language: string, link: string) {
       const cleanedLink = sanitizeLink(link);
       const safeLink = encodeURIComponent(cleanedLink);
       const fieldName = `${language}:${safeLink}`;
       const docRef = doc(db, "data", "linkCounts");
       try {
         await updateDoc(docRef, { [fieldName]: increment(0) });
       } catch (error: any) {
         if (error.code === "not-found") {
           await setDoc(docRef, { [fieldName]: 0 }, { merge: true });
         } else {
           //console.error("Error incrementing link count:", error);
         }
       }
       return safeLink;
     }
    

    And clicking on each link-opening button to initialize the fields. Don’t forget to set up the fields for other types of links in a similar manner.

  • Restore the original functions.

  • Set the following rules for Firestore:

     service cloud.firestore {
       match /databases/{database}/documents {
     
         // LIKE COUNTS: Anyone can read and increment
         match /data/likeCounts {
           allow read: if true;
           allow update: if isIncrement(resource, request);
           allow create, delete: if false;
         }
     
         // LINK COUNTS: Anyone can increment, no one can read
         match /data/linkCounts {
           allow read: if false;
           allow update: if isIncrement(resource, request);
           allow create, delete: if false;
         }
     
         // SOCIAL COUNTS: Same as LINK COUNTS
         match /data/socialCounts {
           allow read: if false;
           allow update: if isIncrement(resource, request);
           allow create, delete: if false;
         }
       
         // ARTICLE COUNTS: Same as LINK COUNTS
         match /data/articleCounts {
           allow read: if false;
           allow update: if isIncrement(resource, request);
           allow create, delete: if false;
         }
       }
     
       // Only allow updates that increment a single existing numeric field
       function isIncrement(resource, request) {
         return request.writeFields.size() == 1 &&
           resource.data[request.writeFields[0]] is int &&
           request.resource.data[request.writeFields[0]] > resource.data[request.writeFields[0]] &&
           request.resource.data.keys().hasOnly(resource.data.keys());
       }
     }
    

Credit

The existence of this portfolio (at least in its current form) wouldn't've been possible without the following:

BUTTONS by TAYLOR

Spotlight Card by HextaUI

tabler-icons by tabler

lucide by lucide-icons

react-toastify by Fadi Khadra

react-i18next by i18next

firebase-js-sdk by firebase

Animated Tooltip by Aceternity UI

Daily UI#011 | Flash Message (Error/Success) by Julie Park

すりガラスなプロフィールカード by あしざわ - Webクリエイター

Neon Button by Thea

Signup Form from Aceternity UI

motion by motiondivision

GSAP by greensock

react-three-fiber by Poimandres

Bouncing Cube Loader by Haja Randriakoto

JTB studios - Link by Nico

AI Image Generator by Red Panda AI

Bento Grid by Aceternity UI

Lens by Aceternity UI

Glowing Effect by Aceternity UI

Draggable Card by Aceternity UI

Canvas Reveal Effect by Aceternity UI

Blog Post Header by Nodws

Confetti by HextaUI

Card hover effect by Aaron Iker

ogl by oframe

Balatro by React Bits

Aurora by React Bits

Letter Glitch by React Bits

Decrypted Text by React Bits

Blur Text by React Bits

animate-css by animate-css

Click Spark by React Bits

Silk by React Bits

Perplexity

Mistral's Le Chat

Used Namer UI components:

  • Halomot Button

  • Structured Block

  • Unfolding Sidebar

  • Blog Post Header

  • Fancy Notification

About

A statically-compiled web app built using Next.js 15, It utilizes the GSAP and Framer Motion, and employs components from Aceternity UI, Namer UI, HextaUI, and React Bits.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published