Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the health-check domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /home/yogisv5/sitesbyyogi.com/wp-includes/functions.php on line 6260
MODULE 10: Final Project β€” Interactive Portfolio v2.0 - SitesByYogi

JavaScript Foundations β€’ StreetGeek Academy

🧠 MODULE 10: Final Project β€” Interactive Portfolio v2.0

Combine all the concepts from previous modules to create a dynamic, data-driven, interactive portfolio website that responds to user input,
loads data automatically, and saves preferences locally.

1) Project Overview

You’ll take your existing portfolio site and upgrade it into a fully interactive application using modern JavaScript.

🎯 Core Goals

2) Folder Structure

Make sure your project is clean and modular:

portfolio-site/
β”œβ”€β”€ index.html
β”œβ”€β”€ about.html
β”œβ”€β”€ projects.html
β”œβ”€β”€ contact.html
β”‚
β”œβ”€β”€ css/
β”‚   └── style.css
β”‚
β”œβ”€β”€ js/
β”‚   β”œβ”€β”€ app.js
β”‚   β”œβ”€β”€ data.js
β”‚   β”œβ”€β”€ utils.js
β”‚   └── theme.js
β”‚
└── images/
βœ… Each file handles a specific purpose (functions, data, themes).

3) Step-by-Step Project Tasks

πŸ”Ή Step 1: Setup Your Data

Create data.js:

const localProjects = [
  { title: "ShopBlocks WP",     tech: "PHP / WooCommerce",       link: "#" },
  { title: "Nfinite Dashboard", tech: "JavaScript / CSS Grid",   link: "#" },
  { title: "BoldGrid Addon",    tech: "WordPress / JavaScript",  link: "#" }
];

You’ll display this data if GitHub data isn’t available.

πŸ”Ή Step 2: Dynamic Project Rendering

In app.js:

async function loadProjects() {
  const projectGrid = document.getElementById("project-grid");
  projectGrid.innerHTML = "<p>⏳ Loading projects...</p>";

  try {
    const response = await fetch("https://api.github.com/users/SitesByYogi/repos");
    const repos = await response.json();
    const projects = repos.slice(0, 6);

    projectGrid.innerHTML = "";
    projects.forEach(repo => {
      const card = document.createElement("div");
      card.className = "card";
      card.innerHTML = `
        <h3>${repo.name}</h3>
        <p>${repo.language || "JavaScript"}</p>
        <a href="${repo.html_url}" target="_blank">View Project</a>
      `;
      projectGrid.appendChild(card);
    });

    localStorage.setItem("repos", JSON.stringify(projects));
    localStorage.setItem("lastUpdated", new Date().toLocaleString());

  } catch (error) {
    projectGrid.innerHTML = "<p>⚠️ Unable to fetch GitHub projects. Showing local data.</p>";
    localProjects.forEach(p => {
      const card = document.createElement("div");
      card.className = "card";
      card.innerHTML = `
        <h3>${p.title}</h3>
        <p>${p.tech}</p>
        <a href="${p.link}">View</a>
      `;
      projectGrid.appendChild(card);
    });
  }
}

loadProjects();
βœ… Pulls data from GitHub and falls back to local data if offline.

πŸ”Ή Step 3: Theme Toggle (Persistent)

In theme.js:

const toggleBtn = document.getElementById("theme-toggle");
const body = document.body;

// Apply saved preference
if (localStorage.getItem("theme") === "dark") {
  body.classList.add("dark");
}

// Toggle mode
toggleBtn.addEventListener("click", () => {
  body.classList.toggle("dark");
  const mode = body.classList.contains("dark") ? "dark" : "light";
  localStorage.setItem("theme", mode);
});
βœ… The theme is remembered even after closing or refreshing.

πŸ”Ή Step 4: Dynamic Greeting + Date

Add this near your header or hero area:

<p id="greeting"></p>
const greeting = document.getElementById("greeting");
const hour = new Date().getHours();

if (hour < 12) {
  greeting.textContent = "β˜€οΈ Good morning, StreetGeek!";
} else if (hour < 18) {
  greeting.textContent = "🌀️ Good afternoon, Developer!";
} else {
  greeting.textContent = "πŸŒ™ Good evening, Coder!";
}
βœ… Adds personality and relevance to your page.

πŸ”Ή Step 5: Form Validation + Feedback

Reuse the validated form logic from Module 8:

const form = document.getElementById("contactForm");
const status = document.getElementById("form-status");

form.addEventListener("submit", (e) => {
  e.preventDefault();
  const email = document.getElementById("email").value;

  if (!email.includes("@") || !email.includes(".")) {
    status.textContent = "⚠️ Invalid email!";
    status.style.color = "red";
    return;
  }

  status.textContent = "βœ… Message sent successfully!";
  status.style.color = "green";
  form.reset();
});
βœ… Keeps your UX smooth and professional.

πŸ”Ή Step 6: Add Scroll Animation (Bonus Enhancement)

const cards = document.querySelectorAll(".card");

window.addEventListener("scroll", () => {
  cards.forEach(card => {
    const position = card.getBoundingClientRect().top;
    if (position < window.innerHeight - 100) {
      card.style.opacity = "1";
      card.style.transform = "translateY(0)";
    }
  });
});

CSS

.card {
  opacity: 0;
  transform: translateY(50px);
  transition: all 0.6s ease-out;
}
βœ… Smooth entrance animations for your portfolio projects.

4) LocalStorage Data Recap

Add a β€œLast Updated” footer text:

<footer id="update-footer"></footer>
const footer = document.getElementById("update-footer");
const lastUpdated = localStorage.getItem("lastUpdated");

footer.textContent = lastUpdated
  ? `Last Updated: ${lastUpdated}`
  : "No data fetched yet.";
βœ… Shows the last time data was refreshed from GitHub.

5) Optional Features for Extra Credit

Feature Description
πŸ” Search Add a search bar that filters projects in real-time.
πŸ’¬ Modal Create a modal popup for project details.
🧠 Save State Use localStorage to remember selected filter or tab.
⏰ Update Button Add a button to re-fetch new data manually.
⚑ Lazy Loading Only load visible cards for faster performance.

6) Quiz Time

Show/Hide Questions

1) What is the purpose of the async keyword?

2) How does localStorage differ from sessionStorage?

3) How can you show fallback data when a fetch request fails?

4) What does await do inside a function?

5) How can you persist user preferences like dark mode?

7) Final Challenge Task β€” Portfolio v2.0 Completion Checklist

βœ… HTML

βœ… CSS

βœ… JavaScript

βœ… User Features

Bonus

Host it on GitHub Pages or your VPS β€” share your live interactive portfolio with the world 🌍.

🏁 Module 10 Summary

πŸŽ“ Congratulations, Developer!

You’ve officially completed JavaScript Foundations: From Static to Interactive.
You now understand the full front-end stack:
HTML + CSS + JavaScript β†’ Dynamic, Professional Websites.

Next up β†’ PHP Foundations: From Interactive Frontend to Dynamic Backend
We’ll connect your JavaScript-powered portfolio to a live database and server, creating full-stack applications that can store and serve real data.