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.
// 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 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}`);
}); |
// 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; |
// 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.
// 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
.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;
|
// 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;
|
// 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;
|
// 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
.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;
|
/* 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: