├── server ├── routers │ ├── api.js │ ├── posts.js │ ├── listItems.js │ └── pets.js ├── models │ ├── petModel.js │ └── BucketListModels.js ├── server.js └── controllers │ ├── ListItemController.js │ ├── petController.js │ └── PostController.js ├── .gitignore ├── README.md ├── client ├── index.js ├── components │ ├── DisplayPost.jsx │ ├── UncompletedBucketlistItem.jsx │ ├── CreateListItem.jsx │ ├── feed.jsx │ ├── AddPostForm.jsx │ ├── Login.jsx │ ├── Signup.jsx │ ├── CompletedBucketlistItem.jsx │ ├── Bucketlist.jsx │ └── BucketlistItemDisplay.jsx ├── styles.scss └── App.jsx ├── index.html ├── webpack.config.js ├── LICENSE └── package.json /server/routers/api.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # yeti-crab-professionals -------------------------------------------------------------------------------- /client/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import App from './App.jsx' 4 | 5 | render( 6 | , 7 | document.getElementById('app') 8 | ); -------------------------------------------------------------------------------- /client/components/DisplayPost.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function DisplayPost(props) { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } 10 | 11 | export default DisplayPost; -------------------------------------------------------------------------------- /client/styles.scss: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | margin: 0; 4 | padding: 0; 5 | font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif; 6 | color: black; 7 | } 8 | 9 | .form-container { 10 | width: 100%; 11 | display: flex; 12 | flex-direction: column; 13 | align-items: center; 14 | } -------------------------------------------------------------------------------- /client/components/UncompletedBucketlistItem.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function UncompletedBucketlistItem(props) { 4 | return ( 5 |
6 |

{props.listItem}

7 | handleCheckedOffClick(props.listItem)} type="checkbox"/> 8 |
9 | ) 10 | } 11 | 12 | export default UncompletedBucketlistItem; -------------------------------------------------------------------------------- /client/components/CreateListItem.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function CreateListItem(props) { 4 | 5 | return ( 6 |
7 | 8 | 9 | 10 |
11 | ) 12 | } 13 | 14 | export default CreateListItem; -------------------------------------------------------------------------------- /server/routers/posts.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const postController = require('../controllers/PostController'); 4 | 5 | const router = express.Router(); 6 | 7 | router.post('/', postController.addPost); 8 | router.get('/', postController.getAllPosts); 9 | router.get('/:title', postController.getPost); 10 | router.put('/:title', postController.updatePost); 11 | router.delete('/:title', postController.deletePost); 12 | 13 | module.exports = router; 14 | -------------------------------------------------------------------------------- /server/models/petModel.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | const Schema = mongoose.Schema; 3 | 4 | const petSchema = new Schema({ 5 | username: { type: String, required: true }, 6 | password: { type: String, required: true }, 7 | profilePicture: { type: String, required: true }, 8 | age: { type: Number, require: true }, 9 | bio: { type: String }, 10 | name: { type: String, required: true }, 11 | }); 12 | 13 | module.exports = mongoose.model("Pet", petSchema); 14 | -------------------------------------------------------------------------------- /server/routers/listItems.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const listItemController = require('../controllers/ListItemController.js'); 4 | 5 | const router = express.Router(); 6 | 7 | router.post('/', listItemController.addItem); 8 | router.get('/', listItemController.getAllItems); 9 | router.get('/:item', listItemController.getItem); 10 | router.put('/:item', listItemController.updateItem); 11 | router.delete('/:item', listItemController.deleteItem); 12 | 13 | module.exports = router; 14 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Petrospective 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /server/routers/pets.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const petController = require("../controllers/petController.js"); 3 | const router = express.Router(); 4 | 5 | //create pet docuemnt and send data back to frontend to map data 6 | router.post("/signup", petController.createPet, (req, res) => { 7 | res.send(200).json(res.locals.pets); 8 | }); 9 | 10 | router.get("/login", petController.validateUser); 11 | 12 | router.put("/:username", petController.updatePetBio, (req, res) => { 13 | // CHANGED ALL res.send to res.status 14 | res.status(200).json(res.locals.updatedBio); 15 | }); 16 | 17 | router.delete("/:username", petController.deletePet, (req, res) => { 18 | // CHANGED ALL res.send to res.status 19 | res.status(200); 20 | }); 21 | 22 | // exported the router 23 | module.exports = router; -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | 4 | module.exports = { 5 | mode: process.env.NODE_ENV, 6 | entry: './client/index.js', 7 | output: { 8 | path: path.join(__dirname, 'build'), 9 | filename: 'bundle.js' 10 | }, 11 | 12 | devServer: { 13 | publicPath: '/build', 14 | proxy: { 15 | '/api/': 'http://localhost:3000' 16 | }, 17 | port: 8080 18 | }, 19 | module: { 20 | rules: [ 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: /\.s[ac]ss$/i, 33 | exclude: /(node_modules)/, 34 | use: ['style-loader', 'css-loader', 'sass-loader'] 35 | } 36 | ] 37 | } 38 | 39 | }; 40 | 41 | -------------------------------------------------------------------------------- /client/App.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { BrowserRouter as Router, Route, Switch } from "react-router-dom"; 3 | // import './styles.css'; 4 | import Login from "./components/Login.jsx"; 5 | // import Home from "./components/Home.jsx"; 6 | // import Feed from "./containers/Feed.jsx"; 7 | import Signup from "./components/Signup.jsx"; 8 | import Bucketlist from './components/Bucketlist.jsx' 9 | 10 | 11 | class App extends Component { 12 | render() { 13 | return ( 14 |
15 | 16 | {/* */} 17 |
18 | ) 19 | } 20 | } 21 | 22 | export default App; 23 | 24 | // 25 | // 26 | // 27 | // 28 | // {/* */} 29 | // 30 | //
404, dawg
31 | //
32 | //
33 | -------------------------------------------------------------------------------- /client/components/feed.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { BrowserRouter as Router, Route, Switch } from "react-router-dom"; 3 | 4 | const Feed = (props) => { 5 | 6 | function renderTable() { 7 | return this.props.something.map((info, index) => { 8 | {listItem, date, postDescription, location, youtubeLink} = info; 9 | return ( 10 | 11 | 13 | 14 | {listItem} 15 | {date} 16 | {postDescription} 17 | {location} 18 | {`
12 | This is where a header would go
20 | ) 21 | }) 22 | } 23 | 24 | render() { 25 | return ( 26 |
27 |
28 |
{this.renderTable()}
29 |
30 |
31 | ) 32 | } 33 | } 34 | 35 | 36 | 37 | export default Feed; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Yeti-Crab-Squad 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /server/models/BucketListModels.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | // save the Schema class to a variable so we can use its constructor 4 | const Schema = mongoose.Schema; 5 | 6 | // create schema for bucket list items 7 | const listItemSchema = new Schema({ 8 | listItem: { type: String, required: true }, 9 | isChecked: { type: Boolean }, 10 | hasPost: { type: Boolean }, 11 | }); 12 | 13 | // create a model that conforms to the schema 14 | const ListItem = mongoose.model('listItem', listItemSchema); 15 | 16 | // create schema for bucket list posts 17 | const postSchema = new Schema({ 18 | listItem: { type: String, required: true }, 19 | datePosted: { type: String, required: true }, 20 | dateCompleted: { type: String, required: true }, 21 | postDescription: { type: String, required: true }, 22 | location: { type: String, required: true }, 23 | // is the above a Google Maps URL or a string that tells Maps to make a map? 24 | youtubeLink: String, 25 | images: Array, 26 | }); 27 | 28 | // create a model that conforms to the schema 29 | const Post = mongoose.model('post', postSchema); 30 | 31 | module.exports = { 32 | ListItem, 33 | Post, 34 | }; 35 | -------------------------------------------------------------------------------- /client/components/AddPostForm.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function AddPostForm(props) { 4 | return ( 5 |
6 | {/* research ways to formalize Date input data */} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | ) 21 | } 22 | 23 | 24 | export default AddPostForm; -------------------------------------------------------------------------------- /client/components/Login.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { Redirect } from "react-router-dom"; 3 | import { Link } from "react-router-dom"; 4 | 5 | class Login extends Component { 6 | constructor() { 7 | super(); 8 | this.state = { 9 | loggedIn: false, 10 | failedLogin: false, 11 | }; 12 | 13 | // this.validate = this.validate.bind(this); 14 | } 15 | 16 | render() { 17 | // if (this.state.loggedIn) { 18 | // return ; 19 | // } 20 | 21 | return ( 22 |
23 |

Welcome !

24 |
25 | 29 |
30 | 34 |
35 | 39 | 40 | 41 |
42 |
43 | ); 44 | } 45 | } 46 | 47 | export default Login; 48 | -------------------------------------------------------------------------------- /client/components/Signup.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { Redirect } from "react-router-dom"; 3 | import { Link } from "react-router-dom"; 4 | 5 | class Signup extends Component { 6 | constructor() { 7 | super(); 8 | this.state = { 9 | loggedIn: false, 10 | failedLogin: false, 11 | }; 12 | 13 | // this.validate = this.validate.bind(this); 14 | } 15 | 16 | render() { 17 | // if (this.state.loggedIn) { 18 | // return ; 19 | // } 20 | 21 | return ( 22 |
23 |

Welcome !

24 |
25 | 29 |
30 | 34 |
35 | 39 |
40 | 44 | 45 | 46 |
47 |
48 | ); 49 | } 50 | } 51 | 52 | export default Signup; 53 | -------------------------------------------------------------------------------- /client/components/CompletedBucketlistItem.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function CompletedBucketlistItem(props) { 4 | const [displayPost, setDisplayPost] = useState(false) 5 | const [postData, setPostData] = useState() 6 | 7 | function showPost(listItem) { 8 | fetch(`/api/posts/${listItem}`) 9 | .then(res => res.json()) 10 | .then(data => { 11 | setDisplayPost(true) 12 | setPostData(data) 13 | }) 14 | } 15 | 16 | if (!displayPost) { 17 | return ( 18 |
19 |
20 |

{props.listItem}

21 |

{props.dateCompleted}

22 | 23 | 24 |
25 |
26 | ) 27 | } else { 28 | return ( 29 |
30 |

{postData.listItem}

31 |
{postData.date}
32 |

{postData.postDescription}

33 | {postData.images.map(image => { 34 | return 35 | })} 36 | 37 | 38 |
39 | ) 40 | } 41 | 42 | 43 | 44 | 45 | } 46 | 47 | export default CompletedBucketlistItem; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "petrospective", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest --verbose", 8 | "start": "nodemon server/server.js", 9 | "build": "NODE_ENV=production webpack", 10 | "dev": "NODE_ENV=development webpack-dev-server & nodemon server/server.js --open" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/andrew-lovato/petrospective.git" 15 | }, 16 | "author": "", 17 | "license": "ISC", 18 | "bugs": { 19 | "url": "https://github.com/andrew-lovato/petrospective/issues" 20 | }, 21 | "homepage": "https://github.com/andrew-lovato/petrospective#readme", 22 | "dependencies": { 23 | "bcrypt": "^5.0.0", 24 | "cors": "^2.8.5", 25 | "enzyme": "^3.11.0", 26 | "express": "^4.17.1", 27 | "mongoose": "^5.10.7", 28 | "oauth": "^0.9.15", 29 | "react": "^16.13.1", 30 | "react-dom": "^16.13.1", 31 | "react-router": "^5.2.0", 32 | "react-router-dom": "^5.2.0" 33 | }, 34 | "devDependencies": { 35 | "@babel/core": "^7.11.6", 36 | "@babel/preset-env": "^7.11.5", 37 | "@babel/preset-react": "^7.10.4", 38 | "babel-loader": "^8.1.0", 39 | "css-loader": "^4.3.0", 40 | "fs": "0.0.1-security", 41 | "path": "^0.12.7", 42 | "sass-loader": "^10.0.2", 43 | "style-loader": "^1.3.0", 44 | "webpack": "^4.44.2", 45 | "webpack-cli": "^3.3.12", 46 | "webpack-dev-server": "^3.11.0" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /client/components/Bucketlist.jsx: -------------------------------------------------------------------------------- 1 | // import React, { useState, useEffect } from 'react'; 2 | // import BucketlistItemDisplay from './BucketlistItemDisplay.jsx' 3 | // import CreateListItem from './CreateListItem.jsx' 4 | 5 | // function Bucketlist(props) { 6 | // const [bucketlistItems, setBucketlistItems] = useState([]); 7 | // const [newItem, setNewItem] = useState() 8 | 9 | // useEffect(() => { 10 | // fetch('/api/listItems') 11 | // .then(res => res.json()) 12 | // .then(data => { 13 | // setBucketlistItems(data) 14 | // }) 15 | // }) 16 | 17 | // function handleNewItemClick() { 18 | 19 | // fetch('/api/listItems', { 20 | // method: 'POST', 21 | // headers: { 22 | // 'Content-Type': 'application/json' 23 | // }, 24 | // body: newItem 25 | // }) 26 | // .then(res => res.json()) 27 | // .then(data => { 28 | // setNewItem('') 29 | 30 | // useEffect(() => { 31 | // fetch('/api/listItems') 32 | // .then(res => res.json()) 33 | // .then(data => { 34 | // setBucketlistItems(data) 35 | 36 | // }) 37 | // }) 38 | // }) 39 | // } 40 | 41 | // function handleNewItemChange(e) { 42 | // e.preventDefault() 43 | // let newItem = e.target.value; 44 | 45 | // setNewItem(newItem); 46 | // } 47 | 48 | // return( 49 | //
50 | // 54 | // {bucketlistItems.map(item => { 55 | // return 56 | // }) 57 | // } 58 | 59 | //
60 | 61 | // ) 62 | // } 63 | 64 | // export default Bucketlist; -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | const path = require("path"); 4 | const cors = require("cors"); 5 | const PORT = 3000; 6 | const mongoose = require("mongoose"); 7 | 8 | const petRouter = require("./routers/pets.js"); 9 | const postRouter = require('./routers/posts'); 10 | const listItemRouter = require('./routers/listItems'); 11 | // uncomment once there is middleware 12 | // const api = require('./routers/api') 13 | 14 | const MONGO_URI = 15 | "mongodb+srv://AndrewL:bucketlist@cluster0.00tox.mongodb.net/?retryWrites=true&w=majority"; 16 | 17 | mongoose 18 | .connect(MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true }) 19 | .then(() => console.log("CONNECTED TO MONGO DB")); 20 | 21 | app.use(express.json()); 22 | app.use(express.urlencoded({ extended: true })); 23 | app.use(cors()); // remember to npm install cors 24 | 25 | // changed to /api/pet so that it will be routed through the webpack router 26 | // app.use("/pet", petRouter); 27 | app.use("/api/pet", petRouter); 28 | app.use('/api/listItems', listItemRouter); 29 | app.use('/api/posts', postRouter); 30 | 31 | app.get("/", (req, res) => { 32 | res.sendFile(path.join(__dirname, "../index.html")); 33 | }); 34 | 35 | // app.post request will trigger controller to Create a New User --> Pushing data to the noSQL database 36 | 37 | // Global error handler for catching middleware errors 38 | app.use((err, req, res, next) => { 39 | const defaultError = { 40 | log: "Error during unknown middleware", 41 | status: 400, 42 | message: { err: "Uh-oh, error time, baby! :)" }, 43 | }; 44 | const errorObj = Object.assign({}, defaultErr, err); 45 | console.log(errorObj.log); 46 | return res.status(errorObj.status).json(errorObj.message); 47 | }); 48 | 49 | // Seed the database 50 | const postSeedDb = { 51 | listItem: 'I want to hike in Central Park with Rocko', 52 | date: 'Jan 7th 2058', 53 | postDescription: "Central Park sure has changed", 54 | youtubeLink: 'fakeYoutubeLink', 55 | location: 'fakeYoutubeLink', 56 | images: ['image1', 'image2'] 57 | } 58 | 59 | fetch('/api/posts', { 60 | method: 'POST', 61 | headers: { 62 | 'Content-type': 'application/json' 63 | }, 64 | body: postSeedDb 65 | }) 66 | .then(res => res.json()) 67 | .then(data => { 68 | console.log('Post data was added to the DB') 69 | }) 70 | .catch(err =>{ 71 | console.log(err) 72 | }) 73 | 74 | 75 | app.listen(PORT, () => console.log(`Listening on PORT: ${PORT}.`)); 76 | -------------------------------------------------------------------------------- /server/controllers/ListItemController.js: -------------------------------------------------------------------------------- 1 | const { ListItem } = require('../models/BucketListModels'); 2 | 3 | const ListItemController = { 4 | // creates new list items for the bucket list 5 | addItem(req, res, next) { 6 | ListItem.create({ 7 | listItem: req.body.listItem, 8 | isChecked: false, 9 | }, (err, newItem) => { 10 | if (err) { 11 | next({ 12 | log: 'Error creating list item. Please check middleware syntax.', 13 | }); 14 | } 15 | res.locals.items = newItem; 16 | // changed to.json 17 | // res.status(200).send(newItem); 18 | res.status(200).json(newItem); 19 | }); 20 | }, 21 | 22 | getAllItems(req, res, next) { 23 | ListItem.find({}, 24 | (err, allItems) => { 25 | if (err) { 26 | next({ 27 | log: 'Error grabbing list items. Please check middleware syntax.', 28 | }); 29 | } else { 30 | // setting the value to -1 sorts IDs descending, so posts from newest to oldest 31 | allItems.sort({ _id: -1 }); 32 | res.status(200).send(allItems); 33 | } 34 | }); 35 | }, 36 | 37 | getItem(req, res, next) { 38 | const itemTitle = req.params.item; 39 | ListItem.findOne({ listItem: itemTitle }, 40 | (err, foundItem) => { 41 | if (err) { 42 | next({ 43 | log: 'Error getting list item. Please check middleware syntax.', 44 | }); 45 | } else { 46 | // changed to.json 47 | // res.status(200).send(newItem); 48 | res.status(200).json(foundItem); 49 | } 50 | }); 51 | }, 52 | 53 | updateItem(req, res, next) { 54 | const itemTitle = req.params.item; 55 | const update = { 56 | listItem: req.body.listItem, 57 | isChecked: req.body.isChecked, 58 | hasPost: req.body.hasPost, 59 | }; 60 | ListItem.findOneAndUpdate({ listItem: itemTitle }, update, 61 | (err, updatedItem) => { 62 | if (err) { 63 | next({ 64 | log: 'Error updating list item. Please check middleware syntax.', 65 | }); 66 | } else { 67 | res.status(200).send(updatedItem); 68 | } 69 | }); 70 | }, 71 | 72 | // deletes items from the bucket list 73 | deleteItem(req, res, next) { 74 | const itemTitle = req.params.item; 75 | ListItem.deleteOne({ 76 | listItem: itemTitle, 77 | }, (err) => { 78 | if (err) { 79 | next({ 80 | log: 'Error deleting list item. Please check middleware syntax.', 81 | }); 82 | } else { 83 | res.sendStatus(200); 84 | } 85 | }); 86 | }, 87 | }; 88 | 89 | module.exports = ListItemController; 90 | -------------------------------------------------------------------------------- /server/controllers/petController.js: -------------------------------------------------------------------------------- 1 | const Pet = require("../models/petModel.js"); 2 | const PetController = {}; 3 | const SALT_WORK_FACTOR = 10; 4 | const bcrypt = require("bcryptjs"); 5 | 6 | PetController.createPet = (req, res, next) => { 7 | const { username, password, profilePicture, age, bio, name } = req.body; 8 | bcrypt.hash(password, SALT_WORK_FACTOR, (err, hash) => { 9 | Pet.create( 10 | { username, password: hash, profilePicture, age, bio, name }, 11 | (err, pet) => { 12 | if (err) { 13 | return next({ 14 | log: 15 | "Error occured in PetController.createPet middleware. Please check your syntax.", 16 | message: { err: err }, 17 | }); 18 | } 19 | res.locals.pets = pet; 20 | return next(); 21 | } 22 | ); 23 | }); 24 | }; 25 | 26 | PetController.validateUser = (req, res, next) => { 27 | const { username, password } = req.params; 28 | Pet.find({ username, password }, (err, user) => { 29 | if (err) { 30 | res.send("please enter a valid username and password."); 31 | return next({ 32 | log: 33 | "Error occured in PetController.validateUser middleware. Please check your syntax.", 34 | message: { err: err }, 35 | }); 36 | } 37 | bcrypt.compare(password, user.password, (err, result) => { 38 | if (result) { 39 | console.log("success!"); 40 | //change this redirect name based on the frontend 41 | res.send(200).redirect("/home"); 42 | } else { 43 | //change this redirect name based on the frontend 44 | res.send("Incorrect password. Please try again."); 45 | res.send(200).redirect("/"); 46 | } 47 | }); 48 | }); 49 | return next(); 50 | }; 51 | 52 | PetController.updatePetBio = (req, res, next) => { 53 | // Changed this to access the username, not bio, because was set to username in the initial route 54 | // const { bio } = req.params; 55 | const { username } = req.params 56 | const { updatedBio } = req.body; 57 | Pet.findOneAndUpdate( 58 | // changed to username 59 | // { name: bio }, 60 | { name: username}, 61 | // changed name to bio 62 | // { name: updatedBio }, 63 | { bio: updatedBio }, 64 | { new: true }, 65 | (err, pet) => { 66 | if (err) { 67 | return next({ 68 | log: 69 | "Error occured in PetController.updatePetName middleware. Please check your syntax.", 70 | message: { err: err }, 71 | }); 72 | } 73 | res.locals.updatedBio = pet; 74 | return next(); 75 | } 76 | ); 77 | }; 78 | 79 | PetController.deletePet = (req, res, next) => { 80 | const { username } = req.params; 81 | Pet.deleteOne({ username: username }, (err, pet) => { 82 | if (err) { 83 | return next({ 84 | log: 85 | "Error occured in PetController.deletePet middleware. Please check your syntax.", 86 | message: { err: err }, 87 | }); 88 | } 89 | return next(); 90 | }); 91 | }; 92 | 93 | module.exports = PetController; 94 | -------------------------------------------------------------------------------- /server/controllers/PostController.js: -------------------------------------------------------------------------------- 1 | const { Post } = require('../models/BucketListModels'); 2 | 3 | const PostController = { 4 | // creates new posts for your feed 5 | 6 | addPost(req, res, next) { 7 | 8 | Post.create({ 9 | listItem: req.body.listItem, 10 | date: req.body.date, 11 | postDescription: req.body.postDescription, 12 | location: req.body.location, 13 | // is the above a Google Maps URL or a string that tells Maps to make a map? 14 | youtubeLink: req.body.youtubeLink, 15 | // imageUpload: Come back to this. Use GridFS to store images 16 | }, (err, newPost) => { 17 | if (err) { 18 | next({ 19 | log: 'Error creating post. Please check middleware syntax.', 20 | }); 21 | } else { 22 | // changed to.json 23 | // res.status(200).send(newPost); 24 | res.status(200).json(newPost); 25 | } 26 | }); 27 | }, 28 | 29 | getAllPosts(req, res, next) { 30 | Post.find({}, 31 | (err, allPosts) => { 32 | if (err) { 33 | next({ 34 | log: 'Error grabbing post feed. Please check middleware syntax.', 35 | }); 36 | } else { 37 | // setting the value to -1 sorts IDs descending, so posts from newest to oldest 38 | allPosts.sort({ _id: -1 }); 39 | res.status(200).send(allPosts); 40 | } 41 | }); 42 | }, 43 | 44 | // displays posts in database 45 | getPost(req, res, next) { 46 | const postTitle = req.params.title; 47 | Post.findOne({ listItem: postTitle }, 48 | (err, foundPost) => { 49 | if (err) { 50 | next({ 51 | log: 'Error getting post. Please check middleware syntax.', 52 | }); 53 | } else { 54 | // changed to.json 55 | // res.status(200).send(newPost); 56 | res.status(200).json(foundPost); 57 | } 58 | }); 59 | }, 60 | 61 | // allows you to edit and update posts for your feed 62 | updatePost(req, res, next) { 63 | const postTitle = req.params.title; 64 | const update = { 65 | listItem: req.body.listItem, 66 | datePosted: req.body.datePosted, 67 | dateCompleted: req.body.dateCompleted, 68 | postDescription: req.body.postDescription, 69 | location: req.body.location, 70 | youtubeLink: req.body.youtubeLink, 71 | // imageUpload: req.body.imageUpload, 72 | }; 73 | Post.findOneAndUpdate({ listItem: postTitle }, update, 74 | (err, updatedPost) => { 75 | if (err) { 76 | next({ 77 | log: 'Error updating post. Please check middleware syntax.', 78 | }); 79 | } else { 80 | // changed to.json 81 | // res.status(200).send(newPost); 82 | res.status(200).json(updatedPost); 83 | } 84 | }); 85 | }, 86 | 87 | // deletes posts from your feed 88 | deletePost(req, res, next) { 89 | const postTitle = req.params.title; 90 | Post.deleteOne({ listItem: postTitle }, 91 | (err) => { 92 | if (err) { 93 | next({ 94 | log: 'Error deleting post. Please check middleware syntax.', 95 | }); 96 | } else { 97 | res.sendStatus(200); 98 | } 99 | }); 100 | }, 101 | }; 102 | 103 | module.exports = PostController; 104 | -------------------------------------------------------------------------------- /client/components/BucketlistItemDisplay.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useState, useEffect } from 'react' 3 | import { json } from 'express'; 4 | import AddPostForm from './AddPostForm.jsx' 5 | import CompletedBucketlistItem from './CompletedBucketlistItem.jsx' 6 | import UncompletedBucketlistItem from './UncompletedBucketlistItem.jsx' 7 | import DisplayPost from './DisplayPost.jsx' 8 | 9 | function BucketlistItemDisplay(props) { 10 | const [state, setState] = useState(props.item); 11 | setState(...state, state.images = []) 12 | setState(...state, state.handleImagesChange = '') 13 | 14 | function handleCheckedOffClick(listItem) { 15 | 16 | useEffect(() => { 17 | const updatedItem = { 18 | isChecked: true, 19 | mustAddPost: true 20 | } 21 | 22 | fetch(`/api/listItems/${listItem}`,{ 23 | method: 'POST', 24 | headers: { 25 | 'Content-Type': 'application/json' 26 | }, 27 | body: updatedItem 28 | }) 29 | .then(res => res.json()) 30 | .then(data => { 31 | setState(...state, state.isChecked = true, state.mustAddPost = true) 32 | }) 33 | .catch(error => { 34 | console.log(error) 35 | }) 36 | }) 37 | 38 | 39 | 40 | 41 | 42 | } 43 | 44 | function handleDateChange(e) { 45 | e.preventDefault() 46 | const newValue = e.target.value; 47 | 48 | setState(...state, state.dateCompleted = newValue) 49 | } 50 | 51 | function handleBodyOfPostChange(e) { 52 | e.preventDefault() 53 | const newValue = e.target.value; 54 | 55 | setState(...state, state.postDescription = newValue) 56 | } 57 | 58 | function handleYoutubeURLChange(e) { 59 | e.preventDefault() 60 | const newValue = e.target.value; 61 | 62 | setState(...state, state.youtubeLink = newValue) 63 | } 64 | 65 | function handleEmbedGoogleMaps(e) { 66 | // NOT SURE YET HOW TO DO THIS ONE!! 67 | e.preventDefault() 68 | const newValue = e.target.value; 69 | 70 | setState(...state, state.location = newValue) 71 | } 72 | 73 | function handleSubmitPostClick() { 74 | 75 | useEffect(() => { 76 | const newPost = { 77 | listItem: state.listItem, 78 | date: state.dateCompleted, 79 | postDescription: state.postDescription, 80 | youtubeLink: state.youtubeLink, 81 | location: state.googleLink, 82 | images: state.images 83 | } 84 | 85 | fetch('/api/post/compelete-bucket-list-item', { 86 | method: 'PUT', 87 | headers: { 88 | 'Content-Type': 'application/json' 89 | }, 90 | body: newPost 91 | }) 92 | .then(res => res.json()) 93 | .then(data => { 94 | console.log(data) 95 | setState(...state, state.mustAddPost = false) 96 | }) 97 | .catch(error => { 98 | console.log(error) 99 | }) 100 | }) 101 | } 102 | 103 | function handleGoogleLinkChange(e) { 104 | e.preventDefault() 105 | const newValue = e.target.value; 106 | 107 | setState(...state, state.googleLink = newValue) 108 | } 109 | 110 | function handleAddImagesChange(e){ 111 | e.preventDefault() 112 | const newValue = e.target.value; 113 | 114 | setState(...state, state.handleImagesChange = newValue) 115 | } 116 | 117 | function handleAddImagesClick() { 118 | setState(...state, state.images.push(state.handleImagesChange)) 119 | setState(...state, state.handleImagesChange = ''); 120 | 121 | } 122 | 123 | 124 | 125 | if (state.isChecked && state.mustAddPost) { 126 | 127 | return ( 128 |
129 | 135 | 145 |
146 | ) 147 | } else if (state.item.isChecked) { 148 | return ( 149 | 155 | ) 156 | } else { 157 | 158 | return ( 159 | 166 | ) 167 | } 168 | 169 | } 170 | 171 | export default BucketlistItemDisplay; 172 | 173 | 174 | // make only one dexcription 175 | 176 | // const newPost = { 177 | // listItem, 178 | // dateCompleted, 179 | // // some sort of access to an embedded google map 180 | // // add a date the listItem was created 181 | // dateCreated, 182 | // location, 183 | // postDescription, 184 | // // maybe rename to originalDescription 185 | // // REMOVE DESCRIPTION 186 | // description, 187 | // youtubeLink, 188 | // hasCompleted, 189 | // mustAddPost 190 | // // Image upload parameter. 191 | // } --------------------------------------------------------------------------------