diff --git a/lamdanotes/.gitignore b/lamdanotes/.gitignore new file mode 100644 index 0000000000..4d29575de8 --- /dev/null +++ b/lamdanotes/.gitignore @@ -0,0 +1,23 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/lamdanotes/README.md b/lamdanotes/README.md new file mode 100644 index 0000000000..fadf48deb1 --- /dev/null +++ b/lamdanotes/README.md @@ -0,0 +1 @@ +Trello: https://trello.com/b/b4MoDi9V/lambda-notes diff --git a/lamdanotes/package.json b/lamdanotes/package.json new file mode 100644 index 0000000000..092e51de0f --- /dev/null +++ b/lamdanotes/package.json @@ -0,0 +1,30 @@ +{ + "name": "lamdanotes", + "version": "0.1.0", + "private": true, + "dependencies": { + "@material-ui/core": "^3.9.2", + "react": "^16.8.1", + "react-dom": "^16.8.1", + "react-redux": "^6.0.1", + "react-router-dom": "^4.3.1", + "react-scripts": "2.1.5", + "reactstrap": "^7.1.0", + "redux": "^4.0.1" + }, + "scripts": { + "start": "react-scripts start", + "build": "react-scripts build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": "react-app" + }, + "browserslist": [ + ">0.2%", + "not dead", + "not ie <= 11", + "not op_mini all" + ] +} diff --git a/lamdanotes/public/favicon.ico b/lamdanotes/public/favicon.ico new file mode 100644 index 0000000000..a11777cc47 Binary files /dev/null and b/lamdanotes/public/favicon.ico differ diff --git a/lamdanotes/public/index.html b/lamdanotes/public/index.html new file mode 100644 index 0000000000..75980d58e2 --- /dev/null +++ b/lamdanotes/public/index.html @@ -0,0 +1,41 @@ + + + + + + + + + + + React App + + + +
+ + + diff --git a/lamdanotes/public/manifest.json b/lamdanotes/public/manifest.json new file mode 100644 index 0000000000..1f2f141faf --- /dev/null +++ b/lamdanotes/public/manifest.json @@ -0,0 +1,15 @@ +{ + "short_name": "React App", + "name": "Create React App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/lamdanotes/src/App.css b/lamdanotes/src/App.css new file mode 100644 index 0000000000..2ea60d1508 --- /dev/null +++ b/lamdanotes/src/App.css @@ -0,0 +1,504 @@ +/*CSS RESET*/ + +/* http://meyerweb.com/eric/tools/css/reset/ + v2.0 | 20110126 + License: none (public domain) +*/ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} + + +/*STYLING STARTS HERE*/ + +.container { + height: 100vh; + background-color: #F2F1F2; +} + +@import url('https://fonts.googleapis.com/css?family=Dosis|Open+Sans'); + +html { + font-family: 'Open Sans', sans-serif; + font-size: 16px; + color: #49525B; +} + +h1, h2 { + font-family: 'Dosis', sans-serif; + font-weight: bold; +} + +h3, h4, h5, h6 { + font-family: 'Dosis', sans-serif; + font-size: 20px; + padding-top: 15px; + padding-bottom: 20px; +} + + +/*-------------------------------Side Bar Styling*/ +.sideBarWrap { + width: 15em; + height: 100vh; + position: fixed; + background-color: #283D4A; +} + +.sbTitle { + width: 8.8em; + margin: 0; + font-size: 25px; + padding-top: 20px; + padding-bottom: 20px; + padding-left: 18px; + + background-color: #101B2F; + color: #91A8C3; +} + +.sbButtons { + display: flex; + flex-direction: row; + width: 18em; + padding-left: 15px; + margin: 10px auto 0; + text-align: left; + color: white; + border: none; + background-color: inherit; +} + +.sbButtons p { + padding-top: .3em; + padding-left: 1em; +} + +button:focus { + outline: 0; +} + +input:focus { + outline: 0; +} + + +/*-------------------------------Notes View Styling*/ + +.notesView { + max-width: 100%; + padding-left: 16em; + background-color: #F2F1F2; + height: auto; +} + +.nvTitle { + padding-top: 50px; + font-family: 'Dosis', sans-serif; + font-weight: bold; +} + +.noteBox { + /* padding-left: 16em; */ + display: inline-grid; + grid-template-rows: 1fr; + grid-template-columns: 15em 15em 15em; + grid-gap: 20px; +} + +.card-body { + border: 1px solid #FFF; + border-radius: 15px; + padding: 10px; + background-color: white; + box-shadow: 5px 5px 8px #D9D9D9; + line-height: 1.3em; + overflow: hidden; +} + +.card-text { + padding: .2em .2em .8em; + max-height: 4em; + overflow: hidden; +} + +.card-title { + font-size: 22px; + width: 99%; + border-bottom: 1px solid #91A8C3; + padding-bottom: .5em; +} + +.btn { + margin-top: .5em; + border: #1DA0E0; + background-color: #1DA0E0; + border-radius: 25px; + color: white; + padding: .4em .8em; +} + + +/*-------------------------------Add Note form Styling*/ + +.addForm { + width: 80%; + margin: 0 auto; + padding-left: 16em; + background-color: #F2F1F2; + height: 100vh; + display: flex; + flex-direction: column; +} + +.addTitle { + width: 40%; + height: 30px; + margin-bottom: 15px; + border-radius: 4px; + padding-left: 12px; +} +.addBody { + width: 50%; + height: 40%; + border-radius: 4px; + padding-left: 15px; + padding-top: 20px; +} + +.addButton { + width: 22%; + margin-top: 10px; + height: 5%; + margin-left: 2px; + + font-size: 16px; + font-weight: 600; + color: white; + + background-color: #1DA0E0; + border: .5px solid #B7B7B7; + border-radius: 25px; + box-shadow: -.5px .5px .8px #B7B7B7; +} + +a { + text-decoration: none; + color: inherit; +} + +/*-------------------------------Single Note Styling*/ +.viewNoteContainer { + width: 50%; + margin: 0 auto; + padding: 2% 5% 5% 16em ; + overflow-y: hidden; + background-color: #F2F1F2; +} + +.viewNoteTitle { + margin-top: 40px; + margin-bottom: 10px; +} + +.viewNoteLinks { + margin-left: 20px; + text-decoration: underline; + position: relative; + top: 10px; + left: 80%; +} + + + +/*-------------------------------Delete Modal*/ +.dModal { + position: fixed; + z-index: 1; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: hidden; + background-color: rgba(221, 221, 221, .7) +} + +.modalContent { + position: fixed; + top: 40%; + left: 40%; + padding: 40px 80px; + border: 1px solid black; + border-radius: 25px; + background-color: white; + text-align: center; +} + +.modalButtons { + width: 180px; + margin-top: 20px; + padding: 10px 10px; + background-color: #1DA0E0; + color: white; + font-weight: bold; + font-size: 16px; + border: 1px solid #1DA0E0; + border-radius: 25px; +} + +.firstButton { + color: white; + font-weight: bold; + margin-right: 25px; + background-color: #D0011B; + border: 1px solid #D0011B; +} + + + +/*-------------------------------EDIT FORM STYLING*/ + +.editForm { + width: 80%; + margin: 0 auto; + padding-left: 16em; + background-color: #F2F1F2; + height: 100vh; + display: flex; + flex-direction: column; +} + +.formTitles { + padding-top: 50px; +} + + +/*-------------------------------searchbar STYLING*/ +.searchWrapper { + width: 14em; + padding-bottom: 10px; + margin: 30px auto 50px; +} + +.search { + display: flex; + flex-direction: row; + border: #152134; + background-color:#152134; + height: 2.5em; + border-radius: 25px; + width: 100%; + +} + +.searchBar { + border: none; + flex-grow: 2; + padding-left: 15px; + background-color:#152134; + color: #fff; + border-radius: 25px; +} + +.searchBar::placeholder { + color: white; + font-family: 'Open Sans', sans-serif; + font-size: 11px; +} + +.searchButton { + border: none; + background-color:#152134; + color: #fff; + padding-right: 20px; + border-radius: 25px; +} + +.searchButton:hover { + cursor: pointer; +} + +/* -------------------------------------SORT STYLE */ +.alphaContainer { + width: 10em; + margin: 0 auto; + display: flex; + justify-content: left; +} + +.alphaSortButtons { + padding: 5px; +} + +.firstSort { + margin-right: 1em; + margin-left: .2em; +} + +/* -------------------------------------LOGIN PAGE */ +.loginFormWrapper { + width: 100%; + height:100vh; + background-color: #2E414C; +} + +.test{ + position: relative; + top: 30%; +} + +.appName { + width: 20.3%; + padding: 0; + height: 3em; + text-align: center; + margin: 0 auto; + text-align: center; + + + background-color: #152134; + border-top-left-radius: 25px; + border-top-right-radius: 25px; + color: #91A8C3; +} + +.appName h1 { + margin-top: 0; + font-size: 22px; + position: relative; + top: 20%; +} + +.headingOne { + padding: 0; + margin: 1.5em 0 .5em; +} + +.headingTwo { + padding: 0; + margin: 1em 0 .5em; +} + +.loginForm { + width: 20%; + height: 280px; + margin: 0 auto; + text-align: center; + + background-color: #FFF; + border: 1px solid #fff; + border-bottom-left-radius: 25px; + border-bottom-right-radius: 25px; + box-shadow: 3px 3px 5px #152134; +} + +.loginInputs { + width: 65%; + height: 2.5em; + background-color: #152134; + color: #fff; + border: #152134; + border-radius: 25px; + padding-left: 15px; +} + +.loginInputs::placeholder { + color: white; + font-family: 'Open Sans', sans-serif; + font-size: 11px; +} + +.loginSubmit { + margin-top: 1.5em; + margin-bottom: 2em; + width: 32.5%; + height: 2.5em; + + color: #fff; + background-color: #1DA0E0; + border: #1DA0E0; + border-radius: 25px; +} + + + +/* ERROR */ +.errorWrapper { + width: 100%; + height:100vh; + background-color: #2E414C; +} + +.error{ + position: relative; + top: 30%; +} + +.errorArea { + width: 20%; + height: 120px; + margin: 0 auto; + text-align: center; + + background-color: #FFF; + border: 1px solid #fff; + border-radius: 25px; + box-shadow: 3px 3px 5px #152134; +} + +.errorButton { + margin-top: 1.5em; + margin-bottom: 2em; + width: 32.5%; + height: 2.5em; + + color: #fff; + background-color: #1DA0E0; + border: #1DA0E0; + border-radius: 25px; +} + +.errorArea h2 { + margin-top: 1.5em; +} \ No newline at end of file diff --git a/lamdanotes/src/App.js b/lamdanotes/src/App.js new file mode 100644 index 0000000000..19b4f175f9 --- /dev/null +++ b/lamdanotes/src/App.js @@ -0,0 +1,213 @@ +import React, { Component } from 'react'; +import axios from 'axios'; +import { Route } from 'react-router-dom'; + + +import SidebarView from './sidebarComponent/sbView'; +import NotesView from './NotesComponent/NotesView/notesView'; +import AddNote from './formComponent/addNote'; +import SingleNote from './NotesComponent/singleNote/singleNote'; +import EditForm from './formComponent/editNote'; +import LoginPage from './LoginPage/loginview'; + +import './App.css'; + +class App extends Component { + constructor() { + super(); + this.state = { + notes: [], + cNote: "", + isLoggedIn: false, + loginFailed: true, + users: { + username: "test", + password: "test" + } + } + } + + // {/* Call to API and set the response data to our state */} + componentDidMount() { + this.getUpdatedNotes() + } + + // <---------------------{{{----API CALLS------}}}---------------------> + getUpdatedNotes = () => { + axios + .get('https://fe-notes.herokuapp.com/note/get/all') + .then(res => this.setState({ + notes: res.data + })) + .catch(err => console.log(err)) + } + + + addNote = (note) => { + axios + .post('https://fe-notes.herokuapp.com/note/create', note) + .then(res => console.log("returned from add",res)) + .catch(err => console.log(err)) + } + + deleteNote = (id) => { + axios + .delete(`https://fe-notes.herokuapp.com/note/delete/${id}`) + .then(res => { + const filtered = this.state.notes.filter(note => note._id !== id); + console.log(filtered) + this.setState({ + notes: filtered + }) + }) + .catch(err => console.log(err)); + } + + editNote = (id, obj) => { + axios + .put(`https://fe-notes.herokuapp.com/note/edit/${id}`, obj) + .then(res => console.log(res)) + .catch(err => console.log(err)) + } + + + // <---------------------{{{----EVENT HANDLERS------}}}---------------------> + setCNote = (id) => { + this.setState({ + cNote: id + }) + } + + searchNotes = (term) => { + console.log(term) + const filteredSearch = this.state.notes.filter(string => string.title.toUpperCase().includes(term.toUpperCase()) || string.textBody.toUpperCase().includes(term.toUpperCase()) ) + this.setState({ + notes: filteredSearch + }) + } + + handleAscendingSort = () => { + //map through state to create a new array (keeps us from mutating state) then sort through the new array to compare each title and sort in acsending order. + let nArr = this.state.notes.map(arr => {return arr}).sort((a,b) => { + + const titleA = a.title.toUpperCase(); + const titleB = b.title.toUpperCase(); + + let comparison = 0; + + if(titleA > titleB) { + comparison = 1 + } else if (titleA < titleB) { + comparison = -1; + } + return comparison + }) + + //set the state to the nArr so view will update + this.setState({ + notes: nArr + }) + } + + handleDescendingSort = () => { + + let nArr = this.state.notes.map(arr => {return arr}).sort((a,b) => { + + const titleA = a.title.toUpperCase(); + const titleB = b.title.toUpperCase(); + + let comparison = 0; + + if(titleA < titleB) { + comparison = 1 + } else if (titleA > titleB) { + comparison = -1; + } + return comparison + }) + + this.setState({ + notes: nArr + }) + } + + handleLogin = obj => { + console.log(obj.username) + if(obj.username === this.state.users.username && obj.password === this.state.users.password) { + this.setState({ + isLoggedIn: true + }) + } else { + console.log("failed") + } + } + + handleLogout = () => { + this.setState({ + isLoggedIn: false + }) + } + +handleError = () => { + +} + +// <---------------------{{{----APPLICATION/ROUTES------}}}---------------------> + +// {/* Declare Routes, Sidebar navigation should always show so it is the root */} + render() { + if(this.state.isLoggedIn === false) { + return( + ( + + )} /> + ) + } + return ( +
+ + ( + )} /> + + ( )} + /> + + ( )} + /> + + ( + + )} + /> + + + ( )} + /> + + +
+ ); + } +} + +export default App; diff --git a/lamdanotes/src/LoginPage/error.js b/lamdanotes/src/LoginPage/error.js new file mode 100644 index 0000000000..d79bf6162a --- /dev/null +++ b/lamdanotes/src/LoginPage/error.js @@ -0,0 +1,24 @@ +import React from 'react'; + +//props from loginview.js +//handleLogout={this.handleLogout} + +const Error = props => { + + return( +
+
+
+

Invalid UserName or Password

+ + +
+
+
+ ) +} + +export default Error \ No newline at end of file diff --git a/lamdanotes/src/LoginPage/loginview.js b/lamdanotes/src/LoginPage/loginview.js new file mode 100644 index 0000000000..896317a4aa --- /dev/null +++ b/lamdanotes/src/LoginPage/loginview.js @@ -0,0 +1,95 @@ +import React from 'react'; + +import Error from './error'; + +//props from app.js +//handleLogin={this.handleLogin} +// loginFailed={this.state.loginFailed} +//handleLogout={this.handleLogout} + +class LoginPage extends React.Component { + constructor(props) { + super(props); + this.state={ + users: { + username: '', + password: '' + } + } + } + + handleChange = e => { + this.setState({ + users: { + ...this.state.users, + [e.target.name]: e.target.value + } + }); + } + + sendAndReset = e => { + this.props.handleLogin(this.state.users); + this.setState({ + notes: { + username: "", + password: "" + } + }); + } + + + render() { + // if(this.props.loginFailed === true) { + // return( + // + // ) + // } + console.log(this.state.users) + return( +
+
+ +
+

Login to Lambda Notes

+
+ +
+ + +
Username:
+ + +
Password:
+ + +
+ + + +
+
+
+ ) + } +} + +export default LoginPage \ No newline at end of file diff --git a/lamdanotes/src/NotesComponent/NotesView/note.js b/lamdanotes/src/NotesComponent/NotesView/note.js new file mode 100644 index 0000000000..6a0a11510a --- /dev/null +++ b/lamdanotes/src/NotesComponent/NotesView/note.js @@ -0,0 +1,18 @@ +import React from 'react'; +import { Card, Button, CardTitle, CardText, Row, Col } from 'reactstrap'; + +const Note = (props) => { + return ( + + + + {props.title} + {props.body} + + + + + ); +}; + +export default Note; \ No newline at end of file diff --git a/lamdanotes/src/NotesComponent/NotesView/notesIterator.js b/lamdanotes/src/NotesComponent/NotesView/notesIterator.js new file mode 100644 index 0000000000..d390de0218 --- /dev/null +++ b/lamdanotes/src/NotesComponent/NotesView/notesIterator.js @@ -0,0 +1,24 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; + +import Note from './note.js'; + + +//props from notesView.js +//notes={props.notes} + +const NotesIterator = props => { + console.log("props from notesIterator", props) + return ( +
+ {props.notes.map( note => { + return ( + + + + )})} +
+ ) +} + +export default NotesIterator \ No newline at end of file diff --git a/lamdanotes/src/NotesComponent/NotesView/notesView.js b/lamdanotes/src/NotesComponent/NotesView/notesView.js new file mode 100644 index 0000000000..08c401723b --- /dev/null +++ b/lamdanotes/src/NotesComponent/NotesView/notesView.js @@ -0,0 +1,18 @@ +import React from 'react'; + +import NotesIterator from './notesIterator'; + +//props from app.js +//notes={this.state.notes} + +const NotesView = props => { + console.log("props from notesView", props) + return ( +
+
Your Notes:

+ +
+ ) +} + +export default NotesView \ No newline at end of file diff --git a/lamdanotes/src/NotesComponent/singleNote/deleteModal.js b/lamdanotes/src/NotesComponent/singleNote/deleteModal.js new file mode 100644 index 0000000000..8fa2d24e53 --- /dev/null +++ b/lamdanotes/src/NotesComponent/singleNote/deleteModal.js @@ -0,0 +1,27 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; + +//props from singleNote {...props} id={this.state.notes._id} deleteNote={this.deleteNote} + +const DeleteModal = (props) => { + return( +
+ + Are you sure you want to delete this?
+ {/* needs onClick later to delete */} + + + + + +
+
+ ) +} + +export default DeleteModal \ No newline at end of file diff --git a/lamdanotes/src/NotesComponent/singleNote/singleNote.js b/lamdanotes/src/NotesComponent/singleNote/singleNote.js new file mode 100644 index 0000000000..e70a30e83d --- /dev/null +++ b/lamdanotes/src/NotesComponent/singleNote/singleNote.js @@ -0,0 +1,69 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import { Route } from 'react-router-dom'; + +import DeleteModal from './deleteModal'; + + + +//props from app note={this.state.notes} deleteNote={this.deleteNote} + +class SingleNote extends React.Component { + constructor(props) { + super(props); + this.state = { + notes: {}, + modalDisplay: true + } + } + + componentDidMount() { + // fill this in + let noteId; + if (this.props.note._id) { + noteId = this.props.note._id; + } else { + noteId = this.props.match.params.id; + } + const foundNote = this.props.note.find(n => n._id === noteId); + if (!foundNote) return; + this.setState({ notes: foundNote }); + } + + + render() { + console.log(this.state.notes) + return( +
+ {/* link to edit note and send the id for this note. onClick will send the id of this note to app.js */} + {this.props.setCNote(this.state.notes._id)}} + > + edit + + + + delete + + + ( )} + /> + + + +

{this.state.notes.title}

+

{this.state.notes.textBody}

+ +
+ ) + } +} + +export default SingleNote \ No newline at end of file diff --git a/lamdanotes/src/formComponent/addNote.js b/lamdanotes/src/formComponent/addNote.js new file mode 100644 index 0000000000..1db0ed0c88 --- /dev/null +++ b/lamdanotes/src/formComponent/addNote.js @@ -0,0 +1,67 @@ +import React, { Component } from 'react'; + + +//passed as props: +//{...props} addNote={this.addNote} + +class AddNote extends Component { + constructor(props) { + super(props); + this.state = { + notes: { + textBody: "", + title: "" + } + } + } + + handleChange = e => { + this.setState({ + notes: { + ...this.state.notes, + [e.target.name]: e.target.value + } + }); + } + + sendAndReset = e => { + this.props.addNote(this.state.notes); + this.setState({ + notes: { + textBody: "", + title: "" + } + }); + } + + render() { + console.log('Props from addForm', this.props) + console.log('State from addForm', this.state) + return( +
+

Create New Note:

+ +