├── .eslintrc.json
├── .gitignore
├── LICENSE
├── README.md
├── babel.config.js
├── backend
├── data
│ ├── people.js
│ └── planets.js
├── helpers.js
├── mock-server.js
├── routers
│ └── starwars.js
└── server.js
├── frontend
├── .eslintrc.json
├── components
│ ├── App.js
│ └── Character.js
├── favicon.ico
├── fonts
│ └── TitilliumWeb-SemiBold.ttf
├── images
│ └── background.jpg
├── index.html
├── index.js
└── styles
│ ├── reset.css
│ └── styles.css
├── jest.config.js
├── jest.globals.js
├── mvp.test.js
├── package-lock.json
├── package.json
└── webpack.config.js
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "commonjs": true,
4 | "es2021": true,
5 | "node": true,
6 | "browser": true,
7 | "jest": true
8 | },
9 | "extends": [
10 | "eslint:recommended",
11 | "plugin:react/recommended"
12 | ],
13 | "parserOptions": {
14 | "ecmaVersion": "latest",
15 | "sourceType": "module"
16 | },
17 | "rules": {}
18 | }
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | .pnpm-debug.log*
9 |
10 | # Diagnostic reports (https://nodejs.org/api/report.html)
11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # Snowpack dependency directory (https://snowpack.dev/)
46 | web_modules/
47 |
48 | # TypeScript cache
49 | *.tsbuildinfo
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Optional stylelint cache
58 | .stylelintcache
59 |
60 | # Microbundle cache
61 | .rpt2_cache/
62 | .rts2_cache_cjs/
63 | .rts2_cache_es/
64 | .rts2_cache_umd/
65 |
66 | # Optional REPL history
67 | .node_repl_history
68 |
69 | # Output of 'npm pack'
70 | *.tgz
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # dotenv environment variable files
76 | .env
77 | .env.development.local
78 | .env.test.local
79 | .env.production.local
80 | .env.local
81 |
82 | # parcel-bundler cache (https://parceljs.org/)
83 | .cache
84 | .parcel-cache
85 |
86 | # Next.js build output
87 | .next
88 | out
89 |
90 | # Nuxt.js build / generate output
91 | .nuxt
92 | dist
93 |
94 | # Gatsby files
95 | .cache/
96 | # Comment in the public line in if your project uses Gatsby and not Next.js
97 | # https://nextjs.org/blog/next-9-1#public-directory-support
98 | # public
99 |
100 | # vuepress build output
101 | .vuepress/dist
102 |
103 | # vuepress v2.x temp and cache directory
104 | .temp
105 | .cache
106 |
107 | # Docusaurus cache and generated files
108 | .docusaurus
109 |
110 | # Serverless directories
111 | .serverless/
112 |
113 | # FuseBox cache
114 | .fusebox/
115 |
116 | # DynamoDB Local files
117 | .dynamodb/
118 |
119 | # TernJS port file
120 | .tern-port
121 |
122 | # Stores VSCode versions used for testing VSCode extensions
123 | .vscode-test
124 |
125 | # yarn v2
126 | .yarn/cache
127 | .yarn/unplugged
128 | .yarn/build-state.yml
129 | .yarn/install-state.gz
130 | .pnp.*
131 |
132 | .DS_Store
133 | .vscode
134 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 BloomTech Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sprint 6 Challenge
2 |
3 | ## Introduction
4 |
5 | Welcome to Sprint Challenge 6! Today, you'll practice using React to manipulate the DOM by fetching data from the network and building a list of Star Wars characters.
6 |
7 | This experience will give you a taste of what a take-home assignment might be like, during a hiring process for a React position.
8 |
9 | Here's an overview of the tasks you need to complete:
10 |
11 | 1. **Obtain** JSON data from a web service.
12 | 1. **Combine** data obtained from different sources into a single data structure.
13 | 1. **Render** repeatable components to the DOM using the combined data and React.
14 |
15 | To succeed in this challenge, you'll need the following technical skills:
16 |
17 | 1. **Promises** and the ability to deal with asynchronous code.
18 | 1. **Making HTTP requests** with Axios or fetch.
19 | 1. **Using React's state and effect** hooks.
20 | 1. **Creating React components** and rendering them inside other components.
21 | 1. **Looping over data** passing props into React components.
22 | 1. **Adding simple interactivity** to components using event handlers.
23 |
24 | Additionally, the following soft skills will greatly impact your performance:
25 |
26 | 1. Attention to detail. Make sure there isn't a single character out of place!
27 | 1. Perseverance. Keep trying until you figure it out!
28 | 1. Patience. Make sure to read the entire README for important information.
29 |
30 | ## Instructions
31 |
32 | You are in the middle of a hiring process for a local startup that's looking for a React developer, and you have been assigned a challenge to complete. The task involves demonstrating proficiency with basic React moves like fetching data, building interactive components and using hooks.
33 |
34 | Specifically, you need to complete a website that displays a list of Star Wars characters along with their basic information such as ID, name, date of birth, and home world. Users of the app should be able to click on a character to expand some information about the character's home world. You can refer to the [full mockup](https://bloominstituteoftechnology.github.io/W_U2_S6_sprint_challenge) to have an idea of the design.
35 |
36 | To help you complete the task, several members of your team will provide you with instructions and advice.
37 |
38 | ### 💾 DevOps Engineer
39 |
40 | **Below, your future DevOps expert will help you set up your local environment and launch the project.**
41 |
42 |
43 | Click to read
44 |
45 | ---
46 |
47 | This is a **full-stack web application** that comprises both back-end and front-end components. If deployed to production, the back-end part would run in the cloud (think Amazon Web Services or Azure), while the front-end -a React app- would execute inside the user's web browser (like Chrome for Android, or Firefox for desktop).
48 |
49 | As a front-end engineer, your focus is mainly on the files that load **on the user's device**. In this particular case, these files live inside the `frontend` folder. The `backend` folder contains a web server built in Node, containing the API needed for this project.
50 |
51 | 1. You will **clone this repository** to your computer, which will allow you to run the software locally for development and testing purposes.
52 |
53 | 1. You will navigate your terminal to the project folder **and execute `npm install`**. This will install the libraries declared inside `package.json`. Some of these packages are needed for the back-end to do its job of serving JSON data.
54 |
55 | 1. After successful installation, **execute `npm run dev` in a terminal, and `npm test` in a different terminal**. On successful start, you will load the app in Chrome **at `http://localhost:3003`**.
56 |
57 | 1. If you haven't already, install the [Eslint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) for VSCode. It will highlight syntax errors and problems right inside your editor, which saves tons of time.
58 |
59 | My job assisting you with local setup of the app is done! You will speak to our designer next.
60 |
61 | ---
62 |
63 |
64 |
65 | ### 🎨 Product Designer
66 |
67 | **Below, you will find information on how to approach the task, from your future Product Designer.**
68 |
69 |
70 | Click to read
71 |
72 | ---
73 |
74 | Collaboration between a designer and a web developer can be very powerful. Designers excel at creating amazing user experiences and have a keen eye for beauty and usability, while developers are experts in the underlying technology of the product.
75 |
76 | Your job as a web developer is to implement the design with as much fidelity as possible. While a developer might think they have a better way to arrange elements on the screen, the mocks and designs are the result of research and hard work. It's important to treat them with the respect they deserve.
77 |
78 | It's crucial to use the readable texts designed for the user interface **verbatim**. If a design reads "Loading Characters...", then "Loading _the_ Characters..." is incorrect. Attention to detail is critical!
79 |
80 | There are other constraints and requirements that must be followed, such as sticking to certain class names or keeping the structure of the HTML a certain way.
81 |
82 | Fortunately, you have [a very detailed mock](https://bloominstituteoftechnology.github.io/W_U2_S6_sprint_challenge/) that you can load in your browser and inspect closely, which will make your job much easier.
83 |
84 | ---
85 |
86 |
87 |
88 | ### 🥷 Lead Developer
89 |
90 | **Below, your future Team Lead will discuss strategy and tactics for dealing with this ticket.**
91 |
92 |
93 | Click to read
94 |
95 | ---
96 |
97 | Hey! Let's make sure you're up to speed with your **action items so far**.
98 |
99 | - [x] The app is installed on your machine, with both `dev` and `test` scripts running in terminals.
100 | - [x] You studied the [mock](https://bloominstituteoftechnology.github.io/W_U2_S6_sprint_challenge/) in the Elements tab of Dev Tools.
101 | - [x] You saw how the "planet" paragraph is mounted and unmounted from the DOM as the user clicks on a character.
102 |
103 | Awesome! Our back-end engineer says that the JSON data needed to build the Star Wars Character Cards comes from two endpoints:
104 |
105 | - Endpoint A [GET]
106 | - Endpoint B [GET]
107 |
108 | ❗ You should stop now, and **try out both endpoints using Postman**, to see what they return.
109 |
110 | Inside `frontend/components/App.js`, and **on first render only**, fetch the data from both endpoints above.
111 |
112 | Here's the tricky thing: each character fetched using Endpoint A has a "homeworld" property, but the value of this property is not the name of the planet but its ID instead. This means you must use the data obtained from Endpoint B to obtain the missing piece of information about each character: the name of their home world.
113 |
114 | For fetching, you can optionally use `Promise.all` to handle the requests. We do not need the data from request A in order to _start_ request B, so the requests can happen concurrently instead of back-to-back. **This will make the app feel faster** to the user!
115 |
116 | Once you have the responses from Endpoints A and B stored inside variables, check that they match what you saw in Postman, and then **use your JavaScript skills to combine the two lists into a single data structure** that is comfortable to work with. There may be some array methods that can help with this...Ideally, it would look something like this:
117 |
118 | ```js
119 | [
120 | {
121 | id: 18,
122 | name: "Luke Skywalker",
123 | // etc
124 | homeworld: {
125 | id: 31,
126 | name: "Tatooine",
127 | // etc
128 | }
129 | },
130 | // other characters
131 | ]
132 | ```
133 |
134 | Once you have the data in the right shape stored in App state, you can **start working on your `/frontend/components/Character.js` component** that takes in the information about a single character via props. You will use this component inside App.js, looping over the data held in App state and rendering a Character at each iteration of the loop.
135 |
136 | Make sure that each character that renders to the DOM has the **exact same class names and text contents** as those in the design! Also, render the characters **in the same order** as they arrive from Endpoint A.
137 |
138 | Note how for each card, the planet information is not available on page load. A paragraph containing the name of a given character's home world appears in the DOM when a user clicks on the character's card, as you can see in the [mock](https://bloominstituteoftechnology.github.io/W_U2_S6_sprint_challenge). Clicking again on the character unmounts the paragraph completely, removing the planet information from the DOM. Whether the home world name shows or not is private component state of the Character component.
139 |
140 | Reach out if you get too stuck, and have fun!
141 |
142 | ---
143 |
144 |
145 |
146 | ## FAQ
147 |
148 |
149 | How do I submit this task?
150 |
151 | You submit via Codegrade. Check the assignment page on your learning platform.
152 |
153 |
154 |
155 |
156 | I am getting errors when I run npm install or npm run dev. What is going on?
157 |
158 | This project requires Node correctly installed on your computer in order to work. Your learning materials should have covered installation of Node. Sometimes Node can be installed but mis-configured. You can try executing `npm run install:violently` (check out `package.json` to see what this does), but if Node errors are recurrent, it indicates something is wrong with your machine or configuration, in which case you should request assistance from Staff.
159 |
160 |
161 |
162 |
163 | Do I need to install any libraries?
164 |
165 | No. Everything you need should be installed already, including Axios.
166 |
167 |
168 |
169 |
170 | Am I allowed to edit the styling of the site?
171 |
172 | You are welcome to add your personal touch to the site using Styled Components. There is just one rule: all tests must pass! Our recommendation is to wait until MVP is achieved before editing the styles.
173 |
174 |
175 |
176 |
177 | My app does not work! How do I debug it?
178 |
179 | Save your changes, and reload the site in Chrome. If you have a syntax problem in your code, the app will print error messages in the Console. Focus on the first message. Place console logs right before the crash site (errors usually inform of the line number where the problem is originating) and see if your variables contain the data you think they do. If there are no errors but the page is not doing what it's supposed to, the debugging technique is similar: put console logs to ensure that the code you are working on is actually executing, and to check that all variables in the area hold the correct data.
180 |
181 |
182 |
183 |
184 | How do I run tests against my code?
185 |
186 | Execute `npm test` in your terminal. These are the same tests that execute inside Codegrade. Although this never crossed your mind, tampering with the test file won't change your score, because Codegrade uses a pristine copy of the original test file, `mvp.test.js`. If a particular test is giving you grief, don't jump straight to the code to try and fix it. Go to Chrome first, and make sure you can replicate the problem there. A problem we can reliably replicate is a problem mostly fixed.
187 |
188 |
189 |
190 |
191 | I believe my code is correct and the test is wrong. What do I do?
192 |
193 | On occasion the test runner will get stuck. Use CTRL-C to kill the tests, and then `npm test` to launch them again. Try to reproduce the problem the test is complaining about by interacting with the site in Chrome, and do not code "to make the test happy". Code so that **your app does exactly what the mock does**. The tests are there for confirmation. Although it's possible that a particular test be flawed, it's more likely that the bug is in your own code. If the problem persists, please request assistance from Staff.
194 |
195 |
196 |
197 |
198 | The output of the test script is just too overwhelming! What can I do?
199 |
200 | If you need to disable all tests except the one you are focusing on, edit the `mvp.test.js` file and, as an example, change `test('👉 focus on this', () => { etc })` to be `test.only('👉 focus on this', () => { etc })`. (Note the "only".) This won't affect Codegrade, because Codegrade runs its own version of the tests. Keep in mind though, if there is a syntax problem with your code that is causing an error to be thrown, all tests will fail.
201 |
202 |
203 |
204 |
205 | Why can't endpoints provide the data in the correct shape from the get-go?
206 |
207 | As web developers, we often don't have control over our sources of data, and it's common to have to combine JSON from various sources into a data structure that works for the front-end. Even if the endpoints were under our control, and the back-end team were willing to build a new endpoint or improve existing ones, bug fixes and features sometimes can't wait that long.
208 |
209 |
210 |
211 |
212 | I messed up and want to start over! How do I do that?
213 |
214 | **Do NOT delete your repository from GitHub!** Instead, commit _frequently_ as you work. Make a commit whenever you achieve _anything_ and the app isn't crashing in Chrome. This in practice creates restore points you can use should you wreak havoc with your app. If you find yourself in a mess, use `git reset --hard` to simply discard all changes to your code since your last commit. If you are dead-set on restarting the challenge from scratch, you can do this with Git as well, but it is advised that you request assistance from Staff.
215 |
216 |
217 |
218 |
219 | Why are there so many files in this project?
220 |
221 | Although a small, "old-fashioned" website might be made of just HTML, CSS and JS files, these days we mostly manage projects with Node and its package manager, NPM. Node apps typically have a `package.json` file and several other configuration files placed at the root of the project. This project also includes automated tests and a web server, which adds a little bit of extra complexity and files.
222 |
223 |
224 |
225 |
226 | Is this how React projects are normally organized?
227 |
228 | React projects can be organized in a million ways, there aren't many standards. Some developers like it like this, while others prefer to use opinionated frameworks, which do a lot of magic but prescribe that folders and files be structured and named just so.
229 |
230 |
231 |
232 |
233 | What are the package.json and package-lock.json files?
234 |
235 | The `package.json` file contains meta-information about the project like its version number, scripts that the developer can execute, and a list of the dependencies that are downloaded when you execute `npm install`. There can be some wiggle room to allow newer versions of the dependencies to be installed, so the `package-lock.json` file, when present, makes sure the exact same versions of everything are used every time the project is installed from scratch.
236 |
237 |
238 |
239 |
240 | What is the .eslintrc.js file?
241 |
242 | This file works in combination with the Eslint extension for VSCode to highlight syntax errors and problems in your code. By editing this file you can customize your linting rules.
243 |
244 |
245 |
246 |
247 | What is Jest?
248 |
249 | Jest is a framework that allows you to write tests and execute them, to alert you very quickly of problems with the code. Jest can do in seconds what an entire Quality Assurance team would take hours or even days. In the context of the Sprint Challenge, Jest is used to check your code against specification and give you a grade (% of tests passing).
250 |
251 |
252 |
253 | **Project created with [@bloomtools/react@0.1.7](https://github.com/bloominstituteoftechnology/npm-tools-react) and Node v18.16.0 on Thu, July 06, 2023 at 03:27 PM**
254 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | const PLUGIN_TRANSFORM_RUNTIME = '@babel/plugin-transform-runtime'
2 | const PLUGIN_STYLED_COMPONENTS = 'babel-plugin-styled-components'
3 |
4 | const PRESET_REACT = '@babel/preset-react'
5 | const PRESET_ENV = '@babel/preset-env'
6 |
7 | module.exports = {
8 | env: {
9 | testing: {
10 | plugins: [
11 | [PLUGIN_TRANSFORM_RUNTIME],
12 | ],
13 | presets: [
14 | [PRESET_REACT],
15 | [PRESET_ENV, { modules: 'commonjs', debug: false }]
16 | ]
17 | },
18 | development: {
19 | plugins: [
20 | [PLUGIN_STYLED_COMPONENTS],
21 | ],
22 | presets: [
23 | [PRESET_REACT],
24 | [PRESET_ENV, { targets: { chrome: '110' } }]
25 | ]
26 | },
27 | production: {
28 | plugins: [
29 | [PLUGIN_STYLED_COMPONENTS],
30 | ],
31 | presets: [
32 | [PRESET_REACT],
33 | [PRESET_ENV, { targets: { chrome: '110' } }]
34 | ]
35 | },
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/backend/data/people.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | "id": 18,
4 | "name": "Luke Skywalker",
5 | "birth_year": "19BBY",
6 | "created": "2014-12-09T13:50:51.644000Z",
7 | "edited": "2014-12-20T21:17:56.891000Z",
8 | "eye_color": "blue",
9 | "gender": "male",
10 | "hair_color": "blond",
11 | "height": "172",
12 | "mass": "77",
13 | "skin_color": "fair",
14 | "homeworld": 31
15 | },
16 | {
17 | "id": 52,
18 | "name": "C-3PO",
19 | "birth_year": "112BBY",
20 | "created": "2014-12-10T15:10:51.357000Z",
21 | "edited": "2014-12-20T21:17:50.309000Z",
22 | "eye_color": "yellow",
23 | "gender": "n/a",
24 | "hair_color": "n/a",
25 | "height": "167",
26 | "mass": "75",
27 | "skin_color": "gold",
28 | "homeworld": 31
29 | },
30 | {
31 | "id": 71,
32 | "name": "R2-D2",
33 | "birth_year": "33BBY",
34 | "created": "2014-12-10T15:11:50.376000Z",
35 | "edited": "2014-12-20T21:17:50.311000Z",
36 | "eye_color": "red",
37 | "gender": "n/a",
38 | "hair_color": "n/a",
39 | "height": "96",
40 | "mass": "32",
41 | "skin_color": "white, blue",
42 | "homeworld": 28
43 | },
44 | {
45 | "id": 7,
46 | "name": "Darth Vader",
47 | "birth_year": "41.9BBY",
48 | "created": "2014-12-10T15:18:20.704000Z",
49 | "edited": "2014-12-20T21:17:50.313000Z",
50 | "eye_color": "yellow",
51 | "gender": "male",
52 | "hair_color": "none",
53 | "height": "202",
54 | "mass": "136",
55 | "skin_color": "white",
56 | "homeworld": 31
57 | },
58 | {
59 | "id": 65,
60 | "name": "Leia Organa",
61 | "birth_year": "19BBY",
62 | "created": "2014-12-10T15:20:09.791000Z",
63 | "edited": "2014-12-20T21:17:50.315000Z",
64 | "eye_color": "brown",
65 | "gender": "female",
66 | "hair_color": "brown",
67 | "height": "150",
68 | "mass": "49",
69 | "skin_color": "light",
70 | "homeworld": 12
71 | },
72 | {
73 | "id": 88,
74 | "name": "Owen Lars",
75 | "birth_year": "52BBY",
76 | "created": "2014-12-10T15:52:14.024000Z",
77 | "edited": "2014-12-20T21:17:50.317000Z",
78 | "eye_color": "blue",
79 | "gender": "male",
80 | "hair_color": "brown, grey",
81 | "height": "178",
82 | "mass": "120",
83 | "skin_color": "light",
84 | "homeworld": 31
85 | },
86 | {
87 | "id": 82,
88 | "name": "Beru Whitesun Lars",
89 | "birth_year": "47BBY",
90 | "created": "2014-12-10T15:53:41.121000Z",
91 | "edited": "2014-12-20T21:17:50.319000Z",
92 | "eye_color": "blue",
93 | "gender": "female",
94 | "hair_color": "brown",
95 | "height": "165",
96 | "mass": "75",
97 | "skin_color": "light",
98 | "homeworld": 31
99 | },
100 | {
101 | "id": 47,
102 | "name": "R5-D4",
103 | "birth_year": "unknown",
104 | "created": "2014-12-10T15:57:50.959000Z",
105 | "edited": "2014-12-20T21:17:50.321000Z",
106 | "eye_color": "red",
107 | "gender": "n/a",
108 | "hair_color": "n/a",
109 | "height": "97",
110 | "mass": "32",
111 | "skin_color": "white, red",
112 | "homeworld": 93
113 | },
114 | {
115 | "id": 39,
116 | "name": "Yoda",
117 | "birth_year": "896BBY",
118 | "created": "2014-12-10T15:59:50.509000Z",
119 | "edited": "2014-12-20T21:17:50.323000Z",
120 | "eye_color": "brown",
121 | "gender": "male",
122 | "hair_color": "white",
123 | "height": "66",
124 | "skin_color": "green",
125 | "homeworld": 5
126 | }
127 | ]
128 |
--------------------------------------------------------------------------------
/backend/data/planets.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | "id": 31,
4 | "name": "Tatooine",
5 | "climate": "arid",
6 | "created": "2014-12-09T13:50:49.641000Z",
7 | "diameter": "10465",
8 | "edited": "2014-12-20T20:58:18.411000Z",
9 | "gravity": "1 standard",
10 | "orbital_period": "304",
11 | "population": "200000",
12 | "rotation_period": "23",
13 | "surface_water": "1",
14 | "terrain": "desert"
15 | },
16 | {
17 | "id": 12,
18 | "name": "Alderaan",
19 | "climate": "temperate",
20 | "created": "2014-12-10T11:35:48.479000Z",
21 | "diameter": "12500",
22 | "edited": "2014-12-20T20:58:18.420000Z",
23 | "gravity": "1 standard",
24 | "orbital_period": "364",
25 | "population": "2000000000",
26 | "rotation_period": "24",
27 | "surface_water": "40",
28 | "terrain": "grasslands, mountains"
29 | },
30 | {
31 | "id": 38,
32 | "name": "Yavin IV",
33 | "climate": "temperate, tropical",
34 | "created": "2014-12-10T11:37:19.144000Z",
35 | "diameter": "10200",
36 | "edited": "2014-12-20T20:58:18.421000Z",
37 | "gravity": "1 standard",
38 | "orbital_period": "4818",
39 | "population": "1000",
40 | "rotation_period": "24",
41 | "surface_water": "8",
42 | "terrain": "jungle, rainforests"
43 | },
44 | {
45 | "id": 19,
46 | "name": "Hoth",
47 | "climate": "frozen",
48 | "created": "2014-12-10T11:39:13.934000Z",
49 | "diameter": "7200",
50 | "edited": "2014-12-20T20:58:18.423000Z",
51 | "gravity": "1.1 standard",
52 | "orbital_period": "549",
53 | "population": "unknown",
54 | "rotation_period": "23",
55 | "surface_water": "100",
56 | "terrain": "tundra, ice caves, mountain ranges"
57 | },
58 | {
59 | "id": 5,
60 | "name": "Dagobah",
61 | "climate": "murky",
62 | "created": "2014-12-10T11:42:22.590000Z",
63 | "diameter": "8900",
64 | "edited": "2014-12-20T20:58:18.425000Z",
65 | "gravity": "N/A",
66 | "orbital_period": "341",
67 | "population": "unknown",
68 | "rotation_period": "23",
69 | "surface_water": "8",
70 | "terrain": "swamp, jungles"
71 | },
72 | {
73 | "id": 84,
74 | "name": "Bespin",
75 | "climate": "temperate",
76 | "created": "2014-12-10T11:43:55.240000Z",
77 | "diameter": "118000",
78 | "edited": "2014-12-20T20:58:18.427000Z",
79 | "gravity": "1.5 (surface), 1 standard (Cloud City)",
80 | "orbital_period": "5110",
81 | "population": "6000000",
82 | "rotation_period": "12",
83 | "surface_water": "0",
84 | "terrain": "gas giant"
85 | },
86 | {
87 | "id": 54,
88 | "name": "Endor",
89 | "climate": "temperate",
90 | "created": "2014-12-10T11:50:29.349000Z",
91 | "diameter": "4900",
92 | "edited": "2014-12-20T20:58:18.429000Z",
93 | "gravity": "0.85 standard",
94 | "orbital_period": "402",
95 | "population": "30000000",
96 | "rotation_period": "18",
97 | "surface_water": "8",
98 | "terrain": "forests, mountains, lakes"
99 | },
100 | {
101 | "id": 28,
102 | "name": "Naboo",
103 | "climate": "temperate",
104 | "created": "2014-12-10T11:52:31.066000Z",
105 | "diameter": "12120",
106 | "edited": "2014-12-20T20:58:18.430000Z",
107 | "gravity": "1 standard",
108 | "orbital_period": "312",
109 | "population": "4500000000",
110 | "rotation_period": "26",
111 | "surface_water": "12",
112 | "terrain": "grassy hills, swamps, forests, mountains"
113 | },
114 | {
115 | "id": 93,
116 | "name": "Coruscant",
117 | "climate": "temperate",
118 | "created": "2014-12-10T11:54:13.921000Z",
119 | "diameter": "12240",
120 | "edited": "2014-12-20T20:58:18.432000Z",
121 | "gravity": "1 standard",
122 | "orbital_period": "368",
123 | "population": "1000000000000",
124 | "rotation_period": "26",
125 | "surface_water": "12",
126 | "terrain": "grassy hills, swamps, forests, mountains"
127 | }
128 | ]
129 |
--------------------------------------------------------------------------------
/backend/helpers.js:
--------------------------------------------------------------------------------
1 | const people = require('./data/people')
2 | const planets = require('./data/planets')
3 |
4 | const getPeople = () => people
5 | const getPlanets = () => planets
6 |
7 | module.exports = {
8 | getPeople,
9 | getPlanets,
10 | }
11 |
--------------------------------------------------------------------------------
/backend/mock-server.js:
--------------------------------------------------------------------------------
1 | const { setupServer } = require('msw/node')
2 | const { rest } = require('msw')
3 | const StarWars = require('./helpers')
4 |
5 | function getPeople(req, res, ctx) {
6 | return res(
7 | ctx.json(StarWars.getPeople()),
8 | )
9 | }
10 |
11 | function getPlanets(req, res, ctx) {
12 | return res(
13 | ctx.json(StarWars.getPlanets()),
14 | )
15 | }
16 |
17 | const handlers = [
18 | rest.get('http://localhost:9009/api/people', getPeople),
19 | rest.get('http://localhost:9009/api/planets', getPlanets),
20 | ]
21 |
22 | module.exports = setupServer(...handlers)
23 |
--------------------------------------------------------------------------------
/backend/routers/starwars.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const StarWars = require('../helpers')
3 |
4 | const router = express.Router()
5 |
6 | router.get('/people', (req, res) => {
7 | res.json(StarWars.getPeople())
8 | })
9 |
10 | router.get('/planets', (req, res) => {
11 | res.json(StarWars.getPlanets())
12 | })
13 |
14 | module.exports = router
15 |
--------------------------------------------------------------------------------
/backend/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const cors = require('cors')
3 | const path = require('path')
4 | const starWarsRouter = require('./routers/starwars')
5 |
6 | const PORT = process.env.PORT || 9009
7 |
8 | const server = express()
9 |
10 | server.use(express.json())
11 |
12 | server.use(express.static(path.join(__dirname, '../dist')))
13 |
14 | server.use(cors())
15 |
16 | server.use('/api', starWarsRouter)
17 |
18 | server.get('*', (req, res) => {
19 | res.sendFile(path.join(__dirname, '../dist/index.html'))
20 | })
21 |
22 | server.use((req, res) => {
23 | res.status(404).json({
24 | message: `Endpoint [${req.method}] ${req.path} does not exist`,
25 | })
26 | })
27 |
28 | server.listen(PORT, () => {
29 | console.log(`listening on ${PORT}`)
30 | })
31 |
--------------------------------------------------------------------------------
/frontend/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es2021": true,
5 | "node": true,
6 | "jest": true
7 | },
8 | "extends": [
9 | "eslint:recommended",
10 | "plugin:react/recommended"
11 | ],
12 | "parserOptions": {
13 | "ecmaFeatures": {
14 | "jsx": true
15 | },
16 | "ecmaVersion": "latest",
17 | "sourceType": "module"
18 | },
19 | "plugins": [
20 | "react"
21 | ],
22 | "rules": {
23 | "react/prop-types": 0
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/frontend/components/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import axios from 'axios'
3 | import Character from './Character'
4 |
5 | const urlPlanets = 'http://localhost:9009/api/planets'
6 | const urlPeople = 'http://localhost:9009/api/people'
7 |
8 | function App() {
9 | // ❗ Create state to hold the data from the API
10 | // ❗ Create effects to fetch the data and put it in state
11 | return (
12 |
13 |
Star Wars Characters
14 |
See the README of the project for instructions on completing this challenge
15 | {/* ❗ Map over the data in state, rendering a Character at each iteration */}
16 |
17 | )
18 | }
19 |
20 | export default App
21 |
22 | // ❗ DO NOT CHANGE THE CODE BELOW
23 | if (typeof module !== 'undefined' && module.exports) module.exports = App
24 |
--------------------------------------------------------------------------------
/frontend/components/Character.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | function Character() { // ❗ Add the props
4 | // ❗ Create a state to hold whether the homeworld is rendering or not
5 | // ❗ Create a "toggle" click handler to show or remove the homeworld
6 | return (
7 |
8 | {/* Use the same markup with the same attributes as in the mock */}
9 |