├── .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 |
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 |
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 |
--------------------------------------------------------------------------------