diff --git a/lms/README.md b/lms/README.md new file mode 100644 index 00000000..53a29c22 --- /dev/null +++ b/lms/README.md @@ -0,0 +1,102 @@ +# Learning Managment System + +## Step 1: Project Setup + +### Create a new directory for your project and navigate into it. +```bash +mkdir learning-management-app +cd learning-management-app +``` +### Initialize a new Node.js project. +```bash +npm init -y +``` +### Install necessary dependencies. +```bash +npm install express mysql ejs bcryptjs express-session express-validator +``` + +## Step 2: Set up the Backend + +### Create a `server.js` file in your project directory. + +### Create a MySQL database named `learning_management` + +#### Create users table +```bash +-- Create users table +CREATE TABLE users ( + id INT AUTO_INCREMENT PRIMARY KEY, + username VARCHAR(255) UNIQUE, + password VARCHAR(255), + email VARCHAR(255) UNIQUE, + full_name VARCHAR(255) +); +``` + +#### Create courses table +```bash +-- Create courses table +CREATE TABLE courses ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) +); + +-- Insert sample data into courses table +INSERT INTO courses (name) VALUES +('Introduction to HTML'), +('CSS Fundamentals'), +('JavaScript Basics'); +``` + +#### Create leaderboard table +```bash +-- Create leaderboard table +CREATE TABLE leaderboard ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255), + score INT +); + +-- Insert sample data into leaderboard table +INSERT INTO leaderboard (name, score) VALUES +('John Doe', 100), +('Jane Smith', 90), +('Michael Brown', 85), +('Emily Jones', 80); +``` + +### Run the server. +```bash +node server.js +``` + +## Step 3: Frontend Setup + +### Create an `index.html` file for the frontend. + +### Create a `course-content.html` file for the course content. + +### Create a `leader-board.html` file for the leader board. + +### Create a `style.css` file to style your HTML. + +### Create a `script.js` file to handle frontend interactions. + +## Step 4: Testing +Open your web browser and navigate to http://localhost:3000. + +# Hackathon Instructions +Finish up the project by: +1. creating functionality for the logged in user to select their preferred courses. +2. store the selection in the database +3. create a page where the selected courses for each specific logged in user is displayed. + +## Submission Guidelines +Fork this repository and clone it to your local machine. +Create a new branch with your GitHub username (git checkout -b username). +Complete the tasks. +Commit your changes and push them to your forked repository. +Submit a pull request to the main repository for review. + +Happy hacking! 🚀 diff --git a/lms/app.js b/lms/app.js new file mode 100644 index 00000000..8437cdf4 --- /dev/null +++ b/lms/app.js @@ -0,0 +1,20 @@ +// Route to select a course +app.post('/select-course', (req, res) => { + const userId = req.user.id; + const courseId = req.body.courseId; + const query = 'INSERT INTO user_courses (user_id, course_id) VALUES (?, ?)'; + db.query(query, [userId, courseId], (err) => { + if (err) throw err; + res.redirect('/my-courses'); + }); +}); + +// Route to display selected courses +app.get('/my-courses', (req, res) => { + const userId = req.user.id; + const query = 'SELECT * FROM courses WHERE id IN (SELECT course_id FROM user_courses WHERE user_id = ?)'; + db.query(query, [userId], (err, results) => { + if (err) throw err; + res.render('my-courses', { courses: results }); + }); +}); diff --git a/lms/course-content.html b/lms/course-content.html new file mode 100644 index 00000000..11b4f8a4 --- /dev/null +++ b/lms/course-content.html @@ -0,0 +1,31 @@ + + + + + + My Courses + + + +
+

My Courses

+ +
+
+
+

Selected Courses

+ +
+
+ + + + diff --git a/lms/dashboard.html b/lms/dashboard.html new file mode 100644 index 00000000..11b4f8a4 --- /dev/null +++ b/lms/dashboard.html @@ -0,0 +1,31 @@ + + + + + + My Courses + + + +
+

My Courses

+ +
+
+
+

Selected Courses

+ +
+
+ + + + diff --git a/lms/index.html b/lms/index.html new file mode 100644 index 00000000..2120ac50 --- /dev/null +++ b/lms/index.html @@ -0,0 +1,47 @@ + + + + + + Learning Management App + + + +
+

Learning Management App

+ +
+
+
+

Register

+
+ + + + + +
+
+
+

Login

+ + + +
+
+ +
+
+ + + + diff --git a/lms/leader-board.html b/lms/leader-board.html new file mode 100644 index 00000000..8a719b56 --- /dev/null +++ b/lms/leader-board.html @@ -0,0 +1,29 @@ + + + + + + Leaderboard + + + +
+

Leaderboard

+ +
+
+ +
+ + + + + diff --git a/lms/script.js b/lms/script.js new file mode 100644 index 00000000..8df90815 --- /dev/null +++ b/lms/script.js @@ -0,0 +1,206 @@ +document.addEventListener('DOMContentLoaded', () => { + const registerForm = document.getElementById('register-form'); + const loginForm = document.getElementById('login-form'); + const logoutForm = document.getElementById('logout-form'); + + registerForm.addEventListener('submit', async (e) => { + e.preventDefault(); + const formData = new FormData(registerForm); + const username = formData.get('username'); + const password = formData.get('password'); + const email = formData.get('email'); + const full_name = formData.get('full_name'); + try { + const response = await fetch('/register', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ username, password, email, full_name }) + }); + if (response.ok) { + alert('Registration successful'); + } else { + alert('Registration failed'); + } + } catch (error) { + console.error('Error:', error); + } + }); + + loginForm.addEventListener('submit', async (e) => { + e.preventDefault(); + const formData = new FormData(loginForm); + const username = formData.get('username'); + const password = formData.get('password'); + try { + const response = await fetch('/login', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ username, password }) + }); + if (response.ok) { + alert('Login successful'); + } else { + alert('Invalid username or password'); + } + } catch (error) { + console.error('Error:', error); + } + }); + + logoutForm.addEventListener('submit', async (e) => { + e.preventDefault(); + try { + const response = await fetch('/logout', { + method: 'POST' + }); + if (response.ok) { + alert('Logout successful'); + } else { + alert('Logout failed'); + } + } catch (error) { + console.error('Error:', error); + } + }); + + // Check if the current page is the course content page + if (window.location.pathname === 'course-content') { + // Call the fetchCourseContent function + fetchCourseContent(); + } + + // Check if the current page is the course content page + if (window.location.pathname === 'leader-board') { + // Fetch course content from server + fetchLeaderboardData(); + } + + // Check if the current page is the course content page + if (window.location.pathname === 'dashboard') { + //fetch Logged in user's full name + fetchFullName(); + } +}); + +function fetchCourseContent() { + // Get course ID from URL parameter (assuming course ID is passed in the URL) + const urlParams = new URLSearchParams(window.location.search); + const courseId = urlParams.get('id'); + + // Make AJAX request to fetch course content from server + fetch(`course/${courseId}`) + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok'); + } + return response.json(); + }) + .then(data => { + // Display course content on the page + displayCourseContent(data); + }) + .catch(error => { + console.error('Error fetching course content:', error); + }); +} + +function displayCourseContent(courseContent) { + // Get the course name element + const courseNameElement = document.getElementById('course-name'); + // Set the course name + courseNameElement.textContent = courseContent.name; + + // Get the course content element + const courseContentElement = document.getElementById('course-content'); + // Clear previous content + courseContentElement.innerHTML = ''; + + // Loop through the modules and display them + courseContent.modules.forEach(module => { + const moduleSection = document.createElement('section'); + moduleSection.innerHTML = ` +

${module.title}

+

${module.description}

+ + `; + courseContentElement.appendChild(moduleSection); + }); +} + +function fetchLeaderboardData() { + // Make AJAX request to fetch leaderboard data from server + fetch('leaderboard') + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok'); + } + return response.json(); + }) + .then(data => { + // Display leaderboard data on the page + displayLeaderboardData(data); + }) + .catch(error => { + console.error('Error fetching leaderboard data:', error); + }); +} + +function displayLeaderboardData(leaderboardData) { + // Get the leaderboard element + const leaderboardElement = document.getElementById('leaderboard'); + // Clear previous content + leaderboardElement.innerHTML = ''; + + // Create a table to display leaderboard data + const table = document.createElement('table'); + table.innerHTML = ` + + Rank + Name + Score + + `; + + // Loop through the leaderboard data and add rows to the table + leaderboardData.forEach((entry, index) => { + const row = document.createElement('tr'); + row.innerHTML = ` + ${index + 1} + ${entry.name} + ${entry.score} + `; + table.appendChild(row); + }); + + // Append the table to the leaderboard element + leaderboardElement.appendChild(table); +} + +function fetchFullName() { + // Make AJAX request to fetch the user's full name from the server + fetch('/get-fullname') + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok'); + } + return response.json(); + }) + .then(data => { + // Display the user's full name on the dashboard + displayFullName(data.fullName); + }) + .catch(error => { + console.error('Error fetching user full name:', error); + }); +} + +function displayFullName(fullName) { + // Get the element where the full name will be displayed + const fullNameElement = document.getElementById('user-fullname'); + // Set the inner HTML of the element to the user's full name + fullNameElement.textContent = fullName; +} \ No newline at end of file diff --git a/lms/server.js b/lms/server.js new file mode 100644 index 00000000..2b854861 --- /dev/null +++ b/lms/server.js @@ -0,0 +1,168 @@ +// server.js +const express = require('express'); +const session = require('express-session'); +const bcrypt = require('bcryptjs'); +const bodyParser = require('body-parser'); +const mysql = require('mysql'); +const { check, validationResult } = require('express-validator'); +const app = express(); + +// Configure session middleware +app.use(session({ + secret: 'secret-key', + resave: false, + saveUninitialized: true +})); + +// Create MySQL connection +const connection = mysql.createConnection({ + host: 'localhost', + user: 'root', + password: '', + database: 'learning_management' +}); + +// Connect to MySQL +connection.connect((err) => { + if (err) { + console.error('Error connecting to MySQL: ' + err.stack); + return; + } + console.log('Connected to MySQL as id ' + connection.threadId); +}); + +// Serve static files from the default directory +app.use(express.static(__dirname)); + +// Set up middleware to parse incoming JSON data +app.use(express.json()); +app.use(bodyParser.json()); +app.use(express.urlencoded({ extended: true })); +app.use(bodyParser.urlencoded({ extended: true })); + +// Define routes +app.get('/', (req, res) => { + res.sendFile(__dirname + '/index.html'); +}); + + + +// Define a User representation for clarity +const User = { + tableName: 'users', + createUser: function(newUser, callback) { + connection.query('INSERT INTO ' + this.tableName + ' SET ?', newUser, callback); + }, + getUserByEmail: function(email, callback) { + connection.query('SELECT * FROM ' + this.tableName + ' WHERE email = ?', email, callback); + }, + getUserByUsername: function(username, callback) { + connection.query('SELECT * FROM ' + this.tableName + ' WHERE username = ?', username, callback); + } +}; + +// Registration route +app.post('/register', [ + // Validate email and username fields + check('email').isEmail(), + check('username').isAlphanumeric().withMessage('Username must be alphanumeric'), + + // Custom validation to check if email and username are unique + check('email').custom(async (value) => { + const user = await User.getUserByEmail(value); + if (user) { + throw new Error('Email already exists'); + } + }), + check('username').custom(async (value) => { + const user = await User.getUserByUsername(value); + if (user) { + throw new Error('Username already exists'); + } + }), +], async (req, res) => { + // Check for validation errors + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ errors: errors.array() }); + } + + // Hash the password + const saltRounds = 10; + const hashedPassword = await bcrypt.hash(req.body.password, saltRounds); + + // Create a new user object + const newUser = { + email: req.body.email, + username: req.body.username, + password: hashedPassword, + full_name: req.body.full_name + }; + + // Insert user into MySQL + User.createUser(newUser, (error, results, fields) => { + if (error) { + console.error('Error inserting user: ' + error.message); + return res.status(500).json({ error: error.message }); + } + console.log('Inserted a new user with id ' + results.insertId); + res.status(201).json(newUser); + }); +}); + +// Login route +app.post('/login', (req, res) => { + const { username, password } = req.body; + // Retrieve user from database + connection.query('SELECT * FROM users WHERE username = ?', [username], (err, results) => { + if (err) throw err; + if (results.length === 0) { + res.status(401).send('Invalid username or password'); + } else { + const user = results[0]; + // Compare passwords + bcrypt.compare(password, user.password, (err, isMatch) => { + if (err) throw err; + if (isMatch) { + // Store user in session + req.session.user = user; + res.send('Login successful'); + } else { + res.status(401).send('Invalid username or password'); + } + }); + } + }); +}); + +// Logout route +app.post('/logout', (req, res) => { + req.session.destroy(); + res.send('Logout successful'); +}); + +//Dashboard route +app.get('/dashboard', (req, res) => { + // Assuming you have middleware to handle user authentication and store user information in req.user + const userFullName = req.user.full_name; + res.render('dashboard', { fullName: userFullName }); +}); + +// Route to retrieve course content +app.get('/course/:id', (req, res) => { + const courseId = req.params.id; + const sql = 'SELECT * FROM courses WHERE id = ?'; + db.query(sql, [courseId], (err, result) => { + if (err) { + throw err; + } + // Send course content as JSON response + res.json(result); + }); + }); + +// Start server +const PORT = process.env.PORT || 3000; +app.listen(PORT, () => { + console.log(`Server running on port ${PORT}`); +}); \ No newline at end of file diff --git a/lms/style.css b/lms/style.css new file mode 100644 index 00000000..390053b4 --- /dev/null +++ b/lms/style.css @@ -0,0 +1,79 @@ +body { + font-family: Arial, sans-serif; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + align-items: center; +} + +form { + margin-top: 20px; + display: flex; + flex-direction: column; + align-items: center; +} +#input-section{ + background-color: #666; + font-family:Arial, Helvetica, sans-serif; + +} + +input, button { + margin-top: 10px; + padding: 5px; + background: #333; +} + +header { + background-color: #333; + color: #fff; + padding: 20px; + text-align: center; +} + +nav ul { + list-style-type: none; + margin: 0; + padding: 0; +} + +nav ul li { + display: inline; + margin-right: 20px; +} + +main { + padding: 20px; +} + +footer { + background-color: #333; + color: #fff; + padding: 20px; + text-align: center; +} + +/* Course content page styles */ +section { + margin-bottom: 30px; + border-bottom: 1px solid #ccc; + padding-bottom: 20px; +} + +h2 { + color: #333; + font-size: 24px; +} + +p { + color: #666; + font-size: 16px; + line-height: 1.5; +} + +iframe { + width: 100%; + height: 400px; + margin-top: 20px; +} \ No newline at end of file diff --git a/lms/update_schema.sql b/lms/update_schema.sql new file mode 100644 index 00000000..e7903551 --- /dev/null +++ b/lms/update_schema.sql @@ -0,0 +1,7 @@ +CREATE TABLE user_courses ( + user_id INT, + course_id INT, + PRIMARY KEY (user_id, course_id), + FOREIGN KEY (user_id) REFERENCES users(id), + FOREIGN KEY (course_id) REFERENCES courses(id) +);