Skip to content
This repository has been archived by the owner on Jun 28, 2022. It is now read-only.

Starter files added #17

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
796c135
Updated dependencies.
veronikabenkeser Nov 20, 2018
bdea78a
Update README.md
veronikabenkeser Nov 20, 2018
838c1d5
Update dependencies
veronikabenkeser Nov 20, 2018
086ad9c
Change order of dependencies
veronikabenkeser Nov 20, 2018
7e740f2
installed src code
khaloodi Jun 18, 2021
3f81993
building ListContacts component
khaloodi Jun 18, 2021
dc08db2
changed names to match data coming from server
khaloodi Jun 18, 2021
2e78826
using state to hold contact array info
khaloodi Jun 25, 2021
b115837
added removeContact method and hooked up in LC
khaloodi Jul 1, 2021
323491e
added input field to LC comp and updateQuery fn
khaloodi Jul 1, 2021
848c7d5
binded search state with listcontact component
khaloodi Jul 1, 2021
0a1e0ed
filter working added showingContacts to LC
khaloodi Jul 1, 2021
4870a36
added bar below input field if filter is active
khaloodi Jul 3, 2021
1c61d0a
added now showing # of # and added class
khaloodi Jul 3, 2021
74eb9b7
added the clearQuery and attached to show all
khaloodi Jul 3, 2021
b4ab0a9
using ContactsAPI to fetch and remove
khaloodi Jul 15, 2021
f32ff73
dynamic screens
khaloodi Jul 16, 2021
e3a7dd7
dynamic routing to show how back button doen't wrk
khaloodi Jul 16, 2021
2103313
added router and BrowserRouter in index.js
khaloodi Jul 16, 2021
ad7999f
setup routes in app.js and removed screen state
khaloodi Jul 16, 2021
18178aa
added imageinput component and link in cc.js
khaloodi Jul 17, 2021
2ac1515
wip
khaloodi Jul 25, 2021
82c8932
added serializeForm to CreateContact.js
khaloodi Jul 25, 2021
11d0905
save new contact to the server
khaloodi Jul 25, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ Most of the commits in this repository correspond to videos in the program.
Because this is a code-along project and the commits correspond to specific videos in the program, we will not be accepting pull requests.

If you feel like there's a major problem, please open an issue to discuss the problem and potential resolution.

## Contributing

MIT
32,963 changes: 32,963 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

26 changes: 20 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,28 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-scripts": "1.1.1"
"escape-string-regexp": "^1.0.5",
"form-serialize": "^0.7.2",
"prop-types": "^15.7.2",
"react": "^16.6.3",
"react-dom": "^16.6.3",
"react-router-dom": "^4.3.1",
"react-scripts": "2.1.1",
"sort-by": "^1.2.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"test": "react-scripts test",
"eject": "react-scripts eject"
}
}
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}
69 changes: 68 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,77 @@
import React, { Component } from 'react';
import ListContacts from './ListContacts'
import * as ContactsAPI from './utils/ContactsAPI'
import CreateContact from './CreateContact'
import { Route } from 'react-router-dom'

class App extends Component {
state = {
contacts: [],
// screen: 'list'
}

componentDidMount() {
ContactsAPI.getAll()
.then((contacts) => {
this.setState(() => ({
contacts
}))
})
}
removeContact = contact => {
this.setState((currentState) => ({
contacts: currentState.contacts.filter((c) => {
return c.id !== contact.id
})
}))
ContactsAPI.remove(contact)

createContact = (contact) => {
ContactsAPI.create(contact)
.then((contact)=> {
this.setState((currentState) => ({
contacts: currentState.contacts.concat([contact])
}))
})
}
}
render() {
return (
<div>
Hello World
<Route exact path='/' render={()=> (
<ListContacts
contacts={this.state.contacts}
onDeleteContact={this.removeContact}
// onNavigate={()=> {
// this.setState(()=>({
// screen: 'create'
// }))
// }}
/>
)} />
{/* <Route path='/create' component={CreateContact} /> */}
{/* {this.state.screen === 'list' && (
<ListContacts
contacts={this.state.contacts}
onDeleteContact={this.removeContact}
onNavigate={()=> {
this.setState(()=>({
screen: 'create'
}))
}}
/>
)}
{this.state.screen === 'create' && (
<CreateContact />
)} */}
<Route path='/create' render={({history}) => (
<CreateContact
onCreateContact={(contact) => {
this.createContact(contact)
history.push('/')
}}
/>
)} />
</div>
);
}
Expand Down
41 changes: 41 additions & 0 deletions src/CreateContact.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import ImageInput from './ImageInput'
import serializeForm from 'form-serialize'

class CreateContact extends Component {
handleSubmit = (e) => {
e.preventDefault()
const values = serializeForm(e.target, { hash: true })

if (this.props.onCreateContact) {
this.props.onCreateContact(values)
}
}

render() {
return (
<div>
<Link
className='close-create-contact'
to='/'>
Close
</Link>
<form onSubmit={this.handleSubmit} className='create-contact-form'>
<ImageInput
className='create-contact-avatar-input'
name='avatarURL'
maxHeight={64}
/>
<div className='create-contact-details'>
<input type='text' name='name' placeholder='Name'/>
<input type='text' name='handle' placeholder='Handle'/>
<button>Add Contact</button>
</div>
</form>
</div>
)
}
}

export default CreateContact
3 changes: 3 additions & 0 deletions src/ImageInput.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// ****** NOTES ******
// The ImageInput component is a custom <input> that dynamically reads and resizes image files before submitting them to the server as data URLs. It also shows a preview of the image. We chose to give this component to you rather than build it ourselves because it contains features related to files and images on the web that aren't crucial to your education in this context. If you're curious, feel free to dive into the code, but know it's not a requirement.

import React from 'react'
import PropTypes from 'prop-types'

Expand Down
129 changes: 129 additions & 0 deletions src/ListContacts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import React, {Component} from 'react'
import { Link } from 'react-router-dom'
import PropTypes from 'prop-types'

class ListContacts extends Component {
static propTypes = {
contacts: PropTypes.array.isRequired,
onDeleteContact: PropTypes.func.isRequired
}

state = {
query: ''
}

updateQuery = (query) => {
this.setState(()=> ({
query: query.trim()
}))
}

clearQuery = () => {
this.updateQuery('')
}

render() {
const { query } = this.state
const { contacts, onDeleteContact, onNavigate } = this.props

// sticking the input field onto components state to trigger re-render on input then filter that input to perform working search
const showingContacts = query === ''
? contacts
: contacts.filter(contact => (
contact.name.toLowerCase().includes(query.toLowerCase())
))

return (
<div className = 'list-contacts'>
{/* {JSON.stringify(this.state)} */}
<div className= 'list-contacts-top'>
<input
className='search-contacts'
type="text"
placeholder='Search Contacts'
value={query}
onChange={(event)=> {this.updateQuery(event.target.value)}}
// To recap how user input affects the ListContacts component's own state:
// 1. The user enters text into the input field.
// 2. The onChange event listener invokes the updateQuery() function.
// 3. updateQuery() then calls setState(), merging in the new state to update the component's internal state.
// 4. Because its state has changed, the ListContacts component re-renders.
/>
<Link
// href='#create'
to='/create'
// onClick={onNavigate}
className='add-contact'
>Add contact</Link>
</div>

{/* When a user is typing into input field aka query, the contacts.length will be different, in other words, we want to know if we're filtering out any contacts, if so, display the bar with showing # out of how many contacts */}
{/* using logical && for if condition below */}
{showingContacts.length !== contacts.length && ( // true && ... do something if true
<div className='showing-contacts'>
<span>Now showing {showingContacts.length} of {contacts.length}</span>
<button onClick={this.clearQuery}>Show All</button>
</div>
)}

<ol className='contacts-list'>
{showingContacts.map( contact => (
(
<li key={contact.id} className='contact-list-item'>
<div
className='contact-avatar'
style={{ //first bracket says lets go into JS, 2nd bracket makes it an object
backgroundImage: `url(${contact.avatarURL})`
}}
></div>
<div className='contact-details'>
<p>{contact.name}</p>
<p>{contact.handle}</p>
</div>
<button
onClick={() => onDeleteContact(contact)}
className='contact-remove'>
Remove
</button>
</li>
)
))}
</ol>
</div>
)
}
}
// const ListContacts = (props) => {
// return (
// <ol className='contacts-list'>
// {props.contacts.map( contact => (
// (
// <li key={contact.id} className='contact-list-item'>
// <div
// className='contact-avatar'
// style={{ //first bracket says lets go into JS, 2nd bracket makes it an object
// backgroundImage: `url(${contact.avatarURL})`
// }}
// ></div>
// <div className='contact-details'>
// <p>{contact.name}</p>
// <p>{contact.handle}</p>
// </div>
// <button
// onClick={() => props.onDeleteContact(contact)}
// className='contact-remove'>
// Remove
// </button>
// </li>
// )
// ))}
// </ol>
// )
// }

// ListContacts.propTypes = {
// contacts: PropTypes.array.isRequired,
// onDeleteContact: PropTypes.func.isRequired
// }

export default ListContacts
6 changes: 5 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter} from 'react-router-dom'
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';

ReactDOM.render(<App />, document.getElementById('root'));
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>, document.getElementById('root'));
registerServiceWorker();
31 changes: 31 additions & 0 deletions src/utils/ContactsAPI.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const api = process.env.REACT_APP_CONTACTS_API_URL || 'http://localhost:5001'

let token = localStorage.token

if (!token)
token = localStorage.token = Math.random().toString(36).substr(-8)

const headers = {
'Accept': 'application/json',
'Authorization': token
}

export const getAll = () =>
fetch(`${api}/contacts`, { headers })
.then(res => res.json())
.then(data => data.contacts)

export const remove = (contact) =>
fetch(`${api}/contacts/${contact.id}`, { method: 'DELETE', headers })
.then(res => res.json())
.then(data => data.contact)

export const create = (body) =>
fetch(`${api}/contacts`, {
method: 'POST',
headers: {
...headers,
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
}).then(res => res.json())
Loading