Mini Project — Learning Progress Dashboard
Ab poore JavaScript course ke concepts ko ek real browser project me combine karte hain. Hum searchable, filterable aur interactive learning dashboard banayenge jisme arrays, objects, functions, DOM, events, state, async flow, accessibility aur debugging sab ek saath use honge.
Run the finished project
Code padhne se pehle completed dashboard ko browser me use karke behavior observe karo.
Project goal
We will build a small learning dashboard that shows course progress and lets a learner search, filter and update progress without reloading the page.
Is project ka focus flashy UI nahi, balki clean JavaScript architecture hai. Data ek array me rahega, UI us data se render hogi, user events state ko update karenge aur state change ke baad screen dubara render hogi.
Features we will build
- Course cards rendered from JavaScript data.
- Search by course title or category.
- All, In Progress and Completed filters.
- Mark complete / mark in progress action.
- Total, in-progress, completed and average-progress stats.
- Reset button for the demo state.
- Accessible status updates with a polite live region.
- Responsive layout for desktop and mobile.
- Async initialization pattern with error handling.
Project file structure
mini-project/
├── index.html
├── style.css
└── app.jsHTML structure provide karta hai, CSS layout/design handle karta hai, aur JavaScript data + behavior manage karta hai.
1. Build the semantic HTML shell
Dashboard me native inputs and buttons use karo. Search ke liye input type="search", filters ke liye buttons, aur dynamic results ke liye empty section container rakho.
<label>
<span>Search courses</span>
<input id="course-search" type="search">
</label>
<div class="filters" role="group" aria-label="Filter courses">
<button data-filter="all" aria-pressed="true">All</button>
<button data-filter="progress" aria-pressed="false">In progress</button>
<button data-filter="complete" aria-pressed="false">Completed</button>
</div>
<p id="dashboard-status" role="status" aria-live="polite"></p>
<section id="course-grid" aria-label="Courses"></section>aria-pressed filter button state communicate karta hai, aur status region result changes announce kar sakta hai.2. Model course data with objects
Each course is an object and the full collection is an array.
const COURSE_DATA = [
{ id: 1, title: "HTML Fundamentals", category: "Web Development", progress: 100 },
{ id: 2, title: "CSS Styling", category: "Web Development", progress: 82 },
{ id: 3, title: "JavaScript", category: "Programming", progress: 68 }
];Stable id events me specific course identify karne ke kaam aata hai.
3. Keep UI state in one place
const state = {
courses: [],
query: "",
filter: "all"
};This small state object makes it clear what can change during the session.
4. Initialize asynchronously
Real apps often load data from APIs. Demo local data use karta hai, but initialization Promise-based rakha gaya hai so async architecture practice ho.
function loadCourses() {
return Promise.resolve(
COURSE_DATA.map(course => ({ ...course }))
);
}
async function init() {
try {
status.textContent = "Loading courses…";
state.courses = await loadCourses();
renderCourses();
} catch (error) {
console.error("Dashboard failed to initialize", error);
status.textContent = "Could not load the dashboard.";
}
}Later loadCourses() ko real fetch() function se replace karna easy hoga.
5. Derive visible courses
Search and filter original array ko mutate nahi karte. We derive a visible list with filter().
function getVisibleCourses() {
const query = state.query.trim().toLowerCase();
return state.courses.filter(course => {
const matchesQuery =
course.title.toLowerCase().includes(query) ||
course.category.toLowerCase().includes(query);
const matchesFilter =
state.filter === "all" ||
(state.filter === "complete" && course.progress === 100) ||
(state.filter === "progress" && course.progress > 0 && course.progress < 100);
return matchesQuery && matchesFilter;
});
}Search aur filter ko separate DOM hacks se manage karne ke bajay ek function visible data calculate karta hai. Isse behavior predictable aur debug karna easier hota hai.
6. Render cards safely
API ya user-like data ko plain text ke roop me render karte waqt textContent use karo.
function createCourseCard(course) {
const card = document.createElement("article");
card.className = "course-card";
const title = document.createElement("h2");
title.textContent = course.title;
const button = document.createElement("button");
button.type = "button";
button.dataset.courseId = String(course.id);
button.textContent = course.progress === 100
? "Mark in progress"
: "Mark complete";
card.append(title, button);
return card;
}Full demo adds category, progress label and progress bar too.
7. Replace the rendered list
function renderCourses() {
const courses = getVisibleCourses();
grid.replaceChildren(...courses.map(createCourseCard));
updateStats();
status.textContent = courses.length
? `${courses.length} courses shown.`
: "No courses match this search or filter.";
}replaceChildren() old rendered cards ko clear karke current derived state render karta hai.
8. Calculate dashboard stats
const completed = state.courses.filter(
course => course.progress === 100
).length;
const average = Math.round(
state.courses.reduce((sum, course) => sum + course.progress, 0) /
state.courses.length
);Yahan arrays chapter ke filter() and reduce() real UI data ke saath use ho rahe hain.
9. Search with the input event
searchInput.addEventListener("input", event => {
state.query = event.target.value;
renderCourses();
});User type karta hai → state change hoti hai → render function current state ko UI me reflect karta hai.
10. Filter buttons
Filter group par one listener use karke event delegation apply kar sakte hain.
filters.addEventListener("click", event => {
const button = event.target.closest("button[data-filter]");
if (!button) return;
state.filter = button.dataset.filter;
renderCourses();
});Finished project button state ko aria-pressed ke saath synchronize bhi karta hai.
11. Update one course
Course action buttons dynamic hain, so grid-level event delegation useful hai.
function toggleCourseProgress(courseId) {
state.courses = state.courses.map(course => {
if (course.id !== courseId) return course;
return {
...course,
progress: course.progress === 100 ? 50 : 100
};
});
renderCourses();
}Object spread old object ko mutate karne ke bajay updated object banata hai.
12. Event delegation on dynamic cards
grid.addEventListener("click", event => {
const button = event.target.closest("button[data-course-id]");
if (!button) return;
toggleCourseProgress(Number(button.dataset.courseId));
});13. Reset demo state
Reset action starting dataset ka fresh shallow copy banata hai, search clear karta hai and filter ko All par return karta hai.
state.courses = COURSE_DATA.map(course => ({ ...course }));
state.query = "";
state.filter = "all";
renderCourses();14. Responsive CSS
JavaScript project ka responsive behavior mostly CSS responsibility hai. Cards desktop par grid me, tablet par two columns and small screens par one column ho sakte hain.
.course-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}
@media (max-width: 820px) {
.course-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 560px) {
.course-grid { grid-template-columns: 1fr; }
}15. Accessibility review
- Search input has a visible label.
- Filter controls are real buttons.
- Selected filter uses
aria-pressed. - Status text uses
role="status"and polite live updates. - Focus styles remain visible.
- All actions work with keyboard activation.
- Progress is also written as text, not shown by color alone.
16. Debug the project systematically
DevTools me three places inspect karo: Console for runtime errors, Elements for generated cards/attributes, aur Sources for breakpoints in filter/render functions.
course.progress === 100 ko course.progress = 100 bana kar bug observe karo, then console/breakpoint se reason find karke fix karo.Project data flow
User action
↓
Update state
↓
Derive visible data
↓
Render DOM
↓
Update status + statsYe simple one-way flow larger frontend frameworks ke concepts samajhne ke liye bhi strong foundation hai.
Course concepts used
- Variables and data types → application state.
- Conditions → filters and button labels.
- Functions → reusable logic.
- Arrays →
filter(),map(),reduce(). - Objects → course records and state.
- DOM → dynamic card creation.
- Events → search, filters, reset and progress actions.
- Modern JS → spread, template literals, arrow callbacks.
- Async JavaScript → Promise-based initialization.
- Debugging → console, breakpoints and deliberate test cases.
Challenge extensions
Base project complete hone ke baad in improvements ko khud build karo:
- Real API se course data load karo using
fetch(). - Sort dropdown add karo: title, progress high-to-low, low-to-high.
- Course category filter add karo.
- Progress change ke liye 25%, 50%, 75%, 100% controls add karo.
- URL query parameters me current search/filter reflect karo.
- Error simulation button add karke error state test karo.
- Automated tests ke liye pure filtering/stat functions separate module me move karo.
Final project checklist
- Search title/category dono par work karta hai?
- All / In Progress / Completed filters correct hain?
- Mark complete state and stats update hote hain?
- Reset initial state restore karta hai?
- No-result message visible hai?
- Keyboard-only use possible hai?
- Console me unexpected errors nahi hain?
- Mobile width par cards readable hain?
- Dynamic text safe
textContentse render hota hai? - Code functions me clearly organized hai?
Final Task — Rebuild Without Copying
Demo use karne aur chapter samajhne ke baad project ko blank folder se khud rebuild karo.
- HTML skeleton banao.
- Responsive CSS layout banao.
- At least 6 course objects create karo.
- State object define karo.
- Course cards JS se render karo.
- Search implement karo.
- Three filters implement karo.
- Progress toggle implement karo.
- Stats calculate karo.
- Reset action add karo.
- Status live region add karo.
- Keyboard and mobile testing karo.
- Breakpoints se one bug debug karo.
- Finally code ko small focused functions me review karo.
Agar tum is project ko blank folder se independently bana sakte ho aur har major function explain kar sakte ho, to tumne JavaScript fundamentals ko sirf padha nahi — practically apply bhi kiya.