├── 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 |
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 |
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 |
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 | //
192 | // Save category
193 | // Save category
194 |
--------------------------------------------------------------------------------
/src/client/components/CreateTechnology2.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 NewTechnology(props) {
7 |
8 | /*
9 | {
10 | "name": React,
11 | "fields": {
12 | "FrontendLibrary:" {"pros": "....", "cons": "...", "dataflow": "upwards"}
13 | "Server:" {"pros": "....", "cons": "...", "dataflow": "upwards"}
14 | }
15 | }
16 |
17 | {
18 |
19 | }
20 | */
21 | //GET Req
22 |
23 | //Save - Post Req.
24 | const [globalState, setState] = useState(
25 | {
26 | "name": "", //Need to fill this in based on textarea Inputs.
27 | "fields": {}
28 | }
29 | );
30 |
31 | const [technology, settechnology] = useState(""); //"React"
32 | const [categories, setCategories] = useState(""); //"Frontend"
33 | const [pros, setPros] = useState(""); //"Awesomeness, data flows awesome"
34 | const [cons, setCons] = useState(""); //"No Cons"
35 | //OnClick, merge all states into globalState (using setState)
36 | //Post req. to DB
37 | //If came from view 2, go back to view 2 (With Updated name; Maybe use useHistory?)
38 | //If came from view 4, go back to view 4 (Updated view 5; useHistory)
39 |
40 |
41 | //Need to handle categories before handling pros, cons, and other fields.
42 | const handleCategories = (e) => {
43 | const { value } = e.target;
44 | setCategories(currentState => (
45 | {...currentState, value}
46 | )
47 | )
48 | }
49 |
50 | // console.log("categories: ", categories);
51 |
52 | const handletechnology = (e) => {
53 | const { value } = e.target;
54 | console.log("~~~vals :", value);
55 | settechnology(currentState => (
56 | {...currentState, value}
57 | )
58 | )
59 | }
60 |
61 |
62 | /*
63 | {
64 | "name": React,
65 | "fields": {
66 | "FrontendLibrary:" {"pros": "....", "cons": "...", "dataflow": "upwards"}
67 | "Server:" {"pros": "....", "cons": "...", "dataflow": "upwards"}
68 | }
69 | }
70 | */
71 |
72 | const mergeAllStates = (e) => {
73 | let technologyValue = technology.value;
74 | console.log("technology value: ", technologyValue);
75 | let addtechnology = {
76 | ...setState.name,
77 | technologyValue
78 | }
79 | console.log("~~~addtech: ", addtechnology);
80 | setState(addtechnology);
81 |
82 | console.log("global state: ", globalState);
83 |
84 | // let updatedState = {
85 | // ...setState,
86 | // }
87 | // setState(currentState => (
88 | // {
89 | // ...currentState,
90 | // currentState["name"] = technology,
91 | // currentState["fields"][categories] = {},
92 |
93 | // }
94 | // )
95 | // )
96 | }
97 |
98 | console.log("Tech Obj: ", technology);
99 |
100 | const handlePros = (e) => {
101 | // const { value } = e.target;
102 | // settechnologyObj(currentState => {
103 | // ...currentState,
104 | // currentState[fields]
105 | // })
106 | }
107 |
108 | const handleCons = (e) => {
109 | const { value } = e.target;
110 | // prosChange(value);
111 | // console.log("name: ", name);
112 | console.log("value: ", value);
113 | // prosChange(value); //pros state will now update to value.
114 | }
115 |
116 | const handleChange = (e) => {
117 | const { value } = e.target;
118 | // prosChange(value);
119 | // console.log("name: ", name);
120 | console.log("value: ", value);
121 | // prosChange(value); //pros state will now update to value.
122 | }
123 |
124 | const handleSubmit = (event) => {
125 | event.preventDefault();
126 | }
127 |
128 | return (
129 |
130 |
New Technology Fill Up!!!
131 |
132 |
142 |
143 |
153 | {/*
154 |
164 |
165 |
*/}
175 |
176 |
183 |
184 |
187 |
188 | {/*Just to test how the item data would look.*/}
189 | {/*
{JSON.stringify(inputList)}
*/}
190 |
191 |
192 | );
193 |
194 | // const [inputList, setInputList] = useState([{ firstName: "", lastName: "" }]);
195 |
196 | // // handle input change
197 | // const handleInputChange = (e, index) => {
198 | // console.log("E target: ", e.target);
199 | // const { name, value } = e.target;
200 | // console.log("name: ", name );
201 | // console.log("value: ", value );
202 | // const list = [...inputList];
203 | // list[index][name] = value;
204 | // setInputList(list);
205 | // };
206 |
207 | // // handle click event of the Remove button
208 | // const handleRemoveClick = index => {
209 | // const list = [...inputList];
210 | // list.splice(index, 1);
211 | // setInputList(list);
212 | // };
213 |
214 | // // handle click event of the Add button
215 | // const handleAddClick = () => {
216 | // setInputList([...inputList, { firstName: "", lastName: "" }]);
217 | // };
218 |
219 | // return (
220 | //
221 | //
TEST!
222 | // {inputList.map((x, i) => {
223 | // console.log("X: ", x);
224 | // return (
225 | //
246 | // );
247 | // })}
248 | //
{JSON.stringify(inputList)}
249 | //
250 | // );
251 | }
252 |
253 |
254 |
--------------------------------------------------------------------------------