Guest Mode:

BCA Student Stories & UpdatesLive

You
Your StorySign in & share
Er. Manoj Kumar
D5
Er. ManojActive nowPages
Day 40Mini Project Capstone3.5 Hours Practical
Push to GitHub to Complete

Sprint 4 Capstone: University Resource Hub Single Page Application

This Sprint 4 Capstone unites everything learned in Advanced JavaScript: localStorage state persistence, async/await REST data fetching, event delegation, dynamic modals, and interactive UI state management into a complete University Resource Hub.

Interactive Learning Roadmap

Day 40 Step-by-Step Practical Flow

🎯1. What You Will Learn Today

✔Assemble Days 31–39 techniques into a complete, responsive Single Page Application (SPA).
✔Integrate LocalStorage for persistent bookmarks and study preferences.
✔Fetch dynamic course resources and syllabus data via asynchronous REST APIs or mock endpoints.
✔Build modal preview dialogs with backdrop dismissal, body scroll locks, and Escape key listeners.
✔Deploy the completed Capstone project to your personal GitHub repository and GitHub Pages.
Day 40 MasterclassMini Project Capstone

Sprint 4 Capstone: University Resource Hub Single Page Application — Comprehensive Practical Guide

Har concept ko visual mental models, code anatomy aur practical industry tips ke sath samjhein.

Real-World Purpose Aur Zaroorat

This Sprint 4 Capstone unites everything learned in Advanced JavaScript: localStorage state persistence, async/await REST data fetching, event delegation, dynamic modals, and interactive UI state management into a complete University Resource Hub.

Component Orchestration: Coordinate state across search filtering, bookmarks array, modal dialogs, and storage.
Offline Resilience: Read bookmarks and preferences from localStorage so the app works even when disconnected.
Clean Separation: Separate data fetching (async API), state management (arrays & storage), and DOM rendering.
Event Delegation: Use delegated click listeners on the resource grid to handle bookmarking and viewing details.
Sprint 4 Milestone: Represents graduation from DOM manipulation to building real Single Page Web Applications.
Dimaag Mein Bithane Ka Model

"Think of this project like building your own university digital library portal: students can search textbooks, bookmark papers for later study, open detailed preview cards in popups, and pick up right where they left off tomorrow."

💡 Pro Tip: Code ko ratta mat maaro; imagine karo ki browser har tag ko visually kaise draw karta hai.

👨‍🏫3. Teacher Demonstration Code

Examine how search filtering, modal popups, and localStorage bookmarking coordinate seamlessly.

Complete University Resource Hub SPA Architecturehtml
<!DOCTYPE html>
<html>
<head>
  <style>
    body { font-family: 'Segoe UI', sans-serif; background: #060c18; color: #fff; padding: 24px; }
    .app { max-width: 640px; margin: auto; }
    .search-row { display: flex; gap: 8px; margin-bottom: 20px; }
    input { flex: 1; padding: 10px; background: #0c1328; border: 1px solid #334155; color: #fff; border-radius: 6px; }
    .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 12px; }
    .card { background: #0c1328; border: 1px solid #1e293b; padding: 16px; border-radius: 10px; }
    button { padding: 6px 12px; border-radius: 6px; border: none; font-weight: bold; cursor: pointer; }
    .btn-view { background: #2563eb; color: #fff; }
    .btn-bm { background: #f59e0b; color: #000; margin-left: 6px; }
    .modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.8); display: none; align-items: center; justify-content: center; z-index: 1000; }
    .modal-overlay.open { display: flex; }
    .modal-body { background: #0c1328; border: 1px solid #334155; padding: 24px; border-radius: 12px; max-width: 440px; width: 90%; }
  </style>
</head>
<body>
  <div class="app">
    <h2>🎓 BCA University Resource Hub</h2>
    <div class="search-row">
      <input type="text" id="searchInput" placeholder="Search subjects (e.g. C, HTML, Math)..." />
      <span id="bmCount" style="align-self:center;font-weight:bold;color:#f59e0b;">⭐ 0 Bookmarks</span>
    </div>
    <div class="grid" id="resourceGrid"></div>
  </div>

  <div class="modal-overlay" id="modal">
    <div class="modal-body">
      <h3 id="modalTitle"></h3>
      <p id="modalDesc" style="color:#94a3b8;"></p>
      <button onclick="closeModal()" style="background:#ef4444;color:#fff;width:100%;">Close Modal (Esc)</button>
    </div>
  </div>

  <script>
    const resources = [
      { id: 1, title: "BCS-011: Computer Basics", desc: "Hardware, software, Von Neumann architecture, and OS fundamentals." },
      { id: 2, title: "BCSL-013: PC Software Lab", desc: "Hands-on lab for Windows commands, HTML5, and Linux basics." },
      { id: 3, title: "BCS-012: Basic Mathematics", desc: "Matrices, determinants, differential calculus, and algebra." },
      { id: 4, title: "MCS-011: Problem Solving & C", desc: "Pointers, memory allocation, arrays, and structured programming." }
    ];

    let bookmarks = JSON.parse(localStorage.getItem("bca_hub_bookmarks")) || [];
    const grid = document.getElementById("resourceGrid");
    const modal = document.getElementById("modal");

    function render(list) {
      grid.innerHTML = "";
      document.getElementById("bmCount").textContent = "⭐ " + bookmarks.length + " Bookmarks";
      list.forEach(r => {
        const isBm = bookmarks.includes(r.id);
        const card = document.createElement("div");
        card.className = "card";
        card.dataset.id = r.id;
        card.innerHTML = '<h4>' + r.title + '</h4>' +
          '<p style="color:#94a3b8;font-size:13px;">' + r.desc + '</p>' +
          '<button class="btn-view">View Details</button>' +
          '<button class="btn-bm">' + (isBm ? "★ Saved" : "☆ Save") + '</button>';
        grid.appendChild(card);
      });
    }

    // Delegated listener
    grid.addEventListener("click", (e) => {
      const viewBtn = e.target.closest(".btn-view");
      const bmBtn = e.target.closest(".btn-bm");
      const card = e.target.closest(".card");
      if (!card) return;
      const id = parseInt(card.dataset.id, 10);
      const res = resources.find(r => r.id === id);

      if (viewBtn && res) {
        document.getElementById("modalTitle").textContent = res.title;
        document.getElementById("modalDesc").textContent = res.desc;
        modal.classList.add("open");
        document.body.style.overflow = "hidden";
      }

      if (bmBtn) {
        if (bookmarks.includes(id)) bookmarks = bookmarks.filter(b => b !== id);
        else bookmarks.push(id);
        localStorage.setItem("bca_hub_bookmarks", JSON.stringify(bookmarks));
        render(resources);
      }
    });

    window.closeModal = () => {
      modal.classList.remove("open");
      document.body.style.overflow = "";
    };

    window.addEventListener("keydown", (e) => { if (e.key === "Escape") closeModal(); });
    modal.addEventListener("click", (e) => { if (e.target === modal) closeModal(); });

    document.getElementById("searchInput").addEventListener("input", (e) => {
      const q = e.target.value.toLowerCase();
      render(resources.filter(r => r.title.toLowerCase().includes(q) || r.desc.toLowerCase().includes(q)));
    });

    render(resources);
  </script>
</body>
</html>

2. Hands-On Practical Lab: Follow Instructions in VS Code

Code like a professional software engineer! Follow the step-by-step instructions below in Visual Studio Code (VS Code) on your PC, test your output locally, and push to GitHub.

2 Coding Tasks
Interactive Coding TestAuto-Evaluates as you type

Day 40 Practical Task Test Studio

Test Score:0 / 2 Passed
1. Practical Task Specifications & Live Test Checklist:Type code below to pass tests
Task 1Integrate LocalStorage Bookmarks

Save bookmarks array to localStorage.setItem("bca_hub_bookmarks", JSON.stringify(bookmarks)).

⏳ WAITING
Task 2Implement Search Filtering

Filter resources using Array.prototype.filter() based on search input value.

⏳ WAITING
2. Type HTML & CSS Code:index.html
index.html
⚡ Real-Time Testing Active
Tab inserts 2 spaces • Live DOM refresh270 chars
Live Browser Output:
🔒 http://localhost:5500/index.html
Live
Renders live HTML & CSS typed on the left.
Interactive Bug Detective

Empty LocalStorage Initialization Null Reference Bug

Tap or click on the exact line below that contains the error:

Click any code line
⚠️ Broken Code Inspector
Tap buggy line
1
// ❌ Bug: When user visits site for the first time, localStorage is empty!
2
// JSON.parse(null) returns null. Calling bookmarks.includes() crashes!
3
let bookmarks = JSON.parse(localStorage.getItem("bca_bookmarks"));
4
if (bookmarks.includes(101)) { // 💥 TypeError: Cannot read properties of null
5
console.log("Bookmarked!");
6
}
Stuck? Need a detective clue before checking the solution?

Senior Developer Forensic Analysis:

When an item does not exist in localStorage, getItem() returns null. JSON.parse(null) evaluates to null. Calling array methods like .includes() or .push() on null causes a fatal TypeError.

Golden Rule: Always validate code syntax and security attributes before pushing to GitHub!

📝Independent Practice: Assignment

📄 index.html

Sprint 4 Capstone: Production University Resource & Study Hub

  • •Build a complete single-page resource hub featuring 6 university subjects with title, code, semester, and description.
  • •Provide a live Search Bar that filters cards instantly on keystroke.
  • •Implement an interactive Bookmark system that saves favorite subjects to localStorage.
  • •Include a detail modal that displays syllabus topics and lecture links, closable with ESC key.
  • •Deploy the finished application to GitHub Pages and share the live link.
Submission Evaluation Criteria: Push complete index.html, styles.css, and app.js to your GitHub repository under day-40.
Exact Assignment Solution Code: index.html

Open Visual Studio Code on your PC (code .), create index.html, paste this complete step-by-step verified code, test in your browser, and push to your GitHub repo:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>BCA Day 40 - Sprint 4 Capstone</title>
  <style>
    body { font-family: 'Segoe UI', sans-serif; background: #060c18; color: #fff; padding: 24px; margin: 0; }
    .container { max-width: 720px; margin: auto; }
    .top-bar { display: flex; gap: 12px; margin-bottom: 20px; align-items: center; }
    input { flex: 1; padding: 12px; background: #0c1328; border: 1px solid #334155; color: #fff; border-radius: 8px; }
    .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px; }
    .card { background: #0c1328; border: 1px solid #1e293b; padding: 20px; border-radius: 12px; display: flex; flex-direction: column; justify-content: space-between; }
    .card h3 { margin: 0 0 6px 0; color: #38bdf8; }
    .card p { color: #94a3b8; font-size: 13px; line-height: 1.5; flex: 1; }
    .actions { display: flex; gap: 8px; margin-top: 14px; }
    button { flex: 1; padding: 10px; border-radius: 6px; border: none; font-weight: bold; cursor: pointer; }
    .btn-view { background: #2563eb; color: #fff; }
    .btn-save { background: #f59e0b; color: #000; }
    .modal-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.85); backdrop-filter: blur(4px); display: none; align-items: center; justify-content: center; z-index: 9999; }
    .modal-backdrop.open { display: flex; }
    .modal-box { background: #0c1328; border: 1px solid #334155; padding: 24px; border-radius: 14px; width: 90%; max-width: 480px; }
  </style>
</head>
<body>
  <div class="container">
    <div class="top-bar">
      <input type="text" id="filterInput" placeholder="🔍 Search curriculum subjects..." />
      <span id="savedCounter" style="color:#f59e0b;font-weight:bold;white-space:nowrap;">⭐ 0 Saved</span>
    </div>
    <div class="grid" id="subjectsGrid"></div>
  </div>

  <div class="modal-backdrop" id="modalBackdrop">
    <div class="modal-box">
      <h3 id="modalHeading" style="margin-top:0;color:#38bdf8;"></h3>
      <p id="modalBody" style="color:#cbd5e1;line-height:1.6;"></p>
      <button onclick="closeDialog()" style="background:#ef4444;color:#fff;width:100%;margin-top:14px;">Close (Esc)</button>
    </div>
  </div>

  <script>
    const subjects = [
      { id: 101, code: "BCS-011", name: "Computer Basics and PC Software", desc: "Data representation, hardware architectures, CPU registers, and Windows/Linux tools." },
      { id: 102, code: "BCSL-013", name: "Software Lab (PC Software)", desc: "Interactive command prompt, HTML5 document structures, CSS formatting, and web labs." },
      { id: 103, code: "BCS-012", name: "Basic Mathematics", desc: "Determinants, matrices, limits, continuity, coordinate geometry, and linear programming." },
      { id: 104, code: "MCS-011", name: "Problem Solving and C Programming", desc: "Structured programming, memory pointers, dynamic allocation, structures, and arrays." },
      { id: 105, code: "MCS-021", name: "Data and File Structures", desc: "Stacks, queues, linked lists, trees, binary search graphs, and hashing algorithms." },
      { id: 106, code: "BCS-053", name: "Web Programming & Technologies", desc: "Full-stack development, JavaScript DOM, async fetch APIs, and database integration." }
    ];

    let saved = JSON.parse(localStorage.getItem("bca_capstone_saved")) || [];
    const grid = document.getElementById("subjectsGrid");
    const modal = document.getElementById("modalBackdrop");

    function renderCards(list) {
      grid.innerHTML = "";
      document.getElementById("savedCounter").textContent = "⭐ " + saved.length + " Saved";

      list.forEach(s => {
        const isSaved = saved.includes(s.id);
        const card = document.createElement("div");
        card.className = "card";
        card.dataset.id = s.id;
        card.innerHTML = '<div>' +
          '<h3>' + s.code + '</h3>' +
          '<div style="font-weight:bold;font-size:14px;margin-bottom:6px;">' + s.name + '</div>' +
          '<p>' + s.desc + '</p>' +
          '</div>' +
          '<div class="actions">' +
          '<button class="btn-view">Details</button>' +
          '<button class="btn-save">' + (isSaved ? "★ Saved" : "☆ Save") + '</button>' +
          '</div>';
        grid.appendChild(card);
      });
    }

    grid.addEventListener("click", (e) => {
      const card = e.target.closest(".card");
      if (!card) return;
      const id = parseInt(card.dataset.id, 10);
      const sub = subjects.find(s => s.id === id);

      if (e.target.closest(".btn-view") && sub) {
        document.getElementById("modalHeading").textContent = sub.code + ": " + sub.name;
        document.getElementById("modalBody").textContent = sub.desc + " This module is accredited by NextSem Academy under lead faculty Er. Manoj Kumar.";
        modal.classList.add("open");
        document.body.style.overflow = "hidden";
      }

      if (e.target.closest(".btn-save")) {
        if (saved.includes(id)) saved = saved.filter(x => x !== id);
        else saved.push(id);
        localStorage.setItem("bca_capstone_saved", JSON.stringify(saved));
        renderCards(subjects);
      }
    });

    window.closeDialog = () => {
      modal.classList.remove("open");
      document.body.style.overflow = "";
    };

    window.addEventListener("keydown", (e) => { if (e.key === "Escape") closeDialog(); });
    modal.addEventListener("click", (e) => { if (e.target === modal) closeDialog(); });

    document.getElementById("filterInput").addEventListener("input", (e) => {
      const q = e.target.value.toLowerCase();
      renderCards(subjects.filter(s => s.code.toLowerCase().includes(q) || s.name.toLowerCase().includes(q) || s.desc.toLowerCase().includes(q)));
    });

    renderCards(subjects);
  </script>
</body>
</html>
Verified Assignment Code Solution (100% Tested & Ready to Run)

🎓4. Viva Check: Mandatory Conceptual Test

Why is it critical to provide a fallback value like || [] when parsing localStorage data with JSON.parse(localStorage.getItem(key))?

5. Mandatory Step: Push to GitHub to Unlock Next Lesson

Target Repo: html-css-projects

Windows 11 Terminal / VS Code
1$
mkdir day-40
Create a new project folder (day-40)
2$
cd day-40
Change working directory to day-40
3$
git checkout -b feature/day-40-sprint-capstone
4$
git add index.html
5$
git commit -m "feat(day-40): build sprint 4 capstone university resource hub single page application"
Commit staged changes with a descriptive message
6$
git push origin feature/day-40-sprint-capstone
Push local commits directly to your remote GitHub repository
⌨️ Paste into terminal:Ctrl + V✨ Word wrap active (no horizontal cutoff)
Completion Prerequisites VerificationIncomplete Steps
Google Login
✔ Required
VS Code Implementation
Step 4 Viva Check
Own GitHub Project
Required

Exact GitHub Setup Guide & Expected Structure

Day 40 Requirements

Follow these exact folder and file names to prevent verification errors.

1. GitHub Repository
html-css-projects
Must be set to Public
2. Exact FolderNEW
day-40/ or bca-day-40/
Create inside repo root
3. Exact File NameREQUIRED
index.html
⚠️ No spaces or custom names
4. What to Submit Below
https://github.com/YOU/html-css-projects
Or direct link to index.html
Terminal Commands to Create & Push Day 40
1$
# 1. Enter your repository directory
2$
cd html-css-projects
Change working directory to html-css-projects
3$
# 2. Create the exact folder for Day 40
4$
mkdir day-40
Create a new project folder (day-40)
5$
cd day-40
Change working directory to day-40
6$
# 3. Create your code file: index.html
7$
# (Write your Day 40 homework code inside index.html)
8$
# 4. Save, stage, commit and push to GitHub:
9$
git add .
Stage all modified files and new assets for commit
10$
git commit -m "Day 40: Sprint 4 Capstone: University Resource Hub Single Page Application"
Commit staged changes with a descriptive message
11$
git push origin main
Push local commits directly to your remote GitHub repository
⌨️ Paste into terminal:Ctrl + V✨ Word wrap active (no horizontal cutoff)
How to Avoid "Could not find your practical code file" Errors:
1.Use Lowercase index.html: Avoid spaces or custom names like Alan Turing..html or Index.HTML. Linux/GitHub servers are strictly case-sensitive.
2.Separate Day Folders: Create a new folder day-40/ for today so you don't overwrite previous days.
3.Ensure Public Visibility: If your GitHub repo is Private, automated validation cannot inspect your code. Set it to Public.
4.Check on GitHub Website: Before clicking "Verify", open your repo in Google Chrome to confirm your files were pushed to branch main.
📁 github.com/YOUR_USERNAME/html-css-projects
Branch: main
README.md# BCA Practical Portfolio overview
day-01/Day 1 Prior Lesson
└──index.html
day-02/Day 2 Prior Lesson
└──index.html
day-03/Day 3 Prior Lesson
└──index.html
day-04/Day 4 Prior Lesson
└──index.html
day-05/Day 5 Prior Lesson
└──index.html
day-06/Day 6 Prior Lesson
└──index.html
day-07/Day 7 Prior Lesson
└──index.html
day-08/Day 8 Prior Lesson
└──index.html
day-09/Day 9 Prior Lesson
└──index.html
day-10/Day 10 Prior Lesson
└──index.html
└──styles.css
day-11/Day 11 Prior Lesson
└──index.html
day-12/Day 12 Prior Lesson
└──index.html
day-13/Day 13 Prior Lesson
└──index.html
day-14/Day 14 Prior Lesson
└──index.html
day-15/Day 15 Prior Lesson
└──index.html
day-16/Day 16 Prior Lesson
└──index.html
day-17/Day 17 Prior Lesson
└──index.html
day-18/Day 18 Prior Lesson
└──index.html
day-19/Day 19 Prior Lesson
└──index.html
day-20/Day 20 Prior Lesson
└──index.html
└──styles.css
day-21/Day 21 Prior Lesson
└──index.html
day-22/Day 22 Prior Lesson
└──index.html
day-23/Day 23 Prior Lesson
└──index.html
day-24/Day 24 Prior Lesson
└──index.html
day-25/Day 25 Prior Lesson
└──index.html
day-26/Day 26 Prior Lesson
└──index.html
day-27/Day 27 Prior Lesson
└──index.html
day-28/Day 28 Prior Lesson
└──index.html
day-29/Day 29 Prior Lesson
└──index.html
day-30/Day 30 Prior Lesson
└──index.html
└──styles.css
day-31/Day 31 Prior Lesson
└──index.html
day-32/Day 32 Prior Lesson
└──index.html
day-33/Day 33 Prior Lesson
└──index.html
day-34/Day 34 Prior Lesson
└──index.html
day-35/Day 35 Prior Lesson
└──index.html
day-36/Day 36 Prior Lesson
└──index.html
day-37/Day 37 Prior Lesson
└──index.html
day-38/Day 38 Prior Lesson
└──index.html
day-39/Day 39 Prior Lesson
└──index.html
day-40/✨ CREATE THIS FOLDER FOR TODAY (Day 40)
└──index.htmlMust contain:
Sequential Progression Enforced

You are welcome to read all notes, inspect code snippets, and practice all exercises of Day 40 freely! To officially submit your GitHub repository and unlock subsequent days in your portfolio, please complete Day 39: Building a Dynamic GitHub Profile Explorer SPA (Async REST API Mini App) first.

Complete Day 39 First
Curriculum & Practical Systems Authored by Er. Manoj Kumar
NextSem Academy • BCA Practical Learning Portal