Open In App

Fundraiser Platform using MEAN

Last Updated : 05 May, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Creating a fundraiser platform using the MEAN stack (MongoDB, Express.js, Angular, and Node.js) provides a powerful combination of technologies for building dynamic and scalable web applications. In this article, we will see a step-wise process of creating the application.

Project Preview:

imresizer-1713972739121

output preview

Approach to build a fundraiser application using MEAN

  • Set Up Backend with Express, Node, and Mongoose.
  • Install Express.js, Node.js, and Mongoose to create a backend server.
  • Initialize an Express application and configure routes to handle HTTP requests.
  • Connect to MongoDB Database.
  • Set up a connection to MongoDB using Mongoose and define Mongoose schemas to structure and model the data for the application.
  • Implement API Endpoints and Define routes to handle requests for creating, reading, updating, and deleting data.
  • Use Angular CLI to generate a new Angular project.
  • Navigate to the project directory and install required dependencies.
  • Set Up Angular Components and Services.
  • Create Angular components to define the user interface and structure of the application.
  • Develop Angular services to encapsulate business logic and interact with backend APIs.

Steps to Create Server

Step 1: Create a new directory named backend.

mkdir backend
cd backend

Step 2: Create a server using the following command in your terminal.

npm init -y

Step 3: Install the necessary package in your server using the following command.

npm install express mongoose cors

Dependencies:

 "dependencies": {
"cors": "^2.8.5",
"express": "^4.19.2",
"mongoose": "^8.3.2"
}

Folder Structure:

Screenshot-2024-04-24-191515

project structure

Step 5: Create a file ‘index.js’ and set up the server.

JavaScript
//index.js

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

const app = express();
const PORT = process.env.PORT || 5000;
app.use(cors());

// Connect to MongoDB
mongoose.connect("<MONGODB_URL>", {
    useNewUrlParser: true,
    useUnifiedTopology: true,
});

// Define a Donation schema
const donationSchema = new mongoose.Schema({
    name: { type: String, required: true },
    amount: { type: Number, required: true },
    description: { type: String, required: true },
});

const Donation = mongoose.model("Donation", donationSchema);

// Define a Total Amount schema
const totalAmountSchema = new mongoose.Schema({
    total: { type: Number, required: true },
    current: { type: Number, required: true },
});

const TotalAmount = mongoose.model("TotalAmount", totalAmountSchema);

// GET route to fetch total amount
app.get("/api/totalAmount", async (req, res) => {
    try {
        // Fetch total amount
        const totalAmount = await TotalAmount.findOne();
        res.json(totalAmount);
    } catch (error) {
        console.error(error);
        res.status(500).send("Internal Server Error");
    }
});

// PUT route to update current amount
app.put("/api/totalAmount", async (req, res) => {
    try {
        const { current } = req.body;

        // Update current amount
        const totalAmount = await TotalAmount.findOne();
        totalAmount.current = current;
        await totalAmount.save();

        res.status(200).send("Current amount updated successfully");
    } catch (error) {
        console.error(error);
        res.status(500).send("Internal Server Error");
    }
});

// API routes for donations
app.post("/api/donations", async (req, res) => {
    try {
        const { name, amount, description } = req.body;

        // Create a new donation
        const newDonation = new Donation({ name, amount, description });
        await newDonation.save();

        // Update the current total amount
        const totalAmount = await TotalAmount.findOne();
        totalAmount.current += amount;
        await totalAmount.save();

        res.status(201).json(newDonation);
    } catch (error) {
        console.error(error);
        res.status(500).send("Internal Server Error");
    }
});

app.get("/api/donations", async (req, res) => {
    try {
        // Fetch all donations
        const donations = await Donation.find();
        res.json(donations);
    } catch (error) {
        console.error(error);
        res.status(500).send("Internal Server Error");
    }
});

// Start the server
app.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`);
});

Start your server using the following command.

node index.js

Steps to Create the Frontend

Step 1: Setup Angular Project

Make sure you have Angular CLI installed. If not, you can install it globally using npm:

npm install -g @angular/cli

Step 2: Set up the repository

ng new fundraiser-app
cd fundraiser-app

Step 3: Install Angular Material

ng add @angular/material

Dependencies:

  "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"
},

Project Structure:

Screenshot-2024-04-24-193954

project structure

Example: Create the files according to the project structure and write the following code in respective files.

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

<div class="App">
  <header>
    <div class="navbar">
      <img
        src="https://media.geeksforgeeks.org/gfg-gg-logo.svg"
        alt="GeeksforGeeks Logo"
      />
      <h1>Fund raiser</h1>
    </div>
  </header>

  <!-- Donation Form -->
  <div class="app-container">
    <div class="donation-form">
      <label>
        Name:
        <input type="text" [(ngModel)]="name" />
      </label>
      <br />
      <label>
        Amount:
        <input type="number" [(ngModel)]="amount" />
      </label>
      <br />
      <label>
        Description:
        <input type="text" [(ngModel)]="description" />
      </label>
      <br />
      <button (click)="handleDonate()">Donate</button>
    </div>
  </div>

  <!-- Donation Statistics -->
  <div>
    <h2>Donation Statistics:</h2>
    <div class="div_stats" id="current">
      <p>Current Amount: {{ currentTotal }}</p>
    </div>
    <div class="div_stats" id="total">
      <p>Amount Required: {{ totalAmount?.total }}</p>
    </div>
  </div>

  <!-- Donations Table -->
  <div>
    <h2>Donations:</h2>
    <table>
      <thead>
        <tr>
          <th>Name</th>
          <th>Amount</th>
          <th>Description</th>
        </tr>
      </thead>
      <tbody>
        <tr *ngFor="let donation of donations">
          <td>{{ donation.name }}</td>
          <td>{{ donation.amount }}</td>
          <td>{{ donation.description }}</td>
        </tr>
      </tbody>
    </table>
  </div>
</div>
CSS
/* app.component.css */

body {
    font-family: Arial, sans-serif;
    margin: 0;
    padding: 0;
    background-color: #f4f4f4;
}

header {
    background-color: #333;
    color: #fff;
    text-align: center;
    padding: 1em 0;
}

section {
    max-width: 600px;
    margin: 2em auto;
    background-color: #fff;
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

#totalAmount {
    font-size: 24px;
    margin-bottom: 20px;
}

form {
    display: flex;
    flex-direction: column;
}

label {
    margin-bottom: 8px;
}

input {
    padding: 10px;
    margin-bottom: 16px;
    border: 1px solid #ccc;
    border-radius: 4px;
}

button {
    background-color: #4caf50;
    color: #fff;
    padding: 10px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}

/* General styling */
table {
    width: 100%;
    border-collapse: collapse;
    margin-top: 20px;
}

/* Table header */
th {
    background-color: #4caf50;
    color: white;
    padding: 12px 15px;
    text-align: left;
}

/* Table rows */
tr {
    border-bottom: 1px solid #ddd;
}


tr:nth-child(even) {
    background-color: #f2f2f2;
}

td {
    padding: 12px 15px;
    text-align: left;
}


tr:hover {
    background-color: #e0f7fa;
    transition: background-color 0.3s ease;
}

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

.donation-form input {
    width: 100%;
    padding: 10px;
    margin-bottom: 16px;
    border: 1px solid #ccc;
    border-radius: 4px;
}

.div_stats {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px;
    margin-bottom: 10px;
    border: 1px solid #ccc;
    border-radius: 5px;
    background-color: #f9f9f9;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}


.div_stats p {
    margin: 0;
    font-weight: bold;
    font-size: 16px;
}


#current {
    border-color: #4caf50;
}

#total {
    border-color: #f44336;
}

header {
    background-color: #333;
    color: #fff;
    text-align: center;
}

.navbar {
    display: flex;
    align-items: center;
    background-color: #333;
    color: #fff;
    height: 30px;
    margin-bottom: 10px;
}

.navbar img {
    width: 70px;
    height: 80px;
    margin-right: 10px;
}

.app-container {
    max-width: 800px;
    margin: 0 auto;
    padding: 20px;
}
JavaScript
// app.component.spec.ts

import { TestBed } from "@angular/core/testing";
import { AppComponent } from "./app.component";

describe("AppComponent", () => {
    beforeEach(async () => {
        await TestBed.configureTestingModule({
            imports: [AppComponent],
        }).compileComponents();
    });

    it("should create the app", () => {
        const fixture = TestBed.createComponent(AppComponent);
        const app = fixture.componentInstance;
        expect(app).toBeTruthy();
    });

    it(`should have the 'fundraiser-app' title`, () => {
        const fixture = TestBed.createComponent(AppComponent);
        const app = fixture.componentInstance;
        expect(app.title).toEqual("fundraiser-app");
    });

    it("should render title", () => {
        const fixture = TestBed.createComponent(AppComponent);
        fixture.detectChanges();
        const compiled = fixture.nativeElement as HTMLElement;
        expect(compiled.querySelector("h1")?.textContent).toContain(
            "Hello, fundraiser-app"
        );
    });
});
JavaScript
// app.component.ts

import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';

interface Donation {
    name: string;
    amount: number;
    description: string;
}

interface TotalAmount {
    total: number;
    current: number;
}

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
    donations: Donation[] = [];
    amount: number = 0;
    description: string = '';
    name: string = '';
    currentTotal: number = 0;
    totalAmount: TotalAmount | undefined;

    constructor(private http: HttpClient) { }

    ngOnInit(): void {
        this.fetchDonations();
        this.fetchTotalAmount();
    }

    fetchDonations(): void {
        this.http.get<Donation[]>('http://localhost:5000/api/donations')
            .subscribe(donations => {
                this.donations = donations;
            });
    }

    fetchTotalAmount(): void {
        this.http.get<TotalAmount>('http://localhost:5000/api/totalAmount')
            .subscribe(
                totalAmount => {
                    this.totalAmount = totalAmount;
                    this.currentTotal = totalAmount.current;
                },
                error => {
                    console.error('Error fetching total amount:', error);
                }
            );
    }

    handleDonate(): void {
        const newAmount = this.amount;
        this.http.post<Donation>('http://localhost:5000/api/donations', {
            amount: newAmount,
            description: this.description,
            name: this.name
        })
            .subscribe(
                response => {
                    this.donations.push(response);
                    this.amount = 0;
                    this.description = '';
                    this.name = '';
                    this.currentTotal += newAmount;
                },
                error => {
                    console.error('Error donating:', error);
                }
            );
    }
}
JavaScript
//app.config.ts

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

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

export const appConfig: ApplicationConfig = {
    providers: [provideRouter(routes)]
};
JavaScript
//app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';


@NgModule({
    declarations: [
        AppComponent,

    ],
    imports: [
        BrowserModule,
        HttpClientModule,
        FormsModule
    ],
    providers: [],
    bootstrap: [AppComponent]
})
export class AppModule { }
JavaScript
//main.ts

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';

// You can uncomment this line if you want to enable production mode
// enableProdMode();

platformBrowserDynamic().bootstrapModule(AppModule)
    .catch(err => console.error(err));

Step 4: Start the app using the following command.

ng serve

Output:

Recording-2024-04-24-210014-(1)

output



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads