Find a meeting time.
1. Prerequisites
Before starting, ensure you have the following:
- Ubuntu 24.04 installed.
- Node.js (v18 or later) and npm installed:
sudo apt updatesudo apt install -y nodejs npm - MariaDB installed:
sudo apt install -y mariadb-server mariadb-client - Bootstrap (included via CDN for simplicity).
2. Set Up the Project
- Create a new project directory:
mkdir meeting-time-appcd meeting-time-appnpm init -y - Install dependencies:
npm install express mysql2 body-parser cors
3. Configure MariaDB
- Log into MariaDB:
sudo mariadb - Create a database and user:
CREATE DATABASE meeting_app;CREATE USER 'meeting_user'@'localhost' IDENTIFIED BY 'Kenyon1824-348';GRANT ALL PRIVILEGES ON meeting_app.* TO 'meeting_user'@'localhost';FLUSH PRIVILEGES; - Create the required table:
USE meeting_app;CREATE TABLE meetings ( id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255) NOT NULL, link VARCHAR(255) NOT NULL UNIQUE, times TEXT NOT NULL, responses TEXT DEFAULT '{}' );
4. Back-End API with Node.js
Create a file index.js in the project directory:
const express = require('express');
const bodyParser = require('body-parser');
const mysql = require('mysql2');
const cors = require('cors');
const path = require('path');
const app = express();
app.use(bodyParser.json());
app.use(cors());
// Database connection
const db = mysql.createConnection({
host: 'localhost',
user: 'meeting_user',
password: 'Kenyon1824-348',
database: 'meeting_app',
});
console.log("Starting");
// Serve static files from the "public" directory
app.use(express.static(path.join(__dirname, 'public')));
// Serve "index.html" when accessing the root route "/"
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
// Endpoint to create a meeting
app.post('/api/meetings', (req, res) => {
const { title, times } = req.body;
console.log("Meeting:",title,times);
const link = Math.random().toString(36).substring(2, 15);
const query = 'INSERT INTO meetings (title, link, times) VALUES (?, ?, ?)';
db.execute(query, [title, link, JSON.stringify(times)], (err, result) => {
if (err) return res.status(500).send(err);
res.send({ link });
});
});
// Endpoint to get meeting details
app.get('/api/meetings/:link', (req, res) => {
const { link } = req.params;
const query = 'SELECT * FROM meetings WHERE link = ?';
db.execute(query, [link], (err, results) => {
if (err || results.length === 0) return res.status(404).send('Meeting not found');
res.send(results[0]);
});
});
// Endpoint to fetch all meetings
app.get('/api/meetings', (req, res) => {
const query = 'SELECT title, link FROM meetings';
db.execute(query, (err, results) => {
if (err) {
console.error("Database Error:", err);
return res.status(500).json({ error: "Failed to fetch meetings" });
}
res.json(results);
});
});
// Endpoint to submit availability
app.post('/api/meetings/:link/respond', (req, res) => {
const { link } = req.params;
const { name, selectedTimes } = req.body;
const query = 'SELECT * FROM meetings WHERE link = ?';
db.execute(query, [link], (err, results) => {
if (err || results.length === 0) return res.status(404).send('Meeting not found');
const meeting = results[0];
const responses = JSON.parse(meeting.responses || '{}');
responses[name] = selectedTimes;
const updateQuery = 'UPDATE meetings SET responses = ? WHERE link = ?';
db.execute(updateQuery, [JSON.stringify(responses), link], (updateErr) => {
if (updateErr) return res.status(500).send(updateErr);
res.send('Response submitted');
});
});
});
// Start the server
const PORT = 3000;
app.listen(PORT,'0.0.0.0', () => console.log(`Server running on http://0.0.0.0:${PORT}`));
5. Front-End with JavaScript and Bootstrap
Create a public directory for the front-end.
In the following code replace all occurance of “138.28.162.128” with the IP address of your server.
Add the following:
index.html
<!DOCTYPE html>138.28.162.128
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
<title>Meeting Time App</title>
</head>
<body>
<div class="container mt-5">
<h1>Create a Meeting</h1>
<form id="createMeetingForm">
<div class="mb-3">
<label for="title" class="form-label">Meeting Title</label>
<input type="text" class="form-control" id="title" required>
</div>
<div class="mb-3">
<label for="times" class="form-label">Times (comma-separated)</label>
<input type="text" class="form-control" id="times" required>
</div>
<button type="submit" class="btn btn-primary">Create Meeting</button>
</form>
<div id="meetingLink" class="mt-3"></div>
</div>
<script>
document.getElementById('createMeetingForm').addEventListener('submit', async (e) => {
e.preventDefault();
const title = document.getElementById('title').value;
const times = document.getElementById('times').value.split(',');
const response = await fetch('http://:3000/api/meetings', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title, times })
});
const data = await response.json();
document.getElementById('meetingLink').innerHTML = `
<div class="alert alert-success">
Meeting created! Share this link: <a href="/meeting.html?link=${data.link}" target="_blank">View Meeting</a>
</div>
`;
});
</script>
</body>
</html>
meeting.html
<!DOCTYPE html>138.28.162.128
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
<title>Meeting Details</title>
</head>
<body>
<div class="container mt-5">
<h1 id="meetingTitle"></h1>
<form id="responseForm" class="mt-3">
<div id="timesList"></div>
<div class="mb-3">
<label for="name" class="form-label">Your Name</label>
<input type="text" class="form-control" id="name" required>
</div>
<button type="submit" class="btn btn-primary">Submit Response</button>
</form>
<div id="summary" class="mt-5"></div>
</div>
<script>
const urlParams = new URLSearchParams(window.location.search);
const link = urlParams.get('link');
async function fetchMeeting() {
const response = await fetch(`http://:3000/api/meetings/${link}`);138.28.162.128
const data = await response.json();
document.getElementById('meetingTitle').textContent = data.title;
const times = JSON.parse(data.times);
const responses = JSON.parse(data.responses || '{}');
const summary = {};
document.getElementById('timesList').innerHTML = times.map((time, index) => {
summary[time] = 0;
for (const user in responses) {
if (responses[user].includes(time)) summary[time]++;
}
return `
<div class="form-check">
<input class="form-check-input" type="checkbox" value="${time}" id="time${index}">
<label class="form-check-label" for="time${index}">${time}</label>
</div>
`;
}).join('');
document.getElementById('summary').innerHTML = `
<h3>Summary</h3>
${Object.entries(summary).map(([time, count]) => `
<p>${time}: ${count} people available</p>
`).join('')}
`;
}
document.getElementById('responseForm').addEventListener('submit', async (e) => {
e.preventDefault();
const name = document.getElementById('name').value;
const selectedTimes = Array.from(document.querySelectorAll('input[type="checkbox"]:checked')).map(input => input.value);
await fetch(`http://:3000/api/meetings/${link}/respond`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, selectedTimes })
});
fetchMeeting();
});
fetchMeeting();
</script>
</body>
</html>
6. Run the Program
- Start the server:
node index.js - Open the front-end in a browser:
http://138.28.162.128:3000/public/index.html
7. Summary
This program allows users to:
- Create a meeting with available times.
- Share a unique link with others.
- See who is available for each time slot in a summary.
You can extend this with features like user authentication, email notifications, or even calendar integration.
