Riley Front‐End - TheEvergreenStateCollege/upper-division-cs-23-24 GitHub Wiki

Back

Front-End


3/12/24 Tuesday ✔️ I followed along with the instructions to create a "profile.html". I am having issues on conceptualizing how to implement this in React instead of HTML, and also how to implement it into my specific project.

Response from ppham

Thanks for documenting Riley. One way to use React's ability to combine JSX (a functional, Javascript-like subset) with collections of data is to iterate over a collection such as this code which creates an HTML image for each item in a list.

https://github.com/TheEvergreenStateCollege/upper-division-cs/blob/main/web-24wi/assignments/ddnsc/frontend/week03/AdoptMe/src/Carousel.jsx#L28


HW-2 ✔️ So it is spring and I am just now coming back to do this dev diary (sorry). I ended up completing all of the components of the Adopt Me React project last quarter, so I was struggling trying to figure out how I was going to show that I had done the work. Then I remembered that GitHub has this amazing feature called commit history, so I found the work I had committed for HW 2 and opened it in codespaces, problem solved. So I was able to get everything running on the website besides getting the individual pet pages to work once they are clicked. I tried many things to get this to work: changing the API calls from HTTP to HTTPS worked in some cases but in this case I would just indefinitely load until an error was thrown. Months later and I finally found the answer, I had the link in the API call wrong, I fixed it and everything started working! I am still getting an error regarding Details with the pets constant, since it is a reference to a reference to a reference. It doesn't seem to be causing issues with the pet page being loaded, so I will leave it.
const pet = results.data.pets[0];
const fetchPet = async ({queryKey}) => {
    const id = queryKey[1];

    const apiRes = await fetch(`https://pets-v2.dev-apis.com/pets?id=${id}`);

    if (!apiRes.ok) {
        throw new Error(`details/${id} fetch no work :(`);
    }

    return apiRes.json();
};

export default fetchPet;
import { useParams } from "react-router-dom";
import { useQuery } from "@tanstack/react-query";
import fetchPet from "./fetchPet";
import Carousel from "./Carousel";

const Details = () => {
  const { id } = useParams();
  const results = useQuery(["details", id], fetchPet);

  if (results.isLoading) {
    return (
      <div className="loading-pane">
        <h2 className="loader">🌀</h2>
      </div>
    );
  }

  const pet = results.data.pets[0];

  return (
    <div className="details">
      <Carousel images={pet.images} />
      <div>
        <h1>{pet.name}</h1>
        <h2>{`${pet.animal} — ${pet.breed} — ${pet.city}, ${pet.state}`}</h2>
        <button>Adopt {pet.name}</button>
        <p>{pet.description}</p>
      </div>
    </div>
  );
};

export default Details;
import { useQuery } from '@tanstack/react-query';
import fetchBreedList from './fetchBreedList';

export default function useBreedList(animal) {
    const results = useQuery(["breeds", animal], fetchBreedList);
    return [results?.data?.breeds?? [], results.status];
}

image


HW-3 ✔️ So it turns out that a codespace repo gets automatically deleted after a certain length of inactivity, so the repo I did this assignment with is gone (karma for procrastination). I believe I completed up to the end of the Complete Intro to React, and will now carry on with the first three sections of Intermediate React, despite me knowing what Tailwind CSS is and having already implemented it into my final project. I actually really enjoy using Tailwind because it feels more implemented into React, like CSS is just a part of React. Also, I feel like you can write style in almost shorthand, a lot of abbreviations are used which makes it quicker. But also, it may not be as intuitive as fully writing it like in a CSS file.
import { Link } from "react-router-dom";
const Pet = (props) => {
  const { name, animal, breed, images, location, id } = props;
  let hero = "http://pets-images.dev-apis.com/pets/none.jpg";
  if (images.length) {
    hero = images[0];
  }
  return (
    <Link to={`/details/${id}`} className="relative block">
      <div className="image-container">
        <img src={hero} alt={name} />
      </div>
      <div className="absolute bottom-0 left-0 bg-gradient-to-tr from-white to-transparent pr-2 pt-2">
        <h1>{name}</h1>
        <h2>{`${animal} — ${breed} — ${location}`}</h2>
      </div>
    </Link>
  );
};
export default Pet;
import { useState, useContext } from "react";
import { useQuery } from "@tanstack/react-query";
import AdoptedPetContext from "./AdoptedPetContext";
import Results from "./Results";
import useBreedList from "./useBreedList";
import fetchSearch from "./fetchSearch";
const ANIMALS = ["bird", "cat", "dog", "rabbit", "reptile"];
const SearchParams = () => {
  const [requestParams, setRequestParams] = useState({
    location: "",
    animal: "",
    breed: "",
  });
  const [animal, setAnimal] = useState("");
  const [breeds] = useBreedList(animal);
  const [adoptedPet] = useContext(AdoptedPetContext);
  const results = useQuery(["search", requestParams], fetchSearch);
  const pets = results?.data?.pets ?? [];
  return (
    <div className="my-0 mx-auto w-11/12">
       <form className="p-10 mb-10 rounded-lg bg-gray-200 shadow-lg flex flex-col justify-center items-center"
        onSubmit={(e) => {
          e.preventDefault();
          const formData = new FormData(e.target);
          const obj = {
            animal: formData.get("animal") ?? "",
            breed: formData.get("breed") ?? "",
            location: formData.get("location") ?? "",
          };
          setRequestParams(obj);
        }}
      >
      {
        adoptedPet ? (
          <div className="pet image-container">
            <img src={adoptedPet.images[0]} alt={adoptedPet.name} />
          </div>
        ) : null
      }
        <label htmlFor="location">
          Location
          <input className="search-input"type="text" id="location" name="location" placeholder="Location" />
        </label>

        <label htmlFor="animal">
          Animal
          <select
            className="search-input"
            id="animal"
            name="animal"
            onChange={(e) => {
              setAnimal(e.target.value);
            }}
            onBlur={(e) => {
              setAnimal(e.target.value);
            }}
          >
            <option />
            {ANIMALS.map((animal) => (
              <option key={animal} value={animal}>
                {animal}
              </option>
            ))}
          </select>
        </label>
        <label htmlFor="breed">
          Breed
          <select disabled={!breeds.length} id="breed" name="breed" className="search-input greyed-out-disabled">
            <option />
            {breeds.map((breed) => (
              <option key={breed} value={breed}>
                {breed}
              </option>
            ))}
          </select>
        </label>
        <button className="rounded px-6 py-2 color text-white hover:opacity-50 border-none bg-orange-500">Submit</button>
      </form>
      <Results pets={pets} />
    </div>
  );
};
export default SearchParams;
import Pet from "./Pet";
const Results = ({ pets }) => {
  return (
    <div className="grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
      {!pets.length ? (
        <h1>No Pets Found</h1>
      ) : (
        pets.map((pet) => {
          return (
            <Pet
              animal={pet.animal}
              key={pet.id}
              name={pet.name}
              breed={pet.breed}
              images={pet.images}
              location={`${pet.city}, ${pet.state}`}
              id={pet.id}
            />
          );
        })
      )}
    </div>
  );
};
export default Results;

image


HW-6 ✔️

I am finally coming back to this homework, and alas: I lost all of my work because my Codespace expired. I rewatched all of the Frontend Masters content and followed coding the tutorial. I added the login form to the index.html, as well as the registration form. I then added the endpoints to server.js, like the login post request. I also coded along the registration post request, and was interested by the findUser function, since it checks if a user already exists. Overall this assignment was interesting, but I am pretty overwhelmed by how all of the pieces work together. Does anything change when React is used for the front-end? How do we connect what we learned in this homework to the protected routes we learn about in Infra?

import express from 'express'
import { Low } from 'lowdb'
import { JSONFile } from 'lowdb/node'
import * as url from 'url';
import bcrypt from 'bcryptjs';
import * as jwtJsDecode from 'jwt-js-decode';
import base64url from "base64url";
import SimpleWebAuthnServer from '@simplewebauthn/server';

const __dirname = url.fileURLToPath(new URL('.', import.meta.url));

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

const adapter = new JSONFile(__dirname + '/auth.json');
const db = new Low(adapter);
await db.read();
db.data ||= { users: [] }

const rpID = "localhost";
const protocol = "http";
const port = 5050;
const expectedOrigin = `${protocol}://${rpID}:${port}`;

app.use(express.static('public'));
app.use(express.json());
app.use(express.urlencoded({
  extended: true
}));

app.post("/auth/login", (req, res) => {
  const user = findUser(req.body.email);
  if (user) {
      // user exists, check password
      if (bcrypt.compareSync(req.body.password, user.password)) {
          res.send({ok: true, email: user.email, name: user.name});
      } else {
          res.send({ok: false, message: 'Data is invalid'});            
      }
  } else {
      // User doesn't exist
      res.send({ok: false, message: 'Data is invalid'});
  }
});

function findUser(email) {
  const results = db.data.users.filter(u=>u.email==email);
  if (results.length==0) return undefined;
  return results[0];
}

app.post("/auth/register", (req, res) => {
  var salt = bcrypt.genSaltSync(10);
  var hash = bcrypt.hashSync(req.body.password, salt);

  const user = {
      name: req.body.name,
      email: req.body.email,
      password: hash
  };
  const userFound = findUser(req.body.email);

  if (userFound) {
      // User already registered
      res.send({ok: false, message: 'User already exists'});
  } else {
      // New User
      db.data.users.push(user);
      db.write();
      res.send({ok: true});
  }
});



app.get("*", (req, res) => {
    res.sendFile(__dirname + "public/index.html"); 
});

app.listen(port, () => {
  console.log(`App listening on port ${port}`)
});
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>Coffee Masters</title>

    <link rel="preconnect" href="https://fonts.googleapis.com"> 
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 
    <link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;700&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@48,400,0,0" />


    <link rel="stylesheet" href="styles.css">
    <link rel="manifest" href="app.webmanifest">
    <link rel="apple-touch-icon" href="images/icons/icon.png">
    <link rel="icon" href="images/icons/icon.png">

    <script src="scripts/app.js" type="module"></script>

</head>

<body>
    <header>
        <h1><img src="images/logo.svg" width="140" alt="Coffee Masters"></h1>
        <nav>
            <a class="navlink material-symbols-outlined" id="linkHome" href="/">
                    local_cafe
            </a>
            <a class="navlink material-symbols-outlined" id="linkOrder" href="/account">
                    account_box
            </a>
        </nav>
    </header>

    <main>

        <section class="page" id="home">
            <header>
                <h2>Welcome!<h2>
            </header>

            <section class="logged_out">
                <p>You are currently logged out.</p>
                <p><a href="/login" class="navlink">Log in</a>.</p>
            </section>
      
            <section class="logged_in">
                <p>You are currently logged in as <span class='account_name' class="navlink"></span>.</p>
                <p>You can see details of your <a href='/account' class="navlink">Account</a></p>
            </section>       
        </section>

        <section class="page"  id="register">
            <h2>Register</h2>
            <form id="formRegister" onsubmit="Auth.register(event)">
                <fieldset>
                    <label for="register_name">Name</label>
                    <input placeholder="Name" id="register_name"
                            required autocomplete="name">
                    <label for="register_email">E-mail (your username)</label>
                    <input placeholder="Email" id="register_email" 
                            required type="email" autocomplete="username">
                    
                    <label for="register_password">Your Password</label>
                    <input type="password" id="register_password"
                            required autocomplete="new-password">
                </fieldset>
            
                <button>Register Account</button>
            </form>
        </section>

        <section class="page" id="login">
            <h2>Log In</h2>

            <form id="formLogin" onsubmit="Auth.login(event)">
                <fieldset>
                    <label for="login_email">E-mail</label>
                    <input placeholder="email" id="login_email"
                        required autocomplete="webauthn username">      
                    <section hidden id="login_section_password">              
                        <label for="login_password">Password</label>
                        <input type="password" id="login_password"
                            autocomplete="webauthn current-password">
                    </section>                                      
                    <section hidden id="login_section_webauthn">
                        <a href="#" class="navlink" onclick="Auth.webAuthnLogin(); event.preventDefault">Log in with your Authenticator</a> 
                    </section>                                      
                </fieldset>
                <button>Continue</button>
            
                <p>
                    <a href="/register" class="navlink">Register a new account instead</a> 
                </p>                   
            
            </form>           
        </section>

         
        <section class="page"  id="account">
            <h2>My Account</h2>
            <dl>
                <dt>Name</dt>
                <dd class="account_name"></dd>
                <dt>Email</dt>
                <dd class="account_username"></dd>
            </dl>

            <button onclick="Auth.logout()">Log out</button>
        </section>
    </main>
</body>
</html>
import API from "./API.js";
import Router from "./Router.js";

const Auth = {
    isLoggedIn: false,
    account: null,
    updateStatus() {
        if (Auth.isLoggedIn && Auth.account) {
            document.querySelectorAll(".logged_out").forEach(
                e => e.style.display = "none"
            );
            document.querySelectorAll(".logged_in").forEach(
                e => e.style.display = "block"
            );
            document.querySelectorAll(".account_name").forEach(
                e => e.innerHTML = Auth.account.name
            );
            document.querySelectorAll(".account_username").forEach(
                e => e.innerHTML = Auth.account.email
            );

        } else {
            document.querySelectorAll(".logged_out").forEach(
                e => e.style.display = "block"
            );
            document.querySelectorAll(".logged_in").forEach(
                e => e.style.display = "none"
            );

        }
    },    
    init: () => {
        
    },
    login: async (event) => {
        if (event) event.preventDefault();
        const user = {
            email: document.getElementById("login_email").value,
            password: document.getElementById("login_password").value
    
        };
        const response = await API.login(user);
        Auth.postLogin(response, { 
            ...user,
            name: response.name
        });
    
    },
    register: async  (event) => {
        event.preventDefault();
        const user = {
            name: document.getElementById("register_name").value,
            email: document.getElementById("register_email").value,
            password: document.getElementById("register_password").value
        }
        const response = await API.register(user);
        Auth.postLogin(response, user);
    },
    postLogin: (response, user) => {
        if (response.ok) {
            Auth.isLoggedIn = true;
            Auth.account = user;
            Auth.updateStatus();
    
            Router.go("/account");        
        } else {
            alert(response.message)
        }           
    },    
    logout: () => {
        Auth.isLoggedIn = false;
        Auth.account = null;
        Auth.updateStatus();
        Router.go("/");
    },
}
Auth.updateStatus();

export default Auth;

// make it a global object
window.Auth = Auth;

Screenshot 2024-06-12 144139


HW-8 ✔️ I followed a part of the D3.js tutorial, but was not able to finish the whole tutorial because of time constraints. Overall I had a lot of issues with the method we were doing our work in, the website was extremely confusing on how to use it. I was able to replicate the code to generate a smiley face, and was able to understand the coordinates and how they were working to draw the smiley face. I then had issues with getting the website to generate a flower petal, but understood how that worked as well. Here are some screenshots of the work I did.

Screenshot 2024-06-12 152017

Screenshot 2024-06-12 152305

Screenshot 2024-06-12 152619


⚠️ **GitHub.com Fallback** ⚠️