├── .eslintrc.js ├── .gitignore ├── .vscode └── settings.json ├── README.md ├── package-lock.json ├── package.json ├── server ├── controllers │ └── petLoggerController.js ├── models │ └── petLoggerModels ├── routers │ └── petLoggerRouter.js └── server.js ├── src ├── App.jsx ├── components │ ├── AddNewComponent.jsx │ ├── DependentComponent.jsx │ ├── DogIcon.jsx │ ├── HelpIcon.jsx │ ├── LogComponent.jsx │ ├── NavComponent.jsx │ ├── NewLogComponent.jsx │ └── SettingIcon.jsx ├── containers │ ├── DependentContainer.jsx │ ├── MainContainer.jsx │ └── UserProfileContainer.jsx ├── images │ ├── pet_supplies_FILL0_wght400_GRAD0_opsz24.svg │ └── pets_FILL0_wght400_GRAD0_opsz24.svg ├── index.html ├── index.js └── stylesheets │ └── style.css └── webpack.config.js /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2021: true, 5 | }, 6 | extends: 'plugin:react/recommended', 7 | overrides: [ 8 | { 9 | env: { 10 | node: true, 11 | }, 12 | files: ['.eslintrc.{js,cjs}'], 13 | parserOptions: { 14 | sourceType: 'script', 15 | }, 16 | }, 17 | ], 18 | parserOptions: { 19 | ecmaVersion: 'latest', 20 | sourceType: 'module', 21 | }, 22 | plugins: ['react'], 23 | rules: { 24 | indent: ['warn', 2], 25 | 'no-unused-vars': ['off', { vars: 'local' }], 26 | 'prefer-const': 'warn', 27 | quotes: ['warn', 'single'], 28 | semi: ['warn', 'always'], 29 | 'space-infix-ops': 'warn', 30 | }, 31 | }; 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": ["Bongi"] 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pet-Logger 2 | Keep track of your pets and their activities! 3 | 4 | ## Scratch Project Team: 5 | - Neul Seo - [neulseo2](https://github.com/neulseo2) 6 | - Reem Abi Ammar - [RGA29](https://github.com/RGA29) 7 | - Michael Underbrink - [MUnderbrink90](https://github.com/MUnderbrink90) 8 | - Bongi Sibanda - [trialnerr](https://github.com/trialnerr) 9 | - Evan Griffith - [EvanCG](https://github.com/EvanCG) 10 | 11 | ## Iteration Project Team: 12 | - Adeeb Bayat - [adeebbayat](https://github.com/adeebbayat) 13 | - Emma Ijiogbe - [emmagawd](https://github.com/emmagawd) 14 | - Marselena Romero - [marsbird](https://github.com/marsbird) 15 | - Howard Sun - [howardCodeGit](https://github.com/howardCodeGit) 16 | - Lillian Tenn - [tenn501](https://github.com/tenn501) 17 | 18 | ## Installation 19 | 20 | Clone repo (`dev` = development, `main` = production) 21 | 22 | ``` 23 | npm install 24 | ``` 25 | 26 | Run the dev server: 27 | ``` 28 | npm run dev 29 | ``` 30 | Build `bundle.js` 31 | ``` 32 | npm run build 33 | ``` 34 | 35 | Run production: 36 | ``` 37 | npm start 38 | ``` 39 | 40 | ## Resources 41 | 42 | [Original Project Brief](https://docs.google.com/document/d/1FRxqzQAyEkf6vKRIRw1K4uQE90Ns0TCO/edit) 43 | 44 | [Excalidraw](https://excalidraw.com/#room=cfdad5ee56f4643e116a,x7qBxZRVR2t59n9GJfMYBQ) - High level Mongo Shema, and UI frameworks. Note: Excalidraw shared files do not retain history, so be careful! 45 | 46 | [Scrum Board](https://team-omydjgcen49r.atlassian.net/jira/software/projects/GGS/boards/1). 47 | 48 | [#gobin-shark-scratch-project slack channel](https://codesmithecri46.slack.com/archives/C06N9RH4L87) 49 | 50 | [Iteration Project Brief](https://docs.google.com/document/d/18PJVy5rqm_WkpULniAe3l4CnA7rD-iY7/edit) 51 | 52 | ### Routes 53 | 54 | Almost all requests defined in `server/routers/petLoggerRouter.js` and `server/controllers/petLogger/Controller.js`, so check there for source of truth. 55 | 56 | **POST request to add new dog** 57 | ``` 58 | Method: POST 59 | Endpoint: localhost:3000/api/dog 60 | Body: { name, breed, age, gender } 61 | Controller Method: addDog 62 | Return: newDog object 63 | ``` 64 | 65 | **GET dogs of user** 66 | ``` 67 | METHOD: GET 68 | Endpoint: localhost:3000/api/dog/:user 69 | Controller method: getDogs 70 | Response: array of matching dog objects 71 | ``` 72 | 73 | **GET for all posts of dog** 74 | ``` 75 | Method: GET 76 | Endpoint: localhost:3000/api/post (query dogId) 77 | Controller mtehod: getPost 78 | Response: array of post objects [{postType, details, date}] 79 | ``` 80 | 81 | **POST for new activity to dog** 82 | ``` 83 | Method: POST 84 | Endpoint: localhost:3000/api/post 85 | Body: { dogID, postType, details } 86 | controller Method: addPost 87 | Response: New Post object 88 | ``` 89 | 90 | ### Database 91 | 92 | MongoDB backend is associated with [trialnerr](https://github.com/trialnerr)'s account. IP Address allowlists will need to be explicitly indicated in order to read/write. 93 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pet-logger", 3 | "version": "1.0.0", 4 | "description": "Javascript-project", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node server/server.js", 8 | "build": "webpack --mode production", 9 | "dev": "nodemon server/server.js & NODE_ENV=development webpack serve --open --hot", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "dotenv": "^16.4.5", 17 | "mongodb": "^6.4.0", 18 | "mongoose": "^8.2.1", 19 | "nodemon": "^3.1.0", 20 | "pg": "^8.11.3", 21 | "react": "^18.2.0", 22 | "react-router": "^4.3.1", 23 | "react-dom": "^18.2.0", 24 | "react-router-dom": "^6.22.3" 25 | }, 26 | "devDependencies": { 27 | "@babel/core": "^7.24.0", 28 | "@babel/preset-env": "^7.24.0", 29 | "@babel/preset-react": "^7.23.3", 30 | "babel-loader": "^9.1.3", 31 | "css-loader": "^6.10.0", 32 | "eslint": "^8.57.0", 33 | "eslint-plugin-react": "^7.34.0", 34 | "file-loader": "^6.2.0", 35 | "html-webpack-plugin": "^5.6.0", 36 | "sass": "^1.71.1", 37 | "sass-loader": "^14.1.1", 38 | "style-loader": "^3.3.4", 39 | "svg-inline-loader": "^0.8.2", 40 | "webpack": "^5.90.3", 41 | "webpack-cli": "^5.1.4", 42 | "webpack-dev-server": "^5.0.2" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /server/controllers/petLoggerController.js: -------------------------------------------------------------------------------- 1 | // DOG { 2 | // DogID: integer, 3 | // Name: string, 4 | // Age: number, 5 | // Breed: string 6 | // gender: string, 7 | // Users: [userID: integer] 8 | // Posts: [{postType: string, details: string, date:date}] 9 | // } 10 | 11 | // GET REQUEST TO LOAD ALL DEPENDENTS. Info needed: name and image and id. Type for response : ARRAY of dog objects 12 | // Method: GET 13 | // Endpoint: /dog 14 | // ControllerMethod: 15 | // Response format: 16 | 17 | // Method: POST 18 | // Endpoint: localhost:3000/dog 19 | // Body: { name, breed, age, gender } 20 | // Controller Method: addDog 21 | const model = require('../models/petLoggerModels'); 22 | const petLoggerController = {}; 23 | 24 | // GET: middleware for retreiving a dog data 25 | petLoggerController.getDogs = async (req, res, next) => { 26 | try { 27 | //retrieve the userId from req.params 28 | console.log('req params', req.params); 29 | const userId = req.params.user; 30 | //get all the dogs from the database 31 | const dogs = await model.Dog.find({}); 32 | console.log({ dogs }); 33 | // for each dog returned, check if the usersarray contained the userId 34 | const matchingDogs = dogs.filter((dog) => dog.users.includes(userId)); 35 | res.locals.matchingDogs = matchingDogs; 36 | return next(); 37 | } catch (err) { 38 | // handle errors 39 | return next({ 40 | log: `Error in getDog middleware ${err}`, 41 | status: 500, 42 | message: `Error in getDog middleware`, 43 | }); 44 | } 45 | }; 46 | 47 | // petLoggerController.getDogs2 = async (req, res, next) => { 48 | // try { 49 | // //retrieve the userId from req.params 50 | // const userId = req.params.user; 51 | // //get all the dogs from the database 52 | // const dogs = await model.Dog.find({ $in: ['$userId', '$users'] }); 53 | // // for each dog returned, check if the usersarray contained the userId 54 | // res.locals.matchingDogs = dogs; 55 | // return next(); 56 | // } catch (err) { 57 | // // handle errors 58 | // return next({ 59 | // log: `Error in getDog middleware ${err}`, 60 | // status: 500, 61 | // message: `Error in getDog middleware`, 62 | // }); 63 | // } 64 | // }; 65 | 66 | // POST: middleware for adding a new dog 67 | petLoggerController.addDog = async (req, res, next) => { 68 | try { 69 | // declare the constants we're going to use 70 | const { name, breed, age, gender } = req.body; 71 | // async connect to the mongo DB, and create a new dog 72 | const newDog = await model.Dog.create({ name, breed, age, gender }); 73 | // save the dog object in res.locals as newDog 74 | res.locals.newDog = newDog; 75 | console.log("newDog", res.locals.newDog) 76 | return next(); 77 | } catch (err) { 78 | // handle errors 79 | return next({ 80 | log: `Error in addDog middleware ${err}`, 81 | status: 500, 82 | message: `Error in addDog middleware`, 83 | }); 84 | } 85 | }; 86 | 87 | // POST: middleware for adding a new user 88 | petLoggerController.addUser = async (req, res, next) => { 89 | try { 90 | const { name, username, password } = req.body; 91 | const newUser = await model.User.create({ name, username, password }); 92 | res.locals.newUser = newUser; 93 | return next(); 94 | } catch (err) { 95 | // handle errors 96 | return next({ 97 | log: `Error in addUser middleware ${err}`, 98 | status: 500, 99 | message: `Error in addUser middleware`, 100 | }); 101 | } 102 | }; 103 | 104 | // POST: for adding a new post for a dependent(dog) 105 | petLoggerController.addPost = async (req, res, next) => { 106 | try { 107 | const { dogId, postType, details } = req.body; 108 | 109 | // update the dog object to have a new post in its post array 110 | // update we'll make is pushing a new post into the posts array on the dog 111 | // we'll return just the new post 112 | 113 | const filter = { _id: dogId }; 114 | const update = { $push: { posts: { postType, details } } }; 115 | 116 | const dog = await model.Dog.findOneAndUpdate(filter, update, { new: true }); 117 | // console.log(dog.posts); 118 | res.locals.dog = dog.posts[dog.posts.length - 1]; 119 | return next(); 120 | } catch (err) { 121 | // handle errors 122 | return next({ 123 | log: `Error in addPost middleware ${err}`, 124 | status: 500, 125 | message: `Error in addPost middleware ${err}`, 126 | }); 127 | } 128 | }; 129 | 130 | // GET: for getting all dogs posts when given dog Id in req.query 131 | petLoggerController.getPost = async (req, res, next) => { 132 | try { 133 | const { dogId } = req.query; 134 | const foundDog = await model.Dog.find({ _id: dogId }); 135 | console.log(foundDog[0].posts); 136 | res.locals.posts = foundDog[0].posts; 137 | 138 | return next(); 139 | } catch (error) { 140 | return next({ 141 | log: `Error in getPost middleware ${err}`, 142 | status: 500, 143 | message: `Error in getPost middleware ${err}`, 144 | }); 145 | } 146 | }; 147 | 148 | module.exports = petLoggerController; 149 | -------------------------------------------------------------------------------- /server/models/petLoggerModels: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | // const { MongoClient, ServerApiVersion } = require('mongodb'); 3 | const { Schema } = mongoose; 4 | 5 | const uri = 6 | 'mongodb+srv://bongisiba:9amflRSy13ObViXE@petloggercluster.afs6n5g.mongodb.net/?retryWrites=true&w=majority&appName=PetLoggerCluster'; 7 | 8 | mongoose 9 | .connect(uri, { 10 | // options for the connect method to parse the URI 11 | // sets the name of the DB that our collections are part of 12 | dbName: 'petLogger', 13 | }) 14 | .then(() => console.log('Connected to Mongo DB.')) 15 | .catch((err) => console.log(err)); 16 | 17 | //user Schema 18 | const userSchema = new Schema({ 19 | name: { type: String, required: true, unique: true }, 20 | userName: { type: String, required: true }, 21 | password: { type: String, required: true }, 22 | }); 23 | 24 | const User = mongoose.model('user', userSchema); 25 | 26 | //dogSchema 27 | const dogSchema = new Schema({ 28 | name: { type: String, required: true }, 29 | age: { type: Number }, 30 | breed: { type: String }, 31 | gender: { type: String }, 32 | // users: [ 33 | // { 34 | // userId: { 35 | // type: Schema.Types.ObjectId, 36 | // ref: 'user', 37 | // }, 38 | // userId: { 39 | // type: Schema.Types.ObjectId, 40 | // default: '65ecbe30d6da6de8222431e2' 41 | // } 42 | // }, 43 | // ], 44 | users: { 45 | type: [Schema.Types.ObjectId], 46 | default: ['65ecbe30d6da6de8222431e2'], 47 | }, 48 | posts: [ 49 | { 50 | postType: String, 51 | details: String, 52 | date: { type: Date, default: Date.now }, 53 | }, 54 | ], 55 | }); 56 | 57 | const Dog = mongoose.model('dog', dogSchema); 58 | 59 | module.exports = { 60 | User, 61 | Dog, 62 | }; 63 | -------------------------------------------------------------------------------- /server/routers/petLoggerRouter.js: -------------------------------------------------------------------------------- 1 | //Importing express and creating our router 2 | const express = require('express'); 3 | const router = express.Router(); 4 | 5 | // import our relevante controllers 6 | const petLoggerController = require('../controllers/petLoggerController'); 7 | /* WHAT IS THIS FILE DOING? 8 | use the controllers based on what kind of a request is coming in 9 | define the endpoint and method 10 | run middleware 11 | send a response back 12 | */ 13 | 14 | // Create a new dog 15 | // Method: POST 16 | // Endpoint: localhost:3000/api/dog 17 | // Body: { name, breed, age, gender } 18 | // Controller Method: addDog 19 | // Return: newDog object 20 | router.post('/dog', petLoggerController.addDog, (req, res) => { 21 | res.status(200).json(res.locals.newDog); 22 | }); 23 | 24 | // get a user's dogs 25 | // METHOD: GET 26 | // Endpoint: localhost:3000/api/dog/:user 27 | // Controller method: getDogs 28 | // Response: array of matching dog objects 29 | 30 | router.get('/dog/:user', petLoggerController.getDogs, (req, res) => { 31 | res.status(200).json(res.locals.matchingDogs); 32 | }); 33 | 34 | // Get a dog's posts 35 | // Method: GET 36 | // Endpoint: localhost:3000/api/post (query: dogId) 37 | // Controller mtehod: getPost 38 | // Response: array of post objects [{postType, details, date}] 39 | router.get('/post', petLoggerController.getPost, (req, res) => { 40 | res.status(200).json(res.locals.posts); 41 | }); 42 | 43 | //add new activity 44 | //Method: POST 45 | // Endpoint: localhost:3000/api/post 46 | // Body: { dogID, postType, details } 47 | // controller Method: addPost 48 | // Response: updated dog object 49 | router.post('/post', petLoggerController.addPost, (req, res) => { 50 | res.status(200).json(res.locals.dog); 51 | }); 52 | 53 | // POST to USER => create a new user 54 | // Method: POST 55 | // Endpoint: localhost:3000/api/user 56 | // Body: { username, name, password } 57 | // Controller Method: addUser 58 | // Return: newUser object 59 | router.post('/user', petLoggerController.addUser, (req, res) => { 60 | res.status(200).json(res.locals.addUser); 61 | }); 62 | 63 | // export this file 64 | module.exports = router; 65 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const express = require('express'); 3 | const petLoggerRouter = require('./routers/petLoggerRouter'); 4 | 5 | const app = express(); 6 | const PORT = 3000; 7 | 8 | if (process.env.NODE_ENV === 'development') { 9 | app.use(express.static(path.resolve(__dirname, '../src'))); 10 | } else { 11 | app.use(express.static(path.resolve(__dirname, '../dist'))); 12 | } 13 | 14 | // parsing requests to json 15 | app.use(express.json()); 16 | // define the route handlers 17 | app.use('/api', petLoggerRouter); 18 | 19 | // Unkown route handler here 20 | app.use('*', (req, res) => { 21 | console.log('This is 404'); 22 | res.sendStatus(404); 23 | }); 24 | 25 | // Global error handler 26 | app.use((err, req, res, next) => { 27 | const defaultErr = { 28 | log: 'Error from middleware', 29 | status: 500, 30 | message: { err: 'ERROR!!!' }, 31 | }; 32 | 33 | const errorObj = Object.assign({}, defaultErr, err); 34 | 35 | return res.status(errorObj.status).json(errorObj.message); 36 | }); 37 | 38 | app.listen(PORT, () => { 39 | console.log(`server listening on port ${PORT}: http://localhost:${PORT}/`); 40 | }); 41 | 42 | module.exports = app; 43 | -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Routes, Route } from 'react-router-dom'; 3 | 4 | import MainContainer from "./containers/MainContainer"; 5 | import UserProfileContainer from "./containers/UserProfileContainer"; 6 | import DependentContainer from "./containers/DependentContainer"; 7 | import AddNewComponent from "./components/AddNewComponent"; 8 | import NavComponent from "./components/NavComponent"; 9 | 10 | 11 | 12 | const App = () => { 13 | return ( 14 |
15 | {/*

Michael was up in here!

16 |

This is Reem

17 |

Hello Guys Bongi

*/} 18 |
19 | 20 | 21 | 22 | 23 | } /> 24 | }/> 25 | }/> 26 | 27 | 28 | 29 | 30 | {/* */} 31 |
32 |
33 | 34 | ); 35 | }; 36 | 37 | export default App; -------------------------------------------------------------------------------- /src/components/AddNewComponent.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import { useState, useEffect } from 'react'; 4 | //import { useNavigate } from "react-router-dom"; 5 | 6 | const breedArray = [ 7 | 'Husky', 8 | 'Golden Retreiver', 9 | 'Dalmatian', 10 | 'German Shepard', 11 | 'Lab', 12 | 'Beagle', 13 | 'Poodle', 14 | ]; 15 | 16 | const useInput = (init) => { 17 | const [value, setValue] = useState(init); 18 | const onChange = (e) => { 19 | setValue(e.target.value); 20 | }; 21 | return [value, onChange]; 22 | }; 23 | 24 | const AddNewComponent = () => { 25 | const breedlist = [ 26 | { label: "German Shepard", value: "German Shepard" }, 27 | { label: "Lab", value: "Lab" }, 28 | { label: "Husky", value: "Husky" }, 29 | { label: "Dalmatian", value: "Dalmatian" }, 30 | ]; 31 | 32 | const genderList = [ 33 | { label: "male", value: "male" }, 34 | { label: "female", value: "female" }, 35 | ]; 36 | 37 | const [name, nameOnChange] = useInput(""); 38 | const [age, ageOnChange] = useInput(""); 39 | const [breed, breedOnChange] = useInput(""); 40 | const [gender, genderOnChange] = useInput(""); 41 | 42 | const saveCharacter = () => { 43 | // check if name is empty 44 | const body = { 45 | name, 46 | age, 47 | breed, 48 | gender, 49 | }; 50 | console.log('req.body', body); 51 | 52 | fetch("/api/dog", { 53 | method: "POST", 54 | headers: { 55 | "Content-Type": "Application/JSON", 56 | }, 57 | body: JSON.stringify(body), 58 | }) 59 | .then((resp) => resp.json()) 60 | 61 | .catch((err) => 62 | console.log(" add dependent fetch /api/character: ERROR: ", err) 63 | ); 64 | }; 65 | 66 | 67 | return ( 68 |
69 |
70 |
71 |

Add new dependent

72 | 73 | 80 | 81 | 88 | 89 | 102 | 103 | 116 | 117 | 121 | 122 | 123 | 127 | 128 |
129 |
130 |
131 | ); 132 | }; 133 | 134 | export default AddNewComponent; 135 | -------------------------------------------------------------------------------- /src/components/DependentComponent.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | // import DependentContainer from './DependentContainer'; 5 | 6 | // to add link to the dependent page 7 | 8 | const DependentComponent = ({ traits }) => { 9 | const dogImages = { 10 | 'German Shepard': 11 | 'https://images.pexels.com/photos/236622/pexels-photo-236622.jpeg', 12 | Lab: 'https://images.pexels.com/photos/4000307/pexels-photo-4000307.jpeg', 13 | Husky: 'https://images.pexels.com/photos/245035/pexels-photo-245035.jpeg', 14 | Dalmatian: 15 | 'https://images.pexels.com/photos/13764529/pexels-photo-13764529.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', 16 | }; 17 | 18 | const { _id, name, age, breed, gender } = traits; 19 | console.log({ breed }); 20 | 21 | return ( 22 |
23 |
24 | {/* */} 25 | 26 |
27 |
28 |

29 | {name} 30 |

31 | 36 |
37 |
38 | ); 39 | }; 40 | 41 | export default DependentComponent; 42 | -------------------------------------------------------------------------------- /src/components/DogIcon.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | 4 | 5 | const DogIcon = () => { 6 | 7 | 8 | return ( 9 | 10 | 11 | ); 12 | } 13 | 14 | export default DogIcon; 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/components/HelpIcon.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | 4 | 5 | const HelpIcon = () => { 6 | 7 | 8 | return ( 9 | 10 | 11 | 12 | 13 | ); 14 | } 15 | 16 | export default HelpIcon; -------------------------------------------------------------------------------- /src/components/LogComponent.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const LogComponent = ({ logs }) => { 4 | const { postType, details, date } = logs; 5 | 6 | let formatDate = date.substring(0,10) 7 | formatDate = formatDate.split('-'); 8 | formatDate = formatDate.reverse().join('/') 9 | 10 | let formatTime = date.substring(11, 16) 11 | 12 | return ( 13 |
14 | 15 | {/*

Oil Rig Placeholder

*/} 16 |
17 | {postType} 18 | {details} 19 | {`${formatDate} at ${formatTime}`} 20 |
21 |
22 | ); 23 | }; 24 | 25 | export default LogComponent; 26 | -------------------------------------------------------------------------------- /src/components/NavComponent.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import SettingIcon from './SettingIcon'; 4 | import HelpIcon from './HelpIcon'; 5 | import DogIcon from './DogIcon'; 6 | 7 | 8 | 9 | const NavComponent = () => { 10 | 11 | 12 | return ( 13 |
14 | 15 | PetLogger 16 | 17 | 25 |
26 | ) 27 | } 28 | 29 | export default NavComponent; -------------------------------------------------------------------------------- /src/components/NewLogComponent.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useState, useEffect } from "react"; 3 | import { Link } from "react-router-dom"; 4 | 5 | const useInput = (init) => { 6 | const [value, setValue] = useState(init); 7 | const onChange = (e) => { 8 | setValue(e.target.value); 9 | }; 10 | return [value, onChange]; 11 | }; 12 | 13 | const NewLogComponent = (props) => { 14 | const [postType, postTypeOnChange] = useInput(""); 15 | const [details, detailsOnChange] = useInput(""); 16 | 17 | const saveLog = (id) => { 18 | // NEED TO DO FEILD INPUT VALIDATION 19 | const body = { 20 | dogId: id, 21 | postType, 22 | details, 23 | }; 24 | fetch("/api/post", { 25 | method: "POST", 26 | headers: { 27 | "Content-Type": "Application/JSON", 28 | }, 29 | body: JSON.stringify(body), 30 | }) 31 | .then((resp) => resp.json()) 32 | .then((data) => { 33 | console.log("request body", data); 34 | window.location.reload(); 35 | }) 36 | .catch((err) => console.log(" add log fetch: ERROR: ", err)); 37 | }; 38 | 39 | return ( 40 |
41 |

Log new activity

42 |
43 |
44 |
45 | 59 | 65 |
66 | 67 | 68 | 71 | 72 |
73 |
74 | ); 75 | }; 76 | 77 | export default NewLogComponent; 78 | -------------------------------------------------------------------------------- /src/components/SettingIcon.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | 4 | 5 | const SettingIcon = () => { 6 | 7 | 8 | return ( 9 | 10 | 11 | 12 | ); 13 | } 14 | 15 | export default SettingIcon; -------------------------------------------------------------------------------- /src/containers/DependentContainer.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { withRouter } from "react-router-dom"; 3 | import NewLogComponent from "../components/NewLogComponent"; 4 | import LogComponent from "../components/LogComponent"; 5 | 6 | import DependentComponent from "../components/DependentComponent"; 7 | import { useParams } from "react-router-dom"; 8 | 9 | const logArray = [{activity:'nap', time:'12:50pm', note: 'Did not want to take a nap.'}, {activity:'medicine', time:'1:50pm', note: 'Gave red pills' }]; 10 | 11 | const DependentContainer = (props) => { 12 | const { id } = useParams(); 13 | console.log(id); 14 | 15 | // Method: GET 16 | // Endpoint: localhost:3000/api/post?dogId=48374837483743 (query dogId) 17 | // Controller mtehod: getPost 18 | // Response: array of post objects [{postType, details, date}] 19 | 20 | const [log, setLog] = useState([]); 21 | // const [reset, setreset] = useState(false) 22 | 23 | useEffect (() => { 24 | fetch (`/api/post?dogId=${id}`) 25 | .then(resp => resp.json()) 26 | .then (data => setLog(data)) 27 | .catch((err)=> console.log("get logs request error", err)); 28 | }, []); 29 | 30 | 31 | // iterate using a for loop over the data array of objects, pass in the info from each element to a dependent component. 32 | 33 | // const logActivities = log.map((elem, i)=>{ 34 | // return ( 35 | // ); 39 | // }); 40 | 41 | let logActivities = []; 42 | for (let i = log.length-1; i>=0; i--){ 43 | logActivities.push( 44 | 48 | ) 49 | } 50 | 51 | 52 | return ( 53 |
54 | {/* */} 57 | 60 | {logActivities} 61 |
62 | ); 63 | }; 64 | 65 | export default DependentContainer; 66 | -------------------------------------------------------------------------------- /src/containers/MainContainer.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import DependentContainer from './DependentContainer'; 4 | import NavComponent from '../components/NavComponent'; 5 | 6 | 7 | const MainContainer = () => { 8 | 9 | 10 | return ( 11 |
12 | 13 |
14 | ) 15 | } 16 | 17 | export default MainContainer; -------------------------------------------------------------------------------- /src/containers/UserProfileContainer.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import {withRouter} from 'react-router-dom'; 3 | import { Link } from 'react-router-dom'; 4 | 5 | import DependentComponent from "../components/DependentComponent"; 6 | 7 | 8 | // const dogArray = [{name: "Oil Rig", breed:"German Shepard", age: 5, gender: 'female'}, {name: "Shadow", breed:"German Shepard", age: 7, gender: 'male'}, {name: "Penelope", breed:"Grey Hound", age: 2, gender: 'female'}, {name: "Antonio", breed:"English Bulldog", age: 10, gender: 'male'}] 9 | 10 | const DependentContainer = () => { 11 | 12 | const [result, setResult] = useState([]); 13 | 14 | useEffect (() => { 15 | fetch ('/api/dog/65ecbe30d6da6de8222431e2') 16 | .then(resp => resp.json()) 17 | .then((data)=> setResult(data)) // data is going to be an array of objects 18 | .catch((err)=> console.log("get dependents request error", err)); 19 | 20 | }, []); 21 | 22 | 23 | // console.log("dogarray", dogArray) 24 | // console.log("dependents", dependents) 25 | 26 | const dependents = result.map((elem, i)=>{ 27 | return ( 28 | ); 32 | }); 33 | 34 | return ( 35 |
36 |

Dependents you are tracking

37 | 38 |
{dependents}
39 |
40 | 41 | 42 | 43 | 44 |
45 |
46 | ); 47 | }; 48 | 49 | export default DependentContainer; 50 | -------------------------------------------------------------------------------- /src/images/pet_supplies_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/images/pets_FILL0_wght400_GRAD0_opsz24.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Pet Logger 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import { BrowserRouter, HashRouter } from 'react-router-dom'; 5 | 6 | import styles from './stylesheets/style.css'; 7 | 8 | ReactDOM.render( 9 | 10 | 11 | , 12 | document.getElementById('root') 13 | ); -------------------------------------------------------------------------------- /src/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | 2 | @import url('https://fonts.googleapis.com/css2?family=Madimi+One&display=swap'); 3 | 4 | * { 5 | margin: 0; 6 | padding: 0; 7 | box-sizing: border-box; 8 | font-family: Arial, Helvetica, sans-serif; 9 | } 10 | 11 | body { 12 | background: whitesmoke; 13 | } 14 | 15 | .navheader { 16 | width: 100%; 17 | padding: 10px 20px; 18 | background: rgba(0, 145, 255, 0.69); 19 | display: flex; 20 | align-items: center; 21 | justify-content: space-between; 22 | } 23 | 24 | .logo { 25 | font-size: 1.5rem; 26 | color: whitesmoke; 27 | text-decoration: none; 28 | font-family: "Madimi One", sans-serif; 29 | font-weight: 400; 30 | font-style: normal; 31 | 32 | } 33 | .logo svg{ 34 | color: whitesmoke; 35 | 36 | } 37 | 38 | 39 | .navbar{ 40 | display: flex; 41 | align-items: center; 42 | justify-content: space-between; 43 | } 44 | 45 | .profileinfocontainer { 46 | background: rgba(0, 145, 255, 0.69); 47 | padding: 5px 10px; 48 | color: whitesmoke; 49 | border: solid rgba(0, 145, 255, 0.69);; 50 | border-radius: 5px; 51 | display: flex; 52 | align-items: center; 53 | justify-content: center; 54 | } 55 | 56 | .profileinfocontainer img{ 57 | height: 30px; 58 | width: 30px; 59 | border-radius: 50%; 60 | margin-right: 0px; 61 | 62 | } 63 | 64 | .profileinfocontainer span { 65 | margin-left: 10px; 66 | 67 | } 68 | 69 | 70 | .navbar a { 71 | font-size: 1rem; 72 | color: white; 73 | font-weight: 500; 74 | text-decoration: none; 75 | margin-left: 20px; 76 | 77 | } 78 | 79 | .navbar svg{ 80 | color: whitesmoke; 81 | 82 | } 83 | 84 | 85 | /*User Container styles*/ 86 | .usermaincontainer { 87 | /* border: solid black; */ 88 | /* background: whitesmoke; */ 89 | 90 | } 91 | .usermaincontainer h2{ 92 | margin: 10px 20px; 93 | color: rgba(0, 145, 255, .69); 94 | font-weight: 300; 95 | 96 | } 97 | .buttoncontainer{ 98 | width: 100%; 99 | /* text-align: right; */ 100 | margin-top: 20px; 101 | } 102 | 103 | .buttoncontainer button{ 104 | float: right; 105 | padding: 10px; 106 | margin-right: 20px; 107 | background: rgba(0, 145, 255, 1); 108 | color: white; 109 | border: none; 110 | border-radius: 5px; 111 | 112 | 113 | } 114 | 115 | .alldependents{ 116 | border: solid thin grey; 117 | background-color: white; 118 | display: flex; 119 | flex-wrap: wrap; 120 | /* flex-direction: column; */ 121 | align-items: center; 122 | justify-content: flex-start; 123 | 124 | } 125 | 126 | .dependentcomponent{ 127 | border: solid rgba(0, 145, 255, 0.69); 128 | border-radius: 10px; 129 | background: rgba(0, 145, 255, 0.1); 130 | width: 325px; 131 | height: 150px; 132 | padding: 20px; 133 | margin: 20px; 134 | display: flex; 135 | align-items: center; 136 | /* justify-content: space-around; */ 137 | 138 | } 139 | 140 | .dependentimgcontainer img { 141 | height: 100px; 142 | width: 100px; 143 | border-radius: 50%; 144 | 145 | } 146 | 147 | .dependentname h3{ 148 | text-decoration: none; 149 | } 150 | 151 | .dependentinfocontainer { 152 | padding: 10px; 153 | margin-left: 20px; 154 | } 155 | 156 | .dependentinfocontainer li{ 157 | text-decoration: none; 158 | list-style: none; 159 | } 160 | 161 | 162 | /*Add new form styling*/ 163 | .addnewcomponent{ 164 | display: flex; 165 | justify-content: center; 166 | align-items: center; 167 | background-color: whitesmoke; 168 | padding: 50px; 169 | } 170 | 171 | .formwrapper{ 172 | width: 420px; 173 | background: rgba(0, 145, 255, 0.1); 174 | border: solid rgba(0, 145, 255, 0.69); 175 | border-radius: 5px; 176 | padding: 20px; 177 | } 178 | 179 | .formwrapper h3{ 180 | margin-bottom: 10px; 181 | font-weight: bold; 182 | text-align: center; 183 | color: rgba(0, 145, 255, 0.8); 184 | } 185 | 186 | .formwrapper input { 187 | width: 100%; 188 | padding: 5px; 189 | margin-top: 5px; 190 | margin-bottom: 10px; 191 | 192 | } 193 | 194 | .formwrapper select { 195 | width: 100%; 196 | padding: 5px; 197 | margin-top: 5px; 198 | margin-bottom: 10px; 199 | 200 | } 201 | 202 | .formwrapper label { 203 | margin-top: 20px; 204 | } 205 | 206 | 207 | .cancel{ 208 | float: right; 209 | padding: 10px; 210 | background: none; 211 | color: rgba(0, 145, 255, 1); 212 | border: solid rgba(0, 145, 255, 1); 213 | border-radius: 5px; 214 | min-width: 100px; 215 | margin-right: 5px; 216 | } 217 | 218 | .submitnew{ 219 | float: right; 220 | padding: 10px; 221 | background: rgba(0, 145, 255, 1); 222 | color: white; 223 | border: solid rgba(0, 145, 255, 1); 224 | border-radius: 5px; 225 | min-width: 100px; 226 | margin-left: 5px; 227 | } 228 | 229 | /*activity log page*/ 230 | 231 | .dependentpagecontainer{ 232 | background: whitesmoke; 233 | display: flex; 234 | flex-direction: column; 235 | align-items: baseline; 236 | 237 | } 238 | 239 | .newlogcontainer { 240 | margin-top: 20px; 241 | border: solid rgba(0, 145, 255, 0.69); 242 | border-radius: 10px; 243 | background: rgba(0, 145, 255, 0.1); 244 | width: 700px; 245 | height: 150px; 246 | padding: 20px; 247 | margin: 20px; 248 | 249 | } 250 | 251 | .newlogcontainer h4 { 252 | margin-bottom: 10px; 253 | font-weight: bold; 254 | text-align: left; 255 | color: rgba(0, 145, 255, 0.8); 256 | } 257 | 258 | .logforminputs select{ 259 | margin-right: 20px; 260 | padding: 4px; 261 | border-radius: 5px; 262 | width: 20%; 263 | border: solid thin rgba(0, 145, 255, 0.8); 264 | } 265 | 266 | .logforminputs input{ 267 | /* margin-right: 20px; */ 268 | width: 75%; 269 | padding: 5px; 270 | border-radius: 5px; 271 | border: solid thin rgba(0, 145, 255, 0.8); 272 | } 273 | 274 | .newlogcontainer button { 275 | float: right; 276 | padding: 5px; 277 | background: rgba(0, 145, 255, 1); 278 | color: white; 279 | border: solid rgba(0, 145, 255, 1); 280 | border-radius: 5px; 281 | min-width: 100px; 282 | margin-right: 10px; 283 | margin-top: 10px; 284 | } 285 | 286 | /* individual activity logs style*/ 287 | 288 | .logcomponent { 289 | margin-top: 20px; 290 | border: solid rgba(0, 145, 255, 0.1); 291 | border-radius: 10px; 292 | background: rgba(0, 145, 255, 0.69); 293 | color: whitesmoke; 294 | width: 700px; 295 | height: 100px; 296 | padding: 20px; 297 | margin: 20px; 298 | display: flex; 299 | align-items: center; 300 | /* justify-content: space-between; */ 301 | } 302 | 303 | .notesIcon { 304 | max-width: 50px; 305 | max-height: 50px; 306 | } 307 | 308 | .loginfo { 309 | display: flex; 310 | justify-content: space-around; 311 | align-items: center; 312 | width: 90%; 313 | 314 | 315 | } 316 | 317 | .activity { 318 | border: solid thin whitesmoke; 319 | border-radius: 10px; 320 | margin: 5px; 321 | padding: 10px; 322 | width: 100px; 323 | text-align: center; 324 | background: rgba(0, 145, 255, 0.69); 325 | 326 | 327 | } 328 | 329 | .details { 330 | border: solid thin whitesmoke; 331 | border-radius: 10px; 332 | margin: 5px; 333 | padding: 10px; 334 | width: 400px; 335 | 336 | } 337 | 338 | .time { 339 | width: 225px; 340 | text-align: center; 341 | border: solid thin whitesmoke; 342 | border-radius: 10px; 343 | padding: 10px 5px ; 344 | background: rgba(0, 145, 255, 0.69); 345 | margin: 5px; 346 | 347 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | const HTMLWebpackPlugin = require('html-webpack-plugin'); 4 | 5 | module.exports = { 6 | entry: './src/index.js', 7 | output: { 8 | path: path.resolve(__dirname, 'dist'), 9 | filename: 'bundle.js', 10 | }, 11 | devServer: { 12 | host: 'localhost', 13 | port: 8080, 14 | hot: true, 15 | static: { 16 | directory: path.resolve(__dirname, 'dist') 17 | }, 18 | proxy: [{ 19 | context: ['/api/**'], 20 | target: 'http://localhost:3000/', 21 | }, 22 | ], 23 | }, 24 | plugins: [ 25 | new HTMLWebpackPlugin({ 26 | template: './src/index.html' 27 | }) 28 | ], 29 | module: { 30 | rules: [ 31 | { 32 | test: /\.jsx?/, 33 | exclude: /node_modules/, 34 | use: { 35 | loader: 'babel-loader', 36 | options: { 37 | presets: ['@babel/env', '@babel/react'] 38 | } 39 | } 40 | }, 41 | { 42 | test: /\.s?css$/, 43 | use: [ 'style-loader', 'css-loader', 'sass-loader' ], 44 | }, 45 | 46 | ] 47 | }, 48 | resolve: { 49 | // Enable importing JS / JSX files without specifying their extension 50 | extensions: ['.js', '.jsx'], 51 | }, 52 | 53 | }; --------------------------------------------------------------------------------