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:
Prerequisites:
Approach to Create Car Vault
Backend:
- Set up a new node js project
- Set up the server using express with CORS as the middleware in file server.js
- Create the app instance using const app = express()
- Create controllers folder which will define the API methods
- Create models folder to create the database schema for car-vault and user
- Create router folder and mention all the routes related to user login, register, creating, retrieving, updating and deleting the car details in the car vault
- Set up local Mongo DB database
- Set up the connection to local MongoDB in server.js file
- Create collections within the database to store and retrieve the data from database
- Two collections are created – car-vault, user
- Implement the core logic of creating, retrieving, updating and deleting the car details
- Test the API endpoints using postman
Frontend:
- Create a new angular project
- Create components folder and seperate components for seperate routes
- Create HTML, CSS and ts files for all the components
- Create service to establish communication between frontend and backend routes
- Create various routes in app.routes.ts folder
- Test the frontend application in browser at https://localhost:3000
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):
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):
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:
Contact Us