Horje
Fruit and Vegetable Market Shop using MEAN

In this comprehensive guide, we’ll walk through the step-by-step process of creating a Fruit and Vegetable Market Shop using the MEAN (MongoDB, Express.js, Angular, Node.js) stack. This project will showcase how to set up a full-stack web application where users can view, filter, and purchase various fruits and vegetables.

Screenshot-(198)-(1)-2-(1)-(1)-(1)

Final Project Output

Prerequisites:

Approach

The project will be divided into two main parts:

Client-side: The client-side will be built using Angular, a popular JavaScript framework for building web applications. We’ll create the necessary components, services, and routing to handle the user interface and interact with the server-side.

Server-side: The server-side will be built using Node.js and Express, a web application framework for Node.js. We’ll create the API endpoints, handle database operations using Mongoose, and manage Cross-Origin Resource Sharing (CORS) policies.

Steps to Create the Project

Step 1: Create a root directory(MEAN) for your project

mkdir Fruits
cd Fruits

Step 2: Create a root directory for your server-side project.

mkdir server
cd server

Step 3: Initialize a new Node.js project

npm init -y

Step 4: Install the required dependencies

npm install cors dotenv express mongoose 

Project Structure (Backend):


Screenshot-(285)

Backend Structure

Updated dependencies will look like:

{
"name": "server",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"cors": "^2.8.5",
"express": "^4.19.2",
"mongoose": "^8.4.0"
}
}

Example: To demonstrate creating the server endpoint which is consumed by the front-end application.

JavaScript
//index.js

const express = require("express");
const mongoose = require("mongoose");
const cors = require("cors");

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

// Connect to MongoDB
const MONGO_URL = `your db url`;

mongoose
  .connect(MONGO_URL)
  .then(() => {
    console.log("Connected to MongoDB");
    seedProductsData();
  })
  .catch((error) => {
    console.log("Failed to connect to MongoDB", error);
  });

const productsSchema = new mongoose.Schema({
  name: String,
  description: String,
  price: Number,
  type: String,
  image: String,
});
const ProductsModel = mongoose.model("Products", productsSchema);

async function seedProductsData() {
  await ProductsModel.deleteMany({});

  const productsData = [
    {
      name: "Apple",
      type: "Fruit",
      description: "Fresh and crispy",
      price: 150,
      image:
        "https://media.geeksforgeeks.org/wp-content/uploads/20240104142542/apple.jpg",
    },
    {
      name: "Banana",
      type: "Fruit",
      description: "Rich in potassium",
      price: 75,
      image:
        "https://media.geeksforgeeks.org/wp-content/uploads/20240104142554/banana.jpg",
    },
    {
      name: "Orange",
      type: "Fruit",
      description: "Packed with vitamin C",
      price: 200,
      image:
        "https://media.geeksforgeeks.org/wp-content/uploads/20240104142641/orange.jpg",
    },
    {
      name: "Carrot",
      type: "Vegetable",
      description: "Healthy and crunchy",
      price: 100,
      image:
        "https://media.geeksforgeeks.org/wp-content/uploads/20240104142613/carrot.jpg",
    },
    {
      name: "Broccoli",
      type: "Vegetable",
      description: "Nutrient-rich greens",
      price: 175,
      image:
        "https://media.geeksforgeeks.org/wp-content/uploads/20240104142601/brocoli.jpg",
    },
    {
      name: "Grapes",
      type: "Fruit",
      description: "Sweet and juicy",
      price: 250,
      image:
        "https://media.geeksforgeeks.org/wp-content/uploads/20240104142629/grapes.jpg",
    },
    {
      name: "Strawberry",
      type: "Fruit",
      description: "Delicious red berries",
      price: 300,
      image:
        "https://media.geeksforgeeks.org/wp-content/uploads/20240104142657/strawberry.jpg",
    },
    {
      name: "Lettuce",
      type: "Vegetable",
      description: "Crisp and fresh",
      price: 120,
      image:
        "https://media.geeksforgeeks.org/wp-content/uploads/20240104142635/lettue.jpg",
    },
    {
      name: "Tomato",
      type: "Vegetable",
      description: "Versatile and flavorful",
      price: 180,
      image:
        "https://media.geeksforgeeks.org/wp-content/uploads/20240104142704/tomato.jpg",
    },
    {
      name: "Cucumber",
      type: "Vegetable",
      description: "Cool and hydrating",
      price: 130,
      image:
        "https://media.geeksforgeeks.org/wp-content/uploads/20240104142621/cocumber.jpg",
    },
  ];

  await ProductsModel.insertMany(productsData);
  console.log("Products data seeded successfully!");
}

app.get("/api/products", async (req, res) => {
  try {
    const products = await ProductsModel.find({});
    res.status(200).json({ success: true, data: products });
  } catch (error) {
    res.status(500).json({ success: false, error: error.message });
  }
});

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

To start the server run the following command.

node index.js

Steps to create Front End

Step 1: Run the following command to install Angular CLI globally:

npm i @angular/[email protected]

Step 2: Go to Fruits directory and run this command

mkdir client

Step 3: Create Angular App

ng new fruit-vegetable-market-app

Updated dependencies will look like:

{
"name": "fruit-vegetable-market-app",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"private": true,
"dependencies": {
"@angular/animations": "^17.3.0",
"@angular/common": "^17.3.0",
"@angular/compiler": "^17.3.0",
"@angular/core": "^17.3.0",
"@angular/forms": "^17.3.0",
"@angular/platform-browser": "^17.3.0",
"@angular/platform-browser-dynamic": "^17.3.0",
"@angular/router": "^17.3.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.14.3"
},
"devDependencies": {
"@angular-devkit/build-angular": "^17.3.7",
"@angular/cli": "^17.3.7",
"@angular/compiler-cli": "^17.3.0",
"@types/jasmine": "~5.1.0",
"jasmine-core": "~5.1.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.2.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~5.4.2"
}
}

Folder Structure:

Screenshot-(283)

Front end structure

Stpes to create the front-end of the project

Step 1: Create navbar.component.html,navbar.component.ts inside navbar folder

Step 2: Create product-card.component.ts, product-card.component.html insider product-card folder

Step 3: Update code of styles.css and main.ts

Step 4: Update code of app.component.html , app.component.ts and app.component.css

HTML
<!-- app.component.html -->
<div class="container">
  <app-navbar></app-navbar>
  <div class="filters">
    <div class="rating-container">
      <label for="rating">Filter by Type:</label>
      <select id="rating" [formControl]="rating" (change)="filterRestaurants()">
        <option value="">All</option>
        <option value="Fruit">Fruit</option>
        <option value="Vegetable">Vegetable</option>
      </select>
    </div>
    <div>
      <button (click)="sortByPrice()">Sort by Price</button>&nbsp;
      <label for="minPrice">Min Price:</label>
      <input
        type="number"
        id="minPrice"
        [formControl]="minPrice"
        (change)="filterRestaurants()"
      />&nbsp;
      <label for="maxPrice">Max Price:</label>
      <input
        type="number"
        id="maxPrice"
        [formControl]="maxPrice"
        (change)="filterRestaurants()"
      />
      <button (click)="filterByRange()">Filter by Range</button>
    </div>
    <div>
      Search :
      <input
        type="text"
        [(ngModel)]="searchText"
        (keyup)="applySearch()"
        placeholder="Search..."
      />
    </div>
  </div>

  <div class="restaurants">
    <h3>Restaurants</h3>
    <span class="cart">
      Cart
      <div className="cart-content">
        <span style="color: brown">Total Price : $ {{ cartTotal }}</span>
      </div>
    </span>
    <div class="restaurant-cards">
      <div
        *ngFor="let restaurant of filteredRestaurants"
        class="restaurant-card"
      >
        <img [src]="restaurant.image" alt="{{ restaurant.name }}" />
        <div class="card-body">
          <h5 class="card-title">{{ restaurant.name }}</h5>
          <p class="card-text">
            {{ restaurant.description }}
          </p>
        </div>
        <ul class="list-group list-group-flush">
          <li class="list-group-item">Price: {{ restaurant.price }} Rs/Kg</li>
        </ul>
        <button class="btn1" (click)="addToCart(restaurant.price)">
          Add to Cart</button
        >{{ " " }}
        <button (click)="removeFromCart(restaurant.price)">-</button>
      </div>
    </div>
  </div>
</div>
HTML
<!-- navbar/navbar.component.html -->

<nav class="navbar navbar-expand-lg" style="background-color: green;">
    <div class="container" >
        <a class="navbar-brand" href="#"><img src={{title}}></a>
        Fruit And Vegetable Market using MEAN
        <div class="collapse navbar-collapse" id="navbarNav">
            <ul class="navbar-nav ms-auto">
            </ul>
        </div>
    </div>
</nav>
HTML
<!-- product-card/product-card.component.html -->

<div class="card">
    <img [src]="restaurant.image" 
    class="card-img-top" [alt]="restaurant.name">
    <div class="card-body">
        <h5 class="card-title">{{ restaurant.name }}</h5>
        <p class="card-text">
            Types of food we offer: {{ restaurant.cuisines.join(', ') }}
        </p>
    </div>
    <ul class="list-group list-group-flush">
        <li class="list-group-item">Address: {{ restaurant.address }}</li>
        <li class="list-group-item">City: {{ restaurant.location }}</li>
        <li class="list-group-item">Rating: {{ restaurant.rating }}</li>
    </ul>
</div>
HTML
<!-- index.html -->

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>ResturentApp</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <!-- Latest compiled and minified CSS -->
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://unpkg.com/[email protected]/components/blogs/blog-2/assets/css/blog-2.css">
</head>
<body>
  <app-root></app-root>
</body>
</html>
CSS
/* app.component.css */

.headers {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-left: 5vw;
    margin-right: 5vw;
    margin-top: 3vh;
    padding-bottom: 2vh;
    border-bottom: 1px solid gray;
}

.location-container {
    width: 10vw;
}

.cuisines-container {
    margin-top: 5vh;
    width: 15vw;
    height: 10vh;
    overflow-y: scroll;
}

.cuisine-checkbox {
    display: flex;
    align-items: center;
    margin-bottom: 0.5rem;
}

.cuisine-checkbox input {
    margin-right: 0.5rem;
}

.rating-container {
    width: 10vw;
}

.restaurants {
    padding-bottom: 3vh;
}

.restaurants h3 {
    margin-left: 5vw;
    margin-top: 1vh;
    margin-bottom: 1vh;
}

.restaurant-cards {
    display: flex;
    flex-wrap: wrap;
    margin-left: 5vw;
}

.restaurant-card {
    width: 18rem;
    margin-left: 4vw;
    margin-top: 4vh;
}

.restaurant-card img {
    height: 20vh;
    background-size: cover;
}
.btn1{
    margin: 10px;
}
.cart{
     position: fixed;
    top: 250px;
    right: 10px;
    width: 200px;
    border: 2px solid #4CAF50;
    border-radius: 10px;
    height: 20vh;
    background-color: #fff;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    overflow-y: auto;
    z-index: 1000;
}
.cart-content{
     padding: 16px;
}
CSS
/* You can add global styles to this file, and also import other style files */
/* styles.css */
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap');

body {
    font-family: 'Poppins', sans-serif;
    background-color: #f5f5f5;
    color: #333;
    margin: 0;
    padding: 0;
}

.container {
    max-width: 1600px;
    margin: 0 auto;
    padding: 1rem;
}

.navbar {
    font-size: 25px;
    background-color: #333;
    color: white;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.navbar-brand {
    font-size: 1.5rem;
    font-weight: 700;
    text-decoration: none;
}

.filters {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 2rem;
    padding-bottom: 2rem;
    border-bottom: 1px solid #ddd;
}

.location-container,
.rating-container {
    width: 15%;
}

.cuisines-container {
    width: 30%;
    max-height: 200px;
    overflow-y: auto;
}

.cuisine-checkbox {
    display: flex;
    align-items: center;
    margin-bottom: 0.5rem;
}

.cuisine-checkbox input {
    margin-right: 0.5rem;
}

.restaurants {
    margin-top: 2rem;
}

.restaurants h3 {
    font-size: 1.75rem;
    font-weight: 600;
    margin-bottom: 1.5rem;
}

.restaurant-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    grid-gap: 2rem;
}

.restaurant-card {
    background-color: #fff;
    border-radius: 0.5rem;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
    overflow: hidden;
    transition: transform 0.3s ease-in-out;
}

.restaurant-card:hover {
    transform: translateY(-5px);
    box-shadow: 0 8px 12px rgba(0, 0, 0, 0.15);
}

.restaurant-card img {
    height: 100%;
    width: 90%;
    /* object-fit: contain; */
    /* object-position: center; */
    margin-left: 15px;
}


.card-body {
    padding: 1.5rem;
}

.card-title {
    font-size: 1.25rem;
    font-weight: 600;
    margin-bottom: 0.5rem;
}

.card-text {
    font-size: 0.9rem;
    color: #666;
    margin-bottom: 1rem;
}

.list-group-item {
    font-size: 0.9rem;
    padding: 0.75rem 1.5rem;
    border-bottom: 1px solid #f1f1f1;
}

.list-group-item:last-child {
    border-bottom: none;
}
JavaScript
// appConfig.config.js

import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [provideRouter(routes)]
};
JavaScript
// app.component.ts

import { NgModule } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { NavbarComponent }
  from './components/navbar/navbar.component';
import { ResturentCardComponent }
  from './components/product-card/product-card.component';

import { FormsModule } from '@angular/forms';
import { Router } from '@angular/router';

  
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  imports: [
    NavbarComponent,
    ResturentCardComponent,
    ReactiveFormsModule,
    CommonModule,
    FormsModule,
    
  ],
  standalone: true,
})
export class AppComponent implements OnInit {
  title = 'Restro - find your restaurant';
  rating = new FormControl('');
  restaurants: any[] = [];
  filteredRestaurants: any[] = [];
  searchResults: string[] = [];
  searchText: string = '';
  cartTotal: number = 0;
  minPrice = new FormControl('');
  maxPrice = new FormControl('');
  minFilterPrice: number | null = null;
  maxFilterPrice: number | null = null;

  constructor(private http: HttpClient, private router: Router) { }
  

  ngOnInit() {
    this.getRestaurants();
  }

  getRestaurants() {
    this.http.get('http://localhost:4000/api/products').subscribe(
      (response: any) => {
        if (response.success) {
          this.restaurants = response.data;
          this.filterRestaurants();
        }
      },
      (error) => {
        console.log('Error:', error);
      }
    );
  }

  filterRestaurants() {
    this.filteredRestaurants = this.restaurants.filter((restaurant) => {
      let passType = true;
      if (this.rating.value === 'Fruit') {
        passType = restaurant.type === 'Fruit';
      } else if (this.rating.value === 'Vegetable') {
        passType = restaurant.type === 'Vegetable';
      }

      let passPriceRange = true;
      if (this.minFilterPrice !== null) {
        passPriceRange = restaurant.price >= this.minFilterPrice;
      }
      if (this.maxFilterPrice !== null) {
        passPriceRange = passPriceRange && restaurant.price <= this.maxFilterPrice;
      }

      return passType && passPriceRange;
    });
  }
  
  filterByRange() {
    const minValue = this.minPrice.value;
    const maxValue = this.maxPrice.value;
    if (minValue !== null && maxValue !== null) {
      const minPrice = +minValue;
      const maxPrice = +maxValue;

      if (!isNaN(minPrice) && !isNaN(maxPrice) && maxPrice >= minPrice) {
        this.minFilterPrice = minPrice;
        this.maxFilterPrice = maxPrice;
        this.filterRestaurants();
      } else {
        alert("Invalid price range");
      }
    }
  }

  applySearch(): void {
    this.filteredRestaurants = this.restaurants.filter(restaurant =>
      restaurant.name.toLowerCase().includes(this.searchText.toLowerCase())
    );
  }

  addToCart(price: number): void {
    this.cartTotal += price;
  }

  removeFromCart(price: number): void {
    if (this.cartTotal > 0) {
      this.cartTotal -= price;
    } else if(this.cartTotal === 0) {
      // Optionally handle negative total or alert user
      alert("No Item")
      return;
    }
  }
  sortByPrice() {
    this.filteredRestaurants.sort((a, b) => a.price - b.price);
  }
  
}
JavaScript
// product-card/product-card.component.ts

import { Component, Input } from '@angular/core';

@Component({
    selector: 'app-resturent-card',
    templateUrl: './product-card.component.html',
    standalone: true
})
export class ResturentCardComponent {
    @Input() restaurant: any;
}
JavaScript
// navbar/navbar.component.ts

import { Component } from '@angular/core';

@Component({
    selector: 'app-navbar',
    templateUrl: './navbar.component.html',
    standalone: true
})
export class NavbarComponent {
    title = 'https://media.geeksforgeeks.org/gfg-gg-logo.svg';
}
JavaScript
// main.ts

import { bootstrapApplication } from '@angular/platform-browser';
import { importProvidersFrom } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app/app.component';

bootstrapApplication(AppComponent, {
    providers: [
        importProvidersFrom(HttpClientModule)
    ]
})
    .catch(err => console.error(err));

Start the Angular App using the following command.

ng serve

Output:

ResturentApp-Brave2024-05-1514-24-09-ezgifcom-optimize

Final Project Output




Reffered: https://www.geeksforgeeks.org


AngularJS

Related
How to Implement Angular Forms? How to Implement Angular Forms?
Explain the Role of @Component Decorator in Angular Explain the Role of @Component Decorator in Angular
How to Use Swiper Element with Angular 17? How to Use Swiper Element with Angular 17?
Pass Data Between Siblings in Angular Pass Data Between Siblings in Angular
How to Navigate Fragment URL in Angular 15? How to Navigate Fragment URL in Angular 15?

Type:
Geek
Category:
Coding
Sub Category:
Tutorial
Uploaded by:
Admin
Views:
14