From 86a87856b42b343770a43d9a68fa516091c0417d Mon Sep 17 00:00:00 2001 From: Tony Gaskell Date: Mon, 12 May 2025 11:27:48 -1000 Subject: [PATCH 01/94] Add initial lessons --- docs/learn/lesson-1.mdx | 132 +++++++++++++++++ docs/learn/lesson-2.mdx | 146 +++++++++++++++++++ docs/learn/lesson-3.mdx | 78 ++++++++++ docs/learn/lesson-4.mdx | 39 +++++ docs/learn/lesson-5.mdx | 308 ++++++++++++++++++++++++++++++++++++++++ docs/learn/lesson-6.mdx | 109 ++++++++++++++ docusaurus.config.js | 6 + sidebars.js | 6 + 8 files changed, 824 insertions(+) create mode 100644 docs/learn/lesson-1.mdx create mode 100644 docs/learn/lesson-2.mdx create mode 100644 docs/learn/lesson-3.mdx create mode 100644 docs/learn/lesson-4.mdx create mode 100644 docs/learn/lesson-5.mdx create mode 100644 docs/learn/lesson-6.mdx diff --git a/docs/learn/lesson-1.mdx b/docs/learn/lesson-1.mdx new file mode 100644 index 0000000..9035a43 --- /dev/null +++ b/docs/learn/lesson-1.mdx @@ -0,0 +1,132 @@ +import CodeBlock from '@theme/CodeBlock'; + +# Lesson 1: Offline First and Navigation + +Welcome to the RADFish tutorial! In this first lesson, we'll introduce the concept of Progressive Web Applications (PWAs) and their ability to work offline. We'll start by building the application and running it locally. Then, we'll simulate an offline environment to see how the PWA behaves. We'll fix a missing application icon issue and implement the first piece of navigation by making the "Start New Trip" button functional. + +```sh +git clone https://github.com/NMFS-RADFish/learn-radfish.git +cd learn-radfish + +# This step will not be included in the final tutorial +git reset --hard 3c42de24b330fa8da92ed06b0e5528d218d2d84f +``` + +## Getting Started + +Before we begin coding, let's build the application and serve it locally to see its current state. + +1. **Install Dependencies:** If you haven't already, open your terminal in the project root directory and run: + ```bash + npm install + ``` +2. **Build the Application:** Run the build command to generate the production-ready files, including the service worker needed for offline capabilities. + ```bash + npm run build + ``` +3. **Serve the Application:** Use a simple HTTP server to serve the built application. We've included `http-server` as a dev dependency. + ```bash + npm run serve + ``` + This command will typically make the application available at `http://localhost:8080`. Open this URL in your browser. + +4. **Simulate Offline Mode:** Open your browser's developer tools (usually by pressing F12 or right-clicking and selecting "Inspect" or "Inspect Element"). + * Go to the "Network" tab. + * Find the option to throttle the network connection. Look for a dropdown that might say "No throttling" or "Online" and change it to "Offline". + + ![Developer Tools Offline Toggle](https://developer.chrome.com/static/docs/devtools/network/image/new-resource-in-log_1920.png) + *(Image source: Chrome Developers)* + +5. **Reload the Page:** With the network set to "Offline", try reloading the page (Ctrl+R or Cmd+R). You should see that the application still loads! This is the service worker in action, serving cached assets. + +6. **Notice the Missing Icon:** While offline, you might notice that the application icon (in the browser tab or if you try to "install" the PWA) is missing or incorrect. We'll fix this first. + +7. **Go Back Online:** Remember to set your network back to "Online" in the developer tools when you're ready to proceed with the next steps. + +## Step 1: Add App Icons to Manifest + +The PWA manifest file (`public/manifest.json` generated via `vite.config.js`) tells the browser about your web application and how it should behave when 'installed' on the user's mobile device or desktop. This includes specifying icons of various sizes for different contexts. We need to add the correct icon definitions to our Vite configuration, which generates the manifest. + +Open `vite.config.js`. Locate the `/* Step 1: ... */` comment within the `manifest.icons` array and replace it with the following code: + +```javascript showLineNumbers=19 title="vite.config.js" + manifest: { + short_name: "RADFish", + name: "RADFish React Boilerplate", + icons: [ + // diff-add-start + { + src: "icons/radfish-144.ico", + sizes: "144x144 64x64 32x32 24x24 16x16", + type: "image/x-icon", + }, + { + src: "icons/radfish-144.ico", + type: "image/icon", + sizes: "144x144", + purpose: "any", + }, + { + src: "icons/radfish-192.ico", + type: "image/icon", + sizes: "192x192", + purpose: "any", + }, + { + src: "icons/radfish-512.ico", + type: "image/icon", + sizes: "512x512", + purpose: "any", + }, + // diff-add-end + { + src: "icons/144.png", + type: "image/png", + sizes: "144x144", + purpose: "any", + }, +``` + +**Explanation:** We are adding several definitions for our application icons (`radfish-*.ico`) to the `icons` array within the `VitePWA` plugin configuration. These definitions tell the browser where to find icons of different sizes and types (`.ico` in this case). Providing multiple sizes ensures the icon looks sharp on various devices and contexts (like the home screen, app launcher, etc.). The `purpose: "any"` indicates these icons are suitable for general use. + +**Rebuild and Test:** +* Stop the server if it's running (Ctrl+C in the terminal). +* Re-run `npm run build` and `npm run serve`. +* Go offline again in developer tools and reload. The correct application icon should now appear. + +## Step 2: Implement "Start New Trip" Navigation + +Currently, the "Start New Trip" button on the home page doesn't do anything. We need to make it navigate the user to the first step of the trip logging process, which will be at the `/start` route. We'll use the `useNavigate` hook from `react-router-dom` for this. + +Open `src/pages/Home.jsx`. Locate the `/* Step 2: ... */` comment within the `Button` component for "Start New Trip" and replace it with the following `onClick` handler: + +```jsx showLineNumbers=293 + +``` + +**Explanation:** +* We're adding an `onClick` event handler to the `Button`. +* Inside the handler, we call the `navigate` function (which we get from the `useNavigate()` hook provided earlier in the component). +* We pass the string `"/start"` to `navigate`, telling React Router to change the URL to `/start` when the button is clicked. This will cause the `App` component to render the `StartTrip` page component associated with that route. + +**Test Navigation:** +* You might need to restart your development server if you stopped it. If you are still running `npm run serve`, stop it (Ctrl+C) and start the development server for easier testing with Hot Module Replacement (HMR): + ```bash + npm run start + ``` +* Open the application in your browser (usually `http://localhost:3000` for the dev server). +* Click the "Start New Trip" button. You should now be navigated to a new page (which is currently blank or basic, as we haven't built the `StartTrip` form yet). + +## Conclusion + +In this lesson, you learned how to build and serve the PWA, verify its basic offline capabilities using the browser's developer tools, and add necessary application icons to the PWA manifest via the Vite configuration. You also implemented the first navigation feature, linking the home page button to the start of the trip logging workflow using React Router. The application now works offline and has a functional starting point for logging a new trip. \ No newline at end of file diff --git a/docs/learn/lesson-2.mdx b/docs/learn/lesson-2.mdx new file mode 100644 index 0000000..e4a456f --- /dev/null +++ b/docs/learn/lesson-2.mdx @@ -0,0 +1,146 @@ +# Lesson 2: Form Inputs + +This lesson focuses on building the first step of the trip logging form, the "Start Trip" page. We'll use components from the U.S. Web Design System (USWDS) library (`@trussworks/react-uswds`) to add inputs for the trip date, start time, and weather conditions. We will also start the application in development mode to take advantage of Hot Module Replacement (HMR) for faster feedback during development. + +## Getting Started + +Before adding the form inputs, let's start the development server. Open your terminal, navigate to the project's root directory (`learn-radfish`), and run the following command: + +```bash +npm run start +``` + +This command starts the Vite development server. It will automatically open the application in your default web browser. Thanks to Hot Module Replacement (HMR), most changes you make to the code will be reflected in the browser almost instantly without needing a full page reload, speeding up your development workflow. Keep this terminal window open while you work through the lesson. + +## Step 1: Add Date Picker Input + +We need a way for the user to select the date of their fishing trip. We'll use the `DatePicker` component from `@trussworks/react-uswds`. + +Open `src/pages/StartTrip.jsx`. Locate the `/* Step 1: Add Date Picker Input */` comment and replace it with the following code: + +```jsx +// filepath: src/pages/StartTrip.jsx + {/* Trip Date - USWDS DatePicker */} + {/* FormGroup adds spacing and connects label/input/error */} + + {/* Label with required indicator */} + + {/* Hint text for date format */} +
+ mm/dd/yyyy +
+ + {/* Error message displayed below input */} + {/* [Lesson 5.2:START] Display Trip Date Error */} + {/* This block will be deleted in the snapshot */} + {/* [Lesson 5.2:END] */} +
+``` + +**Explanation:** + +* We wrap the `DatePicker` and its `Label` in a `FormGroup` component. This helps with spacing and accessibility, associating the label, input, and potential error messages. +* The `Label` includes a red asterisk (`*`) to visually indicate that the field is required. +* A `div` with the class `usa-hint` provides helpful text about the expected date format. +* The `DatePicker` component itself is configured with an `id` and `name`. +* `defaultValue={formData.tripDate}` sets the initial value of the picker based on the component's state. +* `onChange={handleDateChange}` connects the picker to our custom `handleDateChange` function (already present in the file) which updates the `formData` state when the user selects a date. +* `aria-describedby` links the input to the hint text for screen readers. +* The `className` is conditionally set to apply error styling if the form has been submitted (`submitted` is true) and there's an error for this field (`errors.tripDate` exists). We'll implement the error handling logic in a later lesson. + +## Step 2: Add Time Picker Input + +Next, we'll add an input for the trip's start time using the `TimePicker` component. + +In the same file (`src/pages/StartTrip.jsx`), locate the `/* Step 2: Add Time Picker Input */` comment and replace it with the following code: + +```jsx +// filepath: src/pages/StartTrip.jsx + {/* Trip Start Time - USWDS TimePicker */} + + + + {/* [Lesson 5.3:START] Display Start Time Error */} + {/* This block will be deleted in the snapshot */} + {/* [Lesson 5.3:END] */} + +``` + +**Explanation:** + +* Similar to the `DatePicker`, we use `FormGroup` and `Label` for structure and accessibility. +* The `TimePicker` component provides a user-friendly way to select a time. +* `defaultValue={formData.startTime}` binds the input to the `startTime` field in our state. +* `onChange={handleTimeChange}` calls our specific handler function (already present) to update the state. +* `minTime`, `maxTime`, and `step={15}` configure the available time options (from 00:00 to 23:30 in 15-minute increments). +* `validationStatus` and `className` are used for conditional error styling, similar to the `DatePicker`. + +## Step 3: Add Weather Select Input + +Finally, we need a dropdown menu for the user to select the weather conditions at the start of the trip. We'll use the `Select` component. + +Still in `src/pages/StartTrip.jsx`, locate the `/* Step 3: Add Weather Select Input */` comment and replace it with the following code: + +```jsx +// filepath: src/pages/StartTrip.jsx + {/* Weather Conditions - USWDS Select */} + + + + {/* [Lesson 5.4:START] Display Weather Error */} + {/* This block will be deleted in the snapshot */} + {/* [Lesson 5.4:END] */} + +``` + +**Explanation:** + +* Again, `FormGroup` and `Label` provide the necessary structure. +* The `Select` component creates a dropdown menu. +* `value={formData.weather}` binds the selected option to the `weather` field in the state. +* `onChange={handleInputChange}` uses the standard input handler (already present) because the `Select` component behaves like a standard HTML select element in this regard. +* We define the available weather options using standard HTML `