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

Eval submission #90

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
35 changes: 32 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
## Contact List
# Contact List

This project has been created by a student at Parsity, an online software engineering course. The work in this repository is wholly of the student based on a sample starter project that can be accessed by looking at the repository that this project forks.
Hey! This is a contact list app that I built using React and React Router.

If you have any questions about this project or the program in general, visit [parsity.io](https://parsity.io/) or email [email protected].
## Run locally

As a prerequisite, make sure you have [Node](https://nodejs.org/en) installed on your machine.

To run locally, fork and clone this repository.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice job on the readme


In the directory you want to install this app in, run the following commands in your bash terminal:

```
git clone [repo link here]
cd contact-list
npm install
npm start
```

That's it! Now you should be able to use the app at `http://localhost:3000/`.

## Features

To get started, click "New" to create a new contact.

Fill out as much (or as little) information about your contact as you would like. If you leave your contact blank, they will show up as No Name in your contacts sidebar.

To edit a contact once you've created it, click "Edit" and change whichever fields you would like.

Once you have a number of contacts created, you might want to search for one in your contacts search bar. This will sort through your contacts and display any that match your search term.

If you find yourself on a page that doesn't exist, you will run into a 404 error page and be redirected back here.

⚠ Note: Right now, this app does not utilize local storage, so if you reload the browser, your contacts will be lost.
47,141 changes: 21,551 additions & 25,590 deletions package-lock.json

Large diffs are not rendered by default.

20 changes: 15 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@
"@testing-library/jest-dom": "^5.11.10",
"@testing-library/react": "^11.2.6",
"@testing-library/user-event": "^12.8.3",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "4.0.3",
"bootstrap": "^5.3.2",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-drag-drop-files": "^2.3.10",
"react-phone-number-input": "^3.3.6",
"react-router-dom": "^6.16.0",
"react-scripts": "^5.0.1",
"web-vitals": "^1.1.1"
},
"scripts": {
Expand All @@ -19,8 +24,7 @@
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
"wesbos"
]
},
"browserslist": {
Expand All @@ -34,5 +38,11 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"eslint": "^8.0.0",
"eslint-config-wesbos": "^3.2.3",
"prettier": "^2.7.1",
"typescript": "^4.8.4"
}
}
Binary file removed public/logo192.png
Binary file not shown.
Binary file removed public/logo512.png
Binary file not shown.
10 changes: 0 additions & 10 deletions public/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,6 @@
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
Expand Down
99 changes: 81 additions & 18 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,87 @@
import logo from './logo.svg';
import './App.css';
import { Route, Routes } from 'react-router-dom';
import { useState } from 'react';
import Root from './components/Root';
import StartHere from './components/StartHere';
import NewContact from './components/NewContact';
import Contact from './components/Contact';
import NotFound from './components/NotFound';
import EditContact from './components/EditContact';

function App() {
const [contactList, setContactList] = useState([]);

const addContact = (data) => {
const copyOfContactList = contactList.map((contact) => {
const clone = { ...contact };
return clone;
});
copyOfContactList.push(data);
setContactList(copyOfContactList);
};

const replaceContact = (data) => {
const copyOfContactList = contactList.map((contact) => {
if (contact.contactId === data.contactId) {
return data;
}

const clone = { ...contact };
return clone;
});

setContactList(copyOfContactList);
};

const getContact = (contactId) =>
contactList.find((element) => element.contactId === contactId);

const deleteContact = (data) => {
const copyOfContactList = contactList
.map((contact) => {
const clone = { ...contact };
return clone;
})
.filter((contact) => {
if (contact.contactId === data.contactId) {
return false;
}

return true;
});

setContactList(copyOfContactList);
};

const allContacts = () => contactList;

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>
<Routes>
<Route path="/" element={<Root allContacts={allContacts} />}>
<Route index element={<StartHere />} />
<Route
path="/contacts/new"
element={<NewContact addContact={addContact} />}
/>
<Route path="/contacts/:contactId">
<Route
index
element={
<Contact getContact={getContact} deleteContact={deleteContact} />
}
/>
<Route
path="edit"
element={
<EditContact
replaceContact={replaceContact}
getContact={getContact}
/>
}
/>
</Route>
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
);
}

Expand Down
94 changes: 94 additions & 0 deletions src/components/Contact.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { useLocation, useNavigate } from 'react-router-dom';
import PropTypes from 'prop-types';
import {
formatPhoneNumber,
parsePhoneNumber,
formatPhoneNumberIntl,
} from 'react-phone-number-input';
import NotFound from './NotFound';

export default function Contact({ getContact, deleteContact }) {
const navigate = useNavigate();
const location = useLocation();

const currentContactId = location.pathname.replace(/^\//, '');

if (!getContact(currentContactId)) {
return <NotFound />;
}

const contact = getContact(currentContactId);
let displayPhoneNumber = '';

if (contact.phone) {
const phoneNumber = parsePhoneNumber(contact.phone);
displayPhoneNumber = formatPhoneNumberIntl(phoneNumber.number);

if (phoneNumber.country === 'US') {
displayPhoneNumber = formatPhoneNumber(phoneNumber.number);
}
}

const handleDelete = () => {
if (
window.confirm(
'Are you sure you want to delete this contact? This action cannot be undone.'
)
) {
deleteContact(contact);
navigate('/');
}
};

const noName = <i>No Name</i>;

return (
<div className="d-flex flex-wrap">
<div className="p-2">
<img
src={
contact.profilePicture ||
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice way to set a default

'https://cdn.pixabay.com/photo/2015/10/05/22/37/blank-profile-picture-973460_1280.png'
}
alt="contact"
className="img-thumbnail custom-img"
/>
</div>
<div className="p-2">
<h1>{contact.name || noName}</h1>
<h3>{displayPhoneNumber}</h3>
{contact.email ? (
<p>
<strong>Email: </strong>
<a href={`mailto:${contact.email}`}>{contact.email}</a>
</p>
) : (
''
)}
<div className="d-flex flex-wrap">
<button
type="button"
className="btn btn-success me-2 mt-2"
onClick={() =>
navigate(`/${contact.contactId}/edit`, { state: contact })
}
>
Edit
</button>
<button
type="button"
className="btn btn-danger mt-2"
onClick={handleDelete}
>
Delete
</button>
</div>
</div>
</div>
);
}

Contact.propTypes = {
getContact: PropTypes.func,
deleteContact: PropTypes.func,
};
59 changes: 59 additions & 0 deletions src/components/ContactList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/* eslint-disable no-shadow */
import { NavLink } from 'react-router-dom';
import PropTypes from 'prop-types';
import ContactListItem from './ContactListItem';

export default function ContactList({ allContacts, sortedContacts }) {
let contactList = allContacts();

const sortContacts = (contactList, value) => {
const regex = value.toLowerCase();

const contacts = contactList
.map((contact) => {
const clone = { ...contact };
return clone;
})
.filter((contact) => {
if (contact.name.toLowerCase().match(regex)) {
return true;
}

return false;
});

return contacts;
};

if (sortedContacts !== null && sortedContacts.length > 0) {
contactList = sortContacts(contactList, sortedContacts);
}

if (contactList.length <= 0) {
return (
<div className="list-group border-0 rounded-0 text-md-start">
<NavLink
to="/contacts/new"
className="list-group-item border-end-0 d-inline-block text-truncate text-center"
>
<i>Add a new contact</i>
</NavLink>
</div>
);
}

const contacts = contactList.map((contact, index) => (
<ContactListItem key={index} contact={contact} />
));

return (
<div className="list-group border-0 rounded-0 text-md-start">
{contacts}
</div>
);
}

ContactList.propTypes = {
allContacts: PropTypes.func,
sortedContacts: PropTypes.string,
};
19 changes: 19 additions & 0 deletions src/components/ContactListItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { NavLink } from 'react-router-dom';
import PropTypes from 'prop-types';

export default function ContactListItem({ contact }) {
const noName = <i>No Name</i>;

return (
<NavLink
to={`${contact.contactId}`}
className="list-group-item border-end-0 d-inline-block text-truncate"
>
{contact.name || noName}
</NavLink>
);
}

ContactListItem.propTypes = {
contact: PropTypes.object,
};
Loading