BCA Student Stories & UpdatesLive
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.
Day 40 Step-by-Step Practical Flow
🎯1. What You Will Learn Today
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.
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.
"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."
👨🏫3. Teacher Demonstration Code
Examine how search filtering, modal popups, and localStorage bookmarking coordinate seamlessly.
<!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.
Day 40 Practical Task Test Studio
Save bookmarks array to localStorage.setItem("bca_hub_bookmarks", JSON.stringify(bookmarks)).
Filter resources using Array.prototype.filter() based on search input value.
Empty LocalStorage Initialization Null Reference Bug
Tap or click on the exact line below that contains the error:
Click any code line// ❌ Bug: When user visits site for the first time, localStorage is empty!// JSON.parse(null) returns null. Calling bookmarks.includes() crashes!let bookmarks = JSON.parse(localStorage.getItem("bca_bookmarks"));if (bookmarks.includes(101)) { // 💥 TypeError: Cannot read properties of null console.log("Bookmarked!");}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.
📝Independent Practice: Assignment
📄 index.htmlSprint 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.
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>🎓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
Exact GitHub Setup Guide & Expected Structure
Day 40 RequirementsFollow these exact folder and file names to prevent verification errors.
index.html: Avoid spaces or custom names like Alan Turing..html or Index.HTML. Linux/GitHub servers are strictly case-sensitive.day-40/ for today so you don't overwrite previous days.main.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