├── README.md ├── .babelrc ├── .gitignore ├── src └── client │ ├── styles │ ├── _variables.scss │ ├── categories.scss │ └── index.scss │ ├── index.js │ └── components │ ├── Login.jsx │ ├── Compare.jsx │ ├── App.jsx │ ├── Categories.jsx │ ├── CreateTechnology.jsx │ ├── CreateCategory.jsx │ └── CreateTechnology2.jsx ├── index.html ├── server ├── db │ ├── db.js │ └── initDb.js ├── routes │ ├── apiRouter.js │ ├── categoriesRouter.js │ └── loginRouter.js ├── server.js └── controllers │ ├── categoriesController.js │ └── loginController.js ├── package.json └── webpack.config.js /README.md: -------------------------------------------------------------------------------- 1 | # Comparisoon -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@babel/plugin-transform-runtime"] 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | 4 | #### build ##### 5 | build/ 6 | .env 7 | 8 | ### DS Store ##### 9 | **/.DS_Store -------------------------------------------------------------------------------- /src/client/styles/_variables.scss: -------------------------------------------------------------------------------- 1 | $font-family: 'Luckiest Guy', "Helvetica Neue", Helvetica, sans-serif; 2 | $font-color: #f0ece4; 3 | $bg-color: #a1bfe6; 4 | $btn-color: #274c7d; 5 | $border-radius: 4px; -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Comparisoon 5 | 6 | 7 |
8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /server/db/db.js: -------------------------------------------------------------------------------- 1 | const { Client } = require('pg'); 2 | 3 | //const connectToDb = async () => { 4 | // const client = new Client({ connectionString: process.env.PGURL}); 5 | // await client.connect(); 6 | // return client; 7 | //} 8 | 9 | module.exports = new Client({ connectionString: process.env.PGURL}); -------------------------------------------------------------------------------- /src/client/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "react-dom"; 3 | import App from "./components/App.jsx"; 4 | 5 | import './styles/index.scss'; 6 | 7 | render(, document.getElementById("root")); 8 | 9 | //render( 10 | // 11 | // 12 | // , document.getElementById("root")); -------------------------------------------------------------------------------- /src/client/styles/categories.scss: -------------------------------------------------------------------------------- 1 | .mainContainer { 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: flex-start; 5 | align-items: center; 6 | padding: 0.5rem; 7 | margin: 1rem; 8 | 9 | .container { 10 | display: flex; 11 | flex-direction: row; 12 | justify-content: center; 13 | align-self: flex-start; 14 | } 15 | } -------------------------------------------------------------------------------- /server/routes/apiRouter.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | const categoriesRouter = require("./categoriesRouter"); 4 | 5 | router.use("/categories", categoriesRouter); 6 | 7 | router.use("/items", (req, res) => 8 | res.status(200).json({ 9 | message: "items", 10 | }) 11 | ); 12 | 13 | router.use("/compare", (req, res) => 14 | res.status(200).json({ 15 | message: "In Compare", 16 | }) 17 | ); 18 | 19 | module.exports = router; 20 | -------------------------------------------------------------------------------- /src/client/components/Login.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | // once logged in, redirect to Landing Page (view 2) 4 | // implement anchor tag with proper endpoint for backend to pick up 5 | export default function Login() { 6 | return ( 7 |
8 |

***COMPARISOON***

9 | 10 | Sign in to GITHUB 11 | 12 |
13 | ); 14 | } 15 | 16 | //

You need to log in first!

-------------------------------------------------------------------------------- /server/routes/categoriesRouter.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | const { 4 | getCategories, 5 | insertCategory, 6 | getCategoryById, 7 | } = require("../controllers/categoriesController.js"); 8 | 9 | router.get("/", getCategories, (req, res) => { 10 | res.status(200).json(res.locals.categories); 11 | }); 12 | 13 | router.post("/", insertCategory, (req, res) => { 14 | res.sendStatus(200); 15 | }); 16 | 17 | router.get("/:categoryId", getCategoryById, (req, res) => { 18 | res.status(200).json(res.locals.category); 19 | }); 20 | 21 | module.exports = router; 22 | -------------------------------------------------------------------------------- /src/client/components/Compare.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, useState, useEffect } from "react"; 2 | import { Link } from "react-router-dom"; 3 | // import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; 4 | 5 | // 2 buttons with Links on them 6 | export default function ComparePage() { 7 | // const history = useHistory(); 8 | // const goMain = () => history.push('categories'); 9 | return ( 10 |
11 |

Comparison of Technologies in the Category!!!

12 | 15 |
16 | ); 17 | } -------------------------------------------------------------------------------- /server/routes/loginRouter.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const path = require('path'); 3 | const router = express.Router(); 4 | const { 5 | getGitHubData, 6 | storeId, 7 | setCookie, 8 | } = require("../controllers/loginController.js"); 9 | 10 | router.get("/callback", getGitHubData, storeId, setCookie, (req, res) => { 11 | console.log('end of callback chain'); 12 | res.status(200).redirect('/'); 13 | //res.status(200).json(res.locals.oauthData); 14 | }); 15 | 16 | router.get("/", (req, res) => { 17 | res.redirect( 18 | `https://github.com/login/oauth/authorize?client_id=${process.env.CLIENT_ID}` 19 | ); 20 | }); 21 | 22 | module.exports = router; -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | const express = require("express"); 3 | const app = express(); 4 | const path = require("path"); 5 | const cors = require("cors"); 6 | const cookieParser = require('cookie-parser'); 7 | const apiRouter = require("./routes/apiRouter.js"); 8 | const loginRouter = require("./routes/loginRouter.js"); 9 | const db = require("./db/db.js"); 10 | 11 | db.connect(); 12 | 13 | /* GLOBAL HANDLERS */ 14 | app.use(cookieParser()); 15 | app.use(express.json()); 16 | app.use(express.urlencoded({ 17 | extended: false 18 | })); 19 | 20 | app.use(cors()); 21 | 22 | /* ROUTES */ 23 | app.use("/api", apiRouter); 24 | 25 | app.use("/login", loginRouter); 26 | 27 | app.use("/build", express.static(path.join(__dirname, "/build"))); 28 | 29 | // maybe 30 | app.get('/bundle.js', (req, res) => res.sendFile(path.resolve(__dirname, '../build/bundle.js'))); 31 | 32 | app.get("*", (req, res) => { 33 | res.sendFile(path.join(__dirname, "../index.html")); 34 | }); 35 | 36 | /* Global error handler */ 37 | app.use((err, req, res, next) => { 38 | const defaultErr = { 39 | status: 400, 40 | message: "Bad Request", 41 | }; 42 | const error = { 43 | ...defaultErr, 44 | ...err 45 | }; 46 | res.status(error.status).json(error); 47 | }); 48 | 49 | const port = 3000; 50 | app.listen(port, () => console.log(`Server running on port ${port}`)); -------------------------------------------------------------------------------- /src/client/styles/index.scss: -------------------------------------------------------------------------------- 1 | // @use 'variables' as *; 2 | @import 'variables'; 3 | 4 | body { 5 | margin: 0; 6 | font-family: $font-family; 7 | background-color: $bg-color; 8 | // position: relative; 9 | // width: 100%; 10 | // margin: 0 auto; 11 | // display: block; 12 | } 13 | 14 | button { 15 | padding: 0.7rem; 16 | font-size: 1em; 17 | } 18 | 19 | .mainContainer { 20 | display: flex; 21 | // background-color: $bg-color; 22 | height: 100vh; 23 | color: white; 24 | } 25 | 26 | .mainContainer.loginPage { 27 | flex-direction: column; 28 | align-items: center; 29 | justify-content: center; 30 | } 31 | 32 | .mainContainer.newCategory { 33 | display: flex; 34 | flex-direction: column; 35 | justify-content: flex-start; 36 | align-items: center; 37 | padding: 0.5rem; 38 | margin: 1rem; 39 | // height: 100vh; 40 | .inputArea { 41 | display: flex; 42 | flex-direction: row; 43 | justify-content: center; 44 | align-self: flex-start; 45 | .fieldsContainer { 46 | width: calc(100% - 1rem); 47 | // height: 1em; 48 | // padding: 0.7rem; 49 | // margin: 1rem 0; 50 | } 51 | .btn, button { 52 | margin: 1rem 0; 53 | padding: 0 10px; 54 | } 55 | } 56 | // .fieldsContainer { 57 | // display: flex; 58 | // } 59 | } 60 | 61 | .githubBtn { 62 | // color: white; 63 | color: $font-color; 64 | // background-color: $blue; 65 | background-color: $btn-color; 66 | text-decoration: none; 67 | padding: 1rem; 68 | border-radius: $border-radius; 69 | border: none; 70 | font-size: 1rem; 71 | } 72 | 73 | .newCategory {} -------------------------------------------------------------------------------- /server/controllers/categoriesController.js: -------------------------------------------------------------------------------- 1 | const db = require("../db/db.js"); 2 | const categoriesController = {}; 3 | 4 | categoriesController.getCategories = (req, res, next) => { 5 | const sqlQuery = `SELECT * FROM categories WHERE userid=$1;`; 6 | db.query(sqlQuery, [req.cookies.id]) 7 | .then((response) => { 8 | res.locals.categories = response.rows; 9 | console.log("getCategories was successful"); 10 | next(); 11 | }) 12 | .catch((err) => { 13 | next({ message: "Error in categoriesController.getCategories: " + err }); 14 | }); 15 | }; 16 | 17 | categoriesController.insertCategory = (req, res, next) => { 18 | const sqlQuery = `INSERT INTO categories(name, fields, userid) VALUES($1,$2,$3);`; 19 | console.log('BODY ', req.body); 20 | db.query(sqlQuery, [req.body.name, req.body.fields, req.cookies.id]) 21 | .then(() => { 22 | console.log("insertCategory was successful"); 23 | next(); 24 | }) 25 | .catch((err) => { 26 | next({ message: "Error in categoriesController.insertCategory: " + err }); 27 | }); 28 | }; 29 | 30 | categoriesController.getCategoryById = (req, res, next) => { 31 | const sqlQuery = `SELECT * FROM categories WHERE id=$1;`; 32 | db.query(sqlQuery, [req.params.categoryId]) 33 | .then((response) => { 34 | res.locals.category = response.rows[0]; 35 | console.log("getCategories by ID was successful"); 36 | next(); 37 | }) 38 | .catch((err) => { 39 | next({ message: "Error in categoriesController.getCategoryById: " + err }); 40 | }); 41 | }; 42 | 43 | module.exports = categoriesController; 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "comparisoon", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "", 6 | "scripts": { 7 | "start": "cross-env NODE_ENV=production nodemon server/server.js", 8 | "build": "cross-env NODE_ENV=production webpack", 9 | "dev": "cross-env NODE_ENV=development concurrently \"nodemon server/server.js\" \"webpack-dev-server --open\"" 10 | }, 11 | "author": "The Comparisooners, Inc. Arggggggg", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/core": "^7.10.3", 15 | "@babel/plugin-transform-runtime": "^7.10.4", 16 | "@babel/preset-env": "^7.10.3", 17 | "@babel/preset-react": "^7.10.1", 18 | "babel-loader": "^8.1.0", 19 | "concurrently": "^5.3.0", 20 | "cross-env": "^7.0.2", 21 | "css-loader": "^3.6.0", 22 | "dotenv": "^8.2.0", 23 | "http-proxy-middleware": "^1.0.4", 24 | "nodemon": "^2.0.4", 25 | "sass": "^1.26.9", 26 | "node-sass": "^4.14.1", 27 | "sass-loader": "^8.0.2", 28 | "style-loader": "^1.2.1", 29 | "webpack": "^4.43.0", 30 | "webpack-cli": "^3.3.12", 31 | "webpack-dev-server": "^3.11.0" 32 | }, 33 | "dependencies": { 34 | "@babel/runtime": "^7.10.3", 35 | "axios": "^0.19.2", 36 | "bcrypt": "^5.0.0", 37 | "cookie-parser": "^1.4.5", 38 | "cors": "^2.8.5", 39 | "express": "^4.17.1", 40 | "fetch": "^1.1.0", 41 | "node-fetch": "^2.6.0", 42 | "pg": "^8.3.0", 43 | "react": "^16.13.1", 44 | "react-dom": "^16.13.1", 45 | "react-router": "^5.2.0", 46 | "react-router-dom": "^5.2.0", 47 | "styled-components": "^5.1.1", 48 | "webpack": "^4.43.0" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /server/db/initDb.js: -------------------------------------------------------------------------------- 1 | const db = require('./db.js'); 2 | 3 | db.query( 4 | `create table users ( 5 | id character varying(60) not null primary key 6 | );`) 7 | .then(() => console.log('Created users table')) 8 | .then(db.query( 9 | `create table items ( 10 | id serial primary key, 11 | name character varying(200) not null, 12 | userId character varying(60) not null, 13 | FOREIGN KEY(userId) REFERENCES users(id) 14 | );` 15 | )) 16 | .then(() => console.log('Created items table')) 17 | .catch(err => console.log('Error in creating tables', err)); 18 | 19 | 20 | // Create users table DONE 21 | `create table users ( 22 | id character varying(60) not null primary key 23 | );` 24 | 25 | // Create items table DONE 26 | `create table items ( 27 | id serial primary key, 28 | name character varying(200) not null, 29 | userId character varying(60) not null, 30 | FOREIGN KEY(userId) REFERENCES users(id) 31 | );` 32 | 33 | // Create categories table DONE 34 | `create table categories ( 35 | id serial primary key, 36 | name character varying(200) not null, 37 | fields character varying(200) [] not null, 38 | userId character varying(60) not null, 39 | FOREIGN KEY(userId) REFERENCES users(id) 40 | );` 41 | 42 | // Create item_category table DONE 43 | `create table item_category ( 44 | userId character varying(60) not null, 45 | itemId int not null, 46 | categoryId int not null, 47 | FOREIGN KEY(userId) REFERENCES users(id), 48 | FOREIGN KEY(itemId) REFERENCES items(id), 49 | FOREIGN KEY(categoryId) REFERENCES categories(id) 50 | );` 51 | 52 | 53 | //Create table all_inputs 54 | `create table all_inputs ( 55 | userId character varying(60) not null, 56 | itemId int, 57 | categoryId int, 58 | fieldName character varying(200), 59 | value text, 60 | FOREIGN KEY(userId) REFERENCES users(id), 61 | FOREIGN KEY(itemId) REFERENCES items(id), 62 | FOREIGN KEY(categoryId) REFERENCES categories(id) 63 | );` 64 | 65 | 66 | -------------------------------------------------------------------------------- /server/controllers/loginController.js: -------------------------------------------------------------------------------- 1 | const fetch = require("node-fetch"); 2 | const db = require("../db/db.js"); 3 | 4 | const loginController = {}; 5 | 6 | // get user's GitHub data from the API with an access token 7 | loginController.getGitHubData = (req, res, next) => { 8 | const body = { 9 | client_id: process.env.CLIENT_ID, 10 | client_secret: process.env.CLIENT_SECRET, 11 | code: req.query.code, 12 | }; 13 | fetch(`https://github.com/login/oauth/access_token`, { 14 | method: "POST", 15 | headers: { 16 | "Content-Type": "application/json", 17 | Accept: "application/json", 18 | }, 19 | body: JSON.stringify(body), 20 | }) 21 | .then((res) => res.json()) 22 | .then((data) => { 23 | let token = data["access_token"]; 24 | return token; 25 | }) 26 | .then((token) => { 27 | return fetch("https://api.github.com/user", { 28 | headers: { 29 | "Content-Type": "application/json", 30 | Accept: "application/json", 31 | Authorization: `token ${token}`, 32 | }, 33 | }); 34 | }) 35 | .then((data) => data.json()) 36 | .then((data) => { 37 | const { id, avatar_url, name } = data; 38 | res.locals.oauthData = { avatar_url, name }; 39 | res.locals.id = id; 40 | console.log("res.locals", res.locals); 41 | next(); 42 | }) 43 | .catch((err) => next({ message: err.message })); 44 | }; 45 | 46 | // store plaintext id in users table 47 | loginController.storeId = (req, res, next) => { 48 | const idQuery = `INSERT INTO users(id) VALUES($1)`; 49 | db.query(idQuery, [res.locals.id]) 50 | .then((res) => { 51 | next(); 52 | }) 53 | .catch((err) => { 54 | //console.log("storeId err", err); 55 | if (err.code == "23505") { 56 | console.log("duplicate key error"); 57 | next(); 58 | } else { 59 | next({ message: "Error in loginController.storeId: " + err }); 60 | } 61 | }); 62 | }; 63 | 64 | // set cookie with hash id 65 | loginController.setCookie = (req, res, next) => { 66 | res.cookie("id", res.locals.id, { httpOnly: true }); 67 | next(); 68 | }; 69 | 70 | module.exports = loginController; 71 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | // const path = require('path'); 2 | // // const MinifyPlugin = require("babel-minify-webpack-plugin"); 3 | 4 | // module.exports = { 5 | // mode: process.env.NODE_ENV, 6 | // entry: './src/client/index.js', 7 | // output: { 8 | // filename: 'bundle.js', 9 | // path: path.resolve(__dirname, 'build') 10 | // }, 11 | // // devServer: { 12 | // // publicPath: '/build/', 13 | // // proxy: { 14 | // // "*": "http://localhost:3000" 15 | // // }, 16 | // // hot: true 17 | // // }, 18 | // module: { 19 | // rules: [ 20 | // { 21 | // test: /\.jsx?/, 22 | // exclude: path.resolve(__dirname, 'node_modules/'), 23 | // use: { 24 | // loader: 'babel-loader', 25 | // options: { 26 | // presets: ['@babel/preset-env', '@babel/preset-react'] 27 | // } 28 | // } 29 | // }, 30 | // { 31 | // test: /\.s?css$/i, 32 | // exclude: path.resolve(__dirname, 'node_modules/'), 33 | // use: ['style-loader', 'css-loader', 'sass-loader'] 34 | // } 35 | // ] 36 | // } 37 | // }; 38 | 39 | const path = require('path'); 40 | // const MinifyPlugin = require("babel-minify-webpack-plugin"); 41 | 42 | module.exports = { 43 | entry: './src/client/index.js', 44 | output: { 45 | filename: 'bundle.js', 46 | path: path.resolve(__dirname, 'build') 47 | }, 48 | mode: process.env.NODE_ENV, 49 | devServer: { 50 | historyApiFallback: true, 51 | publicPath: 'http://localhost:8080/build/', 52 | proxy: { 53 | '/': { 54 | target: 'http://localhost:3000/' 55 | }, 56 | }, 57 | hot: true, 58 | }, 59 | module: { 60 | rules: [{ 61 | test: /\.jsx?/, 62 | exclude: path.resolve(__dirname, 'node_modules/'), 63 | use: { 64 | loader: 'babel-loader', 65 | options: { 66 | presets: ['@babel/preset-env', '@babel/preset-react'] 67 | }, 68 | } 69 | }, 70 | { 71 | test: /\.s?css$/i, 72 | exclude: path.resolve(__dirname, 'node_modules/'), 73 | use: ['style-loader', 'css-loader', 'sass-loader'], 74 | } 75 | ], 76 | }, 77 | } -------------------------------------------------------------------------------- /src/client/components/App.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { 3 | BrowserRouter as Router, 4 | Switch, 5 | Route, 6 | Redirect, 7 | } from "react-router-dom"; 8 | 9 | import Categories from "./Categories.jsx"; 10 | import CreateCategory from "./CreateCategory.jsx"; 11 | import CreateTechnology from "./CreateTechnology.jsx"; 12 | import Login from "./Login.jsx"; 13 | import Compare from "./Compare.jsx"; 14 | 15 | function App() { 16 | // "/login" - OAuth 17 | // "/categories/{categoryID}" 18 | // "/technologies/{technologyID}" 19 | // "/compare" 20 | 21 | console.log('COOKIES ', document.cookie); 22 | // const loggedIn = document.cookie 23 | // .split(";") 24 | // .some((item) => item.trim().startsWith("id=")); 25 | // modify later! 26 | const loggedIn = true; 27 | // const loggedIn = false; 28 | return ( 29 | 30 | 31 | 32 | {loggedIn ? : } 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | ); 52 | } 53 | 54 | export default App; 55 | 56 | // { useState, useEffect } 57 | 58 | // 59 | // 60 | // 61 | 62 | //Hooks don't work in classes 63 | //useState - returns an array with 2 values. 64 | //Use array destructuring 65 | // 1. Current State (count). 66 | // 2. Function (allow us to update our state). 67 | 68 | // function App() { 69 | // const [count, setCount] = useState(4); 70 | 71 | // //Every time we call setCount (update) function, it re-renders our component with a new value for our count. 72 | // //If you're chaining setCount (update) function, use function version inside parameter. 73 | // function decrementCount() { 74 | // setCount(count - 1); 75 | // } 76 | 77 | // function incrementCount() { 78 | // setCount(count + 1); 79 | // } 80 | 81 | // return ( 82 | //
83 | // 84 | // {count} 85 | // 86 | //
87 | // ) 88 | // } 89 | -------------------------------------------------------------------------------- /src/client/components/Categories.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { Link } from "react-router-dom"; 3 | import "../styles/categories.scss"; 4 | // import styled from 'styled-components'; 5 | // import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; 6 | 7 | // 2 buttons with Links on them 8 | function Categories() { 9 | const [state, setState] = useState([]); 10 | const [hasError, setHasError] = useState(false); 11 | 12 | useEffect(() => { 13 | fetch("/api/categories") 14 | .then((response) => { 15 | console.log("response ", response); 16 | return response.json(); 17 | }) 18 | .then((data) => { 19 | console.log("data", data); 20 | for (let i = 0; i < data.length; i++) { 21 | console.log("data.name", data[i].name); 22 | console.log("data.id", data[i].id); 23 | } 24 | 25 | setState(data); 26 | console.log("state", state); 27 | // let {name , id} = state; 28 | }) 29 | .catch((err) => { 30 | console.log("error", err); 31 | setHasError(true); 32 | }); 33 | }, []); 34 | 35 | return ( 36 |
37 |

Categories!!!

38 |
39 | {hasError ? ( 40 |
Oh, no! There was an error.
41 | ) : ( 42 | state.map((category) => { 43 | return ( 44 |
  • 45 | 51 | {category.name} 52 | 53 |
  • 54 | ); 55 | }) 56 | )} 57 |
    58 | 61 | 64 |
    65 | ); 66 | } 67 | 68 | export default Categories; 69 | 70 | /* 71 | const renderCategories = () => { 72 | return state.map((category) => { 73 | const { name, id } = category; 74 | 75 | return ( 76 |
  • 77 | 83 | Hi, I'm an li and I work now, yay!!! 84 | {name} 85 |
  • 86 | ); 87 | }); 88 | }; 89 | 90 | return ( 91 |
    92 |

    Categories

    93 |
    {renderCategories()}
    94 | 97 | 100 |
    101 | ); 102 | */ 103 | -------------------------------------------------------------------------------- /src/client/components/CreateTechnology.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, useState, useEffect, useRef } from "react"; 2 | import { Link } from "react-router-dom"; 3 | 4 | export default function NewTechnology(props) { 5 | /* 6 | { 7 | "name": React, 8 | "fields": { 9 | "FrontendLibrary:" {"pros": "....", "cons": "...", "dataflow": "upwards"} 10 | "Server:" {"pros": "....", "cons": "...", "dataflow": "upwards"} 11 | } 12 | } 13 | */ 14 | 15 | const inputItemRef = useRef(null); 16 | const [categories, setCategories] = useState([]); //fetched data (together with fields, id, name), all categories 17 | const [category, setCategory] = useState(''); //Individual Category (id) 18 | const [values, setValues] = useState([]); 19 | 20 | //item name - input form 21 | //Categories - Dropdown 22 | //Fields - text area 23 | 24 | useEffect(() => { 25 | fetch("/api/categories") 26 | .then((response) => { 27 | console.log("response ", response); 28 | return response.json(); 29 | }) 30 | .then((data) => { 31 | console.log("data", data); 32 | for (let i = 0; i < data.length; i++) { 33 | console.log("data.name", data[i].name); 34 | console.log("data.id", data[i].id); 35 | console.log("data.fields: ", data[i].fields); 36 | for (let f of data[i].fields) { 37 | console.log('F ', f); 38 | } 39 | } 40 | setCategories(data); 41 | }) 42 | }, []); 43 | 44 | // console.log("categories: ", categories); 45 | // {fields.map((f, i) => { 46 | // return ( 47 | // 53 | // ); 54 | // })} 55 | 56 | const handleChosenCategory = (e) => { 57 | setCategory(e.target.value); 58 | } 59 | 60 | // const handleField = (e) => { 61 | 62 | // } 63 | 64 | const Dropdown = (props) => { 65 | return ( 66 |
    67 | 74 |
    75 | ) 76 | } 77 | 78 | //Create field components 79 | const Field = (props) => { 80 | return ( 81 |
    82 | 90 |
    91 | ) 92 | } 93 | 94 | return ( 95 |
    96 |
    97 | 105 |
    106 | 107 |
    108 |
    109 | {console.log("category: ", category)} 110 | { 111 | categories.filter((c) => c === category)['fields'].map((field, i) => { 112 | 113 | })} 114 |
    115 | 118 |
    119 |
    120 | ) 121 | } -------------------------------------------------------------------------------- /src/client/components/CreateCategory.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef, useEffect } from "react"; 2 | import { Link } from "react-router-dom"; 3 | // import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; 4 | // , useHistory 5 | 6 | // 2 buttons with Links on them 7 | export default function NewCategory() { 8 | // const [categoryName, setCategoryName] = useState(""); 9 | // make frontend add pros and cons fields? 10 | const [fields, setFields] = useState(["pros", "cons"]); 11 | // increment on every addFiled button click 12 | const [count, setCount] = useState(fields.length); 13 | const inputCategoryRef = useRef(null); 14 | // const inputFieldsRef = useRef(null); 15 | // const inputFieldsRef = useRef([]); 16 | 17 | // useLayoutEffect(() => { 18 | // effect 19 | // return () => { 20 | // cleanup 21 | // }; 22 | // }, [input]); 23 | 24 | const handleFieldValue = (e) => { 25 | const fieldsArr = [...fields]; 26 | fieldsArr[e.target.id] = e.target.value; 27 | setFields(fieldsArr); 28 | }; 29 | 30 | // add another little component for the field to the bottom of container 31 | const handleNewField = (e) => { 32 | e.preventDefault(); 33 | // const fieldsArr = fields.push(""); 34 | // fields.push(e.target.value); 35 | // console.log('ARR ', fieldsArr); 36 | // console.log('TYPE ', typeof fieldsArr); 37 | setFields(fields.concat("")); 38 | // inputFieldsRef.current.value.push(e.target.value); 39 | // inputFieldsRef.current.value.push(""); 40 | const newC = count + 1; 41 | setCount(newC); 42 | console.log("COUNT, FIELDS ", count, fields); 43 | // const fieldsArr = fields.push(inputFieldRef.current.value); 44 | // setFields(fieldsArr); 45 | }; 46 | 47 | const handleSaveCategory = (e) => { 48 | // if (inputCategoryRef.current.value && inputFieldsRef.current.value) { 49 | e.preventDefault(); 50 | const unique = (new Set(fields)).size === fields.length; 51 | console.log('UNIQUE ', unique); 52 | console.log("FIELDS IN SAVE1 ", fields); 53 | if (inputCategoryRef.current.value && fields && unique) { 54 | console.log("FIELDS IN SAVE ", fields); 55 | const data = { 56 | name: inputCategoryRef.current.value, 57 | fields: fields, 58 | }; 59 | 60 | // const newC = 0; 61 | // setCount(0); 62 | console.log("DATA TO SAVE ", data); 63 | // saveCategory(data) 64 | fetch("/api/categories", { 65 | method: "POST", 66 | headers: { 67 | "Content-Type": "application/json", 68 | }, 69 | body: JSON.stringify(data), 70 | }) 71 | // .then((resp) => { 72 | // resp = resp.json(); 73 | // console.log("RESP ", resp); 74 | // return resp; 75 | // }) 76 | .then((status) => { 77 | // check if responding with status code 200? 78 | // status.ok should be true 79 | console.log("Status ", status); 80 | inputCategoryRef.current.value = ""; 81 | setFields(["pros", "cons"]); 82 | }) 83 | .catch((err) => { 84 | // give user an error message on unsuccessful Post req 85 | console.log("Error ", err); 86 | }); 87 | } 88 | }; 89 | 90 | // pass in data from the filled up form 91 | // data in format { name: ‘frontend library’, fields: [‘data flow’, etc.] } 92 | // category doesn't exist for the user 93 | // no duplicates in fields 94 | 95 | // async/ await?? 96 | // const saveCategory = (data) => { 97 | // console.log("DATA TO SAVE ", data); 98 | // const response = fetch("/api/categories", { 99 | // method: "POST", 100 | // body: JSON.stringify(data), 101 | // }); 102 | // // console.log("RESPONSE ", response); 103 | // // return response.json(); 104 | // }; 105 | 106 | // const handleGoBackMainClick = () => { 107 | // // 108 | // } 109 | 110 | // useEffect(() => { 111 | // handleFieldChange(); 112 | // }); 113 | 114 | console.log("CATEGORY ", inputCategoryRef); 115 | return ( 116 |
    117 |
    118 |

    Fill up information for the category:

    119 |
    120 |
    121 |
    122 | 127 |

    Fields you would want to compare:

    128 |
    129 | {fields.map((f, i) => { 130 | return ( 131 | 137 | ); 138 | })} 139 |
    140 | 141 |
    142 | 143 |
    144 |
    145 | 146 | Back to Categories 147 | 148 |
    149 | ); 150 | } 151 | 152 | // {fields.map((f) => { 153 | // 154 | // })} 155 | // {fields.forEach((f) => { 156 | // 157 | // })} 158 | // 159 | // onClick={handleSaveCategory} 160 | 161 | // onClick={handleGoBackMainClick} 162 | // {fields.map((el) => ( 163 | // 164 | // ))} 165 | 166 | // instead of form submission have onClick for button with Link? 167 | // Save category 168 | 169 | // ref={inputFieldsRef} 170 | // component for field? 171 | function Field(props) { 172 | return ( 173 |
    174 | 181 |
    182 | ); 183 | } 184 | 185 | // 186 | // ref={inputFieldRef} 187 | 188 | // const history = useHistory(); 189 | // const goMain = () => history.push('categories'); 190 | 191 | //