Skip to content

Commit

Permalink
✅ Final Version Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
yevheniidatsenko committed Feb 15, 2024
1 parent 1301c88 commit 47dc857
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 96 deletions.
99 changes: 53 additions & 46 deletions src/components/App/App.jsx
Original file line number Diff line number Diff line change
@@ -1,77 +1,84 @@
import React, { useState } from 'react';
import React, { Component } from 'react';
import { nanoid } from 'nanoid';

import styles from './App.module.css';
import ContactForm from '../ContactForm/ContactForm';
import ContactList from '../ContactList/ContactList';
import Filter from '../Filter/Filter';

const App = () => {
const [contacts, setContacts] = useState([
{ id: 'id-1', name: 'Beyoncé', number: '342-12-44' },
{ id: 'id-2', name: 'Drake', number: '440-22-78' },
{ id: 'id-3', name: 'Taylor Swift', number: '897-11-20' },
{ id: 'id-4', name: 'Shawn Mendes', number: '284-91-51' },
]);

const [filter, setFilter] = useState('');
class App extends Component {
state = {
contacts: [
{ id: 'id-1', name: 'Beyoncé', number: '342-12-44' },
{ id: 'id-2', name: 'Drake', number: '440-22-78' },
{ id: 'id-3', name: 'Taylor Swift', number: '897-11-20' },
{ id: 'id-4', name: 'Shawn Mendes', number: '284-91-51' },
],
filter: '',
};

const addContact = contact => {
const isInContacts = contacts.some(
addContact = contact => {
const isInContacts = this.state.contacts.some(
({ name }) => name.toLowerCase() === contact.name.toLowerCase()
);

if (isInContacts) {
alert(`${contact.name} is already in contacts`);
return;
}

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

const changeFilter = event => {
setFilter(event.target.value);
changeFilter = event => {
this.setState({ filter: event.target.value });
};

const getVisibleContacts = () => {
getVisibleContacts = () => {
const { filter, contacts } = this.state;
const normalizedFilter = filter.toLowerCase();

return contacts.filter(contact =>
contact.name.toLowerCase().includes(normalizedFilter)
);
};

const removeContact = contactId => {
setContacts(prevContacts =>
prevContacts.filter(({ id }) => id !== contactId)
);
removeContact = contactId => {
this.setState(prevState => {
return {
contacts: prevState.contacts.filter(({ id }) => id !== contactId),
};
});
};

const visibleContacts = getVisibleContacts();
render() {
const visibleContacts = this.getVisibleContacts();
const { filter } = this.state;

return (
<div>
<h1 className={styles.title}>Phonebook</h1>
<ContactForm onSubmit={addContact} />
{contacts.length > 0 ? (
<>
<h2 className={styles.title}>Contacts</h2>
<Filter value={filter} onChangeFilter={changeFilter} />
</>
) : (
<p>Your phonebook is empty. Add first contact!</p>
)}
{contacts.length > 0 && (
<ContactList
contacts={visibleContacts}
onRemoveContact={removeContact}
/>
)}
</div>
);
};
return (
<div>
<h1 className={styles.title}>Phonebook</h1>

<ContactForm onSubmit={this.addContact} />

<h2 className={styles.title}>Contacts</h2>
{this.state.contacts.length > 0 ? (
<Filter value={filter} onChangeFilter={this.changeFilter} />
) : (
<p className={styles.noContacts}>
Your phonebook is empty. Add first contact!
</p>
)}
{this.state.contacts.length > 0 && (
<ContactList
contacts={visibleContacts}
onRemoveContact={this.removeContact}
/>
)}
</div>
);
}
}

export default App;
10 changes: 10 additions & 0 deletions src/components/App/App.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,13 @@
text-align: center;
margin-top: 20px;
}

.noContacts {
text-align: center;
margin-top: 20px;
font-family: -apple-system, system-ui, 'Segoe UI', Helvetica, Arial,
sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji';
font-size: 18px;
font-weight: 600;
line-height: 40px;
}
60 changes: 33 additions & 27 deletions src/components/ContactForm/ContactForm.jsx
Original file line number Diff line number Diff line change
@@ -1,66 +1,72 @@
import React, { useState } from 'react';
import React from 'react';
import { nanoid } from 'nanoid';
import styles from './ContactForm.module.css';

const ContactForm = ({ onSubmit }) => {
const [name, setName] = useState('');
const [number, setNumber] = useState('');
class ContactForm extends React.Component {
state = {
name: '',
number: '',
};

nameInputId = nanoid();
numberInputId = nanoid();

const handleSubmit = event => {
handleSubmit = event => {
event.preventDefault();
onSubmit({ name, number });
setName('');
setNumber('');

this.props.onSubmit({ name: this.state.name, number: this.state.number });

this.reset();
};

const handleChange = event => {
handleChange = event => {
const { name, value } = event.target;
if (name === 'name') {
setName(value);
} else if (name === 'number') {
setNumber(value);
}
this.setState({ [name]: value });
};

reset = () => {
this.setState({ number: '', name: '' });
};

return (
<div>
<form className={styles.form} onSubmit={handleSubmit}>
<label className={styles.label} htmlFor={nanoid()}>
render() {
return (
<form className={styles.form} onSubmit={this.handleSubmit}>
<label className={styles.label} htmlFor={this.nameInputId}>
Name
<input
className={styles.input}
type="text"
placeholder="Name"
name="name"
value={name}
onChange={handleChange}
value={this.state.name}
onChange={this.handleChange}
pattern="^[a-zA-Zа-яА-Я]+(([' -][a-zA-Zа-яА-Я ])?[a-zA-Zа-яА-Я]*)*$"
title="Name may contain only letters, apostrophe, dash and spaces. For example Adrian, Jacob Mercer, Charles de Batz de Castelmore d'Artagnan"
required
/>
</label>

<label className={styles.label} htmlFor={nanoid()}>
<label className={styles.label} htmlFor={this.numberInputId}>
Number
<input
className={styles.input}
type="tel"
placeholder="Number"
name="number"
value={number}
onChange={handleChange}
value={this.state.number}
onChange={this.handleChange}
pattern="\+?\d{1,4}?[-.\s]?\(?\d{1,3}?\)?[-.\s]?\d{1,4}[-.\s]?\d{1,4}[-.\s]?\d{1,9}"
title="Phone number must be digits and can contain spaces, dashes, parentheses and can start with +"
required
/>
</label>

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

export default ContactForm;
42 changes: 23 additions & 19 deletions src/components/ContactList/ContactList.jsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
import React from 'react';
import PropTypes from 'prop-types';
import styles from './ContactList.module.css';

const ContactList = ({ contacts, onRemoveContact }) => (
<div>
<ul className={styles.contactListUl}>
{contacts.map(contact => (
<li className={styles.contactListLi} key={contact.id}>
{contact.name + ' : ' + contact.number}
{
<button
className={styles.button}
type="button"
name="delete"
onClick={() => onRemoveContact(contact.id)}
>
delete
</button>
}
</li>
))}
</ul>
</div>
<ul className={styles.contactListUl}>
{contacts.map(contact => (
<li className={styles.contactListLi} key={contact.id}>
{contact.name + ' : ' + contact.number}
{
<button
className={styles.button}
type="button"
name="delete"
onClick={() => onRemoveContact(contact.id)}
>
delete
</button>
}
</li>
))}
</ul>
);

ContactList.propTypes = {
contacts: PropTypes.object.isRequired,
onRemoveContact: PropTypes.func.isRequired,
};

export default ContactList;
13 changes: 9 additions & 4 deletions src/components/Filter/Filter.jsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
import React from 'react';
import PropTypes from 'prop-types';
import styles from './Filter.module.css';

const Filter = ({ value, onChangeFilter }) => {
function Filter({ value, onChangeFilter }) {
return (
<div>
<label className={styles.label}>
Find contacts by name
<input
className={styles.input}
type="text"
name="filter"
placeholder="Search contact"
placeholder="Search contacts..."
value={value}
onChange={e => onChangeFilter(e.target.value)}
onChange={onChangeFilter}
/>
</label>
</div>
);
}

Filter.propTypes = {
value: PropTypes.string.isRequired,
onChangeFilter: PropTypes.func.isRequired,
};

export default Filter;

0 comments on commit 47dc857

Please sign in to comment.