Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Contact List #76

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
21,918 changes: 11,342 additions & 10,576 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
"@testing-library/jest-dom": "^5.11.10",
"@testing-library/react": "^11.2.6",
"@testing-library/user-event": "^12.8.3",
"bootstrap": "^5.2.3",
"lodash": "^4.17.21",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^5.3.0",
"react-scripts": "4.0.3",
"web-vitals": "^1.1.1"
},
Expand Down
39 changes: 22 additions & 17 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
import logo from './logo.svg';
import './App.css';
import { Switch, Route } from "react-router-dom";
import React, { useState } from "react";
import Contacts from "./Contacts";
import ContactList from "./ContactList";

function App() {
const [contacts, setContacts] = useState([]);

const addContact = (contact) => {
setContacts((contacts) => {
return [...contacts, contact];
});
};

return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
<div>
<Switch>
<Route exact path="/" component={ContactList} />
<Route
path="/contacts"
render={() => (
<Contacts addContact={addContact} contacts={contacts} />
)}
/>
</Switch>
</div>
);
}
Expand Down
60 changes: 60 additions & 0 deletions src/ContactList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Link, useHistory } from "react-router-dom";
import PropTypes from "prop-types";

const ContactList = ({ contacts }) => {
const history = useHistory();

return (
<div>
<h1 className="text-center mt-5">Contact List</h1>
<div className="align-content-center mx-5 w-75 mx-auto">
<Link to="/contacts/new">
<button className="btn btn-primary">Add Contact</button>
</Link>
<table className="table table-hover table-bordered mt-3">
<thead>
<tr>
<th scope="col">Profile Pic</th>
<th scope="col">Name</th>
<th scope="col">Email</th>
<th scope="col">Phone Number</th>
</tr>
</thead>
<tbody>
{contacts.map((contact) => (
<tr
onClick={() => history.push(`/contacts/${contact.id}`)}
key={contact.id}
>
<td className="">
<img
className="mx-auto d-block img-fluid"
style={{ maxWidth: "200px", maxHeight: "200px" }}
src={contact.image_url}
/>
</td>
<td className="align-middle">{contact.name}</td>
<td className="align-middle">{contact.email}</td>
<td className="align-middle">{contact.phone_number}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
};

ContactList.propTypes = {
contacts: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
email: PropTypes.string.isRequired,
phone_number: PropTypes.number.isRequired,
image_url: PropTypes.string.isRequired,
})
).isRequired,
};

export default ContactList;
103 changes: 103 additions & 0 deletions src/ContactNew.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import React, { useState } from "react";
import PropTypes from "prop-types";

const ContactNew = (props) => {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [phone_number, setPhone] = useState("");
const [image_url, setImage] = useState("");

const handleSubmitContact = () => {
if (
name === "" ||
email === "" ||
phone_number === "" ||
image_url === ""
) {
alert("Please fill in all the fields");
return;
}

const generateId = () => Math.round(Math.random() * 100000000);
let id = generateId();

props.addContact({
id,
name,
email,
phone_number,
image_url,
});

props.history.push("/contacts");
};

return (
<div>
<h1 className="text-center mt-5">Contact List</h1>
<form className="col-md-6 offset-md-3">
<div class="form-group mt-5">
<label>Full Name</label>
<input
type="text"
className="form-control mt-2"
placeholder="Enter Full Name"
onChange={(event) => setName(event.target.value)}
/>
</div>
<div class="form-group mt-3">
<label>Email Address</label>
<input
type="email"
className="form-control mt-2"
placeholder="Enter Email"
onChange={(event) => setEmail(event.target.value)}
/>
</div>
<div class="form-group mt-3">
<label>Phone Number</label>
<input
type="text"
className="form-control mt-2"
placeholder="Enter Phone"
onChange={(event) => setPhone(event.target.value)}
/>
</div>
<div class="form-group mt-3">
<label>Image URL</label>
<input
type="text"
className="form-control mt-2"
placeholder="Image URL"
onChange={(event) => setImage(event.target.value)}
/>
</div>
<button
type="button"
onClick={handleSubmitContact}
className="btn btn-primary mt-3"
>
Add Contact
</button>
</form>
</div>
);
};

ContactNew.propTypes = {
addContact: PropTypes.func.isRequired,
history: PropTypes.shape({
push: PropTypes.func.isRequired,
}).isRequired,
contacts: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
email: PropTypes.string.isRequired,
phone_number: PropTypes.number.isRequired,
image_url: PropTypes.string.isRequired,
})
).isRequired,
};

export default ContactNew;
44 changes: 44 additions & 0 deletions src/Contacts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Switch, Route } from "react-router-dom";
import PropTypes from "prop-types";
import ContactNew from "./ContactNew";
import Profile from "./Profile";
import ContactList from "./ContactList";

const Contacts = ({ contacts, addContact }) => {
return (
<Switch>
<Route
path="/contacts/new"
render={(routerProps) => (
<ContactNew
history={routerProps.history}
contacts={contacts}
addContact={addContact}
/>
)}
/>

<Route
path="/contacts/:id"
render={(routerProps) => (
<Profile
contactId={parseInt(routerProps.match.params.id, 10)}
contacts={contacts}
/>
)}
/>

<Route
path="/contacts"
render={() => <ContactList contacts={contacts} />}
/>
</Switch>
);
};

Contacts.propTypes = {
contacts: PropTypes.array.isRequired,
addContact: PropTypes.func.isRequired,
};

export default Contacts;
54 changes: 54 additions & 0 deletions src/Profile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Link } from "react-router-dom";
import PropTypes from "prop-types";
import React from "react";
import _ from "lodash";

const Profile = ({ contactId, contacts }) => {
const contact = _.find(contacts, { id: contactId });

if (!contact) {
return (
<div className="text-center mt-5">
<h3>Sorry, contact was not found.</h3>
</div>
);
}

return (
<div>
<h1 className="text-center mt-5">Contact List</h1>
<div className="text-center mt-5">
<div className="d-inline-block border">
<img
src={contact.image_url}
className="img-fluid"
style={{ maxWidth: "300px", maxHeight: "300px" }}
/>
<h4 className="mt-3">{contact.name}</h4>
<p className="mt-3">{contact.email}</p>
<p>{contact.phone_number}</p>
</div>
</div>
<div className="text-center mt-3">
<Link to="/contacts">
<button className="btn btn-primary">Back</button>
</Link>
</div>
</div>
);
};

Profile.propTypes = {
contactId: PropTypes.number.isRequired,
contacts: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
email: PropTypes.string.isRequired,
phone_number: PropTypes.number.isRequired,
image_url: PropTypes.string.isRequired,
})
).isRequired,
};

export default Profile;
18 changes: 11 additions & 7 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import "bootstrap/dist/css/bootstrap.min.css";
import { BrowserRouter } from "react-router-dom";

ReactDOM.render(
<React.StrictMode>
<App />
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>,
document.getElementById('root')
document.getElementById("root")
);

// If you want to start measuring performance in your app, pass a function
Expand Down