Project Overview
SC-ITWEB delivers a complete, static front-end for Federal University Dutse’s IT Department. It combines responsive layout, client-side interactivity, and local credential handling to support students, faculty, and administrators without requiring a backend.
Goals
• Offer a deployable template for departmental websites
• Enable students to register, view, filter, and remove courses entirely on the client
• Provide a simple login flow for students and admins with immediate dashboard redirects
• Ensure a responsive experience on desktop and mobile through a mobile menu toggle
Main Features
• Responsive Navigation
– HTML/CSS layout with a JavaScript toggle for mobile menus
• Login Interface
– Local credential validation in login.html
– Redirects to student or admin dashboard on success; inline error messages on failure
• Course Registration Module
– Uses localStorage to persist courses
– Supports adding, filtering, viewing, and removing entries without a server
• Department Content Sections
– News, faculty profiles, image gallery, courses overview, and contact form
Typical Use Cases
• Student Course Management
– A student opens index.html → logs in → registers courses → views or filters registered courses
• Admin Dashboard Access
– An administrator logs in → accesses the admin dashboard for departmental data updates
• Department Website Deployment
– IT staff clone the repo, host static files on any HTTP server, and update content by editing HTML/CSS
By combining static content with client-side logic, SC-ITWEB provides a lightweight, maintainable foundation for departmental portals.
Quick Start & Development Setup
Get a local copy of SC-ITWEB up and running in minutes. Follow these steps to clone the repo, launch a static server, and reach your first working dashboard.
Prerequisites
• Git
• Node.js (for npm) or Python 3.x
• VS Code (optional, for Live Server)
1. Clone the Repository
Run:
git clone https://github.com/AbuJulaybeeb/SC-ITWEB.git
cd SC-ITWEB
2. Serve the Site Locally
a) Using npm http-server
- Install globally (if not already):
npm install -g http-server
- Start the server from the project root:
http-server ./ -p 8080
- Open
http://localhost:8080/index.html
b) Using Python 3
From the project root, run:
python -m http.server 8000
Then visit http://localhost:8000/index.html
c) Using VS Code Live Server
- Open the
SC-ITWEB
folder in VS Code. - Install and enable the Live Server extension.
- Right-click on
index.html
→ Open with Live Server. - Your browser opens at
http://127.0.0.1:5500/index.html
3. Reach the First Working Dashboard
- On the homepage (
index.html
), click Login in the nav. - Use the default admin credentials:
• Username:Admin@IT
• Password:InfoTech
- You’ll redirect to Admin_dashboard.html.
- For student views, register via localStorage (see Users section in README) or manually inject a user:
// In browser console on index.html
const users = JSON.parse(localStorage.getItem('users')) || [];
users.push({ username: 'jsmith', email: 'jsmith@fud.edu.ng', password: 'Password123' });
localStorage.setItem('users', JSON.stringify(users));
- Log in as
jsmith
/Password123
to reach Student_dashboard.html.
Next Steps
• Explore index.html
’s layout, styles, and mobile menu toggle in assets/js
.
• Review login.html
logic for authentication flow.
• Customize or extend dashboards under Admin_dashboard.html
and Student_dashboard.html
.
Project Structure & Core Concepts
This section breaks down the repository into three layers—HTML views, JavaScript modules, and CSS themes—and explains how they interact to deliver a modular, maintainable web application.
Directory Layout
SC-ITWEB/
├─ index.html
├─ courses.html
├─student_profile.html
├─ admin_dashboard.html
├─ css/
│ ├─ tailwind.css
│ ├─ theme-default.css
│ ├─ theme-dark.css
│ └─ admin_dashboard.css
├─ js/
│ ├─ courseRegistration.js
│ ├─ studentProfile.js
│ └─ adminDashboard.js
└─ assets/
├─ images/
└─ fonts/
HTML Views
Each .html
file represents a distinct section of the app. They share a common layout but load only the scripts and styles they need.
Common Template Snippet
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SC-ITWEB – {{ PageTitle }}</title>
<link rel="stylesheet" href="css/tailwind.css">
<link rel="stylesheet" href="css/theme-default.css">
<!-- Page-specific CSS -->
{{#if AdminDashboard}}<link rel="stylesheet" href="css/admin_dashboard.css">{{/if}}
</head>
<body>
<!-- Header, navigation, footer included via partials or server includes -->
{{> header }}
<main>
<!-- page-specific content -->
</main>
<!-- Common JS -->
<script src="js/courseRegistration.js"></script>
<script src="js/studentProfile.js"></script>
{{#if AdminDashboard}}<script src="js/adminDashboard.js"></script>{{/if}}
</body>
</html>
JavaScript Logic Modules
All scripts live in /js
and attach behavior to elements in their corresponding view.
1. courseRegistration.js
• Manages “Register Courses” / “View Courses” tabs
• Persists course list in localStorage
under key registeredCourses
• Provides add, remove, search, and render functions
Import in courses.html
:
<script src="js/courseRegistration.js"></script>
2. studentProfile.js
• Populates LGA dropdown based on selected state
• Uses a hard-coded lookup object for Nigerian states→LGAs
• Exposes populateLGA()
to pre-select values in edit forms
Import in student_profile.html
:
<script src="js/studentProfile.js"></script>
3. adminDashboard.js
• Toggles .active
class on .sidebar-menu
via the hamburger button
• Enables responsive sidebar show/hide at ≤699px
Import in admin_dashboard.html
:
<script src="js/adminDashboard.js"></script>
CSS Themes
All styles reside in /css
. The project uses Tailwind for utility classes plus custom theme files.
1. Tailwind Base
css/tailwind.css
Provides core utility classes.
2. Theme Files
css/theme-default.css
Applies color palette, typography, and spacing rules.css/theme-dark.css
Overrides default colors for dark mode.
Switch themes by swapping the <link>
tag or toggling via JavaScript:
<!-- Default -->
<link id="themeStylesheet" rel="stylesheet" href="css/theme-default.css">
<script>
// Toggle dark mode
document.getElementById('darkModeToggle').addEventListener('click', () => {
const link = document.getElementById('themeStylesheet');
link.href = link.href.includes('default')
? 'css/theme-dark.css'
: 'css/theme-default.css';
});
</script>
3. Page-Specific CSS
css/admin_dashboard.css
Contains sidebar layout, media queries, and styles specific to the admin dashboard.
Core Concepts & Best Practices
- Separation of concerns: HTML for structure, CSS for styling, JS for behavior.
- Lazy loading of page assets ensures each view only loads what it needs.
- Utility-first styling via Tailwind speeds up layout and responsive design.
- LocalStorage simplifies client-side persistence without a backend.
- Modular JS files improve maintainability and scope isolation.
Use this structure as a blueprint when adding new pages or features:
- Create
new_page.html
in the root. - Add page-specific
<link>
and<script>
includes. - Implement logic in
/js/newPage.js
and styles in/css/new_page.css
. - Register any new UI components (tabs, forms, menus) with clear separation of HTML, CSS, and JS.
Core Features & Workflows
Step-by-step guides for end-to-end workflows in the Student and Admin areas.
Client-Side Course Registration (Course_registration.html)
Purpose
Enable students to add, view, search and remove courses entirely in the browser using localStorage.
Key Concepts
- Data model: array of
{ code, title }
stored underregisteredCourses
. - Two tabs: Register Courses (form) and View Courses (table with search & remove).
- Core functions:
renderCourses()
, form submit handler, remove handler, search handler.
- Initialization
// On page load
let courses = JSON.parse(localStorage.getItem('registeredCourses') || '[]');
renderCourses();
- Rendering Courses
function renderCourses(filter = '') {
const tbody = document.getElementById('courseList');
tbody.innerHTML = '';
courses
.filter(c =>
c.code.toLowerCase().includes(filter.toLowerCase()) ||
c.title.toLowerCase().includes(filter.toLowerCase())
)
.forEach((course, idx) => {
const tr = document.createElement('tr');
tr.innerHTML = `
<td class="p-2">${course.code}</td>
<td class="p-2">${course.title}</td>
<td class="p-2">
<button class="bg-red-500 text-white px-3 py-1 rounded" data-idx="${idx}">
Remove
</button>
</td>`;
tbody.appendChild(tr);
});
}
- Registering a Course
document.querySelector('#registerTab form').addEventListener('submit', e => {
e.preventDefault();
const [codeInput, titleInput] = e.target.querySelectorAll('input[type="text"]');
const code = codeInput.value.trim();
const title = titleInput.value.trim();
if (!code || !title) return alert('Both fields are required.');
courses.push({ code, title });
localStorage.setItem('registeredCourses', JSON.stringify(courses));
e.target.reset();
alert('Course added!');
});
- Removing a Course
document.getElementById('viewTab').addEventListener('click', e => {
if (!e.target.matches('button[data-idx]')) return;
const idx = +e.target.dataset.idx;
courses.splice(idx, 1);
localStorage.setItem('registeredCourses', JSON.stringify(courses));
renderCourses(document.getElementById('searchCourse').value);
});
- Searching Courses
document.getElementById('searchCourse').addEventListener('input', e => {
renderCourses(e.target.value);
});
- Tab Switching
registerTabBtn.onclick = () => {
registerTab.style.display = ''; viewTab.style.display = 'none';
registerTabBtn.classList.add('active');
viewTabBtn.classList.remove('active');
};
viewTabBtn.onclick = () => {
registerTab.style.display = 'none'; viewTab.style.display = '';
viewTabBtn.classList.add('active');
registerTabBtn.classList.remove('active');
renderCourses();
};
Practical Tips
- Ensure unique course codes or handle duplicates.
- Persist active tab in localStorage if needed.
- Replace
alert()
with custom toasts for better UX. - To preload courses, fetch from an API and merge before first
renderCourses()
.
Persisting Uploaded Courses in localStorage
Purpose
Enable admins to add new courses via a form and persist them across page reloads in localStorage.
How It Works
- Store all uploaded courses under
adminUploadedCourses
as a JSON-encoded array. - On load, retrieve and render the array.
- On form submit, append new course, save, and re-render.
- Retrieve stored courses
function getUploadedCourses() {
return JSON.parse(localStorage.getItem('adminUploadedCourses') || '[]');
}
- Save courses to storage
function saveUploadedCourses(courses) {
localStorage.setItem('adminUploadedCourses', JSON.stringify(courses));
}
- Render courses into the table
function renderCourses() {
const courses = getUploadedCourses();
const tbody = document.getElementById('uploaded-courses-body');
tbody.innerHTML = '';
courses.forEach(({ code, name, credits }) => {
const tr = document.createElement('tr');
tr.innerHTML = `
<td>${code}</td>
<td>${name}</td>
<td>${credits}</td>`;
tbody.appendChild(tr);
});
}
- Handle the upload form
document.getElementById('upload-course-form').addEventListener('submit', function(e) {
e.preventDefault();
const code = document.getElementById('course-code').value.trim();
const name = document.getElementById('course-name').value.trim();
const credits = parseInt(document.getElementById('course-credits').value, 10);
if (!code || !name || credits < 0) return;
const courses = getUploadedCourses();
courses.push({ code, name, credits });
saveUploadedCourses(courses);
renderCourses();
this.reset();
});
- Initial rendering on page load
document.addEventListener('DOMContentLoaded', renderCourses);
Practical Tips
- Prevent duplicates by checking
courses.find(c => c.code === code)
. - Migrate to a backend for large data sets.
- Always validate and sanitize inputs if exposing data via APIs.
Lecture Slides Management (Admin Upload & Student View)
Purpose
Manage lecture slides end-to-end: admins upload slides via NoteSlide, storing them in localStorage; students view and download them on their dashboard.
Essential Details
- Storage key:
lectureSlides
(array of slide objects). - Slide object shape:
id
: unique timestamp-based IDtitle
: slide titlefileDataUrl
: Base64-encoded filefileName
: original filename
Admin: Uploading, Listing & Deleting Slides
HTML (NoteSlide page)
<form id="upload-form">
<input id="slide-title" placeholder="Slide Title" required>
<input id="slide-file" type="file" required>
<button type="submit">Upload</button>
</form>
<div id="slides-list"></div>
Core Logic (noteslide.js
)
function loadSlides() {
return JSON.parse(localStorage.getItem('lectureSlides')) || [];
}
function saveSlides(slides) {
localStorage.setItem('lectureSlides', JSON.stringify(slides));
}
function renderAdminSlides() {
const slides = loadSlides();
const container = document.getElementById('slides-list');
container.innerHTML = slides.length
? slides.map(s => `
<div class="uploaded-slide-item">
<h4>${s.title}</h4>
<a href="${s.fileDataUrl}" download="${s.fileName}">Download</a>
<button class="delete-slide-btn" data-id="${s.id}">Delete</button>
</div>`).join('')
: '<p>No lecture slides uploaded yet.</p>';
container.querySelectorAll('.delete-slide-btn').forEach(btn => {
btn.addEventListener('click', e => {
const id = e.target.dataset.id;
const updated = loadSlides().filter(sl => sl.id !== id);
saveSlides(updated);
renderAdminSlides();
});
});
}
document.getElementById('upload-form').addEventListener('submit', e => {
e.preventDefault();
const title = document.getElementById('slide-title').value.trim();
const file = document.getElementById('slide-file').files[0];
if (!title || !file) return alert('Fill all fields.');
const reader = new FileReader();
reader.onload = evt => {
const slides = loadSlides();
slides.push({
id: Date.now().toString(),
title,
fileDataUrl: evt.target.result,
fileName: file.name
});
saveSlides(slides);
alert('Lecture slide uploaded!');
e.target.reset();
renderAdminSlides();
};
reader.readAsDataURL(file);
});
renderAdminSlides();
Student: Viewing & Downloading Slides
HTML (Student Dashboard)
<a id="noteslide-nav-link" href="#">Lecture Slides</a>
<section id="noteslide-section" class="hidden">
<div id="slides-list"></div>
</section>
Core Logic (Student_dashboard.js
)
function loadLectureSlides() {
return JSON.parse(localStorage.getItem('lectureSlides')) || [];
}
function renderLectureSlides() {
const container = document.getElementById('slides-list');
const slides = loadLectureSlides();
container.innerHTML = slides.length
? slides.map(s => `
<div class="bg-white p-6 rounded shadow">
<h3 class="text-xl font-semibold">${s.title}</h3>
<a href="${s.fileDataUrl}"
download="${s.fileName}"
class="btn btn-primary mt-2">Download</a>
</div>`).join('')
: '<p class="text-gray-600">No lecture slides available yet.</p>';
}
document.getElementById('noteslide-nav-link').addEventListener('click', e => {
e.preventDefault();
const section = document.getElementById('noteslide-section');
section.classList.toggle('hidden');
renderLectureSlides();
});
Practical Guidance
- Use consistent IDs across admin and student pages (
upload-form
,slide-title
,slide-file
,slides-list
,noteslide-nav-link
,noteslide-section
). - Base64 files can quickly fill localStorage; consider server‐side storage for large slide decks.
- Each action re-renders the list to reflect updates immediately.
- Customize markup and CSS to fit your design system.
Extending & Contributing
This section guides you through customizing pages, adding new features, wiring a real backend, and submitting pull requests to SC-ITWEB.
1. Modifying Existing Pages
Every HTML page lives at the project root (e.g., Admin_dashboard.html
, Student_dashboard.html
). Shared components (sidebar, header, footer) use consistent IDs and classes.
Steps
- Open the target HTML file.
- Locate the sidebar
<ul>
and add or update<li><a>
entries. - Mirror any new IDs in your JS handlers.
Example: Add “Reports” to Admin Sidebar
<!-- Admin_dashboard.html -->
<div class="sidebar-menu">
<button id="sidebarHamburger" class="hamburger-btn">☰</button>
<ul>
<li><a href="Admin_dashboard.html" class="active">Dashboard</a></li>
<!-- New item -->
<li><a href="Admin_reports.html" id="admin-reports-link">Reports</a></li>
</ul>
</div>
<script src="js/Admin_dashboard.js"></script>
// js/Admin_dashboard.js
document.getElementById('admin-reports-link')
.addEventListener('click', e => {
e.preventDefault();
// insert report-view logic here
window.location.href = 'Admin_reports.html';
});
2. Adding New Modules
Organize new features under js/modules/
. Each module pairs an HTML page with a JS file.
File Structure
js/
modules/
reports/
reports.html
reports.js
services/
utils/
Steps
- Create
reports.html
underjs/modules/reports/
. - Reference shared CSS/JS from root.
- Implement interaction in
reports.js
. - Add a sidebar link in your main layout.
Example: Reports Module
reports.html:
<link rel="stylesheet" href="../css/main.css">
<script defer src="../js/modules/reports/reports.js"></script>
<main>
<h1>Reports</h1>
<div id="reports-list"></div>
</main>
reports.js:
import { api } from '../../services/api.js';
async function loadReports() {
const reports = await api.getReports();
const container = document.getElementById('reports-list');
container.innerHTML = reports.map(r =>
`<div class="card">
<h2>${r.title}</h2>
<p>${r.summary}</p>
</div>`
).join('') || '<p>No reports found.</p>';
}
document.addEventListener('DOMContentLoaded', loadReports);
3. Integrating a Real Backend
Replace localStorage
mocks with HTTP calls via a centralized service.
Setup services/api.js
// js/services/api.js
const BASE_URL = 'https://api.example.com'; // update per environment
async function request(path, options = {}) {
const res = await fetch(`${BASE_URL}${path}`, {
headers: { 'Content-Type': 'application/json' },
...options
});
if (!res.ok) throw new Error(await res.text());
return res.json();
}
export const api = {
getSlides: () => request('/slides'),
postSlide: data => request('/slides', { method: 'POST', body: JSON.stringify(data) }),
getReports: () => request('/reports'),
// add more endpoints here
};
Migrate loadLectureSlides()
// Before: localStorage
function loadLectureSlides() {
const raw = localStorage.getItem('lectureSlides');
return raw ? JSON.parse(raw) : [];
}
// After: HTTP
import { api } from '../services/api.js';
async function loadLectureSlides() {
try {
return await api.getSlides();
} catch (e) {
console.error('Failed fetching slides', e);
return [];
}
}
Environment Configuration
- Store
BASE_URL
inconfig.js
and ignore via.gitignore
. - Use a simple proxy in development (e.g.,
vite.config.js
orhttp-proxy-middleware
).
4. Contributing Pull Requests
Follow these conventions to streamline reviews.
Workflow
- Fork the repo and clone your fork.
- Create a feature branch:
git checkout -b feat/noteslide-enhancement
. - Implement changes; run lint and manual tests.
- Commit with [Conventional Commits]:
feat(noteslide): support file size display
fix(api): handle 401 unauthorized
- Push and open a PR against
main
. - Link any related issue, describe your changes, and request reviewers.
Guidelines
- Target
main
branch. - Write clear PR descriptions and reference issue IDs.
- Ensure no ESLint or console errors.
- Add or update documentation under
docs/
. - Squash or rebase commits before merge.
5. Coding Standards
- Follow ESLint rules in
.eslintrc.js
. - Use Prettier for consistent formatting.
- Name modules and files in
kebab-case
. - Write modern ES modules (
import
/export
).
6. Testing & QA
- Perform manual smoke tests on all pages.
- Verify mobile responsiveness (sidebar toggle, menu collapse).
- Check API error handling (network failures, empty data).
- Run browser devtools console to catch uncaught exceptions.
By following these guidelines, you ensure that new features integrate smoothly, maintain code quality, and align with SC-ITWEB’s architecture.