Daily Activity Planner App using MERN Stack
This project is a web-based Daily Planner application built using React for the frontend and Node.js with Express for the backend. It allows users to schedule and manage their daily tasks, providing features such as adding, deleting, and marking tasks as completed.
Output Preview: Let us have a look at how the final output will look like.
Prerequisites
- Node.js
- Express.js
- React
- Axios
- MongoDB (for data storage)
Approach
1. Frontend (React):
- Calendar component to select dates.
- Display tasks for the selected date.
- Add, delete, and mark tasks as completed.
2. Backend (Express):
- CRUD operations for tasks (Create, Read, Update, Delete).
- MongoDB as the database to store tasks.
3. Communication (Axios):
- Communication between frontend and backend through Axios for API requests.
Steps to Create the Project
1. Setting up server side
Project structure for server:
Step 1: Initialize a Node.js Project
- Open your terminal.
- Create a new project directory,Navigate to your project directory.
mkdir <<name of project>>
cd <<name of project>>
Run the following command to initialize a new Node.js project and create a package.json file:
npm init -y
Step 2: Install Dependencies
- Run the following command to install the required dependencies (Express.js,cors):
npm install express cors
Step 3: The server code uses MongoDB as the database. Make sure you have MongoDB installed and running.
npm install mongoose
Step 4: Create index.js File
// server/index.js
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const mongoose = require("mongoose");
const app = express();
const PORT = 5000;
app.use(cors());
app.use(bodyParser.json());
// Connect to MongoDB
mongoose.connect(
"mongodb+srv://Ashish:Ashish@cluster0.7ulei1j.mongodb.net/plan?retryWrites=true&w=majority&appName=Cluster0",
{ useNewUrlParser: true, useUnifiedTopology: true }
);
// Define Task schema;
const taskSchema = new mongoose.Schema({
date: String,
todo: String,
startTime: String,
endTime: String,
completed: { type: Boolean, default: false },
});
// Create Task model
const Task = mongoose.model("Task", taskSchema);
app.get("/plan", async (req, res) => {
console.log("Received GET request at /plan");
try {
const tasks = await Task.find();
res.json(tasks);
} catch (error) {
res.status(500).send(error.message);
}
});
app.post("/plan", async (req, res) => {
const { date, todo, startTime, endTime } = req.body;
const newTask = new Task({
date,
todo,
startTime,
endTime,
completed: false,
});
try {
await newTask.save();
res.sendStatus(201);
} catch (error) {
res.status(500).send(error.message);
}
});
app.delete("/plan/:taskId", async (req, res) => {
const taskId = req.params.taskId;
try {
const result = await Task.deleteOne({ _id: taskId });
if (result.deletedCount === 0) {
return res.status(404).json({ error: "Task not found" });
}
res.sendStatus(204);
} catch (error) {
console.error("Error deleting task:", error);
res.status(500).send(error.message);
}
});
app.patch("/plan/:taskId", async (req, res) => {
const taskId = req.params.taskId;
try {
const updatedTask = await Task.findByIdAndUpdate(
taskId,
{ completed: req.body.completed },
{ new: true }
);
res.json(updatedTask);
} catch (error) {
res.status(500).send(error.message);
}
});
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
Step 5: Start the Server
In the terminal, run the following command to start the server:
node index.js
This will run your server on http://localhost:5000/plan. You should see the message “Server is running on port 5000” in the terminal.
2. Setting up FRONTEND
Project Structure for Frontend (client)
Step 1:
- Open your terminal.
- Choose or create a directory for your React app.
npx create-react-app <<Name_of_project>>
cd <<Name_of_project>>
- Install Axios (for making HTTP requests):
npm install axios
- Also install other required packages
npm install react-calendar
Step 2:
- Replace the contents of src/App.js and App.css with the following:
- Replace the contents of src/index.js with the following content:
/* App.css */
body {
font-family: 'Arial', sans-serif;
margin: 0;
padding: 0;
background-color: #f2f2f2;
}
.App {
text-align: center;
}
nav {
padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
background-color: #3498db;
color: #fff;
}
.logo {
font-size: 1.5rem;
}
.button {
border: 1px solid #3498db;
padding: 0.5rem 1rem;
font-size: 1rem;
cursor: pointer;
}
.content {
display: flex;
justify-content: center;
align-items: center;
/* margin-top: 1rem; */
}
.hero-section {
display: flex;
align-items: flex-start;
padding: 7rem;
background-color: #fff;
}
.left-part {
flex: 1;
text-align: left;
width: 100%;
max-width: none;
padding-right: 2rem;
}
.calendar-container {
width: 100%;
max-width: none;
border-right: 1px solid #ccc;
box-sizing: border-box;
}
.right-part {
flex: 1;
text-align: left;
padding-left: 2rem;
}
.tasks {
margin-left: 2rem;
}
h2 {
font-size: 1.5rem;
}
ul {
list-style: none;
padding: 0;
}
li {
margin: 0.5rem 0;
}
.add-task {
margin-top: 1rem;
}
input {
padding: 0.5rem;
font-size: 1rem;
}
button {
margin-left: 0.5rem;
border: 1px solid #3498db;
padding: 0.5rem 1rem;
font-size: 1rem;
cursor: pointer;
}
.react-calendar__tile--active {
background-color: #3498db;
color: #fff;
}
.task-details {
display: flex;
justify-content: space-between;
align-items: center;
}
.time-range {
font-size: 0.8rem;
opacity: 0.7;
}
.task-details {
display: flex;
justify-content: space-between;
align-items: center;
}
.delete-button {
background: none;
border: none;
font-size: 1rem;
cursor: pointer;
color: red;
}
//client/src/App.js
import React, { useState, useEffect } from "react";
import axios from "axios";
import Calendar from "react-calendar";
import "react-calendar/dist/Calendar.css";
import "./App.css";
function App() {
const [tasks, setTasks] = useState([]);
const [selectedDate, setSelectedDate] = useState(new Date());
const [newTask, setNewTask] = useState("");
const [newTaskStartTime, setNewTaskStartTime] = useState("00:00");
const [newTaskEndTime, setNewTaskEndTime] = useState("00:00");
const handleDateClick = (date) => {
setSelectedDate(date);
};
const fetchTasks = async () => {
try {
const response = await axios.get(
`http://localhost:5000/plan?date=${
selectedDate.toISOString().split("T")[0]
}`
);
const tasksData = response.data;
setTasks(tasksData);
// Store tasks in local storage
localStorage.setItem("tasks", JSON.stringify(tasksData));
} catch (error) {
console.error("Error fetching tasks:", error);
}
};
const handleAddTask = async () => {
if (newTask) {
const formattedDate = selectedDate.toISOString()
.split("T")[0];
const startTime = newTaskStartTime;
const endTime = newTaskEndTime;
try {
// Add the new task
await axios.post("http://localhost:5000/plan", {
date: formattedDate,
todo: newTask,
startTime,
endTime,
});
// Fetch tasks after adding the new task
await fetchTasks();
// Clear the input fields
setNewTask("");
setNewTaskStartTime("00:00");
setNewTaskEndTime("00:00");
} catch (error) {
console.error("Error adding task:", error);
}
}
};
useEffect(() => {
fetchTasks();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedDate]);
const handleDeleteTask = async (taskId) => {
try {
await axios.delete(`http://localhost:5000/plan/${taskId}`);
// Update state by removing the deleted task
setTasks((prevTasks) =>
prevTasks.filter((task) => task._id !== taskId)
);
} catch (error) {
console.error("Error deleting task:", error);
}
};
const handleCompleteTask = async (taskId, taskIndex) => {
try {
// Send a request to mark the task as completed on the server
await axios.patch(`http://localhost:5000/plan/${taskId}`, {
completed: true,
});
// Fetch the updated tasks from the server after completion
await fetchTasks();
setTasks((prevTasks) => {
const updatedTasks = [...prevTasks];
updatedTasks[taskIndex].completed = true;
return updatedTasks;
});
} catch (error) {
console.error("Error marking task as completed:", error);
}
};
useEffect(() => {
// Retrieve tasks from local storage on component mount
const storedTasks = localStorage.getItem("tasks");
if (storedTasks) {
setTasks(JSON.parse(storedTasks));
} else {
fetchTasks();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
localStorage.setItem("tasks", JSON.stringify(tasks));
}, [tasks]);
return (
<div className="App">
<nav>
<div className="logo">Daily Planner App</div>
</nav>
<div className="content">
<div className="hero-section">
<div className="left-part">
<div className="calendar-container">
<Calendar
onChange={handleDateClick}
value={selectedDate}
onClickDay={() => {}}
/>
</div>
</div>
<div className="right-part">
<div className="tasks">
<h2>Tasks for {selectedDate.toDateString()}</h2>
<ul>
{tasks
.filter(
(task) =>
task.date ===
selectedDate
.toISOString()
.split("T")[0]
)
.map((task, index) => (
<li
key={index}
style={{
backgroundColor: task.completed
? "lightgreen"
: "inherit",
}}
>
<div className="task-details">
<span className="task-text">
{task.todo}
</span>
{task.startTime &&
task.endTime && (
<span className="time-range">
{task.startTime} -{" "}
{task.endTime}
</span>
)}
<button
className="delete-button"
onClick={() =>
handleDeleteTask(
task._id
)
}
>
X
</button>
{!task.completed && (
<button
className="complete-button"
onClick={() =>
handleCompleteTask(
task._id,
index
)
}
>
✔
</button>
)}
</div>
</li>
))}
</ul>
<div className="add-task">
<input
type="text"
placeholder="Add a new task"
value={newTask}
onChange={(e) => setNewTask(e.target.value)}
/>
<div className="time-inputs">
<input
type="time"
value={newTaskStartTime}
onChange={(e) =>
setNewTaskStartTime(e.target.value)
}
/>
<span>-</span>
<input
type="time"
value={newTaskEndTime}
onChange={(e) =>
setNewTaskEndTime(e.target.value)
}
/>
</div>
<button onClick={handleAddTask}>
Add Task
</button>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
export default App;
//client/src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
Step 3: Start the React app:
cd client
npm start
Ouput:
Contact Us