Open In App

DIY Home Improvement Guide using MERN Stack

Last Updated : 19 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

This project aims to provide users with a platform to perform various calculations related to home improvement tasks. From simple calculations like measuring wall paint requirements to advanced calculations like estimating material costs for renovations, our application will serve as a handy tool for DIY enthusiasts.

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

hjg

Project Preview

Prerequisites

Approach to create DIY Home Improvement Guide:

  • Create React components for adding, editing, and deleting projects.
  • Implement forms for project details input.
  • Set up routes and controllers in the backend to handle CRUD operations for projects.
  • Use Fetch API to send requests from the frontend to the backend.
  • Add functionality for adding, editing, and deleting projects.

Steps to Create the Project

Step 1: Create a new directory for your project and initialize a new Node.js project using npm init.

mkdir diy-home-improvement-guide
cd diy-home-improvement-guide

Step 2: Initialize the Node project

npm init -y

Step 3: Install required dependencies like Express.js, React.js, and other necessary packages using npm.

npm install express mongoose body-parser cors

Project Structure(Backend):

Screenshot-2024-03-05-111902

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

"dependencies": {
    "body-parser": "^1.20.2",
    "cors": "^2.8.5",
    "express": "^4.18.3",
    "mongoose": "^8.2.1"
}

Step 4: Create server.js file for setting up Express.js server. Implement routes for user authentication, CRUD operations, and any additional functionalities.

Javascript
//server.js

const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const cors = require('cors');

const app = express();

app.use(cors());

app.use(bodyParser.json());

const mongoURI = 'mongodb://localhost:27017/diy-home-improvement';
mongoose
    .connect(mongoURI, { useNewUrlParser: true, useUnifiedTopology: true })
    .then(() => console.log('MongoDB connected'))
    .catch(err => console.log(err));

const Project = mongoose.model('project', {
    title: {
        type: String,
        required: true
    },
    description: {
        type: String,
        required: true
    },
    category: {
        type: String,
        required: true
    },
    steps: {
        type: [String],
        required: true
    }
});

app.get('/api/projects', (req, res) => {
    Project.find()
        .then(projects => res.json(projects))
        .catch(err => res.status(404)
            .json({ noProjectsFound: 'No projects found' }));
});

app.post('/api/projects', (req, res) => {
    const { title, description, category, steps } = req.body;

    const newProject = new Project({
        title,
        description,
        category,
        steps
    });

    newProject.save()
        .then(project => res.json(project))
        .catch(err => console.log(err));
});

app.delete('/api/projects/:id', (req, res) => {
    const { id } = req.params;

    Project.findByIdAndDelete(id)
        .then(deletedProject => {
            if (!deletedProject) {
                return res.status(404)
                    .json({ error: 'Project not found' });
            }
            res.json(deletedProject);
        })
        .catch(err => res.status(500)
            .json({ error: 'Internal server error' }));
});

app.put('/api/projects/:id', (req, res) => {
    const { id } = req.params;
    const { title, description, category, steps } = req.body;

    Project.findByIdAndUpdate(id,
        { title, description, category, steps },
        { new: true })
        .then(updatedProject => {
            if (!updatedProject) {
                return res.status(404)
                    .json({ error: 'Project not found' });
            }
            res.json(updatedProject);
        })
        .catch(err => res.status(500)
            .json({ error: 'Internal server error' }));
});

const port = process.env.PORT || 5000;
app.listen(port, () =>
    console.log(`Server running on port ${port}`));

Step 5: Set up the React.js frontend by creating components for user interface elements, forms, and data visualization.

npx create-react-app diy-home-improvement-frontend
cd diy-home-improvement-frontend

Project Structure(Frontend):

Screenshot-2024-03-16-214023

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

"dependencies": {
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
}

Step 6: Implement the desired functionalities such as ProjectList and AddProject components.

CSS
/* App.css */

body {
    margin: 2%;
}

.full {
    display: flex;
    flex-direction: row;
    justify-content: space-evenly;

}

.project-list {
    padding: 20px;
    background-color: #fcfefe;
    border-radius: 10px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

.project {
    margin-bottom: 20px;
    padding: 20px;
    background-color: #ffffff;
    border-radius: 10px;
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
}

.project h3 {
    color: #f76b8a;
}

.project p {
    color: #555555;
}

.project ul {
    margin-top: 10px;
    padding-left: 20px;
}

.project li {
    color: #777777;
}

.add-project-form {
    width: 100%;
    padding: 20px;
}

.add-project-form label {
    color: #f76b8a;
}

.add-project-form input[type='text'],
.add-project-form textarea {
    width: 100%;
    padding: 10px;

    margin-bottom: 10px;
    border: 1px solid #dddddd;
    border-radius: 5px;
}

.add-project-form button {
    padding: 10px 20px;
    background-color: #f76b8a;
    color: #ffffff;
    border: none;
    border-radius: 5px;
    cursor: pointer;
}

.add-project-form button:hover {
    background-color: #e25e7f;
}

.edit-btn,
.delete-btn {
    margin-right: 10px;
    padding: 8px 16px;
    background-color: #f76b8a;
    color: #ffffff;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    transition: background-color 0.3s ease;
}

.edit-btn:hover,
.delete-btn:hover {
    background-color: #e25e7f;
}


.edit-modal {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    background-color: rgba(0, 0, 0, 0.5);
    z-index: 1000;
}

.edit-form-container {
    background-color: #ffffff;
    padding: 50px;
    width: 700px;
    border-radius: 5px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
}

.edit-form-container h2 {
    margin-bottom: 20px;
}

.edit-form-container form label {
    display: block;
    margin-bottom: 10px;
}

.edit-form-container form input,
.edit-form-container form textarea {
    width: 100%;
    padding: 8px;
    margin-bottom: 10px;
}

.edit-form-container form button {
    padding: 8px 16px;
    background-color: #f76b8a;
    color: #ffffff;
    border: none;
    border-radius: 5px;
    cursor: pointer;
}

.edit-form-container form button:hover {
    background-color: #e25e7f;
}
Javascript
import React, { useState } from 'react';
import ProjectList from './ProjectList';
import AddProjectForm from './AddProjectForm';

import AreaCalculator from './AreaCalculator';

function App() {
    const [roomInfo, setRoomInfo] = useState(null);

    const handleRoomInfo = info => {
        setRoomInfo(info);
    };
    return (
        <div>
            <h1>DIY Home Improvement Guide</h1>
            <div className='full'>
                <AreaCalculator onRoomInfo={handleRoomInfo} />
                <AddProjectForm />
            </div>

            <hr />
            <ProjectList />
        </div>
    );
}

export default App;
Javascript
//AreaCalculator.js

import React, { useState } from 'react';

const AreaCalculator = () => {
    const [area, setArea] = useState('');
    const [error, setError] = useState('');
    const [suggestedResult, setSuggestedResult] = useState(null);

    const handleChange = event => {
        setArea(event.target.value);
    };

    const handleSubmit = event => {
        event.preventDefault();
        if (!area || isNaN(area) || area <= 0) {
            setError('Please enter a valid positive number for the area.');
            setSuggestedResult(null);
            return;
        }

        const roomArea = 150;
        const hallArea = 200;
        const bathroomArea = 60;
        const kitchenArea = 100;

        const maxRooms = Math.floor(area / roomArea);
        const maxHalls = Math.floor(area / hallArea);
        const maxBathrooms = Math.floor(area / bathroomArea);
        const maxKitchens = Math.floor(area / kitchenArea);

        setSuggestedResult({
            rooms: maxRooms,
            halls: maxHalls,
            kitchens: maxKitchens,
            bathrooms: maxBathrooms,
        });

        setError('');
    };

    return (
        <div style={styles.container}>
            <h2 style={styles.title}>Area Calculator</h2>
            <form onSubmit={handleSubmit} style={styles.form}>
                <label style={styles.label}>
                    Enter Area (in square feet):
                    <input type="number" value={area}
                        onChange={handleChange} style={styles.input} />
                </label>
                <button type="submit" style={styles.button}>Calculate</button>
            </form>
            {error && <p style={styles.error}>{error}</p>}
            {suggestedResult && (
                <div style={styles.result}>
                    <h3 style={styles.resultTitle}>Suggested Configuration</h3>
                    <p>Maximum number of rooms: {suggestedResult.rooms}</p>
                    <p>Maximum number of halls: {suggestedResult.halls}</p>
                    <p>Maximum number of kitchens: {suggestedResult.kitchens}</p>
                    <p>Maximum number of bathrooms: {suggestedResult.bathrooms}</p>
                </div>
            )}
        </div>
    );
};

const styles = {
    container: {
        maxWidth: '500px',
        padding: '20px',
    },
    title: {
        textAlign: 'center',
        marginBottom: '20px',
    },
    form: {
        display: 'flex',
        flexDirection: 'column',
    },
    label: {
        marginBottom: '10px',
    },
    input: {
        padding: '8px',
        marginBottom: '15px',
        border: '1px solid #ccc',
        borderRadius: '3px',
    },
    button: {
        padding: '10px',
        backgroundColor: 'pink',
        color: 'black',
        border: 'none',
        borderRadius: '3px',
        cursor: 'pointer',
    },
    error: {
        color: 'red',
        textAlign: 'center',
        marginTop: '15px',
    },
    result: {
        marginTop: '20px',
    },
    resultTitle: {
        marginBottom: '10px',
    },
};

export default AreaCalculator;
Javascript
//ProjectList.js

import React, { useState, useEffect } from 'react';
import './App.css';
import GetColor from './GetColor';

const ProjectList = () => {
    const [projects, setProjects] = useState([]);
    const [error, setError] = useState(null);
    const [editFormData, setEditFormData] = useState({
        id: '',
        title: '',
        description: '',
        category: '',
        steps: ''
    });
    const [isEditModalOpen, setIsEditModalOpen] = useState(false);

    useEffect(() => {
        fetch('http://localhost:5000/api/projects')
            .then(response => response.json())
            .then(data => setProjects(data))
            .catch(error => console.error('Error fetching projects:', error));
    }, []);

    const handleDelete = id => {
        fetch(`http://localhost:5000/api/projects/${id}`, {
            method: 'DELETE'
        })
            .then(response => response.json())
            .then(alert("Deleted Sucessfully !!"))
            .then(data => {
                setProjects(projects.filter(project => project._id !== id));
            })
            .catch(error => console.error('Error deleting project:', error));

    };

    const handleEdit = project => {
        setEditFormData({
            id: project._id,
            title: project.title,
            description: project.description,
            category: project.category,
            steps: project.steps.join('\n')
        });
        setIsEditModalOpen(true);

    };

    const handleSubmitEdit = event => {
        event.preventDefault();
        fetch(`http://localhost:5000/api/projects/${editFormData.id}`, {
            method: 'PUT',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                title: editFormData.title,
                description: editFormData.description,
                category: editFormData.category,
                steps: editFormData.steps.split('\n')
            })
        })
            .then(response => response.json())
            .then(data => {
                setProjects(projects.map(project => (project._id ===
                    editFormData.id ? data : project)));
                setEditFormData({
                    id: '',
                    title: '',
                    description: '',
                    category: '',
                    steps: ''
                });
                setIsEditModalOpen(false);
            })
            .catch(error => console.error('Error editing project:', error));
    };

    if (error) {
        return <div>Error: {error}</div>;
    }

    return (
        <div className="project-list">
            <h2>Projects</h2>
            {projects.map(project => (
                <div key={project._id} className="project">
                    <h3>{project.title}</h3>
                    <p>{project.description}</p>
                    <p>Category: {project.category}</p>
                    <ul>
                        {project.steps.map((step, index) => (
                            <li key={index}>{step}</li>
                        ))}
                    </ul>

                    <div>
                        <button className="edit-btn" onClick={() =>
                            handleEdit(project)}>Edit</button>
                        <button className="delete-btn" onClick={() =>
                            handleDelete(project._id)}>Delete</button>
                    </div>
                    <GetColor project={project} />
                </div>
            ))}
            {isEditModalOpen && (
                <div className="edit-modal">
                    <div className="edit-form-container">
                        <h2>Edit Project</h2>
                        <form onSubmit={handleSubmitEdit}>
                            <label>
                                Title:
                                <input
                                    type="text"
                                    name="title"
                                    value={editFormData.title}
                                    onChange={event => setEditFormData(
                                        { ...editFormData, title: event.target.value })}
                                />
                            </label>
                            <br />
                            <label>
                                Description:
                                <textarea
                                    name="description"
                                    value={editFormData.description}
                                    onChange={event => setEditFormData(
                                        { ...editFormData, description: event.target.value })}
                                />
                            </label>
                            <br />
                            <label>
                                Category:
                                <input
                                    type="text"
                                    name="category"
                                    value={editFormData.category}
                                    onChange={event => setEditFormData(
                                        { ...editFormData, category: event.target.value })}
                                />
                            </label>
                            <br />
                            <label>
                                Steps:
                                <textarea
                                    name="steps"
                                    value={editFormData.steps}
                                    onChange={event => setEditFormData(
                                        { ...editFormData, steps: event.target.value })}
                                />
                            </label>
                            <br />
                            <button type="submit">Save Changes</button>
                        </form>
                    </div>
                </div>
            )}
        </div>
    );
};

export default ProjectList;
JavaScript
//GetColor.js

import React, { useState } from 'react';
import './App.css';
const colorData = {
    colors: [
        { name: 'Red', hex: '#f95959' },
        { name: 'Green', hex: '#cbf078' },
        { name: 'Blue', hex: '#93deff' },
        { name: 'Yellow', hex: '#f8f398' },
        { name: 'Purple', hex: '#7c73e6' },
        { name: 'Orange', hex: '#ff9a3c' },
        { name: 'Pink', hex: '#ffb5b5' },
        { name: 'Black', hex: '#141010' },
        { name: 'White', hex: '#fcfefe' },
        { name: 'Gray', hex: '#ececec' }
    ]
};

const GetColor = ({ project }) => {
    const [GetColor, setGetColor] = useState(null);

    const handleGetColor = () => {
        const randomIndex = Math.floor(Math.random() *
            colorData.colors.length);
        const selectedColor = colorData.colors[randomIndex];
        setGetColor(selectedColor);
    };

    return (
        <div>
            <button onClick={handleGetColor} className='edit-btn'
                style={{ marginTop: "1%" }}>Get Color Recommendation</button>
            {GetColor && (
                <div style={{
                    backgroundColor: GetColor.hex, padding: '10px',
                    marginTop: '10px', margin: '2%', border: "2px solid black"
                }}>
                    <p>Name: {GetColor.name}</p>
                    <p>Hex: {GetColor.hex}</p>
                </div>
            )}
        </div>
    );
};

export default GetColor;
JavaScript
import React, { useState } from 'react';
import './App.css';

const AddProjectForm = () => {
    const [formData, setFormData] = useState({
        title: '',
        description: '',
        category: '',
        steps: ''
    });

    const handleChange = event => {
        setFormData({ ...formData, [event.target.name]: event.target.value });
    };

    const handleSubmit = event => {
        event.preventDefault();
        fetch('http://localhost:5000/api/projects', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(formData)
        })
            .then(response => response.json())
            .then(data => alert("Added Sucessfully !!"))
            .catch(error => console.error('Error adding project:', error));

    };

    return (
        <div className="add-project-form">
            <h2>Add New Project</h2>
            <form onSubmit={handleSubmit}>
                <label>
                    Title:
                    <input type="text" name="title"
                        value={formData.title} onChange={handleChange} />
                </label>
                <br />
                <label>
                    Description:
                    <textarea name="description"
                        value={formData.description} onChange={handleChange} />
                </label>
                <br />
                <label>
                    Category:
                    <input type="text" name="category"
                        value={formData.category} onChange={handleChange} />
                </label>
                <br />
                <label>
                    Steps:
                    <textarea name="steps"
                        value={formData.steps} onChange={handleChange} />
                </label>
                <br />
                <button type="submit">Add Project</button>
            </form>
        </div>
    );
};

export default AddProjectForm;

Steps to Run the Application

  • Step 1: Navigate to the project directory in your terminal.
  • Step 2: Start the backend server by running
node server.js
  • Step 3: Navigate to the client directory.
  • Step 4: Start the frontend development server by running
npm start
  • Step 5: Open your web browser and go to http://localhost:3000 to access the application.

Output:

Animation47

DIY Home Improvement Guide with MERN Stack



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads