├── .gitignore
├── README.md
├── package.json
├── public
├── _redirects
├── favicon.ico
├── index.html
└── robots.txt
├── src
├── components
│ ├── App.js
│ └── TeamLogo.js
├── hooks
│ ├── useArticle.js
│ ├── useFetch.js
│ ├── usePlayers.js
│ ├── useTeam.js
│ ├── useTeamNames.js
│ └── useTeamsArticles.js
├── index.css
└── index.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 | ### Info
13 |
14 | This is the repository for [UI.dev](https://ui.dev)'s "React Router v5" course project.
15 |
16 | For more information on the course, visit __[ui.dev/react-router-v5](https://ui.dev/react-router-v5/)__.
17 |
18 | ### Project
19 |
20 | This project is an app for a fictional Basketball league.
21 |
22 | You can view the final project __[here](https://basketball-v5.ui.dev/)__
23 |
24 | ### Branches
25 |
26 | Every `(Project)` video in the course coincides with a branch in this repo. If you want to compare your code with Tyler's or you just want to play around with the code, check out the different branches.
27 |
28 | Below every `(Project)` video in the course will be a direct link to both the commit for that video as well as its branch.
29 |
30 |
31 |
32 | ### Project Preview
33 |
34 | 
35 |
36 | 
37 |
38 | 
39 |
40 | 
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hash-history-basketball-league-v5",
3 | "version": "1.0.0",
4 | "dependencies": {
5 | "react": "^16.13.1",
6 | "react-dom": "^16.13.1",
7 | "react-scripts": "3.4.1"
8 | },
9 | "scripts": {
10 | "start": "react-scripts start",
11 | "build": "react-scripts build",
12 | "eject": "react-scripts eject"
13 | },
14 | "eslintConfig": {
15 | "extends": "react-app"
16 | },
17 | "browserslist": {
18 | "production": [
19 | ">0.2%",
20 | "not dead",
21 | "not op_mini all"
22 | ],
23 | "development": [
24 | "last 1 chrome version",
25 | "last 1 firefox version",
26 | "last 1 safari version"
27 | ]
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/public/_redirects:
--------------------------------------------------------------------------------
1 | /* /index.html 200
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/uidotdev/react-router-v5-course/e67171e23c9c7b423ba9b484f3500aacbf08357c/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
26 | Hash History Basketball League
27 |
28 |
29 |
30 |
31 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/components/App.js:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 |
3 | export default function App () {
4 | return (
5 |
6 | Hash History Basketball League
7 |
8 | )
9 | }
--------------------------------------------------------------------------------
/src/components/TeamLogo.js:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 |
3 | const logos = {
4 | bulls: ,
5 | foxes: ,
6 | hedgehogs: ,
7 | koalas: ,
8 | lemurs: ,
9 | }
10 |
11 | export default function TeamLogo ({ width='200px ', id, ...rest}) {
12 | return (
13 |
16 | )
17 | }
--------------------------------------------------------------------------------
/src/hooks/useArticle.js:
--------------------------------------------------------------------------------
1 | import useFetch from './useFetch'
2 |
3 | export default function useArticle (args) {
4 | return useFetch('/article', 'POST', JSON.stringify(args))
5 | }
--------------------------------------------------------------------------------
/src/hooks/useFetch.js:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 |
3 | const API = 'https://api.ui.dev/hash-history-basketball-league'
4 |
5 | export default function useFetch (path, method, body='') {
6 | const [response, setResponse] = React.useState(null)
7 | const [loading, setLoading] = React.useState(true)
8 |
9 | React.useEffect(() => {
10 | const abortController = new AbortController()
11 | const signal = abortController.signal
12 |
13 | fetch(`${API}${path}`, {
14 | method,
15 | ...(body ? { body } : {}),
16 | headers: {
17 | 'Accept': 'application/json',
18 | 'Content-Type': 'application/json',
19 | },
20 | })
21 | .then((res) => res.json())
22 | .then(({ body }) => body ? JSON.parse(body) : null)
23 | .then((data) => {
24 | if (!signal.aborted) {
25 | setResponse(data)
26 | setLoading(false)
27 | }
28 | })
29 | .catch((error) => console.warn('Uh-oh.', error))
30 |
31 | return () => abortController.abort()
32 | }, [path, method, body])
33 |
34 | return { response, loading }
35 | }
36 |
--------------------------------------------------------------------------------
/src/hooks/usePlayers.js:
--------------------------------------------------------------------------------
1 | import useFetch from './useFetch'
2 |
3 | export default function usePlayers (team) {
4 | return useFetch('/players', 'POST', JSON.stringify({ team }))
5 | }
--------------------------------------------------------------------------------
/src/hooks/useTeam.js:
--------------------------------------------------------------------------------
1 | import useFetch from './useFetch'
2 |
3 | export default function useTeam (team) {
4 | return useFetch('/team', 'POST', JSON.stringify({ team }))
5 | }
--------------------------------------------------------------------------------
/src/hooks/useTeamNames.js:
--------------------------------------------------------------------------------
1 | import useFetch from './useFetch'
2 |
3 | export default function useTeamNames () {
4 | return useFetch('/teams', 'GET')
5 | }
--------------------------------------------------------------------------------
/src/hooks/useTeamsArticles.js:
--------------------------------------------------------------------------------
1 | import useFetch from './useFetch'
2 |
3 | export default function useTeamsArticles (team) {
4 | return useFetch('/articles', 'POST', JSON.stringify({ team }))
5 | }
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @import url("https://ui.dev/font");
2 |
3 | :root {
4 | --black: #000;
5 | --white: #fff;
6 | --red: #f32827;
7 | --purple: #a42ce9;
8 | --blue: #2d7fea;
9 | --yellow: #f4f73e;
10 | --pink: #eb30c1;
11 | --gold: #ffd500;
12 | --aqua: #2febd2;
13 | --gray: #282c35;
14 | }
15 |
16 | *,
17 | *:before,
18 | *:after {
19 | box-sizing: inherit;
20 | }
21 |
22 | html {
23 | font-family: proxima-nova, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen-Sans, Ubuntu, Cantarell, Helvetica Neue, sans-serif;
24 | text-rendering: optimizeLegibility;
25 | -webkit-font-smoothing: antialiased;
26 | -moz-osx-font-smoothing: grayscale;
27 | box-sizing: border-box;
28 | font-size: 18px;
29 | }
30 |
31 | body {
32 | margin: 0;
33 | padding: 0;
34 | min-height: 100vh;
35 | background: var(--black);
36 | color: var(--white);
37 | }
38 |
39 | a {
40 | color: var(--gold);
41 | text-decoration: none;
42 | }
43 |
44 | li {
45 | list-style-type: none;
46 | }
47 |
48 | .border-container {
49 | padding: 0;
50 | margin: 0;
51 | display: flex;
52 | margin-bottom: 20px;
53 | }
54 |
55 | .border-item {
56 | width: 20vw;
57 | height: 12px;
58 | }
59 |
60 | .container {
61 | position: relative;
62 | max-width: 1000px;
63 | min-width: 280px;
64 | margin: 0 auto;
65 | padding: 20px;
66 | }
67 |
68 | .large-header {
69 | font-size: 105px;
70 | text-align: center;
71 | font-weight: 900;
72 | }
73 |
74 | .medium-header {
75 | font-size: 55px;
76 | text-align: center;
77 | margin: 20px 0;
78 | font-weight: 900;
79 | }
80 |
81 | .header {
82 | margin: 20px 0;
83 | font-weight: 300;
84 | font-size: 35px;
85 | font-weight: 900;
86 | }
87 |
88 | .center {
89 | display: block;
90 | margin: 0 auto;
91 | }
92 |
93 | .text-center {
94 | text-align: center;
95 | }
96 |
97 | .row {
98 | display: flex;
99 | justify-content: space-around;
100 | }
101 |
102 | .home-grid {
103 | display: flex;
104 | flex-direction: row;
105 | justify-content: center;
106 | }
107 |
108 | .navbar {
109 | font-size: 16px;
110 | text-transform: uppercase;
111 | flex: 1;
112 | display: flex;
113 | justify-content: space-between;
114 | font-weight: bold;
115 | }
116 |
117 | .nav-links a {
118 | padding: 0 10px;
119 | }
120 |
121 | .two-column {
122 | display: flex;
123 | }
124 |
125 | .panel {
126 | flex: 1;
127 | display: flex;
128 | flex-direction: column;
129 | align-items: center;
130 | justify-content: center;
131 | max-width: 1200px;
132 | margin: 0 auto;
133 | }
134 |
135 | .avatar {
136 | width: 200px;
137 | border-radius: 100px;
138 | }
139 |
140 | .info-list {
141 | margin-bottom: 30px;
142 | padding: 0;
143 | }
144 |
145 | .info-list li {
146 | font-size: 33px;
147 | margin: 20px 0;
148 | font-weight: 700;
149 | }
150 |
151 | .info-list div {
152 | font-size: 24px;
153 | font-weight: 300;
154 | }
155 |
156 | .sidebar-list {
157 | display: flex;
158 | padding: 10px;
159 | flex-direction: column;
160 | padding: 0;
161 | }
162 |
163 | .sidebar-list a {
164 | line-height: 25px;
165 | }
166 |
167 | .sidebar-instruction {
168 | display: flex;
169 | justify-content: center;
170 | align-items: center;
171 | flex: 1;
172 | font-weight: 900;
173 | font-size: 22px;
174 | }
175 |
176 | .articles {
177 | margin: 0;
178 | padding: 0;
179 | }
180 |
181 | .articles li {
182 | font-size: 28px;
183 | margin-bottom: 30px;
184 | cursor: pointer;
185 | }
186 |
187 | .article {
188 | margin: 20px;
189 | padding: 20px;
190 | }
191 |
192 | .article p {
193 | line-height: 35px;
194 | font-size: 20px;
195 | }
196 |
197 | .article-title {
198 | margin: 0;
199 | font-weight: 300;
200 | }
201 |
202 | .article-date {
203 | font-size: 14px;
204 | padding: 0;
205 | }
206 |
207 | .btn-main {
208 | max-width: 240px;
209 | min-width: 150px;
210 | padding: 10px;
211 | border: 2px solid black;
212 | color: var(--white);
213 | text-align: center;
214 | background: var(--pink);
215 | font-weight: 900;
216 | font-size: 15px;
217 | }
218 |
219 | .btn-main:hover {
220 | cursor: pointer;
221 | font-weight: 500;
222 | }
223 |
224 | .championships {
225 | display: flex;
226 | margin: 0;
227 | padding: 0;
228 | }
229 |
230 | .championships li {
231 | margin: 0 10px;
232 | }
233 |
234 | @media (max-width: 680px) {
235 | .container {
236 | padding: 8px;
237 | }
238 |
239 | .large-header {
240 | font-size: 60px;
241 | }
242 |
243 | .medium-header {
244 | font-size: 40px;
245 | }
246 |
247 | .header {
248 | font-size: 25px;
249 | margin: 10px 0;
250 | }
251 |
252 | .avatar {
253 | width: 125px;
254 | border-radius: 62.5px;
255 | }
256 |
257 | .home-grid {
258 | flex-direction: column;
259 | align-items: center;
260 | }
261 |
262 | .info-list li {
263 | font-size: 20px;
264 | }
265 |
266 | .info-list div {
267 | font-size: 16px;
268 | }
269 | .championships li {
270 | font-size: 12px;
271 | }
272 |
273 | .sidebar-list {
274 | font-size: 12px;
275 | }
276 |
277 | .article {
278 | padding: 5px;
279 | margin: 5px;
280 | }
281 |
282 | .article-title {
283 | font-size: 16px;
284 | }
285 | }
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import * as ReactDOM from 'react-dom'
3 | import './index.css'
4 | import App from './components/App'
5 |
6 | function ColorfulBorder() {
7 | return (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | )
16 | }
17 |
18 | ReactDOM.render(
19 |
20 |
21 |
22 | ,
23 | document.getElementById('root')
24 | )
--------------------------------------------------------------------------------