├── .gitignore ├── client ├── assets │ └── images │ │ ├── UI.png │ │ ├── banq.png │ │ └── catherinechiu.png ├── views │ ├── layout.pug │ ├── algorithm.pug │ ├── index.pug │ ├── edit_algorithm.pug │ └── algorithms.pug ├── index.jsx ├── containers │ ├── Display.jsx │ ├── Prompts.jsx │ └── MainContainer.jsx ├── components │ ├── Modal.jsx │ ├── Boxes.jsx │ ├── App.jsx │ ├── SingleAlgo.jsx │ ├── MainContainer.jsx │ └── PostAlgo.jsx └── stylesheets │ └── style.css ├── index.html ├── webpack.config.js ├── package.json ├── server ├── models │ └── algorithmModels.js ├── controllers │ └── promptController.js └── server.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build/*.js 3 | npm-debug.log 4 | .DS_Store 5 | package-lock.json -------------------------------------------------------------------------------- /client/assets/images/UI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catherinechiu/BanQ/HEAD/client/assets/images/UI.png -------------------------------------------------------------------------------- /client/assets/images/banq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catherinechiu/BanQ/HEAD/client/assets/images/banq.png -------------------------------------------------------------------------------- /client/assets/images/catherinechiu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/catherinechiu/BanQ/HEAD/client/assets/images/catherinechiu.png -------------------------------------------------------------------------------- /client/views/layout.pug: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title Algorithm Bank 5 | body 6 | block content 7 | br 8 | hr 9 | footer 10 | p made with ♥ by Catherine Chiu -------------------------------------------------------------------------------- /client/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import App from './components/App.jsx'; 4 | 5 | // uncomment so that webpack can bundle styles 6 | // '/' means you're looking for a folder 7 | import './stylesheets/style.css'; 8 | 9 | render( 10 | , 11 | document.getElementById('root') 12 | ); 13 | -------------------------------------------------------------------------------- /client/views/algorithm.pug: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= algorithm.title 5 | h5 Submitted by #{author} 6 | p #[strong Difficulty:] #{algorithm.difficulty} 7 | p #[strong Likes:] #{algorithm.likes} ♥'s 8 | p #[strong Tags:] #{algorithm.tags} 9 | hr 10 | p= algorithm.body 11 | p #[strong Tests:] #{algorithm.tests} 12 | br 13 | a.btn.btn-default(href='../algorithm/edit/'+algorithm._id) Edit -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Algorithm Bank 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 |
21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /client/containers/Display.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { render } from 'react-dom'; 3 | 4 | class Display extends Component { 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | render() { 10 | return ( 11 |
12 | 13 | 14 | 15 |
16 | ) 17 | }; 18 | }; 19 | 20 | export default Display; -------------------------------------------------------------------------------- /client/components/Modal.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { render } from 'react-dom'; 3 | 4 | const Modal = ({ handleClose, open, children }) => { 5 | const showHideClassName = open ? "modal display-block" : "modal display-none"; 6 | 7 | return ( 8 |
9 |
10 | {children} 11 | 12 |
13 |
14 | ); 15 | }; 16 | 17 | 18 | export default Modal; -------------------------------------------------------------------------------- /client/containers/Prompts.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { render } from 'react-dom'; 3 | 4 | import Boxes from '../components/Boxes.jsx' 5 | 6 | class Prompts extends Component { 7 | constructor(props) { 8 | super(props); 9 | } 10 | 11 | render() { 12 | // deconstruct from props 13 | const { prompts } = this.props; 14 | let boxes = []; 15 | 16 | for (let i = 0; i < prompts.length; i++) { 17 | let info = prompts[i] 18 | boxes.push() 23 | } 24 | 25 | return ( 26 |
27 | {/*

Prompts

*/} 28 |
29 | {boxes} 30 |
31 |
32 | ) 33 | }; 34 | }; 35 | 36 | export default Prompts; -------------------------------------------------------------------------------- /client/components/Boxes.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { render } from 'react-dom'; 3 | 4 | // import ability to handle routers in React 5 | import { BrowserRouter as Router, Switch, Route } from 'react-router-dom' 6 | import { Link } from 'react-router-dom' 7 | 8 | import SingleAlgo from '../components/SingleAlgo.jsx' 9 | 10 | const Boxes = (props) => { 11 | const { title, author, _id, key, difficulty } = props.info 12 | 13 | const url = `/algorithm/${_id}` 14 | 15 | // color code boxes by difficulty 16 | let color = 'blue' 17 | if (difficulty.toLowerCase() === 'warm-up') color = '#00FF00' 18 | if (difficulty.toLowerCase() === 'intermediate') color = '#00FFFF' 19 | if (difficulty.toLowerCase() === 'advanced') color = '#FF0099' 20 | 21 | 22 | return ( 23 | < button className='box'> 24 | 25 |

{title}

26 | 27 | 28 | ); 29 | } 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | export default Boxes; -------------------------------------------------------------------------------- /client/views/index.pug: -------------------------------------------------------------------------------- 1 | h1 Hello World 2 | 3 | //- extends layout 4 | 5 | //- block content 6 | //- h1 #{title} 7 | //- br 8 | //- form(method='POST', action='/') 9 | //- #form-group 10 | //- label Title: 11 | //- input.form-control(name='title', type='text') 12 | //- #form-group 13 | //- label Author: 14 | //- input.form-control(name='author', type='text') 15 | //- #form-group 16 | //- label Body: 17 | //- textarea.form-control(name='body') 18 | //- #form-group 19 | //- label Difficulty: 20 | //- textarea.form-control(name='difficulty', type='text') 21 | //- #form-group 22 | //- label Tests: 23 | //- textarea.form-control(name='tests') 24 | //- #form-group 25 | //- label Tags: 26 | //- textarea.form-control(name='tags', type='text') 27 | //- br 28 | //- input.btn.btn-primary(type='submit', value='Add Algo') 29 | //- br 30 | //- br 31 | //- hr 32 | //- ul 33 | //- each algorithm, i in algorithms 34 | //- li.list-group-item 35 | //- a(href='/algorithm/edit/'+algorithm._id)= algorithm.title -------------------------------------------------------------------------------- /client/views/edit_algorithm.pug: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1 #{title} 5 | p Edit the algorithm here... 6 | br 7 | form(method='POST', action='/algorithm/edit/'+algorithm._id) 8 | #form-group 9 | label Title: 10 | input.form-control(name='title', type='text', value=algorithm.title) 11 | #form-group 12 | label Author: 13 | input.form-control(name='author', type='text', value=algorithm.author) 14 | #form-group 15 | label Body: 16 | textarea.form-control(name='body')= algorithm.body 17 | #form-group 18 | label Difficulty: 19 | textarea.form-control(name='difficulty', type='text')= algorithm.difficulty 20 | #form-group 21 | label Tests: 22 | textarea.form-control(name='tests')= algorithm.tests 23 | #form-group 24 | label Tags: 25 | textarea.form-control(name='tags', type='text')= algorithm.tags 26 | br 27 | input.btn.btn-primary(type='submit', value='Save Changes') 28 | br 29 | br 30 | // cannot use delete with form 31 | // must use ajax to delete to keep database secure 32 | // pt 7 13:00 33 | // replace this delete button when you're done building front end 34 | a.btn.btn-danger.delete-algorithm(href='#', data-id=algorithm._id) Delete Algo -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | // entry: './client/views/index.pug', 5 | entry: './client/index.jsx', 6 | output: { 7 | path: path.resolve(__dirname, 'build'), 8 | filename: 'bundle.js', 9 | publicPath: '/' // for no 404 error with react router 10 | }, 11 | mode: process.env.NODE_ENV, 12 | module: { 13 | rules: [ 14 | { 15 | test: /\.pug?/, 16 | exclude: /node_modules/, 17 | use: { 18 | loader: 'pug-loader', 19 | } 20 | }, 21 | { 22 | test: /\.jsx?/, 23 | exclude: /node_modules/, 24 | use: { 25 | loader: 'babel-loader', 26 | options: { 27 | presets: ['@babel/preset-env', '@babel/preset-react'], 28 | } 29 | } 30 | }, 31 | { 32 | test: /\.css$/, 33 | exclude: /node_modules/, 34 | use: ['style-loader', 'css-loader'], 35 | } 36 | ] 37 | }, 38 | devServer: { 39 | publicPath: "http://localhost:8080/build/", 40 | proxy: { 41 | '/': 'http://localhost:3000' 42 | }, 43 | // enables hot module reloading 44 | watchContentBase: true, 45 | historyApiFallback: true, // for no 404 error with react router 46 | }, 47 | } -------------------------------------------------------------------------------- /client/components/App.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { render } from 'react-dom'; 3 | import ReactDOM from "react-dom"; 4 | 5 | // import ability to handle routers in React 6 | import { BrowserRouter as Router, Switch, Route } from 'react-router-dom' 7 | // import form to post algo 8 | import PostAlgo from '../components/PostAlgo.jsx' 9 | import SingleAlgo from '../components/SingleAlgo.jsx' 10 | import MainContainer from "../containers/MainContainer.jsx" 11 | 12 | 13 | class App extends Component { 14 | constructor(props) { 15 | super(props); 16 | this.state = { 17 | fetchedPrompts: false, 18 | prompts: [], 19 | openModal: false, 20 | } 21 | } 22 | 23 | 24 | 25 | render() { 26 | return ( 27 | 28 | 29 |
30 | {/* */} 31 | 32 | 33 | 34 | } /> 35 | 36 | 37 |
38 |
39 | ); 40 | } 41 | } 42 | 43 | export default App; 44 | 45 | render(, document.querySelector('#root')); 46 | -------------------------------------------------------------------------------- /client/views/algorithms.pug: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | 4 | block content 5 | h1 #{title} 6 | form(method='POST', action='/algorithms/add') 7 | #form-group 8 | label Title: 9 | input.form-control(name='title', type='text') 10 | #form-group 11 | label Author: 12 | input.form-control(name='author', type='text') 13 | #form-group 14 | label Body: 15 | textarea.form-control(name='body') 16 | #form-group 17 | label Difficulty: 18 | textarea.form-control(name='difficulty', type='text') 19 | #form-group 20 | label Tests: 21 | textarea.form-control(name='tests') 22 | #form-group 23 | label tags: 24 | textarea.form-control(name='tags', type='text') 25 | input.btn.btn-primary(type='submit', value='Submit') 26 | 27 | 28 | 29 | 30 | 31 | 32 | //- title: { 33 | //- type: String, 34 | //- required: true, 35 | //- }, 36 | //- author: { 37 | //- type: String, 38 | //- required: true, 39 | //- }, 40 | //- likes: { 41 | //- type: Number, 42 | //- required: false, 43 | //- }, 44 | //- body: { 45 | //- type: String, 46 | //- required: true, 47 | //- }, 48 | //- difficulty: { 49 | //- type: String, 50 | //- required: true, 51 | //- }, 52 | //- tests: { 53 | //- type: Array, 54 | //- required: true, 55 | //- }, 56 | //- tags: { 57 | //- type: Array, 58 | //- required: false, -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "solo", 3 | "version": "1.0.0", 4 | "description": "multi-user algorithm and solution bank", 5 | "main": "index.jsx", 6 | "scripts": { 7 | "start": "NODE_ENV=production nodemon server/server.js", 8 | "build": "NODE_ENV=production webpack", 9 | "dev": "NODE_ENV=development nodemon server/server.js & webpack-dev-server --hot --open" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/catherinechiu/solo.git" 14 | }, 15 | "author": "Catherine Chiu", 16 | "license": "ISC", 17 | "bugs": { 18 | "url": "https://github.com/catherinechiu/solo/issues" 19 | }, 20 | "homepage": "https://github.com/catherinechiu/solo#readme", 21 | "devDependencies": { 22 | "@babel/core": "^7.10.5", 23 | "@babel/preset-env": "^7.10.4", 24 | "@babel/preset-react": "^7.10.4", 25 | "babel-loader": "^8.1.0", 26 | "concurrently": "^5.2.0", 27 | "cross-env": "^7.0.2", 28 | "css": "^3.0.0", 29 | "css-loader": "^3.6.0", 30 | "loader": "^2.1.1", 31 | "mongoose": "^5.9.25", 32 | "pug": "^3.0.0", 33 | "pug-loader": "^2.4.0", 34 | "style-loader": "^1.2.1", 35 | "webpack": "^4.43.0", 36 | "webpack-cli": "^3.3.12", 37 | "webpack-dev-server": "^3.11.0" 38 | }, 39 | "dependencies": { 40 | "express": "^4.17.1", 41 | "react": "^16.13.1", 42 | "react-dom": "^16.13.1", 43 | "react-player": "^2.5.0", 44 | "react-router-dom": "^5.2.0" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /client/components/SingleAlgo.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { render } from 'react-dom'; 3 | 4 | import ReactPlayer from 'react-player' 5 | 6 | // import ability to handle routers in React 7 | import { BrowserRouter as Router, Switch, Route } from 'react-router-dom' 8 | import { Link } from 'react-router-dom' 9 | 10 | 11 | class SingleAlgo extends Component { 12 | constructor(props) { 13 | super(props); 14 | this.state = {}; 15 | } 16 | 17 | render() { 18 | 19 | return ( 20 |
21 |

REDUCE

22 |
23 |

Submitted by: WILL SENTANCE

24 |

Difficulty: WARM-UP

25 |

Prompt: Create a function, reduce, that takes an array and reduces the elements to a single value. The reduce function loops through the array and applies any operation that you can put into a function to each element in the array while keeping track of the outcome of each loop. In this way, we could use reduce to do things like sum all the numbers in an array or multiply them all together. 26 |

27 |
28 |

29 |

30 |

31 |

32 | 33 |
34 | 35 |
36 |

37 |

38 |

39 |

40 |
41 | 42 | 44 | 45 | 46 | 48 |
49 |
50 | ); 51 | } 52 | } 53 | 54 | 55 | export default SingleAlgo; -------------------------------------------------------------------------------- /client/components/MainContainer.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { render } from 'react-dom'; 3 | 4 | // import ability to handle routers in React 5 | import { BrowserRouter as Router, Switch, Route } from 'react-router-dom' 6 | import { Link } from 'react-router-dom' 7 | import PostAlgo from '../components/PostAlgo.jsx' 8 | import SingleAlgo from '../components/SingleAlgo.jsx' 9 | 10 | import Display from "./Display.jsx" 11 | import Prompts from "./Prompts.jsx" 12 | 13 | class MainContainer extends Component { 14 | constructor(props) { 15 | super(props); 16 | this.state = { 17 | fetchedPrompts: false, 18 | prompts: [], 19 | } 20 | } 21 | 22 | componentDidMount() { 23 | fetch('http://localhost:8080/prompts') 24 | .then(res => res.json()) 25 | .then((data) => { 26 | if (!Array.isArray(data)) prompts = [] 27 | console.log('data', data); 28 | console.log('HIII'); 29 | 30 | 31 | // call globalUpdate to update state in App from child MainContainer 32 | this.props.updateGlobal({ 33 | fetchedPrompts: true, 34 | prompts: data, 35 | }) 36 | 37 | console.log('Succeeded: ', data); 38 | return this.setState({ 39 | fetchedPrompts: true, 40 | prompts: data, 41 | }); 42 | 43 | }) 44 | .catch(err => console.log('Prompts.componentDidMount: get prompts controller: ERROR: ', err)); 45 | } 46 | 47 | 48 | 49 | render() { 50 | const { prompts, boxes } = this.state; 51 | console.log('hii'); 52 | return ( 53 |
54 | 55 |
56 |

Algorithms

57 | 58 | 59 | 60 | 61 |
62 | 63 | 67 | 68 |
69 | ) 70 | }; 71 | }; 72 | 73 | 74 | 75 | export default MainContainer; -------------------------------------------------------------------------------- /server/models/algorithmModels.js: -------------------------------------------------------------------------------- 1 | /****** DATABASE SCRIPT CHEAT SHEET ******/ 2 | 3 | 4 | // Database: nodekb 5 | // create database: use 6 | // list databases: show dbs 7 | // Collection: Articles 8 | // create collection in current db: db.createCollection(''); 9 | // show collections 10 | // Models: Articles 11 | // add object into Articles Model 12 | 13 | 14 | /* 15 | db.articles.insert({ 16 | "title": "Article Three", 17 | "author": "Catherine", 18 | "body": "This is article three" 19 | }) 20 | 21 | db.algorithms.insert({ 22 | "title": "TWO-SUM", 23 | "author": "Catherine", 24 | "likes": 7, 25 | "body": "This is the algo body.", 26 | "difficulty": "warm-up", 27 | "tests": ['algo test 1', 'algo test 2', 'algo test 3'], 28 | "tags": ['pattern', 'recursion', 'permutation'] 29 | }) 30 | */ 31 | 32 | 33 | // clear mongo shell: cls 34 | 35 | // display articles 36 | // db.articles.find().pretty() 37 | 38 | 39 | /****** DATABASE SCRIPT CHEAT SHEET ******/ 40 | 41 | 42 | 43 | 44 | // import mongoose (library for MongoDB) 45 | const mongoose = require('mongoose'); 46 | 47 | // connect to 'solo-project' db in mongodb 48 | const MONGO_URI = 'mongodb://localhost/solo-project'; 49 | mongoose.connect(MONGO_URI) 50 | .then(() => console.log('Connected to Mongo DB.')) 51 | .catch((err) => console.log(err)); 52 | 53 | 54 | // Algorithm Collection Schema 55 | const algorithmSchema = mongoose.Schema({ 56 | title: { 57 | type: String, 58 | required: true, 59 | }, 60 | author: { 61 | type: String, 62 | required: true, 63 | }, 64 | // likes: { 65 | // type: Number, 66 | // required: false, 67 | // }, 68 | body: { 69 | type: String, 70 | required: true, 71 | }, 72 | difficulty: { 73 | type: String, 74 | required: true, 75 | }, 76 | url: { 77 | type: String, 78 | required: false, 79 | } 80 | // tests: { 81 | // type: Array, 82 | // required: false, 83 | // }, 84 | // tags: { 85 | // type: Array, 86 | // required: false, 87 | // }, 88 | }); 89 | 90 | const Algorithm = mongoose.model('Algorithm', algorithmSchema); 91 | 92 | // exports all the models in an object to be used in the controller 93 | module.exports = { Algorithm }; 94 | -------------------------------------------------------------------------------- /server/controllers/promptController.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | // let Algorithm = require('../models/algorithmModels'); 5 | const { Algorithm } = require('../models/algorithmModels'); 6 | 7 | 8 | const promptController = {}; 9 | 10 | promptController.getPrompts = (req, res, next) => { 11 | Algorithm.find({}, (err, algorithmData) => { 12 | if (err) { 13 | return next({ 14 | log: 'promptController.getPrompts: ERROR: Error getting prompts data from database.', 15 | message: { 16 | err: 'Error occurred in promptController.getPrompts. Check server logs for more details.' 17 | }, 18 | }); 19 | } else { 20 | res.locals.algorithms = algorithmData; 21 | return next(); 22 | }; 23 | }); 24 | } 25 | 26 | promptController.postPrompt = (req, res, next) => { 27 | console.log('req.body', req.body); 28 | // deconstruct values from req.body 29 | const { title, author, likes, prompt, difficulty, tests, tags, url } = req.body 30 | Algorithm.create({ 31 | title: title, 32 | author: author, 33 | // likes: likes, 34 | difficulty: difficulty, 35 | body: prompt, 36 | url: url, 37 | // tests: tests, 38 | // tags: tags, 39 | }) 40 | console.log('post request succeeded'); 41 | return next(); 42 | } 43 | 44 | promptController.getSinglePrompt = (req, res, next) => { 45 | const algorithmId = req.params.id 46 | console.log('algorithmId', algorithmId) 47 | Algorithm.findById(algorithmId, (err, algorithmData) => { 48 | if (err) { 49 | return next({ 50 | log: 'promptController.getSinglePrompt: ERROR: Error getting SINGLE prompt from database.', 51 | message: { 52 | err: 'Error occurred in promptController.getSinglePrompt. Check server logs for more details.' 53 | }, 54 | }); 55 | } else { 56 | console.log('algorithmData', algorithmData); 57 | res.locals.algorithm = algorithmData; 58 | return next(); 59 | } 60 | }); 61 | }; 62 | 63 | // promptController.editSinglePrompt = (req, res, next) => { 64 | // const algorithmId = req.params.id; 65 | // Algorithm.findById(algorithmId, (err, algorithmData) => { 66 | // console.log('found ID:', req.params.id); 67 | // // // render view file, then pass payload so view can dynamically render using declared variables 68 | // // res.render('edit_algorithm', { 69 | // // title: algorithmData.title, 70 | // // algorithm: algorithmData, 71 | // // }); 72 | // }); 73 | // }; 74 | 75 | module.exports = promptController; -------------------------------------------------------------------------------- /client/containers/MainContainer.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { render } from 'react-dom'; 3 | 4 | // import ability to handle routers in React 5 | import { BrowserRouter as Router, Switch, Route } from 'react-router-dom' 6 | import { Link } from 'react-router-dom' 7 | import PostAlgo from '../components/PostAlgo.jsx' 8 | 9 | import Display from "./Display.jsx" 10 | import Prompts from "./Prompts.jsx" 11 | import Modal from '../components/Modal.jsx' 12 | 13 | class MainContainer extends Component { 14 | constructor(props) { 15 | super(props); 16 | this.state = { 17 | fetchedPrompts: false, 18 | prompts: [], 19 | openModal: false, 20 | } 21 | this.addPrompt = this.addPrompt.bind(this) 22 | // this.openModal = this.openModal.bind(this) 23 | // this.closeModal = this.closeModal.bind(this) 24 | } 25 | 26 | componentDidMount() { 27 | fetch('http://localhost:8080/prompts') 28 | .then(res => res.json()) 29 | .then((data) => { 30 | if (!Array.isArray(data)) prompts = [] 31 | return this.setState({ 32 | prompts: data, 33 | fetchedPrompts: true, 34 | }); 35 | }) 36 | .catch(err => console.log('Prompts.componentDidMount: get prompts controller: ERROR: ', err)); 37 | } 38 | 39 | // openModal() { 40 | // console.log('open modal clicked'); 41 | // this.setState({ openModal: true }) 42 | // } 43 | 44 | // closeModal() { 45 | // console.log('close modal clicked'); 46 | // this.setState({ openModal: false }) 47 | // } 48 | 49 | // drill down props to boxes 50 | // update database & state; then new object will be added to prompts array; set state will trigger re-render of main container & all its children 51 | // when re-render of box is triggered, prompt will get most updated state with new object, prop drill, then children will get updated 52 | addPrompt() { 53 | console.log('i was clicked'); 54 | // copy current prompts array in state 55 | let prompts = this.state.prompts.slice(); 56 | 57 | // fetch request to post route on server to add prompt 58 | const newBox = { 59 | title: 'new title', 60 | author: 'new author', 61 | likes: 7, 62 | prompt: 'new prompt', 63 | difficulty: 'advanced', 64 | tests: ['tests'], 65 | tags: ['tags'], 66 | } 67 | 68 | prompts.push(newBox) 69 | 70 | // re-renders components 71 | return this.setState({ prompts }); 72 | } 73 | 74 | // TODO: connect add prompt to modal button!! 75 | 76 | render() { 77 | const { prompts, boxes } = this.state; 78 | 79 | 80 | return ( 81 |
82 |
83 |

Algorithms

84 | 85 | 86 | 87 | {/* 88 |

Modal

89 |

Data

90 |
91 | */} 92 | 93 |
94 | 95 | 99 |
100 | ) 101 | }; 102 | }; 103 | 104 | export default MainContainer; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/catherinechiu/BanQ) 7 | ![Release: 1.0](https://img.shields.io/badge/Release-1.0-red) 8 | ![License: MIT](https://img.shields.io/badge/License-MIT-orange.svg) 9 | ![Contributions Welcome](https://img.shields.io/badge/Contributions-welcome-blue.svg) 10 | [![Github stars](https://img.shields.io/github/stars/catherinechiu/BanQ?style=social)](https://github.com/catherinechiu/BanQ) 11 | 12 |

BanQ: An app to crowd-source algorithm solutions in both video explanations and code form.

13 | 14 |
15 | 16 | 17 | ## Table of Contents 18 | 19 | * [About the Project](#about-the-project) 20 | * [Built With](#built-with) 21 | * [Getting Started](#getting-started) 22 | 23 | * [Installation](#installation) 24 | * [Usage](#usage) 25 | * [Roadmap](#roadmap) 26 | * [Contributing](#contributing) 27 | * [License](#license) 28 | * [Contact](#contact) 29 | * [Acknowledgements](#acknowledgements) 30 | 31 | 32 | 33 | ### Built With 34 | 35 | * [React](https://reactjs.org/) 36 | * [React Router](https://reactrouter.com/) 37 | * [Node](https://nodejs.org/en/) 38 | * [Express](https://expressjs.com/) 39 | * [MongoDB](https://www.mongodb.com/) 40 | * [Webpack](https://webpack.js.org/) 41 | * [CodeMirror](https://codemirror.net/) 42 | 43 | 44 | 45 | 46 | ## Getting Started 47 | 48 | Coming soon! 49 | 50 | ### Installation 51 | 52 | 1. Fork the repo 53 | 2. Clone the repo 54 | ```sh 55 | git clone https://github.com/your_username_/BanQ.git 56 | ``` 57 | 3. Install NPM packages 58 | ```sh 59 | npm install 60 | ``` 61 | 4. npm start 62 | ```JS 63 | npm start 64 | ``` 65 | 66 | 67 | 68 | 69 | ## Usage 70 | 71 |
72 |

73 |
74 | 75 | 76 | 77 | ## Roadmap 78 | 79 | See the [open issues](https://github.com/catherinechiu/BanQ/issues) for a list of proposed features (and known issues). 80 | 81 | 82 | 83 | ## Contributing 84 | 85 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 86 | 87 | 1. Fork the Project 88 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 89 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 90 | 4. Push to the Branch (`git push origin feature/AmazingFeature`) 91 | 5. Open a Pull Request 92 | 93 | ## License 94 | Distributed under the MIT License. See `LICENSE` for more information. 95 | 96 | ## Contact 97 | 98 |
99 |
100 | Catherine Chiu 101 |
102 | 103 | 104 | [contributors-shield]: https://img.shields.io/github/contributors/othneildrew/Best-README-Template.svg?style=flat-square 105 | [contributors-url]: https://github.com/othneildrew/Best-README-Template/graphs/contributors 106 | [forks-shield]: https://img.shields.io/github/forks/othneildrew/Best-README-Template.svg?style=flat-square 107 | [forks-url]: https://github.com/othneildrew/Best-README-Template/network/members 108 | [stars-shield]: https://img.shields.io/github/stars/othneildrew/Best-README-Template.svg?style=flat-square 109 | [stars-url]: https://github.com/othneildrew/Best-README-Template/stargazers 110 | [issues-shield]: https://img.shields.io/github/issues/othneildrew/Best-README-Template.svg?style=flat-square 111 | [issues-url]: https://github.com/othneildrew/Best-README-Template/issues 112 | [license-shield]: https://img.shields.io/github/license/othneildrew/Best-README-Template.svg?style=flat-square 113 | [license-url]: https://github.com/othneildrew/Best-README-Template/blob/master/LICENSE.txt 114 | [linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=flat-square&logo=linkedin&colorB=555 115 | [linkedin-url]: https://linkedin.com/in/othneildrew 116 | [product-screenshot]: images/screenshot.png -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | // import node express framework 2 | const express = require('express'); 3 | // init server 4 | const app = express(); 5 | // define port 6 | const PORT = 3000; 7 | 8 | 9 | // import path module (included w/ node.js by default) 10 | const path = require('path'); 11 | 12 | // import bodyParser 13 | const bodyParser = require('body-parser'); 14 | 15 | 16 | const promptController = require('./controllers/promptController'); 17 | 18 | // parse req.body 19 | app.use(bodyParser.urlencoded({ extended: true })); 20 | app.use(bodyParser.json()); 21 | 22 | 23 | 24 | // Production Mode vs Development Mode: statically serve everything in the build folder on the route '/build 25 | if (process.env.NODE_ENV === 'production') { 26 | app.use('/build', express.static(path.join(__dirname, '../build'))); 27 | } 28 | 29 | // ROUTES 30 | 31 | // READ: Get Home Route 32 | // serve index.html on the route '/' 33 | app.get('/', (req, res, next) => { 34 | console.log('Received get request to home route.'); 35 | res.status(200).sendFile(path.join(__dirname, '../index.html')) 36 | }); 37 | 38 | // app.get('/add', (req, res, next) => { 39 | // console.log('Received get request to add route.'); 40 | // res.status(200).sendFile(path.join(__dirname, '../client/components/PostAlgo.jsx')) 41 | // }); 42 | // 43 | 44 | app.get('/prompts', promptController.getPrompts, (req, res, next) => { 45 | res.status(200).json(res.locals.algorithms); 46 | }) 47 | 48 | // CREATE: POST Algo Prompt Route 49 | app.post('/add', promptController.postPrompt, (req, res, next) => { 50 | res.redirect(200, '/') 51 | }); 52 | 53 | 54 | // READ: GET Single Algorithm 55 | app.get('/algorithm/:id', promptController.getSinglePrompt, (req, res, next) => { 56 | res.status(200) 57 | }); 58 | 59 | // // READ: GET Edit Form for Single Algorithm 60 | // app.get('/algorithm/edit/:id', (req, res) => { 61 | // Algorithm.findById(req.params.id, (err, algorithmData) => { 62 | // console.log('found ID:', req.params.id); 63 | // // render view file, then pass payload so view can dynamically render using declared variables 64 | // res.render('edit_algorithm', { 65 | // title: algorithmData.title, 66 | // algorithm: algorithmData, 67 | // }); 68 | // }); 69 | // }); 70 | 71 | // // UPDATE: POST Edit Single Algorithm (change to put?) 72 | // app.post('/algorithm/edit/:id', (req, res) => { 73 | // // deconstruct values from req.body 74 | // const { title, author, body, difficulty, tests, tags } = req.body 75 | // // init empty object to store updated data 76 | // let algorithm = {}; 77 | // algorithm.title = title; 78 | // algorithm.author = author; 79 | // algorithm.body = body; 80 | // algorithm.difficulty = difficulty; 81 | // algorithm.tests = tests; 82 | // algorithm.tags = tags; 83 | 84 | // // store algo _id in query variable 85 | // let query = { _id: req.params.id } 86 | // // call update on Algorithm collection, passing in unique selector, updated object, 87 | // // callback to be invoked after update is complete 88 | // Algorithm.updateOne(query, algorithm, (err) => { 89 | // if (err) { 90 | // console.log(err); 91 | // } else { 92 | // res.redirect('/'); 93 | // } 94 | // }); 95 | // }); 96 | 97 | 98 | // // DELETE: 99 | // app.delete('/algorithms/edit/:id', (req, res) => { 100 | // // store algo _id in query variable 101 | // let query = { _id: req.params.id }; 102 | 103 | // Algorithm.deleteOne(query, (err) => { 104 | // if (err) { 105 | // console.log(err); 106 | // } 107 | // res.send(200).send('Success'); 108 | // }) 109 | // }) 110 | 111 | 112 | // // // Add Route for Solutions 113 | // // app.get('/algorithms/add', (req, res) => { 114 | // // res.render('algorithms', { 115 | // // title: 'Add Algorithm' 116 | // // }); 117 | // // }); 118 | 119 | 120 | // catch-all route handler for any requests to an unknown route 121 | // app.use('*', (req, res) => { 122 | // console.log('Unknown route!') 123 | // res.sendStatus(404) 124 | // }); 125 | 126 | 127 | app.get('/*', function (req, res) { 128 | res.sendFile(path.join(__dirname, '../index.html'), function (err) { //for no 404 error w/ react router 129 | if (err) { 130 | res.status(500).send(err) 131 | } 132 | }) 133 | }) 134 | 135 | 136 | // Start Server 137 | app.listen(PORT, () => console.log(`Server listening on ${PORT}...`)); 138 | 139 | 140 | 141 | // // update algo 142 | // // delete algo 143 | 144 | module.exports = app; -------------------------------------------------------------------------------- /client/components/PostAlgo.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { render } from 'react-dom'; 3 | 4 | // import ability to handle routers in React 5 | import { BrowserRouter as Router, Switch, Route } from 'react-router-dom' 6 | import { Link } from 'react-router-dom' 7 | 8 | class PostAlgo extends Component { 9 | constructor(props) { 10 | super(props); 11 | this.state = { 12 | title: '', 13 | author: '', 14 | difficulty: '', 15 | prompt: '', 16 | url: '', 17 | // tags: [], 18 | }; 19 | 20 | this.handleChange = this.handleChange.bind(this); 21 | this.onSubmit = this.onSubmit.bind(this); 22 | } 23 | 24 | handleChange(event) { 25 | this.setState({ [event.target.name]: event.target.value }); 26 | } 27 | 28 | onSubmit(event) { 29 | event.preventDefault(); 30 | 31 | // deconstruct form data out of state 32 | const { title, author, difficulty, prompt, url } = this.state; 33 | 34 | fetch('/add/', { 35 | method: 'POST', 36 | headers: { 37 | 'Content-Type': 'application/json', 38 | Accept: 'application/json', 39 | }, 40 | // stringify this.state (populated by input form) and place in request.body to send to server 41 | body: JSON.stringify(this.state) 42 | }) 43 | .then((res) => res.json()) 44 | .then((data) => { 45 | if (!Array.isArray(data)) { 46 | data = []; 47 | } 48 | this.props.history.push("/"); 49 | }) 50 | .catch((err) => { 51 | console.log('error: ', err) 52 | this.props.history.push("/"); 53 | }) 54 | } 55 | 56 | 57 | render() { 58 | 59 | // const backStyle = { 60 | // padding: '20%', 61 | // width: '150px', 62 | // height: '40px', 63 | // // display: 'flex', 64 | // // alignItems: 'center', 65 | // // textDecoration: 'underline', 66 | // } 67 | 68 | return ( 69 |
70 |
71 |

Add Algo

72 |
73 | 74 | < div className="form-details"> 75 |
76 | 80 | 81 | 85 | 86 | 90 | 91 | 95 | 96 | 100 | 101 |
102 | 103 |
104 | 105 | 106 | 107 | 108 | this.onSubmit(event)} 112 | > 113 |
114 | 115 |
116 | 117 | 118 | 119 | ); 120 | } 121 | } 122 | 123 | 124 | export default PostAlgo; 125 | 126 | // styling for back buton 127 | // style={{ color: 'inherit', textDecoration: 'inherit' }} 128 | 129 | 130 | 131 | //
132 | // 136 | // 141 | 142 | // 147 | 148 | // 153 | 154 | // 159 | //

160 | //

161 | // 162 | //
163 | // -------------------------------------------------------------------------------- /client/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | color: #e8e9ec; 3 | font-family: Arial, Helvetica, sans-serif; 4 | } 5 | 6 | /* * button :hover { 7 | background-color: red; 8 | } */ 9 | 10 | body { 11 | margin: 0; 12 | text-align: center; 13 | background-color: #1d1d1d; 14 | } 15 | 16 | main { 17 | margin: 40px 0px; 18 | } 19 | 20 | 21 | .header { 22 | /* border: black 1px solid; */ 23 | 24 | display: flex; 25 | justify-content: space-between; 26 | align-items: center; 27 | 28 | padding: 0 10%; 29 | 30 | margin-bottom: 1.5%; 31 | } 32 | 33 | h1 { 34 | display: inline; 35 | font-size: 50px; 36 | } 37 | 38 | .header-button { 39 | height: 100px; 40 | width: 100px; 41 | border-radius: 100%; 42 | border: solid white 3px; 43 | background-color: #1d1d1d; 44 | font-size: 100px; 45 | font-weight: 400; 46 | /* color: white; */ 47 | display: flex; 48 | justify-content: center; 49 | align-items: center; 50 | } 51 | 52 | .difficulty { 53 | /* border: solid black 1px; */ 54 | 55 | margin-bottom: 10%; 56 | } 57 | 58 | .difficulty button { 59 | background-color: #1d1d1d; 60 | margin: 0 18px; 61 | padding: 2.8%; 62 | font-size: 25px; 63 | border: 4px solid white; 64 | border-radius: 50px; 65 | font-weight: 800; 66 | } 67 | 68 | .difficulty :nth-child(1) { 69 | color: #00FF00; 70 | } 71 | 72 | .difficulty :nth-child(2) { 73 | color: #00FFFF; 74 | } 75 | 76 | .difficulty :nth-child(3) { 77 | color: #FF0099; 78 | } 79 | 80 | .main-Container{ 81 | /* border: solid black 1px; */ 82 | } 83 | 84 | .prompts { 85 | /* border: solid black 1px; */ 86 | 87 | text-align: center; 88 | display: flex; 89 | flex-wrap: wrap; 90 | justify-content: center; 91 | 92 | margin: 3% 93 | } 94 | 95 | 96 | .box { 97 | border: solid white 4px; 98 | background-color: #1d1d1d; 99 | width: 100px; 100 | height: 100px; 101 | font-size: 30px; 102 | border-radius: 10px; 103 | margin: 5px; 104 | flex-basis: 30%; 105 | display: flex; 106 | justify-content: center; 107 | align-items: center; 108 | } 109 | 110 | .box button { 111 | background-color: #1d1d1d; 112 | } 113 | 114 | 115 | 116 | .post-algo { 117 | /* border: solid pink 1px; */ 118 | height: 900px; 119 | margin: 80px 100px; 120 | 121 | } 122 | 123 | .post-algo h1 { 124 | font-size: 50px; 125 | } 126 | 127 | .post-algo button { 128 | border: 4px solid white; 129 | 130 | color: white; 131 | width: 100px; 132 | 133 | background-color: #1d1d1d; 134 | margin: 0 18px; 135 | padding: 2.8%; 136 | font-size: 60x; 137 | border-radius: 10px; 138 | font-weight: 800; 139 | } 140 | 141 | form { 142 | /* border: solid blue 1px; */ 143 | 144 | margin-top: 60px; 145 | 146 | font-size: 25px; 147 | padding: 60px; 148 | 149 | 150 | display: flex; 151 | flex-direction: column; 152 | align-items: space-around; 153 | } 154 | 155 | form input { 156 | /* color: white; */ 157 | width: 400px; 158 | 159 | background-color: #1d1d1d; 160 | margin: 0 18px; 161 | padding: 2.8%; 162 | font-size: 10x; 163 | border: 4px solid white; 164 | 165 | font-weight: 800; 166 | 167 | text-decoration: none; 168 | } 169 | 170 | form label { 171 | margin: 10px 0px; 172 | display: flex; 173 | justify-content: space-between; 174 | align-items: center; 175 | } 176 | 177 | .buttons { 178 | /* border: solid 1px purple; */ 179 | display: flex; 180 | justify-content: space-between; 181 | border-radius: 10px; 182 | color: white; 183 | /* width: 400px; */ 184 | 185 | background-color: #1d1d1d; 186 | margin: 0 18px; 187 | padding: 0 10%; 188 | font-size: 10x; 189 | 190 | font-weight: 800; 191 | 192 | text-decoration: none; 193 | } 194 | 195 | 196 | 197 | .submit-button { 198 | border-radius: 10px; 199 | color: white; 200 | 201 | background-color: #1d1d1d; 202 | margin: 0 23px; 203 | padding: 12%; 204 | font-size: 30px; 205 | border: 4px solid white; 206 | 207 | font-weight: 900; 208 | 209 | text-decoration: none; 210 | 211 | width: 180px; 212 | 213 | /* align-self: flex-end; 214 | display: inline; */ 215 | } 216 | 217 | .buttons button { 218 | /* border: 1px solid pink; */ 219 | border: none; 220 | font-size: 30px; 221 | 222 | } 223 | 224 | .back { 225 | border: solid white 4px; 226 | text-decoration: none; 227 | 228 | display: flex; 229 | align-items: center; 230 | border-radius: 10px; 231 | 232 | padding: 2%; 233 | } 234 | 235 | 236 | 237 | 238 | 239 | .single-algo { 240 | /* border: solid pink 1px; */ 241 | 242 | display: flex; 243 | flex-direction: column; 244 | justify-content: center; 245 | align-items: center; 246 | 247 | } 248 | 249 | 250 | .single-algo h4 { 251 | font-size: 36px; 252 | margin-top: 30px; 253 | padding: 0; 254 | } 255 | 256 | .single-algo .info { 257 | border: solid 1px pink; 258 | 259 | display: flex; 260 | flex-direction: column; 261 | justify-content: space-between; 262 | 263 | font-size: 20px; 264 | 265 | width: 60%; 266 | } 267 | 268 | .player { 269 | display: flex; 270 | justify-content: center; 271 | } 272 | 273 | .single-algo button { 274 | background-color: inherit; 275 | color: inherit; 276 | border: solid white 1px; 277 | padding: 1%; 278 | font-size: 25px; 279 | border-radius: 10px; 280 | margin: 0 30px; 281 | 282 | 283 | width: 150px; 284 | font-weight: 600; 285 | } 286 | 287 | button:hover { 288 | cursor: pointer; 289 | } 290 | 291 | /* .single-back { 292 | background-color: inherit; 293 | color: inherit; 294 | border: solid white 1px; 295 | padding: 10px; 296 | font-size: 25px; 297 | border-radius: 10px; 298 | 299 | margin: 20px; 300 | text-decoration: none; 301 | color: white; 302 | } 303 | 304 | .delete { 305 | background-color: inherit; 306 | color: white; 307 | border: solid white 1px; 308 | padding: 10px; 309 | font-size: 25px; 310 | 311 | } */ 312 | 313 | 314 | .footer { 315 | /* border: solid blue 1px; */ 316 | position: fixed; 317 | left: 0; 318 | bottom: 90px; 319 | width: 100%; 320 | color: white; 321 | text-align: center; 322 | font-size: 20px; 323 | } --------------------------------------------------------------------------------