├── .eslintrc.json ├── next.config.js ├── public ├── favicon.ico └── chucknorris_logo_coloured_small.png ├── pages ├── _app.js ├── api │ └── hello.js └── index.js ├── styles ├── globals.css └── Home.module.css ├── .gitignore ├── package.json ├── .github └── workflows │ └── node.js.yml └── README.md /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | reactStrictMode: true, 3 | } 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scraper/chuck-norris-joke-app/master/public/favicon.ico -------------------------------------------------------------------------------- /public/chucknorris_logo_coloured_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scraper/chuck-norris-joke-app/master/public/chucknorris_logo_coloured_small.png -------------------------------------------------------------------------------- /pages/_app.js: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css' 2 | 3 | function MyApp({ Component, pageProps }) { 4 | return 5 | } 6 | 7 | export default MyApp 8 | -------------------------------------------------------------------------------- /pages/api/hello.js: -------------------------------------------------------------------------------- 1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction 2 | 3 | export default function handler(req, res) { 4 | res.status(200).json({ name: 'John Doe' }) 5 | } 6 | -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | padding: 0; 4 | margin: 0; 5 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, 6 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 7 | } 8 | 9 | a { 10 | color: inherit; 11 | text-decoration: none; 12 | } 13 | 14 | * { 15 | box-sizing: border-box; 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chuck-norris-joke-app", 3 | "private": true, 4 | "author": "Petro Franko", 5 | "license": "ISC", 6 | "scripts": { 7 | "dev": "next dev", 8 | "build": "next build", 9 | "start": "next start", 10 | "lint": "next lint" 11 | }, 12 | "dependencies": { 13 | "bootstrap": "^5.1.3", 14 | "next": "12.0.3", 15 | "node-fetch": "^3.1.0", 16 | "react": "17.0.2", 17 | "react-bootstrap": "^2.0.2", 18 | "react-dom": "17.0.2" 19 | }, 20 | "devDependencies": { 21 | "eslint": "7.32.0", 22 | "eslint-config-next": "12.0.3" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [12.x, 14.x, 16.x] 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | 22 | steps: 23 | - uses: actions/checkout@v3 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v3 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | cache: 'npm' 29 | - run: npm ci 30 | - run: npm run build --if-present 31 | - run: npm run lint 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | 15 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. 16 | 17 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. 18 | 19 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 20 | 21 | ## Learn More 22 | 23 | To learn more about Next.js, take a look at the following resources: 24 | 25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 27 | 28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 29 | 30 | ## Deploy on Vercel 31 | 32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 33 | 34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 35 | -------------------------------------------------------------------------------- /styles/Home.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | padding: 0 2rem; 3 | } 4 | 5 | .main { 6 | min-height: 100vh; 7 | padding: 4rem 0; 8 | flex: 1; 9 | display: flex; 10 | flex-direction: column; 11 | justify-content: center; 12 | align-items: center; 13 | } 14 | 15 | .footer { 16 | display: flex; 17 | flex: 1; 18 | padding: 2rem 0; 19 | border-top: 1px solid #eaeaea; 20 | justify-content: center; 21 | align-items: center; 22 | } 23 | 24 | .footer a { 25 | display: flex; 26 | justify-content: center; 27 | align-items: center; 28 | flex-grow: 1; 29 | } 30 | 31 | .title a { 32 | color: #0070f3; 33 | text-decoration: none; 34 | } 35 | 36 | .title a:hover, 37 | .title a:focus, 38 | .title a:active { 39 | text-decoration: underline; 40 | } 41 | 42 | .title { 43 | margin: 0; 44 | line-height: 1.15; 45 | font-size: 3rem; 46 | } 47 | 48 | .title, 49 | .description { 50 | text-align: center; 51 | margin-bottom: 1em; 52 | } 53 | 54 | .description { 55 | margin: 4rem 0; 56 | line-height: 1.5; 57 | font-size: 1.5rem; 58 | } 59 | 60 | .code { 61 | background: #fafafa; 62 | border-radius: 5px; 63 | padding: 0.75rem; 64 | font-size: 1.1rem; 65 | font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, 66 | Bitstream Vera Sans Mono, Courier New, monospace; 67 | } 68 | 69 | .grid { 70 | display: flex; 71 | align-items: center; 72 | justify-content: center; 73 | flex-wrap: wrap; 74 | /* max-width: 800px; */ 75 | /* padding:20px; */ 76 | } 77 | 78 | .card { 79 | margin: 1rem; 80 | padding: 1.5rem; 81 | text-align: left; 82 | color: inherit; 83 | text-decoration: none; 84 | border: 1px solid #eaeaea; 85 | border-radius: 10px; 86 | transition: color 0.15s ease, border-color 0.15s ease; 87 | max-width: 300px; 88 | } 89 | 90 | .card:hover, 91 | .card:focus, 92 | .card:active { 93 | color: #0070f3; 94 | border-color: #0070f3; 95 | } 96 | 97 | .card h2 { 98 | margin: 0 0 1rem 0; 99 | font-size: 1.5rem; 100 | } 101 | 102 | .card p { 103 | margin: 0; 104 | font-size: 1.25rem; 105 | line-height: 1.5; 106 | } 107 | 108 | .logo { 109 | height: 1em; 110 | margin-left: 0.5rem; 111 | } 112 | 113 | @media (max-width: 600px) { 114 | .grid { 115 | width: 100%; 116 | flex-direction: column; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /pages/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import Head from "next/head"; 3 | import Image from "next/image"; 4 | import styles from "../styles/Home.module.css"; 5 | import "bootstrap/dist/css/bootstrap.min.css"; 6 | import { 7 | InputGroup, 8 | Alert, 9 | Form, 10 | ListGroup, 11 | Button, 12 | Container, 13 | Row, 14 | Col, 15 | } from "react-bootstrap"; 16 | 17 | export default function Home({ randomJoke, categories }) { 18 | const [selectedCategory, setSelectedCategory] = useState(categories[""]); 19 | const [currentJoke, setCurrentJoke] = useState(randomJoke.value); 20 | const [currentSearchQuery, setCurrentSearchQuery] = useState(""); 21 | const [currentSearchRes, setCurrentSearchRes] = useState([]); 22 | 23 | const getJoke = async () => { 24 | if (selectedCategory && selectedCategory !== "random") { 25 | const res = await fetch( 26 | `https://api.chucknorris.io/jokes/random?category=${selectedCategory}` 27 | ); 28 | const joke = await res.json(); 29 | return setCurrentJoke(joke.value); 30 | } 31 | const res = await fetch(`https://api.chucknorris.io/jokes/random`); 32 | const joke = await res.json(); 33 | return setCurrentJoke(joke.value); 34 | }; 35 | const handleCategoryChange = (e) => { 36 | e.preventDefault(); 37 | getJoke(); 38 | }; 39 | 40 | const findJoke = async () => { 41 | console.log("currentSearchQuery: ", currentSearchQuery); 42 | if (currentSearchQuery && currentSearchQuery.length > 2) { 43 | const res = await fetch( 44 | `https://api.chucknorris.io/jokes/search?query=${currentSearchQuery}` 45 | ); 46 | const jokes = await res.json(); 47 | return setCurrentSearchRes(jokes.result.slice(0, 5)); 48 | } 49 | return setCurrentSearchRes([]); 50 | }; 51 | useEffect(() => { 52 | findJoke(); 53 | }, [currentSearchQuery]); 54 | 55 | const handleSearch = (e) => { 56 | e.preventDefault(); 57 | findJoke(); 58 | }; 59 | 60 | return ( 61 | 62 | 63 | Chuck Norris React App 64 | 65 | 66 | 67 | 68 |
69 | 70 | Chuck Norris 76 | 77 |
78 | 79 | 80 |

Welcome to Chuck Norris Jokes

81 | 82 |
83 |
84 | 85 | 86 | {currentJoke} 87 | 88 | 89 | 90 | 91 | Select category 92 | 93 | setSelectedCategory(e.target.value)} 98 | > 99 | 100 | {categories.map((category, index) => ( 101 | 104 | ))} 105 | 106 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | setCurrentSearchQuery(e.target.value)} 121 | /> 122 | 125 | 126 | 127 | 128 |
129 |
130 | 131 | {currentSearchRes.map((res, index) => ( 132 | {res.value} 133 | ))} 134 | 135 |
136 |
137 | 138 | 139 |
140 | ); 141 | } 142 | export async function getServerSideProps() { 143 | const randomJokeRes = await fetch("https://api.chucknorris.io/jokes/random"); 144 | const randomJoke = await randomJokeRes.json(); 145 | const categoriesRes = await fetch( 146 | "https://api.chucknorris.io/jokes/categories" 147 | ); 148 | const categories = await categoriesRes.json(); 149 | 150 | return { 151 | props: { 152 | randomJoke, 153 | categories, 154 | }, 155 | }; 156 | } 157 | --------------------------------------------------------------------------------