Event Management Web App using MERN
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.
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:
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:
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.
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; |
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 ; } |
Steps to run the app:
npm start
Output:
Contact Us