# 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. :)