Open In App

Event Management Web App using MERN

Last Updated : 30 Jul, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

In this guide, we'll walk through the step-by-step process of building a feature-rich Event Management Web App. We will make use of the MERN stack to build this project.

Preview of final output: Let us have a look at how the final output will look like.

Screenshot-2566-12-29-at-152922
Final Output of Event Management App

Prerequisite

Approach to create Event Management Application

  • Define the structure of an event using Mongoose schemas in a model file (e.g., `Event.js`).
  • Develop routes for handling Create, Read, Update, and Delete (CRUD) operations in a dedicated `eventRoutes.js` file.
  • Set up a MongoDB database and establish a connection in your Express application.
  • Create a server file (e.g., `server.js`) where Express is configured to listen on a specific port.
  • Design and implement a form component (`EventForm.js`) for adding new events.
  • Develop a component (`EventList.js`) to display a list of events fetched from the server.
  • Create a detailed event display component (`EventItem.js`) with features like editing, toggling reminders, and deleting.
  • Style your components for an engaging user interface. You can utilize CSS .

Steps to Setup Backend with Node.js and Express

Step 1: Creating express app:

npm init -y

Step 2: Installing the required packages

npm install express mongoose body-parser cors

Project Structure:

Screenshot-2566-12-29-at-155011
Backend Project Structure

The updated dependencies in package.json file for backend will look like:

"dependencies": {
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.18.2",
"mongoose": "^8.0.0",
}

Example: Below is the code example of the backend.

JavaScript
// server.js
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const cors = require('cors');
const eventRoutes = require('./routes/eventRoutes');

const app = express();
const PORT = process.env.PORT || 5000;

// Middleware
app.use(cors());
app.use(bodyParser.json());

// Connect to MongoDB
mongoose.connect('mongodb://localhost:27017/event_management', {
	useNewUrlParser: true,
	useUnifiedTopology: true,
}).then(() => {
	console.log('Connected to MongoDB')
});

// Routes
app.use('/api/events', eventRoutes);

// Start server
app.listen(PORT, () => {
	console.log(`Server is running on port ${PORT}`);
});
JavaScript
// routes/eventRoutes.js
const express = require('express');
const router = express.Router();
const Event = require('../models/Event');

// Get all events
router.get('/', async (req, res) => {
	try {
		const events = await Event.find();
		res.json(events);
	} catch (error) {
		res.status(500).json({ message: error.message });
	}
});
// Create a new event
router.post('/', async (req, res) => {
	const event = new Event({
		title: req.body.title,
		date: req.body.date,
		reminder: req.body.reminder || false,
	});
	try {
		const newEvent = await event.save();
		res.status(201).json(newEvent);
	} catch (error) {
		res.status(400).json({ message: error.message });
	}
});

// Delete an event
router.delete('/:id', async (req, res) => {
	console.log('****** Deleting event ******');
	try {
		console.log('Delete route called');
		// Use findByIdAndDelete instead of findByIdAndRemove
		await Event.findByIdAndDelete(req.params.id);
		console.log('Event deleted');
		res.json({ message: 'Event deleted' });
	} catch (error) {
		console.error('Error deleting event:', error);
		res.status(500).json({ message: error.message });
	}
});
// Update an event by ID
router.put('/:id', async (req, res) => {
	const eventId = req.params.id;
	const { title, date, reminder } = req.body;

	console.log('reminder', reminder);
	try {
		// Find the event by ID in the database
		const event = await Event.findById(eventId);
		if (!event) {
			return res.status(404).json({ message: 'Event not found' });
		}

		// Update the event properties
		event.date = date;
		event.title = title;
		event.reminder = reminder;
		console.log('event updated', event.reminder);
		// Save the updated event
		await event.save();

		// You can send the updated event in the response if needed
		res.json(event);
	} catch (error) {
		console.error('Error updating event:', error);
		res.status(500).json({ message: 'Internal Server Error' });
	}
});

module.exports = router;
JavaScript
// models/Event.js
const mongoose = require('mongoose');

const eventSchema = new mongoose.Schema({
	title: { type: String, required: true },
	date: { type: Date, required: true },
	reminder: { type: Boolean, default: false },

});
const Event = mongoose.model('Event', eventSchema);

module.exports = Event;

Steps to run the backend:

node server.js

Steps to Setup Frontend with React

Step 1: Create React App:

npx create-react-app event-management-frontend

Step 2: Switch to the project directory:

cd event-management-frontend

Step 3: Installing the required packages:

npm install axios

Project Structure:

Screenshot-2566-12-29-at-154953
Frontend Project Structure

The updated dependencies in package.json for frontend will look like:

"dependencies": {
"axios": "^1.5.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}

Example: Below is the code example of the frontend.

CSS
/* src/EventList.css */
.event-list {
    display: flex;
    flex-wrap: wrap;
}

.event-card {
    background-color: #D2E3C8;
    border: 1px solid #ddd;
    border-radius: 8px;
    margin: 10px;
    padding: 15px;
    width: 200px;
    overflow: hidden;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

.event-card:hover {
    box-shadow: 0 4px 8px green;
}

.event-info {
    margin-bottom: 10px;
}

.event-title {
    font-weight: bold;
}

.event-date {
    color: #555;
}

.event-actions {
    display: flex;
    justify-content: space-between;
}

.delete-btn:hover {
    background-color: red;
    color: white;

}

.rem-para {
    background-color: green;
    color: white;
    padding: 2px;
    width: fit-content;

    position: relative;
    left: -1px;
    bottom: 22px;

}

.gfg {
    padding: 15px;
    color: white;
    background-color: rgb(6, 162, 6);
    border-radius: 25px;
}


/* form 
   */

input {
    padding: 5px;
    width: 500px;
}

form {
    display: flex;
    flex-direction: column;
    justify-content: center;
    border: 2px solid red;
    padding: 20px;
    border-radius: 10px;
    gap: 5px;
    background: linear-gradient(to right, #FFA500, #FF6347);
    ;
}

.main-container {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
}

label {
    font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
    font-weight: 500;
}
JavaScript
// src/App.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import EventForm from './components/EventForm';
import EventList from './components/EventList';
import './App.css'


const App = () => {
	const [events, setEvents] = useState([]);

	useEffect(() => {
		// Fetch events from the server
		axios.get('http://localhost:5000/api/events')
			.then(response => setEvents(response.data))
			.catch(error => console.error(error));
	}, []);

	const handleEventAdd = (newEvent) => {
		setEvents([...events, newEvent]);
	};

	const handleEventDelete = (id) => {
		console.log("delete event " + id)
		// Delete an event
		axios.delete(`http://localhost:5000/api/events/${id}`)
			.then(
				() =>
					setEvents(events.filter(event => event._id !== id)))
			.catch(error => console.error(error));
	};

	const handleToggleReminder = (eventId) => {
		// console.log('HI');

		// Find the event by ID
		const selectedEvent =
			events.find(event => event._id === eventId);

		// Toggle the reminder status
		const updatedEvent =
		{
			...selectedEvent,
			reminder: !selectedEvent.reminder
		};

		// console.log('Updated',updatedEvent);


		// Update the event in the database
		axios.put(`http://localhost:5000/api/events/${eventId}`, updatedEvent)
			.then(response => {
				// console.log('res',response.data);

				// If the update is successful, update the events in the state
				const updatedEvents = events.map(event =>
					event._id === eventId ? updatedEvent : event
				);
				setEvents(updatedEvents);
			})
			.catch(
				error =>
					console.error(`Error updating reminder status for
				event with ID ${eventId}:`, error));
	};

	const onEventEdit = (eventId, updatedData) => {
		// Update the event in the database
		axios.put(`http://localhost:5000/api/events/${eventId}`, updatedData)
			.then(response => {
				// If the update is successful, update the events in the state
				const updatedEvents = events.map(event =>
					event._id ===
						eventId ?
						{ ...event, ...updatedData } : event
				);
				setEvents(updatedEvents);
			})
			.catch(
				error =>
					console.error(`Error updating event with
						ID ${eventId}:`, error)
			);
	};

	return (
		<div className='main-container'>
			<h1 className='gfg'>
				GFG
			</h1>
			<h2>Event Management App</h2>
			<EventForm onEventAdd={handleEventAdd} />
			<EventList
				events={events}
				onEventDelete={handleEventDelete}
				onToggleReminder={handleToggleReminder}
				onEventEdit={onEventEdit}
			/>
		</div>
	);
};

export default App;
JavaScript
// src/EventList.js
import React, { useState } from 'react';
// Import the new EventItem component
import EventItem from './EventItem';


const EventList = (
	{ events, onEventDelete,
		onToggleReminder, onEventEdit
	}
) => {
	const [editedEvents, setEditedEvents] = useState([]);

	const handleEventEdit = (eventId, updatedData) => {
		// Find the index of the event being edited
		const eventIndex =
			editedEvents
				.findIndex(
					event =>
						event._id === eventId
				);

		if (eventIndex !== -1) {
			// Update the edited event in the local state
			const updatedEditedEvents = [...editedEvents];
			updatedEditedEvents[eventIndex] = {
				...updatedEditedEvents[eventIndex],
				...updatedData,
			};

			setEditedEvents(updatedEditedEvents);
		} else {
			// If the event is not already in the local state, add it
			setEditedEvents(
				[...editedEvents,
				{ _id: eventId, ...updatedData }
				]
			);
		}
		// Pass the edit request to the parent component
		onEventEdit(eventId, updatedData);
	};

	return (
		<div className="event-list">
			{events.map(event => (
				<EventItem
					key={event._id}
					event={
						editedEvents
							.find(
								editedEvent =>
									editedEvent._id === event._id) || event
					}
					onToggleReminder={onToggleReminder}
					onEventDelete={onEventDelete}
					onEventEdit={handleEventEdit}
				/>
			))}
		</div>
	);
};

export default EventList;
JavaScript
// src/EventItem.js
import React, { useEffect, useState } from 'react';
import moment from 'moment';


const EventItem = (
	{ event, onEventDelete,
		onToggleReminder, onEventEdit
	}) => {
	const [isEditing, setIsEditing] = useState(false);
	const [editedTitle, setEditedTitle] = useState(event.title);
	const [editedDate, setEditedDate] =
		useState(moment(event.date).format("YYYY-MM-DD"));
	const [rem, setRem] = useState("")


	useEffect(() => {
		if (event) {
			setRem(event.reminder ? "" : "Reminder On");

			// Check if the event is today and has a reminder
			const today = new Date();
			const eventDate = new Date(event.date);

			today.setHours(0, 0, 0, 0);
			eventDate.setHours(0, 0, 0, 0);

			if (today.getTime() ===
				eventDate.getTime() &&
				event.reminder) {
				alert(`Today is the day of the event: 
				${event.title}`);
			}
		} else {
			setRem("Reminder On");
		}
	}, [event, event.reminder]);

	const handleEditClick = () => {
		setIsEditing(true);
	};

	const handleSaveClick = () => {

		// Perform the update in the database (you may use an API request here)
		onEventEdit(event._id,
			{
				title: editedTitle,
				date: editedDate
			});

		// Exit the edit mode
		setIsEditing(false);
	};

	const handleCancelClick = () => {
		// Reset the edited values and exit the edit mode
		setEditedTitle(event.title);
		setEditedDate(moment(event.date)
			.format("YYYY-MM-DD"));
		setIsEditing(false);
	};

	return (
		<div className="event-card">

			<p className='rem-para'>
				{
					event.reminder ? "Reminder On" : ""
				}
			</p>


			<div className="event-info">

				{isEditing ? (
					<>
						<input
							type="text"
							value={editedTitle}
							onChange={
								(e) =>
									setEditedTitle(e.target.value)
							}
						/>
						<input
							type="date"
							value={editedDate}
							onChange={
								(e) =>
									setEditedDate(e.target.value)
							}
						/>
					</>
				) : (
					<>
						<h3 className="event-title">{event.title}</h3>
						<hr />
						<span className="event-date">

							<span style={{ "fontWeight": "700" }}>
								Event On:
							</span>
							{
								moment(event.date)
									.add(1, 'days').calendar()
							};
						</span>
					</>
				)}
			</div>
			<div className="event-actions">
				{isEditing ? (
					<>
						<button onClick={handleSaveClick}>
							Save
						</button>
						<button onClick={handleCancelClick}>
							Cancel
						</button>
					</>
				) : (
					<>
						<button onClick={
							() => onToggleReminder(event._id)
						}>
							{
								event.reminder ?
									'Disable Reminder' : 'Enable Reminder'
							}
						</button>
						<button className='delete-btn'
							onClick={
								() => onEventDelete(event._id)}>
							Delete
						</button>
						<button onClick={handleEditClick}>
							Edit
						</button>
					</>
				)}
			</div>
		</div>
	);
};

export default EventItem;
JavaScript
// src/EventForm.js
import React, { useState } from 'react';
import axios from 'axios';
import './EventForm.css';

const EventForm = ({ onEventAdd }) => {
	const [newEvent, setNewEvent] =
		useState({ title: '', date: '', reminder: false });

	const handleInputChange = (e) => {
		setNewEvent(
			{
				...newEvent,
				[e.target.name]: e.target.value
			}
		);
	};

	const handleSubmit = (e) => {
		e.preventDefault();

		// Create a new event
		axios.post('http://localhost:5000/api/events', newEvent)
			.then(response => {
				onEventAdd(response.data);
				setNewEvent({ title: '', date: '', reminder: false });
			})
			.catch(error => console.error(error));
	};
	return (
		<form onSubmit={handleSubmit}>
			<label>Title:</label>
			<input type="text" name="title"
				value={newEvent.title}
				onChange={handleInputChange} required />
			<label>Date:</label>
			<input type="date"
				name="date" value={newEvent.date}
				onChange={handleInputChange} required />

			<button type="submit">Add Event</button>
		</form>
	);
};
export default EventForm;

Steps to run the app:

npm start

Output:


Next Article

Similar Reads