├── vite.config.js ├── .gitignore ├── index.html ├── .eslintrc.cjs ├── src ├── App.css ├── main.jsx ├── App.jsx ├── components │ ├── Users.jsx │ └── Update.jsx ├── index.css └── assets │ └── react.svg ├── package.json └── public └── vite.svg /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { browser: true, es2020: true }, 3 | extends: [ 4 | 'eslint:recommended', 5 | 'plugin:react/recommended', 6 | 'plugin:react/jsx-runtime', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 10 | settings: { react: { version: '18.2' } }, 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': 'warn', 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-crud-client", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint src --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "localforage": "^1.10.0", 14 | "match-sorter": "^6.3.1", 15 | "react": "^18.2.0", 16 | "react-dom": "^18.2.0", 17 | "react-router-dom": "^6.11.0", 18 | "sort-by": "^1.2.0" 19 | }, 20 | "devDependencies": { 21 | "@types/react": "^18.0.28", 22 | "@types/react-dom": "^18.0.11", 23 | "@vitejs/plugin-react": "^4.0.0", 24 | "eslint": "^8.38.0", 25 | "eslint-plugin-react": "^7.32.2", 26 | "eslint-plugin-react-hooks": "^4.6.0", 27 | "eslint-plugin-react-refresh": "^0.3.4", 28 | "vite": "^4.3.2" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | import { 6 | createBrowserRouter, 7 | RouterProvider, 8 | } from "react-router-dom"; 9 | import Users from './components/Users.jsx'; 10 | import Update from './components/Update.jsx'; 11 | 12 | const router = createBrowserRouter([ 13 | { 14 | path: "/", 15 | element: , 16 | }, 17 | { 18 | path:'/users', 19 | element: , 20 | loader: () => fetch('http://localhost:5000/users') 21 | }, 22 | { 23 | path: '/update/:id', 24 | element: , 25 | loader: ({params}) => fetch(`http://localhost:5000/users/${params.id}`) 26 | } 27 | ]); 28 | 29 | ReactDOM.createRoot(document.getElementById('root')).render( 30 | 31 | 32 | , 33 | ) 34 | -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import './App.css' 2 | 3 | function App() { 4 | const handleAddUser = event => { 5 | event.preventDefault(); 6 | const form = event.target; 7 | const name = form.name.value; 8 | const email = form.email.value; 9 | const user = { name, email }; 10 | console.log(user); 11 | 12 | fetch('http://localhost:5000/users', { 13 | method: 'POST', 14 | headers: { 15 | 'content-type': 'application/json' 16 | }, 17 | body: JSON.stringify(user) 18 | }) 19 | .then(res => res.json()) 20 | .then(data => { 21 | console.log(data); 22 | if (data.insertedId) { 23 | alert('Users added successfully'); 24 | form.reset(); 25 | } 26 | }) 27 | } 28 | 29 | return ( 30 | <> 31 |

Simple CRUD

32 |
33 | 34 |
35 | 36 |
37 | 38 |
39 | 40 | ) 41 | } 42 | 43 | export default App 44 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Users.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { Link, useLoaderData } from 'react-router-dom'; 3 | 4 | const Users = () => { 5 | 6 | const loadedUsers = useLoaderData(); 7 | const [users, setUsers] = useState(loadedUsers); 8 | 9 | const handleDelete = _id =>{ 10 | console.log( 'delete', _id); 11 | fetch(`http://localhost:5000/users/${_id}`, { 12 | method: 'DELETE' 13 | }) 14 | .then(res=> res.json()) 15 | .then(data => { 16 | console.log(data); 17 | if(data.deletedCount>0){ 18 | alert('deleted successfully'); 19 | const remaining = users.filter(user => user._id !== _id); 20 | setUsers(remaining); 21 | } 22 | }) 23 | } 24 | 25 | return ( 26 |
27 |

{users.length}

28 |
29 | { 30 | users.map(user =>

{user.name} : {user.email} {user._id} 33 | 34 | 35 | 36 |

) 39 | } 40 |
41 |
42 | ); 43 | }; 44 | 45 | export default Users; -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | -webkit-text-size-adjust: 100%; 15 | } 16 | 17 | a { 18 | font-weight: 500; 19 | color: #646cff; 20 | text-decoration: inherit; 21 | } 22 | a:hover { 23 | color: #535bf2; 24 | } 25 | 26 | body { 27 | margin: 0; 28 | display: flex; 29 | place-items: center; 30 | min-width: 320px; 31 | min-height: 100vh; 32 | } 33 | 34 | h1 { 35 | font-size: 3.2em; 36 | line-height: 1.1; 37 | } 38 | 39 | button { 40 | border-radius: 8px; 41 | border: 1px solid transparent; 42 | padding: 0.6em 1.2em; 43 | font-size: 1em; 44 | font-weight: 500; 45 | font-family: inherit; 46 | background-color: #1a1a1a; 47 | cursor: pointer; 48 | transition: border-color 0.25s; 49 | } 50 | button:hover { 51 | border-color: #646cff; 52 | } 53 | button:focus, 54 | button:focus-visible { 55 | outline: 4px auto -webkit-focus-ring-color; 56 | } 57 | 58 | @media (prefers-color-scheme: light) { 59 | :root { 60 | color: #213547; 61 | background-color: #ffffff; 62 | } 63 | a:hover { 64 | color: #747bff; 65 | } 66 | button { 67 | background-color: #f9f9f9; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/components/Update.jsx: -------------------------------------------------------------------------------- 1 | import { useLoaderData } from 'react-router-dom'; 2 | 3 | const Update = () => { 4 | const loadedUser = useLoaderData(); 5 | 6 | const handleUpdate = event => { 7 | event.preventDefault(); 8 | const form = event.target; 9 | const name = form.name.value; 10 | const email = form.email.value; 11 | console.log(name, email); 12 | const updatedUser = { name, email }; 13 | 14 | fetch(`http://localhost:5000/users/${loadedUser._id}`, { 15 | method: 'PUT', 16 | headers: { 17 | 'content-type': 'application/json' 18 | }, 19 | body: JSON.stringify(updatedUser) 20 | }) 21 | .then(res => res.json()) 22 | .then(data => { 23 | console.log(data); 24 | if (data.modifiedCount > 0) { 25 | alert('user updated successfully') 26 | } 27 | }) 28 | } 29 | 30 | return ( 31 |
32 |

Update information of {loadedUser.name}

33 |
34 | 35 |
36 | 37 |
38 | 39 |
40 |
41 | ); 42 | }; 43 | 44 | export default Update; -------------------------------------------------------------------------------- /src/assets/react.svg: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------------------