├── .gitignore
├── package-lock.json
├── package.json
├── public
├── favicon.png
└── index.html
└── src
├── App.js
├── assets
└── styles
│ ├── global
│ ├── _fonts.scss
│ └── _reset.scss
│ └── index.scss
├── components
├── Editable.js
├── ReactForm.js
└── ReadOnly.js
└── index.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 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "17",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.5",
7 | "@testing-library/react": "^13.3.0",
8 | "@testing-library/user-event": "^13.5.0",
9 | "bootstrap": "^5.2.0",
10 | "react": "^18.2.0",
11 | "react-bootstrap": "^2.5.0",
12 | "react-dom": "^18.2.0",
13 | "react-icons": "^4.4.0",
14 | "react-scripts": "5.0.1",
15 | "sass": "^1.54.4",
16 | "uuid": "^8.3.2",
17 | "web-vitals": "^2.1.4"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": [
27 | "react-app",
28 | "react-app/jest"
29 | ]
30 | },
31 | "browserslist": {
32 | "production": [
33 | ">0.2%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 1 chrome version",
39 | "last 1 firefox version",
40 | "last 1 safari version"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/parham-ab/react-crud-table/c8010dc55de4353a93559a6cacb1898106e174ae/public/favicon.png
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 | CRUD Table
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | // components
2 | import ReactForm from "./components/ReactForm";
3 |
4 | const App = () => {
5 | return ;
6 | };
7 |
8 | export default App;
--------------------------------------------------------------------------------
/src/assets/styles/global/_fonts.scss:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css2?family=Quicksand&family=Rubik:ital,wght@0,300;1,300&display=swap");
--------------------------------------------------------------------------------
/src/assets/styles/global/_reset.scss:
--------------------------------------------------------------------------------
1 | // CSS Reset
2 | *,
3 | *::before,
4 | *::after {
5 | margin: 0;
6 | padding: 0;
7 | border: none;
8 | outline: none;
9 | box-sizing: border-box;
10 | text-decoration: none;
11 | user-select: none;
12 | }
13 | a {
14 | color: inherit;
15 | }
16 | body {
17 | font-family: "Quicksand", sans-serif !important;
18 | font-family: "Rubik", sans-serif !important;
19 | }
20 | // bootstrap
21 | textarea:hover,
22 | textarea:active,
23 | textarea:focus,
24 | input:hover,
25 | input:active,
26 | input:focus,
27 | select:hover,
28 | select:active,
29 | select:focus,
30 | button:hover,
31 | button:active,
32 | button:focus,
33 | label:focus,
34 | .btn:active,
35 | .btn.active {
36 | outline: 0px !important;
37 | -webkit-appearance: none;
38 | box-shadow: none !important;
39 | }
40 | @import "~bootstrap/scss/bootstrap";
41 | // scroll bar
42 | ::-webkit-scrollbar {
43 | width: 9px;
44 | }
45 | ::-webkit-scrollbar-track {
46 | background-color: transparent;
47 | }
48 | ::-webkit-scrollbar-thumb {
49 | background-color: rgb(158, 158, 158);
50 | border-radius: 5px;
51 | }
52 | ::-webkit-scrollbar-thumb:hover {
53 | background-color: rgb(187, 187, 187);
54 | }
55 | ::-webkit-scrollbar-thumb:active {
56 | background-color: rgb(207, 207, 207);
57 | }
58 | svg {
59 | font-size: 23px;
60 | cursor: pointer;
61 | }
--------------------------------------------------------------------------------
/src/assets/styles/index.scss:
--------------------------------------------------------------------------------
1 | @import "global/reset";
2 | @import "global/fonts";
3 |
--------------------------------------------------------------------------------
/src/components/Editable.js:
--------------------------------------------------------------------------------
1 | // bootstrap
2 | import { Form } from "react-bootstrap";
3 | // icons
4 | import { FaSave } from "react-icons/fa";
5 | import { MdCancel } from "react-icons/md";
6 |
7 | const Editable = ({
8 | handleEditFormChange,
9 | editFormData,
10 | handleCancelClick,
11 | }) => {
12 | return (
13 |
14 |
15 |
16 |
27 |
28 | |
29 |
30 |
31 |
41 |
42 | |
43 |
44 |
45 |
55 |
56 | |
57 |
58 |
59 |
70 |
71 | |
72 |
73 |
74 |
84 |
85 | |
86 |
87 |
90 |
91 |
92 |
93 | |
94 |
95 | );
96 | };
97 |
98 | export default Editable;
99 |
--------------------------------------------------------------------------------
/src/components/ReactForm.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef, useState } from "react";
2 | // bootstrap components
3 | import { Button, Table } from "react-bootstrap";
4 | import Form from "react-bootstrap/Form";
5 | // uuid
6 | import { v4 as uuidv4 } from "uuid";
7 | // components
8 | import ReadOnly from "./ReadOnly";
9 | import Editable from "./Editable";
10 |
11 | const ReactForm = () => {
12 | // refs
13 | const priorityVal = useRef();
14 | const nameVal = useRef();
15 | const emailVal = useRef();
16 | const addressVal = useRef();
17 | const phoneNoVal = useRef();
18 | // states
19 | const [data, setData] = useState([]);
20 | const [addFormData, setAddFormData] = useState({
21 | priority: "",
22 | fullName: "",
23 | address: "",
24 | phoneNo: "",
25 | email: "",
26 | });
27 | const [editFormData, setEditFormData] = useState({
28 | priority: "",
29 | fullName: "",
30 | address: "",
31 | phoneNo: "",
32 | email: "",
33 | });
34 | const [refVals, setRefVals] = useState([
35 | priorityVal,
36 | nameVal,
37 | emailVal,
38 | addressVal,
39 | phoneNoVal,
40 | ]);
41 |
42 | const [editDataId, setEditDataId] = useState(null);
43 | // onchange handler of inputs
44 | const handleAddFormChange = (e) => {
45 | e.preventDefault();
46 | setAddFormData({ ...addFormData, [e.target.name]: e.target.value });
47 | };
48 | // edit forms
49 | const handleEditFormChange = (e) => {
50 | e.preventDefault();
51 | setEditFormData({ ...editFormData, [e.target.name]: e.target.value });
52 | };
53 | // add new value to the table
54 | const handleAddFormSubmit = (e) => {
55 | e.preventDefault();
56 | const newContact = {
57 | id: uuidv4(),
58 | priority: addFormData.priority,
59 | fullName: addFormData.fullName,
60 | address: addFormData.address,
61 | phoneNo: addFormData.phoneNo,
62 | email: addFormData.email,
63 | };
64 | const newData = [...data, newContact];
65 | setData(newData);
66 | localStorage.setItem("crud-table-data", JSON.stringify(newData));
67 | // clear inputs
68 | refVals.forEach((items) => {
69 | items.current.value = "";
70 | });
71 | };
72 | // submit edited forms
73 | const handleEditFormSubmit = (e) => {
74 | e.preventDefault();
75 | const editedData = {
76 | id: editDataId,
77 | priority: editFormData.priority,
78 | fullName: editFormData.fullName,
79 | address: editFormData.address,
80 | phoneNo: editFormData.phoneNo,
81 | email: editFormData.email,
82 | };
83 | const newData = [...data];
84 | const index = data.findIndex((item) => item.id === editDataId);
85 | newData[index] = editedData;
86 | setData(newData);
87 | localStorage.setItem("crud-table-data", JSON.stringify(newData));
88 | setEditDataId(null);
89 | };
90 | // edit button
91 | const handleEditClick = (e, data) => {
92 | e.preventDefault();
93 | setEditDataId(data.id);
94 | const formValues = {
95 | priority: data.priority,
96 | fullName: data.fullName,
97 | address: data.address,
98 | phoneNo: data.phoneNo,
99 | email: data.email,
100 | };
101 | setEditFormData(formValues);
102 | };
103 | // cancel button
104 | const handleCancelClick = () => {
105 | setEditDataId(null);
106 | };
107 | // delete
108 | const handleDeleteClick = (contactId) => {
109 | const newData = [...data];
110 | const index = data.findIndex((contact) => contact.id === contactId);
111 | newData.splice(index, 1);
112 | setData(newData);
113 | localStorage.setItem("crud-table-data", JSON.stringify(newData));
114 | };
115 | // load data from localStorage per each change
116 | useEffect(() => {
117 | const savedData = localStorage.getItem("crud-table-data");
118 | const parsedData = JSON.parse(savedData);
119 | parsedData !== null && setData(parsedData);
120 | }, []);
121 | // reorder tables based on priority
122 | const [reorderedData, setReOrderedData] = useState([]);
123 | useEffect(() => {
124 | const sortedData = data.sort((a, b) => a.priority - b.priority);
125 | setReOrderedData(sortedData);
126 | }, [data]);
127 |
128 | return (
129 | <>
130 |
165 | {/* add new data */}
166 |
225 | >
226 | );
227 | };
228 |
229 | export default ReactForm;
230 |
--------------------------------------------------------------------------------
/src/components/ReadOnly.js:
--------------------------------------------------------------------------------
1 | // icons
2 | import { FaRegTrashAlt } from "react-icons/fa";
3 | import { FiEdit2 } from "react-icons/fi";
4 |
5 | const ReadOnly = ({ data, handleEditClick, handleDeleteClick }) => {
6 | return (
7 |
8 | {data.priority} |
9 | {data.fullName} |
10 | {data.address} |
11 | {data.phoneNo} |
12 | {data.email} |
13 |
14 | handleEditClick(e, data)}
17 | className="text-warning"
18 | >
19 |
20 |
21 | handleDeleteClick(data.id)}
24 | className="text-danger"
25 | >
26 |
27 |
28 | |
29 |
30 | );
31 | };
32 |
33 | export default ReadOnly;
34 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App";
4 | // styles
5 | import "./assets/styles/index.scss";
6 |
7 | const root = ReactDOM.createRoot(document.getElementById("root"));
8 | root.render();
--------------------------------------------------------------------------------