├── .DS_Store ├── .babelrc ├── .gitignore ├── LICENSE ├── README.md ├── assets └── random-dog.jpg ├── build └── bundle.js ├── client ├── .DS_Store ├── App.jsx ├── StateProvider.js ├── components │ ├── .DS_Store │ ├── ARCHIVE FILES - PETSPERSECTIOn │ │ ├── .DS_Store │ │ ├── AddPostForm.jsx │ │ ├── Bucketlist.jsx │ │ ├── BucketlistItemDisplay.jsx │ │ ├── CompletedBucketlistItem.jsx │ │ ├── CreateListItem.jsx │ │ ├── Header.jsx │ │ ├── Login.jsx │ │ ├── Signup.jsx │ │ ├── UncompletedBucketlistItem.jsx │ │ ├── feed.jsx │ │ └── profile.jsx │ ├── ChatPage │ │ ├── .DS_Store │ │ ├── ChatBox │ │ │ ├── .DS_Store │ │ │ ├── Chat │ │ │ │ ├── .DS_Store │ │ │ │ ├── ChatBox.jsx │ │ │ │ ├── ChatBoxHeader.jsx │ │ │ │ ├── InputContainer.jsx │ │ │ │ └── Message.jsx │ │ │ └── ChatContainer.jsx │ │ ├── ChatPage.jsx │ │ ├── PartnerInfo │ │ │ ├── PartnerInfo.jsx │ │ │ └── PartnerInfo │ │ │ │ ├── Avatar.jsx │ │ │ │ ├── PartnerActivitiesList.jsx │ │ │ │ ├── PartnerInfoItem.jsx │ │ │ │ └── PartnerPetInfoItem.jsx │ │ └── UserList │ │ │ ├── .DS_Store │ │ │ ├── UserItem │ │ │ └── UserItem.jsx │ │ │ └── UserList.jsx │ ├── Header │ │ ├── .DS_Store │ │ ├── Header.jsx │ │ └── UserInfo │ │ │ └── UserInfo.jsx │ ├── Login │ │ ├── FacebookLogin.jsx │ │ └── Login.jsx │ └── ProfilePage │ │ ├── .DS_Store │ │ ├── Activities │ │ ├── .DS_Store │ │ ├── Activities.jsx │ │ └── ActivityItem │ │ │ ├── .DS_Store │ │ │ └── ActivityItem.jsx │ │ ├── Profile │ │ └── Profile.jsx │ │ └── ProfilePage.jsx ├── index.js ├── main.js ├── styled-items.js └── styles.css ├── index.html ├── package-lock.json ├── package.json ├── server ├── controllers │ ├── MatchesController.js │ └── UserController.js ├── models │ ├── UserModels.js │ └── activitiesModels.js ├── routers │ └── users.js └── server.js ├── utils └── messages.js └── webpack.config.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoffeeAndWoof/Woof/275aba4dded583aa8fef595a543dcc88776735da/.DS_Store -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-react" 5 | ], 6 | "plugins": [ 7 | [ 8 | "@babel/plugin-proposal-class-properties", 9 | { 10 | "loose": true 11 | } 12 | ] 13 | ] 14 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # yeti-crab-professionals 2 | Coffee&Woof 3 | * An activities match dating site for dogs! 4 | (1) Login with your facebook picture! 5 | 6 | 7 | 8 | Routes 9 | 10 | /api/login 11 | * POST 12 | * request - FB login object 13 | * response - object with user data 14 | 15 | /api/getUserData/userID 16 | * GET 17 | * put userID string from mongo in params 18 | * response - object with user data 19 | 20 | /api/updateUserData/userID 21 | * PUT 22 | * put userID string from mongo in params 23 | * request - user object with updated user data 24 | * response - object with user data 25 | 26 | 27 | ******* STRETCH FEATURE ******* 28 | 29 | /api/deleteUser/userID 30 | * DELETE 31 | * put userID string from mongo in params 32 | -------------------------------------------------------------------------------- /assets/random-dog.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoffeeAndWoof/Woof/275aba4dded583aa8fef595a543dcc88776735da/assets/random-dog.jpg -------------------------------------------------------------------------------- /client/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoffeeAndWoof/Woof/275aba4dded583aa8fef595a543dcc88776735da/client/.DS_Store -------------------------------------------------------------------------------- /client/App.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, useEffect } from 'react'; 2 | import { 3 | BrowserRouter as Router, 4 | Route, 5 | Switch, 6 | Redirect, 7 | } from 'react-router-dom'; 8 | import styled from 'styled-components'; 9 | 10 | import Header from './components/Header/Header.jsx'; 11 | import Login from './components/Login/Login.jsx'; 12 | import ProfilePage from './components/ProfilePage/ProfilePage.jsx'; 13 | import { ChatPage } from './components/ChatPage/ChatPage.jsx'; 14 | import { StateProvider, useStateValue } from './StateProvider'; 15 | import styledItems from './styled-items'; 16 | 17 | const RouterDiv = styled.div` 18 | width: 85%; 19 | background-color: ${styledItems.white}; 20 | min-height: 1000px; 21 | display: flex; 22 | justify-content: center; 23 | `; 24 | 25 | const StyledDiv = styled.div` 26 | width: 100%; 27 | display: flex; 28 | flex-direction: column; 29 | align-items: center; 30 | `; 31 | 32 | const App = () => { 33 | // define initialState here as an object 34 | const initialState = { 35 | userInfo: { 36 | _id: 0, 37 | fullName: '', 38 | firstName: '', 39 | location: '', 40 | age: '', 41 | avatarUrl: '', 42 | activities: {}, 43 | email: '', 44 | }, 45 | petInfo: { 46 | name: '', 47 | age: '', 48 | breed: '', 49 | size: '', 50 | avatarUrl: '', 51 | }, 52 | partnerInfo: { 53 | _id: 564345532, 54 | fullName: 'Gary Slootskiy', 55 | firstName: 'Gary', 56 | location: 'New York, NY', 57 | age: '30', 58 | avatarUrl: '', 59 | activities: {}, 60 | petInfo: { 61 | name: 'Tully', 62 | age: '7', 63 | breed: 'Labrador', 64 | size: 'Medium', 65 | avatarUrl: '', 66 | }, 67 | }, 68 | chatItems: {}, 69 | matchList: [], 70 | mainMatch: false, 71 | loggedIn: false, 72 | }; 73 | 74 | const reducer = (state, action) => { 75 | let userInfo; 76 | let petInfo; 77 | 78 | switch (action.type) { 79 | case 'clickLogin': 80 | userInfo = Object.assign({}, state.userInfo); 81 | petInfo = Object.assign({}, state.petInfo); 82 | userInfo._id = action.id; 83 | userInfo.fullName = action.full_name; 84 | userInfo.firstName = action.first_name; 85 | userInfo.email = action.email; 86 | userInfo.avatarUrl = action.profile_img; 87 | userInfo.age = action.user_age; 88 | userInfo.location = action.location; 89 | petInfo.name = action.dog_name; 90 | petInfo.avatarUrl = action.dog_image; 91 | petInfo.age = action.dog_age; 92 | petInfo.size = action.dog_size; 93 | petInfo.breed = action.dog_breed; 94 | userInfo.activities = {}; 95 | action.preferred_activities.forEach((activity) => { 96 | userInfo.activities[activity.activity] = activity.description; 97 | }); 98 | // console.log('userInfo:',userInfo); 99 | // console.log('petInfo:',petInfo); 100 | return { 101 | ...state, 102 | userInfo, 103 | petInfo, 104 | loggedIn: true, 105 | }; 106 | 107 | case 'addActivity': 108 | userInfo = Object.assign({}, state.userInfo); 109 | userInfo.activities[action.activity] = `i like ${action.activity}`; 110 | return { 111 | ...state, 112 | userInfo, 113 | }; 114 | 115 | case 'removeActivity': 116 | userInfo = Object.assign({}, state.userInfo); 117 | delete userInfo.activities[action.activity]; 118 | return { 119 | ...state, 120 | userInfo, 121 | }; 122 | 123 | case 'updateMatches': 124 | userInfo = Object.assign({}, state.userInfo); 125 | petInfo = Object.assign({}, state.petInfo); 126 | userInfo.location = action.userInfo.location; 127 | userInfo.age = action.userInfo.age; 128 | userInfo.activities = action.userInfo.activities; 129 | petInfo.name = action.petInfo.name; 130 | petInfo.age = action.petInfo.age; 131 | petInfo.breed = action.petInfo.breed; 132 | petInfo.size = action.petInfo.size; 133 | petInfo.avatarUrl = action.petInfo.avatarUrl; 134 | console.log("save profile"); 135 | 136 | let matchList = action.matchList; 137 | console.log('Hello',matchList); 138 | return { 139 | ...state, 140 | matchList, 141 | userInfo, 142 | petInfo, 143 | }; 144 | 145 | case 'saveProfile': 146 | userInfo = Object.assign({}, state.userInfo); 147 | petInfo = Object.assign({}, state.petInfo); 148 | userInfo.location = action.userInfo.location; 149 | userInfo.age = action.userInfo.age; 150 | userInfo.activities = action.userInfo.activities; 151 | petInfo.name = action.petInfo.name; 152 | petInfo.age = action.petInfo.age; 153 | petInfo.breed = action.petInfo.breed; 154 | petInfo.size = action.petInfo.size; 155 | petInfo.avatarUrl = action.petInfo.avatarUrl; 156 | console.log("save profile"); 157 | return { 158 | ...state, 159 | userInfo, 160 | petInfo, 161 | }; 162 | 163 | default: 164 | return state; 165 | } 166 | }; 167 | 168 | return ( 169 | // CONTEXT API: everything inside of StateProvider will now be able to access state 170 | 171 | 172 | 173 |
174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | ); 185 | }; 186 | 187 | export default App; 188 | -------------------------------------------------------------------------------- /client/StateProvider.js: -------------------------------------------------------------------------------- 1 | import React, {Component, createContext, useContext, useReducer} from 'react'; 2 | 3 | // CONTEXT API CODE 4 | export const StateContext = createContext(); 5 | 6 | // result of useReducer hook is passed on to the Provider => 7 | // ...so it becomes available in any component in the app component tree 8 | export const StateProvider = ({reducer, initialState, children}) =>( 9 | 10 | {children} 11 | 12 | ); 13 | 14 | // callback function so we can shorten our useContext call.... 15 | export const useStateValue = () => useContext(StateContext); 16 | -------------------------------------------------------------------------------- /client/components/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoffeeAndWoof/Woof/275aba4dded583aa8fef595a543dcc88776735da/client/components/.DS_Store -------------------------------------------------------------------------------- /client/components/ARCHIVE FILES - PETSPERSECTIOn/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoffeeAndWoof/Woof/275aba4dded583aa8fef595a543dcc88776735da/client/components/ARCHIVE FILES - PETSPERSECTIOn/.DS_Store -------------------------------------------------------------------------------- /client/components/ARCHIVE FILES - PETSPERSECTIOn/AddPostForm.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function AddPostForm(props) { 4 | return ( 5 |
6 |

{props.state.listItem}

7 | 8 | 14 | 15 | 21 | 22 | 23 | 24 | 29 | 30 | 36 | 37 | 40 |
41 | ); 42 | 43 | } 44 | 45 | export default AddPostForm; 46 | -------------------------------------------------------------------------------- /client/components/ARCHIVE FILES - PETSPERSECTIOn/Bucketlist.jsx: -------------------------------------------------------------------------------- 1 | import React, {useState, useEffect} from "react"; 2 | import BucketlistItemDisplay from "./BucketlistItemDisplay.jsx"; 3 | import CreateListItem from "./CreateListItem.jsx"; 4 | import Header from "./Header.jsx"; 5 | 6 | function Bucketlist(props) { 7 | const [newItem, setNewItem] = useState(""); 8 | const [bucketlistItems, setBucketlistItems] = useState([]); 9 | 10 | useEffect(() => { 11 | fetch("/api/listItems") 12 | .then((res) => res.json()) 13 | .then((data) => { 14 | setBucketlistItems(data); 15 | console.log(bucketlistItems.concat(...data)); 16 | }); 17 | }, []); 18 | 19 | 20 | function handleCheckedOffClick(listItem) { 21 | const updatedItem = { 22 | listItem: listItem, 23 | isChecked: true, 24 | hasPost: false 25 | } 26 | 27 | fetch(`/api/listItems/${listItem}`,{ 28 | method: 'PUT', 29 | headers: { 30 | 'Content-Type': 'application/json' 31 | }, 32 | body: JSON.stringify(updatedItem) 33 | }) 34 | .then(res => res.json()) 35 | .then(listItemData => { 36 | console.log("",listItemData) 37 | console.log("PRE",bucketlistItems) 38 | fetch('/api/listItems') 39 | .then(res => res.json()) 40 | .then(data => { 41 | setBucketlistItems(data) 42 | console.log("POST",bucketlistItems) 43 | 44 | }) 45 | 46 | 47 | // setState(state.isChecked = true, state.hasPost = false) 48 | }) 49 | .catch(error => { 50 | console.log(error) 51 | }) 52 | } 53 | 54 | function handleNewItemClick() { 55 | const newListItem = { 56 | listItem: newItem, 57 | }; 58 | 59 | fetch(`/api/listItems`, { 60 | method: "POST", 61 | headers: { 62 | "Content-Type": "application/json", 63 | }, 64 | body: JSON.stringify(newListItem), 65 | }) 66 | .then((res) => res.json()) 67 | .then((data) => { 68 | setNewItem(""); 69 | fetch("/api/listItems") 70 | .then((res) => res.json()) 71 | .then((data) => { 72 | setBucketlistItems(data); 73 | console.log(bucketlistItems.concat(...data)); 74 | }); 75 | }); 76 | 77 | } 78 | 79 | function handleNewItemChange(e) { 80 | e.preventDefault(); 81 | let newItem = e.target.value; 82 | 83 | return setNewItem(newItem); 84 | } 85 | 86 | return ( 87 | <> 88 |
89 |
90 | 95 | {bucketlistItems.map((item) => { 96 | return ; 97 | })} 98 |
99 | 100 | ); 101 | 102 | } 103 | 104 | export default Bucketlist; 105 | -------------------------------------------------------------------------------- /client/components/ARCHIVE FILES - PETSPERSECTIOn/BucketlistItemDisplay.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useState, useEffect } from 'react' 3 | import AddPostForm from './AddPostForm.jsx' 4 | import CompletedBucketlistItem from './CompletedBucketlistItem.jsx' 5 | import UncompletedBucketlistItem from './UncompletedBucketlistItem.jsx' 6 | 7 | function BucketlistItemDisplay(props) { 8 | const [state, setState] = useState(props.item); 9 | const [newPost, setNewPost] = useState('') 10 | 11 | const [images, setImages] = useState([]) 12 | const [imagesChange, setImagesChange] = useState('') 13 | 14 | const [date, setDateChange] = useState('') 15 | const [postDescription, setPostDescription] = useState('') 16 | const [YTLink, setYTLink] = useState('') 17 | const [GMLink, setGMLink] = useState('') 18 | 19 | 20 | 21 | // function handleCheckedOffClick(listItem) { 22 | // const updatedItem = { 23 | // listItem: listItem, 24 | // isChecked: true, 25 | // hasPost: false 26 | // } 27 | 28 | // fetch(`/api/listItems/${listItem}`,{ 29 | // method: 'PUT', 30 | // headers: { 31 | // 'Content-Type': 'application/json' 32 | // }, 33 | // body: JSON.stringify(updatedItem) 34 | // }) 35 | // .then(res => res.json()) 36 | // .then(data => { 37 | 38 | // // setState(state.isChecked = true, state.hasPost = false) 39 | // }) 40 | // .catch(error => { 41 | // console.log(error) 42 | // }) 43 | // } 44 | 45 | function handleDateChange(e) { 46 | e.preventDefault() 47 | const newValue = e.target.value; 48 | setDateChange(newValue) 49 | } 50 | 51 | function handleBodyOfPostChange(e) { 52 | e.preventDefault() 53 | const newValue = e.target.value; 54 | 55 | setPostDescription(newValue) 56 | } 57 | 58 | function handleYoutubeURLChange(e) { 59 | e.preventDefault() 60 | const newValue = e.target.value; 61 | 62 | setYTLink(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 | // setGMLink(newValue) 71 | // } 72 | 73 | function handleSubmitPostClick(listItem) { 74 | const fullLink = `https://www.google.com/maps/embed/v1/place?key=AIzaSyCyw8Q7SqmZ8RbKT6HgInw5Bcp93emrlNU&q=${GMLink}` 75 | 76 | console.log("LIST ITEM!!!!",listItem) 77 | const newPost = { 78 | listItem: listItem, 79 | dateCompleted: date, 80 | postDescription: postDescription, 81 | youtubeLink: YTLink, 82 | location: fullLink, 83 | images: images 84 | } 85 | 86 | fetch('/api/posts/', { 87 | method: 'POST', 88 | headers: { 89 | 'Content-Type': 'application/json' 90 | }, 91 | body: JSON.stringify(newPost) 92 | }) 93 | .then(res => res.json()) 94 | .then(data => { 95 | 96 | console.log(data) 97 | // setState(...state, state.hasPost:true) 98 | 99 | const updatedItem = { 100 | listItem: listItem, 101 | isChecked: true, 102 | hasPost: true 103 | } 104 | 105 | fetch(`/api/listItems/${listItem}`, { 106 | method: 'PUT', 107 | headers: { 108 | 'Content-Type': 'application/json' 109 | }, 110 | body: JSON.stringify(updatedItem) 111 | }) 112 | .then(res => res.json()) 113 | .then(data => { 114 | 115 | fetch('/api/listItems') 116 | .then(res => res.json()) 117 | .then(data => { 118 | setState(data) 119 | console.log("POST",bucketlistItems) 120 | 121 | }) 122 | console.log('Have updated list item, HAS POST is TRUE') 123 | }) 124 | 125 | }) 126 | .catch(error => { 127 | console.log(error) 128 | }) 129 | 130 | } 131 | 132 | function handleGoogleLinkChange(e) { 133 | e.preventDefault() 134 | const newValue = e.target.value; 135 | 136 | setGMLink(newValue) 137 | 138 | } 139 | 140 | function handleAddImagesChange(e){ 141 | e.preventDefault() 142 | const newValue = e.target.value; 143 | setImagesChange(newValue) 144 | } 145 | 146 | function handleAddImagesClick() { 147 | setImages([...images, imagesChange]) 148 | // setImagesChange('') 149 | 150 | } 151 | 152 | // function handleDeleteClick(listItem) { 153 | 154 | // } 155 | 156 | 157 | // console.log(state) 158 | if (state.isChecked && !state.hasPost) { 159 | // console.log('THIS IS STATE',state) 160 | 161 | return ( 162 |
163 | 168 | 185 |
186 | ) 187 | } else if (state.isChecked) { 188 | return ( 189 | 195 | ) 196 | } else { 197 | 198 | return ( 199 | 205 | ) 206 | } 207 | 208 | } 209 | 210 | export default BucketlistItemDisplay; 211 | 212 | 213 | -------------------------------------------------------------------------------- /client/components/ARCHIVE FILES - PETSPERSECTIOn/CompletedBucketlistItem.jsx: -------------------------------------------------------------------------------- 1 | import React, {useState} from "react"; 2 | 3 | function CompletedBucketlistItem(props) { 4 | const [displayPost, setDisplayPost] = useState(false); 5 | const [postData, setPostData] = useState({}); 6 | 7 | function showPost(listItem) { 8 | console.log(listItem); 9 | fetch(`/api/posts/${listItem}`) 10 | .then((res) => res.json()) 11 | .then((data) => { 12 | // console.log("POST DATQ",data) 13 | setPostData(data); 14 | setDisplayPost(true); 15 | }); 16 | } 17 | 18 | if (!displayPost) { 19 | return ( 20 |
21 |
22 |

{props.listItem}

23 |

{props.dateCompleted}

24 | 25 | 26 |
27 |
28 | ); 29 | } else { 30 | return ( 31 |
32 |

{postData.listItem}

33 |
{postData.dateCompleted}
34 |

{postData.postDescription}

35 | {console.log("POST DATAaaa", postData)} 36 | {postData.images.map((image, index) => { 37 | return ; 38 | })} 39 | 44 | 45 |
46 | ); 47 | } 48 | } 49 | 50 | export default CompletedBucketlistItem; 51 | -------------------------------------------------------------------------------- /client/components/ARCHIVE FILES - PETSPERSECTIOn/CreateListItem.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function CreateListItem(props) { 4 | return ( 5 |
6 | 7 | 13 | 16 |
17 | ); 18 | } 19 | 20 | export default CreateListItem; 21 | -------------------------------------------------------------------------------- /client/components/ARCHIVE FILES - PETSPERSECTIOn/Header.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {Link} from "react-router-dom"; 3 | 4 | const Header = (props) => { 5 | return ( 6 |
7 | 17 |
18 | ); 19 | }; 20 | export default Header; 21 | -------------------------------------------------------------------------------- /client/components/ARCHIVE FILES - PETSPERSECTIOn/Login.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { Redirect } from "react-router-dom"; 3 | import { Link } from "react-router-dom"; 4 | import "../styles.css"; 5 | 6 | class Login extends Component { 7 | constructor() { 8 | super(); 9 | this.state = { 10 | loggedIn: false, 11 | failedLogin: false, 12 | //object -- all the info 13 | }; 14 | 15 | this.validate = this.validate.bind(this); 16 | } 17 | 18 | validate() { 19 | const username = document.getElementById("username").value; 20 | const password = document.getElementById("password").value; 21 | 22 | const body = { 23 | username, 24 | password, 25 | }; 26 | 27 | // console.log("This is outside of the POST", body) 28 | 29 | fetch("/api/pet/login", { 30 | method: "POST", 31 | headers: { 32 | "Content-Type": "application/json", 33 | }, 34 | body: JSON.stringify(body), 35 | }) 36 | .then((res) => res.json()) 37 | .then((data) => { 38 | this.setState({ 39 | loggedIn: true, 40 | }); 41 | }) 42 | .catch((err) => console.log(`Error: ${err} `)); 43 | } 44 | 45 | render() { 46 | if (this.state.loggedIn) { 47 | return ; 48 | } 49 | 50 | return ( 51 |
52 |

Petrospective!

53 |
54 |
55 | 56 | 61 |
62 |
63 | 64 | 69 |
70 | 76 | Sign up here! 77 |
78 |
79 | ); 80 | } 81 | } 82 | 83 | export default Login; 84 | -------------------------------------------------------------------------------- /client/components/ARCHIVE FILES - PETSPERSECTIOn/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 | validate() { 17 | const bio = document.getElementById("bio").value; 18 | const username = document.getElementById("signupUsername").value; 19 | const password = document.getElementById("signupPassword").value; 20 | const name = document.getElementById("name").value; 21 | const age = document.getElementById("age").value; 22 | const profilePicture = "this is a src for am img"; 23 | 24 | const body = { 25 | username, 26 | password, 27 | profilePicture, 28 | age, 29 | bio, 30 | name, 31 | }; 32 | 33 | fetch("/api/pet/signup", { 34 | method: "POST", 35 | headers: { 36 | "Content-Type": "application/json", 37 | }, 38 | body: JSON.stringify(body), 39 | }) 40 | .then((res) => { 41 | console.log(res); 42 | }) 43 | .then((data) => { 44 | this.setState({ 45 | loggedIn: true, 46 | }); 47 | }) 48 | .catch((err) => console.log(`Error: ${err} `)); 49 | } 50 | 51 | render() { 52 | if (this.state.loggedIn) { 53 | return ; 54 | } 55 | 56 | return ( 57 |
58 |

Woof!

59 |
60 |
61 | 62 | 63 |
64 |
65 | 66 | 67 |
68 |
69 | 70 | 75 |
76 |
77 | 78 | 83 |
84 |
85 | 86 | 93 |
94 | 100 |
101 |
102 | ); 103 | } 104 | } 105 | 106 | export default Signup; 107 | -------------------------------------------------------------------------------- /client/components/ARCHIVE FILES - PETSPERSECTIOn/UncompletedBucketlistItem.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function UncompletedBucketlistItem(props) { 4 | return ( 5 |
6 |

{props.listItem}

7 | props.handleCheckedOffClick(props.listItem)} 9 | type='checkbox' 10 | /> 11 |
12 | ); 13 | 14 | } 15 | 16 | export default UncompletedBucketlistItem; 17 | -------------------------------------------------------------------------------- /client/components/ARCHIVE FILES - PETSPERSECTIOn/feed.jsx: -------------------------------------------------------------------------------- 1 | import React, {useState, useEffect} from "react"; 2 | import {BrowserRouter as Router, Route, Switch} from "react-router-dom"; 3 | import Header from "./Header.jsx"; 4 | 5 | const Feed = (props) => { 6 | const [posts, setPosts] = useState([]); 7 | 8 | useEffect(() => { 9 | fetch("http://localhost:3000/api/posts") 10 | .then((res) => res.json()) 11 | .then((completeItems) => { 12 | setPosts(posts.concat(...completeItems)); 13 | }); 14 | }, []); 15 | 16 | return ( 17 |
18 |
19 | {/*
*/} 20 | {posts.map((post) => { 21 | { 22 | console.log(post); 23 | } 24 | return ; 25 | })} 26 |
27 | ); 28 | }; 29 | 30 | //API_KEY = AIzaSyCyw8Q7SqmZ8RbKT6HgInw5Bcp93emrlNU 31 | function RenderPost(props) { 32 | return ( 33 | <> 34 |
35 |
36 |

{props.post.listItem}

37 |
{props.post.dateCompleted}
38 |
39 | 40 | 45 |
46 |

{props.post.postDescription}

47 | 51 | {props.post.images.map((image) => { 52 | return ( 53 | image not loaded 58 | ); 59 | })} 60 |
61 |
62 | 63 | ); 64 | } 65 | 66 | export default Feed; 67 | -------------------------------------------------------------------------------- /client/components/ARCHIVE FILES - PETSPERSECTIOn/profile.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { Redirect } from "react-router-dom"; 3 | import { Link } from "react-router-dom"; 4 | import "../styles.css"; 5 | 6 | const Profile = (props) => { 7 | return ( 8 |
9 |
10 |

Your Profile

11 |
12 |

Line 1

13 |

Line 2

14 |

Line 3

15 |
16 |
17 |
18 | ); 19 | } 20 | 21 | export default Profile; -------------------------------------------------------------------------------- /client/components/ChatPage/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoffeeAndWoof/Woof/275aba4dded583aa8fef595a543dcc88776735da/client/components/ChatPage/.DS_Store -------------------------------------------------------------------------------- /client/components/ChatPage/ChatBox/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoffeeAndWoof/Woof/275aba4dded583aa8fef595a543dcc88776735da/client/components/ChatPage/ChatBox/.DS_Store -------------------------------------------------------------------------------- /client/components/ChatPage/ChatBox/Chat/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoffeeAndWoof/Woof/275aba4dded583aa8fef595a543dcc88776735da/client/components/ChatPage/ChatBox/Chat/.DS_Store -------------------------------------------------------------------------------- /client/components/ChatPage/ChatBox/Chat/ChatBox.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | import styled from 'styled-components'; 3 | import styledItems from '../../../../styled-items'; 4 | 5 | export const ChatBoxStyle = styled.div` 6 | border: 1px solid #5AC4FF; 7 | background-color: ${styledItems.secondaryBlue}; 8 | min-height: 60%; 9 | max-height: 60%; 10 | overflow: auto; 11 | margin: 1rem; 12 | border-radius: 5px; 13 | `; 14 | 15 | export function ChatBox({ chat }) { 16 | console.log({chat}); 17 | return ( 18 | 19 |
{chat}
20 |
21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /client/components/ChatPage/ChatBox/Chat/ChatBoxHeader.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | // CONTEXT API IMPORT 4 | import { useStateValue , StateContext , createContext} from '../../../../StateProvider'; 5 | import styledItems from '../../../../styled-items'; 6 | 7 | const StyledHeader = styled.div` 8 | color: ${styledItems.darkGray}; 9 | font-size: 1.5em; 10 | display: flex; 11 | justify-content: flex-end; 12 | align-items: center; 13 | `; 14 | 15 | const StyledImg = styled.img` 16 | width: 36px; 17 | height: 36px; 18 | margin: 10px; 19 | border-radius: 18px; 20 | border: 1px solid ${styledItems.darkGray} 21 | `; 22 | 23 | export function ChatBoxHeader(props) { 24 | const selectedPartner = props.selectedPartner; 25 | const nameString = `${selectedPartner.firstName} & ${selectedPartner.petInfo.name}`; 26 | const userAvatar = selectedPartner.avatarUrl; 27 | const petAvatar = selectedPartner.petInfo.avatarUrl; 28 | return {nameString}; 29 | } 30 | -------------------------------------------------------------------------------- /client/components/ChatPage/ChatBox/Chat/InputContainer.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import styledItems from '../../../../styled-items'; 4 | 5 | export const InputStyle = styled.div` 6 | text-align: center; 7 | margin: .7em; 8 | `; 9 | 10 | const StyledInput = styled.input` 11 | padding: 10px; 12 | width: 80%; 13 | `; 14 | 15 | const StyledButton = styled.button` 16 | background-color: #5AC4FF; 17 | color: #fff; 18 | height: 2rem; 19 | width: 4rem; 20 | border-radius: 5px; 21 | border: 1px solid #2cb4fd; 22 | margin-left: 1rem; 23 | box-shadow: 1px 1px 1px #5AC4FF; 24 | &:hover { 25 | cursor: pointer; 26 | background-color: ${styledItems.lightGray}; 27 | color: ${styledItems.darkGray}; 28 | } 29 | `; 30 | 31 | export function InputContainer() { 32 | return ( 33 | 34 |
35 |
36 | 37 | Woof! 38 | 39 |
40 |
41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /client/components/ChatPage/ChatBox/Chat/Message.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | // CONTEXT API IMPORT 4 | import { useStateValue , StateContext , createContext} from '../../../../StateProvider'; 5 | import styledItems from '../../../../styled-items'; 6 | 7 | const StyledText = styled.div` 8 | color: ${styledItems.darkGray}; 9 | font-size: 1.2em; 10 | display: flex; 11 | justify-content: flex-end; 12 | align-items: center; 13 | `; 14 | 15 | const StyledPartnerText = styled.div` 16 | color: ${styledItems.darkGray}; 17 | font-size: 1.2em; 18 | display: flex; 19 | justify-content: flex-start; 20 | align-items: center; 21 | `; 22 | 23 | const StyledImg = styled.img` 24 | width: 30px; 25 | height: 30px; 26 | margin: 6px; 27 | border-radius: 15px; 28 | border: 1px solid ${styledItems.darkGray} 29 | `; 30 | 31 | const StyledTime = styled.span` 32 | font-size: .08em; 33 | color: ${styledItems.lightGray}; 34 | `; 35 | 36 | 37 | export function Message({ messageInfo, selectedPartner, currentId }) { 38 | const [{ userInfo , petInfo }, dispatch] = useStateValue(); 39 | const timeString = messageInfo.time 40 | const textString = `${messageInfo.text}`; 41 | const partnerAvatar = selectedPartner.avatarUrl; 42 | const partnerPetAvatar = selectedPartner.petInfo.avatarUrl; 43 | const userAvatar = userInfo.avatarUrl; 44 | const userPetAvatar = petInfo.avatarUrl; 45 | 46 | let userItem = {userInfo.firstName}: {textString}{timeString}; 47 | let partnerItem = {timeString}{selectedPartner.firstName}: {textString} 48 | let chatItem = userItem; 49 | if (messageInfo.userID != currentId) chatItem = partnerItem; 50 | return ( 51 | //
52 | //

{messageInfo.username}

53 | // {messageInfo.time} 54 | //

{messageInfo.text}

55 | //
56 | 57 |
{chatItem}
58 | 59 | ); 60 | 61 | 62 | // return {nameString}; 63 | } 64 | 65 | 66 | {/* ) 37 | } 38 | setComponentMessages(messageList); 39 | // const div = document.createElement('div'); 40 | // div.classList.add('message'); 41 | // div.innerHTML = `

${message.username} ${message.time}

${message.text}

`; 42 | // document.querySelector('.chat-messages').appendChild(div); 43 | } 44 | 45 | 46 | 47 | useEffect(() => { 48 | const chatForm = document.getElementById('chat-form'); 49 | const chatMessages = document.querySelector('.chat-box'); 50 | 51 | const socket = io.connect('http://localhost:3000'); 52 | 53 | // Message from server 54 | 55 | socket.on('message', (message) => { 56 | console.log("message: ", message); 57 | outputMessage(message); 58 | 59 | // Scroll down 60 | chatMessages.scrollTop = chatMessages.scrollHeight; 61 | }); 62 | 63 | chatForm.addEventListener('submit', (e) => { 64 | e.preventDefault(); 65 | 66 | // Get message text 67 | const msg = e.target.elements.msg.value; 68 | 69 | const send = formatMessage(userInfo.firstName, msg, userInfo._id); 70 | console.log(send); 71 | // Emitting a message to the server 72 | socket.emit('chatMessage', send); 73 | 74 | // Clear input 75 | e.target.elements.msg.value = ''; 76 | e.target.elements.msg.focus(); 77 | }); 78 | }, []); 79 | 80 | return ( 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | ); 92 | } 93 | -------------------------------------------------------------------------------- /client/components/ChatPage/ChatPage.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | import { UserList } from './UserList/UserList.jsx'; 5 | import { ChatContainer } from './ChatBox/ChatContainer.jsx'; 6 | import { useStateValue , StateContext } from '../../StateProvider'; 7 | 8 | const ChatPageStyle = styled.div` 9 | display: flex; 10 | min-height: 80%; 11 | min-width: 100%; 12 | margin: 0 10px; 13 | `; 14 | 15 | export function ChatPage() { 16 | //use context to pull down matchlist 17 | const [{ userInfo, petInfo, matchList}, dispatch] = useStateValue(); 18 | console.log("MATCHLIST HERE, ", matchList); 19 | // const matchList = [ 20 | // { 21 | // _id: 564345532, 22 | // firstName: 'Gary', 23 | // location: 'Los Angeles, CA', 24 | // age: '28', 25 | // avatarUrl: 'https://picsum.photos/id/117/300/150', 26 | // activities: { 27 | // coffee: 'i like starbucks', 28 | // hiking: 'not much but sure', 29 | // walking: 'why?', 30 | // }, 31 | // petInfo: { 32 | // name: 'Chico', 33 | // age: '2', 34 | // breed: 'Pitbull Terrier Mix', 35 | // size: 'Medium', 36 | // avatarUrl: 'https://picsum.photos/id/237/300/150/', 37 | // }, 38 | // }, 39 | // { 40 | // _id: 1007, 41 | // firstName: 'Kevin', 42 | // location: 'Los Angeles, CA', 43 | // age: '28', 44 | // avatarUrl: 'https://picsum.photos/id/197/300/150/', 45 | // activities: { 46 | // xcv: 'i like starbucks', 47 | // asdf: 'test', 48 | // }, 49 | // petInfo: { 50 | // name: 'Chachi', 51 | // age: '2', 52 | // breed: 'Pitbull Terrier Mix', 53 | // size: 'Medium', 54 | // avatarUrl: 'https://picsum.photos/id/222/300/150/', 55 | // }, 56 | // }, 57 | // { 58 | // _id: 3001, 59 | // firstName: 'John', 60 | // location: 'Los Angeles, CA', 61 | // age: '28', 62 | // avatarUrl: 'https://picsum.photos/id/111/300/150/', 63 | // activities: { asdf: 'i like starbucks' }, 64 | // petInfo: { 65 | // name: 'Luna', 66 | // age: '2', 67 | // breed: 'Kitty Kat', 68 | // size: 'Tiny thang', 69 | // avatarUrl: 'https://picsum.photos/id/217/300/150/', 70 | // }, 71 | // }, 72 | // ]; 73 | 74 | const [selectedPartner, setSelectedPartner] = useState(null); 75 | if (selectedPartner) { 76 | return ( 77 | 78 | 83 | 84 | 85 | ); 86 | } else { 87 | return ( 88 | 89 | 94 | {/* empty divs for placeholders for styling */} 95 |
96 |
97 |
98 | ); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /client/components/ChatPage/PartnerInfo/PartnerInfo.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import { Avatar } from './PartnerInfo/Avatar.jsx'; 4 | import { PartnerActivitiesList } from './PartnerInfo/PartnerActivitiesList.jsx'; 5 | import { PartnerInfoItem } from './PartnerInfo/PartnerInfoItem.jsx'; 6 | import { PartnerPetInfoItem } from './PartnerInfo/PartnerPetInfoItem.jsx'; 7 | 8 | const PartnerInfoStyle = styled.div` 9 | min-height: 100%; 10 | min-width: 40%; 11 | display: flex; 12 | flex-direction: column; 13 | align-items: center; 14 | border: 2px solid #aedfff; 15 | `; 16 | 17 | export function PartnerInfo({ selectedPartner }) { 18 | const { 19 | firstName, 20 | location, 21 | age, 22 | // avatarUrl, 23 | activities, 24 | petInfo, 25 | } = selectedPartner; 26 | const partnerInfo = { firstName, location, age }; 27 | console.log("activity", activities); 28 | 29 | return ( 30 | 31 | 32 | {/* */} 33 | 34 | {/* */} 35 | 36 | 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /client/components/ChatPage/PartnerInfo/PartnerInfo/Avatar.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | const AvatarStyles = styled.div` 5 | display: flex; 6 | justify-content: center; 7 | align-items: center; 8 | min-width: 90%; 9 | width: 90%: 10 | min-height: 200px; 11 | max-height: 150px; 12 | padding: 10px; 13 | transition: transform 0.4s; 14 | `; 15 | 16 | const AvatarImgStyles = styled.img` 17 | max-width: 50px; 18 | max-height: 50px; 19 | 20 | &:hover { 21 | transform: scale(1.4) rotate(0.1deg); 22 | transition: 0.6s ease-in; 23 | cursor: pointer; 24 | } 25 | `; 26 | 27 | export function Avatar({ url }) { 28 | return ( 29 | 30 | 31 | 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /client/components/ChatPage/PartnerInfo/PartnerInfo/PartnerActivitiesList.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | const PartnerActivitiesListStyles = styled.div` 5 | display: flex; 6 | flex-direction: column; 7 | align-items: center; 8 | width: 70%; 9 | height: 150px; 10 | background-color: #5ac4ff; 11 | margin-top: 10px; 12 | overflow: auto; 13 | margin: 10px; 14 | 15 | h3 { 16 | margin-top: 10px; 17 | } 18 | 19 | div { 20 | display: flex; 21 | width: 100%; 22 | max-width: 100%; 23 | justify-content: space-evenly; 24 | white-space: wrap; 25 | 26 | div { 27 | margin: 5px 0; 28 | } 29 | } 30 | `; 31 | 32 | export function PartnerActivitiesList({ activities }) { 33 | const activitiesJSX = []; 34 | for (const activity in activities) { 35 | activitiesJSX.push( 36 |
37 |
{formatActivity(activity)}:
38 |
{activities[activity]}
39 |
40 | ); 41 | } 42 | return ( 43 | 44 |

Activities:

45 | {activitiesJSX} 46 |
47 | ); 48 | } 49 | 50 | function formatActivity(str) { 51 | return str[0].toUpperCase() + str.slice(1).toLowerCase(); 52 | } 53 | -------------------------------------------------------------------------------- /client/components/ChatPage/PartnerInfo/PartnerInfo/PartnerInfoItem.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | const PartnerInfoItemStyles = styled.div` 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: center; 8 | align-items: center; 9 | width: 70%; 10 | height: 150px; 11 | background-color: #5ac4ff; 12 | overflow: scroll; 13 | margin: 10px; 14 | `; 15 | 16 | export function PartnerInfoItem({ partnerInfo }) { 17 | const { firstName, age, location } = partnerInfo; 18 | return ( 19 | 20 |

{firstName}

21 |

Location: {location}

22 |

Age: {age} years old

23 |
24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /client/components/ChatPage/PartnerInfo/PartnerInfo/PartnerPetInfoItem.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | const PartnerPetInfoStyles = styled.div` 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: center; 8 | align-items: center; 9 | width: 70%; 10 | height: 150px; 11 | background-color: #5ac4ff; 12 | overflow: scroll; 13 | margin: 10px; 14 | `; 15 | 16 | export function PartnerPetInfoItem({ petInfo }) { 17 | const { name, age, breed, size } = petInfo; 18 | return ( 19 | 20 |

{name}

21 |

Age: {age} years old

22 |

Breed: {breed}

23 |

Size: {size}

24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /client/components/ChatPage/UserList/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoffeeAndWoof/Woof/275aba4dded583aa8fef595a543dcc88776735da/client/components/ChatPage/UserList/.DS_Store -------------------------------------------------------------------------------- /client/components/ChatPage/UserList/UserItem/UserItem.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | 4 | const UserItemStyle = styled.div` 5 | display: flex; 6 | align-items: center; 7 | border: 1px solid #acacac; 8 | margin: 5px; 9 | min-height: 60px; 10 | max-height: 60px; 11 | background-color: ${({ isCurrentMatchedPartner }) => 12 | isCurrentMatchedPartner ? '#5ac4ff' : 'transparent'}; 13 | 14 | &:hover { 15 | background-color: #aedfff; 16 | transition: 1.5s all ease-out; 17 | cursor: pointer; 18 | } 19 | `; 20 | 21 | const AvatarStyle = styled.img` 22 | max-height: 40px; 23 | min-height: 40px; 24 | max-width: 40px; 25 | min-width: 40px; 26 | border-radius: 50%; 27 | width: 60%; 28 | 29 | &:hover { 30 | transform: scale(1.4); 31 | transition: 0.3s ease-in; 32 | } 33 | `; 34 | 35 | const LeftAvatarStyle = styled.div` 36 | padding-left: 50px; 37 | width: 60%; 38 | display: flex; 39 | justify-content: center; 40 | `; 41 | 42 | const RightAvatarStyle = styled.div` 43 | padding-right: 50px; 44 | width: 40%; 45 | display: flex; 46 | justify-content: space-evenly; 47 | padding: 0 10px; 48 | transition: transform 0.4s; 49 | `; 50 | 51 | const PartnersNameStyle = styled.h4` 52 | font-size: 1.3rem; 53 | color: #636363; 54 | `; 55 | 56 | export function UserItem({ user, setSelectedPartner, selectedPartner }) { 57 | const {firstName, petInfo, avatarUrl } = user; 58 | // const {firstName } = user; 59 | console.log(JSON.stringify(user)); 60 | 61 | //check to see if user is the selectedPartner; 62 | let isCurrentMatchedPartner; 63 | if (user && selectedPartner) { 64 | isCurrentMatchedPartner = selectedPartner._id === user._id; 65 | } 66 | 67 | return ( 68 | 69 | 70 | setSelectedPartner(user)}> 71 | {firstName} & {petInfo.name} 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | ); 81 | } 82 | -------------------------------------------------------------------------------- /client/components/ChatPage/UserList/UserList.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styled from 'styled-components'; 3 | import { UserItem } from './UserItem/UserItem.jsx'; 4 | 5 | const UserListStyle = styled.div` 6 | border: 2px solid #aedfff; 7 | min-height: 100%; 8 | min-width: 25%; 9 | max-width: 25%; 10 | overflow: scroll; 11 | `; 12 | 13 | export function UserList({ matchList, setSelectedPartner, selectedPartner }) { 14 | console.log("matchList",matchList); 15 | 16 | const displayMatchList = matchList.map((user, idx) => { 17 | return ( 18 | 26 | ); 27 | }); 28 | 29 | return {displayMatchList}; 30 | } 31 | -------------------------------------------------------------------------------- /client/components/Header/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoffeeAndWoof/Woof/275aba4dded583aa8fef595a543dcc88776735da/client/components/Header/.DS_Store -------------------------------------------------------------------------------- /client/components/Header/Header.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | import styled from 'styled-components'; 4 | // CONTEXT API IMPORT 5 | import { useStateValue, StateContext } from '../../StateProvider'; 6 | import styledItems from '../../styled-items'; 7 | import UserInfo from './UserInfo/UserInfo.jsx'; 8 | 9 | const HeaderBG = styled.div` 10 | background-color: ${styledItems.primaryBlue}; 11 | height: 130px; 12 | padding: 20px; 13 | display: flex; 14 | justify-content: space-between; 15 | width: 100%; 16 | `; 17 | 18 | const Logo = styled.div` 19 | font-size: 2em; 20 | color: ${styledItems.darkGray}; 21 | font-weight: 100; 22 | `; 23 | 24 | const BlackText = styled.span` 25 | color: ${styledItems.black}; 26 | font-weight: 800; 27 | `; 28 | 29 | const Header = () => { 30 | return ( 31 | 32 | 33 | Coffee&Woof 34 | 35 | 36 | 37 | ); 38 | }; 39 | 40 | export default Header; 41 | -------------------------------------------------------------------------------- /client/components/Header/UserInfo/UserInfo.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {Link, Redirect} from "react-router-dom"; 3 | import styled from 'styled-components'; 4 | import { useStateValue , StateContext } from '../../../StateProvider'; 5 | import styledItems from '../../../styled-items'; 6 | import FacebookLogin from 'react-facebook-login/dist/facebook-login-render-props' 7 | 8 | const Register = styled.div` 9 | color: ${styledItems.darkGray}; 10 | font-size: 1.4em; 11 | `; 12 | 13 | const OuterDiv = styled.div` 14 | margin-right: 100px; 15 | &:hover { 16 | cursor: pointer; 17 | } 18 | `; 19 | 20 | const StyledImg = styled.img` 21 | width: 30px; 22 | height: 30px; 23 | border-radius: 15px; 24 | margin: 10px; 25 | `; 26 | 27 | const OverallDiv = styled.div` 28 | display: flex; 29 | flex-direction: column; 30 | `; 31 | 32 | const LineDiv = styled.div` 33 | display: flex; 34 | align-items: center; 35 | `; 36 | 37 | 38 | const UserInfo = () => { 39 | // CONTEXT API, RELEVENT STATE ELEMENTS 40 | const [{ userInfo, petInfo }, dispatch] = useStateValue(); 41 | 42 | // FACEBOOK RESPONSE 43 | const responseFacebook = response => { 44 | if (response.accessToken) { 45 | // send user data to DB 46 | fetch('/api/login', { 47 | method: 'POST', 48 | headers: { 49 | "Content-Type": "Application/JSON", 50 | }, 51 | body: JSON.stringify(response) 52 | }) 53 | .then((res) => res.json()) 54 | .then((data) => { 55 | // data contains email, first_name, full_name, profile_img 56 | console.log('response from server', data); 57 | const info = data[0]; 58 | dispatch({ 59 | type: 'clickLogin', 60 | id: info._id, 61 | full_name: info.full_name, 62 | first_name: info.first_name, 63 | email: info.email, 64 | profile_img: info.profile_img, 65 | user_age: info.user_age, 66 | location: info.location, 67 | dog_name: info.dog_name, 68 | dog_image: info.dog_image, 69 | dog_age: info.dog_age, 70 | dog_size: info.dog_size, 71 | dog_breed: info.dog_breed, 72 | preferred_activities: info.preferred_activities, 73 | }); 74 | }) 75 | .catch((err) => console.log('POST: FB info to DB ERROR: ', err)); 76 | 77 | // send user to destination 78 | // console.log(`You're logged in ${response.name}, ${response.email}, ${response.picture.data.url}`); 79 | } 80 | } 81 | 82 | const modifyDirection = () => { 83 | redirection = ; 84 | } 85 | 86 | // CONDITIONAL RENDERING OF CORNER DIV 87 | let cornerDiv; 88 | let redirection; 89 | if(userInfo.fullName == '') { 90 | cornerDiv = 91 | (login with facebook)} 96 | />; 97 | } else if(petInfo.name == '') { 98 | cornerDiv = {userInfo.firstName}; 99 | redirection = ; 100 | } else { 101 | //console.log(JSON.stringify(userInfo)); 102 | cornerDiv = {userInfo.firstName}{petInfo.name}; 103 | redirection = ; 104 | } 105 | 106 | return ( 107 | 108 | {cornerDiv} 109 | {redirection} 110 | 111 | ); 112 | } 113 | 114 | export default UserInfo; -------------------------------------------------------------------------------- /client/components/Login/FacebookLogin.jsx: -------------------------------------------------------------------------------- 1 | import { useHistory } from 'react-router-dom'; 2 | import React, { useState, useEffect } from 'react'; 3 | import FacebookLogin from "react-facebook-login"; 4 | 5 | const FBLogin = props => { 6 | const [isLoggedIn, setIsLoggedIn] = useState(false); 7 | const [name, setName] = useState(''); 8 | const [email, setEmail] = useState(''); 9 | const [picture, setPicture] = useState(''); 10 | //const history = useHistory(); 11 | 12 | //let fbContent; 13 | const handleClick = event => 0; 14 | 15 | const responseFacebook = response => { 16 | if (response.accessToken) { 17 | setIsLoggedIn(true); 18 | setEmail(response.email); 19 | setName(response.name); 20 | setPicture(response.picture); 21 | console.log(`Logging in`); 22 | // send user data to DB 23 | fetch('/api/login', { 24 | method: 'POST', 25 | headers: { 26 | "Content-Type": "Application/JSON", 27 | }, 28 | body: JSON.stringify(response) 29 | }) 30 | .then((res) => res.json()) 31 | .then((data) => { 32 | console.log('response from server', data); 33 | }) 34 | .catch((err) => console.log('POST: FB info to DB ERROR: ', err)); 35 | // send user to destination 36 | console.log(`You're logged in ${response.name}`); 37 | } 38 | } 39 | 40 | return ( 41 | 48 | ); 49 | } 50 | 51 | export default FBLogin; -------------------------------------------------------------------------------- /client/components/Login/Login.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {Link} from "react-router-dom"; 3 | import styled from 'styled-components'; 4 | // CONTEXT API IMPORT 5 | import { useStateValue , StateContext } from '../../StateProvider'; 6 | import styledItems from '../../styled-items'; 7 | 8 | const StyledDiv = styled.div` 9 | width: 100%; 10 | display: flex; 11 | justify-content: center; 12 | align-items: center; 13 | `; 14 | 15 | const StyledText = styled.div` 16 | width: 300px; 17 | color: ${styledItems.darkGray}; 18 | font-size: 2em; 19 | text-align: center; 20 | `; 21 | 22 | const Login = () => { 23 | // CONTEXT API, RELEVENT STATE ELEMENTS 24 | const [{ userName }, dispatch] = useStateValue(); 25 | return ( 26 | 27 | 28 | Meet your pup's new best friend today.... 29 | 30 | 31 | ); 32 | } 33 | 34 | export default Login; -------------------------------------------------------------------------------- /client/components/ProfilePage/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoffeeAndWoof/Woof/275aba4dded583aa8fef595a543dcc88776735da/client/components/ProfilePage/.DS_Store -------------------------------------------------------------------------------- /client/components/ProfilePage/Activities/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoffeeAndWoof/Woof/275aba4dded583aa8fef595a543dcc88776735da/client/components/ProfilePage/Activities/.DS_Store -------------------------------------------------------------------------------- /client/components/ProfilePage/Activities/Activities.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {Link} from "react-router-dom"; 3 | import styled from 'styled-components'; 4 | // CONTEXT API IMPORT 5 | import { useStateValue , StateContext } from '../../../StateProvider'; 6 | import ActivityItem from './ActivityItem/ActivityItem.jsx'; 7 | import styledItems from '../../../styled-items'; 8 | 9 | const OuterDiv = styled.div` 10 | width: 35%; 11 | align-self: flex-start; 12 | background-color: ${styledItems.lightGray}; 13 | min-height: 600px; 14 | margin: 25px; 15 | padding: 20px; 16 | `; 17 | const Heading = styled.div` 18 | font-size: 1.6em; 19 | color: ${styledItems.darkGray}; 20 | `; 21 | 22 | const stockActivities = [ 23 | 'Coffee', 24 | 'Hiking', 25 | 'Birdwatch', 26 | 'Running', 27 | 'Fetch', 28 | 'Swimming', 29 | 'Grooming', 30 | 'Beach', 31 | ]; 32 | 33 | const activitiesList = []; 34 | 35 | 36 | const Activities = (props) => { 37 | 38 | // stockActivities.forEach((activity, index) => { 39 | // const item =
; 40 | // activitiesList.push(item); 41 | // }); 42 | 43 | // CONTEXT API, RELEVENT STATE ELEMENTS 44 | const [{ userName }, dispatch] = useStateValue(); 45 | return ( 46 | 47 | Activities 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | ); 58 | } 59 | 60 | export default Activities; -------------------------------------------------------------------------------- /client/components/ProfilePage/Activities/ActivityItem/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CoffeeAndWoof/Woof/275aba4dded583aa8fef595a543dcc88776735da/client/components/ProfilePage/Activities/ActivityItem/.DS_Store -------------------------------------------------------------------------------- /client/components/ProfilePage/Activities/ActivityItem/ActivityItem.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import {Link} from "react-router-dom"; 3 | import styled from 'styled-components'; 4 | // CONTEXT API IMPORT 5 | import { useStateValue , StateContext , createContext} from '../../../../StateProvider'; 6 | import styledItems from '../../../../styled-items'; 7 | 8 | const OuterDiv = styled.div` 9 | display: flex; 10 | flex-direction: column; 11 | margin: 25px; 12 | `; 13 | 14 | const ButtonDiv = styled.div` 15 | width: 40%; 16 | height: 50px; 17 | border: 1px solid ${styledItems.darkGray}; 18 | border-radius: 2px; 19 | background-color: ${styledItems.secondaryBlue}; 20 | display: flex; 21 | justify-content: center; 22 | align-items: center; 23 | color: ${styledItems.darkGray}; 24 | &:hover { 25 | background-color: ${styledItems.primaryBlue}; 26 | color: ${styledItems.white}; 27 | cursor: pointer; 28 | } 29 | `; 30 | 31 | const StyledForm = styled.form` 32 | margin: 0; 33 | font-size: 1em; 34 | `; 35 | 36 | const StyledInput = styled.input` 37 | padding: 5px; 38 | margin-top: 4px; 39 | width: 100%; 40 | border: 1px solid ${styledItems.darkGray}; 41 | border-radius: 4px; 42 | color: ${styledItems.darkGray}: 43 | `; 44 | 45 | const ActivityItem = (props) => { 46 | // CONTEXT API, RELEVENT STATE ELEMENTS 47 | const activityName = props.activity; 48 | const actId = props.id; 49 | const [{ userInfo }, dispatch] = useStateValue(); 50 | 51 | let formItem; 52 | if(activityName in userInfo.activities) { 53 | formItem = 54 | 55 | 56 | ; 57 | } 58 | 59 | const activityClick = () => { 60 | if(activityName in userInfo.activities) { 61 | dispatch({ 62 | type: 'removeActivity', 63 | activity: activityName, 64 | }) 65 | } else { 66 | dispatch({ 67 | type: 'addActivity', 68 | activity: activityName, 69 | }) 70 | } 71 | } 72 | 73 | return ( 74 | 75 | activityClick()}> 76 | {activityName} 77 | 78 | {formItem} 79 | 80 | ); 81 | } 82 | 83 | export default ActivityItem; -------------------------------------------------------------------------------- /client/components/ProfilePage/Profile/Profile.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import {Link} from "react-router-dom"; 3 | import styled from 'styled-components'; 4 | // CONTEXT API IMPORT 5 | import { useStateValue , StateContext } from '../../../StateProvider'; 6 | import styledItems from '../../../styled-items'; 7 | 8 | const OuterDiv = styled.div` 9 | width: 100; 10 | align-self: flex-start; 11 | background-color: ${styledItems.white}; 12 | border: 3px solid ${styledItems.lightGray}; 13 | margin: 25px; 14 | padding: 20px; 15 | `; 16 | const Heading = styled.div` 17 | font-size: 1.4em; 18 | color: ${styledItems.darkGray}; 19 | `; 20 | const LesserText = styled.div` 21 | font-size: .8em; 22 | color: ${styledItems.white}; 23 | `; 24 | 25 | const StyledImg = styled.img` 26 | width: 50px; 27 | height: 50px; 28 | border-radius: 25px; 29 | `; 30 | 31 | const HumanInfo = styled.div` 32 | display: flex; 33 | justify-content: space-around; 34 | align-items: center; 35 | `; 36 | 37 | const CreatureTextArea = styled.div` 38 | display: flex; 39 | flex-direction: column; 40 | align-items: center; 41 | `; 42 | 43 | const HumanTextDesc = styled.div` 44 | display: flex; 45 | flex-direction: column; 46 | align-items: center; 47 | padding: 20px; 48 | margin-top: 10px; 49 | background-color: ${styledItems.primaryBlue}; 50 | color: ${styledItems.white}; 51 | `; 52 | 53 | const StyledForm = styled.form` 54 | margin: 0; 55 | font-size: 1em; 56 | display: flex; 57 | align-items: center; 58 | justify-content: center; 59 | `; 60 | 61 | const StyledInputLeft = styled.input` 62 | padding: 5px; 63 | margin-top: 4px; 64 | border: none; 65 | background-color: ${styledItems.primaryBlue}; 66 | color: ${styledItems.white}; 67 | text-align: left; 68 | width: 100px; 69 | border-bottom: 1px solid white; 70 | `; 71 | 72 | const StyledInputRight = styled.input` 73 | padding: 5px; 74 | margin-top: 4px; 75 | border: none; 76 | background-color: ${styledItems.primaryBlue}; 77 | color: ${styledItems.white}; 78 | text-align: right; 79 | width: 60px; 80 | border-bottom: 1px solid white; 81 | `; 82 | 83 | const StyledInputCenter = styled.input` 84 | padding: 5px; 85 | margin-top: 4px; 86 | border: none; 87 | background-color: ${styledItems.primaryBlue}; 88 | color: ${styledItems.white}; 89 | text-align: center; 90 | width: 100px; 91 | border-bottom: 1px solid white; 92 | `; 93 | 94 | const DogInfo = styled.div` 95 | display: flex; 96 | justify-content: space-around; 97 | align-items: center; 98 | `; 99 | 100 | const DogNameInput = styled.input` 101 | padding: 5px; 102 | margin-top: 4px; 103 | border: none; 104 | font-size: 1.4em; 105 | background-color: ${styledItems.white}; 106 | color: ${styledItems.darkGray}; 107 | text-align: center; 108 | `; 109 | 110 | const Profile = (props) => { 111 | // CONTEXT API, RELEVENT STATE ELEMENTS 112 | const [{ userInfo , petInfo }, dispatch] = useStateValue(); 113 | console.log(userInfo); 114 | return ( 115 | 116 | 117 | 118 | 119 | {userInfo.fullName} 120 | 121 | {userInfo.email} 122 | 123 | 124 | years old 125 | 126 | 127 | I live in 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | ); 159 | } 160 | 161 | export default Profile; -------------------------------------------------------------------------------- /client/components/ProfilePage/ProfilePage.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component} from "react"; 2 | import {Link} from "react-router-dom"; 3 | import styled from 'styled-components'; 4 | // CONTEXT API IMPORT 5 | import { useStateValue , StateContext } from '../../StateProvider'; 6 | import styledItems from '../../styled-items'; 7 | import Activities from './Activities/Activities.jsx'; 8 | import Profile from './Profile/Profile.jsx'; 9 | 10 | const OuterDiv = styled.div` 11 | width: 100%; 12 | display: flex; 13 | justify-content: space-between; 14 | height: 100%; 15 | `; 16 | 17 | const RightDiv = styled.div` 18 | display: flex; 19 | flex-direction: column; 20 | height: 100%; 21 | width: 40%; 22 | `; 23 | 24 | const SaveButton = styled.div` 25 | font-size: 1.7em; 26 | padding: 10px; 27 | width: 180px; 28 | border: 1px solid ${styledItems.darkGray}; 29 | border-radius: 2px; 30 | background-color: ${styledItems.primaryBlue}; 31 | display: flex; 32 | justify-content: center; 33 | align-items: center; 34 | color: ${styledItems.white}; 35 | justify-self: flex-end; 36 | text-decoration: none; 37 | &:hover { 38 | background-color: ${styledItems.darkGray}; 39 | color: ${styledItems.secondaryBlue}; 40 | cursor: pointer; 41 | } 42 | `; 43 | 44 | class ProfilePage extends Component { 45 | constructor (props) { 46 | super(props); 47 | this.state = { 48 | userLocation: '', 49 | userAge: '', 50 | petName: '', 51 | petAge: '', 52 | petBreed: '', 53 | petSize: '', 54 | petAvatar: '', 55 | Coffee: '', 56 | Hiking: '', 57 | Birdwatch: '', 58 | Running: '', 59 | Fetch: '', 60 | Swimming: '', 61 | Grooming: '', 62 | Beach: '', 63 | } 64 | this.handleChange = this.handleChange.bind(this); 65 | } 66 | 67 | handleChange(event) { 68 | const value = event.target.value; 69 | this.setState({ 70 | ...this.state, 71 | [event.target.name]: value, 72 | }) 73 | } 74 | 75 | componentDidMount() { 76 | const [{ userInfo, petInfo }, dispatch] = this.context; 77 | console.log("LOG TEST",userInfo); 78 | this.setState({ 79 | userLocation: userInfo.location, 80 | userAge: userInfo.age, 81 | petName: petInfo.name, 82 | petAge: petInfo.age, 83 | petBreed: petInfo.breed, 84 | petSize: petInfo.size, 85 | petAvatar: petInfo.avatarUrl, 86 | Coffee: userInfo.activities.Coffee || '', 87 | Hiking: userInfo.activities.Hiking || '', 88 | Birdwatch: userInfo.activities.Birdwatch || '', 89 | Running: userInfo.activities.Running || '', 90 | Fetch: userInfo.activities.Fetch || '', 91 | Swimming: userInfo.activities.Swimming || '', 92 | Grooming: userInfo.activities.Grooming || '', 93 | Beach: userInfo.activities.Beach || '', 94 | }) 95 | } 96 | 97 | static contextType = StateContext; 98 | render() { 99 | const [{ userInfo, petInfo}, dispatch] = this.context; 100 | 101 | const saveProfile = () => { 102 | const stockActivities = [ 103 | 'Coffee', 104 | 'Hiking', 105 | 'Birdwatch', 106 | 'Running', 107 | 'Fetch', 108 | 'Swimming', 109 | 'Grooming', 110 | 'Beach', 111 | ]; 112 | 113 | userInfo.location = this.state.userLocation; 114 | userInfo.age = this.state.userAge; 115 | userInfo.activities = {}; 116 | 117 | petInfo.name = this.state.petName, 118 | petInfo.age = this.state.petAge, 119 | petInfo.breed = this.state.petBreed, 120 | petInfo.size = this.state.petSize, 121 | petInfo.avatarUrl = this.state.petAvatar, 122 | 123 | stockActivities.forEach(activity => { 124 | if(this.state[activity] != '') { 125 | userInfo.activities[activity] = this.state[activity]; 126 | } 127 | }) 128 | // console.log(this.state); 129 | // console.log(stockActivities); 130 | // console.log(userInfo); 131 | 132 | // dispatch({ 133 | // type: 'saveProfile', 134 | // userInfo, 135 | // petInfo, 136 | // }); 137 | 138 | // convert state to mongoDB format 139 | const mongoObj = {}; 140 | mongoObj.first_name = userInfo.firstName; 141 | mongoObj.full_name = userInfo.fullName; 142 | mongoObj.email = userInfo.email; 143 | mongoObj.profile_img = userInfo.avatarUrl; 144 | mongoObj.user_age = userInfo.age; 145 | mongoObj.location = userInfo.location; 146 | mongoObj.dog_name = petInfo.name; 147 | mongoObj.dog_image = petInfo.avatarUrl; 148 | mongoObj.dog_age = petInfo.age; 149 | mongoObj.dog_size = petInfo.size; 150 | mongoObj.dog_breed = petInfo.breed; 151 | mongoObj.preferred_activities = []; 152 | Object.entries(userInfo.activities).forEach(([activity, description]) => { 153 | mongoObj.preferred_activities.push({'activity': activity, 'description': description }) 154 | }); 155 | // console.log(mongoObj); 156 | 157 | // console.log('===========>', userInfo._id) 158 | // send user data & pet data to DB 159 | fetch(`/api/updateUserData/${userInfo._id}`, { 160 | method: 'PUT', 161 | headers: { 162 | "Content-Type": "Application/JSON", 163 | }, 164 | body: JSON.stringify(mongoObj) 165 | }) 166 | .then((res) => res.json()) 167 | .then((data) => { 168 | const currentUserId = userInfo._id; 169 | console.log('response from server', data); 170 | const matchList = []; 171 | data.forEach(match => { 172 | if(match._id != currentUserId) { 173 | const userObject = { 174 | _id: match._id, 175 | firstName: match.first_name || '', 176 | fullName: match.full_name || '', 177 | location: match.location || '', 178 | age: match.user_age || '', 179 | avatarUrl: match.profile_img || '', 180 | activities: {}, 181 | petInfo: { 182 | name: match.dog_name || '', 183 | age: match.dog_age || '', 184 | breed: match.dog_breed || '', 185 | size: match.dog_size || '', 186 | avatarUrl: match.dog_image || '', 187 | }, 188 | } 189 | match.preferred_activities.forEach(activity => { 190 | userObject.activities[activity.activity] = activity.description; 191 | }); 192 | matchList.push(userObject); 193 | }; 194 | }); 195 | console.log('HELLO'); 196 | dispatch({ 197 | type: 'updateMatches', 198 | matchList, 199 | userInfo, 200 | petInfo, 201 | }); 202 | }) 203 | .catch((err) => console.log('POST: PROFILE INFO to DB ERROR: ', err)); 204 | } 205 | 206 | return ( 207 | 208 | 209 | 210 | 211 | {saveProfile()}}>Save 212 | 213 | 214 | ); 215 | } 216 | } 217 | 218 | export default ProfilePage; -------------------------------------------------------------------------------- /client/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import App from './App.jsx'; 4 | import './styles.css'; 5 | 6 | 7 | // const chatForm = document.getElementById('chat-form'); 8 | // const chatMessages = document.querySelector('.chat-messages'); 9 | 10 | // // Get username and room from URL 11 | // // const { username, room } = Qs.parse(location.search, { 12 | // // ignoreQueryPrefix: true 13 | // // }); 14 | 15 | // // console.log(username, room); 16 | 17 | // const socket = io.connect('http://localhost:3000'); 18 | 19 | // // socket.emit('joinRoom', ({ username, room }) => { 20 | // // console.log(username, room) 21 | // // }); 22 | 23 | // // Message from server 24 | // socket.on('message', message => { 25 | // console.log(message); 26 | // outputMessage(message); 27 | 28 | // // Scroll down 29 | // chatMessages.scrollTop = chatMessages.scrollHeight; 30 | // }) 31 | 32 | // chatForm.addEventListener('submit', (e)=> { 33 | // e.preventDefault(); 34 | 35 | // // Get message text 36 | // const msg = e.target.elements.msg.value; 37 | 38 | // // Emitting a message to the server 39 | // socket.emit('chatMessage', msg); 40 | 41 | // // Clear input 42 | // e.target.elements.msg.value = ''; 43 | // e.target.elements.msg.focus(); 44 | // }); 45 | 46 | 47 | 48 | // // Output message to DOM 49 | // function outputMessage(message) { 50 | // const div = document.createElement('div'); 51 | // div.classList.add('message'); 52 | // div.innerHTML = `

${message.username} ${message.time}

${message.text}

`; 53 | // document.querySelector('.chat-messages').appendChild(div); 54 | // } 55 | 56 | 57 | render(, document.getElementById('app')); 58 | -------------------------------------------------------------------------------- /client/main.js: -------------------------------------------------------------------------------- 1 | const chatForm = document.getElementById('chat-form'); 2 | const chatMessages = document.querySelector('.chat-messages'); 3 | 4 | // Get username and room from URL 5 | // const { username, room } = Qs.parse(location.search, { 6 | // ignoreQueryPrefix: true 7 | // }); 8 | 9 | // console.log(username, room); 10 | 11 | const socket = io(); 12 | 13 | // socket.emit('joinRoom', ({ username, room }) => { 14 | // console.log(username, room) 15 | // }); 16 | 17 | // Message from server 18 | socket.on('message', message => { 19 | console.log(message); 20 | outputMessage(message); 21 | 22 | // Scroll down 23 | chatMessages.scrollTop = chatMessages.scrollHeight; 24 | }) 25 | 26 | chatForm.addEventListener('submit', (e)=> { 27 | e.preventDefault(); 28 | 29 | // Get message text 30 | const msg = e.target.elements.msg.value; 31 | 32 | // Emitting a message to the server 33 | socket.emit('chatMessage', msg); 34 | 35 | // Clear input 36 | e.target.elements.msg.value = ''; 37 | e.target.elements.msg.focus(); 38 | }); 39 | 40 | 41 | 42 | // Output message to DOM 43 | function outputMessage(message) { 44 | const div = document.createElement('div'); 45 | div.classList.add('message'); 46 | div.innerHTML = `

${message.username} ${message.time}

${message.text}

`; 47 | document.querySelector('.chat-messages').appendChild(div); 48 | } 49 | -------------------------------------------------------------------------------- /client/styled-items.js: -------------------------------------------------------------------------------- 1 | const styledItems = { 2 | primaryBlue: '#5AC4FF', 3 | secondaryBlue: '#F3F8FF', 4 | black: '#000000', 5 | darkGray: '#4A4A4A', 6 | lightGray: '#FAFAFA', 7 | white: '#FFFFFF', 8 | }; 9 | 10 | export default styledItems; -------------------------------------------------------------------------------- /client/styles.css: -------------------------------------------------------------------------------- 1 | /* @import url("https://fonts.googleapis.com/css2?family=Fredoka+One&display=swap"); */ 2 | /* @import url("https://fonts.googleapis.com/css2?family=Black+Ops+One&family=Monoton&family=Shrikhand&display=swap"); */ 3 | @import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap'); 4 | 5 | /* :root { 6 | /* --yellow: #ffefa0; 7 | --light-orange: #ffd57e; 8 | --orange: #fca652; 9 | --red: #ac4b1c; 10 | 11 | //our colors 12 | --primaryBlue: #5AC4FF; 13 | --secondaryBlue: #F3F8FF; 14 | --black: #222; 15 | --white: #fff; 16 | --darkGray: #4A4A4A; 17 | --lightGray: #575757; 18 | } 19 | */ 20 | 21 | * { 22 | box-sizing: border-box; 23 | margin: 0; 24 | padding: 0; 25 | font-family: 'Roboto'; 26 | color: black; 27 | /* color: #636363; */ 28 | } 29 | 30 | #app { 31 | width: 100%; 32 | } 33 | 34 | body { 35 | background-color: #f3f8ff; 36 | display: flex; 37 | justify-content: center; 38 | width: 100%; 39 | } 40 | 41 | #msg { 42 | height: 2rem; 43 | width: 15rem; 44 | border-radius: 5px; 45 | border: 1px solid black; 46 | } 47 | 48 | /* .btn { 49 | background-color: #5AC4FF; 50 | color: #fff; 51 | height: 2rem; 52 | width: 4rem; 53 | border-radius: 5px; 54 | border: 1px solid #2cb4fd; 55 | margin-left: 1rem; 56 | box-shadow: 1px 1px 1px #5AC4FF; 57 | } */ 58 | 59 | /* OLD SITE STYLING */ 60 | /* 61 | * { 62 | box-sizing: border-box; 63 | margin: 0; 64 | padding: 0; 65 | font-family: "Shrikhand", "Arial Narrow", Arial, sans-serif; 66 | color: black; 67 | } 68 | a { 69 | text-decoration: none; 70 | } 71 | 72 | li { 73 | list-style: none; 74 | } 75 | #header-nav { 76 | padding: 20px; 77 | border: 2px solid #ffd57e; 78 | background-color: #ffd57e; 79 | } 80 | #header-nav nav ul { 81 | display: flex; 82 | justify-content: flex-start; 83 | } 84 | #header nav ul li a { 85 | font-size: 1.2rem; 86 | } 87 | #header-nav nav ul li { 88 | margin-right: 10px; 89 | } 90 | 91 | .form-container { 92 | width: 500px; 93 | height: 500px; 94 | margin: 0 auto; 95 | display: flex; 96 | flex-direction: column; 97 | justify-content: center; 98 | align-items: center; 99 | border: 1px solid #222; 100 | border-radius: 6px; 101 | box-shadow: 10px 10px 5px 0px rgba(220, 220, 220, 1); 102 | } 103 | 104 | .input-container { 105 | display: flex; 106 | flex-direction: column; 107 | align-items: center; 108 | } 109 | 110 | .form-container h1 { 111 | font-family: "Shrikhand", cursive; 112 | margin-bottom: 10px; 113 | } 114 | 115 | .input-group input { 116 | margin-top: 5px; 117 | margin-bottom: 5px; 118 | height: 30px; 119 | padding: 0px 10px; 120 | font-size: 16px; 121 | border-radius: 4px; 122 | } 123 | 124 | .login-btn { 125 | background-color: #fca652; 126 | border: 2px solid #fca652; 127 | margin-bottom: 10px; 128 | border-radius: 5px; 129 | cursor: pointer; 130 | } 131 | 132 | .login-btn:hover { 133 | background-color: #ffd57e; 134 | border: 2px solid #ffd57e; 135 | } 136 | .profile { 137 | margin-top: 5px; 138 | display: flex; 139 | flex-direction: column; 140 | align-items: flex-start; 141 | width: fit-content; 142 | } 143 | 144 | .login-btn { 145 | font-size: 1.25em; 146 | display: flex; 147 | flex-grow: 1; 148 | width: 100%; 149 | margin-top: 0.25em; 150 | padding: 0.25em; 151 | align-items: flex-start; 152 | justify-content: center; 153 | } 154 | .feed-container { 155 | margin: 20px auto; 156 | width: 1000px; 157 | border: 1px solid black; 158 | padding: 20px; 159 | } 160 | .title-date { 161 | display: flex; 162 | flex-direction: row; 163 | justify-content: space-between; 164 | margin-bottom: 10px; 165 | /* align-items: flex-start; 166 | } 167 | .title-date h3 { 168 | align-self: center; 169 | } 170 | 171 | .image-container { 172 | margin-top: 20px; 173 | } 174 | 175 | #create-list-item { 176 | padding: 20px 20px 20px 0; 177 | } 178 | #create-list-item label { 179 | margin-right: 10px; 180 | } 181 | 182 | #create-list-item input { 183 | margin-right: 10px; 184 | } 185 | 186 | #bucket-list-container { 187 | width: 100%; 188 | margin: 0 auto; 189 | padding: 20px; 190 | } 191 | 192 | .uncompleted-post-form { 193 | display: flex; 194 | flex-direction: column; 195 | width: 800px; 196 | margin-bottom: 20px; 197 | } 198 | 199 | .completed-list-item, 200 | .uncompleted-list-item { 201 | display: flex; 202 | flex-direction: row; 203 | justify-content: space-between; 204 | width: 800px; 205 | align-items: center; 206 | } 207 | 208 | .uncompleted-post-form button { 209 | margin-bottom: 20px; 210 | } */ 211 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 14 | 15 | Coffee&Woof 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /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": "webpack", 10 | "dev": "concurrently \"webpack-dev-server --open --mode development\" \"nodemon server/server.js\"" 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 | "bcryptjs": "^2.4.3", 25 | "body-parser": "^1.19.0", 26 | "cookie-parser": "^1.4.5", 27 | "cors": "^2.8.5", 28 | "enzyme": "^3.11.0", 29 | "express": "^4.17.1", 30 | "moment": "^2.29.1", 31 | "mongoose": "^5.10.7", 32 | "oauth": "^0.9.15", 33 | "react": "^16.13.1", 34 | "react-dom": "^16.13.1", 35 | "react-facebook-login": "^4.1.1", 36 | "react-router": "^5.2.0", 37 | "socket.io": "^2.3.0", 38 | "styled-components": "^5.2.0" 39 | }, 40 | "devDependencies": { 41 | "@babel/core": "^7.11.6", 42 | "@babel/preset-env": "^7.11.5", 43 | "@babel/preset-react": "^7.10.4", 44 | "@babel/plugin-proposal-class-properties": "^7.0.0", 45 | "babel-loader": "^8.1.0", 46 | "concurrently": "^5.3.0", 47 | "cross-env": "^7.0.2", 48 | "css-loader": "^4.3.0", 49 | "fs": "0.0.1-security", 50 | "isomorphic-fetch": "^3.0.0", 51 | "nodemon": "^2.0.4", 52 | "path": "^0.12.7", 53 | "react-router-dom": "^5.2.0", 54 | "sass": "^1.26.11", 55 | "sass-loader": "^10.0.2", 56 | "style-loader": "^1.3.0", 57 | "supertest": "^5.0.0", 58 | "w": "^1.1.0", 59 | "webpack": "^4.44.2", 60 | "webpack-cli": "^3.3.12", 61 | "webpack-dev-server": "^3.11.0" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /server/controllers/MatchesController.js: -------------------------------------------------------------------------------- 1 | const User = require('../models/UserModels'); 2 | const Activities = require('../models/ActivitiesModels'); 3 | 4 | const matchesController = {}; 5 | 6 | // FINDING AN ACTIVITIES MATCH FOR THE USER BASED ON USER ID. 7 | 8 | matchesController.userActivities = (req, res, next) => { 9 | // console.log(`Requesting for user data`); 10 | const { data } = res.locals; 11 | // console.log('data ---> ', res.locals) 12 | User.findById(data._id, (err, user) => { 13 | if (err) { 14 | return next({ err }); 15 | } 16 | const activities = user.preferred_activities; 17 | // Getting an array of the user's activities 18 | const activitiesArray = activities.map((activity) => { 19 | return activity.activity; 20 | }); 21 | res.locals.activities = activitiesArray; 22 | // console.log(res.locals.activities) 23 | // console.log(res.locals.activities); 24 | next(); 25 | }); 26 | }; 27 | 28 | matchesController.getUniqueIds = (req, res, next) => { 29 | // console.log(`Requesting for activities array`); 30 | const { activities } = res.locals; 31 | 32 | // Changing all activities to lowercase 33 | const activitiesUpdate = activities.map((activity) => activity.toLowerCase()) 34 | 35 | // const matchesArray = [] 36 | Activities.find({ name: { $in: activitiesUpdate } }) 37 | .then((users) => { 38 | // Loop through array of activities to extract the ids. Expect to receive an array of IDs that are not duplicates 39 | // console.log('users -----> ', users); 40 | const arrayOfIds = []; 41 | users.forEach((activity) => { 42 | arrayOfIds.push(activity.users); 43 | }); 44 | // Return a flattened array of IDs with no duplicates. Save result to res.locals 45 | const flattenArrayId = arrayOfIds.flat() 46 | 47 | const result = []; 48 | for (let index = 0; index < flattenArrayId.length; index += 1) { 49 | if (!result.includes(flattenArrayId[index].toString())) result.push(flattenArrayId[index].toString()) 50 | } 51 | // console.log(arrayOfIds) 52 | // const uniqueIds = [...new Set(flattenArrayId)]; 53 | // console.log('uniqueIds1 -->', uniqueIds); 54 | res.locals.uniqueIds = result; 55 | // console.log('uniqueIDs --> ', res.locals.uniqueIds); 56 | next(); 57 | }) 58 | .catch((err) => { 59 | console.log(err); 60 | }); 61 | } 62 | 63 | //Return list of users with this array 64 | matchesController.returnMatches = (req, res, next) => { 65 | // console.log(`Returning Matches`); 66 | const { uniqueIds } = res.locals; 67 | // console.log(res.locals.uniqueIds); 68 | // console.log(typeof res.locals.uniqueIds[0].toString()); 69 | 70 | // // const idString = uniqueIds.map((id) => id.toString()) 71 | // // console.log(idString) 72 | 73 | //Find users based on input ID 74 | User.find({ 75 | _id: { $in: uniqueIds }, 76 | }) 77 | .then((data) => { 78 | console.log(data); 79 | res.locals.matchingUsersInfo = data 80 | next() 81 | }) 82 | .catch((err) => { 83 | console.log(err); 84 | }); 85 | } 86 | 87 | 88 | module.exports = matchesController; -------------------------------------------------------------------------------- /server/controllers/UserController.js: -------------------------------------------------------------------------------- 1 | const Activities = require('../models/ActivitiesModels'); 2 | const User = require('../models/UserModels'); 3 | 4 | const userController = {}; 5 | 6 | userController.getUserData = async (req, res, next) => { 7 | // get the user id from params 8 | const { id } = req.params; 9 | console.log('Send data for user ID:', id); 10 | // verify that user name is valid 11 | if (!id) { 12 | return next({ 13 | log: 'UserController.getUserData ERROR: userName is undefined', 14 | message: { err: 'UserController.getUserData ERROR: Check server logs for details' }, 15 | }); 16 | } 17 | const result = await User.findById(id).exec(); 18 | res.locals.data = result; 19 | return next(); 20 | }; 21 | 22 | 23 | userController.updateUserData = async (req, res, next) => { 24 | 25 | const { id } = req.params; 26 | console.log('updating id:', id); 27 | const userData = req.body; 28 | if (!id) { 29 | return next({ 30 | log: 'UserController.updateUserData ERROR: userName is undefined', 31 | message: { err: 'UserController.updateUserData ERROR: Check server logs for details' }, 32 | }); 33 | } 34 | 35 | // contains the fields from req.body 36 | const queryResult = await User.findByIdAndUpdate(id, userData, {new: true}); // new true sends back the updated user obj 37 | // update the activites collection 38 | res.locals.data = queryResult; 39 | const onlyActivities = queryResult.preferred_activities.map(obj => { 40 | return obj.activity.toLowerCase(); 41 | }); 42 | Activities.updateMany({name: {$in: onlyActivities}}, { $push: {users: queryResult._id}}) 43 | return next(); 44 | }; 45 | 46 | userController.deleteUserData = async (req, res, next) => { 47 | // get the user id from params 48 | const { id } = req.params; 49 | console.log('Delete user ID:', id); 50 | // verify that user name is valid 51 | if (!id) { 52 | return next({ 53 | log: 'UserController.deleteUserData ERROR: userName is undefined', 54 | message: { err: 'UserController.deleteUserData ERROR: Check server logs for details' }, 55 | }); 56 | } 57 | const queryResult = await User.deleteOne({_id: id}).exec(); 58 | res.locals.data = queryResult; 59 | return next(); 60 | }; 61 | 62 | userController.createUser = async (req, res, next) => { 63 | const { name, email } = req.body; 64 | const picURL = req.body.picture.data.url; 65 | 66 | // check if user already exists 67 | console.log('==========>', email); 68 | const doesUserExist = await User.find({ email }); 69 | 70 | // only create new document if user does not exist 71 | if (doesUserExist.length === 0) { 72 | console.log('============> user does not exist'); 73 | const userData = { 74 | full_name: name, 75 | first_name: name.split(' ')[0], 76 | email, 77 | profile_img: picURL 78 | }; 79 | const newUser = new User(userData); 80 | newUser.save((e, r) => { 81 | if (e) console.log(e); 82 | const result = fetchUserData({ email }); 83 | result.then(result => { 84 | res.locals.data = result; 85 | next(); 86 | }) 87 | }); 88 | } else { 89 | res.locals.data = doesUserExist; 90 | next(); 91 | } 92 | } 93 | 94 | const fetchUserData = async (userData) => { 95 | const result = await User.find(userData); 96 | return result; 97 | } 98 | 99 | 100 | 101 | 102 | module.exports = userController; 103 | -------------------------------------------------------------------------------- /server/models/UserModels.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const MONGO_URI = 'mongodb+srv://woof:codesmith123@woof.qaamj.mongodb.net/woof?retryWrites=true&w=majority'; 4 | 5 | mongoose 6 | .connect(MONGO_URI, { 7 | useNewUrlParser: true, 8 | useUnifiedTopology: true, 9 | }) 10 | .then(() => console.log('CONNECTED TO MONGO DB')) 11 | .catch((err) => console.log(err)); 12 | 13 | 14 | 15 | const Schema = mongoose.Schema; 16 | 17 | // SAVING USER SCHEMA FOR MONGODB 18 | const usersSchema = new Schema({ 19 | full_name: { type: String, required: true }, 20 | first_name: String, 21 | email: String, 22 | profile_img: String, 23 | user_age: Number, 24 | location: String, 25 | dog_name: String, 26 | dog_image: String, 27 | dog_age: Number, 28 | dog_size: String, 29 | dog_breed: String, 30 | preferred_activities: [{ activity: String, description: String }], 31 | //timestamps: true, 32 | }); 33 | 34 | const User = mongoose.model('Woof-users', usersSchema); 35 | 36 | 37 | module.exports = User; 38 | 39 | // preferred_activities: [{activity: "coffee", description: "i like it black"}, {activity: "beach", description: "a day under the sun"}, {activity: "hiking", description: "conversation time for our pets"}] 40 | 41 | // /login 42 | // /register 43 | // / setProfile 44 | // / main 45 | -------------------------------------------------------------------------------- /server/models/activitiesModels.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const Schema = mongoose.Schema; 3 | 4 | // SAVING ACTIVITIES SCHEMA FOR MONGODB 5 | const activitiesSchema = new Schema({ 6 | name: String, 7 | users: [{ type: Schema.Types.ObjectId, ref: 'users' }], 8 | }); 9 | 10 | module.exports = mongoose.model('activities', activitiesSchema); 11 | 12 | // coffee: [{user_id: 524535, description: "i like black coffee"}] 13 | -------------------------------------------------------------------------------- /server/routers/users.js: -------------------------------------------------------------------------------- 1 | const { Router } = require('express'); 2 | const express = require('express'); 3 | const userController = require('../controllers/UserController'); 4 | const matchesController = require('../controllers/MatchesController'); 5 | const router = express.Router(); 6 | 7 | router.post('/login', 8 | userController.createUser, 9 | (req, res) => res.status(200).json(res.locals.data) 10 | ); 11 | 12 | router.get('/getUserData/:id', 13 | userController.getUserData, 14 | (req, res) => res.status(200).json(res.locals.data) 15 | ); 16 | 17 | router.delete('/deleteUser/:id', 18 | userController.deleteUserData, 19 | (req, res) => res.status(200).json(res.locals.data) 20 | ); 21 | 22 | router.put( 23 | '/updateUserData/:id', 24 | userController.updateUserData, 25 | matchesController.userActivities, 26 | matchesController.getUniqueIds, 27 | matchesController.returnMatches, 28 | (req, res) => res.status(200).json(res.locals.matchingUsersInfo) 29 | ); 30 | 31 | module.exports = router; 32 | 33 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const express = require('express'); 3 | const socketio = require('socket.io'); 4 | const bodyParser = require("body-parser"); 5 | const cookieParser = require('cookie-parser'); 6 | const mongoose = require('mongoose'); 7 | const usersRouter = require("./routers/users"); 8 | const formatMessage = require('../utils/messages'); 9 | 10 | // SETTING UP SERVER 11 | const app = express(); 12 | const PORT = 3000; 13 | const server = app.listen(PORT, () => { 14 | console.log(`LISTENING ON PORT: ${PORT}`); 15 | }); 16 | const io = socketio(server); 17 | 18 | // ESTABLISH CONNECTION TO DATABASE 19 | const MONGO_URI = 'mongodb+srv://woof:codesmith123@woof.qaamj.mongodb.net/woof?retryWrites=true&w=majority'; 20 | 21 | mongoose 22 | .connect(MONGO_URI, { 23 | useNewUrlParser: true, 24 | useUnifiedTopology: true, 25 | }) 26 | .then(() => console.log('CONNECTED TO MONGO DB')) 27 | .catch((err) => console.log(err)); 28 | 29 | // SET UP 30 | app.use(express.json()); 31 | app.use(express.urlencoded({ extended: true })); 32 | app.use(bodyParser.json()); 33 | app.use(bodyParser.urlencoded({ extended: true })); 34 | app.use(cookieParser()); 35 | 36 | 37 | app.use(express.static(path.join(__dirname, '../client/'))); 38 | 39 | 40 | app.use("/api", usersRouter); 41 | 42 | // DIRECT ALL INCOMING TRAFFIC TO HOMEPAGE 43 | app.use('/', (req, res) => { 44 | res.sendFile(path.join(__dirname, '../index.html')); 45 | }); 46 | 47 | // GLOBAL ERROR HANDLER 48 | app.use((err, req, res, next) => { 49 | const defaultErr = { 50 | log: 'Express error: Unknown middleware', 51 | status: 500, 52 | message: { err: 'An error occurred' }, 53 | }; 54 | const errorObj = { ...defaultErr, err }; 55 | 56 | console.log('ERROR LOG => ', errorObj.log); 57 | return res.status(errorObj.status).json(errorObj.message); 58 | }); 59 | 60 | // Run when a client connects 61 | const botName = 'WoofBot'; 62 | // Run when a client connects 63 | io.on('connection', socket => { 64 | console.log("A USER CONNECTED!"); 65 | 66 | // socket.on('joinRoom', ({ username, room }) => { 67 | // Welcome current user 68 | console.log(botName); 69 | socket.emit('message', formatMessage(botName, 'Welcome to WoofChat!', 1)) 70 | 71 | // Broadcast when a user connects 72 | socket.broadcast.emit('message', formatMessage(botName, 'A user has joined the chat!', 1)); 73 | // }); 74 | 75 | // Listen for chat message 76 | socket.on('chatMessage', ({ username, text, userID}) => { 77 | io.emit('message', formatMessage(username, text, userID)); 78 | }); 79 | 80 | // Runs when client disconnects 81 | socket.on('disconnect', () => { 82 | io.emit('message', formatMessage(botName, 'A user has left the chat!', 1)); 83 | }); 84 | }); 85 | // STARTUP LOGS 86 | // console.log( 87 | // 'Remember to check your .env file if you cannot connect to the database' 88 | // ); 89 | // console.log(`Server is listening at http://localhost:${PORT}`); 90 | // console.log(`Client is live at http://localhost:8080`); 91 | -------------------------------------------------------------------------------- /utils/messages.js: -------------------------------------------------------------------------------- 1 | const moment = require('moment'); 2 | 3 | function formatMessage(username, text, userID) { 4 | return { 5 | userID, 6 | username, 7 | text, 8 | time: moment().format('h:mm a') 9 | } 10 | } 11 | 12 | module.exports = formatMessage; 13 | -------------------------------------------------------------------------------- /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.resolve(__dirname, 'build'), 9 | filename: 'bundle.js', 10 | publicPath: '/' 11 | }, 12 | 13 | devServer: { 14 | contentBase: path.resolve(__dirname, "build"), 15 | historyApiFallback: true, 16 | headers: { 17 | "Access-Control-Allow-Origin": "*" 18 | }, 19 | proxy: { 20 | '/' : { 21 | target: 'http://localhost:3000', 22 | secure: false 23 | } 24 | }, 25 | port: 8080 26 | }, 27 | module: { 28 | rules: [ 29 | { 30 | test: /\.jsx?/, 31 | exclude: /(node_modules)/, 32 | use: [{ 33 | loader: 'babel-loader', 34 | options: { 35 | presets: ['@babel/preset-env', '@babel/preset-react'], 36 | } 37 | }] 38 | }, 39 | { 40 | test: /\.css$/, //changed to specify css instead of previous "catch all" test regex item including scss 41 | exclude: /(node_modules)/, 42 | use: ['style-loader', 'css-loader', 'sass-loader'], 43 | } 44 | ] 45 | } 46 | }; 47 | --------------------------------------------------------------------------------