├── .gitignore
├── README.md
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
├── favicon.ico
├── index.html
├── manifest.json
└── robots.txt
├── src
├── App.js
├── components
│ ├── AddCustomer.js
│ ├── AddEmployee.js
│ ├── DefinitionSearch.js
│ ├── EditEmployee.js
│ ├── Employee.js
│ ├── Header.js
│ └── NotFound.js
├── hooks
│ └── UseFetch.js
├── index.css
├── index.js
├── pages
│ ├── Customer.js
│ ├── Customers.js
│ ├── Definition.js
│ ├── Dictionary.js
│ ├── Employees.js
│ ├── Login.js
│ └── Register.js
└── shared.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 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `npm start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
13 |
14 | The page will reload when you make changes.\
15 | You may also see any lint errors in the console.
16 |
17 | ### `npm test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `npm run build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `npm run eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can't go back!**
35 |
36 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
39 |
40 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
48 | ### Code Splitting
49 |
50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
51 |
52 | ### Analyzing the Bundle Size
53 |
54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
55 |
56 | ### Making a Progressive Web App
57 |
58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
59 |
60 | ### Advanced Configuration
61 |
62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
63 |
64 | ### Deployment
65 |
66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
67 |
68 | ### `npm run build` fails to minify
69 |
70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
71 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hello",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@headlessui/react": "^1.6.6",
7 | "@heroicons/react": "^1.0.6",
8 | "@testing-library/jest-dom": "^5.16.5",
9 | "@testing-library/react": "^13.3.0",
10 | "@testing-library/user-event": "^13.5.0",
11 | "bootstrap": "^5.2.0",
12 | "react": "^18.2.0",
13 | "react-bootstrap": "^2.5.0",
14 | "react-dom": "^18.2.0",
15 | "react-router-dom": "^6.3.0",
16 | "react-scripts": "5.0.1",
17 | "uuid": "^8.3.2",
18 | "web-vitals": "^2.1.4"
19 | },
20 | "scripts": {
21 | "start": "react-scripts start",
22 | "build": "react-scripts build",
23 | "test": "react-scripts test",
24 | "eject": "react-scripts eject"
25 | },
26 | "eslintConfig": {
27 | "extends": [
28 | "react-app",
29 | "react-app/jest"
30 | ]
31 | },
32 | "browserslist": {
33 | "production": [
34 | ">0.2%",
35 | "not dead",
36 | "not op_mini all"
37 | ],
38 | "development": [
39 | "last 1 chrome version",
40 | "last 1 firefox version",
41 | "last 1 safari version"
42 | ]
43 | },
44 | "devDependencies": {
45 | "autoprefixer": "^10.4.8",
46 | "postcss": "^8.4.14",
47 | "tailwindcss": "^3.1.8"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CalebCurry/react/4c5a435acdd1f8e04bd7e178bf79f990945b78ef/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import './index.css';
2 | import { createContext, useState, useEffect } from 'react';
3 | import Header from './components/Header';
4 | import Employees from './pages/Employees';
5 | import { BrowserRouter, Routes, Route } from 'react-router-dom';
6 | import Customers from './pages/Customers';
7 | import Dictionary from './pages/Dictionary';
8 | import Definition from './pages/Definition';
9 | import NotFound from './components/NotFound';
10 | import Customer from './pages/Customer';
11 | import Login from './pages/Login';
12 | import Register from './pages/Register';
13 | import { baseUrl } from './shared';
14 |
15 | export const LoginContext = createContext();
16 |
17 | function App() {
18 | useEffect(() => {
19 | function refreshTokens() {
20 | if (localStorage.refresh) {
21 | const url = baseUrl + 'api/token/refresh/';
22 | fetch(url, {
23 | method: 'POST',
24 | headers: {
25 | 'Content-Type': 'application/json',
26 | },
27 | body: JSON.stringify({
28 | refresh: localStorage.refresh,
29 | }),
30 | })
31 | .then((response) => {
32 | return response.json();
33 | })
34 | .then((data) => {
35 | localStorage.access = data.access;
36 | localStorage.refresh = data.refresh;
37 | setLoggedIn(true);
38 | });
39 | }
40 | }
41 |
42 | const minute = 1000 * 60;
43 | refreshTokens();
44 | setInterval(refreshTokens, minute * 3);
45 | }, []);
46 |
47 | const [loggedIn, setLoggedIn] = useState(
48 | localStorage.access ? true : false
49 | );
50 |
51 | function changeLoggedIn(value) {
52 | setLoggedIn(value);
53 | if (value === false) {
54 | localStorage.clear();
55 | }
56 | }
57 |
58 | return (
59 |
60 |
61 |
62 |
63 | } />
64 | } />
65 | }
68 | />
69 | } />
70 | } />
71 | } />
72 | } />
73 | } />
74 | } />
75 |
76 |
77 |
78 |
79 | );
80 | }
81 |
82 | export default App;
83 |
--------------------------------------------------------------------------------
/src/components/AddCustomer.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import Button from 'react-bootstrap/Button';
3 | import Modal from 'react-bootstrap/Modal';
4 |
5 | export default function AddCustomer(props) {
6 | const [name, setName] = useState('');
7 | const [industry, setIndustry] = useState('');
8 | const [show, setShow] = useState(props.show);
9 |
10 | const handleClose = () => setShow(false);
11 | const handleShow = () => setShow(true);
12 |
13 | return (
14 | <>
15 |
19 | + Add Customer
20 |
21 |
22 |
28 |
29 | Add Customer
30 |
31 |
32 |
87 |
88 |
89 |
93 | Close
94 |
95 |
99 | Add
100 |
101 |
102 |
103 | >
104 | );
105 | }
106 |
--------------------------------------------------------------------------------
/src/components/AddEmployee.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import Button from 'react-bootstrap/Button';
3 | import Modal from 'react-bootstrap/Modal';
4 |
5 | function AddEmployee(props) {
6 | const [name, setName] = useState('');
7 | const [role, setRole] = useState('');
8 | const [img, setImg] = useState('');
9 | const [show, setShow] = useState(false);
10 |
11 | const handleClose = () => setShow(false);
12 | const handleShow = () => setShow(true);
13 |
14 | return (
15 | <>
16 |
20 | + Add Employee
21 |
22 |
23 |
29 |
30 | Add Employee
31 |
32 |
33 |
111 |
112 |
113 |
117 | Close
118 |
119 |
124 | Add
125 |
126 |
127 |
128 | >
129 | );
130 | }
131 |
132 | export default AddEmployee;
133 |
--------------------------------------------------------------------------------
/src/components/DefinitionSearch.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react';
2 | import { useNavigate } from 'react-router-dom';
3 |
4 | export default function DefinitionSearch() {
5 | const [word, setWord] = useState('');
6 | const navigate = useNavigate();
7 |
8 | return (
9 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/EditEmployee.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import Button from 'react-bootstrap/Button';
3 | import Modal from 'react-bootstrap/Modal';
4 |
5 | function EditEmployee(props) {
6 | const [name, setName] = useState(props.name);
7 | const [role, setRole] = useState(props.role);
8 |
9 | const [show, setShow] = useState(false);
10 |
11 | const handleClose = () => setShow(false);
12 | const handleShow = () => setShow(true);
13 |
14 | return (
15 | <>
16 |
20 | Update
21 |
22 |
23 |
29 |
30 | Update Employee
31 |
32 |
33 |
85 |
86 |
87 |
91 | Close
92 |
93 |
97 | Update
98 |
99 |
100 |
101 | >
102 | );
103 | }
104 |
105 | export default EditEmployee;
106 |
--------------------------------------------------------------------------------
/src/components/Employee.js:
--------------------------------------------------------------------------------
1 | import EditEmployee from './EditEmployee';
2 |
3 | function Employee(props) {
4 | return (
5 |
6 |
10 |
11 |
12 |
13 | {props.name}
14 |
15 |
{props.role}
16 |
17 |
18 | {props.editEmployee}
19 |
20 |
21 | );
22 | }
23 |
24 | export default Employee;
25 |
--------------------------------------------------------------------------------
/src/components/Header.js:
--------------------------------------------------------------------------------
1 | /* This example requires Tailwind CSS v2.0+ */
2 | import { useContext, useEffect } from 'react';
3 | import { Disclosure, Menu, Transition } from '@headlessui/react';
4 | import { BellIcon, MenuIcon, XIcon } from '@heroicons/react/outline';
5 | import { NavLink } from 'react-router-dom';
6 | import { LoginContext } from '../App';
7 |
8 | const navigation = [
9 | { name: 'Employees', href: '/Employees' },
10 | { name: 'Customers', href: '/Customers' },
11 | { name: 'Dictionary', href: '/dictionary' },
12 | ];
13 |
14 | function classNames(...classes) {
15 | return classes.filter(Boolean).join(' ');
16 | }
17 |
18 | export default function Header(props) {
19 | const [loggedIn, setLoggedIn] = useContext(LoginContext);
20 |
21 | return (
22 | <>
23 |
24 | {({ open }) => (
25 | <>
26 |
27 |
28 |
29 | {/* Mobile menu button*/}
30 |
31 |
32 | Open main menu
33 |
34 | {open ? (
35 |
39 | ) : (
40 |
44 | )}
45 |
46 |
47 |
48 |
49 |
50 | {/*className={classNames(
51 | item.current
52 | ? 'no-underline '
53 | : 'no-underline',
54 | ''
55 | )}*/}
56 | {navigation.map((item) => (
57 | {
63 | return (
64 | 'px-3 py-2 rounded-md text-sm font-medium no-underline ' +
65 | (!isActive
66 | ? ' text-gray-300 hover:bg-gray-700 hover:text-white'
67 | : 'bg-gray-900 text-white')
68 | );
69 | }}
70 | >
71 | {item.name}
72 |
73 | ))}
74 | {loggedIn ? (
75 | {
78 | setLoggedIn(false);
79 | localStorage.clear();
80 | }}
81 | className="px-3 py-2 rounded-md text-sm font-medium no-underline text-gray-300 hover:bg-gray-700 hover:text-white"
82 | >
83 | Logout
84 |
85 | ) : (
86 |
90 | Login
91 |
92 | )}
93 |
94 |
95 |
96 |
97 |
101 |
102 | View notifications
103 |
104 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 | {navigation.map((item) => (
116 | {
120 | return (
121 | 'block px-3 py-2 rounded-md text-base font-medium no-underline ' +
122 | (!isActive
123 | ? ' text-gray-300 hover:bg-gray-700 hover:text-white'
124 | : 'bg-gray-900 text-white')
125 | );
126 | }}
127 | >
128 | {item.name}
129 |
130 | ))}
131 | {loggedIn ? (
132 | {
135 | setLoggedIn(false);
136 | localStorage.clear();
137 | }}
138 | className="block px-3 py-2 rounded-md text-base font-medium no-underline text-gray-300 hover:bg-gray-700 hover:text-white"
139 | >
140 | Logout
141 |
142 | ) : (
143 |
147 | Login
148 |
149 | )}
150 |
151 |
152 | >
153 | )}
154 |
155 |
156 |
157 | {props.children}
158 |
159 |
160 | >
161 | );
162 | }
163 |
--------------------------------------------------------------------------------
/src/components/NotFound.js:
--------------------------------------------------------------------------------
1 | export default function NotFound() {
2 | return The page you are looking for was not found
;
3 | }
4 |
--------------------------------------------------------------------------------
/src/hooks/UseFetch.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react';
2 | import { useNavigate, useLocation } from 'react-router-dom';
3 |
4 | export default function useFetch(url, { method, headers, body } = {}) {
5 | const [data, setData] = useState();
6 | const [errorStatus, setErrorStatus] = useState();
7 |
8 | const navigate = useNavigate();
9 | const location = useLocation();
10 | function request() {
11 | fetch(url, {
12 | method: method,
13 | headers: headers,
14 | body: body,
15 | })
16 | .then((response) => {
17 | if (response.status === 401) {
18 | navigate('/login', {
19 | state: {
20 | previousUrl: location.pathname,
21 | },
22 | });
23 | }
24 | if (!response.ok) {
25 | throw response.status;
26 | }
27 | return response.json();
28 | })
29 | .then((data) => {
30 | setData(data);
31 | })
32 | .catch((e) => {
33 | setErrorStatus(e);
34 | });
35 | }
36 |
37 | function appendData(newData) {
38 | fetch(url, {
39 | method: 'POST',
40 | headers: headers,
41 | body: JSON.stringify(newData),
42 | })
43 | .then((response) => {
44 | if (response.status === 401) {
45 | navigate('/login', {
46 | state: {
47 | previousUrl: location.pathname,
48 | },
49 | });
50 | }
51 |
52 | if (!response.ok) {
53 | throw response.status;
54 | }
55 |
56 | return response.json();
57 | })
58 | .then((d) => {
59 | const submitted = Object.values(d)[0];
60 |
61 | const newState = { ...data };
62 | Object.values(newState)[0].push(submitted);
63 |
64 | setData(newState); //new object, it's seen as a state change
65 | })
66 | .catch((e) => {
67 | console.log(e);
68 | setErrorStatus(e);
69 | });
70 | }
71 |
72 | return { request, appendData, data, errorStatus };
73 | }
74 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import App from './App';
4 | import 'bootstrap/dist/css/bootstrap.min.css';
5 |
6 | const root = ReactDOM.createRoot(document.getElementById('root'));
7 | root.render(
8 |
9 |
10 |
11 | );
12 |
--------------------------------------------------------------------------------
/src/pages/Customer.js:
--------------------------------------------------------------------------------
1 | import { useParams, Link, useNavigate, useLocation } from 'react-router-dom';
2 | import { useEffect, useState, useContext } from 'react';
3 | import NotFound from '../components/NotFound';
4 | import { baseUrl } from '../shared';
5 | import { LoginContext } from '../App';
6 |
7 | export default function Customer() {
8 | const [loggedIn, setLoggedIn] = useContext(LoginContext);
9 | const { id } = useParams();
10 | const navigate = useNavigate();
11 | const [customer, setCustomer] = useState();
12 | const [tempCustomer, setTempCustomer] = useState();
13 | const [notFound, setNotFound] = useState();
14 | const [changed, setChanged] = useState(false);
15 | const [error, setError] = useState();
16 |
17 | const location = useLocation();
18 |
19 | useEffect(() => {
20 | if (!customer) return;
21 | if (!customer) return;
22 |
23 | let equal = true;
24 | if (customer.name !== tempCustomer.name) equal = false;
25 | if (customer.industry !== tempCustomer.industry) equal = false;
26 |
27 | if (equal) setChanged(false);
28 | });
29 |
30 | useEffect(() => {
31 | const url = baseUrl + 'api/customers/' + id;
32 | fetch(url, {
33 | headers: {
34 | 'Content-Type': 'application/json',
35 | Authorization: 'Bearer ' + localStorage.getItem('access'),
36 | },
37 | })
38 | .then((response) => {
39 | if (response.status === 404) {
40 | //render a 404 component in this page
41 | setNotFound(true);
42 | } else if (response.status === 401) {
43 | setLoggedIn(false);
44 | navigate('/login', {
45 | state: {
46 | previousUrl: location.pathname,
47 | },
48 | });
49 | }
50 |
51 | if (!response.ok) {
52 | throw new Error('Something went wrong, try again later');
53 | }
54 |
55 | return response.json();
56 | })
57 | .then((data) => {
58 | setCustomer(data.customer);
59 | setTempCustomer(data.customer);
60 | setError(undefined);
61 | })
62 | .catch((e) => {
63 | setError(e.message);
64 | });
65 | }, []);
66 |
67 | function updateCustomer(e) {
68 | e.preventDefault();
69 | const url = baseUrl + 'api/customers/' + id;
70 | fetch(url, {
71 | method: 'POST',
72 | headers: {
73 | 'Content-Type': 'application/json',
74 | Authorization: 'Bearer ' + localStorage.getItem('access'),
75 | },
76 | body: JSON.stringify(tempCustomer),
77 | })
78 | .then((response) => {
79 | if (response.status === 401) {
80 | setLoggedIn(false);
81 | navigate('/login', {
82 | state: {
83 | previousUrl: location.pathname,
84 | },
85 | });
86 | }
87 | if (!response.ok) throw new Error('something went wrong');
88 | return response.json();
89 | })
90 | .then((data) => {
91 | setCustomer(data.customer);
92 | setChanged(false);
93 | setError(undefined);
94 | })
95 | .catch((e) => {
96 | setError(e.message);
97 | });
98 | }
99 |
100 | return (
101 |
102 | {notFound ?
The customer with id {id} was not found
: null}
103 |
104 | {customer ? (
105 |
106 |
155 | {changed ? (
156 |
157 | {
160 | setTempCustomer({ ...customer });
161 | setChanged(false);
162 | }}
163 | >
164 | Cancel
165 |
166 |
170 | Save
171 |
172 |
173 | ) : null}
174 |
175 |
176 | {
179 | const url = baseUrl + 'api/customers/' + id;
180 | fetch(url, {
181 | method: 'DELETE',
182 | headers: {
183 | 'Content-Type': 'application/json',
184 | Authorization:
185 | 'Bearer ' +
186 | localStorage.getItem('access'),
187 | },
188 | })
189 | .then((response) => {
190 | if (response.status === 401) {
191 | setLoggedIn(false);
192 | navigate('/login', {
193 | state: {
194 | previousUrl:
195 | location.pathname,
196 | },
197 | });
198 | }
199 | if (!response.ok) {
200 | throw new Error(
201 | 'Something went wrong'
202 | );
203 | }
204 | navigate('/customers');
205 | })
206 | .catch((e) => {
207 | setError(e.message);
208 | });
209 | }}
210 | >
211 | Delete
212 |
213 |
214 |
215 | ) : null}
216 |
217 | {error ?
{error}
: null}
218 |
219 |
220 |
221 | ← Go back
222 |
223 |
224 |
225 | );
226 | }
227 |
--------------------------------------------------------------------------------
/src/pages/Customers.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState, useContext } from 'react';
2 | import { Link, useNavigate, useLocation } from 'react-router-dom';
3 | import AddCustomer from '../components/AddCustomer';
4 | import { baseUrl } from '../shared';
5 | import { LoginContext } from '../App';
6 | import useFetch from '../hooks/UseFetch';
7 |
8 | export default function Customers() {
9 | const [loggedIn, setLoggedIn] = useContext(LoginContext);
10 | //const [customers, setCustomers] = useState();
11 | const [show, setShow] = useState(false);
12 |
13 | function toggleShow() {
14 | setShow(!show);
15 | }
16 |
17 | const location = useLocation();
18 | const navigate = useNavigate();
19 |
20 | const url = baseUrl + 'api/customers/';
21 | const {
22 | request,
23 | appendData,
24 | data: { customers } = {},
25 | errorStatus,
26 | } = useFetch(url, {
27 | method: 'GET',
28 | headers: {
29 | 'Content-Type': 'application/json',
30 | Authorization: 'Bearer ' + localStorage.getItem('access'),
31 | },
32 | });
33 |
34 | useEffect(() => {
35 | request();
36 | }, []);
37 |
38 | //useEffect(() => {
39 | // console.log(request, appendData, customers, errorStatus);
40 | //});
41 |
42 | function newCustomer(name, industry) {
43 | appendData({ name: name, industry: industry });
44 |
45 | if (!errorStatus) {
46 | toggleShow();
47 | }
48 | }
49 |
50 | return (
51 | <>
52 | Here are our customers:
53 | {customers
54 | ? customers.map((customer) => {
55 | return (
56 |
57 |
58 |
59 | {customer.name}
60 |
61 |
62 |
63 | );
64 | })
65 | : null}
66 |
67 |
72 | >
73 | );
74 | }
75 |
--------------------------------------------------------------------------------
/src/pages/Definition.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react';
2 | import { useParams, useNavigate, Link, useLocation } from 'react-router-dom';
3 | import { v4 as uuidv4 } from 'uuid';
4 | import DefinitionSearch from '../components/DefinitionSearch';
5 | import NotFound from '../components/NotFound';
6 | import useFetch from '../hooks/UseFetch';
7 |
8 | export default function Definition() {
9 | //const [word, setWord] = useState();
10 | //const [notFound, setNotFound] = useState(false);
11 | //const [error, setError] = useState(false);
12 | let { search } = useParams();
13 |
14 | const location = useLocation();
15 | const navigate = useNavigate();
16 | const {
17 | request,
18 | data: [{ meanings: word }] = [{}],
19 | errorStatus,
20 | } = useFetch('https://api.dictionaryapi.dev/api/v2/entries/en/' + search);
21 |
22 | useEffect(() => {
23 | request();
24 | }, [search]);
25 |
26 | if (errorStatus === 404) {
27 | return (
28 | <>
29 |
30 | Search another
31 | >
32 | );
33 | }
34 |
35 | if (errorStatus) {
36 | return (
37 | <>
38 | There was a problem with the server, try again later.
39 | Search another
40 | >
41 | );
42 | }
43 |
44 | return (
45 | <>
46 | {word ? (
47 | <>
48 | Here is a definition:
49 | {word.map((meaning) => {
50 | return (
51 |
52 | {meaning.partOfSpeech + ': '}
53 | {meaning.definitions[0].definition}
54 |
55 | );
56 | })}
57 | Search again:
58 |
59 | >
60 | ) : null}
61 | >
62 | );
63 | }
64 |
--------------------------------------------------------------------------------
/src/pages/Dictionary.js:
--------------------------------------------------------------------------------
1 | import DefinitionSearch from '../components/DefinitionSearch';
2 |
3 | export default function Dictionary() {
4 | return (
5 |
6 |
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/src/pages/Employees.js:
--------------------------------------------------------------------------------
1 | import '../index.css';
2 | import Employee from '../components/Employee';
3 | import { useState } from 'react';
4 | import { v4 as uuidv4 } from 'uuid';
5 | import AddEmployee from '../components/AddEmployee';
6 | import EditEmployee from '../components/EditEmployee';
7 | import Header from '../components/Header';
8 |
9 | function Employees() {
10 | const [employees, setEmployees] = useState([
11 | {
12 | id: 1,
13 | name: 'Caleb',
14 | role: 'YouTube Sensation',
15 | img: 'https://images.pexels.com/photos/3831645/pexels-photo-3831645.jpeg',
16 | },
17 | {
18 | id: 2,
19 | name: 'Sal',
20 | role: 'Manager',
21 | img: 'https://images.pexels.com/photos/3586798/pexels-photo-3586798.jpeg',
22 | },
23 | {
24 | id: 3,
25 | name: 'John',
26 | role: 'Director of Eng.',
27 | img: 'https://images.pexels.com/photos/2095582/pexels-photo-2095582.jpeg',
28 | },
29 | {
30 | id: 4,
31 | name: 'Melanie',
32 | role: 'Software Engineer',
33 | img: 'https://images.pexels.com/photos/3760583/pexels-photo-3760583.jpeg',
34 | },
35 | {
36 | id: 5,
37 | name: 'Corey',
38 | role: 'The Devops Guy',
39 | img: 'https://images.pexels.com/photos/2379005/pexels-photo-2379005.jpeg',
40 | },
41 | {
42 | id: 6,
43 | name: 'Jake',
44 | role: 'Senior',
45 | img: 'https://images.pexels.com/photos/2225298/pexels-photo-2225298.jpeg',
46 | },
47 | ]);
48 |
49 | function updateEmployee(id, newName, newRole) {
50 | const updatedEmployees = employees.map((employee) => {
51 | if (id == employee.id) {
52 | return { ...employee, name: newName, role: newRole };
53 | }
54 |
55 | return employee;
56 | });
57 | setEmployees(updatedEmployees);
58 | }
59 |
60 | function newEmployee(name, role, img) {
61 | const newEmployee = {
62 | id: uuidv4(),
63 | name: name,
64 | role: role,
65 | img: img,
66 | };
67 | setEmployees([...employees, newEmployee]);
68 | }
69 |
70 | const showEmployees = true;
71 | return (
72 |
73 | {showEmployees ? (
74 | <>
75 |
76 | {employees.map((employee) => {
77 | const editEmployee = (
78 |
84 | );
85 | return (
86 |
94 | );
95 | })}
96 |
97 |
98 | >
99 | ) : (
100 |
You cannot see the employees
101 | )}
102 |
103 | );
104 | }
105 |
106 | export default Employees;
107 |
--------------------------------------------------------------------------------
/src/pages/Login.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect, useContext } from 'react';
2 | import { baseUrl } from '../shared';
3 | import { useLocation, useNavigate } from 'react-router-dom';
4 | import { LoginContext } from '../App';
5 |
6 | export default function Login() {
7 | const [loggedIn, setLoggedIn] = useContext(LoginContext);
8 | const [username, setUsername] = useState();
9 | const [password, setPassword] = useState();
10 |
11 | const location = useLocation();
12 | const navigate = useNavigate();
13 |
14 | function login(e) {
15 | e.preventDefault();
16 | const url = baseUrl + 'api/token/';
17 | fetch(url, {
18 | method: 'POST',
19 | headers: {
20 | 'Content-Type': 'application/json',
21 | },
22 | body: JSON.stringify({
23 | username: username,
24 | password: password,
25 | }),
26 | })
27 | .then((response) => {
28 | return response.json();
29 | })
30 | .then((data) => {
31 | localStorage.setItem('access', data.access);
32 | localStorage.setItem('refresh', data.refresh);
33 | setLoggedIn(true);
34 | navigate(
35 | location?.state?.previousUrl
36 | ? location.state.previousUrl
37 | : '/customers'
38 | );
39 | });
40 | }
41 |
42 | return (
43 |
83 | );
84 | }
85 |
--------------------------------------------------------------------------------
/src/pages/Register.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect, useContext } from 'react';
2 | import { baseUrl } from '../shared';
3 | import { useLocation, useNavigate } from 'react-router-dom';
4 | import { LoginContext } from '../App';
5 |
6 | export default function Register() {
7 | const [loggedIn, setLoggedIn] = useContext(LoginContext);
8 | const [username, setUsername] = useState();
9 | const [password, setPassword] = useState();
10 | const [email, setEmail] = useState();
11 |
12 | const location = useLocation();
13 | const navigate = useNavigate();
14 |
15 | useEffect(() => {
16 | localStorage.clear();
17 | setLoggedIn(false);
18 | }, []);
19 |
20 | function login(e) {
21 | e.preventDefault();
22 | const url = baseUrl + 'api/register/';
23 | fetch(url, {
24 | method: 'POST',
25 | headers: {
26 | 'Content-Type': 'application/json',
27 | },
28 | body: JSON.stringify({
29 | email: email,
30 | username: username,
31 | password: password,
32 | }),
33 | })
34 | .then((response) => {
35 | return response.json();
36 | })
37 | .then((data) => {
38 | localStorage.setItem('access', data.access);
39 | localStorage.setItem('refresh', data.refresh);
40 | setLoggedIn(true);
41 | navigate(
42 | location?.state?.previousUrl
43 | ? location.state.previousUrl
44 | : '/customers'
45 | );
46 | });
47 | }
48 |
49 | return (
50 |
107 | );
108 | }
109 |
--------------------------------------------------------------------------------
/src/shared.js:
--------------------------------------------------------------------------------
1 | export const baseUrl = 'http://localhost:8000/';
2 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: ['./src/**/*.{js,jsx,ts,tsx}'],
4 | theme: {
5 | extend: {},
6 | },
7 | plugins: [],
8 | };
9 |
--------------------------------------------------------------------------------