├── .gitignore ├── README.md ├── db.json ├── package-lock.json ├── package.json ├── public ├── favicon.ico └── index.html ├── src ├── App.css ├── App.js ├── index.css ├── index.js └── pages │ ├── Home.js │ ├── Navbar.js │ └── user │ ├── Add.js │ ├── Edit.js │ └── Users.js └── tailwind.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CRUD 2 | 3 | 4 | A React CRUD App using Tailwind CSS, Axios, React-Router-Dom and Json-Server. 5 | 6 | 7 | We've uesd the fake JSON-server for our fake APIs and use React Axios package to perform HTTP Requests in react. 8 | 9 | 10 | We follow the RESTful API convention (GET, POST, PUT, DELETE) and see how easily we do CREATE, READ, UPDATE and DELETE operations on our User Details. 11 | 12 | 13 | We've learned so far about how to send data to server and get the response back from server. 14 | 15 | 16 | We've seen how we can update our React state with the data received from the server and render our UI with that updated data. 17 | -------------------------------------------------------------------------------- /db.json: -------------------------------------------------------------------------------- 1 | { 2 | "users": [ 3 | { 4 | "name": "Ervin Howell", 5 | "email": "Ervin@melissa.tv", 6 | "phone": "010-692-6593 x09125", 7 | "id": 2 8 | }, 9 | { 10 | "id": 3, 11 | "name": "Clementine Bauch", 12 | "username": "Samantha", 13 | "email": "Nathan@yesenia.net", 14 | "address": { 15 | "street": "Douglas Extension", 16 | "suite": "Suite 847", 17 | "city": "McKenziehaven", 18 | "zipcode": "59590-4157", 19 | "geo": { 20 | "lat": "-68.6102", 21 | "lng": "-47.0653" 22 | } 23 | }, 24 | "phone": "1-463-123-4447", 25 | "website": "ramiro.info", 26 | "company": { 27 | "name": "Romaguera-Jacobson", 28 | "catchPhrase": "Face to face bifurcated interface", 29 | "bs": "e-enable strategic applications" 30 | } 31 | }, 32 | { 33 | "id": 4, 34 | "name": "Patricia Lebsack", 35 | "username": "Karianne", 36 | "email": "Julianne.OConner@kory.org", 37 | "address": { 38 | "street": "Hoeger Mall", 39 | "suite": "Apt. 692", 40 | "city": "South Elvis", 41 | "zipcode": "53919-4257", 42 | "geo": { 43 | "lat": "29.4572", 44 | "lng": "-164.2990" 45 | } 46 | }, 47 | "phone": "493-170-9623 x156", 48 | "website": "kale.biz", 49 | "company": { 50 | "name": "Robel-Corkery", 51 | "catchPhrase": "Multi-tiered zero tolerance productivity", 52 | "bs": "transition cutting-edge web services" 53 | } 54 | }, 55 | { 56 | "id": 5, 57 | "name": "Chelsey Dietrich", 58 | "username": "Kamren", 59 | "email": "Lucio_Hettinger@annie.ca", 60 | "address": { 61 | "street": "Skiles Walks", 62 | "suite": "Suite 351", 63 | "city": "Roscoeview", 64 | "zipcode": "33263", 65 | "geo": { 66 | "lat": "-31.8129", 67 | "lng": "62.5342" 68 | } 69 | }, 70 | "phone": "(254)954-1289", 71 | "website": "demarco.info", 72 | "company": { 73 | "name": "Keebler LLC", 74 | "catchPhrase": "User-centric fault-tolerant solution", 75 | "bs": "revolutionize end-to-end systems" 76 | } 77 | }, 78 | { 79 | "id": 6, 80 | "name": "Mrs. Dennis Schulist", 81 | "username": "Leopoldo_Corkery", 82 | "email": "Karley_Dach@jasper.info", 83 | "address": { 84 | "street": "Norberto Crossing", 85 | "suite": "Apt. 950", 86 | "city": "South Christy", 87 | "zipcode": "23505-1337", 88 | "geo": { 89 | "lat": "-71.4197", 90 | "lng": "71.7478" 91 | } 92 | }, 93 | "phone": "1-477-935-8478 x6430", 94 | "website": "ola.org", 95 | "company": { 96 | "name": "Considine-Lockman", 97 | "catchPhrase": "Synchronised bottom-line interface", 98 | "bs": "e-enable innovative applications" 99 | } 100 | }, 101 | { 102 | "id": 7, 103 | "name": "Kurtis Weissnat", 104 | "username": "Elwyn.Skiles", 105 | "email": "Telly.Hoeger@billy.biz", 106 | "address": { 107 | "street": "Rex Trail", 108 | "suite": "Suite 280", 109 | "city": "Howemouth", 110 | "zipcode": "58804-1099", 111 | "geo": { 112 | "lat": "24.8918", 113 | "lng": "21.8984" 114 | } 115 | }, 116 | "phone": "210.067.6132", 117 | "website": "elvis.io", 118 | "company": { 119 | "name": "Johns Group", 120 | "catchPhrase": "Configurable multimedia task-force", 121 | "bs": "generate enterprise e-tailers" 122 | } 123 | }, 124 | { 125 | "name": "Curt Hendricks", 126 | "email": "cobolt@codename.com", 127 | "phone": "0000-22-0000", 128 | "id": 8 129 | } 130 | ] 131 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crud", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.3", 7 | "@testing-library/react": "^12.1.4", 8 | "@testing-library/user-event": "^13.5.0", 9 | "axios": "^0.26.1", 10 | "json-server": "^0.17.0", 11 | "react": "^18.0.0", 12 | "react-dom": "^18.0.0", 13 | "react-router-dom": "^6.3.0", 14 | "react-scripts": "5.0.0", 15 | "web-vitals": "^2.1.4" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "json-server": "json-server --watch db.json --port 3001", 20 | "build": "react-scripts build", 21 | "test": "react-scripts test", 22 | "eject": "react-scripts eject" 23 | }, 24 | "eslintConfig": { 25 | "extends": [ 26 | "react-app", 27 | "react-app/jest" 28 | ] 29 | }, 30 | "browserslist": { 31 | "production": [ 32 | ">0.2%", 33 | "not dead", 34 | "not op_mini all" 35 | ], 36 | "development": [ 37 | "last 1 chrome version", 38 | "last 1 firefox version", 39 | "last 1 safari version" 40 | ] 41 | }, 42 | "devDependencies": { 43 | "autoprefixer": "^10.4.4", 44 | "postcss": "^8.4.12", 45 | "tailwindcss": "^3.0.23" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geeky-mind/CRUD/dd8feeaeab5f64559c0b0eb103661060778748f0/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | REACT 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | 40 | @tailwind base; 41 | @tailwind components; 42 | @tailwind utilities; 43 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import { Route, Routes } from 'react-router-dom'; 2 | import './App.css'; 3 | import Home from './pages/Home'; 4 | import Navbar from './pages/Navbar'; 5 | import Add from './pages/user/Add'; 6 | import Edit from './pages/user/Edit'; 7 | import Users from './pages/user/Users'; 8 | 9 | function App() { 10 | return ( 11 |
12 | 13 | 14 | } /> 15 | } /> 16 | } /> 17 | } /> 18 | } /> 19 | 20 |
21 | ); 22 | } 23 | 24 | export default App; 25 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import "./index.css"; 4 | import App from "./App"; 5 | import { BrowserRouter } from "react-router-dom"; 6 | 7 | const root = ReactDOM.createRoot(document.getElementById("root")); 8 | root.render( 9 | 10 | 11 | 12 | 13 | 14 | ); -------------------------------------------------------------------------------- /src/pages/Home.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useEffect, useState } from "react"; 3 | import { Link } from "react-router-dom"; 4 | 5 | function Home() { 6 | const [users, setUsers] = useState([]); 7 | 8 | function loadUsers() { 9 | axios.get("http://localhost:3001/users").then((res) => { 10 | setUsers(res.data.reverse()); 11 | }); 12 | } 13 | 14 | useEffect(() => { 15 | loadUsers(); 16 | }, []); 17 | 18 | function deleteUser(id) { 19 | axios.delete(`http://localhost:3001/users/${id}`).then(loadUsers()); 20 | } 21 | 22 | return ( 23 | <> 24 |
25 |

DATA TABLE

26 |
27 |
28 |
29 |
30 | 31 | 32 | 33 | 39 | 45 | 51 | 57 | 63 | 64 | 65 | 66 | {users.map((data, index) => ( 67 | 71 | 74 | 77 | 80 | 83 | 104 | 105 | ))} 106 | 107 |
37 | # 38 | 43 | Name 44 | 49 | Email 50 | 55 | Phone 56 | 61 | Action 62 |
72 | {index + 1} 73 | 75 | {data.name} 76 | 78 | {data.email} 79 | 81 | {data.phone} 82 | 84 | 88 | VIew 89 | 90 | 94 | Edit 95 | 96 | deleteUser(data.id)} 98 | to={"#"} 99 | className="bg-red-600 text-white px-6 py-2 rounded-lg" 100 | > 101 | Delete 102 | 103 |
108 |
109 |
110 |
111 |
112 |
113 | 114 | ); 115 | } 116 | 117 | export default Home; 118 | -------------------------------------------------------------------------------- /src/pages/Navbar.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'react-router-dom' 3 | 4 | function Navbar() { 5 | return ( 6 | <> 7 |
8 | CRUD 9 | Add Users 11 |
12 | 13 | ) 14 | } 15 | 16 | export default Navbar -------------------------------------------------------------------------------- /src/pages/user/Add.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useState } from "react"; 3 | import { useNavigate } from "react-router-dom"; 4 | 5 | function Add() { 6 | const [name, setName] = useState(""); 7 | const [email, setEmail] = useState(""); 8 | const [phone, setPhone] = useState(""); 9 | 10 | const navigate = useNavigate(); 11 | const data = { 12 | name: name, 13 | email: email, 14 | phone: phone, 15 | }; 16 | 17 | function submitForm(e) { 18 | e.preventDefault(); 19 | axios.post("http://localhost:3001/users", data).then(navigate("/")); 20 | } 21 | return ( 22 |
23 |

ADD USER

24 |
25 | setName(e.target.value)} 28 | className="bg-white/10 outline-none font-normal border border-zinc-400 py-6 pl-6 mt-4" 29 | type="text" 30 | placeholder="Enter your name" 31 | /> 32 | setEmail(e.target.value)} 35 | className="bg-white/10 outline-none font-normal border border-zinc-400 py-6 pl-6 mt-4" 36 | type="email" 37 | placeholder="Enter your email" 38 | /> 39 | setPhone(e.target.value)} 42 | className="bg-white/10 outline-none font-normal border border-zinc-400 py-6 pl-6 mt-4" 43 | type="phone" 44 | placeholder="Enter your phone no." 45 | /> 46 | 53 |
54 |
55 | ); 56 | } 57 | 58 | export default Add; 59 | -------------------------------------------------------------------------------- /src/pages/user/Edit.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useEffect, useState } from "react"; 3 | import { useNavigate, useParams } from "react-router-dom"; 4 | 5 | function Add() { 6 | const [name, setName] = useState(""); 7 | const [email, setEmail] = useState(""); 8 | const [phone, setPhone] = useState(""); 9 | 10 | const { id } = useParams(); 11 | 12 | useEffect(() => { 13 | axios.get(`http://localhost:3001/users/${id}`).then((res) => { 14 | setName(res.data.name); 15 | setEmail(res.data.email); 16 | setPhone(res.data.phone); 17 | }); 18 | }, []); 19 | 20 | const navigate = useNavigate(); 21 | 22 | const data = { 23 | name: name, 24 | email: email, 25 | phone: phone, 26 | }; 27 | 28 | function Update(e) { 29 | e.preventDefault(); 30 | axios.put(`http://localhost:3001/users/${id}`, data).then(navigate("/")); 31 | } 32 | return ( 33 |
34 |

User Details

35 |
36 | setName(e.target.value)} 39 | className="bg-white/10 outline-none font-normal border border-zinc-400 py-6 pl-6 mt-4" 40 | type="text" 41 | placeholder="Enter your name" 42 | /> 43 | setEmail(e.target.value)} 46 | className="bg-white/10 outline-none font-normal border border-zinc-400 py-6 pl-6 mt-4" 47 | type="email" 48 | placeholder="Enter your email" 49 | /> 50 | setPhone(e.target.value)} 53 | className="bg-white/10 outline-none font-normal border border-zinc-400 py-6 pl-6 mt-4" 54 | type="phone" 55 | placeholder="Enter your phone no." 56 | /> 57 | 64 |
65 |
66 | ); 67 | } 68 | 69 | export default Add; 70 | -------------------------------------------------------------------------------- /src/pages/user/Users.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useEffect, useState } from "react"; 3 | import { Link, useParams } from "react-router-dom"; 4 | 5 | function Users() { 6 | const { id } = useParams(); 7 | 8 | const [user, setUser] = useState([]); 9 | 10 | useEffect(() => { 11 | axios.get(`http://localhost:3001/users/${id}`).then((res) => { 12 | setUser(res.data); 13 | }); 14 | }, []); 15 | 16 | console.log(user); 17 | return ( 18 | <> 19 |
20 | 24 | Back To Home 25 | 26 | {user && ( 27 |
28 |
29 |

30 | Name 31 |

32 |

33 | Email 34 |

35 |

36 | Phone 37 |

38 |
39 |
40 |

41 | {user.name} 42 |

43 |

44 | {user.email} 45 |

46 |

47 | {user.phone} 48 |

49 |
50 |
51 | )} 52 |
53 | 54 | ); 55 | } 56 | 57 | export default Users; 58 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./src/**/*.{html,js}"], 3 | theme: { 4 | screens: { 5 | '2xl': {'max': '1535px'}, 6 | // => @media (max-width: 1535px) { ... } 7 | 8 | 'xl': {'max': '1279px'}, 9 | // => @media (max-width: 1279px) { ... } 10 | 11 | 'lg': {'max': '1023px'}, 12 | // => @media (max-width: 1023px) { ... } 13 | 14 | 'md': {'max': '767px'}, 15 | // => @media (max-width: 767px) { ... } 16 | 17 | 'sm': {'max': '639px'}, 18 | // => @media (max-width: 639px) { ... } 19 | } 20 | }, 21 | plugins: [], 22 | } 23 | --------------------------------------------------------------------------------