{"id":16768733,"url":"https://github.com/phptuts/basketballapp","last_synced_at":"2025-03-16T13:43:08.203Z","repository":{"id":205219431,"uuid":"713705582","full_name":"phptuts/basketballapp","owner":"phptuts","description":null,"archived":false,"fork":false,"pushed_at":"2024-01-09T07:12:37.000Z","size":394,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-23T01:26:40.537Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/phptuts.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-11-03T04:23:39.000Z","updated_at":"2023-11-03T04:23:44.000Z","dependencies_parsed_at":null,"dependency_job_id":"c48c5fba-1dbf-4436-a178-d8d4da2d4442","html_url":"https://github.com/phptuts/basketballapp","commit_stats":null,"previous_names":["phptuts/basketballapp"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phptuts%2Fbasketballapp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phptuts%2Fbasketballapp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phptuts%2Fbasketballapp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phptuts%2Fbasketballapp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/phptuts","download_url":"https://codeload.github.com/phptuts/basketballapp/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243878437,"owners_count":20362432,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-13T06:12:23.669Z","updated_at":"2025-03-16T13:43:08.183Z","avatar_url":"https://github.com/phptuts.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Basketball App\n\n## Challenge 1\n\nCreate a react app that where the app component has an h1 with Hello World. Delete App.css and svg files that are imported.\n\n## Challenge 2\n\nSetup react router dom with the app component as the layout and have one home / index page.\n\n## Challenge 3\n\nCreate a Nav Component for Navigation menu using bootstrap.\n\n- Add the bootstrap cdn link tag the head element in the index.html. Change the the title tag to \"Basketball App\"\n\n```html\n\u003clink\n  href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css\"\n  rel=\"stylesheet\"\n  integrity=\"sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN\"\n  crossorigin=\"anonymous\"\n/\u003e\n```\n\n- Take out the hello world from the App Component and add a navigation component. (components/Nav.jsx component)\n- Add a Nav Component and import it into the App.jsx file. Use this html. Notice that I replace class with className.\n\n```html\n\u003cnav className=\"navbar navbar-expand-lg bg-body-tertiary\"\u003e\n  \u003cdiv className=\"container-fluid\"\u003e\n    \u003ca className=\"navbar-brand\" href=\"#\"\u003eBasketball App\u003c/a\u003e\n    \u003cbutton\n      className=\"navbar-toggler\"\n      type=\"button\"\n      data-bs-toggle=\"collapse\"\n      data-bs-target=\"#navbarSupportedContent\"\n      aria-controls=\"navbarSupportedContent\"\n      aria-expanded=\"false\"\n      aria-label=\"Toggle navigation\"\n    \u003e\n      \u003cspan className=\"navbar-toggler-icon\"\u003e\u003c/span\u003e\n    \u003c/button\u003e\n    \u003cdiv className=\"collapse navbar-collapse\" id=\"navbarSupportedContent\"\u003e\n      \u003cul className=\"navbar-nav me-auto mb-2 mb-lg-0\"\u003e\n        \u003cli className=\"nav-item\"\u003e\n          \u003ca className=\"nav-link active\" aria-current=\"page\" href=\"#\"\u003eHome\u003c/a\u003e\n        \u003c/li\u003e\n        \u003cli className=\"nav-item\"\u003e\n          \u003ca className=\"nav-link\" href=\"#\"\u003eLink\u003c/a\u003e\n        \u003c/li\u003e\n      \u003c/ul\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/nav\u003e\n```\n\nAll the can be found on the [bootstrap website](https://getbootstrap.com/)\n\n## Challenge 4\n\nGet mobile navigation working. When you click on the hamburger menu the drop down show appear. To get it work you will need to add and remove collapse class from this div.\n\n```html\n\u003cdiv className=\"collapse navbar-collapse\" id=\"navbarSupportedContent\"\u003e\u003c/div\u003e\n```\n\n## Challenge 5\n\n- Replace all the a tags with NavLink components from react router dom.\n- Add an about page and replace the a tag that has \"Link\" in it with a NavLink that points to the about page.\n\nHint: You can pass the bootstrap classes in the className as a string.\n\n## Challenge 6\n\nCreate the registration page and menu item.\n\n- In the App component wrap the Outlet with div that has the container class.\n- Create a registration page with this HTML.\n\n```html\n\u003cdiv className=\"row\"\u003e\n  \u003cdiv className=\"col\"\u003e\n    \u003ch1\u003eRegister\u003c/h1\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv className=\"row\"\u003e\n  \u003cdiv className=\"col\"\u003e\n    \u003clabel for=\"email\" className=\"form-label\"\u003eEmail\u003c/label\u003e\n    \u003cinput type=\"email\" id=\"email\" className=\"form-control\" /\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv className=\"row\"\u003e\n  \u003cdiv className=\"col\"\u003e\n    \u003clabel for=\"inputPassword5\" className=\"form-label\"\u003ePassword\u003c/label\u003e\n    \u003cinput type=\"password\" id=\"inputPassword5\" className=\"form-control\" /\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv className=\"row mt-3\"\u003e\n  \u003cdiv className=\"col\"\u003e\n    \u003cbutton className=\"btn btn-primary w-100\"\u003eRegister\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n```\n\n- Add a navigation item to the menu that has the word register in it.\n\n## Challenge 7\n\nCreate the input you will need to submit the information in the register form to the server.\n\n- Use State to store the data from the inputs in the form.\n- Create an onClick for the button that will create a javascript object with the email and password.\n- Console.log the result\n\nNote: We are doing this with each to know what we should build on the backend.\n\n## Challenge 8\n\nCreate an express server using port 3000.\n\n- There should be one route that has a path \"/\"\n- Do not create a controller or route for this, just create in the app.js file.\n- Send \"basketball backend\" from the endpoint\n- Create a postman collection for this endpoint.\n\n## Challenge 9\n\nCreate a register endpoint with a controller and route.\n\n- The endpoint should be /users\n- Use the built in json middleware\n- console.log the post body in the controller\n- return the post body back using the json function in the response object.\n\n## Challenge 10\n\nCreate validate the post body in the register endpoint using yup library.\n\n- Install the yup library.\n- Validate that email field is required and is an actual email.\n- Validate that password has a min of 6 and a max 30 characters long.\n- Return a 400 with bad request when input is invalid.\n\n## Challenge 11\n\nSetup the database and database connection with nodejs and dbbeaver.\n\n- Create a new database using [elephant sql](https://elephantsql.com)\n- Install dotenv and sequelize\n- create a .env file and use that string to authenticate with database in the listen function of app.js\n- Connect your new database with dbbeaver.\n\n## Challenge 12\n\nCreate a UserModel and Migration.\n\n- Create the UserModel with a\n  - id primary key int\n  - email String\n  - password String\n  - createdAt DateTime\n  - updatedAt DateTime\n- Create migration, be sure to use the .env for password information.\n\nHint: use the sequelize documentation to complete this. I will be using it.\n\n## Challenge 13\n\nHave the user endpoint create a user. Create a response function that will create a response json to look like this. The function should take in 3 parameters: type, action, and data.\n\nFor the register user\n\n- type = user\n- data = create user\n- action = \"create\"\n\n```json\n{\n  \"meta\": {\n    \"type\": \"user\",\n    \"action\": \"create\"\n  },\n  \"data\": {\n    \"id\": 4,\n    \"email\": \"noah3@gmail.com\",\n    \"password\": \"moomoo\",\n    \"updatedAt\": \"2023-11-06T17:31:58.531Z\",\n    \"createdAt\": \"2023-11-06T17:31:58.531Z\"\n  }\n}\n```\n\nDon't worry about hashing the password or taking the password from the response. We'll do that in the next challenge.\n\n## Challenge 14\n\nInstall [Bcrypt](https://www.npmjs.com/package/bcrypt) and encrypt the password and ensure that the password is not being shown in the json.\n\n```json\n{\n  \"meta\": {\n    \"type\": \"user\",\n    \"action\": \"create\"\n  },\n  \"data\": {\n    \"id\": 4,\n    \"email\": \"noah3@gmail.com\",\n    \"updatedAt\": \"2023-11-06T17:31:58.531Z\",\n    \"createdAt\": \"2023-11-06T17:31:58.531Z\"\n  }\n}\n```\n\n## Challenge 15\n\nCreate validation around the email to prevent duplicate emails from appearing. The error message should be on the email. It should look like this.\n\n```json\n{\n  \"meta\": {\n    \"type\": \"form_errors\"\n  },\n  \"errors\": {\n    \"email\": [\"Email is already taken.\"]\n  }\n}\n```\n\n## Challenge 16\n\nConnect the register page to backend. If it's successful show a success bootstrap alert with a NavLink to the login page. Look up the html for the bootstrap alert. You will also want to hide the register form as well.\nDon't worry about error messages we'll deal with that in the next challenge. :)\n\n- Install [cors](https://www.npmjs.com/package/cors) npm library and use the middleware.\n- Connect the form to the server using the fetch api.\n\n## Challenge 17\n\nDisplay bootstrap errors if the api returns a 400 status code. Use the bootstrap documentation. If there are multiple errors just display the first one.\n\n## Challenge 18\n\nCreate the login page and menu item.\n\n- Create a Login page with this HTML.\n\n```html\n\u003cdiv className=\"row\"\u003e\n  \u003cdiv className=\"col\"\u003e\n    \u003ch1\u003eLogin\u003c/h1\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv className=\"row\"\u003e\n  \u003cdiv className=\"col\"\u003e\n    \u003clabel for=\"email\" className=\"form-label\"\u003eEmail\u003c/label\u003e\n    \u003cinput type=\"email\" id=\"email\" className=\"form-control\" /\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv className=\"row\"\u003e\n  \u003cdiv className=\"col\"\u003e\n    \u003clabel for=\"inputPassword5\" className=\"form-label\"\u003ePassword\u003c/label\u003e\n    \u003cinput type=\"password\" id=\"inputPassword5\" className=\"form-control\" /\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv className=\"row mt-3\"\u003e\n  \u003cdiv className=\"col\"\u003e\n    \u003cbutton className=\"btn btn-primary w-100\"\u003eLogin\u003c/button\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n```\n\n- Add a navigation item to the menu that has the word Login in it.\n\n## Challenge 19\n\nCreate the input you will need to submit the information in the login form to the server.\n\n- Use State to store the data from the inputs in the form.\n- Create an onClick for the button that will create a javascript object with the email and password.\n- Console.log the result\n\nNote: We are doing this with each to know what we should build on the backend.\n\n## Challenge 20\n\nCreate the login endpoint and the frontend validation. We'll be creating the jwt token in the next challenge. For now you can return a 200 with an empty response.\n\n- Create a login route and controller\n- Create a validator for the login that will validate that the email is present and the password is present only.\n- Send a 400 status with an empty string for response if there is a validation error.\n- Connect the frontend and backend code.\n- If you get a 200 response direct the user /admin\n- If you get a 400 so the form errors.\n- If you get a 401 status response show this alert box under the form. (test this on the server by manually setting that status to 401)\n\n```html\n\u003cdiv className=\"row\"\u003e\n  \u003cdiv className=\"col\"\u003e\n    \u003cdiv className=\"alert alert-success\" role=\"alert\"\u003e\n      Successfully Register!!! Please{\" \"}\n      \u003cNavLink to=\"/login\"\u003elogin\u003c/NavLink\u003e.\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n```\n\n## Challenge 21\n\nIn the login endpoint return a JWT token if the user enters the right email and password. If the user does not enter the right email or password return a 401.\n\n- Create an RSA certificate using https://cryptotools.net/rsagen (4096)\n- Install jsonwebtoken library and follow the instructions here for generating a jwt token. The payload of your token should have the user id and should have alorgithm RS25. The token should expire in 4 hours. Please use the documentation found here: https://www.npmjs.com/package/jsonwebtoken\n\nThe postbody should look like this:\n\n```json\n{\n  \"meta\": {\n    \"type\": \"jwt_token\",\n    \"action\": \"login\"\n  },\n  \"data\": {\n    \"token\": \"example_token\"\n  }\n}\n```\n\nOnce you have a jwt go jwt.io to confirm the payload of your webtoken.\n\n## Challenge 22\n\nImplement useContext hook for authentication with the login page. Show whether the user is logged in or on the home page\n\n- Create an Auth Context that will store whether the user is logged in or not.\n- Change the value stored in that context to true when the user logs in.\n- Redirect the user to the home page\n- useContext hook to show wether the user is logged in on the home page.\n- Save jwt token to localstorage.\n- If the jwt token exists in localstorage default the value stored in the context to true.\n\n## Challenge 23\n\nLogout Button and dynamic menu items.\n\n- Create a logout menu item that redirect the user back to the home page and logs the user out. This should change the value in the auth context. Delete the the jwt token in localstorage.\n- Hide Regiser and Login in the nav when the user is loggedin and the logout menu item. Show register and login when the user is not logged in and hide the logout menu item.\n\nHint: use a button instead of anchor tag for logout menu item.\n\n## Challenge 24\n\nCreate a protected route\n\n- Create an admin page and menu item that is only visible if the user is logged in.\n- Create a component like AuthProvider that will return a children prop only if the user is loggedIn. If the user is not logged in return a Navigate Component from react-router-dom that should redirect the user back home. You will want to use useContext to determine if the user is logged in.\n- When you register the Admin component in the main.jsx file. Wrap it in the component that you created.\n\n```\nelement: \u003cProtectedRoute\u003e\u003cAdmin\u003e\u003c/ProtectedRoute\u003e\n```\n\n## Challenge 25\n\nCreate add game page. It should have a field for the name of the home team, the away team and the time that the game will start. All the fields should be text fields. The route should be /admin/addgame and the game should be wrapped in react router dom form. This will be a protected route so make sure only people logged in can see it.\n\n## Challenge 26\n\nCreate an action connected to the addgame page and console.log the object that will turn into json, in later videos. In the action return a redirect to /game/33. Redirect is a function built into react router dom.\n\nThe json object should look like this:\n\n```json\n{\n  \"hometeam\": \"Thunder\",\n  \"awayteam\": \"Mavs\",\n  \"gametime\": \"3:00 EST\"\n}\n```\n\n## Challenge 27\n\nCreate an endpoint for creating a game. It should take in json that looks like the json above. It should validate that each field has between 5 to 20 characters long and is required. (The game time field should have a 50 character max). It should return json that looks like this. We'll be adding authentication middleware in the next challenge.\n\n```json\n{\n  \"meta\": {\n    \"type\": \"game\",\n    \"action\": \"create\"\n  },\n  \"data\": {\n    \"hometeam\": \"Dawgs\",\n    \"awayteam\": \"Beangles\",\n    \"gametime\": \"Tuesday Nov 3rd at 3pm EST Time\"\n  }\n}\n```\n\n## Challenge 28\n\n### Authenication Middleware\n\nCreate a middle that will attach the user to the request if the user is logged in. This middleware should be applied to every request and it should never fail a requeest, meaning force it return a 403.\n\n### Authorization Middleware\n\nCreate another middleware that will fail a request if the user is not logged in. Apply this middleware to the game endpoints. If this fails it should return a 403 with an empty response.\n\n## Challenge 29\n\nCreate a database table for the basketball game. It should have these fields. Once you have created the table try to create a migration for it. Then create a game in the addGame controller. Default the scores to zero and isLive should be false when created.\n\n- id (primary key) auto increment\n- hometeam string\n- awayteam string\n- hometeamScore int nullable\n- awayteamScore int nullable\n- gametime string\n- isLive boolean (set this to false in the controller)\n- isOver boolean (set this to false in the controller)\n- quarter int (nullable)\n- minutes int (nullable)\n- seconds int (nullable)\n- createdAt datetime\n- updatedAt datetime\n\n```json\n{\n  \"meta\": {\n    \"type\": \"game\",\n    \"action\": \"create\"\n  },\n  \"data\": {\n    \"id\": 1,\n    \"hometeam\": \"Dawgs\",\n    \"awayteam\": \"Beangles\",\n    \"gametime\": \"Tuesday Nov 3rd at 3pm EST Time\",\n    \"isOver\": false,\n    \"isLive\": false,\n    \"updatedAt\": \"2023-11-12T19:44:56.953Z\",\n    \"createdAt\": \"2023-11-12T19:44:56.953Z\",\n    \"hometeamScore\": null,\n    \"awayteamScore\": null,\n    \"quarter\": null,\n    \"minutes\": null,\n    \"seconds\": null\n  }\n}\n```\n\n## Challenge 30\n\nCreate a userId field on the game table that is a foreign key. You will need to rollback the create game to run the migration. Use the sequelize migration and association documentation to complete. This is a tough challenge so go easy on yourself if you watch the video.\n\n## Challenge 31\n\nConnect the addGame Form to server.\n\n- Create a logout route that will delete the jwt token and return a \u003cNavigate to=\"/login\"\u003e component. Replace the current implemention going to the logout route. In that component have a useEffect hook that will delete the jwt token and isLoggedIn to false using that useContext.\n\n- When you make the request use the Authorization header with the bearer token. It should look like this in headers keys\n\n```js\n'Authorization': 'Bearer ' + jwtokenvariable,\n```\n\n- Connect the http://localhost:3000/game to the game page\n  - if you get a 400 return and display the form errors\n  - if you get a 403 redirect to the logout page\n  - if you get a 200 redirect to the admin page\n\n## Challenge 32\n\nCreate the get game endpoint.\n\nThis should be a public endpoint, so will be need adjust the middleware configs. It should return a single. If there is no game it return a 404 status.\n\nThe route should look like this:\n\nGET /game/3\n\n```json\n{\n  \"meta\": {\n    \"type\": \"game\",\n    \"action\": \"get\"\n  },\n  \"data\": {\n    \"id\": 5,\n    \"hometeam\": \"asdfs\",\n    \"awayteam\": \"asdfs\",\n    \"gametime\": \"ssssss\",\n    \"isLive\": false,\n    \"isOver\": false,\n    \"hometeamScore\": null,\n    \"awayteamScore\": null,\n    \"quarter\": null,\n    \"minutes\": null,\n    \"seconds\": null,\n    \"createdAt\": \"2023-11-13T06:35:15.914Z\",\n    \"updatedAt\": \"2023-11-13T06:35:15.914Z\",\n    \"userId\": 7\n  }\n}\n```\n\n## Challenge 33\n\nUpdate AuthContext to store userId and email.\n\nUpdate the login endpoint and AuthContext to store the email address and user id in addition to if the user is logged in. You will need to modify both the node server and the react app. You will want to store these values in localstorage. When the user logs out be sure to unset these values in localstorage and in authcontext.\n\n## Challenge 34\n\nCreate the update score game page.\n\n- The page's url should look like this: /admin/game/5/updatescore\n- The page should have these inputs:\n  - away score\n  - home score\n  - minutes\n  - seconds\n  - quarters\n- Their should be load that loads the game's information.\n  - If the user is not logged in or does not have a user id that matches the game log the user out. I did this in a useEffect because you can't load the context in the loader.\n- Use the game to populate the form with data that there. Test this out by manually setting the data in the database.\n\n### HTML\n\n```jsx\n\u003c\u003e\n  \u003cdiv className=\"row\"\u003e\n    \u003cdiv className=\"col\"\u003e\n      \u003ch1\u003eUpdate Score\u003c/h1\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv className=\"row\"\u003e\n    \u003cdiv className=\"col-6\"\u003e\n      \u003clabel htmlFor=\"hometeamScore\" className=\"form-label\"\u003e\n        Home Team Score\n      \u003c/label\u003e\n      \u003cinput type=\"number\" id=\"hometeamScore\" className=\"form-control\" /\u003e\n    \u003c/div\u003e\n    \u003cdiv className=\"col-6\"\u003e\n      \u003clabel htmlFor=\"awayteamScore\" className=\"form-label\"\u003e\n        Away Team Score\n      \u003c/label\u003e\n      \u003cinput type=\"number\" id=\"awayteamScore\" className=\"form-control\" /\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv className=\"row\"\u003e\n    \u003cdiv className=\"col\"\u003e\n      \u003clabel htmlFor=\"quarter\" className=\"form-label\"\u003e\n        Quater\n      \u003c/label\u003e\n      \u003cinput type=\"number\" id=\"quarter\" className=\"form-control\" /\u003e\n    \u003c/div\u003e\n    \u003cdiv className=\"col\"\u003e\n      \u003clabel htmlFor=\"minutes\" className=\"form-label\"\u003e\n        Minutes\n      \u003c/label\u003e\n      \u003cinput type=\"number\" id=\"minutes\" className=\"form-control\" /\u003e\n    \u003c/div\u003e\n    \u003cdiv className=\"col\"\u003e\n      \u003clabel htmlFor=\"seconds\" className=\"form-label\"\u003e\n        Seconds\n      \u003c/label\u003e\n      \u003cinput type=\"number\" id=\"seconds\" className=\"form-control\" /\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n  \u003cdiv className=\"row mt-3\"\u003e\n    \u003cdiv className=\"col\"\u003e\n      \u003cbutton className=\"btn btn-primary w-100\"\u003eUpdate Score\u003c/button\u003e\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/\u003e\n```\n\n## Challenge 35\n\nCreate update score endpoint\n\n- url: PUT /game/33/updatescore\n- the user must be logged in:\n- should have these json fields:\n  - away score min -\u003e 5 , max -\u003e 200, required\n  - home score min -\u003e 5 , max -\u003e 200, required\n  - minutes min -\u003e 0, max -\u003e 15, required\n  - seconds min -\u003e 0, max -\u003e 60, required\n  - quarters min 1, max -\u003e 4 not required\n- should throw a 403 if the user is trying to update a game they did not create.\n- should return a response that looks like this:\n\n```json\n{\n  \"meta\": {\n    \"type\": \"game\",\n    \"action\": \"update_score\"\n  },\n  \"data\": {\n    \"id\": 5,\n    \"hometeam\": \"asdfs\",\n    \"awayteam\": \"asdfs\",\n    \"gametime\": \"ssssss\",\n    \"isLive\": false,\n    \"isOver\": false,\n    \"hometeamScore\": 34,\n    \"awayteamScore\": 33,\n    \"quarter\": 1,\n    \"minutes\": 2,\n    \"seconds\": 15,\n    \"createdAt\": \"2023-11-13T06:35:15.914Z\",\n    \"updatedAt\": \"2023-11-15T04:49:48.153Z\",\n    \"userId\": 7\n  }\n}\n```\n\n## Challenge 36\n\nConnect the updatescore endpoint to the updatescore form. Use the react toastify library to show if the form was successfully submitted. https://fkhadra.github.io/react-toastify/installation\n\n### Bonus\n\nPass a some text in for the number field. You will notice you get an ulgy error use this link and typeError to fix it on the node server.\n\nhttps://github.com/jquense/yup/issues/211#issuecomment-464253977\n\n## Challenge 37\n\nWhen you submit a form with an error and resubmit the form without an error the error validation message on the form should go away.\n\n## Challenge 38\n\nCreate the edit game endpoint.\n\nPUT /game/33\n\n```json\n{\n  \"hometeam\": \"Reds\",\n  \"awayteam\": \"Blues\",\n  \"gametime\": \"2pm Friday\"\n}\n```\n\n- If user is not logged in return a response with status 404\n- If the game does not exist return a response with status 404\n- If the game was not created by the user return a response with status 404\n- Otherwise update the game and return the game back.\n\nThe response should look like this:\n\n```json\n{\n  \"meta\": {\n    \"type\": \"game\",\n    \"action\": \"update_game\"\n  },\n  \"data\": {\n    \"id\": 5,\n    \"hometeam\": \"asdfs\",\n    \"awayteam\": \"asdfs\",\n    \"gametime\": \"ssssss\",\n    \"isLive\": false,\n    \"isOver\": false,\n    \"hometeamScore\": 34,\n    \"awayteamScore\": 33,\n    \"quarter\": 1,\n    \"minutes\": 2,\n    \"seconds\": 15,\n    \"createdAt\": \"2023-11-13T06:35:15.914Z\",\n    \"updatedAt\": \"2023-11-15T04:49:48.153Z\",\n    \"userId\": 7\n  }\n}\n```\n\n## Challenge 39\n\nCreate the update game page.\n\n- Create a component called GameForm that will where you will copy the JSX from AddGame Component\n  - The component should have title prop and game data prop\n    - Use the title prop to populate the h1 and button text\n    - Use the gameData prop to populate the form. You can pass in the [defaultValue](https://react.dev/reference/react-dom/components/input#providing-an-initial-value-for-an-input) in the input. Hint use the elvic operator \"?\".\n  - Reuse the loader you created in updateScore to get the game data to edit form.\n  - See if you can create function that will be shared by both your editGame and and addGame forms. Hit should take in request, url, http and method\n\nThis is a big challenge take your time.\n\n## Challenge 40\n\nCreate the game page\n\n- url /game/:gameId\n- use the game loader\n- If the game is over display something like final score.\n- If the game is isLive display something like live\n- If the game is isLive or over display the score next to the team names.\n- If both are not set show the game time\n\n## Challenge 41\n\nCreate an two endpoints on the node. Try to re use the update function to do this. You will have to refactor it a little bit. This includes and rearranging the parameters of the function.\n\n### Starting the game\n\nPUT /game/:id/start\n\n- requires authenication\n- requires that the user created the game\n- set isLive to true and isOver to false\n\n### Ends the game\n\nPUT /game/:id/end\n\n- requires authenication\n- requires that the user created the game\n- set isLive to true and isOver to false\n\n## Challenge 42\n\nOn the update score page if the game has not started only show a button that says start game. Once the user clicks on the button call the start game endpoint and show the update score form.\n\nIt game has started show a button to end the game that will call the end game endpoint.\n\nIn this challenge I was able to consolidate everything into one function for all the requests. If you are feeling brave you can do that as a bonus.\n\n## Challenge 43\n\nCreate a paginated game endpoint. Use this documentation to create the ednpoint. https://sequelize.org/docs/v6/core-concepts/model-querying-finders/#findandcountall\n\nGET /game?page=1\u0026user_id=3\n\n- The page size should be 10, meaning 10 games per request loaded.\n- Use a query parameter named page to control the page number. It should start at 1. `?page=2`\n- Use a query parameter to specify the user id you want to filter the games by `user_id=3`\n- This should be an open endpoint that everyone can call.\n- All query parameters should be optional.\n- Adjust the page size to test all scenarios.\n\n### Payload\n\n```json\n{\n  \"meta\": {\n    \"type\": \"game_list\",\n    \"action\": \"get_game_list\",\n    \"page\": 2,\n    \"page_size\": 10,\n    \"number_of_pages\": 3,\n    \"is_last_page\": false,\n    \"is_first_page\": false\n  },\n  \"data\": [...]\n}\n```\n\n## Challenge 44\n\nCreate an admin page that uses that shows some games with pagination.\n\nAdjust the sequelize configuration to use this on the startup. You can adjust it in the init.js file\n\n```js\nconst sequelize = new Sequelize(process.env.DB_CONNECTION_STRING, {\n  pool: {\n    max: 5,\n    min: 0,\n    idle: 10000,\n  },\n});\n```\n\n- Change the page size to 2 to make easier to have more than one page.\n- create a loader for the admin page that will use local storage to get the userId and the request to get page param is available.\n- Display the titles in a list of pages\n- Create 2 nav links to test that the pagination works.\n\nHint use this article to see if you can find away to send the query params without manually constructing them in the string.\nhttps://webtips.dev/solutions/send-query-params-in-get-and-post-in-javascript\n\n## Challenge 45\n\nCreate a table to display the admin page. Each row should have an id, home team, an away team, game time, edit link, and update score link.\n\n## Challenge 46\n\nCreate bootstrap pagination component and use it on the admin page.\nhttps://getbootstrap.com/docs/5.3/components/pagination/\n\nHints:\n\n- You can use this stack over question to create the loop you will need for your pagination: https://stackoverflow.com/questions/3895478/does-javascript-have-a-method-like-range-to-generate-a-range-within-the-supp\n- Use the useSearchParams hook to change the pagination. Read the documentation and see if you can keep all the other query parameters in place.\n- Do not use NavLinks or Urls to change the pagination, instead use a function that uses that hook. You can change the a tags to span tags.\n\n## Challenge 47\n\nCreate a migration migrations that will change the gametime to Date type in sequelize. You will need to delete all of your games before doing this. This will be a required field.\n\n## Challenge 48\n\nChange the game time validation to only use a number.\n\n- Use the Date.now() function to make sure the timestamp you are submitting is in the future.\n- Use this website to create a timestamp and multiply that number 1000. You are submitting the number of milliseconds.\n  https://www.unixtimestamp.com/\n\n## Challenge 49\n\nFix the forms and display to deal with the new date time refactoring.\n\n- Fix the create and update forms to send a timestamp.\n  - Change the input type to datetime-local\n  - For the create calls calls convert the date time string into a timestamp. You can use the getTime() function. new Date(timestring).getTime(). https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTime\n  - For the game time input you will need to convert it into date using this example: https://stackoverflow.com/a/61082536\n  - Fix the displays for the gametime. I used toLocaleString which you can find here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString\n\n## Challenge 50\n\nChange the h1 of the admin page to say \"My Games\" and if there is no need for pagination, display an empty fragment \u003c\u003e\u003c/\u003e in the pagination component. You will need adjust the node server.\n\n## Challenge 51\n\nAdjust the games endpoint to accept a type query parameter. ?type=all. There should be 3 types\n\n- all -\u003e means don't filter anything\n- live -\u003e means only show live games\n- over -\u003e means only show games that are over\n- not_started -\u003e means only show games that have not started yet.\n\nAdjust this on the node side and on the react side.\n\nYou will also notice a bug in the pagination. You will need to fix that bug. When you are filter notice that type does not get transfered to the url.\n\nHINT: Use the loader to make the request for the games and not an on change function.\n\n## Challenge 52\n\nCreate a optional query parameter called search that will search the hometeam or the awayteam with term that is typed in. It should use or logic meaning:\n\n(hometeam ilike %term% or awayteam ilike %term%)\n\nYou will want to use ilike for the comparison and not the equal one. Give this your best shot and if you can't figure it out after 5 or 10 minutes watch the video. :)\n\n## Challenge 53\n\nCreate a home page loader that will take in query parameters and create a get games request. It should be able to handle the search, type, and page query parameters. See if you can re use code.\n\nFor now go ahead and load the home teams in an li and load the pagination component.\n\nThere are two bugs I want you to fix as well. One is sending null for the user id will break nodejs app. Meaning that if you send a string for the user id it will break. The other is sending the null string for the user id and other query paramters on the frontend.\n\n## Challenge 54\n\nCreate a create component for showing the individual games. I am going to keep it simple. Feel free to make this challenge yours.\n\n## Challenge 55\n\nCreate a game search bar that will have the type filter and search filter. Use the loader and useSearchParams to get the new page.\n\n## Challenge 56\n\nWhenever a user update a game I want you to send a web socket message with the game the user updated. Pass the web socket server object to the request via middleware. Delete all the code we put into the home endpoint and delete the on message code as well.\n\n## Challenge 57\n\nUsing this [webpage](https://javascript.info/websocket) and useContext connect the frontend to web sockets. Your provider for the the context should be wrapped around the AuthProvider. You will want to update the game and the game component so that that game search results and the individual game page changes when you update the game.\n\n## Challenge 58\n\nUsing error middleware create a middleware to handle errors. It should be used close to the bottom of your app, next to the .listen. Use this as a [guide](https://expressjs.com/en/guide/error-handling.html). Log out the error and be sure to return a response that looks like this with a unique id. Use [uuid](https://www.npmjs.com/package/uuid) library to create the unique id. Throw an Error in one of the controllers to test it out. Take out the error once you are done.\n\n### Example Response\n\n```json\n{\n  \"meta\": {\n    \"type\": \"error\"\n  },\n  \"data\": \"f87e806a-e110-4108-a699-a136e525bda1\"\n}\n```\n\n## Challenge 59\n\nCreate an error page using the errorElement in react router dom. Use the useRouteError hook to get the error and display the error message. Use the App.jsx jsx to help create the error page. Here is the [documenation](https://reactrouter.com/en/main/route/error-element) for errorElement.\n\n## Challenge 60\n\n- Move the gameloader from UpdateScore.jsx file to to the game.helper.js file.\n- When the user goes to a page that does not exist show a \"Page Not Found\" in the h1 of the error page.\n- When a user goes to a game that does not exist throw a new Error that says game not found.\n\n## Challenge 61\n\nCreate a function called serverErrorCheck that will see if the response has a status of 500. If it does it will throw an error. If there is an id in the error message then display the id to the user, otherwise say unknown error.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphptuts%2Fbasketballapp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fphptuts%2Fbasketballapp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphptuts%2Fbasketballapp/lists"}