Skip to content

Commit

Permalink
ready for check
Browse files Browse the repository at this point in the history
  • Loading branch information
TkachenkoKaterina committed Feb 26, 2024
1 parent 94dbe83 commit d442e8c
Show file tree
Hide file tree
Showing 14 changed files with 344 additions and 25 deletions.
48 changes: 40 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"@testing-library/jest-dom": "^5.16.3",
"@testing-library/react": "^12.1.4",
"@testing-library/user-event": "^13.5.0",
"nanoid": "^5.0.6",
"prop-types": "^15.8.1",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-scripts": "5.0.1",
Expand Down
16 changes: 0 additions & 16 deletions src/components/App.jsx

This file was deleted.

64 changes: 64 additions & 0 deletions src/components/App/App.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { nanoid } from 'nanoid';
import ContactForm from '../ContactForm/ContactForm';
import ContactList from '../ContactList/ContactList';
import Filter from '../Filter/Filter';
import css from './App.module.css';

export class App extends Component {
state = {
contacts: [
{ id: 'id-1', name: 'Rosie Simpson', number: '459-12-56' },
{ id: 'id-2', name: 'Hermione Kline', number: '443-89-12' },
{ id: 'id-3', name: 'Eden Clements', number: '645-17-79' },
{ id: 'id-4', name: 'Annie Copeland', number: '227-91-26' },
],
filter: '',
};

handleFilter = e => {
this.setState({ filter: e.target.value });
};

handleSubmit = contact => {
this.setState(prevState => ({
contacts: [{ id: nanoid(), ...contact }, ...prevState.contacts],
}));
};

handleDeleteContact = id => {
this.setState(prevState => ({
contacts: prevState.contacts.filter(contact => contact.id !== id),
}));
};

render() {
const { filter, contacts } = this.state;

return (
<div className={css.container}>
<h1 className={css.titleFirst}>Phonebook</h1>
<ContactForm onSubmit={this.handleSubmit} contacts={contacts} />
<h2 className={css.titleFSecond}>Contacts</h2>
<Filter value={filter} onChange={this.handleFilter} />
<ContactList
contacts={contacts}
filter={filter}
onDeleteContact={this.handleDeleteContact}
/>
</div>
);
}
}

App.propTypes = {
contacts: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
number: PropTypes.string.isRequired,
})
),
filter: PropTypes.string,
};
15 changes: 15 additions & 0 deletions src/components/App/App.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
background-color: #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

.titleFirst {
color: #333;
}

.titleFSecond {
color: #555;
}
76 changes: 76 additions & 0 deletions src/components/ContactForm/ContactForm.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import css from './ContactForm.module.css';

class ContactForm extends Component {
state = {
name: '',
number: '',
};

handleChange = e => {
this.setState({ [e.target.name]: e.target.value });
};

handleSubmit = e => {
e.preventDefault();
const { name, number } = this.state;
const { contacts } = this.props;

const isDuplicate = contacts.some(
contact => contact.name.toLowerCase() === name.toLowerCase()
);

if (isDuplicate) {
alert(`${name} is already in contacts!`);
} else {
this.props.onSubmit({ name, number });
this.setState({ name: '', number: '' });
}
};

render() {
const { name, number } = this.state;

return (
<form className={css.form} onSubmit={this.handleSubmit}>
<label className={css.input} htmlFor="name">
Name
</label>
<input
className={css.form}
id="name"
type="text"
name="name"
value={name}
onChange={this.handleChange}
required
/>

<label className={css.label} htmlFor="tel">
Number
</label>
<input
className={css.input}
id="tel"
type="tel"
name="number"
value={number}
onChange={this.handleChange}
required
/>

<button className={css.button} type="submit">
Add contact
</button>
</form>
);
}
}

ContactForm.propTypes = {
onSubmit: PropTypes.func.isRequired,
contacts: PropTypes.array.isRequired,
};

export default ContactForm;
29 changes: 29 additions & 0 deletions src/components/ContactForm/ContactForm.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
form {
display: flex;
flex-direction: column;
}

label {
margin-bottom: 5px;
font-weight: bold;
}

input {
padding: 8px;
margin-bottom: 15px;
border: 1px solid #ccc;
border-radius: 4px;
}

button {
padding: 10px;
background-color: #4caf50;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
}

button:hover {
background-color: #45a049;
}
28 changes: 28 additions & 0 deletions src/components/ContactItem/ContactItem.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import PropTypes from 'prop-types';
import css from './ContactItem.module.css';

const ContactItem = ({ contact, onDeleteContact }) => {
return (
<li className={css.li}>
{contact.name}: {contact.number}
<button
className={css.button}
onClick={() => onDeleteContact(contact.id)}
>
Delete
</button>
</li>
);
};

ContactItem.propTypes = {
contact: PropTypes.shape({
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
number: PropTypes.string.isRequired,
}).isRequired,
onDeleteContact: PropTypes.func.isRequired,
};

export default ContactItem;
22 changes: 22 additions & 0 deletions src/components/ContactItem/ContactItem.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
li {
padding: 10px;
border: 1px solid #ccc;
margin-bottom: 10px;
border-radius: 4px;
display: flex;
justify-content: space-between;
}

button {
padding: 8px;
background-color: #3587e5;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
}

button:hover {
color: black;
background-color: #ffee05;
}
32 changes: 32 additions & 0 deletions src/components/ContactList/ContactList.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import ContactItem from 'components/ContactItem/ContactItem';
import PropTypes from 'prop-types';
import css from './ContactList.module.css';

const ContactList = ({ contacts, filter, onDeleteContact }) => {
const filteredContacts = contacts.filter(contact =>
contact.name.toLowerCase().includes(filter.toLowerCase())
);

return (
<ul className={css.ul}>
{filteredContacts.map(contact => {
return (
<ContactItem
key={contact.id}
contact={contact}
onDeleteContact={onDeleteContact}
/>
);
})}
</ul>
);
};

ContactList.propTypes = {
contacts: PropTypes.array.isRequired,
filter: PropTypes.string.isRequired,
onDeleteContact: PropTypes.func.isRequired,
};

export default ContactList;
4 changes: 4 additions & 0 deletions src/components/ContactList/ContactList.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
ul {
list-style: none;
padding: 0;
}
Loading

0 comments on commit d442e8c

Please sign in to comment.