Open In App

Car Vault app using MEAN

In this article, we will discuss about how to create a car vault application using MEAN stack technologies. This article will help you to get a thorough understanding of end to end web applications. It discusses the CRUD operations while creating the project.

Project Preview:

preview

PROJECT PREVIEW

Prerequisites:

Approach to Create Car Vault

Backend:

Frontend:

Steps to Create Project

Step 1: Create the main folder for complete project

mkdir car-vault
cd car-vault

Step 2: Initialize the node.js project

npm init -y

Step 3: Install the required dependencies

npm install express mongoose jsonwebtoken bcryptjs nodemon cors body-parser

Folder Structure (Backend):

Screenshot-2024-04-19-020122

PROJECT STRUCTURE FOR BACKEND

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

"dependencies": {
"@auth0/angular-jwt": "^5.2.0",
"bcryptjs": "^2.4.3",
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"express": "^4.19.2",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.3.0",
"nodemon": "^3.1.0"
}

Example: Create the required files as seen on the project structure and add the following codes.

// authController.js

const User = require('../model/authModel');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');

exports.register = async (req, res) => {
    try {
        const { username, email, password } = req.body;
        let user = await User.findOne({ email });
        if (user) {
            return res.status(400).json({
                message: "User Already Exist",
                success: false
            });
        }
        user = new User({
            username: username,
            email: email,
            password: password
        });
        const salt = await bcrypt.genSalt(10);
        user.password = await bcrypt.hash(password, salt);
        await user.save();
        const token = generateJwtToken(user.id);
        res.status(201).json({
            success: true,
            token: token,
            message: "User registered successfully"
        });
    }
    catch (error) {
        res.status(500).json({
            message: "Server error! New user registration failed",
            success: false
        });
    }
};

exports.login = async (req, res) => {
    try {
        const { email, password } = req.body;
        const user = await User.findOne({ email });
        if (!user) {
            return res.status(400).json({
                message: "Invalid credentials",
                success: false
            });
        }
        const isMatched = await bcrypt.compare(password, user.password);
        if (!isMatched) {
            return res.status(400).json({
                message: "Invalid credentials",
                success: false
            });
        }
        const token = generateJwtToken(user.id);
        res.status(200).json({
            success: true,
            message: "User logged in successfully",
            token: token
        });
    }
    catch (error) {
        return res.status(500).json({
            success: false,
            message: "Internal Server Error, Login unsuccessful"
        });
    }
};

function generateJwtToken(userID) {
    const payload = {
        user: {
            id: userID
        }
    };
    return jwt.sign(payload, 'jwtSecret', { expiresIn: 3600 });
}

exports.getUserDetailsFronUserId = async (req, res) => {
    try {
        const { id } = req.params;
        const user = await User.findById(id);
        return res.status(200).json(user);
    }
    catch (error) {
        res.status(500).json({
            success: false,
            message: error.message
        });
    }
};
// carController.js

const Car = require("../model/carModel");
const jwt = require("jsonwebtoken");
const secretKey = "jwtSecret";

exports.getAllCars = async (req, res) => {
    let userId;
    try {
        jwt.verify(
            req.headers["authorization"].substring(7),
            secretKey,
            (error, decodedToken) => {
                if (error) {
                    res.status(401).json({
                        success: false,
                        message: error.message,
                    });
                } else {
                    userId = decodedToken.user.id;
                }
            }
        );
        const cars = await Car.find();
        res.status(200).json(cars);
    } catch (error) {
        res.status(500).json({
            success: false,
            message: error.message,
        });
    }
};

exports.getCarById = async (req, res) => {
    try {
        const id = req.params.id;
        const car = await Car.findById(id);
        res.status(200).json(car);
    } catch (error) {
        res.status(500).json({
            success: false,
            message: error.message,
        });
    }
};

exports.createCar = async (req, res) => {
    try {
        let car = {};
        const {
            company,
            model,
            price,
            color,
            carNumber,
            insuranceDetails,
            serviceHistory,
            image,
        } = req.body;
        if (
            req.headers["authorization"] &&
            req.headers["authorization"].startsWith("Bearer ")
        ) {
            jwt.verify(
                req.headers["authorization"].substring(7),
                secretKey,
                (error, decodedToken) => {
                    if (error) {
                        res.status(401).json({
                            success: false,
                            message: error.message,
                        });
                    } else {
                        car = new Car({
                            owner: decodedToken.user.id,
                            company,
                            model,
                            price,
                            color,
                            carNumber,
                            insuranceDetails,
                            serviceHistory,
                            image,
                        });
                    }
                }
            );
            await car.save();
            res.status(200).json({
                success: true,
                address: car,
            });
        }
    } catch (error) {
        res.status(500).json({
            success: false,
            message: error.message,
        });
    }
};

exports.updateCar = async (req, res) => {
    try {
        const { id } = req.params;
        const {
            company,
            model,
            price,
            color,
            carNumber,
            insuranceDetails,
            serviceHistory,
            image,
        } = req.body;
        const car = await Car.findByIdAndUpdate(
            id,
            {
                company,
                model,
                price,
                color,
                carNumber,
                insuranceDetails,
                serviceHistory,
                image,
            },
            { new: true }
        );
        res.status(201).json({
            success: true,
            car: car,
        });
    } catch (error) {
        res.status(500).json({
            success: false,
            message: error.message,
        });
    }
};

exports.deleteCar = async (req, res) => {
    try {
        const { id } = req.params;
        await Car.findByIdAndDelete(id);
        res.status(200).json({
            success: true,
            message: "Car Deleted Successfully",
        });
    } catch (error) {
        res.status(500).json({
            success: false,
            message: "Error while deleting car",
        });
    }
};
// authModel.js

const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
    username: {
        type: String,
        required: true
    },
    email: {
        type: String,
        required: true,
        uniques: true
    },
    password: {
        type: String,
        required: true
    }
});

module.exports = mongoose.model('User', userSchema);
// carModel.js

const mongoose = require('mongoose');

const carSchema = new mongoose.Schema({
    owner: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User'
    },
    company: {
        type: String,
        required: true
    },
    model: {
        type: String,
        required: true
    },
    price: {
        type: Number,
        required: true
    },
    color: {
        type: String,
        required: true
    },
    carNumber: {
        type: String,
        required: true
    },
    insuranceDetails: {
        policyNumber: String,
        provider: String,
        expirationDate: Date
    },
    serviceHistory: [{
        serviceDate: Date,
        serviceType: String,
        cost: Number,
        provider: String
    }],
    image: {
        type: String,
        required: true
    }
});

module.exports = mongoose.model('Car', carSchema);
// authRoutes.js

const express = require('express');
const router = express.Router();
const authController = require('../controller/authController');

router.post('/register', authController.register);

router.post('/login', authController.login);

router.get('/:id', authController.getUserDetailsFronUserId);

module.exports = router;
// carRoutes.js

const express = require('express');
const router = express.Router();
const carController = require('../controller/carController');

router.get('/getAllCars', carController.getAllCars);

router.get('/getCarById/:id', carController.getCarById);

router.post('/createCar', carController.createCar);

router.put('/updateCar/:id', carController.updateCar);

router.delete('/deleteCar/:id', carController.deleteCar);

module.exports = router;
// server.js

const express = require('express');
const cors = require('cors');
const mongoose = require('mongoose');
const authRoutes = require('../backend/route/authRoute');
const carRoute = require('../backend/route/carRoute');

const app = express();
app.use(cors());
app.use(express.json());

mongoose.connect('mongodb://localhost:27017/car-vault', {
    family: 4
})
    .then(() => console.log("Mongo DB connected"))
    .catch(err => console.log(err));

app.use('/api/auth', authRoutes);
app.use('/api/cars', carRoute);

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

Step 4: To start the backend run the following command.

nodemon server.js

Step 5: Install the angular CLI

npm install -g @angular/cli

Step 6: Create a new angular project

ng new frontend

Step 7: Create components for different functionalities in angular

Syntax - ng generate component <component-name>
ng generate component user
ng generate component car-create
ng generate component car-update
ng generate component car-view
ng generate component car-get-delete

Step 8: Create services for communication between frontend and backend

Syntax - ng generate service <service-name>
ng generate service auth
ng generate service car
ng generate service shared

Folder Structure(Frontend):

output-(6)

PROJECT STRUCTURE FOR FRONTEND

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

"dependencies": {
"@angular/animations": "^17.2.0",
"@angular/common": "^17.2.0",
"@angular/compiler": "^17.2.0",
"@angular/core": "^17.2.0",
"@angular/forms": "^17.2.0",
"@angular/platform-browser": "^17.2.0",
"@angular/platform-browser-dynamic": "^17.2.0",
"@angular/platform-server": "^17.2.0",
"@angular/router": "^17.2.0",
"@angular/ssr": "^17.2.3",
"@auth0/angular-jwt": "^5.2.0",
"express": "^4.18.2",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.3"
}

Example: Create the required files as seen in project structure and add the following codes.

<!-- app.component.html -->

<main class="main">
    <div class="content">
        <div class="left-side">
            <h1>{{ title }}</h1>
            <div>
                <ul>
                    <li><a (click)="login()" *ngIf="!isLoggedIn">Login</a></li>
                    <li><a (click)="register()" *ngIf="!isLoggedIn">Register</a></li>
                    <li><a (click)="logout()" *ngIf="isLoggedIn">Logout</a></li>
                </ul>
            </div>
        </div>

    </div>
</main>
<router-outlet>
</router-outlet>
<!-- user.component.html -->

<div class="error-message" *ngIf="errorMessage">
  {{ errorMessage }}
</div>
<div class="success-message" *ngIf="successMessage">
  {{ successMessage }}
</div>

<div class="container" *ngIf="loginActive">
  <h2>Login</h2>
  <form (ngSubmit)="login()">
    <div class="form-group">
      <label for="email">Email:</label>
      <input
        type="email"
        class="form-control"
        id="email"
        name="email"
        [(ngModel)]="email"
        required
      />
    </div>
    <div class="form-group">
      <label for="password">Password:</label>
      <input
        type="password"
        class="form-control"
        id="password"
        name="password"
        [(ngModel)]="password"
        required
      />
    </div>
    <button type="submit" class="btn btn-primary">Login</button>
  </form>
</div>
<div class="container" *ngIf="registerActive">
  <h2>Register</h2>
  <form (submit)="register()">
    <div class="form-group">
      <label for="username">Username</label>
      <input
        type="text"
        id="username"
        class="form-control"
        [(ngModel)]="username"
        name="username"
        required
      />
    </div>
    <div class="form-group">
      <label for="email">Email</label>
      <input
        type="email"
        id="email"
        class="form-control"
        [(ngModel)]="email"
        name="email"
        required
      />
    </div>
    <div class="form-group">
      <label for="password">Password</label>
      <input
        type="password"
        id="password"
        class="form-control"
        [(ngModel)]="password"
        name="password"
        required
      />
    </div>
    <button type="submit" class="btn btn-primary">Register</button>
  </form>
</div>
<!-- car-view.component.html -->

<h2>View Car Details</h2>
<form [formGroup]="carForm">
  <input formControlName="company" placeholder="Company" />
  <input formControlName="model" placeholder="Model" />
  <input type="number" formControlName="price" placeholder="Price" />
  <input formControlName="color" placeholder="Color" />
  <input formControlName="carNumber" placeholder="Car Number" />
  <div formGroupName="insuranceDetails">
    <input formControlName="policyNumber" placeholder="Policy Number" />
    <input formControlName="provider" placeholder="Provider" />
    <input
      type="date"
      formControlName="expirationDate"
      placeholder="Expiration Date"
    />
  </div>
  <div formArrayName="serviceHistory">
    <div
      *ngFor="let service of serviceHistory.controls; let i = index"
      [formGroupName]="i"
    >
      <input formControlName="serviceDate" type="date" />
      <input formControlName="serviceType" />
      <input formControlName="cost" type="number" />
      <input formControlName="provider" />
    </div>
  </div>
  <input formControlName="image" placeholder="Image URL" readonly />
  <div *ngIf="carForm.get('image')?.value">
    <img [src]="carForm.get('image')?.value" />
  </div>
  <div>
    <button type="button" class="get-car" (click)="getAllCars()">
      Get List of All Cars
    </button>
  </div>
</form>
<!-- car-update.component.html -->

<div>
  <button class="get-car" type="button" (click)="getAllCars()">
    Get List of All Cars
  </button>
</div>

<h2>Update Car Details</h2>
<form [formGroup]="carForm" (ngSubmit)="onSave()">
  <input formControlName="company" placeholder="Company" />
  <input formControlName="model" placeholder="Model" />
  <input type="number" formControlName="price" placeholder="Price" />
  <input formControlName="color" placeholder="Color" />
  <input formControlName="carNumber" placeholder="Car Number" />
  <div formGroupName="insuranceDetails">
    <input formControlName="policyNumber" placeholder="Policy Number" />
    <input formControlName="provider" placeholder="Provider" />
    <input
      type="date"
      formControlName="expirationDate"
      placeholder="Expiration Date"
    />
  </div>
  <div formArrayName="serviceHistory">
    <div
      *ngFor="let service of serviceHistories.controls; let i = index"
      [formGroupName]="i"
    >
      <input formControlName="serviceDate" type="date" />
      <input formControlName="serviceType" />
      <input formControlName="cost" type="number" />
      <input formControlName="provider" />
      <button type="button" (click)="removeServiceHistory(i)">Remove</button>
    </div>
  </div>
  <button type="button" (click)="addServiceHistory()">
    Add Service Record
  </button>
  <input formControlName="image" placeholder="Image URL" />
  <div *ngIf="carForm.get('image')?.value">
    <img [src]="carForm.get('image')?.value" />
  </div>
  <button type="submit">Submit</button>
</form>
<!-- car-get-delete.component.html -->

<div>
    <button type="button" class="add-car" (click)="createCar()">
        Add New Car Detail
    </button>
</div>

<div class="car-list-container">
    <div class="row">
        <div class="col card" *ngFor="let car of cars">
            <div class="car-item">
                <div class="car-info">
                    <span class="info"><strong>Company/Model:</strong> {{ car.company }} -
                        {{ car.model }}</span>
                    <span class="info"><strong>Color:</strong> {{ car.color }}</span>
                    <span class="info"><strong>Car Number:</strong> {{ car.carNumber }}</span>
                </div>
                <div *ngIf="car.insuranceDetails" class="car-info">
                    <span class="info"><strong>INSURANCE DETAILS -</strong></span>
                    <span class="info"><strong>Policy Number:</strong>
                        {{ car.insuranceDetails.policyNumber }}</span>
                    <span class="info"><strong>Provider:</strong>
                        {{ car.insuranceDetails.provider }}</span>
                    <span class="info"><strong>Expiration:</strong>
                        {{
                        car.insuranceDetails.expirationDate | date : "mediumDate"
                        }}</span>
                </div>
                <div *ngIf="car.serviceHistory && car.serviceHistory.length > 0" class="car-info">
                    <span class="info"><strong>SERVICE DETAILS -</strong></span>
                    <div *ngFor="let service of car.serviceHistory" class="info">
                        <strong>Service:</strong>
                        {{ service.serviceDate | date : "mediumDate" }},
                        {{ service.serviceType }}, ${{ service.cost }},
                        {{ service.provider }}
                    </div>
                </div>
                <div *ngIf="car.image" class="car-info">
                    <img [src]="car.image" alt="Image of {{ car.model }}" class="car-image" />
                </div>
                <div class="car-actions">
                    <button class="edit-btn" (click)="editCar(car._id)">Edit</button>
                    <button class="delete-btn" (click)="confirmDelete(car._id)">
                        Delete
                    </button>
                    <button class="view-btn" (click)="viewCar(car._id)">View</button>
                </div>
            </div>
        </div>
    </div>
</div>

<ng-template #noCars>
    <div class="no-car-container">
        <p>No Cars Available</p>
    </div>
</ng-template>
<!-- car-create.component.html -->

<div>
    <button class="get-car" type="button" (click)="getAllCars()">
        Get List of All Cars
    </button>
</div>

<h2>Add New Car</h2>
<form [formGroup]="carForm" (ngSubmit)="createCar()">
    <input formControlName="company" placeholder="Company" />
    <input formControlName="model" placeholder="Model" />
    <input type="number" formControlName="price" placeholder="Price" />
    <input formControlName="color" placeholder="Color" />
    <input formControlName="carNumber" placeholder="Car Number" />
    <div formGroupName="insuranceDetails">
        <input formControlName="policyNumber" placeholder="Policy Number" />
        <input formControlName="provider" placeholder="Provider" />
        <input type="date" formControlName="expirationDate" placeholder="Expiration Date" />
    </div>
    <div formArrayName="serviceHistory">
        <div *ngFor="let service of serviceHistories.controls; let i = index" [formGroupName]="i">
            <input formControlName="serviceDate" type="date" />
            <input formControlName="serviceType" />
            <input formControlName="cost" type="number" />
            <input formControlName="provider" />
            <button type="button" (click)="removeServiceHistory(i)">Remove</button>
        </div>
    </div>
    <button type="button" (click)="addServiceHistory()">
        Add Service Record
    </button>
    <input formControlName="image" placeholder="Image URL" />
    <div *ngIf="carForm.get('image')?.value">
        <img [src]="carForm.get('image')?.value" />
    </div>
    <button type="submit">Submit</button>
    <button type="reset" (click)="resetForm()" style="background-color: crimson">
        Reset
    </button>
</form>
/* car-create.component.css */

body {
    font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
    background-color: #f0f2f5;
    margin: 0;
    padding: 20px;
    color: #333;
}

h2 {
    text-align: center;
}

form {
    background: #ffffff;
    padding: 20px;
    border-radius: 10px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
    width: 96%;
    margin: 10px auto;
}

input,
button {
    width: 100%;
    padding: 10px;
    margin-top: 10px;
    box-sizing: border-box;
    border-radius: 5px;
    border: 2px solid #ccc;
    transition: border-color 0.3s;
}

input:focus,
button:focus {
    border-color: #233e61;
    outline: none;
}

button {
    background-color: #233e61;
    color: white;
    font-size: 16px;
    border: none;
    cursor: pointer;
}

div[formGroupName] {
    margin-top: 0.7vmax;
    width: 98.5%;
}

div[formArrayName] input {
    width: 100%;
}

div[formArrayName="serviceHistory"] {
    margin-top: 20px;
    padding: 10px;
    background: #eef1f4;
    border-radius: 5px;
    width: 98.5%;
}

div[formGroupName] {
    margin-bottom: 10px;
    padding: 10px;
    background: #fff;
    border: 2px solid #e1e4e8;
    border-radius: 5px;
}

[type="button"] {
    background-color: #218838;
    margin-top: 0.6vmax;
    width: 100%;
}

ul {
    list-style: none;
    padding: 0;
}

li {
    background: #fff;
    padding: 15px;
    border-radius: 8px;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
    margin-top: 10px;
}

li button {
    padding: 8px 15px;
    border-radius: 5px;
    border: none;
    margin-left: 10px;
}

li button.edit {
    background-color: #ffc107;
    color: #333;
}

li button.edit:hover {
    background-color: #e0a800;
}

li button.delete {
    background-color: #dc3545;
    color: white;
}

li button.delete:hover {
    background-color: #c82333;
}

.clearfix::after {
    content: "";
    clear: both;
    display: table;
}

img {
    border-radius: 50%;
    width: 3vmax;
    height: 3vmax;
    margin-top: 1vmax;
}

.get-car {
    background-color: darkslategrey;
    padding: 15px 15px;
    border: none;
    color: white;
    cursor: pointer;
    border-radius: 5px;
    font-weight: bold;
    margin-top: 0.2vmax;
    width: 100%;
}
/* car-get-delete.component.css */

.car-list-container {
    margin: 20px auto;
    max-width: 100%;
    padding: 20px;
    background-color: #fff;
    border-radius: 10px;
    box-shadow: 0 2px 15px rgba(0, 0, 0, 0.1);
}

.car-list {
    list-style: none;
    padding: 0;
    width: 100%;
}

.col.card {
    display: flex;
    flex-direction: column;
    background-color: #fff;
    border-radius: 8px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
    margin-bottom: 20px;
    padding: 10px;
    width: 99%;
}

.car-item {
    display: flex;
    flex-direction: column;
}

.car-info {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    margin-bottom: 10px;
}

.info {
    margin-right: 20px;
    margin-bottom: 5px;
}

.car-image {
    width: 100%;
    max-width: 200px;
    height: auto;
    margin-top: 10px;
}

.car-actions {
    display: flex;
    justify-content: flex-start;
    margin-top: auto;
}

.edit-btn,
.delete-btn,
.view-btn {
    padding: 5px 10px;
    margin-right: 10px;
    border: none;
    color: white;
    background-color: #007bff;
    border-radius: 5px;
    cursor: pointer;
}

.edit-btn {
    background-color: #224870;
}

.delete-btn {
    background-color: #dc3545;
}

.view-btn {
    background-color: green;
}



.add-car {
    background-color: darkslategrey;
    padding: 15px 15px;
    border: none;
    color: white;
    cursor: pointer;
    border-radius: 5px;
    font-weight: bold;
    margin-top: 0.2vmax;
    width: 100%;
}

.no-car-container {
    margin-top: 10px;
    padding: 0.3vmax;
    background-color: #f8d7da;
    color: #721c24;
    border: 1px solid #f5c6cb;
    border-radius: 4px;
    text-align: center;
    font-size: 1.7vmax;
}

.car-image {
    border-radius: 50%;
    width: 2.5vmax;
    height: 2.5vmax;
}
/* car-get-delete.component.css */

.car-list-container {
    margin: 20px auto;
    max-width: 100%;
    padding: 20px;
    background-color: #fff;
    border-radius: 10px;
    box-shadow: 0 2px 15px rgba(0, 0, 0, 0.1);
}

.car-list {
    list-style: none;
    padding: 0;
    width: 100%;
}

.col.card {
    display: flex;
    flex-direction: column;
    background-color: #fff;
    border-radius: 8px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
    margin-bottom: 20px;
    padding: 10px;
    width: 99%;
}

.car-item {
    display: flex;
    flex-direction: column;
}

.car-info {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    margin-bottom: 10px;
}

.info {
    margin-right: 20px;
    margin-bottom: 5px;
}

.car-image {
    width: 100%;
    max-width: 200px;
    height: auto;
    margin-top: 10px;
}

.car-actions {
    display: flex;
    justify-content: flex-start;
    margin-top: auto;
}

.edit-btn,
.delete-btn,
.view-btn {
    padding: 5px 10px;
    margin-right: 10px;
    border: none;
    color: white;
    background-color: #007bff;
    border-radius: 5px;
    cursor: pointer;
}

.edit-btn {
    background-color: #224870;
}

.delete-btn {
    background-color: #dc3545;
}

.view-btn {
    background-color: green;
}



.add-car {
    background-color: darkslategrey;
    padding: 15px 15px;
    border: none;
    color: white;
    cursor: pointer;
    border-radius: 5px;
    font-weight: bold;
    margin-top: 0.2vmax;
    width: 100%;
}

.no-car-container {
    margin-top: 10px;
    padding: 0.3vmax;
    background-color: #f8d7da;
    color: #721c24;
    border: 1px solid #f5c6cb;
    border-radius: 4px;
    text-align: center;
    font-size: 1.7vmax;
}

.car-image {
    border-radius: 50%;
    width: 2.5vmax;
    height: 2.5vmax;
}
/* car-view.component.css */

/* Base Styles */
body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    background-color: #f0f2f5;
    margin: 0;
    padding: 20px;
    color: #333;
}

h2 {
    text-align: center;
}

form {
    background: #ffffff;
    padding: 20px;
    border-radius: 10px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
    width: 96%;
    margin: 10px auto;
}

input,
button {
    width: 100%;
    padding: 10px;
    margin-top: 10px;
    box-sizing: border-box;
    border-radius: 5px;
    border: 2px solid #ccc;
    transition: border-color 0.3s;
}

input:focus,
button:focus {
    border-color: #233e61;
    outline: none;
}

button {
    background-color: #233e61;
    color: white;
    font-size: 16px;
    border: none;
    cursor: pointer;
}

div[formGroupName] {
    margin-top: 0.7vmax;
    width: 98.5%;
}

div[formArrayName] input {
    width: 100%;
}

div[formArrayName="serviceHistory"] {
    margin-top: 20px;
    padding: 10px;
    background: #eef1f4;
    border-radius: 5px;
    width: 98.5%;
}

div[formGroupName] {
    margin-bottom: 10px;
    padding: 10px;
    background: #fff;
    border: 2px solid #e1e4e8;
    border-radius: 5px;
}

[type="button"] {
    background-color: #218838;
    margin-top: 0.6vmax;
    width: 100%;
}

ul {
    list-style: none;
    padding: 0;
}

li {
    background: #fff;
    padding: 15px;
    border-radius: 8px;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
    margin-top: 10px;
}

li button {
    padding: 8px 15px;
    border-radius: 5px;
    border: none;
    margin-left: 10px;
}

li button.edit {
    background-color: #ffc107;
    color: #333;
}

li button.edit:hover {
    background-color: #e0a800;
}

li button.delete {
    background-color: #dc3545;
    color: white;
}

li button.delete:hover {
    background-color: #c82333;
}

.clearfix::after {
    content: "";
    clear: both;
    display: table;
}

img {
    border-radius: 50%;
    width: 3vmax;
    height: 3vmax;
    margin-top: 1vmax;
}

.get-car {
    background-color: darkslategrey;
    padding: 15px 15px;
    border: none;
    color: white;
    cursor: pointer;
    border-radius: 5px;
    font-weight: bold;
    margin-top: 0.2vmax;
    width: 100%;
}
/* car-view.component.css */

/* Base Styles */
body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    background-color: #f0f2f5;
    margin: 0;
    padding: 20px;
    color: #333;
}

h2 {
    text-align: center;
}

form {
    background: #ffffff;
    padding: 20px;
    border-radius: 10px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
    width: 96%;
    margin: 10px auto;
}

input,
button {
    width: 100%;
    padding: 10px;
    margin-top: 10px;
    box-sizing: border-box;
    border-radius: 5px;
    border: 2px solid #ccc;
    transition: border-color 0.3s;
}

input:focus,
button:focus {
    border-color: #233e61;
    outline: none;
}

button {
    background-color: #233e61;
    color: white;
    font-size: 16px;
    border: none;
    cursor: pointer;
}

div[formGroupName] {
    margin-top: 0.7vmax;
    width: 98.5%;
}

div[formArrayName] input {
    width: 100%;
}

div[formArrayName="serviceHistory"] {
    margin-top: 20px;
    padding: 10px;
    background: #eef1f4;
    border-radius: 5px;
    width: 98.5%;
}

div[formGroupName] {
    margin-bottom: 10px;
    padding: 10px;
    background: #fff;
    border: 2px solid #e1e4e8;
    border-radius: 5px;
}

[type="button"] {
    background-color: #218838;
    margin-top: 0.6vmax;
    width: 100%;
}

ul {
    list-style: none;
    padding: 0;
}

li {
    background: #fff;
    padding: 15px;
    border-radius: 8px;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
    margin-top: 10px;
}

li button {
    padding: 8px 15px;
    border-radius: 5px;
    border: none;
    margin-left: 10px;
}

li button.edit {
    background-color: #ffc107;
    color: #333;
}

li button.edit:hover {
    background-color: #e0a800;
}

li button.delete {
    background-color: #dc3545;
    color: white;
}

li button.delete:hover {
    background-color: #c82333;
}

.clearfix::after {
    content: "";
    clear: both;
    display: table;
}

img {
    border-radius: 50%;
    width: 3vmax;
    height: 3vmax;
    margin-top: 1vmax;
}

.get-car {
    background-color: darkslategrey;
    padding: 15px 15px;
    border: none;
    color: white;
    cursor: pointer;
    border-radius: 5px;
    font-weight: bold;
    margin-top: 0.2vmax;
    width: 100%;
}
/* app.component.css */

.main {
    display: flex;
    flex-direction: column;
    align-items: center;
    background-color: darkslategrey;
    color: white;
}

.content {
    width: 100%;
    max-width: 1200px;
    padding: 20px;
}

.left-side {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.left-side h1 {
    margin: 0;
    margin-left: 3%;
}

.left-side ul {
    list-style-type: none;
    padding: 0;
    margin: 0;
}

.left-side li {
    display: inline;
    margin-right: 20px;
}

.left-side li a {
    text-decoration: none;
    color: white;
    font-weight: bold;
    font-size: 1.5rem;
}

.left-side li a:hover {
    color: lightgray;
}

a {
    cursor: pointer;
}
// user.component.ts

import { Component, OnInit } from '@angular/core';
import { UserService } from '../../services/user.service';
import { Router } from '@angular/router';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { SharedService } from '../../services/shared.service';

@Component({
    selector: 'app-user',
    standalone: true,
    imports: [FormsModule, CommonModule],
    templateUrl: './user.component.html',
    styleUrl: './user.component.css',
})
export class UserComponent implements OnInit {
    username!: string;
    email!: string;
    password!: string;
    credentials: any = {};
    successMessage: string = '';
    errorMessage: string = '';
    loginActive: boolean = true;
    registerActive: boolean = false;
    constructor(
        private userService: UserService,
        private router: Router,
        private sharedService: SharedService
    ) { }

    ngOnInit(): void {
        this.sharedService.loginEvent.subscribe(() => {
            this.loginActive = true;
            this.registerActive = false;
            this.username = '';
            this.email = '';
            this.password = '';
            this.successMessage = '';
            this.errorMessage = '';
        });
        this.sharedService.registerEvent.subscribe(() => {
            this.registerActive = true;
            this.loginActive = false;
            this.username = '';
            this.email = '';
            this.password = '';
            this.successMessage = '';
            this.errorMessage = '';
        });
    }

    login(): void {
        const credentials = {
            email: this.email,
            password: this.password,
        };
        this.userService.login(credentials).subscribe(
            (response: any) => {
                const token = response.token;
                localStorage.setItem('token', token);
                this.userService.setAuthenticationStatus(true);
                this.userService.emitLoggedInEvent();
                this.loginActive = false;
                this.registerActive = false;
                this.router.navigate(['/createCar']);
                this.successMessage = response.message;
            },
            (error) => {
                console.error('Error logging in:', error);
                this.errorMessage =
                    'Login unsuccessfull ! Please reload or try in incognito tab';
            }
        );
    }

    register(): void {
        const userData = {
            username: this.username,
            email: this.email,
            password: this.password,
        };

        this.userService.register(userData).subscribe(
            (response: any) => {
                this.successMessage = response.message;
                this.loginActive = true;
                this.registerActive = false;
            },
            (error: any) => {
                this.errorMessage = 'User not registered successfully';
            }
        );
    }
}
// car-view.component.ts

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import {
    FormBuilder,
    FormGroup,
    Validators,
    FormArray,
    FormsModule,
    ReactiveFormsModule,
} from '@angular/forms';
import { CarService } from '../../services/car.service';
import { CommonModule } from '@angular/common';

@Component({
    selector: 'app-car-view',
    standalone: true,
    imports: [FormsModule, CommonModule, ReactiveFormsModule],
    templateUrl: './car-view.component.html',
    styleUrls: ['./car-view.component.css'],
})
export class CarViewComponent implements OnInit {
    carForm!: FormGroup;
    carId: any;

    constructor(
        private fb: FormBuilder,
        private carService: CarService,
        private route: ActivatedRoute,
        private router: Router
    ) { }

    ngOnInit() {
        this.carId = this.route.snapshot.paramMap.get('id');
        this.initForm();
        if (this.carId) {
            this.loadCarDetails(this.carId);
        }
    }

    initForm() {
        this.carForm = this.fb.group({
            company: ['', Validators.required],
            model: ['', Validators.required],
            price: [null, [Validators.required, Validators.min(1)]],
            color: ['', Validators.required],
            carNumber: ['', Validators.required],
            insuranceDetails: this.fb.group({
                policyNumber: [''],
                provider: [''],
                expirationDate: [''],
            }),
            serviceHistory: this.fb.array([]),
            image: ['', Validators.required],
        });
    }

    loadCarDetails(id: string) {
        if (typeof localStorage !== 'undefined') {
            const token = localStorage.getItem('token');
            if (token) {
                this.carService.getCarById(id, token).subscribe((car) => {
                    const formattedDate = car.insuranceDetails.expirationDate
                        ? new Date(car.insuranceDetails.expirationDate)
                            .toISOString()
                            .substring(0, 10)
                        : '';
                    this.carForm.patchValue({
                        company: car.company,
                        model: car.model,
                        price: car.price,
                        color: car.color,
                        carNumber: car.carNumber,
                        insuranceDetails: {
                            ...car.insuranceDetails,
                            expirationDate: formattedDate,
                        },
                        image: car.image,
                    });
                    if (car.serviceHistory) {
                        this.setServiceHistory(car.serviceHistory);
                    }
                });
            }
        }
    }

    get serviceHistory(): FormArray {
        return this.carForm.get('serviceHistory') as FormArray;
    }

    setServiceHistory(serviceHistory: any[]) {
        const serviceArray = this.carForm.get('serviceHistory') as FormArray;
        serviceHistory.forEach((service) => {
            const formattedServiceDate = service.serviceDate
                ? new Date(service.serviceDate).toISOString().substring(0, 10)
                : '';
            serviceArray.push(
                this.fb.group({
                    serviceDate: [formattedServiceDate, Validators.required],
                    serviceType: [service.serviceType, Validators.required],
                    cost: [service.cost, Validators.required],
                    provider: [service.provider, Validators.required],
                })
            );
        });
    }

    getAllCars() {
        if (typeof localStorage !== 'undefined') {
            const token = localStorage.getItem('token');
            if (token) {
                this.router.navigate(['/getAllCars']);
            }
        }
    }
}
// car-update.component.ts

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import {
    FormBuilder,
    FormGroup,
    Validators,
    FormArray,
    FormsModule,
    ReactiveFormsModule,
} from '@angular/forms';
import { CarService } from '../../services/car.service';
import { CommonModule } from '@angular/common';

@Component({
    selector: 'app-car-update',
    standalone: true,
    imports: [FormsModule, CommonModule, ReactiveFormsModule],
    templateUrl: './car-update.component.html',
    styleUrls: ['./car-update.component.css'],
})
export class CarUpdateComponent implements OnInit {
    carForm!: FormGroup;
    carId: any;

    constructor(
        private fb: FormBuilder,
        private carService: CarService,
        private route: ActivatedRoute,
        private router: Router
    ) { }

    ngOnInit() {
        this.carId = this.route.snapshot.paramMap.get('id');
        this.initForm();
        if (this.carId) {
            this.loadCarDetails(this.carId);
        }
    }

    initForm() {
        this.carForm = this.fb.group({
            company: ['', Validators.required],
            model: ['', Validators.required],
            price: [null, [Validators.required, Validators.min(1)]],
            color: ['', Validators.required],
            carNumber: ['', Validators.required],
            insuranceDetails: this.fb.group({
                policyNumber: [''],
                provider: [''],
                expirationDate: [''],
            }),
            serviceHistory: this.fb.array([]),
            image: ['', Validators.required],
        });
    }

    loadCarDetails(id: string) {
        if (typeof localStorage !== 'undefined') {
            const token = localStorage.getItem('token');
            if (token) {
                this.carService.getCarById(id, token).subscribe((car) => {
                    const formattedDate = car.insuranceDetails.expirationDate
                        ? new Date(car.insuranceDetails.expirationDate)
                            .toISOString()
                            .substring(0, 10)
                        : '';
                    this.carForm.patchValue({
                        company: car.company,
                        model: car.model,
                        price: car.price,
                        color: car.color,
                        carNumber: car.carNumber,
                        insuranceDetails: {
                            ...car.insuranceDetails,
                            expirationDate: formattedDate,
                        },
                        image: car.image,
                    });
                    if (car.serviceHistory) {
                        this.setServiceHistory(car.serviceHistory);
                    }
                });
            }
        }
    }

    setServiceHistory(serviceHistory: any[]) {
        const serviceArray = this.carForm.get('serviceHistory') as FormArray;
        serviceHistory.forEach((service) => {
            const formattedServiceDate = service.serviceDate
                ? new Date(service.serviceDate).toISOString().substring(0, 10)
                : '';
            serviceArray.push(
                this.fb.group({
                    serviceDate: [formattedServiceDate, Validators.required],
                    serviceType: [service.serviceType, Validators.required],
                    cost: [service.cost, Validators.required],
                    provider: [service.provider, Validators.required],
                })
            );
        });
    }

    get serviceHistories(): FormArray {
        return this.carForm.get('serviceHistory') as FormArray;
    }

    newServiceHistory(): FormGroup {
        return this.fb.group({
            serviceDate: ['', Validators.required],
            serviceType: ['', Validators.required],
            cost: [null, Validators.required],
            provider: ['', Validators.required],
        });
    }

    addServiceHistory() {
        this.serviceHistories.push(this.newServiceHistory());
    }

    removeServiceHistory(index: number) {
        this.serviceHistories.removeAt(index);
    }

    onSave() {
        if (typeof localStorage !== 'undefined') {
            const token = localStorage.getItem('token');
            if (token) {
                if (this.carForm.valid) {
                    this.carService
                        .updateCar(this.carId, this.carForm.value, token)
                        .subscribe(() => {
                            this.router.navigate(['/getAllCars']);
                        });
                }
            }
        }
    }

    getAllCars() {
        if (typeof localStorage !== 'undefined') {
            const token = localStorage.getItem('token');
            if (token) {
                this.router.navigate(['/getAllCars']);
            }
        }
    }
}
// car-get-delete.component.ts

import { CommonModule } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { Router } from '@angular/router';
import { CarService } from '../../services/car.service';

@Component({
    selector: 'app-car-get-delete',
    standalone: true,
    imports: [FormsModule, CommonModule],
    templateUrl: './car-get-delete.component.html',
    styleUrl: './car-get-delete.component.css',
})
export class CarGetDeleteComponent implements OnInit {
    cars: any[] = [];
    constructor(private router: Router, private carService: CarService) { }
    ngOnInit(): void {
        if (typeof localStorage !== 'undefined') {
            const token = localStorage.getItem('token');
            if (token) {
                this.carService.getAllCars(token).subscribe((cars: any) => {
                    this.cars = cars;
                });
            }
        }
    }
    editCar(id: any) {
        this.router.navigate([`/updateCar/${id}`]);
    }

    confirmDelete(carId: string): void {
        const confirmDelete = window.confirm(
            'Are you sure you want to delete this car detail?'
        );
        if (confirmDelete) {
            this.deleteCar(carId);
        }
    }

    deleteCar(id: string): void {
        if (typeof localStorage !== 'undefined') {
            const token = localStorage.getItem('token');
            if (token) {
                this.carService.deleteCar(id, token).subscribe(() => {
                    this.cars = this.cars.filter((car: any) => car._id !== id);
                });
            }
        }
    }

    viewCar(id: any) {
        this.router.navigate([`/getCarById/${id}`]);
    }

    createCar() {
        if (typeof localStorage !== 'undefined') {
            const token = localStorage.getItem('token');
            if (token) {
                this.router.navigate(['/createCar']);
            }
        }
    }
}
// car-create.component.ts

import { Component } from '@angular/core';
import {
    FormArray,
    FormBuilder,
    FormGroup,
    FormsModule,
    ReactiveFormsModule,
    Validators,
} from '@angular/forms';
import { CarService } from '../../services/car.service';
import { CommonModule } from '@angular/common';
import { Router } from '@angular/router';

@Component({
    selector: 'app-car-create',
    standalone: true,
    imports: [FormsModule, CommonModule, ReactiveFormsModule],
    templateUrl: './car-create.component.html',
    styleUrl: './car-create.component.css',
})
export class CarCreateComponent {
    carForm!: FormGroup;
    cars: any;
    editing: boolean = false;

    constructor(
        private fb: FormBuilder,
        private carService: CarService,
        private router: Router
    ) { }

    ngOnInit() {
        this.initForm();
    }

    initForm() {
        this.carForm = this.fb.group({
            company: ['', Validators.required],
            model: ['', Validators.required],
            price: [null, [Validators.required, Validators.min(1)]],
            color: ['', Validators.required],
            carNumber: ['', Validators.required],
            insuranceDetails: this.fb.group({
                policyNumber: [''],
                provider: [''],
                expirationDate: [''],
            }),
            serviceHistory: this.fb.array([]),
            image: ['', Validators.required],
        });
    }

    get serviceHistories(): FormArray {
        return this.carForm.get('serviceHistory') as FormArray;
    }

    newServiceHistory(): FormGroup {
        return this.fb.group({
            serviceDate: ['', Validators.required],
            serviceType: ['', Validators.required],
            cost: [null, Validators.required],
            provider: ['', Validators.required],
        });
    }

    addServiceHistory() {
        this.serviceHistories.push(this.newServiceHistory());
    }

    removeServiceHistory(index: number) {
        this.serviceHistories.removeAt(index);
    }

    createCar() {
        if (typeof localStorage !== 'undefined') {
            const token = localStorage.getItem('token');
            if (token) {
                this.carService.createCar(this.carForm.value, token).subscribe(() => {
                    this.router.navigate(['/getAllCars']);
                });
            }
        }
    }
    resetForm(): void {
        this.carForm.reset();
    }
    getAllCars() {
        if (typeof localStorage !== 'undefined') {
            const token = localStorage.getItem('token');
            if (token) {
                this.router.navigate(['/getAllCars']);
            }
        }
    }
}
// app.component.ts

import { Component } from '@angular/core';
import { Router, RouterOutlet } from '@angular/router';
import { SharedService } from './services/shared.service';
import { UserService } from './services/user.service';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';

@Component({
    selector: 'app-root',
    standalone: true,
    imports: [RouterOutlet, FormsModule, CommonModule],
    templateUrl: './app.component.html',
    styleUrl: './app.component.css',
})
export class AppComponent {
    title = 'Car Vault';
    isLoggedIn: boolean = false;
    constructor(
        private router: Router,
        private userService: UserService,
        private sharedService: SharedService
    ) { }
    ngOnInit(): void {
        this.userService.loggedInEvent.subscribe((data: any) => {
            this.isLoggedIn = true;
        });
        if (typeof localStorage !== 'undefined' && localStorage.getItem('token')) {
            this.isLoggedIn = true;
        }
    }

    login(): void {
        this.sharedService.triggerLoginEvent();
        this.router.navigate(['/']);
    }

    register(): void {
        this.sharedService.triggerRegisterEvent();
        this.router.navigate(['/']);
    }

    logout(): void {
        this.userService.setAuthenticationStatus(false);
        this.isLoggedIn = false;
        localStorage.removeItem('token');
        this.router.navigate(['/']);
    }
}
// app.routes.ts

import { Routes } from '@angular/router';
import { UserComponent } from './components/user/user.component';
import { CarGetDeleteComponent } from './components/car-get-delete/car-get-delete.component';
import { CarCreateComponent } from './components/car-create/car-create.component';
import { CarUpdateComponent } from './components/car-update/car-update.component';
import { CarViewComponent } from './components/car-view/car-view.component';

export const routes: Routes = [
    { path: '', component: UserComponent },
    { path: 'getAllCars', component: CarGetDeleteComponent },
    { path: 'getCarById/:id', component: CarViewComponent },
    { path: 'createCar', component: CarCreateComponent },
    { path: 'updateCar/:id', component: CarUpdateComponent },
    { path: 'deleteCar/:id', component: CarGetDeleteComponent },
    { path: '**', redirectTo: '/' },
];
// app.module.ts

import { InjectionToken, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { JwtHelperService, JWT_OPTIONS } from '@auth0/angular-jwt';
import { RouterModule } from '@angular/router';
import { routes } from './app.routes';
import { CarCreateComponent } from './components/car-create/car-create.component';
import { CarGetDeleteComponent } from './components/car-get-delete/car-get-delete.component';
import { CarUpdateComponent } from './components/car-update/car-update.component';
import { CarViewComponent } from './components/car-view/car-view.component';
import { UserComponent } from './components/user/user.component';
@NgModule({
    declarations: [
        AppComponent,
        CarCreateComponent,
        CarGetDeleteComponent,
        CarUpdateComponent,
        CarViewComponent,
        UserComponent,
    ],
    imports: [BrowserModule, FormsModule, RouterModule.forRoot(routes)],
    exports: [RouterModule],
    providers: [
        { provide: JWT_OPTIONS, useValue: JWT_OPTIONS },
        JwtHelperService,
    ],
    bootstrap: [AppComponent],
})
export class AppModule { }

To start the angular application run the following command:

ng serve

Output:

car vault app using mean stack

Car Vault app using MEAN

Article Tags :