diff --git a/code/01-starting-project/package.json b/code/01-starting-project/package.json new file mode 100644 index 0000000000..edaa0ea9e5 --- /dev/null +++ b/code/01-starting-project/package.json @@ -0,0 +1,38 @@ +{ + "name": "react-complete-guide", + "version": "0.1.0", + "private": true, + "dependencies": { + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^13.5.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-scripts": "5.0.1", + "web-vitals": "^2.1.4" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/code/01-starting-project/public/favicon.ico b/code/01-starting-project/public/favicon.ico new file mode 100644 index 0000000000..a11777cc47 Binary files /dev/null and b/code/01-starting-project/public/favicon.ico differ diff --git a/code/01-starting-project/public/index.html b/code/01-starting-project/public/index.html new file mode 100644 index 0000000000..aa069f27cb --- /dev/null +++ b/code/01-starting-project/public/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + React App + + + +
+ + + diff --git a/code/01-starting-project/public/logo192.png b/code/01-starting-project/public/logo192.png new file mode 100644 index 0000000000..fc44b0a379 Binary files /dev/null and b/code/01-starting-project/public/logo192.png differ diff --git a/code/01-starting-project/public/logo512.png b/code/01-starting-project/public/logo512.png new file mode 100644 index 0000000000..a4e47a6545 Binary files /dev/null and b/code/01-starting-project/public/logo512.png differ diff --git a/code/01-starting-project/public/manifest.json b/code/01-starting-project/public/manifest.json new file mode 100644 index 0000000000..080d6c77ac --- /dev/null +++ b/code/01-starting-project/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/code/01-starting-project/public/robots.txt b/code/01-starting-project/public/robots.txt new file mode 100644 index 0000000000..e9e57dc4d4 --- /dev/null +++ b/code/01-starting-project/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/code/01-starting-project/src/App.js b/code/01-starting-project/src/App.js new file mode 100644 index 0000000000..6f74d94b5d --- /dev/null +++ b/code/01-starting-project/src/App.js @@ -0,0 +1,5 @@ +function App() { + return
; +} + +export default App; diff --git a/code/01-starting-project/src/index.css b/code/01-starting-project/src/index.css new file mode 100644 index 0000000000..70bfa7fb2a --- /dev/null +++ b/code/01-starting-project/src/index.css @@ -0,0 +1,50 @@ +* { + box-sizing: border-box; +} + +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + + --color-gray-100: #f4f3f1; + --color-gray-200: #dddbd8; + --color-gray-300: #ccc9c6; + --color-gray-400: #aeaba7; + --color-gray-500: #8a8784; + --color-gray-600: #656360; + --color-gray-700: #4b4a47; + --color-gray-800: #31302e; + --color-gray-900: #1f1d1b; + + --color-primary-100: #fcf3e1; + --color-primary-200: #fceccd; + --color-primary-300: #fae1af; + --color-primary-400: #fbd997; + --color-primary-500: #ffd37c; + --color-primary-600: #f9c762; + --color-primary-700: #fbc14d; + --color-primary-800: #fab833; + --color-primary-900: #f6ad1b; +} + +body { + margin: 0; +} + +ul { + list-style: none; + margin: 0; + padding: 0; +} diff --git a/code/01-starting-project/src/index.js b/code/01-starting-project/src/index.js new file mode 100644 index 0000000000..09de846062 --- /dev/null +++ b/code/01-starting-project/src/index.js @@ -0,0 +1,12 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; + +import './index.css'; +import App from './App'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + + + +); diff --git a/code/02-route-setup/package.json b/code/02-route-setup/package.json new file mode 100644 index 0000000000..eafe171a6b --- /dev/null +++ b/code/02-route-setup/package.json @@ -0,0 +1,39 @@ +{ + "name": "react-complete-guide", + "version": "0.1.0", + "private": true, + "dependencies": { + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^13.5.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.4.1", + "react-scripts": "5.0.1", + "web-vitals": "^2.1.4" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/code/02-route-setup/public/favicon.ico b/code/02-route-setup/public/favicon.ico new file mode 100644 index 0000000000..a11777cc47 Binary files /dev/null and b/code/02-route-setup/public/favicon.ico differ diff --git a/code/02-route-setup/public/index.html b/code/02-route-setup/public/index.html new file mode 100644 index 0000000000..aa069f27cb --- /dev/null +++ b/code/02-route-setup/public/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + React App + + + +
+ + + diff --git a/code/02-route-setup/public/logo192.png b/code/02-route-setup/public/logo192.png new file mode 100644 index 0000000000..fc44b0a379 Binary files /dev/null and b/code/02-route-setup/public/logo192.png differ diff --git a/code/02-route-setup/public/logo512.png b/code/02-route-setup/public/logo512.png new file mode 100644 index 0000000000..a4e47a6545 Binary files /dev/null and b/code/02-route-setup/public/logo512.png differ diff --git a/code/02-route-setup/public/manifest.json b/code/02-route-setup/public/manifest.json new file mode 100644 index 0000000000..080d6c77ac --- /dev/null +++ b/code/02-route-setup/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/code/02-route-setup/public/robots.txt b/code/02-route-setup/public/robots.txt new file mode 100644 index 0000000000..e9e57dc4d4 --- /dev/null +++ b/code/02-route-setup/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/code/02-route-setup/src/App.js b/code/02-route-setup/src/App.js new file mode 100644 index 0000000000..82e1a0f0fe --- /dev/null +++ b/code/02-route-setup/src/App.js @@ -0,0 +1,13 @@ +import { createBrowserRouter, RouterProvider } from 'react-router-dom'; + +import HomePage from './pages/Home'; + +const router = createBrowserRouter([ + { path: '/', element: }, +]); + +function App() { + return ; +} + +export default App; diff --git a/code/02-route-setup/src/index.css b/code/02-route-setup/src/index.css new file mode 100644 index 0000000000..70bfa7fb2a --- /dev/null +++ b/code/02-route-setup/src/index.css @@ -0,0 +1,50 @@ +* { + box-sizing: border-box; +} + +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + + --color-gray-100: #f4f3f1; + --color-gray-200: #dddbd8; + --color-gray-300: #ccc9c6; + --color-gray-400: #aeaba7; + --color-gray-500: #8a8784; + --color-gray-600: #656360; + --color-gray-700: #4b4a47; + --color-gray-800: #31302e; + --color-gray-900: #1f1d1b; + + --color-primary-100: #fcf3e1; + --color-primary-200: #fceccd; + --color-primary-300: #fae1af; + --color-primary-400: #fbd997; + --color-primary-500: #ffd37c; + --color-primary-600: #f9c762; + --color-primary-700: #fbc14d; + --color-primary-800: #fab833; + --color-primary-900: #f6ad1b; +} + +body { + margin: 0; +} + +ul { + list-style: none; + margin: 0; + padding: 0; +} diff --git a/code/02-route-setup/src/index.js b/code/02-route-setup/src/index.js new file mode 100644 index 0000000000..09de846062 --- /dev/null +++ b/code/02-route-setup/src/index.js @@ -0,0 +1,12 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; + +import './index.css'; +import App from './App'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + + + +); diff --git a/code/02-route-setup/src/pages/Home.js b/code/02-route-setup/src/pages/Home.js new file mode 100644 index 0000000000..e101a222ae --- /dev/null +++ b/code/02-route-setup/src/pages/Home.js @@ -0,0 +1,5 @@ +function HomePage() { + return

My Home Page

; +} + +export default HomePage; diff --git a/code/03-alternative-route-definitions/package.json b/code/03-alternative-route-definitions/package.json new file mode 100644 index 0000000000..eafe171a6b --- /dev/null +++ b/code/03-alternative-route-definitions/package.json @@ -0,0 +1,39 @@ +{ + "name": "react-complete-guide", + "version": "0.1.0", + "private": true, + "dependencies": { + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^13.5.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.4.1", + "react-scripts": "5.0.1", + "web-vitals": "^2.1.4" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/code/03-alternative-route-definitions/public/favicon.ico b/code/03-alternative-route-definitions/public/favicon.ico new file mode 100644 index 0000000000..a11777cc47 Binary files /dev/null and b/code/03-alternative-route-definitions/public/favicon.ico differ diff --git a/code/03-alternative-route-definitions/public/index.html b/code/03-alternative-route-definitions/public/index.html new file mode 100644 index 0000000000..aa069f27cb --- /dev/null +++ b/code/03-alternative-route-definitions/public/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + React App + + + +
+ + + diff --git a/code/03-alternative-route-definitions/public/logo192.png b/code/03-alternative-route-definitions/public/logo192.png new file mode 100644 index 0000000000..fc44b0a379 Binary files /dev/null and b/code/03-alternative-route-definitions/public/logo192.png differ diff --git a/code/03-alternative-route-definitions/public/logo512.png b/code/03-alternative-route-definitions/public/logo512.png new file mode 100644 index 0000000000..a4e47a6545 Binary files /dev/null and b/code/03-alternative-route-definitions/public/logo512.png differ diff --git a/code/03-alternative-route-definitions/public/manifest.json b/code/03-alternative-route-definitions/public/manifest.json new file mode 100644 index 0000000000..080d6c77ac --- /dev/null +++ b/code/03-alternative-route-definitions/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/code/03-alternative-route-definitions/public/robots.txt b/code/03-alternative-route-definitions/public/robots.txt new file mode 100644 index 0000000000..e9e57dc4d4 --- /dev/null +++ b/code/03-alternative-route-definitions/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/code/03-alternative-route-definitions/src/App.js b/code/03-alternative-route-definitions/src/App.js new file mode 100644 index 0000000000..e072a1554f --- /dev/null +++ b/code/03-alternative-route-definitions/src/App.js @@ -0,0 +1,29 @@ +import { + createBrowserRouter, + // createRoutesFromElements, + RouterProvider, + // Route, +} from 'react-router-dom'; + +import HomePage from './pages/Home'; +import ProductsPage from './pages/Products'; + +// const routeDefinitions = createRoutesFromElements( +// +// } /> +// } /> +// +// ); + +const router = createBrowserRouter([ + { path: '/', element: }, + { path: '/products', element: }, +]); + +// const router = createBrowserRouter(routeDefinitions); + +function App() { + return ; +} + +export default App; diff --git a/code/03-alternative-route-definitions/src/index.css b/code/03-alternative-route-definitions/src/index.css new file mode 100644 index 0000000000..70bfa7fb2a --- /dev/null +++ b/code/03-alternative-route-definitions/src/index.css @@ -0,0 +1,50 @@ +* { + box-sizing: border-box; +} + +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + + --color-gray-100: #f4f3f1; + --color-gray-200: #dddbd8; + --color-gray-300: #ccc9c6; + --color-gray-400: #aeaba7; + --color-gray-500: #8a8784; + --color-gray-600: #656360; + --color-gray-700: #4b4a47; + --color-gray-800: #31302e; + --color-gray-900: #1f1d1b; + + --color-primary-100: #fcf3e1; + --color-primary-200: #fceccd; + --color-primary-300: #fae1af; + --color-primary-400: #fbd997; + --color-primary-500: #ffd37c; + --color-primary-600: #f9c762; + --color-primary-700: #fbc14d; + --color-primary-800: #fab833; + --color-primary-900: #f6ad1b; +} + +body { + margin: 0; +} + +ul { + list-style: none; + margin: 0; + padding: 0; +} diff --git a/code/03-alternative-route-definitions/src/index.js b/code/03-alternative-route-definitions/src/index.js new file mode 100644 index 0000000000..09de846062 --- /dev/null +++ b/code/03-alternative-route-definitions/src/index.js @@ -0,0 +1,12 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; + +import './index.css'; +import App from './App'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + + + +); diff --git a/code/03-alternative-route-definitions/src/pages/Home.js b/code/03-alternative-route-definitions/src/pages/Home.js new file mode 100644 index 0000000000..e101a222ae --- /dev/null +++ b/code/03-alternative-route-definitions/src/pages/Home.js @@ -0,0 +1,5 @@ +function HomePage() { + return

My Home Page

; +} + +export default HomePage; diff --git a/code/03-alternative-route-definitions/src/pages/Products.js b/code/03-alternative-route-definitions/src/pages/Products.js new file mode 100644 index 0000000000..6f2dd748d8 --- /dev/null +++ b/code/03-alternative-route-definitions/src/pages/Products.js @@ -0,0 +1,5 @@ +function ProductsPage() { + return

The Products Page

; +} + +export default ProductsPage; diff --git a/code/04-adding-links/package.json b/code/04-adding-links/package.json new file mode 100644 index 0000000000..eafe171a6b --- /dev/null +++ b/code/04-adding-links/package.json @@ -0,0 +1,39 @@ +{ + "name": "react-complete-guide", + "version": "0.1.0", + "private": true, + "dependencies": { + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^13.5.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.4.1", + "react-scripts": "5.0.1", + "web-vitals": "^2.1.4" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/code/04-adding-links/public/favicon.ico b/code/04-adding-links/public/favicon.ico new file mode 100644 index 0000000000..a11777cc47 Binary files /dev/null and b/code/04-adding-links/public/favicon.ico differ diff --git a/code/04-adding-links/public/index.html b/code/04-adding-links/public/index.html new file mode 100644 index 0000000000..aa069f27cb --- /dev/null +++ b/code/04-adding-links/public/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + React App + + + +
+ + + diff --git a/code/04-adding-links/public/logo192.png b/code/04-adding-links/public/logo192.png new file mode 100644 index 0000000000..fc44b0a379 Binary files /dev/null and b/code/04-adding-links/public/logo192.png differ diff --git a/code/04-adding-links/public/logo512.png b/code/04-adding-links/public/logo512.png new file mode 100644 index 0000000000..a4e47a6545 Binary files /dev/null and b/code/04-adding-links/public/logo512.png differ diff --git a/code/04-adding-links/public/manifest.json b/code/04-adding-links/public/manifest.json new file mode 100644 index 0000000000..080d6c77ac --- /dev/null +++ b/code/04-adding-links/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/code/04-adding-links/public/robots.txt b/code/04-adding-links/public/robots.txt new file mode 100644 index 0000000000..e9e57dc4d4 --- /dev/null +++ b/code/04-adding-links/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/code/04-adding-links/src/App.js b/code/04-adding-links/src/App.js new file mode 100644 index 0000000000..e072a1554f --- /dev/null +++ b/code/04-adding-links/src/App.js @@ -0,0 +1,29 @@ +import { + createBrowserRouter, + // createRoutesFromElements, + RouterProvider, + // Route, +} from 'react-router-dom'; + +import HomePage from './pages/Home'; +import ProductsPage from './pages/Products'; + +// const routeDefinitions = createRoutesFromElements( +// +// } /> +// } /> +// +// ); + +const router = createBrowserRouter([ + { path: '/', element: }, + { path: '/products', element: }, +]); + +// const router = createBrowserRouter(routeDefinitions); + +function App() { + return ; +} + +export default App; diff --git a/code/04-adding-links/src/index.css b/code/04-adding-links/src/index.css new file mode 100644 index 0000000000..70bfa7fb2a --- /dev/null +++ b/code/04-adding-links/src/index.css @@ -0,0 +1,50 @@ +* { + box-sizing: border-box; +} + +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + + --color-gray-100: #f4f3f1; + --color-gray-200: #dddbd8; + --color-gray-300: #ccc9c6; + --color-gray-400: #aeaba7; + --color-gray-500: #8a8784; + --color-gray-600: #656360; + --color-gray-700: #4b4a47; + --color-gray-800: #31302e; + --color-gray-900: #1f1d1b; + + --color-primary-100: #fcf3e1; + --color-primary-200: #fceccd; + --color-primary-300: #fae1af; + --color-primary-400: #fbd997; + --color-primary-500: #ffd37c; + --color-primary-600: #f9c762; + --color-primary-700: #fbc14d; + --color-primary-800: #fab833; + --color-primary-900: #f6ad1b; +} + +body { + margin: 0; +} + +ul { + list-style: none; + margin: 0; + padding: 0; +} diff --git a/code/04-adding-links/src/index.js b/code/04-adding-links/src/index.js new file mode 100644 index 0000000000..09de846062 --- /dev/null +++ b/code/04-adding-links/src/index.js @@ -0,0 +1,12 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; + +import './index.css'; +import App from './App'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + + + +); diff --git a/code/04-adding-links/src/pages/Home.js b/code/04-adding-links/src/pages/Home.js new file mode 100644 index 0000000000..adc0e53b5d --- /dev/null +++ b/code/04-adding-links/src/pages/Home.js @@ -0,0 +1,14 @@ +import { Link } from 'react-router-dom'; + +function HomePage() { + return ( + <> +

My Home Page

+

+ Go to the list of products. +

+ + ); +} + +export default HomePage; diff --git a/code/04-adding-links/src/pages/Products.js b/code/04-adding-links/src/pages/Products.js new file mode 100644 index 0000000000..6f2dd748d8 --- /dev/null +++ b/code/04-adding-links/src/pages/Products.js @@ -0,0 +1,5 @@ +function ProductsPage() { + return

The Products Page

; +} + +export default ProductsPage; diff --git a/code/05-layout-wrapper-route/package.json b/code/05-layout-wrapper-route/package.json new file mode 100644 index 0000000000..eafe171a6b --- /dev/null +++ b/code/05-layout-wrapper-route/package.json @@ -0,0 +1,39 @@ +{ + "name": "react-complete-guide", + "version": "0.1.0", + "private": true, + "dependencies": { + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^13.5.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.4.1", + "react-scripts": "5.0.1", + "web-vitals": "^2.1.4" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/code/05-layout-wrapper-route/public/favicon.ico b/code/05-layout-wrapper-route/public/favicon.ico new file mode 100644 index 0000000000..a11777cc47 Binary files /dev/null and b/code/05-layout-wrapper-route/public/favicon.ico differ diff --git a/code/05-layout-wrapper-route/public/index.html b/code/05-layout-wrapper-route/public/index.html new file mode 100644 index 0000000000..aa069f27cb --- /dev/null +++ b/code/05-layout-wrapper-route/public/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + React App + + + +
+ + + diff --git a/code/05-layout-wrapper-route/public/logo192.png b/code/05-layout-wrapper-route/public/logo192.png new file mode 100644 index 0000000000..fc44b0a379 Binary files /dev/null and b/code/05-layout-wrapper-route/public/logo192.png differ diff --git a/code/05-layout-wrapper-route/public/logo512.png b/code/05-layout-wrapper-route/public/logo512.png new file mode 100644 index 0000000000..a4e47a6545 Binary files /dev/null and b/code/05-layout-wrapper-route/public/logo512.png differ diff --git a/code/05-layout-wrapper-route/public/manifest.json b/code/05-layout-wrapper-route/public/manifest.json new file mode 100644 index 0000000000..080d6c77ac --- /dev/null +++ b/code/05-layout-wrapper-route/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/code/05-layout-wrapper-route/public/robots.txt b/code/05-layout-wrapper-route/public/robots.txt new file mode 100644 index 0000000000..e9e57dc4d4 --- /dev/null +++ b/code/05-layout-wrapper-route/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/code/05-layout-wrapper-route/src/App.js b/code/05-layout-wrapper-route/src/App.js new file mode 100644 index 0000000000..5be4b32b70 --- /dev/null +++ b/code/05-layout-wrapper-route/src/App.js @@ -0,0 +1,36 @@ +import { + createBrowserRouter, + // createRoutesFromElements, + RouterProvider, + // Route, +} from 'react-router-dom'; + +import HomePage from './pages/Home'; +import ProductsPage from './pages/Products'; +import RootLayout from './pages/Root'; + +// const routeDefinitions = createRoutesFromElements( +// +// } /> +// } /> +// +// ); + +const router = createBrowserRouter([ + { + path: '/', + element: , + children: [ + { path: '/', element: }, + { path: '/products', element: }, + ], + } +]); + +// const router = createBrowserRouter(routeDefinitions); + +function App() { + return ; +} + +export default App; diff --git a/code/05-layout-wrapper-route/src/components/MainNavigation.js b/code/05-layout-wrapper-route/src/components/MainNavigation.js new file mode 100644 index 0000000000..0180bd334a --- /dev/null +++ b/code/05-layout-wrapper-route/src/components/MainNavigation.js @@ -0,0 +1,22 @@ +import { Link } from 'react-router-dom'; + +import classes from './MainNavigation.module.css'; + +function MainNavigation() { + return ( +
+ +
+ ); +} + +export default MainNavigation; diff --git a/code/05-layout-wrapper-route/src/components/MainNavigation.module.css b/code/05-layout-wrapper-route/src/components/MainNavigation.module.css new file mode 100644 index 0000000000..22cf3202b8 --- /dev/null +++ b/code/05-layout-wrapper-route/src/components/MainNavigation.module.css @@ -0,0 +1,17 @@ +.header { + max-width: 60rem; + margin: auto; + padding: 2rem; + display: flex; + justify-content: center; +} + +.list { + display: flex; + gap: 1rem; +} + +.list a { + text-decoration: none; + color: var(--color-primary-400); +} diff --git a/code/05-layout-wrapper-route/src/index.css b/code/05-layout-wrapper-route/src/index.css new file mode 100644 index 0000000000..70bfa7fb2a --- /dev/null +++ b/code/05-layout-wrapper-route/src/index.css @@ -0,0 +1,50 @@ +* { + box-sizing: border-box; +} + +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + + --color-gray-100: #f4f3f1; + --color-gray-200: #dddbd8; + --color-gray-300: #ccc9c6; + --color-gray-400: #aeaba7; + --color-gray-500: #8a8784; + --color-gray-600: #656360; + --color-gray-700: #4b4a47; + --color-gray-800: #31302e; + --color-gray-900: #1f1d1b; + + --color-primary-100: #fcf3e1; + --color-primary-200: #fceccd; + --color-primary-300: #fae1af; + --color-primary-400: #fbd997; + --color-primary-500: #ffd37c; + --color-primary-600: #f9c762; + --color-primary-700: #fbc14d; + --color-primary-800: #fab833; + --color-primary-900: #f6ad1b; +} + +body { + margin: 0; +} + +ul { + list-style: none; + margin: 0; + padding: 0; +} diff --git a/code/05-layout-wrapper-route/src/index.js b/code/05-layout-wrapper-route/src/index.js new file mode 100644 index 0000000000..09de846062 --- /dev/null +++ b/code/05-layout-wrapper-route/src/index.js @@ -0,0 +1,12 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; + +import './index.css'; +import App from './App'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + + + +); diff --git a/code/05-layout-wrapper-route/src/pages/Home.js b/code/05-layout-wrapper-route/src/pages/Home.js new file mode 100644 index 0000000000..adc0e53b5d --- /dev/null +++ b/code/05-layout-wrapper-route/src/pages/Home.js @@ -0,0 +1,14 @@ +import { Link } from 'react-router-dom'; + +function HomePage() { + return ( + <> +

My Home Page

+

+ Go to the list of products. +

+ + ); +} + +export default HomePage; diff --git a/code/05-layout-wrapper-route/src/pages/Products.js b/code/05-layout-wrapper-route/src/pages/Products.js new file mode 100644 index 0000000000..6f2dd748d8 --- /dev/null +++ b/code/05-layout-wrapper-route/src/pages/Products.js @@ -0,0 +1,5 @@ +function ProductsPage() { + return

The Products Page

; +} + +export default ProductsPage; diff --git a/code/05-layout-wrapper-route/src/pages/Root.js b/code/05-layout-wrapper-route/src/pages/Root.js new file mode 100644 index 0000000000..2d079527b1 --- /dev/null +++ b/code/05-layout-wrapper-route/src/pages/Root.js @@ -0,0 +1,17 @@ +import { Outlet } from 'react-router-dom'; + +import MainNavigation from '../components/MainNavigation'; +import classes from './Root.module.css'; + +function RootLayout() { + return ( + <> + +
+ +
+ + ); +} + +export default RootLayout; diff --git a/code/05-layout-wrapper-route/src/pages/Root.module.css b/code/05-layout-wrapper-route/src/pages/Root.module.css new file mode 100644 index 0000000000..bbf187d826 --- /dev/null +++ b/code/05-layout-wrapper-route/src/pages/Root.module.css @@ -0,0 +1,4 @@ +.content { + margin: 2rem auto; + text-align: center; +} \ No newline at end of file diff --git a/code/06-error-route-404/package.json b/code/06-error-route-404/package.json new file mode 100644 index 0000000000..eafe171a6b --- /dev/null +++ b/code/06-error-route-404/package.json @@ -0,0 +1,39 @@ +{ + "name": "react-complete-guide", + "version": "0.1.0", + "private": true, + "dependencies": { + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^13.5.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.4.1", + "react-scripts": "5.0.1", + "web-vitals": "^2.1.4" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/code/06-error-route-404/public/favicon.ico b/code/06-error-route-404/public/favicon.ico new file mode 100644 index 0000000000..a11777cc47 Binary files /dev/null and b/code/06-error-route-404/public/favicon.ico differ diff --git a/code/06-error-route-404/public/index.html b/code/06-error-route-404/public/index.html new file mode 100644 index 0000000000..aa069f27cb --- /dev/null +++ b/code/06-error-route-404/public/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + React App + + + +
+ + + diff --git a/code/06-error-route-404/public/logo192.png b/code/06-error-route-404/public/logo192.png new file mode 100644 index 0000000000..fc44b0a379 Binary files /dev/null and b/code/06-error-route-404/public/logo192.png differ diff --git a/code/06-error-route-404/public/logo512.png b/code/06-error-route-404/public/logo512.png new file mode 100644 index 0000000000..a4e47a6545 Binary files /dev/null and b/code/06-error-route-404/public/logo512.png differ diff --git a/code/06-error-route-404/public/manifest.json b/code/06-error-route-404/public/manifest.json new file mode 100644 index 0000000000..080d6c77ac --- /dev/null +++ b/code/06-error-route-404/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/code/06-error-route-404/public/robots.txt b/code/06-error-route-404/public/robots.txt new file mode 100644 index 0000000000..e9e57dc4d4 --- /dev/null +++ b/code/06-error-route-404/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/code/06-error-route-404/src/App.js b/code/06-error-route-404/src/App.js new file mode 100644 index 0000000000..2eabec06d9 --- /dev/null +++ b/code/06-error-route-404/src/App.js @@ -0,0 +1,38 @@ +import { + createBrowserRouter, + // createRoutesFromElements, + RouterProvider, + // Route, +} from 'react-router-dom'; + +import ErrorPage from './pages/Error'; +import HomePage from './pages/Home'; +import ProductsPage from './pages/Products'; +import RootLayout from './pages/Root'; + +// const routeDefinitions = createRoutesFromElements( +// +// } /> +// } /> +// +// ); + +const router = createBrowserRouter([ + { + path: '/', + element: , + errorElement: , + children: [ + { path: '/', element: }, + { path: '/products', element: }, + ], + } +]); + +// const router = createBrowserRouter(routeDefinitions); + +function App() { + return ; +} + +export default App; diff --git a/code/06-error-route-404/src/components/MainNavigation.js b/code/06-error-route-404/src/components/MainNavigation.js new file mode 100644 index 0000000000..0180bd334a --- /dev/null +++ b/code/06-error-route-404/src/components/MainNavigation.js @@ -0,0 +1,22 @@ +import { Link } from 'react-router-dom'; + +import classes from './MainNavigation.module.css'; + +function MainNavigation() { + return ( +
+ +
+ ); +} + +export default MainNavigation; diff --git a/code/06-error-route-404/src/components/MainNavigation.module.css b/code/06-error-route-404/src/components/MainNavigation.module.css new file mode 100644 index 0000000000..22cf3202b8 --- /dev/null +++ b/code/06-error-route-404/src/components/MainNavigation.module.css @@ -0,0 +1,17 @@ +.header { + max-width: 60rem; + margin: auto; + padding: 2rem; + display: flex; + justify-content: center; +} + +.list { + display: flex; + gap: 1rem; +} + +.list a { + text-decoration: none; + color: var(--color-primary-400); +} diff --git a/code/06-error-route-404/src/index.css b/code/06-error-route-404/src/index.css new file mode 100644 index 0000000000..8f17b6184c --- /dev/null +++ b/code/06-error-route-404/src/index.css @@ -0,0 +1,55 @@ +* { + box-sizing: border-box; +} + +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + + --color-gray-100: #f4f3f1; + --color-gray-200: #dddbd8; + --color-gray-300: #ccc9c6; + --color-gray-400: #aeaba7; + --color-gray-500: #8a8784; + --color-gray-600: #656360; + --color-gray-700: #4b4a47; + --color-gray-800: #31302e; + --color-gray-900: #1f1d1b; + + --color-primary-100: #fcf3e1; + --color-primary-200: #fceccd; + --color-primary-300: #fae1af; + --color-primary-400: #fbd997; + --color-primary-500: #ffd37c; + --color-primary-600: #f9c762; + --color-primary-700: #fbc14d; + --color-primary-800: #fab833; + --color-primary-900: #f6ad1b; +} + +body { + margin: 0; +} + +ul { + list-style: none; + margin: 0; + padding: 0; +} + +main { + margin: 2rem auto; + text-align: center; +} \ No newline at end of file diff --git a/code/06-error-route-404/src/index.js b/code/06-error-route-404/src/index.js new file mode 100644 index 0000000000..09de846062 --- /dev/null +++ b/code/06-error-route-404/src/index.js @@ -0,0 +1,12 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; + +import './index.css'; +import App from './App'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + + + +); diff --git a/code/06-error-route-404/src/pages/Error.js b/code/06-error-route-404/src/pages/Error.js new file mode 100644 index 0000000000..d36a0318eb --- /dev/null +++ b/code/06-error-route-404/src/pages/Error.js @@ -0,0 +1,15 @@ +import MainNavigation from '../components/MainNavigation'; + +function ErrorPage() { + return ( + <> + +
+

An error occurred!

+

Could not find this page!

+
+ + ); +} + +export default ErrorPage; diff --git a/code/06-error-route-404/src/pages/Home.js b/code/06-error-route-404/src/pages/Home.js new file mode 100644 index 0000000000..adc0e53b5d --- /dev/null +++ b/code/06-error-route-404/src/pages/Home.js @@ -0,0 +1,14 @@ +import { Link } from 'react-router-dom'; + +function HomePage() { + return ( + <> +

My Home Page

+

+ Go to the list of products. +

+ + ); +} + +export default HomePage; diff --git a/code/06-error-route-404/src/pages/Products.js b/code/06-error-route-404/src/pages/Products.js new file mode 100644 index 0000000000..6f2dd748d8 --- /dev/null +++ b/code/06-error-route-404/src/pages/Products.js @@ -0,0 +1,5 @@ +function ProductsPage() { + return

The Products Page

; +} + +export default ProductsPage; diff --git a/code/06-error-route-404/src/pages/Root.js b/code/06-error-route-404/src/pages/Root.js new file mode 100644 index 0000000000..db6babb23a --- /dev/null +++ b/code/06-error-route-404/src/pages/Root.js @@ -0,0 +1,16 @@ +import { Outlet } from 'react-router-dom'; + +import MainNavigation from '../components/MainNavigation'; + +function RootLayout() { + return ( + <> + +
+ +
+ + ); +} + +export default RootLayout; diff --git a/code/07-using-navlinks/package.json b/code/07-using-navlinks/package.json new file mode 100644 index 0000000000..eafe171a6b --- /dev/null +++ b/code/07-using-navlinks/package.json @@ -0,0 +1,39 @@ +{ + "name": "react-complete-guide", + "version": "0.1.0", + "private": true, + "dependencies": { + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^13.5.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.4.1", + "react-scripts": "5.0.1", + "web-vitals": "^2.1.4" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/code/07-using-navlinks/public/favicon.ico b/code/07-using-navlinks/public/favicon.ico new file mode 100644 index 0000000000..a11777cc47 Binary files /dev/null and b/code/07-using-navlinks/public/favicon.ico differ diff --git a/code/07-using-navlinks/public/index.html b/code/07-using-navlinks/public/index.html new file mode 100644 index 0000000000..aa069f27cb --- /dev/null +++ b/code/07-using-navlinks/public/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + React App + + + +
+ + + diff --git a/code/07-using-navlinks/public/logo192.png b/code/07-using-navlinks/public/logo192.png new file mode 100644 index 0000000000..fc44b0a379 Binary files /dev/null and b/code/07-using-navlinks/public/logo192.png differ diff --git a/code/07-using-navlinks/public/logo512.png b/code/07-using-navlinks/public/logo512.png new file mode 100644 index 0000000000..a4e47a6545 Binary files /dev/null and b/code/07-using-navlinks/public/logo512.png differ diff --git a/code/07-using-navlinks/public/manifest.json b/code/07-using-navlinks/public/manifest.json new file mode 100644 index 0000000000..080d6c77ac --- /dev/null +++ b/code/07-using-navlinks/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/code/07-using-navlinks/public/robots.txt b/code/07-using-navlinks/public/robots.txt new file mode 100644 index 0000000000..e9e57dc4d4 --- /dev/null +++ b/code/07-using-navlinks/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/code/07-using-navlinks/src/App.js b/code/07-using-navlinks/src/App.js new file mode 100644 index 0000000000..2eabec06d9 --- /dev/null +++ b/code/07-using-navlinks/src/App.js @@ -0,0 +1,38 @@ +import { + createBrowserRouter, + // createRoutesFromElements, + RouterProvider, + // Route, +} from 'react-router-dom'; + +import ErrorPage from './pages/Error'; +import HomePage from './pages/Home'; +import ProductsPage from './pages/Products'; +import RootLayout from './pages/Root'; + +// const routeDefinitions = createRoutesFromElements( +// +// } /> +// } /> +// +// ); + +const router = createBrowserRouter([ + { + path: '/', + element: , + errorElement: , + children: [ + { path: '/', element: }, + { path: '/products', element: }, + ], + } +]); + +// const router = createBrowserRouter(routeDefinitions); + +function App() { + return ; +} + +export default App; diff --git a/code/07-using-navlinks/src/components/MainNavigation.js b/code/07-using-navlinks/src/components/MainNavigation.js new file mode 100644 index 0000000000..4eb6ac404a --- /dev/null +++ b/code/07-using-navlinks/src/components/MainNavigation.js @@ -0,0 +1,40 @@ +import { NavLink } from 'react-router-dom'; + +import classes from './MainNavigation.module.css'; + +function MainNavigation() { + return ( +
+ +
+ ); +} + +export default MainNavigation; diff --git a/code/07-using-navlinks/src/components/MainNavigation.module.css b/code/07-using-navlinks/src/components/MainNavigation.module.css new file mode 100644 index 0000000000..cb0d5689ea --- /dev/null +++ b/code/07-using-navlinks/src/components/MainNavigation.module.css @@ -0,0 +1,23 @@ +.header { + max-width: 60rem; + margin: auto; + padding: 2rem; + display: flex; + justify-content: center; +} + +.list { + display: flex; + gap: 1rem; +} + +.list a { + text-decoration: none; + color: var(--color-primary-400); +} + +.list a:hover, +.list a.active { + color: var(--color-primary-800); + text-decoration: underline; +} \ No newline at end of file diff --git a/code/07-using-navlinks/src/index.css b/code/07-using-navlinks/src/index.css new file mode 100644 index 0000000000..8f17b6184c --- /dev/null +++ b/code/07-using-navlinks/src/index.css @@ -0,0 +1,55 @@ +* { + box-sizing: border-box; +} + +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + + --color-gray-100: #f4f3f1; + --color-gray-200: #dddbd8; + --color-gray-300: #ccc9c6; + --color-gray-400: #aeaba7; + --color-gray-500: #8a8784; + --color-gray-600: #656360; + --color-gray-700: #4b4a47; + --color-gray-800: #31302e; + --color-gray-900: #1f1d1b; + + --color-primary-100: #fcf3e1; + --color-primary-200: #fceccd; + --color-primary-300: #fae1af; + --color-primary-400: #fbd997; + --color-primary-500: #ffd37c; + --color-primary-600: #f9c762; + --color-primary-700: #fbc14d; + --color-primary-800: #fab833; + --color-primary-900: #f6ad1b; +} + +body { + margin: 0; +} + +ul { + list-style: none; + margin: 0; + padding: 0; +} + +main { + margin: 2rem auto; + text-align: center; +} \ No newline at end of file diff --git a/code/07-using-navlinks/src/index.js b/code/07-using-navlinks/src/index.js new file mode 100644 index 0000000000..09de846062 --- /dev/null +++ b/code/07-using-navlinks/src/index.js @@ -0,0 +1,12 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; + +import './index.css'; +import App from './App'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + + + +); diff --git a/code/07-using-navlinks/src/pages/Error.js b/code/07-using-navlinks/src/pages/Error.js new file mode 100644 index 0000000000..d36a0318eb --- /dev/null +++ b/code/07-using-navlinks/src/pages/Error.js @@ -0,0 +1,15 @@ +import MainNavigation from '../components/MainNavigation'; + +function ErrorPage() { + return ( + <> + +
+

An error occurred!

+

Could not find this page!

+
+ + ); +} + +export default ErrorPage; diff --git a/code/07-using-navlinks/src/pages/Home.js b/code/07-using-navlinks/src/pages/Home.js new file mode 100644 index 0000000000..adc0e53b5d --- /dev/null +++ b/code/07-using-navlinks/src/pages/Home.js @@ -0,0 +1,14 @@ +import { Link } from 'react-router-dom'; + +function HomePage() { + return ( + <> +

My Home Page

+

+ Go to the list of products. +

+ + ); +} + +export default HomePage; diff --git a/code/07-using-navlinks/src/pages/Products.js b/code/07-using-navlinks/src/pages/Products.js new file mode 100644 index 0000000000..6f2dd748d8 --- /dev/null +++ b/code/07-using-navlinks/src/pages/Products.js @@ -0,0 +1,5 @@ +function ProductsPage() { + return

The Products Page

; +} + +export default ProductsPage; diff --git a/code/07-using-navlinks/src/pages/Root.js b/code/07-using-navlinks/src/pages/Root.js new file mode 100644 index 0000000000..db6babb23a --- /dev/null +++ b/code/07-using-navlinks/src/pages/Root.js @@ -0,0 +1,16 @@ +import { Outlet } from 'react-router-dom'; + +import MainNavigation from '../components/MainNavigation'; + +function RootLayout() { + return ( + <> + +
+ +
+ + ); +} + +export default RootLayout; diff --git a/code/08-navigating-programmatically/package.json b/code/08-navigating-programmatically/package.json new file mode 100644 index 0000000000..eafe171a6b --- /dev/null +++ b/code/08-navigating-programmatically/package.json @@ -0,0 +1,39 @@ +{ + "name": "react-complete-guide", + "version": "0.1.0", + "private": true, + "dependencies": { + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^13.5.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.4.1", + "react-scripts": "5.0.1", + "web-vitals": "^2.1.4" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/code/08-navigating-programmatically/public/favicon.ico b/code/08-navigating-programmatically/public/favicon.ico new file mode 100644 index 0000000000..a11777cc47 Binary files /dev/null and b/code/08-navigating-programmatically/public/favicon.ico differ diff --git a/code/08-navigating-programmatically/public/index.html b/code/08-navigating-programmatically/public/index.html new file mode 100644 index 0000000000..aa069f27cb --- /dev/null +++ b/code/08-navigating-programmatically/public/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + React App + + + +
+ + + diff --git a/code/08-navigating-programmatically/public/logo192.png b/code/08-navigating-programmatically/public/logo192.png new file mode 100644 index 0000000000..fc44b0a379 Binary files /dev/null and b/code/08-navigating-programmatically/public/logo192.png differ diff --git a/code/08-navigating-programmatically/public/logo512.png b/code/08-navigating-programmatically/public/logo512.png new file mode 100644 index 0000000000..a4e47a6545 Binary files /dev/null and b/code/08-navigating-programmatically/public/logo512.png differ diff --git a/code/08-navigating-programmatically/public/manifest.json b/code/08-navigating-programmatically/public/manifest.json new file mode 100644 index 0000000000..080d6c77ac --- /dev/null +++ b/code/08-navigating-programmatically/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/code/08-navigating-programmatically/public/robots.txt b/code/08-navigating-programmatically/public/robots.txt new file mode 100644 index 0000000000..e9e57dc4d4 --- /dev/null +++ b/code/08-navigating-programmatically/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/code/08-navigating-programmatically/src/App.js b/code/08-navigating-programmatically/src/App.js new file mode 100644 index 0000000000..2eabec06d9 --- /dev/null +++ b/code/08-navigating-programmatically/src/App.js @@ -0,0 +1,38 @@ +import { + createBrowserRouter, + // createRoutesFromElements, + RouterProvider, + // Route, +} from 'react-router-dom'; + +import ErrorPage from './pages/Error'; +import HomePage from './pages/Home'; +import ProductsPage from './pages/Products'; +import RootLayout from './pages/Root'; + +// const routeDefinitions = createRoutesFromElements( +// +// } /> +// } /> +// +// ); + +const router = createBrowserRouter([ + { + path: '/', + element: , + errorElement: , + children: [ + { path: '/', element: }, + { path: '/products', element: }, + ], + } +]); + +// const router = createBrowserRouter(routeDefinitions); + +function App() { + return ; +} + +export default App; diff --git a/code/08-navigating-programmatically/src/components/MainNavigation.js b/code/08-navigating-programmatically/src/components/MainNavigation.js new file mode 100644 index 0000000000..4eb6ac404a --- /dev/null +++ b/code/08-navigating-programmatically/src/components/MainNavigation.js @@ -0,0 +1,40 @@ +import { NavLink } from 'react-router-dom'; + +import classes from './MainNavigation.module.css'; + +function MainNavigation() { + return ( +
+ +
+ ); +} + +export default MainNavigation; diff --git a/code/08-navigating-programmatically/src/components/MainNavigation.module.css b/code/08-navigating-programmatically/src/components/MainNavigation.module.css new file mode 100644 index 0000000000..cb0d5689ea --- /dev/null +++ b/code/08-navigating-programmatically/src/components/MainNavigation.module.css @@ -0,0 +1,23 @@ +.header { + max-width: 60rem; + margin: auto; + padding: 2rem; + display: flex; + justify-content: center; +} + +.list { + display: flex; + gap: 1rem; +} + +.list a { + text-decoration: none; + color: var(--color-primary-400); +} + +.list a:hover, +.list a.active { + color: var(--color-primary-800); + text-decoration: underline; +} \ No newline at end of file diff --git a/code/08-navigating-programmatically/src/index.css b/code/08-navigating-programmatically/src/index.css new file mode 100644 index 0000000000..8f17b6184c --- /dev/null +++ b/code/08-navigating-programmatically/src/index.css @@ -0,0 +1,55 @@ +* { + box-sizing: border-box; +} + +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + + --color-gray-100: #f4f3f1; + --color-gray-200: #dddbd8; + --color-gray-300: #ccc9c6; + --color-gray-400: #aeaba7; + --color-gray-500: #8a8784; + --color-gray-600: #656360; + --color-gray-700: #4b4a47; + --color-gray-800: #31302e; + --color-gray-900: #1f1d1b; + + --color-primary-100: #fcf3e1; + --color-primary-200: #fceccd; + --color-primary-300: #fae1af; + --color-primary-400: #fbd997; + --color-primary-500: #ffd37c; + --color-primary-600: #f9c762; + --color-primary-700: #fbc14d; + --color-primary-800: #fab833; + --color-primary-900: #f6ad1b; +} + +body { + margin: 0; +} + +ul { + list-style: none; + margin: 0; + padding: 0; +} + +main { + margin: 2rem auto; + text-align: center; +} \ No newline at end of file diff --git a/code/08-navigating-programmatically/src/index.js b/code/08-navigating-programmatically/src/index.js new file mode 100644 index 0000000000..09de846062 --- /dev/null +++ b/code/08-navigating-programmatically/src/index.js @@ -0,0 +1,12 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; + +import './index.css'; +import App from './App'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + + + +); diff --git a/code/08-navigating-programmatically/src/pages/Error.js b/code/08-navigating-programmatically/src/pages/Error.js new file mode 100644 index 0000000000..d36a0318eb --- /dev/null +++ b/code/08-navigating-programmatically/src/pages/Error.js @@ -0,0 +1,15 @@ +import MainNavigation from '../components/MainNavigation'; + +function ErrorPage() { + return ( + <> + +
+

An error occurred!

+

Could not find this page!

+
+ + ); +} + +export default ErrorPage; diff --git a/code/08-navigating-programmatically/src/pages/Home.js b/code/08-navigating-programmatically/src/pages/Home.js new file mode 100644 index 0000000000..e4a6a4e858 --- /dev/null +++ b/code/08-navigating-programmatically/src/pages/Home.js @@ -0,0 +1,23 @@ +import { Link, useNavigate } from 'react-router-dom'; + +function HomePage() { + const navigate = useNavigate(); + + function navigateHandler() { + navigate('/products'); + } + + return ( + <> +

My Home Page

+

+ Go to the list of products. +

+

+ +

+ + ); +} + +export default HomePage; diff --git a/code/08-navigating-programmatically/src/pages/Products.js b/code/08-navigating-programmatically/src/pages/Products.js new file mode 100644 index 0000000000..6f2dd748d8 --- /dev/null +++ b/code/08-navigating-programmatically/src/pages/Products.js @@ -0,0 +1,5 @@ +function ProductsPage() { + return

The Products Page

; +} + +export default ProductsPage; diff --git a/code/08-navigating-programmatically/src/pages/Root.js b/code/08-navigating-programmatically/src/pages/Root.js new file mode 100644 index 0000000000..db6babb23a --- /dev/null +++ b/code/08-navigating-programmatically/src/pages/Root.js @@ -0,0 +1,16 @@ +import { Outlet } from 'react-router-dom'; + +import MainNavigation from '../components/MainNavigation'; + +function RootLayout() { + return ( + <> + +
+ +
+ + ); +} + +export default RootLayout; diff --git a/code/09-dynamic-routes/package.json b/code/09-dynamic-routes/package.json new file mode 100644 index 0000000000..eafe171a6b --- /dev/null +++ b/code/09-dynamic-routes/package.json @@ -0,0 +1,39 @@ +{ + "name": "react-complete-guide", + "version": "0.1.0", + "private": true, + "dependencies": { + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^13.5.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.4.1", + "react-scripts": "5.0.1", + "web-vitals": "^2.1.4" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/code/09-dynamic-routes/public/favicon.ico b/code/09-dynamic-routes/public/favicon.ico new file mode 100644 index 0000000000..a11777cc47 Binary files /dev/null and b/code/09-dynamic-routes/public/favicon.ico differ diff --git a/code/09-dynamic-routes/public/index.html b/code/09-dynamic-routes/public/index.html new file mode 100644 index 0000000000..aa069f27cb --- /dev/null +++ b/code/09-dynamic-routes/public/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + React App + + + +
+ + + diff --git a/code/09-dynamic-routes/public/logo192.png b/code/09-dynamic-routes/public/logo192.png new file mode 100644 index 0000000000..fc44b0a379 Binary files /dev/null and b/code/09-dynamic-routes/public/logo192.png differ diff --git a/code/09-dynamic-routes/public/logo512.png b/code/09-dynamic-routes/public/logo512.png new file mode 100644 index 0000000000..a4e47a6545 Binary files /dev/null and b/code/09-dynamic-routes/public/logo512.png differ diff --git a/code/09-dynamic-routes/public/manifest.json b/code/09-dynamic-routes/public/manifest.json new file mode 100644 index 0000000000..080d6c77ac --- /dev/null +++ b/code/09-dynamic-routes/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/code/09-dynamic-routes/public/robots.txt b/code/09-dynamic-routes/public/robots.txt new file mode 100644 index 0000000000..e9e57dc4d4 --- /dev/null +++ b/code/09-dynamic-routes/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/code/09-dynamic-routes/src/App.js b/code/09-dynamic-routes/src/App.js new file mode 100644 index 0000000000..96b76ceae8 --- /dev/null +++ b/code/09-dynamic-routes/src/App.js @@ -0,0 +1,40 @@ +import { + createBrowserRouter, + // createRoutesFromElements, + RouterProvider, + // Route, +} from 'react-router-dom'; + +import ErrorPage from './pages/Error'; +import HomePage from './pages/Home'; +import ProductDetailPage from './pages/ProductDetail'; +import ProductsPage from './pages/Products'; +import RootLayout from './pages/Root'; + +// const routeDefinitions = createRoutesFromElements( +// +// } /> +// } /> +// +// ); + +const router = createBrowserRouter([ + { + path: '/', + element: , + errorElement: , + children: [ + { path: '/', element: }, + { path: '/products', element: }, + { path: '/products/:productId', element: } + ], + } +]); + +// const router = createBrowserRouter(routeDefinitions); + +function App() { + return ; +} + +export default App; diff --git a/code/09-dynamic-routes/src/components/MainNavigation.js b/code/09-dynamic-routes/src/components/MainNavigation.js new file mode 100644 index 0000000000..4eb6ac404a --- /dev/null +++ b/code/09-dynamic-routes/src/components/MainNavigation.js @@ -0,0 +1,40 @@ +import { NavLink } from 'react-router-dom'; + +import classes from './MainNavigation.module.css'; + +function MainNavigation() { + return ( +
+ +
+ ); +} + +export default MainNavigation; diff --git a/code/09-dynamic-routes/src/components/MainNavigation.module.css b/code/09-dynamic-routes/src/components/MainNavigation.module.css new file mode 100644 index 0000000000..cb0d5689ea --- /dev/null +++ b/code/09-dynamic-routes/src/components/MainNavigation.module.css @@ -0,0 +1,23 @@ +.header { + max-width: 60rem; + margin: auto; + padding: 2rem; + display: flex; + justify-content: center; +} + +.list { + display: flex; + gap: 1rem; +} + +.list a { + text-decoration: none; + color: var(--color-primary-400); +} + +.list a:hover, +.list a.active { + color: var(--color-primary-800); + text-decoration: underline; +} \ No newline at end of file diff --git a/code/09-dynamic-routes/src/index.css b/code/09-dynamic-routes/src/index.css new file mode 100644 index 0000000000..8f17b6184c --- /dev/null +++ b/code/09-dynamic-routes/src/index.css @@ -0,0 +1,55 @@ +* { + box-sizing: border-box; +} + +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + + --color-gray-100: #f4f3f1; + --color-gray-200: #dddbd8; + --color-gray-300: #ccc9c6; + --color-gray-400: #aeaba7; + --color-gray-500: #8a8784; + --color-gray-600: #656360; + --color-gray-700: #4b4a47; + --color-gray-800: #31302e; + --color-gray-900: #1f1d1b; + + --color-primary-100: #fcf3e1; + --color-primary-200: #fceccd; + --color-primary-300: #fae1af; + --color-primary-400: #fbd997; + --color-primary-500: #ffd37c; + --color-primary-600: #f9c762; + --color-primary-700: #fbc14d; + --color-primary-800: #fab833; + --color-primary-900: #f6ad1b; +} + +body { + margin: 0; +} + +ul { + list-style: none; + margin: 0; + padding: 0; +} + +main { + margin: 2rem auto; + text-align: center; +} \ No newline at end of file diff --git a/code/09-dynamic-routes/src/index.js b/code/09-dynamic-routes/src/index.js new file mode 100644 index 0000000000..09de846062 --- /dev/null +++ b/code/09-dynamic-routes/src/index.js @@ -0,0 +1,12 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; + +import './index.css'; +import App from './App'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + + + +); diff --git a/code/09-dynamic-routes/src/pages/Error.js b/code/09-dynamic-routes/src/pages/Error.js new file mode 100644 index 0000000000..d36a0318eb --- /dev/null +++ b/code/09-dynamic-routes/src/pages/Error.js @@ -0,0 +1,15 @@ +import MainNavigation from '../components/MainNavigation'; + +function ErrorPage() { + return ( + <> + +
+

An error occurred!

+

Could not find this page!

+
+ + ); +} + +export default ErrorPage; diff --git a/code/09-dynamic-routes/src/pages/Home.js b/code/09-dynamic-routes/src/pages/Home.js new file mode 100644 index 0000000000..e4a6a4e858 --- /dev/null +++ b/code/09-dynamic-routes/src/pages/Home.js @@ -0,0 +1,23 @@ +import { Link, useNavigate } from 'react-router-dom'; + +function HomePage() { + const navigate = useNavigate(); + + function navigateHandler() { + navigate('/products'); + } + + return ( + <> +

My Home Page

+

+ Go to the list of products. +

+

+ +

+ + ); +} + +export default HomePage; diff --git a/code/09-dynamic-routes/src/pages/ProductDetail.js b/code/09-dynamic-routes/src/pages/ProductDetail.js new file mode 100644 index 0000000000..5d160ce347 --- /dev/null +++ b/code/09-dynamic-routes/src/pages/ProductDetail.js @@ -0,0 +1,14 @@ +import { useParams } from 'react-router-dom'; + +function ProductDetailPage() { + const params = useParams(); + + return ( + <> +

Product Details!

+

{params.productId}

+ + ); +} + +export default ProductDetailPage; diff --git a/code/09-dynamic-routes/src/pages/Products.js b/code/09-dynamic-routes/src/pages/Products.js new file mode 100644 index 0000000000..d85f2a850c --- /dev/null +++ b/code/09-dynamic-routes/src/pages/Products.js @@ -0,0 +1,24 @@ +import { Link } from 'react-router-dom'; + +const PRODUCTS = [ + { id: 'p1', title: 'Product 1' }, + { id: 'p2', title: 'Product 2' }, + { id: 'p3', title: 'Product 3' }, +]; + +function ProductsPage() { + return ( + <> +

The Products Page

+
    + {PRODUCTS.map((prod) => ( +
  • + {prod.title} +
  • + ))} +
+ + ); +} + +export default ProductsPage; diff --git a/code/09-dynamic-routes/src/pages/Root.js b/code/09-dynamic-routes/src/pages/Root.js new file mode 100644 index 0000000000..db6babb23a --- /dev/null +++ b/code/09-dynamic-routes/src/pages/Root.js @@ -0,0 +1,16 @@ +import { Outlet } from 'react-router-dom'; + +import MainNavigation from '../components/MainNavigation'; + +function RootLayout() { + return ( + <> + +
+ +
+ + ); +} + +export default RootLayout; diff --git a/code/10-absolute-relative-paths/package.json b/code/10-absolute-relative-paths/package.json new file mode 100644 index 0000000000..eafe171a6b --- /dev/null +++ b/code/10-absolute-relative-paths/package.json @@ -0,0 +1,39 @@ +{ + "name": "react-complete-guide", + "version": "0.1.0", + "private": true, + "dependencies": { + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^13.5.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.4.1", + "react-scripts": "5.0.1", + "web-vitals": "^2.1.4" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/code/10-absolute-relative-paths/public/favicon.ico b/code/10-absolute-relative-paths/public/favicon.ico new file mode 100644 index 0000000000..a11777cc47 Binary files /dev/null and b/code/10-absolute-relative-paths/public/favicon.ico differ diff --git a/code/10-absolute-relative-paths/public/index.html b/code/10-absolute-relative-paths/public/index.html new file mode 100644 index 0000000000..aa069f27cb --- /dev/null +++ b/code/10-absolute-relative-paths/public/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + React App + + + +
+ + + diff --git a/code/10-absolute-relative-paths/public/logo192.png b/code/10-absolute-relative-paths/public/logo192.png new file mode 100644 index 0000000000..fc44b0a379 Binary files /dev/null and b/code/10-absolute-relative-paths/public/logo192.png differ diff --git a/code/10-absolute-relative-paths/public/logo512.png b/code/10-absolute-relative-paths/public/logo512.png new file mode 100644 index 0000000000..a4e47a6545 Binary files /dev/null and b/code/10-absolute-relative-paths/public/logo512.png differ diff --git a/code/10-absolute-relative-paths/public/manifest.json b/code/10-absolute-relative-paths/public/manifest.json new file mode 100644 index 0000000000..080d6c77ac --- /dev/null +++ b/code/10-absolute-relative-paths/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/code/10-absolute-relative-paths/public/robots.txt b/code/10-absolute-relative-paths/public/robots.txt new file mode 100644 index 0000000000..e9e57dc4d4 --- /dev/null +++ b/code/10-absolute-relative-paths/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/code/10-absolute-relative-paths/src/App.js b/code/10-absolute-relative-paths/src/App.js new file mode 100644 index 0000000000..6d07cf5198 --- /dev/null +++ b/code/10-absolute-relative-paths/src/App.js @@ -0,0 +1,40 @@ +import { + createBrowserRouter, + // createRoutesFromElements, + RouterProvider, + // Route, +} from 'react-router-dom'; + +import ErrorPage from './pages/Error'; +import HomePage from './pages/Home'; +import ProductDetailPage from './pages/ProductDetail'; +import ProductsPage from './pages/Products'; +import RootLayout from './pages/Root'; + +// const routeDefinitions = createRoutesFromElements( +// +// } /> +// } /> +// +// ); + +const router = createBrowserRouter([ + { + path: '/', + element: , + errorElement: , + children: [ + { path: '', element: }, + { path: 'products', element: }, + { path: 'products/:productId', element: } + ], + } +]); + +// const router = createBrowserRouter(routeDefinitions); + +function App() { + return ; +} + +export default App; diff --git a/code/10-absolute-relative-paths/src/components/MainNavigation.js b/code/10-absolute-relative-paths/src/components/MainNavigation.js new file mode 100644 index 0000000000..4eb6ac404a --- /dev/null +++ b/code/10-absolute-relative-paths/src/components/MainNavigation.js @@ -0,0 +1,40 @@ +import { NavLink } from 'react-router-dom'; + +import classes from './MainNavigation.module.css'; + +function MainNavigation() { + return ( +
+ +
+ ); +} + +export default MainNavigation; diff --git a/code/10-absolute-relative-paths/src/components/MainNavigation.module.css b/code/10-absolute-relative-paths/src/components/MainNavigation.module.css new file mode 100644 index 0000000000..cb0d5689ea --- /dev/null +++ b/code/10-absolute-relative-paths/src/components/MainNavigation.module.css @@ -0,0 +1,23 @@ +.header { + max-width: 60rem; + margin: auto; + padding: 2rem; + display: flex; + justify-content: center; +} + +.list { + display: flex; + gap: 1rem; +} + +.list a { + text-decoration: none; + color: var(--color-primary-400); +} + +.list a:hover, +.list a.active { + color: var(--color-primary-800); + text-decoration: underline; +} \ No newline at end of file diff --git a/code/10-absolute-relative-paths/src/index.css b/code/10-absolute-relative-paths/src/index.css new file mode 100644 index 0000000000..8f17b6184c --- /dev/null +++ b/code/10-absolute-relative-paths/src/index.css @@ -0,0 +1,55 @@ +* { + box-sizing: border-box; +} + +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + + --color-gray-100: #f4f3f1; + --color-gray-200: #dddbd8; + --color-gray-300: #ccc9c6; + --color-gray-400: #aeaba7; + --color-gray-500: #8a8784; + --color-gray-600: #656360; + --color-gray-700: #4b4a47; + --color-gray-800: #31302e; + --color-gray-900: #1f1d1b; + + --color-primary-100: #fcf3e1; + --color-primary-200: #fceccd; + --color-primary-300: #fae1af; + --color-primary-400: #fbd997; + --color-primary-500: #ffd37c; + --color-primary-600: #f9c762; + --color-primary-700: #fbc14d; + --color-primary-800: #fab833; + --color-primary-900: #f6ad1b; +} + +body { + margin: 0; +} + +ul { + list-style: none; + margin: 0; + padding: 0; +} + +main { + margin: 2rem auto; + text-align: center; +} \ No newline at end of file diff --git a/code/10-absolute-relative-paths/src/index.js b/code/10-absolute-relative-paths/src/index.js new file mode 100644 index 0000000000..09de846062 --- /dev/null +++ b/code/10-absolute-relative-paths/src/index.js @@ -0,0 +1,12 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; + +import './index.css'; +import App from './App'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + + + +); diff --git a/code/10-absolute-relative-paths/src/pages/Error.js b/code/10-absolute-relative-paths/src/pages/Error.js new file mode 100644 index 0000000000..d36a0318eb --- /dev/null +++ b/code/10-absolute-relative-paths/src/pages/Error.js @@ -0,0 +1,15 @@ +import MainNavigation from '../components/MainNavigation'; + +function ErrorPage() { + return ( + <> + +
+

An error occurred!

+

Could not find this page!

+
+ + ); +} + +export default ErrorPage; diff --git a/code/10-absolute-relative-paths/src/pages/Home.js b/code/10-absolute-relative-paths/src/pages/Home.js new file mode 100644 index 0000000000..7190e222a3 --- /dev/null +++ b/code/10-absolute-relative-paths/src/pages/Home.js @@ -0,0 +1,23 @@ +import { Link, useNavigate } from 'react-router-dom'; + +function HomePage() { + const navigate = useNavigate(); + + function navigateHandler() { + navigate('/products'); + } + + return ( + <> +

My Home Page

+

+ Go to the list of products. +

+

+ +

+ + ); +} + +export default HomePage; diff --git a/code/10-absolute-relative-paths/src/pages/ProductDetail.js b/code/10-absolute-relative-paths/src/pages/ProductDetail.js new file mode 100644 index 0000000000..0f8c86b516 --- /dev/null +++ b/code/10-absolute-relative-paths/src/pages/ProductDetail.js @@ -0,0 +1,15 @@ +import { useParams, Link } from 'react-router-dom'; + +function ProductDetailPage() { + const params = useParams(); + + return ( + <> +

Product Details!

+

{params.productId}

+

Back

+ + ); +} + +export default ProductDetailPage; diff --git a/code/10-absolute-relative-paths/src/pages/Products.js b/code/10-absolute-relative-paths/src/pages/Products.js new file mode 100644 index 0000000000..d15679e5ab --- /dev/null +++ b/code/10-absolute-relative-paths/src/pages/Products.js @@ -0,0 +1,24 @@ +import { Link } from 'react-router-dom'; + +const PRODUCTS = [ + { id: 'p1', title: 'Product 1' }, + { id: 'p2', title: 'Product 2' }, + { id: 'p3', title: 'Product 3' }, +]; + +function ProductsPage() { + return ( + <> +

The Products Page

+
    + {PRODUCTS.map((prod) => ( +
  • + {prod.title} +
  • + ))} +
+ + ); +} + +export default ProductsPage; diff --git a/code/10-absolute-relative-paths/src/pages/Root.js b/code/10-absolute-relative-paths/src/pages/Root.js new file mode 100644 index 0000000000..db6babb23a --- /dev/null +++ b/code/10-absolute-relative-paths/src/pages/Root.js @@ -0,0 +1,16 @@ +import { Outlet } from 'react-router-dom'; + +import MainNavigation from '../components/MainNavigation'; + +function RootLayout() { + return ( + <> + +
+ +
+ + ); +} + +export default RootLayout; diff --git a/code/11-index-routes/package.json b/code/11-index-routes/package.json new file mode 100644 index 0000000000..eafe171a6b --- /dev/null +++ b/code/11-index-routes/package.json @@ -0,0 +1,39 @@ +{ + "name": "react-complete-guide", + "version": "0.1.0", + "private": true, + "dependencies": { + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^13.5.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.4.1", + "react-scripts": "5.0.1", + "web-vitals": "^2.1.4" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/code/11-index-routes/public/favicon.ico b/code/11-index-routes/public/favicon.ico new file mode 100644 index 0000000000..a11777cc47 Binary files /dev/null and b/code/11-index-routes/public/favicon.ico differ diff --git a/code/11-index-routes/public/index.html b/code/11-index-routes/public/index.html new file mode 100644 index 0000000000..aa069f27cb --- /dev/null +++ b/code/11-index-routes/public/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + React App + + + +
+ + + diff --git a/code/11-index-routes/public/logo192.png b/code/11-index-routes/public/logo192.png new file mode 100644 index 0000000000..fc44b0a379 Binary files /dev/null and b/code/11-index-routes/public/logo192.png differ diff --git a/code/11-index-routes/public/logo512.png b/code/11-index-routes/public/logo512.png new file mode 100644 index 0000000000..a4e47a6545 Binary files /dev/null and b/code/11-index-routes/public/logo512.png differ diff --git a/code/11-index-routes/public/manifest.json b/code/11-index-routes/public/manifest.json new file mode 100644 index 0000000000..080d6c77ac --- /dev/null +++ b/code/11-index-routes/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/code/11-index-routes/public/robots.txt b/code/11-index-routes/public/robots.txt new file mode 100644 index 0000000000..e9e57dc4d4 --- /dev/null +++ b/code/11-index-routes/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/code/11-index-routes/src/App.js b/code/11-index-routes/src/App.js new file mode 100644 index 0000000000..dd3673b5f8 --- /dev/null +++ b/code/11-index-routes/src/App.js @@ -0,0 +1,40 @@ +import { + createBrowserRouter, + // createRoutesFromElements, + RouterProvider, + // Route, +} from 'react-router-dom'; + +import ErrorPage from './pages/Error'; +import HomePage from './pages/Home'; +import ProductDetailPage from './pages/ProductDetail'; +import ProductsPage from './pages/Products'; +import RootLayout from './pages/Root'; + +// const routeDefinitions = createRoutesFromElements( +// +// } /> +// } /> +// +// ); + +const router = createBrowserRouter([ + { + path: '/', + element: , + errorElement: , + children: [ + { index: true, element: }, + { path: 'products', element: }, + { path: 'products/:productId', element: } + ], + } +]); + +// const router = createBrowserRouter(routeDefinitions); + +function App() { + return ; +} + +export default App; diff --git a/code/11-index-routes/src/components/MainNavigation.js b/code/11-index-routes/src/components/MainNavigation.js new file mode 100644 index 0000000000..4eb6ac404a --- /dev/null +++ b/code/11-index-routes/src/components/MainNavigation.js @@ -0,0 +1,40 @@ +import { NavLink } from 'react-router-dom'; + +import classes from './MainNavigation.module.css'; + +function MainNavigation() { + return ( +
+ +
+ ); +} + +export default MainNavigation; diff --git a/code/11-index-routes/src/components/MainNavigation.module.css b/code/11-index-routes/src/components/MainNavigation.module.css new file mode 100644 index 0000000000..cb0d5689ea --- /dev/null +++ b/code/11-index-routes/src/components/MainNavigation.module.css @@ -0,0 +1,23 @@ +.header { + max-width: 60rem; + margin: auto; + padding: 2rem; + display: flex; + justify-content: center; +} + +.list { + display: flex; + gap: 1rem; +} + +.list a { + text-decoration: none; + color: var(--color-primary-400); +} + +.list a:hover, +.list a.active { + color: var(--color-primary-800); + text-decoration: underline; +} \ No newline at end of file diff --git a/code/11-index-routes/src/index.css b/code/11-index-routes/src/index.css new file mode 100644 index 0000000000..8f17b6184c --- /dev/null +++ b/code/11-index-routes/src/index.css @@ -0,0 +1,55 @@ +* { + box-sizing: border-box; +} + +:root { + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + + --color-gray-100: #f4f3f1; + --color-gray-200: #dddbd8; + --color-gray-300: #ccc9c6; + --color-gray-400: #aeaba7; + --color-gray-500: #8a8784; + --color-gray-600: #656360; + --color-gray-700: #4b4a47; + --color-gray-800: #31302e; + --color-gray-900: #1f1d1b; + + --color-primary-100: #fcf3e1; + --color-primary-200: #fceccd; + --color-primary-300: #fae1af; + --color-primary-400: #fbd997; + --color-primary-500: #ffd37c; + --color-primary-600: #f9c762; + --color-primary-700: #fbc14d; + --color-primary-800: #fab833; + --color-primary-900: #f6ad1b; +} + +body { + margin: 0; +} + +ul { + list-style: none; + margin: 0; + padding: 0; +} + +main { + margin: 2rem auto; + text-align: center; +} \ No newline at end of file diff --git a/code/11-index-routes/src/index.js b/code/11-index-routes/src/index.js new file mode 100644 index 0000000000..09de846062 --- /dev/null +++ b/code/11-index-routes/src/index.js @@ -0,0 +1,12 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; + +import './index.css'; +import App from './App'; + +const root = ReactDOM.createRoot(document.getElementById('root')); +root.render( + + + +); diff --git a/code/11-index-routes/src/pages/Error.js b/code/11-index-routes/src/pages/Error.js new file mode 100644 index 0000000000..d36a0318eb --- /dev/null +++ b/code/11-index-routes/src/pages/Error.js @@ -0,0 +1,15 @@ +import MainNavigation from '../components/MainNavigation'; + +function ErrorPage() { + return ( + <> + +
+

An error occurred!

+

Could not find this page!

+
+ + ); +} + +export default ErrorPage; diff --git a/code/11-index-routes/src/pages/Home.js b/code/11-index-routes/src/pages/Home.js new file mode 100644 index 0000000000..7190e222a3 --- /dev/null +++ b/code/11-index-routes/src/pages/Home.js @@ -0,0 +1,23 @@ +import { Link, useNavigate } from 'react-router-dom'; + +function HomePage() { + const navigate = useNavigate(); + + function navigateHandler() { + navigate('/products'); + } + + return ( + <> +

My Home Page

+

+ Go to the list of products. +

+

+ +

+ + ); +} + +export default HomePage; diff --git a/code/11-index-routes/src/pages/ProductDetail.js b/code/11-index-routes/src/pages/ProductDetail.js new file mode 100644 index 0000000000..0f8c86b516 --- /dev/null +++ b/code/11-index-routes/src/pages/ProductDetail.js @@ -0,0 +1,15 @@ +import { useParams, Link } from 'react-router-dom'; + +function ProductDetailPage() { + const params = useParams(); + + return ( + <> +

Product Details!

+

{params.productId}

+

Back

+ + ); +} + +export default ProductDetailPage; diff --git a/code/11-index-routes/src/pages/Products.js b/code/11-index-routes/src/pages/Products.js new file mode 100644 index 0000000000..d15679e5ab --- /dev/null +++ b/code/11-index-routes/src/pages/Products.js @@ -0,0 +1,24 @@ +import { Link } from 'react-router-dom'; + +const PRODUCTS = [ + { id: 'p1', title: 'Product 1' }, + { id: 'p2', title: 'Product 2' }, + { id: 'p3', title: 'Product 3' }, +]; + +function ProductsPage() { + return ( + <> +

The Products Page

+
    + {PRODUCTS.map((prod) => ( +
  • + {prod.title} +
  • + ))} +
+ + ); +} + +export default ProductsPage; diff --git a/code/11-index-routes/src/pages/Root.js b/code/11-index-routes/src/pages/Root.js new file mode 100644 index 0000000000..db6babb23a --- /dev/null +++ b/code/11-index-routes/src/pages/Root.js @@ -0,0 +1,16 @@ +import { Outlet } from 'react-router-dom'; + +import MainNavigation from '../components/MainNavigation'; + +function RootLayout() { + return ( + <> + +
+ +
+ + ); +} + +export default RootLayout; diff --git a/code/12-adv-starting-project/backend/app.js b/code/12-adv-starting-project/backend/app.js new file mode 100644 index 0000000000..be62c0f3cd --- /dev/null +++ b/code/12-adv-starting-project/backend/app.js @@ -0,0 +1,24 @@ +const bodyParser = require('body-parser'); +const express = require('express'); + +const eventRoutes = require('./routes/events'); + +const app = express(); + +app.use(bodyParser.json()); +app.use((req, res, next) => { + res.setHeader('Access-Control-Allow-Origin', '*'); + res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PATCH,DELETE'); + res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); + next(); +}); + +app.use('/events', eventRoutes); + +app.use((error, req, res, next) => { + const status = error.status || 500; + const message = error.message || 'Something went wrong.'; + res.status(status).json({ message: message }); +}); + +app.listen(8080); diff --git a/code/12-adv-starting-project/backend/data/event.js b/code/12-adv-starting-project/backend/data/event.js new file mode 100644 index 0000000000..3a5db5ab92 --- /dev/null +++ b/code/12-adv-starting-project/backend/data/event.js @@ -0,0 +1,70 @@ +const fs = require('node:fs/promises'); + +const { v4: generateId } = require('uuid'); + +const { NotFoundError } = require('../util/errors'); + +async function readData() { + const data = await fs.readFile('events.json', 'utf8'); + return JSON.parse(data); +} + +async function writeData(data) { + await fs.writeFile('events.json', JSON.stringify(data)); +} + +async function getAll() { + const storedData = await readData(); + if (!storedData.events) { + throw new NotFoundError('Could not find any events.'); + } + return storedData.events; +} + +async function get(id) { + const storedData = await readData(); + if (!storedData.events || storedData.events.length === 0) { + throw new NotFoundError('Could not find any events.'); + } + + const event = storedData.events.find((ev) => ev.id === id); + if (!event) { + throw new NotFoundError('Could not find event for id ' + id); + } + + return event; +} + +async function add(data) { + const storedData = await readData(); + storedData.events.unshift({ ...data, id: generateId() }); + await writeData(storedData); +} + +async function replace(id, data) { + const storedData = await readData(); + if (!storedData.events || storedData.events.length === 0) { + throw new NotFoundError('Could not find any events.'); + } + + const index = storedData.events.findIndex((ev) => ev.id === id); + if (index < 0) { + throw new NotFoundError('Could not find event for id ' + id); + } + + storedData.events[index] = { ...data, id }; + + await writeData(storedData); +} + +async function remove(id) { + const storedData = await readData(); + const updatedData = storedData.events.filter((ev) => ev.id !== id); + await writeData({events: updatedData}); +} + +exports.getAll = getAll; +exports.get = get; +exports.add = add; +exports.replace = replace; +exports.remove = remove; diff --git a/code/12-adv-starting-project/backend/events.json b/code/12-adv-starting-project/backend/events.json new file mode 100644 index 0000000000..3f458bc3c2 --- /dev/null +++ b/code/12-adv-starting-project/backend/events.json @@ -0,0 +1,11 @@ +{ + "events": [ + { + "id": "e1", + "title": "A dummy event", + "date": "2023-02-22", + "image": "https://blog.hubspot.de/hubfs/Germany/Blog_images/Optimize_Marketing%20Events%20DACH%202021.jpg", + "description": "Join this amazing event and connect with fellow developers." + } + ] +} diff --git a/code/12-adv-starting-project/backend/package.json b/code/12-adv-starting-project/backend/package.json new file mode 100644 index 0000000000..b46c8d32d8 --- /dev/null +++ b/code/12-adv-starting-project/backend/package.json @@ -0,0 +1,17 @@ +{ + "name": "backend-api", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "node app.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "body-parser": "^1.20.0", + "express": "^4.18.1", + "uuid": "^9.0.0" + } +} diff --git a/code/12-adv-starting-project/backend/routes/events.js b/code/12-adv-starting-project/backend/routes/events.js new file mode 100644 index 0000000000..d8dbf375fc --- /dev/null +++ b/code/12-adv-starting-project/backend/routes/events.js @@ -0,0 +1,111 @@ +const express = require('express'); + +const { getAll, get, add, replace, remove } = require('../data/event'); +const { + isValidText, + isValidDate, + isValidImageUrl, +} = require('../util/validation'); + +const router = express.Router(); + +router.get('/', async (req, res, next) => { + try { + const events = await getAll(); + res.json({ events: events }); + } catch (error) { + next(error); + } +}); + +router.get('/:id', async (req, res, next) => { + try { + const event = await get(req.params.id); + res.json({ event: event }); + } catch (error) { + next(error); + } +}); + +router.post('/', async (req, res, next) => { + const data = req.body; + + let errors = {}; + + if (!isValidText(data.title)) { + errors.title = 'Invalid title.'; + } + + if (!isValidText(data.description)) { + errors.description = 'Invalid description.'; + } + + if (!isValidDate(data.date)) { + errors.date = 'Invalid date.'; + } + + if (!isValidImageUrl(data.image)) { + errors.image = 'Invalid image.'; + } + + if (Object.keys(errors).length > 0) { + return res.status(422).json({ + message: 'Adding the event failed due to validation errors.', + errors, + }); + } + + try { + await add(data); + res.status(201).json({ message: 'Event saved.', event: data }); + } catch (error) { + next(error); + } +}); + +router.patch('/:id', async (req, res, next) => { + const data = req.body; + + let errors = {}; + + if (!isValidText(data.title)) { + errors.title = 'Invalid title.'; + } + + if (!isValidText(data.description)) { + errors.description = 'Invalid description.'; + } + + if (!isValidDate(data.date)) { + errors.date = 'Invalid date.'; + } + + if (!isValidImageUrl(data.image)) { + errors.image = 'Invalid image.'; + } + + if (Object.keys(errors).length > 0) { + return res.status(422).json({ + message: 'Updating the event failed due to validation errors.', + errors, + }); + } + + try { + await replace(req.params.id, data); + res.json({ message: 'Event updated.', event: data }); + } catch (error) { + next(error); + } +}); + +router.delete('/:id', async (req, res, next) => { + try { + await remove(req.params.id); + res.json({ message: 'Event deleted.' }); + } catch (error) { + next(error); + } +}); + +module.exports = router; diff --git a/code/12-adv-starting-project/backend/util/errors.js b/code/12-adv-starting-project/backend/util/errors.js new file mode 100644 index 0000000000..db9cf9d805 --- /dev/null +++ b/code/12-adv-starting-project/backend/util/errors.js @@ -0,0 +1,8 @@ +class NotFoundError { + constructor(message) { + this.message = message; + this.status = 404; + } +} + +exports.NotFoundError = NotFoundError; \ No newline at end of file diff --git a/code/12-adv-starting-project/backend/util/validation.js b/code/12-adv-starting-project/backend/util/validation.js new file mode 100644 index 0000000000..c2ddbabd9f --- /dev/null +++ b/code/12-adv-starting-project/backend/util/validation.js @@ -0,0 +1,16 @@ +function isValidText(value) { + return value && value.trim().length > 0; +} + +function isValidDate(value) { + const date = new Date(value); + return value && date !== 'Invalid Date'; +} + +function isValidImageUrl(value) { + return value && value.startsWith('http'); +} + +exports.isValidText = isValidText; +exports.isValidDate = isValidDate; +exports.isValidImageUrl = isValidImageUrl; diff --git a/code/12-adv-starting-project/frontend/package.json b/code/12-adv-starting-project/frontend/package.json new file mode 100644 index 0000000000..edaa0ea9e5 --- /dev/null +++ b/code/12-adv-starting-project/frontend/package.json @@ -0,0 +1,38 @@ +{ + "name": "react-complete-guide", + "version": "0.1.0", + "private": true, + "dependencies": { + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^13.5.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-scripts": "5.0.1", + "web-vitals": "^2.1.4" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + } +} diff --git a/code/12-adv-starting-project/frontend/public/favicon.ico b/code/12-adv-starting-project/frontend/public/favicon.ico new file mode 100644 index 0000000000..a11777cc47 Binary files /dev/null and b/code/12-adv-starting-project/frontend/public/favicon.ico differ diff --git a/code/12-adv-starting-project/frontend/public/index.html b/code/12-adv-starting-project/frontend/public/index.html new file mode 100644 index 0000000000..aa069f27cb --- /dev/null +++ b/code/12-adv-starting-project/frontend/public/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + + + + React App + + + +
+ + + diff --git a/code/12-adv-starting-project/frontend/public/logo192.png b/code/12-adv-starting-project/frontend/public/logo192.png new file mode 100644 index 0000000000..fc44b0a379 Binary files /dev/null and b/code/12-adv-starting-project/frontend/public/logo192.png differ diff --git a/code/12-adv-starting-project/frontend/public/logo512.png b/code/12-adv-starting-project/frontend/public/logo512.png new file mode 100644 index 0000000000..a4e47a6545 Binary files /dev/null and b/code/12-adv-starting-project/frontend/public/logo512.png differ diff --git a/code/12-adv-starting-project/frontend/public/manifest.json b/code/12-adv-starting-project/frontend/public/manifest.json new file mode 100644 index 0000000000..080d6c77ac --- /dev/null +++ b/code/12-adv-starting-project/frontend/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/code/12-adv-starting-project/frontend/public/robots.txt b/code/12-adv-starting-project/frontend/public/robots.txt new file mode 100644 index 0000000000..e9e57dc4d4 --- /dev/null +++ b/code/12-adv-starting-project/frontend/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/code/12-adv-starting-project/frontend/src/App.js b/code/12-adv-starting-project/frontend/src/App.js new file mode 100644 index 0000000000..884fccbe64 --- /dev/null +++ b/code/12-adv-starting-project/frontend/src/App.js @@ -0,0 +1,27 @@ +// Challenge / Exercise + +// 1. Add five new (dummy) page components (content can be simple

elements) +// - HomePage +// - EventsPage +// - EventDetailPage +// - NewEventPage +// - EditEventPage +// 2. Add routing & route definitions for these five pages +// - / => HomePage +// - /events => EventsPage +// - /events/ => EventDetailPage +// - /events/new => NewEventPage +// - /events//edit => EditEventPage +// 3. Add a root layout that adds the component above all page components +// 4. Add properly working links to the MainNavigation +// 5. Ensure that the links in MainNavigation receive an "active" class when active +// 6. Output a list of dummy events to the EventsPage +// Every list item should include a link to the respective EventDetailPage +// 7. Output the ID of the selected event on the EventDetailPage +// BONUS: Add another (nested) layout route that adds the component above all /events... page components + +function App() { + return
; +} + +export default App; diff --git a/code/12-adv-starting-project/frontend/src/components/EventForm.js b/code/12-adv-starting-project/frontend/src/components/EventForm.js new file mode 100644 index 0000000000..f0bc84b08c --- /dev/null +++ b/code/12-adv-starting-project/frontend/src/components/EventForm.js @@ -0,0 +1,39 @@ +import { useNavigate } from 'react-router-dom'; + +import classes from './EventForm.module.css'; + +function EventForm({ method, event }) { + const navigate = useNavigate(); + function cancelHandler() { + navigate('..'); + } + + return ( +
+

+ + +

+

+ + +

+

+ + +

+

+ +