Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 31

“VISVESVARAYA TECHNOLOGICAL UNIVERSITY,

BELAGAVI”

B.L.D.E.A's V.P. DR. P.G. HALAKATTI COLLEGE OF


ENGINEERINGAND TECHNOLOGY, VIJAYAPUR -- 586103

BACHELOR OF ENGINEERING IN COMPUTER SCIENCE &


ENGINEERING

MOBILE APPLICATION DEVELOPMENT (18CSMP68)

MINI PROJECT REPORT ON:


“EXPENSE TRACKING SYSTEM”

UNDER THE GUIDANCE OF


Prof Pavan Mahendrakar
Prof Siddhram Patil

SUBMITTED BY:
Nikita Bhantanur (2BL20CS051)
B.L.D.E.A.’s V.P. Dr. P.G. HALAKATTI COLLEGE OF ENGINEERING
AND TECHNOLOGY, VIJAYAPURA – 586 103
DEPARTMENT OF COMPUTER SCIENCE AND ENGINEERING

CERTIFICATE

This is to certify that mini project work of Mobile Application Development


(18CSMP68) entitled “Expense Tracking System” is a bonafide work carried
out in the sixth semester by Nikita Bhantanur [2BL20CS051] in partial
fulfilment for the award of Bachelor of Engineering in Computer Science and
Engineering from B.L.D.E. A’s V. P. Dr. P. G. Halakatti College of
Engineering & Technology during the academic year 2022-20223

GUIDE HOD PRINCIPAL

Prof Pavan M Dr. Pushpa B. Patil Dr. V.G. Sangam


Prof Siddhram P

Examiner 1:
Examiner 2:

2
ACKNOWLEDGEMENT

Mobile Application Development mini project requires guidance, hard work and co-
ordination. It gives great pleasure to acknowledge with thanks to the assistance and
contribution of many individuals who had been actively involved at various stages of our
project.

I would also like to express my heartfelt gratitude to our beloved Principal Dr. V. G. Sangam
and Dr. Pushpa. B. Patil, HOD, Computer Science and Engineering Sir BLDEA’s CET
whose guidance and support was truly invaluable.

I express our gratitude to our guides Prof. Pavan Mahendrakar and Prof. Siddhram Patil ,
Asst. Prof., BLDEA’s CET for their valuable guidance, instance motivation and constant
supervision at all places of study for making this a success.

I’m extremely thankful to our parents and friends for their unfailing enthusiasm, moral
boosting and encouragement for us in completion of this.

Finally, a vote of thanks to the Department of Computer Science Engineering, both teaching
and non-teaching staff for their co-operation extended.

3
CONTENTS

Chapter No. Name Page No.

1. INTRODUCTION

2. DESIGN

3. IMPLEMENTATION

4. EXPERIMENT RESULTS

5. CONCLUSION

REFERENCES

4
CHAPTER 1: INTRODUCTION
Managing personal finances efficiently is essential in today's fast-paced world. Mobile
applications have revolutionized expense tracking, making it convenient and accessible. This
project focuses on developing an Android Expense Tracker System that empowers users to
take control of their financial management.
The Android Expense Tracker System leverages cutting-edge technologies, including data
encryption and secure cloud storage, to ensure the confidentiality and integrity of users'
financial information. Furthermore, the application will be designed with an intuitive and
visually appealing user interface, making it accessible to a wide range of users.
This app will help the user to keep track of their expenses. Getting your finances in
order begins with tracking your spending. You can reduce your expenses by understanding
what you spend money on and how much you spend. This report will outline the project's
objectives, methodologies, development process, challenges encountered, and the final
implemented solution, highlighting its functionality, usability, and potential for future
enhancements.

In today's fast-paced world, managing personal finances has become

increasingly complex. As individuals juggle multiple sources of income, diverse

expenses, and ever-changing financial goals, it becomes crucial to have an

efficient system in place to track and analyze expenses. The Expense Tracker

System, presented in this project report, aims to address this pressing need by

providing users with a powerful tool to monitor their expenses, gain insights into

their spending patterns, and make informed financial decisions.

The Expense Tracker System is designed to offer a user-friendly and intuitive

interface, making it accessible to individuals of all financial backgrounds. It

leverages advanced technologies and data analysis techniques to provide

accurate and real-time tracking of personal expenses. By capturing and

categorizing financial transactions, the system enables users to visualize their

spending habits, set budgets, and identify areas where they can optimize their

financial management.

The primary objectives of this project are as follows:

5
To develop a robust and secure expense tracking system that can handle a
large volume of transactions while ensuring data privacy and
confidentiality.
To provide users with a comprehensive set of features to track expenses,
categorize transactions, and generate insightful reports.
To empower users to make informed financial decisions by visualizing their
spending patterns, setting budgets, and identifying opportunities for
savings.
To create a scalable and adaptable system architecture that can
accommodate future enhancements and integrations with other
financial tools.

This project report outlines the methodology, design considerations,

implementation details, and evaluation of the Expense Tracker System. It also

presents a detailed analysis of the system's performance, user feedback, and

potential areas for further improvement.

The Expense Tracker System aims to revolutionize personal finance management

by providing individuals with a powerful tool to take control of their financial well-

being. By facilitating the tracking and analysis of expenses, this system

empowers users to make informed decisions, save money, and achieve their

financial goals.

6
CHAPTER 2: DESIGN
There is a dashboard screen. The dashboard screen will show the user their income and their
expenses. There is an add button on the screen that allows users to add expenses and their
income. The income button is to add what you have earned. The expense button is to add
what you have spent.
There is an income tab. This tab shows users all their incomes. In this tab, users can update
their income and even add categories and a note with it.
There is an expense tab. This tab shows users all their expenses. In this tab, users can update
their expenses and add categories and a note.

FEATURES OF THE EXPENSE TRACKER APP

▪ There are three tabs on the main screen.

▪ The dashboard screen shows all their spending and income.

▪ The add button allows users to add new income or spending.

▪ They can even add categories and even a note with it.

▪ The income tab shows all the income of the users and updates it.

▪ They can also see the graph of their income. This graph is a pie chart that shows all
category-wise income of the user.
▪ The expense tab shows all the expenses of the users. You can also update it.

7
▪ They can also see the graph of their expenses. This graph is a pie chart which shows
all category-wise spending to the user.

CHAPTER 3: IMPLEMENTATION

This application includes multiple units to implement different features. The following
describes the implementation of each feature
▪ fragment_dashboard.xml is the file that will contain all the design codes of our
dashboard activity.
▪ The next file is the dashboard.java activity where we will be writing all the logic of
our application.
▪ The next file is income.xml and expense.xml which will contain all the design codes
of our income and expense activity.
▪ The next file is income.java and expense.java which we will be writing all the logic of
income and expense fragments in our application.
▪ We will also create a databaseHandler.java which will handle all the database
activities.

Fragment_dashboard.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/back_color"
tools:context=".fragments.Dashboard">

<TextView
android:id="@+id/tv_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

8
android:layout_marginLeft="30dp"
android:layout_marginTop="20dp"
android:text="Expense Tracker"
android:textColor="@color/text_color"
android:textSize="32sp" />

<TextView
android:id="@+id/tv_text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_text"
android:layout_marginLeft="30dp"
android:layout_marginTop="15dp"
android:layout_marginBottom="20dp"
android:text="Dashboard"
android:textColor="@color/text_color"
android:textSize="28sp" />

<LinearLayout
android:id="@+id/rl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/tv_text2"
android:background="@color/back_color"
android:orientation="horizontal"
android:weightSum="2">

<LinearLayout
android:layout_width="0dp"

9
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/bungee_inline"
android:text="Income"
android:textColor="@color/text_color"
android:textSize="24sp" />

<TextView
android:id="@+id/tv_income"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Rs. 10000.00"
android:textColor="@color/text_color"
android:textSize="24sp" />

</LinearLayout>

<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">

10
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/bungee_inline"
android:text="Expense"
android:textColor="@color/text_color"
android:textSize="24sp" />

<TextView
android:id="@+id/tv_expense"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Rs. 10000.00"
android:textColor="@color/text_color"
android:textSize="24sp" />

</LinearLayout>

</LinearLayout>

<TextView
android:id="@+id/tvInc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/rl"
android:layout_marginLeft="10dp"
android:layout_marginTop="30dp"
android:text="Income:"

11
android:textColor="@color/text_color"
android:textSize="24sp" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_income"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tvInc"
android:layout_marginTop="8dp"
android:scrollbars="horizontal"
tools:listitem="@layout/layout_income_item" />

<TextView
android:id="@+id/tvExp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/rv_income"
android:layout_marginLeft="10dp"
android:layout_marginTop="30dp"
android:text="Expense:"
android:textColor="@color/text_color"
android:textSize="24sp" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/total_amt"
android:layout_below="@id/rv_expense"
android:layout_marginLeft="10dp"

12
android:layout_marginTop="30dp"
android:text="Total Amount:"
android:textColor="@color/text_color"
android:textSize="24sp" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/total_amt_cal"
android:layout_below="@id/total_amt"
android:layout_marginLeft="10dp"
android:layout_marginTop="30dp"

android:textColor="@color/text_color"
android:textSize="24sp" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_expense"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tvExp"
android:layout_marginTop="8dp"
android:scrollbars="horizontal"
tools:listitem="@layout/layout_expense_item" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/add_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

13
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:backgroundTint="@color/text_color"
android:src="@drawable/add"
app:fabSize="normal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:tint="@color/background" />

<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/add_expense_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/add_income_fab"
android:layout_alignParentEnd="true"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:backgroundTint="@color/Button"
app:fabSize="normal"
app:srcCompat="@drawable/minus"
app:tint="@color/textColor" />

<TextView
android:id="@+id/add_expense_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/add_income_fab"

14
android:layout_marginEnd="16dp"
android:layout_marginBottom="34dp"
android:layout_toLeftOf="@id/add_expense_fab"
android:text="Add Expense"
android:textColor="@color/text_color"
android:textSize="16sp" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/add_income_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/add_fab"
android:layout_alignParentEnd="true"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:backgroundTint="@color/Button"
app:fabSize="normal"
app:srcCompat="@drawable/add"
app:tint="@color/textColor" />

<TextView
android:id="@+id/add_income_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/add_fab"
android:layout_marginEnd="16dp"
android:layout_marginBottom="34dp"
android:layout_toLeftOf="@id/add_expense_fab"
android:text="Add Income"
android:textColor="@color/text_color"

15
android:textSize="16sp" />

</RelativeLayout>

dashboard.java
package com.example.expensetrackersystem.fragments;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.SpinnerAdapter;
import android.widget.TextView;

import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.example.expensetrackersystem.DatabaseHandler;
import com.example.expensetrackersystem.DatabaseHandlerExpense;
import com.example.expensetrackersystem.R;
import com.example.expensetrackersystem.adapter.expenseAdapter;
import com.example.expensetrackersystem.adapter.incomeAdapter;
import com.example.expensetrackersystem.model.expenseModel;
import com.example.expensetrackersystem.model.incomeModel;

16
import com.google.android.material.floatingactionbutton.FloatingActionButton;

import java.util.ArrayList;
import java.util.List;

/**
* A simple {@link Fragment} subclass.
* Use the {@link Dashboard#newInstance} factory method to
* create an instance of this fragment.
*/
public class Dashboard extends Fragment {

// TODO: Rename parameter arguments, choose names that match


// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";

// TODO: Rename and change types of parameters


private String mParam1;
private String mParam2;

public Dashboard() {
// Required empty public constructor
}

/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*

17
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment Dashboard.
*/
// TODO: Rename and change types and number of parameters
public static Dashboard newInstance(String param1, String param2) {
Dashboard fragment = new Dashboard();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}

private RecyclerView rv_income, rv_expense;


private TextView tv_income, tv_expense, total_rem;

FloatingActionButton mAddFab, mAddIncomeFab, mAddExpenseFab;


TextView addIncomeText, addExpenseText;
Boolean isAllFabsVisible;

18
private incomeAdapter incomeAdapter;
private expenseAdapter expenseAdapter;

private List<incomeModel> incomeModelList = new ArrayList<>();


private List<expenseModel> expenseModelList = new ArrayList<>();

private DatabaseHandler databaseHandler;


private DatabaseHandlerExpense databaseHandlerExpense;

private String totalIncome, totalExpense;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_dashboard, container, false);

init(view);

databaseHandler = new DatabaseHandler(getContext());


databaseHandlerExpense = new DatabaseHandlerExpense(getContext());
fillIncomeModel();
fillExpenseModel();
fillTotalModel();

mAddFab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {

19
if (!isAllFabsVisible) {
mAddIncomeFab.show();
mAddExpenseFab.show();
addExpenseText.setVisibility(View.VISIBLE);
addIncomeText.setVisibility(View.VISIBLE);

isAllFabsVisible = true;
} else {

mAddIncomeFab.hide();
mAddExpenseFab.hide();
addExpenseText.setVisibility(View.GONE);
addIncomeText.setVisibility(View.GONE);

isAllFabsVisible = false;
}
}
});

mAddIncomeFab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
showIncomeDialog();
}
});

mAddExpenseFab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {

20
showExpenseDialog();
}
});

return view;
}

private void fillExpenseModel() {


expenseModelList = databaseHandlerExpense.getAllIncome();

int total = 0;
for (expenseModel model : expenseModelList) {
total += Integer.parseInt(model.getAmount());
}
totalExpense = String.valueOf(total);
tv_expense.setText("₹" + totalExpense);

expenseAdapter = new expenseAdapter(getContext(), expenseModelList);


rv_expense.setLayoutManager(new LinearLayoutManager(getContext(),
LinearLayoutManager.HORIZONTAL, false));
rv_expense.setHasFixedSize(true);

rv_expense.setAdapter(expenseAdapter);
}

private void fillIncomeModel() {


incomeModelList = databaseHandler.getAllIncome();

int total = 0;
21
for (incomeModel model : incomeModelList) {
total += Integer.parseInt(model.getAmount());
}
totalIncome = String.valueOf(total);
tv_income.setText("₹" + totalIncome);

incomeAdapter = new incomeAdapter(getContext(), incomeModelList);


rv_income.setLayoutManager(new LinearLayoutManager(getContext(),
LinearLayoutManager.HORIZONTAL, false));
rv_income.setHasFixedSize(true);

rv_income.setAdapter(incomeAdapter);

private void fillTotalModel(){


incomeModelList = databaseHandler.getAllIncome();
expenseModelList = databaseHandlerExpense.getAllIncome();
int in=0,ex=0;
for (incomeModel model : incomeModelList) {
in += Integer.parseInt(model.getAmount());
}
for (expenseModel model : expenseModelList) {
ex+= Integer.parseInt(model.getAmount());
}
int total=in-ex;
total_rem.setText("₹" + String.valueOf(total));
}

private void showIncomeDialog() {


22
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());

final View customLayout = getLayoutInflater().inflate(R.layout.income_add_litem,


null);
EditText et_income = customLayout.findViewById(R.id.et_incomeAmount);
Spinner spinner_incomeType =
customLayout.findViewById(R.id.spinner_incomeType);
EditText et_note = customLayout.findViewById(R.id.et_incomeNote);

Button btn_save = customLayout.findViewById(R.id.btn_save);


Button btn_cancel = customLayout.findViewById(R.id.btn_cancel);

builder.setView(customLayout);
AlertDialog alertDialog = builder.create();

SpinnerAdapter incomeTypeAdapter = ArrayAdapter.createFromResource(getContext(),


R.array.income_types, android.R.layout.simple_spinner_item);
spinner_incomeType.setAdapter(incomeTypeAdapter);

alertDialog.show();

btn_cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
alertDialog.dismiss();
}
});

btn_save.setOnClickListener(new View.OnClickListener() {

23
@Override
public void onClick(View v) {
String amount = et_income.getText().toString();
String type = spinner_incomeType.getSelectedItem().toString();

String note = et_note.getText().toString();


long date = System.currentTimeMillis();

if (amount.isEmpty()) {
et_income.setError("Empty amount");
return;
} else if (note.isEmpty()) {
et_note.setError("Empty note");
return;
} else {
databaseHandler.addData(amount, type, note, String.valueOf(date));
alertDialog.dismiss();
fillIncomeModel();
fillTotalModel();
}

}
});

private void showExpenseDialog() {


AlertDialog.Builder builder = new AlertDialog.Builder(getContext());

24
final View customLayout = getLayoutInflater().inflate(R.layout.expense_add_item,
null);
EditText et_income = customLayout.findViewById(R.id.et_incomeAmount);
EditText et_type = customLayout.findViewById(R.id.et_incomeType);
EditText et_note = customLayout.findViewById(R.id.et_incomeNote);

Button btn_save = customLayout.findViewById(R.id.btn_save);


Button btn_cancel = customLayout.findViewById(R.id.btn_cancel);

builder.setView(customLayout);
AlertDialog alertDialog = builder.create();

alertDialog.show();

btn_cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
alertDialog.dismiss();
}
});

btn_save.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String amount = et_income.getText().toString();
String type = et_type.getText().toString();
String note = et_note.getText().toString();
long date = System.currentTimeMillis();

if (amount.isEmpty()) {
25
et_income.setError("Empty amount");
} else if (type.isEmpty()) {
et_type.setError("Empty Type");
} else if (note.isEmpty()) {
et_note.setError("Empty note");
} else {
databaseHandlerExpense.addData(amount, type, note, String.valueOf(date));
alertDialog.dismiss();
fillExpenseModel();
fillTotalModel();
}

/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment Dashboard.
*/

}
});

private void init(View root) {


rv_income = root.findViewById(R.id.rv_income);

26
rv_expense = root.findViewById(R.id.rv_expense);

tv_income = root.findViewById(R.id.tv_income);
tv_expense = root.findViewById(R.id.tv_expense);
total_rem= root.findViewById(R.id.total_amt_cal);

tv_income.setText("Rs. 11000");
tv_expense.setText("Rs. 8000");

mAddFab = root.findViewById(R.id.add_fab);
mAddIncomeFab = root.findViewById(R.id.add_income_fab);
mAddExpenseFab = root.findViewById(R.id.add_expense_fab);

addIncomeText = root.findViewById(R.id.add_income_text);
addExpenseText = root.findViewById(R.id.add_expense_text);

mAddIncomeFab.setVisibility(View.GONE);
mAddExpenseFab.setVisibility(View.GONE);
addIncomeText.setVisibility(View.GONE);
addExpenseText.setVisibility(View.GONE);

isAllFabsVisible = false;
}

CHAPTER 4: RESULTS

27
Fig 4.1Dashboard Fig 4.2 Income Fragment

Fig 4.3 Expense Fragment Fig 4.4 Report for Income

28
Fig 4.5 Report for expense Fig 4.6 Add Expense

Fig 4.7 Add income

29
CHAPTER 5: CONCLUSION

In conclusion, the development of the Expense Tracker System for Android has successfully
addressed the need for an efficient and user-friendly solution to manage personal finances.
Through rigorous analysis, design, and implementation processes, the project has achieved its
objectives of creating an intuitive expense-tracking application.
The system enables users to easily monitor their expenses, categorize transactions,
and generate insightful reports.
The implemented Expense Tracker System showcases promising results in terms of
functionality, usability, and potential for future enhancements. In conclusion, the Expense
Tracker System project has made significant contributions to personal financial management.
It provides individuals with a powerful tool to gain control over their expenses and make
informed financial decisions, ultimately promoting better financial well-being.
In the future, the existing system can be improved by incorporating other
useful features like log-in into accounts so that user can access their profile from any physical
device. We can also add a common graph that includes the comparison of income v/s
expenses. After the expenses, the income should update accordingly.

REFERENCES
▪ https://developer.android.com/

▪ https://www.tutorialspoint.com/index.htm

▪ https://www.youtube.com

▪ https://www.freecodecamp.org

30
31

You might also like