├── README.md
├── client
├── components
│ ├── NoPage.jsx
│ ├── ProfilePic.jsx
│ ├── ProfileUpdate.jsx
│ ├── AddItem.jsx
│ ├── LoginSignup.jsx
│ ├── Navbar.jsx
│ ├── NavBarLoggedOut.jsx
│ ├── NavBarLoggedIn.jsx
│ ├── Login.jsx
│ ├── Inventory.jsx
│ ├── SignUp.jsx
│ └── InventoryDisplay.jsx
├── containers
│ ├── AccountPageContainer.jsx
│ ├── InventoryPageContainer.jsx
│ └── LoginSignupContainer.jsx
├── index.html
├── index.js
└── stylesheets
│ ├── Inventory.scss
│ ├── NavBar.scss
│ └── Login.scss
├── dist
└── index.html
├── server
├── models
│ └── eventoryModel.js
├── routes
│ ├── items.js
│ └── accounts.js
├── server.js
└── controllers
│ ├── itemsController.js
│ └── accountsController.js
├── package.json
└── webpack.config.js
/README.md:
--------------------------------------------------------------------------------
1 | # Welcome to our e-Ventory
--------------------------------------------------------------------------------
/client/components/NoPage.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const NoPage = () => {
4 | return (
5 |
6 |
7 |
8 | );
9 | };
10 |
11 | export default NoPage;
--------------------------------------------------------------------------------
/client/components/ProfilePic.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const ProfilePic = () => {
4 | return (
5 |
6 |
7 |
8 | );
9 | };
10 |
11 | export default ProfilePic;
--------------------------------------------------------------------------------
/client/components/ProfileUpdate.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const ProfileUpdate = () => {
4 | return (
5 |
6 |
7 |
8 | );
9 | };
10 |
11 | export default ProfileUpdate;
--------------------------------------------------------------------------------
/client/containers/AccountPageContainer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ProfileUpdate from '../components/ProfileUpdate.jsx';
3 | import ProfilePic from '../components/ProfilePic.jsx';
4 |
5 | const AccountPageContainer = (props) => {
6 | return (
7 |
8 |
hey yall
9 |
10 | );
11 | };
12 |
13 | export default AccountPageContainer;
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | React App
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/client/components/AddItem.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const AddItem = () => {
4 | // return (
5 | //
6 | //
7 | //
8 | //
9 | //
10 | //
11 | //
12 | //
13 | // );
14 | };
15 |
16 | // export default AddItem;
--------------------------------------------------------------------------------
/client/containers/InventoryPageContainer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | // import AddItem from '../components/AddItem.jsx';
3 | import InventoryDisplay from '../components/InventoryDisplay.jsx';
4 |
5 |
6 | const InventoryPageContainer = (props) => {
7 | return (
8 |
9 |
Inventory
10 | {/*
*/}
11 |
12 |
13 | );
14 | };
15 |
16 | export default InventoryPageContainer;
--------------------------------------------------------------------------------
/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | React App
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/server/models/eventoryModel.js:
--------------------------------------------------------------------------------
1 | const connectionString = 'postgres://rngolnjc:n65esgAAoHqJ7jMSeJAYlw_w77qcdeUf@kashin.db.elephantsql.com/rngolnjc';
2 |
3 | const { Client } = require('pg')
4 | const client = new Client(connectionString)
5 |
6 | client.connect( (err) => {
7 | if(err) {
8 | return console.error('could not connect to postgres', err);
9 | }
10 | });
11 |
12 | module.exports = {
13 | query: (text, params, callback) => {
14 | console.log('exected, query', text);
15 | return client.query(text, params, callback);
16 | }
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/server/routes/items.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | router = express.Router();
3 | const itemsController = require('../controllers/itemsController');
4 |
5 | router.get('/:id',itemsController.getAllItems, (req,res) => {
6 |
7 | // request body sends the item information
8 | // console.log('RECIEVED!', res.locals.items)
9 | res.status(200).json({items: res.locals.items});
10 |
11 | })
12 |
13 | router.patch('/',itemsController.updateItem, (req,res) => {
14 |
15 | // request body sends the item to indicate which item to update
16 |
17 | // res.status(200);
18 | res.status(200).json({item: res.locals.updatedItem});
19 |
20 | })
21 |
22 | router.post('/',itemsController.createItem, (req,res) => {
23 |
24 | // request body sends the item information, then inserted into db table
25 |
26 | res.status(200).json({item: res.locals.createdItem});
27 |
28 | })
29 |
30 | router.delete('/:itemId', itemsController.deleteItem, (req, res) => {
31 |
32 | res.status(200).json({message: 'item deleted'});
33 |
34 | })
35 |
36 |
37 | module.exports = router;
--------------------------------------------------------------------------------
/client/components/LoginSignup.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import '../stylesheets/Login.scss';
3 | import Login from "./Login.jsx";
4 | import SignUp from "./SignUp.jsx";
5 | import { useState } from 'react';
6 |
7 |
8 | //add onclick to a anchor tag ()
9 |
10 | const LoginSignup = (props) => {
11 |
12 | const [isShow, setIsShow] = React.useState(true);
13 | const handleClick = () => {setIsShow(s => !s)};
14 |
15 | if (isShow){
16 | return (
17 |
24 | )
25 | } else {
26 | return (
27 |
34 | )
35 | };
36 | };
37 |
38 | export default LoginSignup;
39 |
--------------------------------------------------------------------------------
/client/index.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
4 | import Navbar from "./components/Navbar.jsx"
5 | import LoginSignupContainer from './containers/LoginSignupContainer.jsx';
6 | import AccountPageContainer from './containers/AccountPageContainer.jsx';
7 | import InventoryPageContainer from './containers/InventoryPageContainer.jsx';
8 | import NoPage from './components/NoPage.jsx';
9 |
10 | export default function App() {
11 | //use state hook
12 | const [user, setUser] = useState({});
13 | console.log('user', user);
14 |
15 |
16 | return (
17 |
18 |
19 |
20 | } />
21 | } />
22 | } />
23 | } />
24 |
25 |
26 | )
27 | }
28 | ReactDOM.render(
29 | ,
30 | document.getElementById('root')
31 | );
--------------------------------------------------------------------------------
/client/components/Navbar.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Outlet, Link } from "react-router-dom";
3 |
4 | import NavBarLoggedIn from '../components/NavBarLoggedIn.jsx'
5 | import NavBarLoggedOut from '../components/NavBarLoggedOut.jsx'
6 | import { useState } from 'react';
7 |
8 | import '../stylesheets/NavBar.scss';
9 |
10 | /*
11 | Navigation Bar Displayed at the top of every page
12 |
13 | Will contain on not logged in page:
14 | Logo,
15 | Links:
16 | About Company,
17 |
18 | will contain on logged in age:
19 | Logo,
20 | Links:
21 | Account
22 | Inventory
23 | Profile Icon
24 | */
25 |
26 | const Navbar = (props) => {
27 |
28 | const [isShow, setIsShow] = React.useState(true);
29 | const handleClick = () => {setIsShow(s => !s)};
30 |
31 |
32 | //for future engineers - if you type in a fake account you will get an error and this needs to be fixed. xoxo gossip girl
33 | if (props.user.name === undefined){
34 | return (
35 |
36 |
37 |
38 | )
39 | } else {
40 | return (
41 |
42 |
43 |
44 | )
45 | };
46 | };
47 |
48 |
49 | export default Navbar;
--------------------------------------------------------------------------------
/client/components/NavBarLoggedOut.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Outlet, Link } from "react-router-dom";
3 | import '../stylesheets/NavBar.scss';
4 |
5 | import AccountPageContainer from '../containers/AccountPageContainer.jsx';
6 | import InventoryPageContainer from '../containers/InventoryPageContainer.jsx';
7 | import LoginSignupContainer from '../containers/LoginSignupContainer.jsx';
8 |
9 |
10 | /*
11 | Navigation Bar Displayed at the top of every page
12 |
13 | Will contain on not logged in page:
14 | Logo,
15 | Links:
16 | About Company,
17 |
18 | will contain on logged in age:
19 | Logo,
20 | Links:
21 | Account
22 | Inventory
23 | Profile Icon
24 | */
25 |
26 | const NavbarLoggedOut = () => {
27 | return (
28 |
29 |

30 |
eVentory
31 | {/*
Inventory Made Easy
*/}
32 |
About Us
33 |
Packages
34 |
35 | );
36 | };
37 |
38 | export default NavbarLoggedOut;
--------------------------------------------------------------------------------
/client/containers/LoginSignupContainer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | //import Login from '../components/Login.jsx';
3 | //import SignUp from '../components/SignUp.jsx';
4 | import LoginSignup from "../components/LoginSignup.jsx"
5 | import '../stylesheets/Login.scss';
6 |
7 | //Page that returns the login and signup components
8 |
9 | const LoginSignupContainer = (props) => {
10 | return (
11 |
12 |

13 |

14 |
15 |
Welcome
16 |
17 |
18 |
19 |
20 |
21 |
24 |
25 |
26 | );
27 | };
28 |
29 | export default LoginSignupContainer;
--------------------------------------------------------------------------------
/client/components/NavBarLoggedIn.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Outlet, Link } from "react-router-dom";
3 | import '../stylesheets/NavBar.scss';
4 |
5 |
6 | import AccountPageContainer from '../containers/AccountPageContainer.jsx';
7 | import InventoryPageContainer from '../containers/InventoryPageContainer.jsx';
8 | import LoginSignupContainer from '../containers/LoginSignupContainer.jsx';
9 |
10 |
11 | /*
12 | Navigation Bar Displayed at the top of every page
13 |
14 | Will contain on not logged in page:
15 | Logo,
16 | Links:
17 | About Company,
18 |
19 | will contain on logged in age:
20 | Logo,
21 | Links:
22 | Account
23 | Inventory
24 | Profile Icon
25 | */
26 |
27 | const NavBarLoggedIn = () => {
28 | return (
29 |
30 |

31 |
eVentory
32 | {/*
About Us
33 |
Packages */}
34 |
Account
35 |
Inventory
36 |
37 | )
38 | };
39 |
40 | export default NavBarLoggedIn
--------------------------------------------------------------------------------
/client/stylesheets/Inventory.scss:
--------------------------------------------------------------------------------
1 | .inventory-container{
2 | display: grid;
3 | padding: 20px;
4 | align-items: center;
5 | grid-template-columns: [first] 60px repeat(4, 150px) repeat(2, 80px);
6 | }
7 |
8 | .inventory-labels {
9 | display: grid;
10 | padding: 20px;
11 | align-items: center;
12 | grid-template-columns: [first] 60px repeat(4, 150px) repeat(2, 100px);
13 | }
14 | .scroll {
15 | background-color: #c7e4eb;
16 | width: 900px;
17 | height: 500px;
18 | overflow-x: hidden;
19 | overflow-y: auto;
20 | text-align: center;
21 | border-radius: 10px;
22 |
23 | }
24 |
25 |
26 | ::-webkit-scrollbar {
27 | width: 20px;
28 | }
29 |
30 |
31 | ::-webkit-scrollbar-track {
32 | box-shadow: inset 0 0 5px grey;
33 | border-radius: 10px;
34 | }
35 |
36 | ::-webkit-scrollbar-thumb {
37 | background: #c7d6eb;
38 | border-radius: 10px;
39 | border-color: black
40 | }
41 |
42 | .id-column {
43 | width: 60px;
44 | text-align: center;
45 | }
46 |
47 | .quantity-column {
48 | width: 100px;
49 | text-align: center;
50 | }
51 | .other-column {
52 | width: 150px;
53 | text-align: center;
54 | border-bottom: 2px;
55 | }
56 |
57 | .update-delete-buttons {
58 | height: 20px;
59 | margin: 10px;
60 | border-radius: 5px;
61 | background-color: #c7d6eb;
62 | border-color: #c7ebe4;
63 | color: #968b93;
64 |
65 |
66 | }
67 |
68 | .all-inventory {
69 | display: grid;
70 | grid-template-columns: 1fr;
71 | justify-items: center;
72 | }
--------------------------------------------------------------------------------
/server/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const app = express();
3 | const PORT_NUMBER = 3000;
4 | const path = require('path');
5 | const cors = require('cors');
6 |
7 |
8 | /**
9 | * require routers
10 | */
11 |
12 | const accountsRouter = require('./routes/accounts.js');
13 | const itemsRouter = require('./routes/items.js');
14 |
15 | // handle parsing request body
16 | // json object
17 | app.use(express.json());
18 |
19 | //use cors() on all requests
20 | app.use(cors());
21 |
22 | /**
23 | * send static files to all requests with path '/'
24 | */
25 | // send static files
26 | app.use('/', express.static(path.resolve(__dirname, '../dist/')));
27 |
28 | //send index.html all all requests with path /
29 | app.get('/', (req, res) => {
30 | return res.status(200).sendFile(path.join(__dirname, '../dist/index.html'));
31 | })
32 |
33 | /**
34 | * ROUTES
35 | */
36 |
37 | // send to user router on path /accounts/
38 | app.use('/accounts/',accountsRouter);
39 | // send to user router on path /items/
40 | app.use('/items/',itemsRouter);
41 |
42 |
43 | // global error handler
44 | app.use((err, req, res, next) => {
45 | const defaultErr = {
46 | log: 'Express error handler caught unknown middleware error',
47 | status: 500,
48 | message: { err: 'An error occurred' },
49 | };
50 |
51 | const errorObj = Object.assign({}, defaultErr, err);
52 | console.log(errorObj.log);
53 | return res.status(errorObj.status).json(errorObj.message);
54 | });
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | app.listen(PORT_NUMBER, () => {
77 | console.log(`listening on port ${PORT_NUMBER}`)
78 | });
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "e-ventory-app",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "webpack.config.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "build": "webpack",
9 | "dev": "webpack serve & nodemon server/server.js",
10 | "start": "nodemon server/server.js"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/e-Ventory/e-Ventory-App.git"
15 | },
16 | "keywords": [],
17 | "author": "",
18 | "license": "ISC",
19 | "bugs": {
20 | "url": "https://github.com/e-Ventory/e-Ventory-App/issues"
21 | },
22 | "homepage": "https://github.com/e-Ventory/e-Ventory-App#readme",
23 | "dependencies": {
24 | "cors": "^2.8.5",
25 | "css-loader": "^6.7.1",
26 | "express": "^4.17.3",
27 | "file-loader": "^6.2.0",
28 | "loader": "^2.1.1",
29 | "nodemon": "^2.0.15",
30 | "pg": "^8.7.3",
31 | "react": "^17.0.2",
32 | "react-dom": "^17.0.2",
33 | "react-hook-form": "^7.28.1",
34 | "react-router": "^6.2.2",
35 | "react-router-dom": "^6.2.2",
36 | "reactjs-popup": "^2.0.5",
37 | "regenerator-runtime": "^0.13.9",
38 | "style": "^0.0.3",
39 | "style-loader": "^3.3.1",
40 | "styled-components": "^5.3.3"
41 | },
42 | "devDependencies": {
43 | "@babel/core": "^7.17.8",
44 | "@babel/preset-env": "^7.16.11",
45 | "@babel/preset-react": "^7.16.7",
46 | "babel-loader": "^8.2.3",
47 | "html-webpack-plugin": "^5.5.0",
48 | "sass": "^1.49.9",
49 | "sass-loader": "^12.6.0",
50 | "url-loader": "^4.1.1",
51 | "webpack": "^5.70.0",
52 | "webpack-cli": "^4.9.2",
53 | "webpack-dev-server": "^4.7.4"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/server/routes/accounts.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | router = express.Router();
3 | const accountsController = require('../controllers/accountsController');
4 |
5 |
6 |
7 | // handle get requests to the root path with a param - get a single account (NOT CURRENTLY USED IN FRONT END)
8 | router.get('/:name', accountsController.getAnAccount, (req,res) => {
9 | res.status(200).json({account: res.locals.account})
10 | });
11 | // handle get requests to the root path - gets all accounts information (USED WHEN SIGNING UP)
12 | router.get('/', accountsController.getAllAccounts, (req,res) => {
13 | res.contentType('application/json').status(200).json({accounts: res.locals.accounts});
14 | });
15 | // handle post requests to the signup path - Creates an Account (USED WHEN SIGNING UP)
16 | router.post('/signup', accountsController.createAccount, (req,res) => {
17 | res.contentType('application/json').status(200).json({message: res.locals.message});
18 | });
19 | // handle post requests to the path login - Logs in an account (USED WHEN LOGGING IN)
20 | router.post('/login/', accountsController.login, (req, res) => {
21 | res.contentType('application/json').status(200).json({account: res.locals.account});
22 | })
23 | // handle patch requests to the rooth path with a param(NOT CURRENTLY USED, SHOULD BE USED WHEN CHANGING PASSWORD)
24 | router.patch('/:name', accountsController.changePassword, (req, res) => {
25 | res.contentType('application/json').status(200).json({message: res.locals.message});
26 | })
27 | // handle delete requests to the root path with a param
28 | router.delete('/:name', accountsController.deleteAccount, (req, res) => {
29 | res.contentType('application/json').status(200).json({message: res.locals.message});
30 | })
31 |
32 |
33 |
34 | module.exports = router;
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const HtmlWebpackPlugin = require('html-webpack-plugin');
4 |
5 | module.exports = {
6 | entry: ["regenerator-runtime/runtime.js", path.join(__dirname, './client/index.js')],
7 | output: {
8 | path: path.resolve(__dirname, 'dist'),
9 | filename: 'bundle.js'
10 | },
11 | module: {
12 | rules: [
13 | {
14 | test: /\.(js|jsx)$/,
15 | exclude: /node_modules/,
16 | use: {
17 | loader: "babel-loader",
18 | options: {
19 | presets: ['@babel/preset-env', '@babel/preset-react']
20 | }
21 | }
22 | },
23 | {
24 | test: /\.s[ac]ss$/i,
25 | use: [ "style-loader", "css-loader", "sass-loader"]
26 | },
27 | {
28 | test: /\.(png|jp(e*)g|svg|gif)$/,
29 | use: ['file-loader', 'url-loader'],
30 | },
31 | {
32 | test: /\.svg$/,
33 | use: ['@svgr/webpack'],
34 | }
35 | ]
36 | },
37 | mode: 'development',
38 | devServer: {
39 | headers: {
40 | 'Access-Control-Allow-Origin': '*',
41 | "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
42 | "Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization"
43 | },
44 | proxy: { // proxy URLs to backend development server
45 | '/accounts/**': {
46 | target: 'http://localhost:3000/',
47 | secure: false,
48 | changeOrigin: true
49 | },
50 | '/items/**': {
51 | target: 'http://localhost:3000/',
52 | secure: false,
53 | changeOrigin: true
54 | },
55 | },
56 | static: {
57 | directory: path.resolve(__dirname, '/client'),// boolean | string | array | object, static file location
58 | publicPath: '/dist'
59 | },
60 | compress: true, // enable gzip compression
61 | historyApiFallback: true, // true for index.html upon 404, object for multiple paths
62 | hot: true, // hot module replacement. Depends on HotModuleReplacementPlugin
63 | https: false, // true for self-signed, object for cert authority
64 | port: 8080,
65 | host: 'localhost'
66 | // ...
67 | },
68 | plugins: [ new HtmlWebpackPlugin( { template: path.join(__dirname, './client/index.html')})],
69 | }
--------------------------------------------------------------------------------
/client/components/Login.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import '../stylesheets/Login.scss';
3 | import { useState } from 'react';
4 | import { useForm } from "react-hook-form";
5 | import { Link, } from 'react-router-dom';
6 | //Login component
7 |
8 |
9 | //let options = {
10 | // method: 'POST',
11 | // headers: {
12 | // 'Content-Type': 'application/json;charset=utf-8'
13 | // },
14 | // body: JSON.stringify(data)
15 | // }
16 |
17 | const Login = (props) => {
18 | // useForm hook returns an object containing a few properties.
19 | //register method helps register an input field into react hook form so that it is available for validation.
20 | //handleSubmit can handle two functions as arguments. first function passed as an arg will be onvoked along with registered field values when the form validation is successfull.
21 | const {register, handleSubmit} = useForm();
22 | //const [data, setData] = useState("")
23 |
24 | return (
25 |
26 |
Please sign in
27 |
61 |
62 |
63 | );
64 | };
65 |
66 | export default Login;
--------------------------------------------------------------------------------
/client/stylesheets/NavBar.scss:
--------------------------------------------------------------------------------
1 |
2 | //nav bar styling
3 |
4 | //logged in nav bar
5 | .LoggedIn {
6 | display: flex;
7 | flex-direction: row;
8 | margin-left: auto;
9 | margin-right: auto;
10 | align-items: center;
11 | // border: 5px #28afd8d2;
12 | background: #28afd8d2;
13 | border-radius: 5px;
14 |
15 | }
16 |
17 | //logged out nav bar
18 | .LoggedOut {
19 | display: flex;
20 | flex-direction: row;
21 | //margin-left: auto;
22 | //margin-right: auto;
23 | align-items: center;
24 | border: 5px solid #28afd8d2;
25 | background: #28afd8d2;
26 | border-radius: 10px;
27 | }
28 |
29 | //logo image
30 | .logo {
31 | max-width: 6%;
32 | max-height: auto;
33 | }
34 |
35 | //company name styling
36 | .brand {
37 | font-family: 'Roboto', sans-serif;
38 | font-style: normal;
39 | font-size: 50px;
40 | color: rgb(250, 250, 250);
41 | padding-left: 1%;
42 | }
43 |
44 | .subHeader {
45 | font-family: 'Roboto', sans-serif;
46 | font-style: normal;
47 | font-size: 20px;
48 | color: rgb(250, 250, 250);
49 | padding-left: 1%;
50 | }
51 |
52 | //about us link
53 | .deadLink1 {
54 | display: flex;
55 | font-family: 'Roboto', sans-serif;
56 | font-style: normal;
57 | font-size: 25px;
58 | color: rgb(250, 250, 250);
59 | padding-left: 1%;
60 | //align-self: right;
61 | //text-align: right;
62 | float: right;
63 | position: absolute;
64 | right: 25px;
65 |
66 | }
67 |
68 | // packages link
69 | .deadLink2 {
70 | display: flex;
71 | font-family: 'Roboto', sans-serif;
72 | font-style: normal;
73 | font-size: 25px;
74 | color: rgb(250, 250, 250);
75 | padding-left: 1%;
76 | //align-self: right;
77 | //text-align: right;
78 | float: right;
79 | position: absolute;
80 | right: 150px;
81 |
82 | }
83 |
84 | #inventoryLink {
85 | display: flex;
86 | font-family: 'Roboto', sans-serif;
87 | font-style: normal;
88 | font-size: 25px;
89 | color: rgb(250, 250, 250);
90 | padding-left: 1%;
91 | //align-self: right;
92 | //text-align: right;
93 | float: right;
94 | position: absolute;
95 | right: 150px;
96 | }
97 |
98 | #accountLink {
99 | display: flex;
100 | font-family: 'Roboto', sans-serif;
101 | font-style: normal;
102 | font-size: 25px;
103 | color: rgb(250, 250, 250);
104 | padding-left: 1%;
105 | //align-self: right;
106 | //text-align: right;
107 | float: right;
108 | position: absolute;
109 | right: 25px;
110 | }
--------------------------------------------------------------------------------
/client/components/Inventory.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Popup from 'reactjs-popup'
3 |
4 | // change state and have if statement for what is displayed
5 | const Inventory = (props) => {
6 | return (
7 | // displays all values shown in columns for each inventory item
8 |
9 |
{props.invInfo.id}
10 |
{props.invInfo.name.toUpperCase()}
11 |
{props.invInfo.quantity}
12 |
{props.invInfo.category.toUpperCase()}
13 |
{props.invInfo.location.toUpperCase()}
14 | {/* popup for add items, no info is required. only add info you want changed */}
15 |
Update}>
16 |
37 |
38 | {/* delete button */}
39 |
43 |
44 | );
45 | };
46 |
47 | export default Inventory;
48 |
49 | {/* {props.inv[props.id].id}
50 | {props.inv[props.id].name}
51 | {props.inv[props.id].quantity}
52 | {props.inv[props.id].category}
53 | {props.inv[props.id].location}
*/}
--------------------------------------------------------------------------------
/server/controllers/itemsController.js:
--------------------------------------------------------------------------------
1 | const itemsController = {};
2 | const client = require('../models/eventoryModel');
3 |
4 | itemsController.getAllItems = async (req , res, next ) => {
5 | // right now this just grabs all items, do we want to make sure we only grab items from
6 | // a particular user????
7 | const { id } = req.params;
8 |
9 | try {
10 | const dbRes = await client.query('SELECT * FROM items WHERE items.account_id = $1', [id]);
11 | // console.log(dbRes.rows);
12 | res.locals.items = [...dbRes.rows];
13 | console.log('GETTING TO THE GET ALL USERS MIDDLEWARE');
14 | return next();
15 | }
16 | catch (err) {
17 | return next({
18 | log: 'Error Express - itemsController.getAllUsers',
19 | status: 500,
20 | message: {err}
21 | })
22 | }
23 |
24 | }
25 |
26 | itemsController.updateItem = async (req , res, next ) => {
27 |
28 | const { id } = req.body.items;
29 |
30 | try {
31 | // Line 31 - 47 is to update only field that were filled in
32 | // copy item
33 | const updateItemInfo = {...req.body.items};
34 | // check item for null values
35 | const newUpdatedItem = {};
36 | for (const property in updateItemInfo) {
37 | if (updateItemInfo[property]) {
38 | newUpdatedItem[property] = updateItemInfo[property];
39 | }
40 | }
41 | // grab old item
42 | const dbItemRes = await client.query('SELECT * FROM items WHERE items.id = $1', [id]);
43 |
44 | // spread new info into old item
45 | const newObject = { ...dbItemRes.rows[0], ...newUpdatedItem };
46 |
47 | const { name, info, quantity, category, location } = newObject;
48 |
49 | const dbRes = await client.query(
50 | "UPDATE items SET name = $1, info = $2, quantity = $3, category = $4, location = $5 WHERE id = $6;"
51 | , [name, info, quantity, category, location, id]);
52 | console.log(dbRes.command);
53 | res.locals.updatedItem = { id, name, info, quantity, category, location};
54 | return next();
55 | }
56 | catch (err) {
57 | return next({
58 | log: 'Error Express - itemsController.updateItem',
59 | status: 500,
60 | message: {err}
61 | })
62 | }
63 |
64 | }
65 |
66 | // Create an inventory item on the database
67 | itemsController.createItem = async (req , res, next ) => {
68 |
69 | // Grabs Query info from the request body
70 | const { name, info, quantity, category, location, account_id } = req.body.items;
71 |
72 | console.log(req.body.items);
73 |
74 | try {
75 | const dbRes = await client.query('INSERT INTO items (name, info, quantity, category, location, account_id) VALUES ($1, $2, $3, $4, $5, $6)', [name, info, quantity, category, location, account_id]);
76 | console.log(dbRes);
77 | res.locals.createdItem = { name, info, quantity, category, location, account_id };
78 | return next();
79 | }
80 | catch (err) {
81 | return next({
82 | log: {err},
83 | status: 500,
84 | message: {err}
85 | })
86 | }
87 |
88 | }
89 |
90 | itemsController.deleteItem = async (req , res, next ) => {
91 |
92 | const item_id = req.params.itemId;
93 |
94 | try {
95 | const dbRes = await client.query('DELETE FROM items WHERE ID = $1', [item_id]);
96 | // console.log(dbRes);
97 | return next();
98 | }
99 | catch (err) {
100 | return next({
101 | log: 'Error Express - itemsController.deleteItem',
102 | status: 500,
103 | message: {err}
104 | })
105 | }
106 |
107 | }
108 |
109 | module.exports = itemsController;
--------------------------------------------------------------------------------
/client/components/SignUp.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import '../stylesheets/Login.scss';
3 | import { useState } from 'react';
4 | import { useForm } from "react-hook-form";
5 | import { useEffect } from 'react'
6 |
7 | const SignUp = (props) => {
8 |
9 | const { register, handleSubmit } = useForm();
10 | //const [data, setData] = useState("");
11 | let userList = [];
12 |
13 |
14 | useEffect(() => {
15 | // fetch the data for all accounts
16 | fetch('/accounts/', {
17 | headers: {
18 | 'Accept' : 'application/json',
19 | 'Content-Type': 'application/json'
20 | },
21 | method: 'GET',
22 | })
23 | .then(response => response.json())
24 | .then(data => {
25 | userList = data.accounts;
26 | console.log(data.accounts)
27 | })
28 | .catch(err => {
29 | console.log('were getting an error',err);
30 | });
31 | },[])
32 |
33 | // const signUpRequest = (data) => {
34 | // useEffect(() => {
35 | // //check to see if name exist in DB already.
36 | // // if it does don't submit (message: user already exists)
37 | // //else send to inventory page
38 | // })
39 | // }
40 |
41 |
42 |
43 |
44 | return (
45 |
46 |
Please sign up
47 |
102 |
103 | );
104 | };
105 |
106 | export default SignUp;
--------------------------------------------------------------------------------
/client/stylesheets/Login.scss:
--------------------------------------------------------------------------------
1 | //display :none
2 | //have container toggle between display block and display none
3 |
4 | //Login/Signup compnent
5 |
6 | // .LSPage {
7 | // display: grid;
8 | // grid-auto-columns: 50px;
9 | // }
10 |
11 | .LoginSignupBox {
12 | display: flex;
13 | flex-direction: column;
14 | margin-left: auto;
15 | margin-right: auto;
16 | border-radius: 5%;
17 | background-color: rgb(240, 241, 231);
18 |
19 | border-style: outset;
20 |
21 | width: 250px;
22 | height: 360px;
23 | //justify-content: center;
24 | align-items: center;
25 | border: 3px solid rgb(240, 241, 231);
26 | background-color: #28afd8d2;
27 | border-radius: 20px;
28 | box-sizing: border-box;
29 | height: 500px;
30 | padding: 20px;
31 | width: 320px;
32 | margin-top: 20px;
33 | };
34 |
35 | //welcome Header
36 | .welcome {
37 | color: #eee;
38 | font-family: sans-serif;
39 | font-size: 36px;
40 | font-weight: 600;
41 | margin-top: 30px;
42 | }
43 |
44 | //sign in / sign up header
45 | .SI {
46 | color: #eee;
47 | font-family: sans-serif;
48 | font-size: 19px;
49 | font-weight: 600;
50 | margin-top: 70px;
51 | text-align: center;
52 | }
53 |
54 | // login email input box
55 | #username {
56 | background-color: #303245;
57 | border-radius: 12px;
58 | border: 0;
59 | box-sizing: border-box;
60 | color: #eee;
61 | font-size: 15px;
62 | height:100%;
63 | outline: 0;
64 | padding: 5px 20px 5px;
65 | width: 100%;
66 | margin-top: 5%;
67 |
68 |
69 | }
70 | // login password input box
71 | #PW {
72 | background-color: #303245;
73 | border-radius: 12px;
74 | border: 0;
75 | box-sizing: border-box;
76 | color: #eee;
77 | font-size: 15px;
78 | height:100%;
79 | outline: 0;
80 | padding: 5px 20px 5px 20px;
81 | width: 100%;
82 | margin-top: 5%;
83 | margin-bottom: 50%;
84 | //width: auto;
85 | }
86 |
87 | // signup name box
88 | #namesignup {
89 | background-color: #303245;
90 | border-radius: 12px;
91 | border: 0;
92 | box-sizing: border-box;
93 | color: #eee;
94 | font-size: 15px;
95 | height:100%;
96 | outline: 0;
97 | padding: 5px 20px 5px;
98 | width: 100%;
99 | margin-top: 2%;
100 | }
101 |
102 | // email signup box
103 | #emailsignup {
104 | background-color: #303245;
105 | border-radius: 12px;
106 | border: 0;
107 | box-sizing: border-box;
108 | color: #eee;
109 | font-size: 15px;
110 | height:100%;
111 | outline: 0;
112 | padding: 5px 20px 5px;
113 | width: 100%;
114 | margin-top: 2%;
115 | }
116 |
117 | // signup password signup box
118 | #pwsignup {
119 | background-color: #303245;
120 | border-radius: 12px;
121 | border: 0;
122 | box-sizing: border-box;
123 | color: #eee;
124 | font-size: 15px;
125 | height:100%;
126 | outline: 0;
127 | padding: 5px 20px 5px;
128 | width: 100%;
129 | margin-top: 2%;
130 | }
131 |
132 | // signup category drop down
133 | #dropdownlogin {
134 | background-color: #303245;
135 | border-radius: 12px;
136 | border: 0;
137 | box-sizing: border-box;
138 | color: #eee;
139 | font-size: 15px;
140 | height:100%;
141 | outline: 0;
142 | padding: 5px 20px 5px;
143 | width: 100%;
144 | margin-top: 2%;
145 | margin-bottom: 25%;
146 | }
147 |
148 | // submit button style
149 | .submitbutton {
150 | background-image: linear-gradient(-180deg, #37AEE2 0%, #1E96C8 100%);
151 | border-radius: .5rem;
152 | box-sizing: border-box;
153 | color: #FFFFFF;
154 | display: flex;
155 | font-size: 16px;
156 | justify-content: center;
157 | padding: 1rem 1.75rem;
158 | text-decoration: none;
159 | width: 100%;
160 | border: 0;
161 | //margin-top: 40%;
162 | margin-bottom: 5%;
163 |
164 |
165 |
166 | .button-43:hover {
167 | background-image: linear-gradient(-180deg, #1D95C9 0%, #17759C 100%);
168 | }
169 |
170 |
171 | }
172 |
173 |
174 | // terms and conditions link
175 | .Terms {
176 | display: grid;
177 | justify-content: center;
178 | align-items: center;
179 | margin-left: auto;
180 | margin-right: auto;
181 | max-height: 10%;
182 | position: right;
183 |
184 | }
185 |
186 | #loginArt1 {
187 | max-width: 35%;
188 | max-height: auto;
189 | position: absolute;
190 | margin-top: auto;
191 | margin-bottom: auto;
192 | margin-top: 80px;
193 | }
194 |
195 | #loginArt2 {
196 | display: flex;
197 | max-width: 30%;
198 | max-height: auto;
199 | position: absolute;
200 | margin-top: 63px;
201 | margin-left: 820px;
202 | align-items: center;
203 | }
204 |
--------------------------------------------------------------------------------
/client/components/InventoryDisplay.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import Inventory from './Inventory.jsx';
3 | import Popup from 'reactjs-popup';
4 | //import AddItem from './AddItem.jsx';
5 | import '../stylesheets/Inventory.scss';
6 | import { useForm } from 'react-hook-form';
7 | import 'regenerator-runtime/runtime';
8 |
9 | const InventoryDisplay = (props) => {
10 |
11 | // state for inventory
12 | const [ inv, setInv ] = useState([]);
13 |
14 |
15 | const [ userId, setUserId ] = useState([props.user.id])
16 | // function to add item
17 | const addedItem = (e) => {
18 | e.preventDefault();
19 | const body = {
20 | items: {
21 | account_id: e.target[0].value,
22 | name: e.target[1].value,
23 | quantity: e.target[2].value,
24 | info: e.target[3].value,
25 | category: e.target[4].value,
26 | location: e.target[5].value,
27 | }
28 | }
29 | const url = 'http://localhost:3000/items/';
30 | fetch(url, {
31 | method: 'POST',
32 | headers: {
33 | 'Content-Type': 'application/json'
34 | },
35 | body: JSON.stringify(body),
36 | })
37 | .then(() => inventoryGet());
38 | }
39 |
40 | // function to get the user ID
41 | // function to delete item
42 | const deletedItem = (e) => {
43 | e.preventDefault();
44 | const url = 'http://localhost:3000/items/'.concat(e.target[0].value);
45 | fetch(url, {
46 | method: 'DELETE',
47 | headers: {
48 | 'Content-Type': 'application/json'
49 | },
50 | })
51 | .then(() => inventoryGet());
52 | }
53 |
54 | // function that runs to update item
55 | const updatedItem = (e) => {
56 | e.preventDefault();
57 |
58 | // grab all relevant info from the synthetic event when updat button is pressed
59 | const body = {
60 | items: {
61 | id: e.target[0].value,
62 | name: e.target[1].value,
63 | quantity: e.target[2].value,
64 | info: e.target[3].value,
65 | category: e.target[4].value,
66 | location: e.target[5].value,
67 | }
68 | }
69 | // Patch request to update an item
70 | const url = 'http://localhost:3000/items/';
71 | fetch(url, {
72 | method: 'PATCH',
73 | headers: {
74 | 'Content-Type': 'application/json'
75 | },
76 | body: JSON.stringify(body),
77 | })
78 | .then(() => inventoryGet());
79 | }
80 |
81 | // function to get current users inventory
82 | const inventoryGet = () => {
83 | let invData;
84 | const url = 'http://localhost:3000/items/'.concat(props.user.id);
85 |
86 | fetch(url, {
87 | method: 'GET',
88 | headers: {
89 | 'Content-Type': 'application/json'
90 | }
91 | })
92 | .then((data) => data.json())
93 | .then((data) => invData = data.items)
94 | .then((data) => {
95 | const invClass = [];
96 | // make an inventory component for each item received in the get request
97 | invData.sort((a, b) => b.id - a.id)
98 | invData.forEach((el, index) => {
99 | invClass.push()
100 | })
101 | setInv(invClass);
102 | });
103 | }
104 | // get inventory on first render
105 | useEffect(() => {
106 | inventoryGet();
107 | }, []);
108 |
109 |
110 | return (
111 |
112 |
113 |
ID
114 | NAME
115 | QUANTITY
116 | CATEGORY
117 | LOCATION
118 |
119 |
120 | {/* whole div needs to be added per item in inventory array */}
121 |
122 | {inv}
123 |
124 |
125 |
Add Item} position="right center">
126 |
149 |
150 |
151 |
152 |
153 | );
154 | };
155 |
156 | export default InventoryDisplay;
--------------------------------------------------------------------------------
/server/controllers/accountsController.js:
--------------------------------------------------------------------------------
1 | const accountsController = {};
2 | const client = require('../models/eventoryModel');
3 |
4 | /*
5 | * Middleware to get all Accounts
6 | */
7 | accountsController.getAllAccounts = async (req , res, next ) => {
8 | try {
9 | // query the database for all accounts in accounts
10 | const dbRes = await client.query('SELECT * FROM accounts');
11 | // store the result of the query into res.locals.accounts
12 | res.locals.accounts = dbRes.rows
13 | // return next
14 | return next();
15 | }
16 | catch (err) {
17 | // if there is an err, return the errorObj to the global error handler
18 | return next({
19 | log: 'Error Express - usersController.getAllUsers',
20 | status: 500,
21 | message: {err}
22 | })
23 | }
24 | }
25 | /*
26 | * Middleware to get a single account
27 | */
28 | accountsController.getAnAccount = async (req , res, next ) => {
29 | // get the name from req.params
30 | const { name } = req.params;
31 | try {
32 | // create an object with the query text, and the values to insert into the query
33 | const query = {
34 | text:'SELECT * FROM accounts WHERE accounts.name = $1',
35 | values: [id],
36 | }
37 | // query the database for all accounts in accounts
38 | const dbRes = await client.query(query);
39 | // store the result of the query into res.locals.accounts
40 | res.locals.account = dbRes.rows[0]
41 | // return next
42 | return next();
43 | }
44 | catch (err) {
45 | // if there is an err, return the errorObj to the global error handler
46 | return next({
47 | log: 'Error Express - accountsController.getAnAccount',
48 | status: 500,
49 | message: {err},
50 | })
51 | }
52 | }
53 | /*
54 | * Middleware to create an account
55 | */
56 | accountsController.createAccount = async (req , res, next ) => {
57 |
58 | // get the name, email, password, type from the req.body
59 | const {name, email, password, type} = req.body;
60 | try {
61 | // create an object with the query text, and the values to insert into the query
62 | const query = {
63 | text:'INSERT INTO accounts (name, email, password, type) VALUES ($1, $2, $3, $4)',
64 | values: [name, email, password, type],
65 | }
66 | // query the database for all accounts in accounts
67 | await client.query(query);
68 | // store the result of the query into res.locals.accounts
69 | res.locals.message = `Account ${name} has been created`
70 | // return next
71 | return next();
72 | }
73 | catch (err) {
74 | // if there is an err, return the errorObj to the global error handler
75 | return next({
76 | log: 'Error Express - accountsController.createAccount',
77 | status: 500,
78 | message: {err},
79 | })
80 | }
81 | }
82 |
83 | /**
84 | * Middleware to login
85 | */
86 | accountsController.login = async (req, res, next) => {
87 | // get the username and password from the req body
88 | const {username, password} = req.body;
89 | // get the account information from the database
90 | try {
91 | // select all users with the username
92 | const query = {
93 | text:'SELECT * from accounts WHERE name = $1',
94 | values: [username],
95 | }
96 | // query the database, assign the result in dbRes
97 | const dbRes = await client.query(query);
98 | //check if the account password matches the req body password
99 | if (dbRes.rows[0].password === password) {
100 | // assign res.locals.account the account information
101 | res.locals.account = dbRes.rows[0];
102 | // go to the next middleware
103 | next();
104 | } else {
105 | // if the passwords dont match return an error
106 | return next({
107 | log: 'Error Express - accountsController.login, password does not match',
108 | status: 500,
109 | message: {err: "Password does not match"},
110 | })
111 | }
112 | }catch(err) {
113 | // if there is an err, return the errorObj to the global error handler
114 | // if it errs here, the account does not exist
115 | return next({
116 | log: 'Error Express - accountsController.login',
117 | status: 500,
118 | message: {err: "Account does not exist"},
119 | })
120 | }
121 | }
122 | /**
123 | * Middleware to change password
124 | */
125 | accountsController.changePassword = async (req , res, next ) => {
126 | // get the name from params
127 | const { name } = req.params;
128 | // get the new password from the body
129 | const { password } = req.body;
130 | try {
131 | // update the password where the account name is equal to name
132 | const query = {
133 | text:'UPDATE accounts SET password = $2 WHERE accounts.name = $1',
134 | values: [name, password]
135 | }
136 | await client.query(query);
137 | // assign res.locals.message a string stating the password has been updated
138 | res.locals.message = "Password has been updated";
139 | // go to the next middleware
140 | next();
141 | }
142 | catch (err) {
143 | // if there is an err, return the errorObj to the global error handler
144 | return next({
145 | log: 'Error Express - accountsController.changePassword',
146 | status: 500,
147 | message: {err},
148 | })
149 | }
150 | }
151 | /**
152 | * Middleware to delete an account
153 | */
154 | accountsController.deleteAccount = async (req , res, next ) => {
155 | // get the name from params
156 | const { name } = req.params;
157 | try {
158 | // update the password where the account name is equal to name
159 | const query = {
160 | text:'DELETE FROM accounts WHERE accounts.name = $1',
161 | values: [name]
162 | }
163 | await client.query(query);
164 | // assign res.locals.message a string stating the password has been updated
165 | res.locals.message = "Account has been deleted";
166 | // go to the next middleware
167 | next();
168 | }
169 | catch (err) {
170 | // if there is an err, return the errorObj to the global error handler
171 | return next({
172 | log: 'Error Express - accountsController.deleteAccount',
173 | status: 500,
174 | message: {err},
175 | })
176 | }
177 | }
178 |
179 |
180 |
181 | module.exports = accountsController;
--------------------------------------------------------------------------------