Skip to content

Commit

Permalink
feat:add solution
Browse files Browse the repository at this point in the history
  • Loading branch information
AM1007 committed Feb 11, 2024
1 parent f3b1ed6 commit 20070c6
Show file tree
Hide file tree
Showing 13 changed files with 637 additions and 47 deletions.
313 changes: 283 additions & 30 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
"nanoid": "^5.0.5",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-icons": "^4.6.0",
"react-scripts": "5.0.1",
"styled-components": "^5.3.6",
"web-vitals": "^2.1.3"
},
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<title>hw-02-phonebook</title>

<!-- Start Single Page Apps for GitHub Pages -->
<script type="text/javascript">
Expand Down
92 changes: 76 additions & 16 deletions src/components/App.jsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,76 @@
export const App = () => {
return (
<div
style={{
height: '100vh',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
fontSize: 40,
color: '#010101'
}}
>
React homework template
</div>
);
};
import React, { Component } from 'react';
import { nanoid } from 'nanoid';

import { Section } from './Section/Section';
import { ContactForm } from './ContactForm/ContactForm';
import { ContactList } from './ContactList/ContactList';
import { Filter } from './Filter/Filter';

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: '',
};

formSubmit = ({ name, number }) => {
const contact = { id: nanoid(), name, number };

if (this.state.contacts.some(e => e.name === name)) {

return alert(`${name} is already in contacts!`);
}

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

getFilteredContacts = () => {
const { contacts, filter } = this.state;
const filterContactsList = contacts.filter(contact => {

return contact.name.toLowerCase().includes(filter.toLowerCase());
});

return filterContactsList;
};

filterList = evt => {
const { name, value } = evt.target;

this.setState({ [name]: value });
};

deleteContact = contactId => {

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

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

return (
<div>
<h1>
Phonebook
</h1>
<Section title="Phonebook">
<ContactForm onSubmit={this.formSubmit} />
</Section>
<Section title="Contacts">
<Filter value={filter} onChange={this.filterList} />
<ContactList
contacts={this.getFilteredContacts()}
deleteContact={this.deleteContact}
/>
</Section>
</div>
);
}
}
72 changes: 72 additions & 0 deletions src/components/ContactForm/ContactForm.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React, { Component } from 'react';
import { nanoid } from 'nanoid';
import PropTypes from 'prop-types';

import { Form, Label, Input, ButtonForm } from './ContactForm.styled';

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

NameInputId = nanoid();
NumberInputId = nanoid();

handleInputChange = evt => {
const { name, value } = evt.currentTarget;
this.setState({ [name]: value });
};

handleSubmit = evt => {
evt.preventDefault();
this.props.onSubmit(this.state);
this.reset();
};

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

render() {
return (
<Form onSubmit={this.handleSubmit}>
<Label htmlFor={this.NameInputId}>
Name
<Input
onChange={this.handleInputChange}
value={this.state.name}
type="text"
name="name"
placeholder="Name"
id={this.NameInputId}
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 htmlFor={this.NumberInputId}>
Number
<Input
value={this.state.number}
onChange={this.handleInputChange}
type="tel"
name="number"
placeholder="Number"
id={this.NumberInputId}
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>
<ButtonForm type="submit">Add contact</ButtonForm>
</Form>
);
}
}
ContactForm.propTypes = {
name: PropTypes.string,
value: PropTypes.string,
handleSubmit: PropTypes.func,
handleInputChange: PropTypes.func,
};
42 changes: 42 additions & 0 deletions src/components/ContactForm/ContactForm.styled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import styled from 'styled-components';

export const Form = styled.form`
margin: 0 auto;
border: 1px solid black;
width: 320px;
padding: 30px;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 30px;
`;
export const Label = styled.label`
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 12px;
font-size: 20px;
font-weight: 600;
`;
export const Input = styled.input`
width: 300px;
height: 24px;
outline: transparent;
padding: 0 8px;
border: 1px solid #000;
&:hover,
&:focus {
border: 2px solid #92a8d1;
}
`;
export const ButtonForm = styled.button`
padding: 6px;
font-size: 18px;
cursor: pointer;
border: transparent;
border: 1px solid black;
&:hover,
&:focus {
background: #92a8d1;
}
`;
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 propTypes from 'prop-types';
import {
ContactListUl,
ContactListLi,
ButtonDelete,
} from './ContactList.styled';
import { FcPhoneAndroid } from 'react-icons/fc';

export const ContactList = ({ contacts, deleteContact }) => (
<ContactListUl>
{contacts.map((contact, id) => (
<ContactListLi key={id}>
<FcPhoneAndroid />
{contact.name}: {contact.number}
<ButtonDelete type="button" onClick={() => deleteContact(contact.id)}>
Delete
</ButtonDelete>
</ContactListLi>
))}
</ContactListUl>
);
ContactList.propTypes = {
deleteContact: propTypes.func.isRequired,
contacts: propTypes.arrayOf(
propTypes.exact({
id: propTypes.string.isRequired,
name: propTypes.string.isRequired,
number: propTypes.string.isRequired,
})
),
};
27 changes: 27 additions & 0 deletions src/components/ContactList/ContactList.styled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import styled from 'styled-components';

export const ContactListUl = styled.ul`
font-size: 24px;
display: flex;
flex-direction: column;
align-items: center;
padding: 0;
`;
export const ContactListLi = styled.li`
display: flex;
align-items: center;
justify-content: space-between;
border: 1px solid black;
list-style-position: inside;
width: 450px;
padding: 20px;
&:not(:last-child) {
margin-bottom: 5px;
}
`;
export const ButtonDelete = styled.button`
margin-left: 18px;
font-size: 18px;
cursor: pointer;
`;
20 changes: 20 additions & 0 deletions src/components/Filter/Filter.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import propTypes from 'prop-types';
import { FilterLabel, FilterInput } from './Filter.styled';

export const Filter = ({ value, onChange }) => (
<FilterLabel>
Find contacts by Name
<FilterInput
type="text"
name="filter"
placeholder="Search contact"
value={value}
onChange={onChange}
/>
</FilterLabel>
);

Filter.propTypes = {
value: propTypes.string.isRequired,
onChange: propTypes.func.isRequired,
};
23 changes: 23 additions & 0 deletions src/components/Filter/Filter.styled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import styled from 'styled-components';

export const FilterLabel = styled.label`
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 30px;
gap: 12px;
font-size: 20px;
font-weight: 600;
`;

export const FilterInput = styled.input`
width: 300px;
height: 24px;
outline: transparent;
padding: 0 8px;
border: 1px solid #000;
&:hover,
&:focus {
border: 2px solid #92a8d1;
}
`;
15 changes: 15 additions & 0 deletions src/components/Section/Section.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import PropTypes from 'prop-types';

import { SectionContainer, Title } from './Section.styled';

export const Section = ({ title, children }) => (
<SectionContainer>
<Title>{title}</Title>
{children}
</SectionContainer>
);

Section.propTypes = {
title: PropTypes.string.isRequired,
};
12 changes: 12 additions & 0 deletions src/components/Section/Section.styled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import styled from 'styled-components';

export const SectionContainer = styled.section`
width: 80vw;
padding: 20px;
text-align: center;
`;
export const Title = styled.h2`
font-size: 34px;
margin-bottom: 30px;
color: #034f84;
`;
32 changes: 32 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,35 @@ code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

h1 {
display: none;
}

h2,
h3,
h4,
h5,
h6,
p {
margin: 0;
}
ul,
ol {
margin: 0;
padding: 0;
list-style: none;
}
img {
display: block;
max-width: 100%;
height: auto;
}

section {
margin: 20px auto 20px auto;
}

button {
font-family: inherit;
}

0 comments on commit 20070c6

Please sign in to comment.