# Rich Web Applications
## Week one
### node package manager (npm)
get the latest **npm** version
```console
npm install -g npm@latest
```
### Next.js
create an app with latest **Next.js** version
```console
npx create-next-app@latest
```
run the Next.js development server
```console
npm run dev
```
## Week two
### Material UI
install **Material UI**
```console
npm install @mui/material @emotion/react @emotion/styled
```
login page code from
https://github.com/mui/material-ui/blob/v5.14.11/docs/data/material/getting-started/templates/sign-in/SignIn.js
fixed the errors by using a client component and installing Material UI Icons
> To use Client Components, you can add the React "use client" directive at the top of a file, above your imports.
>
> "use client" is used to declare a boundary between a Server and Client Component modules. This means that by defining a "use client" in a file, all other modules imported into it, including child components, are considered part of the client bundle.
https://nextjs.org/docs/app/building-your-application/rendering/client-components
```javascript
"use client"
```
install **Material UI Icons**
```console
npm install @mui/icons-material @mui/material @emotion/styled @emotion/react
```
### Docker
download **Docker Desktop** from
https://www.docker.com/products/docker-desktop/
add `compose.yaml` file to folder
```yaml=
# Use root/example as user/password credentials
version: '3.1'
services:
mongo:
image: mongo
restart: always
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: example
mongo-express:
image: mongo-express
restart: always
ports:
- 8081:8081
environment:
ME_CONFIG_MONGODB_ADMINUSERNAME: root
ME_CONFIG_MONGODB_ADMINPASSWORD: example
ME_CONFIG_MONGODB_URL: mongodb://root:example@mongo:27017/
```
create and start containers
```console
docker compose up
```
open Mongo Express
http://localhost:8081/
login credentials:
* username: admin
* password: pass
## Week three
create API routes
```
app/
└── api/
└── login/
└── route.js
```
### Data fetching
https://nextjs.org/docs/app/building-your-application/data-fetching
`fetch()`
```javascript
fetch(`http://localhost:3000/api/login?email=${email}&pass=${pass}`);
```
`route.js`
```javascript=
export async function GET(req,res) {
const email = req.nextUrl.searchParams.get('email');
const pass = req.nextUrl.searchParams.get('pass');
return Response.json({ "email": email, "pass": pass })
}
```
### Mongo DB
installing **mongodb**
```console
npm i mongodb
```
## Week four
### Mongo DB
create a database `app`
create a collection `login`
add two datasets to collection
```json
{
"_id": ObjectId(),
"username": "sample@test.com",
"pass": "12345",
"acctype": "customer"
}
```
```json
{
"_id": ObjectId(),
"username": "sample2@test.com",
"pass": "12345",
"acctype": "manager"
}
```
### Rerunning compose
add in a port definition in the `compose.yaml` file
The purpose of this is to map the docker mongodb port 27017 to the local machine 27017 so you can connect to it.
```diff=12
+ ports:
+ - 27017:27017
```
change `route.js` file in api/login/ folder to add Mongo DB connection.
```javascript=
export async function GET(req,res) {
const email = req.nextUrl.searchParams.get('email');
const pass = req.nextUrl.searchParams.get('pass');
const { MongoClient } = require('mongodb');
const url = 'mongodb://root:example@localhost:27017/';
const client = new MongoClient(url);
const dbName = 'app';
await client.connect();
console.log('Connected successfully to server');
const db = client.db(dbName);
const collection = db.collection('login');
const findResult = await collection.find({"username":
email}).toArray();
console.log('Found documents =>', findResult);
let valid = false
if(findResult.length > 0 ){
valid = findResult[0].pass == pass;
} else {
console.log(`No user with username: ${email} found.`);
}
// at the end of the process we need to send something back.
return Response.json({ "data": valid});
}
```
## Week five
### Customer dashboard to view products
#### Setting up the database
creating a new collection `products`
adding a product to collection
```json
{
"_id":ObjectId(),
"pname":"jam doughnut",
"price":"1 euro"
}
```
#### Creating the dashboard
create a new dashboard page
```
app/
└── dashboard/
└── page.js
```
add code to `page.js` (the following is the final code at the end of week 5)
```javascript
'use client';
import * as React from 'react';
import Avatar from '@mui/material/Avatar';
import Button from '@mui/material/Button';
import CssBaseline from '@mui/material/CssBaseline';
import TextField from '@mui/material/TextField';
import FormControlLabel from '@mui/material/FormControlLabel';
import Checkbox from '@mui/material/Checkbox';
import Link from '@mui/material/Link';
import Grid from '@mui/material/Grid';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import Container from '@mui/material/Container';
import { ThemeProvider } from '@mui/material/styles';
import { createTheme } from '@mui/material/styles';
import { green, purple } from '@mui/material/colors';
import { useState, useEffect } from 'react'
import { withIronSessionApiRoute } from "iron-session/next";
export default function Page() {
//
// function for putting items into the shopping cart.
//
function putInCart(pname) {
console.log("putting in cart: " + pname)
fetch("http://localhost:3000/api/putInCart?pname=" + pname);
}
const [data, setData] = useState(null)
useEffect(() => {
fetch('http://localhost:3000/api/getProducts')
.then((res) => res.json())
.then((data) => {
setData(data)
})
}, [])
if (!data) return <p>No data</p>
const theme = createTheme({
palette: {
secondary: {
main: green[500],
},
},
});
return (
<ThemeProvider theme={theme}>
<Container component="main" maxWidth="xs">
<div style={{ fontSize: '40px' }} > Dashboard</div>
<div>
{
data.map((item, i) => (
<div style={{ padding: '20px' }} key={i} >
Unique ID: {item._id}
<br></br>
{item.pname}
-
{item.price}
<br></br>
<Button onClick={() => putInCart(item.pname)} variant="outlined"> Add to cart
</Button>
</div>
))
}
</div>
</Container>
</ThemeProvider>
);
}
```
create a new API endpoint
```
app/
└── api/
└── getProducts/
└── route.js
```
add code to `route.js`
```javascript
export async function GET(req, res) {
// Make a note we are on
// the api. This goes to the console.
console.log("in the api page")
// =================================================
const { MongoClient } = require('mongodb');
const url = 'mongodb://root:example@localhost:27017/';
const client = new MongoClient(url);
const dbName = 'app'; // database name
await client.connect();
console.log('Connected successfully to server');
const db = client.db(dbName);
const collection = db.collection('shopping_cart'); // collection name
const findResult = await collection.find({}).toArray();
console.log('Found documents =>', findResult);
//==========================================================
// at the end of the process we need to send something back.
return Response.json(findResult)
}
```
### Adding a shopping cart
#### Creating a database collection
create a new collection `shopping_cart`
#### Creating the API page
create a new API endpoint
```
app/
└── api/
└── putInCart/
└── route.js
```
add code to `route.js`
```javascript
export async function GET(req, res) {
// Make a note we are on
// the api. This goes to the console.
console.log("in the putInCart api page")
// get the values
// that were sent across to us.
const { searchParams } = new URL(req.url)
const pname = searchParams.get('pname')
console.log(pname);
// =================================================
const { MongoClient } = require('mongodb');
const url = 'mongodb://root:example@localhost:27017/';
const client = new MongoClient(url);
const dbName = 'app'; // database name
await client.connect();
console.log('Connected successfully to server');
const db = client.db(dbName);
const collection = db.collection('shopping_cart'); // collection name
var myobj = { pname: pname, username: "sample@test.com" };
const insertResult = await collection.insertOne(myobj);
//==========================================================
// at the end of the process we need to send something back.
return Response.json({ "data": "" + "inserted" + "" })
}
```
#### Creating the Cart page
create a new page
```
app/
└── cart/
└── page.js
```
add `app/dashboard/page.js` code to `page.js`
change fetch method
```javascript
fetch('http://localhost:3000/api/getCartProducts')
```
create a new API endpoint
```
app/
└── api/
└── getCartProducts/
└── route.js
```
add `app/api/getProducts/route.js` code to `route.js`
change collection name
```javascript
const collection = db.collection('shopping_cart');
```
## Week six
### Working with the register page
#### Creating the register page
create a new page
```
app/
└── register/
└── page.js
```
add `app/login/page.js` code to `page.js`, adjust it for the register page.
add a Date of Birth field to `page.js`
```javascript
<TextField
margin="normal"
required
fullWidth
id="dob"
label="Date of Birth"
name="dob"
autoComplete=""
autoFocus
/>
```
add a variable for the date of birth field to `handleSubmit` function
add a query parameter for the date of birth field
```javascript
const dob = data.get('dob');
runDBCallAsync(`http://localhost:3000/api/register?email=${email}&pass=${password}&dob=${dob}`);
```
create a new API endpoint
```
app/
└── api/
└── register/
└── route.js
```
add `app/api/register/route.js` code to `route.js`
add a variable for the date of birth field
```javascript
const dob = req.nextUrl.searchParams.get('dob');
```
On the login page, a database query was executed to find an entry; on the register page, a database query is to be executed to add an entry.
```javascript
const findResult = await collection.insertOne({"username": email, "pass": pass, "dob": dob});
```
## Week seven
### Integrating the weather API
Register to https://www.weatherapi.com/
#### Exploring the API documentation
#### Integrating into your application
create a new API endpoint
```
app/
└── api/
└── getWeather/
└── route.js
```
add code to `route.js`
```javascript
export async function GET(req, res) {
// Make a note we are on
// the api. This goes to the console.
console.log("in the weather api page")
const res2 = await fetch("")
const data = await res2.json()
console.log(data.current.temp_c)
let currentTemp = data.current.temp_c
// at the end of the process we need to send something back.
return Response.json({"temp": currentTemp})
}
```
add code to `dashboard/page.js` useState and useEffects definiton.
```javascript
const [weather, setWeatherData] = useState(0)
```
```javascript
fetch('http://localhost:3000/api/getWeather')
.then((res)=>res.json())
.then((weather)=>{
setWeatherData(weather)
})
```
add code to jsx template
```javascript
if(!weather) return <p>Noweather</p>
Today's temperature: {JSON.stringify(weather.temp)}
```
## Week eight
### Deploying the application
#### MongoDB in the cloud
login to https://account.mongodb.com/account/login
create a database
* choose free plan (M0 Free)
* create user
* choose Cloud Environment
* add 0.0.0.0/0 to IP Access list
migrate the local collection to cloud
* click "Browse Collections"
* click "Add My Own Data"
* use "app" for Database name
* use "login" for Collection name
* click "Create"
* click "Insert Document"
* add two users in JSON Format
```json
{
"username": "kyle@goslin.ie",
"pass": "1234",
"acc_type": "customer"
}
```
```json
{
"username": "kyle@goslin.ie",
"pass": "1234",
"acc_type": "manager"
}
```
* click "Create Database"
* use "app" for Database name again
* use "products" for Collection name
* click "Create"
* click "Insert Document"
* add a product in JSON Format
```json
{
"pname":"JamDoughnut",
"price":"1euro"
}
```
Getting the Connection Details
* click "Connect"
* click "Drivers"
* copy connection string and add your username and password to it
* change all occurences of MongoDB localhost to
```javascript
mongodb+srv://haasphilipp:GtPV1ONQReDtHUXE@cluster0.uqlamu8.mongodb.net/?retryWrites=true&w=majority'
```
#### GitHub
Create a new repository on GitHub
```console
git init
git add .
git commit -m "Initial commit"
git branch -M main
git remote add origin https://github.com/haasphilipp/rich-web-app.git
git push -u origin main
```
#### Vercel
import the GitHub project to Vercel and deploy it
Since I have already deployed a lot of projects via GitHub and Vercel, not much more documentation is needed here. :)