├── .DS_Store ├── client ├── scss │ ├── _nav.scss │ ├── application.scss │ ├── _board.scss │ ├── _reset.scss │ └── _form.scss ├── .DS_Store ├── assets │ └── icon.png ├── index.html ├── index.js ├── components │ ├── LoginREADME.md │ ├── Board.jsx │ ├── Card.jsx │ ├── Signup.jsx │ ├── Task.jsx │ ├── Canvas.jsx │ ├── Login.jsx │ ├── NavBar.jsx │ └── App.jsx ├── reducers │ ├── loginSlice.js │ ├── deckSlice.js │ ├── README.md │ └── boardSlice.js └── store │ └── store.js ├── .gitignore ├── README.md ├── server ├── routers │ ├── stack-router.js │ └── user-router.js ├── models │ └── models.js ├── controllers │ ├── board-controller.js │ ├── stack-controller.js │ ├── user-controller.js │ └── github-controller.js └── server.js ├── webpack.config.js ├── package.json └── database └── postgres_create.sql /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cat-Snek/scrumadillo/HEAD/.DS_Store -------------------------------------------------------------------------------- /client/scss/_nav.scss: -------------------------------------------------------------------------------- 1 | .logo { 2 | width: 165px; 3 | height: 80px; 4 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /dist 2 | 3 | node_modules 4 | .env 5 | package-lock.json 6 | /dist 7 | -------------------------------------------------------------------------------- /client/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cat-Snek/scrumadillo/HEAD/client/.DS_Store -------------------------------------------------------------------------------- /client/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cat-Snek/scrumadillo/HEAD/client/assets/icon.png -------------------------------------------------------------------------------- /client/scss/application.scss: -------------------------------------------------------------------------------- 1 | @import 'reset'; 2 | @import 'nav'; 3 | @import 'form'; 4 | @import 'board'; -------------------------------------------------------------------------------- /client/scss/_board.scss: -------------------------------------------------------------------------------- 1 | .task{ 2 | background-color:#e8eaf6; 3 | border-radius: 5px; 4 | } 5 | 6 | body{ 7 | background-color: #e8eaf6 ; 8 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SCRUMADILLO 2 | Goldilocks SCRUM board for sprouting developers 3 | 4 | Bridging the gap between create-react-app and staring at a blank VSC explorer 5 | -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SCRUMAdillo 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /client/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './components/App'; 4 | import store from './store/store'; 5 | import { Provider } from 'react-redux'; 6 | import styles from './scss/application.scss'; 7 | 8 | ReactDOM.render( 9 | 10 | 11 | , 12 | document.getElementById('root') 13 | ); 14 | -------------------------------------------------------------------------------- /client/components/LoginREADME.md: -------------------------------------------------------------------------------- 1 | ### Login/Signup Information 2 | 3 | - We tried to incorporate a signup button with signup functionality 4 | - We pushed the functionality to branch 'new' because it somehow affected the persistent state of the user data 5 | - Github functionality returns a user object. We were thinking of user cookies to be able to authenticate user and redirect to the Canvas component 6 | -------------------------------------------------------------------------------- /server/routers/stack-router.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const stackController = require('../controllers/stack-controller.js'); 3 | 4 | const stackRouter = express.Router(); 5 | 6 | //populates a 'stack' with stored technology cards 7 | 8 | stackRouter.get('/', stackController.getStack, (req, res) => { 9 | return res.status(200).json(res.locals.output); 10 | }); 11 | 12 | module.exports = stackRouter; 13 | -------------------------------------------------------------------------------- /server/models/models.js: -------------------------------------------------------------------------------- 1 | const { Pool } = require('pg'); 2 | require('dotenv').config(); 3 | 4 | const pool = new Pool({ 5 | connectionString: process.env.PG_URI, 6 | }); 7 | 8 | // // You must export your model through module.exports 9 | // // The collection name should be 'student' 10 | module.exports = { 11 | query: (text, params, callback) => { 12 | console.log('executed query', text); 13 | return pool.query(text, params, callback); 14 | }, 15 | }; -------------------------------------------------------------------------------- /server/routers/user-router.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const userController = require('../controllers/user-controller.js'); 3 | 4 | const userRouter = express.Router(); 5 | 6 | // create user 7 | userRouter.post('/signup', userController.createUser, (req, res) => { 8 | return res.status(200).json(res.locals); 9 | }); 10 | 11 | userRouter.post('/login', userController.verifyUser, (req, res) => { 12 | console.log('res locals username and id'); 13 | return res.status(200).json(res.locals); 14 | }); 15 | module.exports = userRouter; 16 | -------------------------------------------------------------------------------- /client/reducers/loginSlice.js: -------------------------------------------------------------------------------- 1 | import { createSlice } from '@reduxjs/toolkit'; 2 | 3 | export const loginSlice = createSlice({ 4 | name: 'Login', 5 | initialState: { 6 | username: 'SCRUMadillo', 7 | userId: '', 8 | }, 9 | 10 | reducers: { 11 | getLogin: (state, action) => { 12 | state.username = action.payload.username; 13 | state.userId = action.payload.userId; 14 | }, 15 | }, 16 | }); 17 | 18 | export const { getLogin } = loginSlice.actions; 19 | export const selectLogin = (state) => state; 20 | export default loginSlice.reducer; 21 | -------------------------------------------------------------------------------- /client/reducers/deckSlice.js: -------------------------------------------------------------------------------- 1 | import { createSlice } from '@reduxjs/toolkit'; 2 | import { getAll } from '../reducers/boardslice'; 3 | import { useDispatch, useSelector } from 'react-redux'; 4 | 5 | export const deckSlice = createSlice({ 6 | name: 'stack', 7 | initialState: { 8 | stacks: [], 9 | targetStack: [], 10 | }, 11 | 12 | reducers: { 13 | getStack: (state, action) => { 14 | // action payload is data returned from fetch request on mounting 15 | state.stacks = action.payload; 16 | }, 17 | selectStack: (state, action) => { 18 | const { deckState, targetStack } = action.payload; 19 | const cards = deckState.deck.stacks[targetStack]; 20 | state.targetStack = cards; 21 | console.log('in selectstack reducer', cards); 22 | }, 23 | }, 24 | }); 25 | 26 | export const { getStack, selectStack } = deckSlice.actions; 27 | export const selectDeck = (state) => state; 28 | export default deckSlice.reducer; 29 | -------------------------------------------------------------------------------- /server/controllers/board-controller.js: -------------------------------------------------------------------------------- 1 | const db = require('../models/models.js'); 2 | const boardController = {}; 3 | 4 | //saves current board state and assigns the logged in user to username 5 | boardController.saveBoard = (req, res, next) => { 6 | let boardData = req.body.card; 7 | console.log('boardData ', boardData); 8 | 9 | Board.update( 10 | { username: boardData.username }, 11 | { 12 | ...boardData, 13 | }, 14 | { upsert: true }, 15 | (err, board) => { 16 | if (err) return next(err); 17 | 18 | res.locals.board = board; 19 | return next(); 20 | } 21 | ); 22 | }; 23 | 24 | //retrieves board state data by username 25 | boardController.getBoard = (req, res, next) => { 26 | let username = req.params.username; 27 | Board.findOne( 28 | { 29 | username: username, 30 | }, 31 | (err, board) => { 32 | if (err) return next(err); 33 | res.locals.board = board; 34 | return next(); 35 | } 36 | ); 37 | }; 38 | 39 | module.exports = boardController; 40 | -------------------------------------------------------------------------------- /client/reducers/README.md: -------------------------------------------------------------------------------- 1 | Reducers folder is commonly called "features" when the component is in the same folder as the Slice Method. 2 | When researching Redux Toolkit you will often see this folder as "features". 3 | 4 | cardSlice ended up representing the whole board state but we didn't have time to rename everything so naming is sometimes confusing. card refers to board state and card.cards refers to cards a user has on the board 5 | 6 | deckSlice refers to the cards or stack of cards for a user to choose from. The first iteration only implemented one stack choice accessed from the get cards button on the navbar. Stretch goal is to have deck manage the state of all possible cards to choose from. Cards from the deck would then be copied into a users Cardslice to be worked through. 7 | 8 | ******** 9 | { useSelector, useDispatch } from 'react-redux' can only be used to connect state to functional components not to Classes. If you'd like to connect a class to the redux store you have to use the Vanilla Redux methods of connect and mapStateToProps 10 | ******** 11 | 12 | More on the createSlice method: 13 | 14 | https://redux-toolkit.js.org/api/createSlice 15 | -------------------------------------------------------------------------------- /client/scss/_reset.scss: -------------------------------------------------------------------------------- 1 | html, body, div, span, applet, object, iframe, 2 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 3 | a, abbr, acronym, address, big, cite, code, 4 | del, dfn, em, img, ins, kbd, q, s, samp, 5 | small, strike, strong, sub, sup, tt, var, 6 | b, u, i, center, 7 | dl, dt, dd, ol, ul, li, 8 | fieldset, form, label, legend, 9 | table, caption, tbody, tfoot, thead, tr, th, td, 10 | article, aside, canvas, details, embed, 11 | figure, figcaption, footer, header, hgroup, 12 | menu, nav, output, ruby, section, summary, 13 | time, mark, audio, video { 14 | margin: 0; 15 | padding: 0; 16 | border: 0; 17 | font-size: 100%; 18 | font: inherit; 19 | vertical-align: baseline; 20 | } 21 | /* HTML5 display-role reset for older browsers */ 22 | article, aside, details, figcaption, figure, 23 | footer, header, hgroup, menu, nav, section { 24 | display: block; 25 | } 26 | body { 27 | line-height: 1; 28 | } 29 | ol, ul { 30 | list-style: none; 31 | } 32 | blockquote, q { 33 | quotes: none; 34 | } 35 | blockquote:before, blockquote:after, 36 | q:before, q:after { 37 | content: ''; 38 | content: none; 39 | } 40 | table { 41 | border-collapse: collapse; 42 | border-spacing: 0; 43 | } -------------------------------------------------------------------------------- /client/scss/_form.scss: -------------------------------------------------------------------------------- 1 | .form-container { 2 | height: 100vh; 3 | width: 100vw; 4 | text-align: center; 5 | display: flex; 6 | justify-content: center; 7 | align-items: center; 8 | 9 | // to refactor css to use EM / REM 10 | .login-form { 11 | border: 1px solid black; 12 | display: inline-block; 13 | text-align: center; 14 | max-width: 650px; 15 | min-height: 400px; 16 | padding: 25px; 17 | padding-bottom: 5px; 18 | border-radius: 4px; 19 | border: 1px solid #d9dce0; 20 | background-color: #f9f9f9; 21 | } 22 | .signup-form { 23 | border: 1px solid black; 24 | display: inline-block; 25 | text-align: center; 26 | max-width: 500px; 27 | min-height: 400px; 28 | padding-bottom: 5px; 29 | border-radius: 4px; 30 | border: 1px solid #d9dce0; 31 | background-color: #f9f9f9; 32 | } 33 | 34 | .form-input { 35 | margin-bottom: 10px; 36 | width: 366px; 37 | height: 54px; 38 | border-radius: 4px; 39 | border: 1px solid #d9dce0; 40 | padding-left: 15px; 41 | } 42 | 43 | .icon { 44 | width: 300px; 45 | margin-bottom: 15px; 46 | } 47 | 48 | .form-submit-button { 49 | width: 120px; 50 | height: 40px; 51 | margin-top: 10px; 52 | margin-bottom: 25px; 53 | border: 1px solid #f9f9f9; 54 | background-color: #9cdaf0; 55 | font-family: sans-serif; 56 | font-weight: bold; 57 | border-radius: 4px; 58 | } 59 | 60 | .github-button { 61 | width: 250px; 62 | } 63 | 64 | .error-msg { 65 | color: red; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /client/components/Board.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useSelector, useDispatch } from 'react-redux'; 3 | import Grid from '@material-ui/core/Grid'; 4 | import Card from './Card.jsx'; 5 | import { selectBoard, increment, decrement } from '../reducers/boardSlice'; 6 | import Button from '@material-ui/core/Button'; 7 | 8 | const Board = ({ id }) => { 9 | const dispatch = useDispatch(); 10 | const { boardState } = useSelector(selectBoard); 11 | const { current } = boardState; 12 | const cardsArr = []; 13 | 14 | if (id === 'stack') 15 | for (let i = current + 1; i < boardState.cards.length; i += 1) { 16 | cardsArr.push(,
); 17 | } 18 | 19 | if (id === 'inProgress' && boardState.cards[current]) { 20 | cardsArr.push( 21 |
22 | 29 | 30 |
31 | ); 32 | } 33 | 34 | if (id === 'complete') { 35 | for (let i = 0; i < current; i++) { 36 | cardsArr.push(,
); 37 | } 38 | cardsArr.push( 39 | 40 | ); 41 | } 42 | return ( 43 |
44 | 45 | {cardsArr} 46 | 47 |
48 | ); 49 | }; 50 | 51 | export default Board; 52 | -------------------------------------------------------------------------------- /server/controllers/stack-controller.js: -------------------------------------------------------------------------------- 1 | const db = require('../models/models.js'); 2 | const stackController = {}; 3 | 4 | //populates a 'deck' with stored technology cards and returns them to client 5 | stackController.getStack = async (req, res, next) => { 6 | try { 7 | const queryText = ` 8 | SELECT techInStack."stackName" AS stackArr, techInStack."techName" AS techArr, 9 | todo.name AS todoName, todo.details AS todoDetails, 10 | todo.completed AS todoStatus, 11 | tech.name AS tech_name, 12 | tech.url, tech.completed AS tech_completed FROM techInStack 13 | INNER JOIN todo 14 | ON todo.tech = techInStack."techName" 15 | INNER JOIN tech 16 | ON todo.tech = tech.name`; 17 | const techInStackQuery = ` 18 | SELECT * 19 | FROM techinstack`; 20 | const techInStackTable = await db.query(techInStackQuery); 21 | const output = {}; 22 | techInStackTable.rows.forEach((row) => { 23 | if (output[row.stackName]) 24 | output[row.stackName].push({ name: row.techName, url: null, todo: [] }); 25 | else output[row.stackName] = [{ name: row.techName, url: null, todo: [] }]; 26 | }); 27 | const todoTable = await db.query(queryText); 28 | todoTable.rows.forEach((todo) => { 29 | const { 30 | stackarr, 31 | techarr, 32 | todoname, 33 | tododetails, 34 | todostatus, 35 | tech_name, 36 | url, 37 | } = todo; 38 | output[stackarr].forEach((tech) => { 39 | if (tech.name === techarr) { 40 | tech.url = url; 41 | tech.todo.push({ 42 | taskName: todoname, 43 | details: tododetails, 44 | completed: todostatus, 45 | }); 46 | } 47 | }); 48 | }); 49 | res.locals.output = output 50 | return next() 51 | } 52 | catch(err) {return next(err)} 53 | }; 54 | 55 | module.exports = stackController; 56 | -------------------------------------------------------------------------------- /server/controllers/user-controller.js: -------------------------------------------------------------------------------- 1 | const db = require('../models/models.js'); 2 | const userController = {}; 3 | const bcrypt = require('bcrypt'); 4 | 5 | // create user 6 | 7 | userController.createUser = async (req, res, next) => { 8 | const { username, password } = req.body; 9 | 10 | const SALT_WORK_FACTOR = 10; 11 | try { 12 | const salt = await bcrypt.genSalt(SALT_WORK_FACTOR); 13 | const hashedPass = await bcrypt.hash(password, salt); 14 | 15 | res.locals.username = username; 16 | res.locals.password = hashedPass; 17 | 18 | const createUserQuery = ` 19 | INSERT INTO public.user (name, password) 20 | VALUES ('${username}','${hashedPass}')`; 21 | 22 | await db.query(createUserQuery); 23 | return next(); 24 | } catch (err) { 25 | res.send({ err: 'User was not able to be created' }); 26 | return next({ error: err }); 27 | } 28 | }; 29 | 30 | // verify user 31 | 32 | userController.verifyUser = async (req, res, next) => { 33 | const { username, password } = req.body; 34 | const checkUserNameExist = `SELECT * FROM public.user WHERE name = '${username}'`; 35 | const user = await db.query(checkUserNameExist); 36 | const userRow = user.rows[0]; 37 | 38 | if (!user.rows) { 39 | return res.status(404).json({ name: 'This user does not exist' }); 40 | } 41 | 42 | const isMatch = await bcrypt.compare(password, userRow.password); 43 | 44 | if (isMatch) { 45 | console.log("we're in!"); 46 | res.locals.username = userRow.name; 47 | res.locals.id = userRow._id; 48 | console.log(res.locals); 49 | return next(); 50 | } else { 51 | res.send({ err: 'Validation failed' }); 52 | return next({ 53 | error: 'Username and Password combination was not found.', 54 | status: 401, 55 | }); 56 | } 57 | }; 58 | 59 | module.exports = userController; 60 | -------------------------------------------------------------------------------- /client/store/store.js: -------------------------------------------------------------------------------- 1 | import { 2 | configureStore, 3 | getDefaultMiddleware, 4 | combineReducers, 5 | } from '@reduxjs/toolkit'; 6 | import boardReducer from '../reducers/boardSlice'; 7 | import deckReducer from '../reducers/deckSlice'; 8 | import loginReducer from '../reducers/loginSlice'; 9 | 10 | //Middleware for logging state with every state change 11 | 12 | const logger = (store) => (next) => (action) => { 13 | console.log('dispatching', action); 14 | let result = next(action); 15 | console.log('next state', store.getState().card); 16 | return result; 17 | }; 18 | 19 | //Middleware that sends the entire state of the app up to Mongo DB with every state change. 20 | //Currently this just overwrites a users board state but the goal was to create an array of board states 21 | // so the user could time travel even after logging out or closing the page 22 | 23 | const updateBoardServer = (store) => (next) => (action) => { 24 | let payload = store.getState(); 25 | console.log('payload.card -> ', payload); 26 | fetch('/server/boardState', { 27 | method: 'POST', 28 | headers: { 29 | 'Content-Type': 'application/json', 30 | }, 31 | body: JSON.stringify(payload), 32 | }) 33 | .then((resp) => resp.json()) 34 | .then((data) => { 35 | console.log('inside updateBoardServer', data); 36 | }); 37 | let result = next(action); 38 | return result; 39 | }; 40 | 41 | //use the name of the reducer to access that piece state in the global environment 42 | //global state is an object containing 3 reducers. store = {card{}, deck{}, login{}} 43 | 44 | const store = configureStore({ 45 | reducer: combineReducers({ 46 | boardState: boardReducer, 47 | deck: deckReducer, 48 | login: loginReducer, 49 | }), 50 | middleware: getDefaultMiddleware().concat(logger).concat(updateBoardServer), 51 | }); 52 | 53 | export default store; 54 | -------------------------------------------------------------------------------- /client/components/Card.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Task from './Task'; 4 | import { makeStyles } from '@material-ui/core/styles'; 5 | import Paper from '@material-ui/core/Paper'; 6 | import Typography from '@material-ui/core/Typography'; 7 | import { useDispatch } from 'react-redux'; 8 | import { skipToCard } from '../reducers/boardSlice'; 9 | 10 | const useStyles = makeStyles((theme) => ({ 11 | paper: { 12 | padding: theme.spacing(2), 13 | textAlign: 'center', 14 | color: 'theme.palette.text.secondary', 15 | }, 16 | heading: { 17 | fontSize: theme.typography.pxToRem(15), 18 | color: '#363738', 19 | fontWeight: theme.typography.fontWeightBold, 20 | }, 21 | })); 22 | 23 | const Card = ({ card = null, name, url }) => { 24 | //props only has card if we're in the "in progress board" 25 | const dispatch = useDispatch(); 26 | console.log('in card - card', card); 27 | 28 | const { paper, heading } = useStyles(); 29 | 30 | const todoArray = []; 31 | if (card) { 32 | const { todo } = card; 33 | for (let i = 0; i < todo.length; i += 1) { 34 | todoArray.push( 35 |
36 | 42 |
43 | ); 44 | } 45 | } 46 | 47 | const handleTitleClick = (e) => { 48 | dispatch(skipToCard({ targetTech: e.target.innerText })); 49 | }; 50 | 51 | return ( 52 |
53 | 54 | 55 | {name} 56 | 57 |
58 | 59 | {url} 60 | 61 | {todoArray} 62 |
63 |
64 | ); 65 | }; 66 | 67 | export default Card; 68 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 3 | const path = require('path'); 4 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 5 | const { CleanWebpackPlugin } = require('clean-webpack-plugin'); 6 | 7 | module.exports = { 8 | entry: ['./client/index.js'], 9 | output: { 10 | path: path.resolve(__dirname, 'dist'), 11 | publicPath: '/dist/', 12 | filename: 'bundle.js', 13 | }, 14 | mode: process.env.NODE_ENV, 15 | devServer: { 16 | // match the output path 17 | contentBase: path.resolve(__dirname, 'client'), 18 | // enable HMR on the devServer 19 | hot: true, 20 | watchContentBase: true, 21 | // match the output 'publicPath' 22 | publicPath: '/dist/', 23 | // fallback to root for other urls 24 | historyApiFallback: true, 25 | inline: true, 26 | headers: { 'Access-Control-Allow-Origin': '*' }, 27 | proxy: { 28 | '/server': { 29 | target: 'http://localhost:3000/', 30 | secure: false, 31 | }, 32 | }, 33 | }, 34 | module: { 35 | rules: [ 36 | { 37 | test: /\.(png|jpe?g|gif)$/i, 38 | use: [ 39 | { 40 | loader: 'file-loader', 41 | }, 42 | ], 43 | }, 44 | { 45 | test: /.(js|jsx)$/, 46 | exclude: /node_modules/, 47 | loader: 'babel-loader', 48 | options: { 49 | presets: ['@babel/preset-env', '@babel/preset-react'], 50 | }, 51 | }, 52 | { 53 | test: /.(css|scss)$/, 54 | exclude: /node_modules/, 55 | use: [MiniCssExtractPlugin.loader, 'css-loader', "sass-loader"], 56 | }, 57 | ], 58 | }, 59 | plugins: [ 60 | new MiniCssExtractPlugin(), 61 | new CleanWebpackPlugin(), 62 | new HtmlWebpackPlugin({ 63 | template: './client/index.html', 64 | }), 65 | ], 66 | resolve: { 67 | // Enable importing JS / JSX files without specifying their extension 68 | extensions: ['.js', '.jsx'], 69 | }, 70 | }; 71 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scrumadillo", 3 | "version": "1.0.0", 4 | "description": "Life changing app development boilerplate", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "cross-env NODE_ENV=production node server/server.js", 8 | "build": "cross-env NODE_ENV=production webpack", 9 | "dev": "cross-env NODE_ENV=development concurrently \" webpack-dev-server --open --hot \" \"nodemon ./server/server.js\"", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/Cat-Snek/scrumadillo.git" 15 | }, 16 | "author": "", 17 | "license": "ISC", 18 | "bugs": { 19 | "url": "https://github.com/Cat-Snek/scrumadillo/issues" 20 | }, 21 | "homepage": "https://github.com/Cat-Snek/scrumadillo#readme", 22 | "devDependencies": { 23 | "@babel/core": "^7.10.5", 24 | "@babel/preset-env": "^7.10.4", 25 | "@babel/preset-react": "^7.10.4", 26 | "babel-loader": "^8.1.0", 27 | "clean-webpack-plugin": "^3.0.0", 28 | "concurrently": "^5.2.0", 29 | "cross-env": "^7.0.2", 30 | "css-loader": "^3.6.0", 31 | "file-loader": "^6.0.0", 32 | "html-webpack-plugin": "^4.3.0", 33 | "mini-css-extract-plugin": "^0.9.0", 34 | "nodemon": "^2.0.4", 35 | "react-scripts": "^3.4.1", 36 | "sass-loader": "^9.0.2", 37 | "style-loader": "^1.2.1", 38 | "webpack": "^4.44.0", 39 | "webpack-cli": "^3.3.12", 40 | "webpack-dev-server": "^3.11.0" 41 | }, 42 | "dependencies": { 43 | "@material-ui/core": "^4.11.0", 44 | "@material-ui/icons": "^4.9.1", 45 | "@reduxjs/toolkit": "^1.4.0", 46 | "axios": "^0.19.2", 47 | "bcrypt": "^5.0.0", 48 | "dotenv": "^8.2.0", 49 | "express": "^4.17.1", 50 | "fontsource-roboto": "^2.2.6", 51 | "mongoose": "^5.9.25", 52 | "node-sass": "^4.14.1", 53 | "pg": "^8.3.0", 54 | "react": "^16.13.1", 55 | "react-dom": "^16.13.1", 56 | "react-hot-loader": "^4.12.21", 57 | "react-redux": "^7.2.0", 58 | "react-router": "^5.2.0", 59 | "react-router-dom": "^5.2.0", 60 | "superagent": "^5.3.1" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /server/controllers/github-controller.js: -------------------------------------------------------------------------------- 1 | const { User } = require('../models/models.js'); 2 | const request = require('superagent'); 3 | require('dotenv').config(); 4 | 5 | const githubController = {}; 6 | 7 | githubController.authorize = (req, res, next) => { 8 | const { query } = req; 9 | const { code } = query; 10 | // post request with code 11 | request 12 | .post('https://github.com/login/oauth/access_token') 13 | .send({ 14 | client_id: process.env.CLIENT_ID, 15 | client_secret: process.env.CLIENT_SECRET, 16 | code: code, 17 | }) 18 | .set('Accept', 'application/json') 19 | .then((result) => { 20 | const { body } = result; 21 | const { access_token } = body; 22 | res.locals.token = access_token; 23 | return next(); 24 | }) 25 | .catch((err) => next(err)); 26 | }; 27 | 28 | githubController.token = (req, res, next) => { 29 | const accessToken = res.locals.token; 30 | request 31 | .get('https://api.github.com/user') 32 | .set('Authorization', 'token ' + accessToken) 33 | // set user agent username 34 | .set('User-Agent', 'test') 35 | .then((result) => { 36 | // redirect to root with user info from body 37 | // const userInfo = result.body; 38 | const username = result.body.login; 39 | const password = result.body.id; 40 | User.findOne({ username: username, password: password }, (error, user) => { 41 | if (error) { 42 | return next(error) 43 | } else { 44 | if (user === null) { 45 | User.create({ username: username, password: password }, (err, us) => { 46 | if (err) { 47 | return next(err) 48 | } else { 49 | res.locals.authorized = us; 50 | console.log('usercreated') 51 | return next(); 52 | } 53 | }) 54 | } else { 55 | res.locals.authorized = user 56 | console.log('usercreated2') 57 | return next(); 58 | } 59 | } 60 | }) 61 | }) 62 | .catch((err) => { 63 | return next(err); 64 | }); 65 | }; 66 | module.exports = githubController; 67 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | const path = require('path'); 4 | // const request = require('superagent'); 5 | 6 | const githubController = require('./controllers/github-controller.js'); 7 | const boardController = require('./controllers/board-controller.js'); 8 | 9 | const stackRouter = require('./routers/stack-router.js'); 10 | const userRouter = require('./routers/user-router.js'); 11 | // const boardRouter = require('./routers/board-router.js');\ 12 | 13 | const main = express.Router(); 14 | 15 | const url = require('url'); 16 | 17 | app.use(express.json()); 18 | app.use(express.urlencoded({ extended: true })); 19 | 20 | //main route for proxy calls to localhost 3000. 21 | app.use('/server', main); 22 | 23 | //OAuth 24 | main.get('/github', githubController.authorize, githubController.token, (req, res) => { 25 | return res.status(200).json(res.locals.authorized); 26 | }); 27 | 28 | // //login and signup routes 29 | // main.post('/login', userController.verifyUser, (req, res) => { 30 | // return res.status(200).json(res.locals.user); 31 | // }); 32 | 33 | // main.post('/signup', userController.createUser, (req, res) => { 34 | // return res.status(200).json(res.locals.user); 35 | // }); 36 | 37 | //Route for storing and retrieving board state 38 | main.get('/boardState/:username', boardController.getBoard, (req, res) => { 39 | return res.status(200).json(res.locals.board); 40 | }); 41 | main.post('/boardState', boardController.saveBoard, (req, res) => { 42 | return res.status(200).json(res.locals.board); 43 | }); 44 | 45 | /** Routes for handling requests **/ 46 | main.use('/stack', stackRouter); 47 | main.use('/user', userRouter); 48 | 49 | /** SERVE STATIC ASSETS IN PRODUCTION MODE ONLY **/ 50 | if (process.env.NODE_ENV === 'production') { 51 | app.use('/dist', express.static(path.join(__dirname, '../dist'))); 52 | app.get('/', express.static('client')); 53 | } 54 | 55 | /* --Error Handling-- */ 56 | /** CATCH-ALL ROUTE HANDLER **/ 57 | app.use('*', (req, res) => { 58 | return res.status(404).json('Error: page not found'); 59 | }); 60 | /** GLOBAL ERROR HANDLER **/ 61 | app.use((err, req, res, next) => { 62 | if (err) return res.status(err.status).json(err); 63 | return res.status(500).json('Server error'); 64 | }); 65 | 66 | app.listen(3000, () => console.log(process.env.NODE_ENV)); 67 | -------------------------------------------------------------------------------- /client/components/Signup.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Redirect } from 'react-router-dom'; 3 | import logo from '../assets/icon.png'; 4 | 5 | // familiarize with how add card works without redirect to diff endpoint 6 | // app, router, axios 7 | 8 | const Signup = ({ registerUser, loggedIn, update, errorMsg }) => { 9 | // const = this.props; 10 | if (loggedIn) return ; 11 | return ( 12 |
13 |
14 | SCRUMadillo 15 |

16 | 24 |

25 | 33 | 41 |

42 | 43 |
{errorMsg}
44 |
45 | 46 | 51 | 52 | {/* Didn't get to Google OAuth 53 | */} 60 |
61 |
62 |
63 | ); 64 | }; 65 | 66 | export default Signup; 67 | -------------------------------------------------------------------------------- /client/components/Task.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { makeStyles } from '@material-ui/core/styles'; 3 | import List from '@material-ui/core/List'; 4 | import ListItem from '@material-ui/core/ListItem'; 5 | import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction'; 6 | import Checkbox from '@material-ui/core/Checkbox'; 7 | import { createMuiTheme } from '@material-ui/core/styles'; 8 | import { useDispatch } from 'react-redux'; 9 | import { completeTask } from '../reducers/boardSlice'; 10 | const themes = createMuiTheme({ 11 | palette: { 12 | primary: { 13 | main: '#fafafa', 14 | contrastText: '#fff', 15 | }, 16 | }, 17 | }); 18 | const useStyles = makeStyles((theme) => ({ 19 | detail: { 20 | backgroundColor: themes.palette.primary.main, 21 | paddingLeft: theme.spacing(4), 22 | }, 23 | })); 24 | 25 | const Task = (props) => { 26 | let { name, detail, complete, id } = props; 27 | const classes = useStyles(); 28 | const dispatch = useDispatch(); 29 | 30 | return ( 31 | 32 | 33 | 34 | {complete ? ( 35 | { 40 | dispatch(completeTask({ todoName: name })); 41 | }} 42 | /> 43 | ) : ( 44 | { 49 | dispatch(completeTask({ todoName: name })); 50 | }} 51 | /> 52 | )} 53 | 54 | {props.name} 55 | 56 | 57 | 65 | {props.detail} 66 | 67 | 68 | {props.complete} 69 | 70 | 71 | ); 72 | }; 73 | 74 | export default Task; 75 | -------------------------------------------------------------------------------- /client/components/Canvas.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Board from './Board'; 3 | import NavBar from './NavBar'; 4 | import { makeStyles } from '@material-ui/core/styles'; 5 | import Paper from '@material-ui/core/Paper'; 6 | import Grid from '@material-ui/core/Grid'; 7 | import Typography from '@material-ui/core/Typography'; 8 | import { useDispatch } from 'react-redux'; 9 | import { getLogin } from '../reducers/loginSlice'; 10 | import { assignUser, newState } from '../reducers/boardSlice'; 11 | import { useEffect } from 'react'; 12 | import { Redirect } from 'react-router-dom'; 13 | 14 | const useStyles = makeStyles((theme) => ({ 15 | paper: { 16 | padding: theme.spacing(2), 17 | textAlign: 'left', 18 | color: theme.palette.text.secondary, 19 | backgroundColor: '#f9f9f9', 20 | }, 21 | heading: { 22 | fontSize: theme.typography.pxToRem(15), 23 | color: '#363738', 24 | fontWeight: theme.typography.fontWeightBold, 25 | }, 26 | })); 27 | 28 | const Canvas = ({ logout, loggedIn, username }) => { 29 | if (!loggedIn) return ; 30 | 31 | // useEffect(() => { 32 | // // // fetch request for user table 33 | // // fetch(`/server/boardState/${username}`) 34 | // // .then((response) => response.json()) 35 | // // .then(({ username, current, cards }) => 36 | // // dispatch(newState({ username, current, cards })) 37 | // // ); 38 | // }); 39 | 40 | const { paper, heading } = useStyles(); 41 | 42 | return ( 43 |
44 | 45 | 46 | 47 | 48 | 49 | 50 | To Do: 51 | 52 | 53 | 54 | 55 | 56 | In Progress: 57 | 58 | 59 | 60 | 61 | 62 | Completed: 63 | 64 | 65 | 66 | 67 |
68 | ); 69 | }; 70 | 71 | export default Canvas; 72 | -------------------------------------------------------------------------------- /client/reducers/boardSlice.js: -------------------------------------------------------------------------------- 1 | import { createSlice } from '@reduxjs/toolkit'; 2 | 3 | //the createSlice method encapsulates a piece of the store. 4 | // The state and the methods for changing state are accessing throughout the app with useSelector and useDispatch from 'react-redux' 5 | 6 | export const boardSlice = createSlice({ 7 | name: 'Board', 8 | initialState: { 9 | current: 0, //index of current card 10 | username: '', 11 | cards: [], // an array of card objects filled from the deck 12 | }, 13 | reducers: { 14 | increment: (state) => { 15 | state.current += 1; 16 | }, 17 | 18 | decrement: (state) => { 19 | if (state.current >= 0) state.current -= 1; 20 | }, 21 | 22 | addCard: (state, action) => { 23 | state.cards.push(action.payload); 24 | }, 25 | // complete: (state) => { 26 | // state.completed = true; 27 | // }, 28 | getAll: (state, action) => { 29 | console.log('in getAll', action.payload); 30 | state.cards = action.payload; 31 | }, 32 | assignUser: (state, action) => { 33 | state.username = action.payload.username; 34 | }, 35 | newState: (state, action) => { 36 | state.username = action.payload.username; 37 | state.current = action.payload.current; 38 | state.cards = action.payload.cards; 39 | }, 40 | 41 | skipToCard: (state, action) => { 42 | const { targetTech } = action.payload; 43 | const newCurrent = state.cards.findIndex( 44 | (card) => card.name === targetTech 45 | ); 46 | state.current = newCurrent; 47 | }, 48 | 49 | completeTask: (state, action) => { 50 | //will receive todoName in payload 51 | //look at cards[current] object for todo array 52 | //iterate through todo array and look for object with a name value === todoName from payload 53 | //set completed = !completed 54 | const { todoName } = action.payload; 55 | const currentCard = state.cards[state.current]; 56 | const currentTodos = currentCard.todo; 57 | currentTodos.forEach((todo) => { 58 | if (todo.taskName === todoName) todo.completed = !todo.completed; 59 | }); 60 | }, 61 | }, 62 | }); 63 | 64 | export const { 65 | addCard, 66 | getAll, 67 | complete, 68 | getCards, 69 | assignUser, 70 | newState, 71 | increment, 72 | decrement, 73 | completeTask, 74 | skipToCard, 75 | } = boardSlice.actions; 76 | 77 | export const selectBoard = (state) => state; 78 | 79 | export default boardSlice.reducer; 80 | -------------------------------------------------------------------------------- /client/components/Login.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link, Redirect } from 'react-router-dom'; 3 | import logo from '../assets/icon.png'; 4 | 5 | const Login = ({ toggleLogin, loggedIn, update, errorMsg }) => { 6 | // check username and password with database 7 | if (loggedIn) return ; 8 | 9 | return ( 10 |
11 |
12 | 13 |

14 | 22 |

23 | 31 |

32 | {/* Takes in the inputs and sets state.logged in to true */} 33 | 34 | 35 | 36 | 37 |

38 |
{errorMsg}
39 | {/* Tried to use a functional get request to Github OAuth but realized we should've just used cookies 40 | */} 47 | 48 | 53 | 54 | {/* Didn't get to Google OAuth 55 | */} 61 |
62 |
63 | ); 64 | }; 65 | 66 | export default Login; 67 | -------------------------------------------------------------------------------- /client/components/NavBar.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import AppBar from '@material-ui/core/AppBar'; 3 | import Toolbar from '@material-ui/core/Toolbar'; 4 | import logo from '../assets/icon.png'; 5 | import Button from '@material-ui/core/Button'; 6 | import { makeStyles } from '@material-ui/core/styles'; 7 | import { IconButton } from '@material-ui/core'; 8 | 9 | import FormControl from '@material-ui/core/FormControl'; 10 | import Select from '@material-ui/core/Select'; 11 | import MenuItem from '@material-ui/core/MenuItem'; 12 | import InputLabel from '@material-ui/core/InputLabel'; 13 | 14 | import { useDispatch, useSelector } from 'react-redux'; 15 | import { getStack, selectStack, selectDeck } from '../reducers/deckSlice'; 16 | import { getAll } from '../reducers/boardslice'; 17 | 18 | const useStyles = makeStyles(() => ({ 19 | button: { 20 | marginLeft: 'auto', 21 | }, 22 | // button2: { 23 | // marginRight: 'auto', 24 | // }, 25 | dropMenu: { 26 | marginRight: 'auto', 27 | minWidth: 120, 28 | }, 29 | })); 30 | 31 | const NavBar = ({ logout }) => { 32 | const { button, button2, dropMenu } = useStyles(); 33 | const dispatch = useDispatch(); 34 | const deckState = useSelector(selectDeck); 35 | console.log('deckState', deckState); 36 | 37 | useEffect(() => { 38 | fetch('/server/stack') 39 | .then((resp) => resp.json()) 40 | .then((data) => { 41 | dispatch(getStack(data)); 42 | }) 43 | .catch((err) => console.log(err)); 44 | }, []); 45 | 46 | useEffect(() => { 47 | if (deckState.deck.targetStack) 48 | dispatch(getAll(deckState.deck.targetStack)); 49 | }); 50 | 51 | const handleClick = (e) => { 52 | let val = e.target.value; 53 | dispatch(selectStack({ deckState, targetStack: val })); 54 | }; 55 | 56 | return ( 57 |
58 | 59 | 60 | 61 | 62 | 63 | 64 | Stacks 65 | 69 | 70 | {/* */} 73 | 76 | 77 | 78 |
79 | ); 80 | }; 81 | export default NavBar; 82 | -------------------------------------------------------------------------------- /client/components/App.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'; 3 | //import { useDispatch, useSelector } from 'react-redux'; 4 | // merge resolved Sunday 5:46 PM 5 | // import { NavBar } from './NavBar'; 6 | import Canvas from './Canvas'; 7 | import Signup from './Signup'; 8 | import Login from './Login'; 9 | 10 | class App extends Component { 11 | constructor(props) { 12 | super(props); 13 | this.state = { 14 | username: null, 15 | password: null, 16 | confirm: null, 17 | userId: null, 18 | loggedIn: false, 19 | loginErrorMsg: '', 20 | registerErrorMsg: '', 21 | }; 22 | this.update = this.update.bind(this); 23 | this.toggleLogin = this.toggleLogin.bind(this); 24 | this.toggleLogout = this.toggleLogout.bind(this); 25 | this.registerUser = this.registerUser.bind(this); 26 | // this.github = this.github.bind(this); 27 | } 28 | 29 | update(field) { 30 | return (e) => this.setState({ [field]: e.target.value }); 31 | } 32 | 33 | toggleLogout() { 34 | this.setState({ loggedIn: false }); 35 | } 36 | 37 | toggleLogin(e) { 38 | e.preventDefault(); 39 | const { username, password } = this.state; 40 | fetch('/server/user/login', { 41 | method: 'POST', 42 | headers: { 43 | 'Content-Type': 'Application/JSON', 44 | }, 45 | body: JSON.stringify({ username, password }), 46 | }) 47 | .then((resp) => resp.json()) 48 | // assign user to state 49 | .then(({ username, id, err = null }) => { 50 | if (!err) 51 | this.setState({ 52 | loggedIn: true, 53 | username, 54 | userId: id, 55 | password: null, 56 | }); 57 | else this.setState({ loginErrorMsg: 'Username/Password Invalid' }); 58 | }) 59 | .catch((error) => console.log(error)); 60 | } 61 | 62 | registerUser(e) { 63 | e.preventDefault(); 64 | const { username, password, confirm } = this.state; 65 | if (password === confirm) { 66 | fetch('/server/user/signup', { 67 | method: 'POST', 68 | headers: { 69 | 'Content-Type': 'Application/JSON', 70 | }, 71 | body: JSON.stringify({ username, password }), 72 | }) 73 | .then((resp) => resp.json()) 74 | .then(({ username, id, err = null }) => { 75 | if (!err) { 76 | alert('account created successfully'); 77 | this.setState({ 78 | loggedIn: true, 79 | username, 80 | userId: id, 81 | password: null, 82 | }); 83 | } else 84 | this.setState({ 85 | registerErrorMsg: 86 | 'User was not able to be created - possible duplicate username', 87 | }); 88 | }); 89 | } else this.setState({ registerErrorMsg: 'Passwords do not match' }); 90 | } 91 | 92 | /* Didn't complete the Github authentication process. 93 | * The process starts on the on or 94 | * The end result is a User object returned to the web browser 95 | * We didn't have time, but we were gonna use cookies to show when Github Auth finishes 96 | */ 97 | // github() { 98 | // if (true) { 99 | // console.log('github'); 100 | // axios 101 | // .post( 102 | // 'https://cors-anywhere.herokuapp.com/https://github.com/login/oauth/authorize', 103 | // { 104 | // params: { client_id: 'fade47f049a7b9f4a3dc' }, 105 | // headers: { 'Access-Control-Allow-Origin': '*' }, 106 | // } 107 | // ) 108 | // .then((data) => console.log('dataaa', data)) 109 | // .catch((err) => console.log('errrr', err)); 110 | // } 111 | // } 112 | 113 | // componentDidMount() { 114 | // fetch('/server/cards') 115 | // .then((data) => data.json()) 116 | // .then((data) => dispatch(() => cardSlice(data))); 117 | // } 118 | 119 | render() { 120 | console.log(this.state); 121 | return ( 122 |
123 | 124 | 125 | ( 128 | 134 | )} 135 | /> 136 | ( 140 | 145 | )} 146 | /> 147 | ( 151 | 157 | )} 158 | /> 159 | 160 | 161 |
162 | ); 163 | } 164 | } 165 | 166 | export default App; 167 | -------------------------------------------------------------------------------- /database/postgres_create.sql: -------------------------------------------------------------------------------- 1 | -- Postgres db dump 2 | -- Note: double quote for column, single quote for value strings 3 | -- For future: add additional column to todo to determine sequence within todo - enable adding/deleting todos 4 | -- use psql -d -f <.sql file> to update db from here - .sql route relative to root dir 5 | 6 | CREATE TABLE public.tech ( 7 | "_id" serial NOT NULL, 8 | "name" varchar NOT NULL, 9 | "url" varchar, 10 | "completed" BOOLEAN NOT NULL, 11 | CONSTRAINT "tech_pk" PRIMARY KEY ("_id") 12 | ) WITH ( 13 | OIDS=FALSE 14 | ); 15 | CREATE TABLE public.todo ( 16 | "_id" serial NOT NULL, 17 | "tech" varchar NOT NULL, 18 | "name" varchar NOT NULL, 19 | "completed" BOOLEAN NOT NULL, 20 | "details" varchar NOT NULL, 21 | CONSTRAINT "todo_pk" PRIMARY KEY ("_id") 22 | ) WITH ( 23 | OIDS=FALSE 24 | ); 25 | CREATE TABLE public.user ( 26 | "_id" serial NOT NULL UNIQUE, 27 | "name" varchar NOT NULL UNIQUE, 28 | "password" varchar NOT NULL, 29 | CONSTRAINT "user_pk" PRIMARY KEY ("_id") 30 | ) WITH ( 31 | OIDS=FALSE 32 | ); 33 | CREATE TABLE public.stack ( 34 | "_id" serial NOT NULL, 35 | "name" varchar NOT NULL UNIQUE, 36 | CONSTRAINT "stack_pk" PRIMARY KEY ("_id") 37 | ) WITH ( 38 | OIDS=FALSE 39 | ); 40 | CREATE TABLE public.techInStack ( 41 | "_id" serial NOT NULL UNIQUE, 42 | "stackName" varchar NOT NULL UNIQUE, 43 | "techName" varchar NOT NULL UNIQUE, 44 | CONSTRAINT "techInStack_pk" PRIMARY KEY ("_id") 45 | ) WITH ( 46 | OIDS=FALSE 47 | ); 48 | CREATE TABLE public.board ( 49 | "_id" serial NOT NULL, 50 | "stackId" serial NOT NULL, 51 | "progress" VARCHAR(255) NOT NULL, 52 | "userId" integer NOT NULL, 53 | CONSTRAINT "board_pk" PRIMARY KEY ("_id") 54 | ) WITH ( 55 | OIDS=FALSE 56 | ); 57 | 58 | ALTER TABLE "todo" ADD CONSTRAINT "todo_fk0" FOREIGN KEY ("tech") REFERENCES public.tech("name"); 59 | ALTER TABLE "techInStack" ADD CONSTRAINT "techInStack_fk0" FOREIGN KEY ("stackName") REFERENCES public.stack("name"); 60 | ALTER TABLE "techInStack" ADD CONSTRAINT "techInStack_fk1" FOREIGN KEY ("techName") REFERENCES public.tech("name"); 61 | ALTER TABLE "board" ADD CONSTRAINT "board_fk0" FOREIGN KEY ("stackId") REFERENCES public.stack("_id"); 62 | ALTER TABLE "board" ADD CONSTRAINT "board_fk1" FOREIGN KEY ("userId") REFERENCES public.user("_id"); 63 | 64 | -- 65 | 66 | -- tech todo user stack techInStack board 67 | 68 | INSERT INTO public.tech VALUES (1, 'webpack', 'https://webpack.js.org', false); 69 | INSERT INTO public.tech VALUES (2, 'mongodb', 'https://docs.mongodb.com', false); 70 | INSERT INTO public.tech VALUES (3, 'express', 'https://expressjs.com', false); 71 | INSERT INTO public.tech VALUES (4, 'react', 'https://reactjs.com', false); 72 | INSERT INTO public.tech VALUES (5, 'postgres', 'https://www.postgresql.org', false); 73 | 74 | INSERT INTO public.todo VALUES (1, 'webpack', 'Install Webpack', false, 'While in the working directory of your project, run npm install webpack in the terminal.'); 75 | INSERT INTO public.todo VALUES (2, 'webpack', 'Initialize webpack.config', false, 'Create a new file in the root directory of your project, and name it "webpack.config.js.'); 76 | INSERT INTO public.todo VALUES (3, 'webpack', 'Entry point', false, 'Add the following to your webpack config file:↵ …pt file that renders your highest level component.'); 77 | INSERT INTO public.todo VALUES (4, 'webpack', 'Output', false, 'The output property tells webpack where to emit th…o the ./dist folder for any other generated file.'); 78 | INSERT INTO public.todo VALUES (5, 'webpack', 'Add loaders', false, 'Out of the box, webpack only understands JavaScript. Designate which loader should be used to do the transforming.'); 79 | INSERT INTO public.todo VALUES (6, 'webpack', 'Add plugins', false, 'In order to use a plugin, you need to require() it by calling it with the new operator.'); 80 | INSERT INTO public.todo VALUES (7, 'webpack', 'Specify webpack mode', false, 'By setting the mode parameter to either development, production or none, you can enable webpacks built-in optimizations that correspond to each environment. The default value is production.'); 81 | INSERT INTO public.todo VALUES (8, 'mongodb', 'Setup MongoDB', false, 'Setup Mongo database through installation of either a local shell or a cloud database.'); 82 | 83 | INSERT INTO public.user VALUES (1, 'luis', 'apple'); 84 | INSERT INTO public.user VALUES (2, 'andy', 'orange'); 85 | 86 | INSERT INTO public.stack VALUES (1, 'MERN'); 87 | INSERT INTO public.stack VALUES (2, 'NERP'); 88 | 89 | INSERT INTO public.techInStack VALUES (1, 'MERN', 'webpack'); 90 | INSERT INTO public.techInStack VALUES (2, 'MERN', 'mongodb'); 91 | INSERT INTO public.techInStack VALUES (3, 'MERN', 'express'); 92 | INSERT INTO public.techInStack VALUES (4, 'MERN', 'react'); 93 | INSERT INTO public.techInStack VALUES (5, 'MERN', 'node'); 94 | INSERT INTO public.techInStack VALUES (6, 'NERP', 'webpack'); 95 | INSERT INTO public.techInStack VALUES (7, 'NERP', 'node'); 96 | INSERT INTO public.techInStack VALUES (8, 'NERP', 'express'); 97 | INSERT INTO public.techInStack VALUES (9, 'NERP', 'react'); 98 | INSERT INTO public.techInStack VALUES (10, 'NERP', 'postgres'); 99 | 100 | INSERT INTO public.board VALUES (1, 1, 'string', 1); 101 | INSERT INTO public.board VALUES (2, 2, 'string', 2); 102 | 103 | select setval('public.user__id_seq', 3, false); 104 | select setval('public.stack__id_seq', 3, false); 105 | select setval('public.board__id_seq', 3, false); 106 | select setval('public.tech__id_seq', 6, false); 107 | select setval('public.techInStack__id_seq', 11, false); 108 | select setval('public.todo__id_seq', 9, false); 109 | 110 | 111 | --------------------------------------------------------------------------------