# Assignments

Table of Contents

  1. Assignment One - Storage
  2. Assignment Two - Service Workers
  3. Assignment Three - React App
  4. Assignment Four - NextJS App

# 1. Storage

Starter code for this exercise will be discussed during class. Here is the link to the code Gist (opens new window).

In this assignment you will be working with an HTML <form> to collect friend information and image files from the website user. The friend data will be saved as a .json file with the Cache API in a json cache and the collected images will also be saved in a second cache with the Cache API. Then the data and images will be read from the caches and displayed in a list on the page.

When the page loads there will be two columns, one that displays a list of friends that have been saved in the local cache and one that displays a form for adding new friends. Each friend will be displayed as a card in a list. Each one will have a name, a date of birth, and an avatar image.

The form will collect the name, date of birth and avatar image using <input type="text">, <input type="date">, and <input type="file" accept="image/*">.

When the user clicks the save button in the form, use crypto.randomUUID() to create an id for the new friend, then create a JavaScript object that contains a UUID, the friend's name, the date of birth, and the name of the image file. All that data will be saved in a cache as a JSON file, using a name made with the random id + .json. The image from the form needs to be saved in a cache too. Name the image using the same random id plus the proper file extension.

Use two separate caches to save the JSON and the Images.

Make sure the whole form is filled out before trying to save a new friend. When the data is successfully saved, be sure to reset the form so it is ready for the next entry.

The JSON contents for each file will look something like the following. Notice how the image file name is made from the same UUID.

{
  "id": "8c54e4ad-df4b-4c9d-9c37-1a3790c0c2f5",
  "name": "John Economos",
  "dob": "1999-03-03",
  "avatar": "8c54e4ad-df4b-4c9d-9c37-1a3790c0c2f5.png"
}
1
2
3
4
5
6

When the page loads, as well after a new friend has been saved in the cache, then you need to read all the keys from the JSON cache and build a card for each friend. The caches names need to include the word purple. Use the id from the JSON to create a data- property for each <li>. The name of each image that needs to be pulled from the image cache will come from the JSON. The HTML for a card would look something like this.

<li data-ref="8c54e4ad-df4b-4c9d-9c37-1a3790c0c2f5" class="card">
  <h2>Person Name</h2>
  <p>2000-01-01</p>
  <p><img src="blob:http://127.0.0.1:5501/b959cd79-a248-4e83-a0f1-1f47238e57c5" alt="Person Name" /></p>
</li>
1
2
3
4
5

The list of JSON files should be used to build the list of cards. As each card is built, make the call to the image cache to match() the image with the same name as the avatar property from the JSON file.

# Demo Animation

Below is an animated demo of a working version that shows the basic functionality.

# Styling Requirements

  1. All colour combinations need to be accessible.
  2. A single sans-serif font from Google fonts should be used for ALL elements.
  3. All the spacing should be made so text from elements do not touch each other or any borders or background-color edges.
  4. The header fills the whole width of the container div, which has a fixed max-width.
  5. The list of friends and the add friend form need to be displayed as two columns under the header.

# General Requirements

  1. There should be no errors in the console.
  2. Your repo should have a minimum of 3 commits with meaningful comments.
  3. The form should have a submit listener that prevents the page from reloading.
  4. The JSON and image files should not be saved in the two caches unless all the needed data is available.
  5. All event listeners must be added through JavaScript, NOT HTML attributes.

# Submission

Invite your professor - prof3ssorSt3v3 to be a collaborator on your private repo.

Submit the URL of your private repo to the Cache Exercise in BS LMS.

Week 3

See BS LMS for the due date and time.

# 2. Service Workers

The second assignment will be to create a webpage that uses a service worker to monitor file requests, cache JSON data, and communicate and share information between multiple copies of a page.

The main script for the webpage should make an initial request for a list of users from https://random-data-api.com. For each user in the fetched data, a card should be created that shows two text properties from the user plus the avatar image. The card needs to have the uid (user id) from the user saved as a data- attribute in the HTML for the card.

When the user clicks on one of the cards, the hash value in the URL should change to # plus the uid of the user. The uid comes from the HTML data- attribute.

In the main script, add a listener for the popstate event or the hashchange event which will be triggered when the page location changes. When the location changes and there is a new hash value, a value of "#" or "" means that all the cards should be shown. A value with a user id means only show the card with the matching id.

The main script also needs to post a message to the service worker when the user clicks to show one card or all the cards. The service worker needs to have a message event listener to accept the message from the main script. The main script should send the id of the selected user plus the name of a function to be called on other pages that are using the same service worker. The function name should be of a function that exists in the main script.

The service worker receives the message from the main script and then uses the Client API to send a message to ALL clients except the one that sent the original message. The message that gets sent from the service worker to all the connected tabs/pages needs to include the name of the function to call plus the user id. It should also send information about something to change in the CSS of the pages that receive the message. As an example, in the demo video I generate a random color in the service worker and pass that to be set as the color of the heading text on each page.

The message from the service worker should NOT be sent back to the one page that sent the original message to the service worker.

An example message could look something like this:

let msg = {
  uid: '09dffcf5-f48a-4961-bf83-7c9a72e6e3a3',
  func: 'changeUser',
};
1
2
3
4

The service worker also needs to have a listener for the fetch event. By default, it will ev.respondWith for every request with a fetch(ev.request) response.

The only exception to the default handling occurs when the main script requests the data from https://random-data-api.com. Be sure to use the version 2 API endpoint. The cache must be named dynamic-doodle-cache. The response from the Random Data API should be saved with the Cache API as users.json.

When the fetch listener gets a request for the Random Data API users endpoint, it should check if users.json is already in the Cache. If it exists in the Cache then return the cached file. If it doesn't exist in the cache, then do the fetch and save a copy of the Response in the cache as users.json.

# Starter Code

Here is the Gist with the Starter Code (opens new window) for this assignment.

# Demo Video

# Submission

Invite your professor - prof3ssorSt3v3 to be a collaborator on your private repo.

Submit the URL of your private repo to the Service Worker Assignment in BS LMS.

Also submit the github.io link for the Github Pages version.

Remember to test your deployed version on Github Pages to make sure the Service Worker loads and runs there

Week 5

See BS LMS for the due date and time.

# 3. React App

This app will be used to demonstrate that you can build a React app from a series of separate components that use shared state objects, plus values and functions that are passed through props from a parent component to a child component. You will use the useState and useEffect hooks to manage the values in the state objects.

The app will use the OpenWeather API (opens new window), which requires an account. Be sure to sign up for your API key early as it can take up to two days to get one.

# Components

The app is a single page application that needs no routing. The components will dynamically render different content based on the state and props values.

The basic structure of the app will look something like this:

<App>
  <Header />
  <SearchBar />
  <FeedbackBar />
  <LocationBar>
    <LocationCard />
    <LocationCard />
  </LocationBar>
  <Weather>
    <Loading />
  </Weather>
</App>
1
2
3
4
5
6
7
8
9
10
11
12

The actual names of the components are up to you, as are the names of the props that you create. Here is a sample image of a demo version of the app. Each of the different background colors is a different React component. (The feedback component is not currently visible.) Each of the components needs its own css file. All the components, except for the App should be kept inside a single folder - components.

Sample app screenshot

The Header component is simply static content. It provides a masthead and title for the app.

The SearchBar component contains a form with a label, input, and button. The input element will need to have a state variable used to hold and update its value with an onChange event listener. The form will need an onSubmit event that calls a function. The function will be inside of the SearchBar component. It will take the value from the input, via the state property, and call the https://api.openweathermap.org/geo/1.0/direct?q API geocoding end point to get a location object based on the value from the input. Once a location object has been created, then the add location function from App is called and the location object is passed to the function. This will update the locations array state property and trigger passing the new value through props to the LocationBar.

The data that comes back from the geocoding end point will get used to create the location object to pass to the array. It will look something like this:

let location = {
  id: crypto.randomUUID(),
  lat: info.lat,
  lng: info.lon,
  country: info.country,
  name: info.name,
};
1
2
3
4
5
6
7

The LocationBar component will be passed an array of location objects from App through props. The array is used to create a series of LocationCard components. A location object is passed through props to each LocationCard.

Each LocationCard will display the name and country of its location, plus a button that can be used to remove a location from the array of locations. The full array of locations is held inside App as a state property. The button will have an onClick event listener to call the remove method, which comes through props from the App component. The card itself should also have an onClick event listener which will call a function that is also passed through props from the App component.

The Weather component will display the weather details for the selected location, including the main, description, temperature, feels like temperature, icon, and wind speed. Those are the minimum values. More can be shown too. The weather object will be held in a state property inside App and then passed through props to the Weather component.

The FeedbackBar component displays an error or feedback message to the user. The message comes from a message state property. If the message is null or an empty string then the FeedbackBar component does not appear (it will have no content). It should have a useEffect method that runs any time that the component is rendered. The useEffect function starts a timer to automatically remove the component by calling the set method for the message state property. The useEffect method's function also needs to have a cleanup function to clear the timeout.

The App component has a state property for the array of locations, a state property for the weather data for the selected location, a state property that holds the current error message for the user, and a state property that holds the currently selected location. The majority of the app functionality will be inside the App component function.

App needs a function for adding a location to the state location array which can be passed through props to child components. The add function needs to limit the number of locations that can be added to the array. If the add function is called and the length of the locations array is already at the maximum (say 4 - 6), then set the error message state property to the message.

App also needs a function for removing a location from the location array, which can also be passed through props to child components.

App also needs a function for calling the https://api.openweathermap.org/data/2.5/weather? API end point. It needs a latitude and a longitude value to get the weather for a specific location.

The Loading component gets used to display a loading message inside the Weather component, when the user clicks one of the LocationCards. Make sure that setting the state value that triggers showing the Loading component happens before making the API call to get the weather. It displays a loading message or a spinner/loader icon while the weather is being fetched. Once the weather data has been updated and passed to the Weather component then the Loading component should disappear. This can be achieved with a separate state property or by setting the weather state value to null.

The Weather component will display different things when:

  • The selectedLocation state property is null. - "no selected location" message.
  • The selectedLocation state property has an object. - location is selected but may or may not be loaded.
  • The weather state property is null. - show the Loading component.
  • The weather state property has an object. - show the weather data.

# General Practices

You should be displaying the Feedback component for every error. That means setting the value of the state variable that holds the error message used in the component from inside every catch in your code.

You should use the { PropTypes } import on all the child components that receive props from a parent component. It should be validating the type of each prop passed into each component.

# Design

Create a custom colour scheme and use it throughout your application. Set up your colour variables inside your App.css file.

The colours, font-sizes, and layout need to be responsive and accessible.

Include a Google Font by adding the 3 <link> tags to the index.html file which preconnect and load the google fonts css and then load the font. Make sure you add the font to your index.css file.

# Demo Video

Hosting This Assignment

This app should be hosted on Vercel, NOT Github Pages.

# Submission

Submitting this assignment requires several steps.

  1. Invite your professor - shah0150 to be a collaborator on your private Github repo.

  2. Submit the URL of your private repo to the React App in BS LMS.

  3. Use Vercel to create the hosted version of your website, NOT Github Pages. Create a project on Vercel that is connected to your private Github repo.

  4. Submit the link to your Vercel React Weather App Website. NOT the development link with the long subdomain, but the shorter link that ends in vercel.app.

  5. Invite your professor - shah0150 to be a collaborator to your Vercel project. This way they can access the error logs in the Vercel dashboard.

Week 12

See BS LMS for the due date and time.

# 4. NextJS App

Details coming soon

# Submission

The submission requires several steps.

  1. Invite your professor - prof3ssorSt3v3 to be a collaborator on your private repo.

  2. Connect Vercel to your Github Repo.

  3. Invite your professor - shah0150 to be a collaborator in your Vercel dashboard. This way they can see the error messages in the log from the Vercel dashboard

  4. Submit the URL of your private repo to the NextJS App in BS LMS PLUS the URL for your Vercel hosted version of the site.

NextJS URL

Make sure you submit the short URL, not the development version url of your Vercel site.

Due Week 13

See BS LMS for the due date and time.

Last Updated: 7/12/2024, 9:06:51 AM