Skip to content
This repository was archived by the owner on Dec 4, 2021. It is now read-only.

Commit 45dd122

Browse files
committed
complete the basic tasks
1 parent 81aff1c commit 45dd122

14 files changed

Lines changed: 378 additions & 44 deletions

File tree

src/actions/index.js

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,42 @@
1-
export const addContact = (contact) => {
2-
return {
3-
type: 'ADD_CONTACT',
4-
value: contact,
5-
}
6-
}
1+
export const showEditor = () => ({ type: 'SHOW_EDITOR' });
2+
3+
export const hideEditor = () => ({ type: 'HIDE_EDITOR' });
4+
5+
export const edit = value => ({
6+
type: 'EDIT',
7+
value
8+
});
9+
10+
export const setFavorite = value => ({
11+
type: 'SET_FAVORITE',
12+
value
13+
});
14+
15+
export const setContact = (id, contact) => ({
16+
type: 'SET_CONTACT',
17+
id,
18+
contact
19+
});
20+
21+
export const delContact = id => ({
22+
type: 'DEL_CONTACT',
23+
id
24+
});
25+
26+
export const addFavorite = id => ({
27+
type: 'ADD_FAVORITE',
28+
id
29+
});
30+
31+
export const delFavorite = id => ({
32+
type: 'DEL_FAVORITE',
33+
id
34+
});
35+
36+
export const filterFavorites = () => ({
37+
type: 'FILTER_FAVORITES'
38+
});
39+
40+
export const resetFilter = () => ({
41+
type: 'RESET_FILTER'
42+
});

src/app/app.jsx

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
11
import React from 'react';
2+
import { connect } from 'react-redux';
23

34
import './app.less';
4-
import { Column, Row, Title, Button } from '../components';
5-
import { connect } from 'react-redux'
6-
import { addContact } from '../actions'
5+
import { Column, Row, Title } from '../components';
76

7+
import Editor from './editor';
8+
import Contacts from './contacts';
89

9-
const App = ({ dispatch }) => {
10-
const onAddClick = (event) => {
11-
dispatch(addContact('ok'))
12-
};
10+
const App = ({ dispatch }) => (<Column className="app">
11+
<Row><Title>Contact Book</Title></Row>
1312

14-
return <Column className="app">
15-
<Row><Title>Contact Book</Title></Row>
16-
<Row><Button label="Add Contact" onClick={onAddClick} /></Row>
17-
</Column>
18-
}
13+
<Editor />
14+
<Contacts />
15+
</Column>);
1916

2017
export default connect()(App);

src/app/contacts.jsx

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import React from 'react';
2+
import { connect } from 'react-redux';
3+
import uuid from 'uuid/v4';
4+
import { Row, Button } from '../components';
5+
import {
6+
showEditor, delContact,
7+
filterFavorites, resetFilter, edit
8+
} from '../actions';
9+
import './contacts.less';
10+
11+
12+
const Contacts = ({ show, filter, contacts, dispatch }) => {
13+
if (show) { return null; }
14+
15+
const onAddClick = () => {
16+
dispatch(showEditor());
17+
dispatch(edit({
18+
id: uuid()
19+
}));
20+
};
21+
22+
const onToggleFilerClick = () => {
23+
if (filter === 'SHOW_ALL') {
24+
dispatch(filterFavorites());
25+
} else {
26+
dispatch(resetFilter());
27+
}
28+
};
29+
30+
return (<div className="contacts">
31+
<Row><Button className="add-contact" label="Add Contact" onClick={onAddClick} /></Row>
32+
33+
<table>
34+
<tbody>
35+
<tr>
36+
<th>
37+
<Button
38+
label={filter === 'SHOW_ALL' ? 'Filter Favorites' : 'Show All'}
39+
onClick={() => onToggleFilerClick()}
40+
/>
41+
</th>
42+
<th>Name</th>
43+
<th>Phone</th>
44+
<th>Email</th>
45+
<th>Operations</th>
46+
</tr>
47+
{contacts.map(el => (<tr key={el.id}>
48+
<td className="favorite">
49+
{el.isFavorite ? '★' : '☆' }
50+
</td>
51+
<td>{el.name}</td>
52+
<td>{el.phone}</td>
53+
<td>{el.email}</td>
54+
<td>
55+
<Button
56+
label="Edit"
57+
onClick={() => {
58+
dispatch(showEditor());
59+
dispatch(edit(el));
60+
}}
61+
/>
62+
63+
{' '}
64+
65+
<Button
66+
label="Del"
67+
onClick={() => {
68+
dispatch(delContact(el.id));
69+
}}
70+
/>
71+
</td>
72+
</tr>))}
73+
</tbody>
74+
</table>
75+
</div>);
76+
};
77+
78+
79+
export default connect(state => ({
80+
show: state.editor.get('show'),
81+
filter: state.filter,
82+
contacts: state.contacts.map((val, id) => ({
83+
id,
84+
isFavorite: state.favorites.has(id),
85+
...val
86+
})).filter((val, id) => {
87+
if (state.filter === 'FAVORITES_ONLY') { return state.favorites.has(id); }
88+
return true;
89+
}).toArray()
90+
}))(Contacts);

src/app/contacts.less

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
.contacts {
2+
.add-contact {
3+
margin: 10px 0;
4+
width: 100%;
5+
}
6+
7+
.favorite {
8+
text-align: center;
9+
font-size: 18px;
10+
}
11+
12+
table {
13+
border-collapse: collapse;
14+
}
15+
16+
table, th, td {
17+
border-bottom: 1px solid #ddd;
18+
}
19+
20+
th, td {
21+
padding: 5px 10px;
22+
}
23+
}

src/app/editor.jsx

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import React from 'react';
2+
import { connect } from 'react-redux';
3+
import { Label, Button } from '../components';
4+
import {
5+
hideEditor, edit,
6+
setContact, addFavorite,
7+
delFavorite
8+
} from '../actions';
9+
10+
const ContactEditor = ({ value, show, dispatch }) => {
11+
if (!show) { return null; }
12+
13+
const onSaveClick = () => {
14+
dispatch(setContact(value.id, {
15+
name: value.name,
16+
phone: value.phone,
17+
email: value.email
18+
}));
19+
dispatch(hideEditor());
20+
21+
if (value.isFavorite) {
22+
dispatch(addFavorite(value.id));
23+
} else {
24+
dispatch(delFavorite(value.id));
25+
}
26+
};
27+
28+
return (<form onSubmit={(e) => {
29+
e.preventDefault()
30+
onSaveClick()
31+
}}><table>
32+
<tbody>
33+
<tr>
34+
<td>
35+
<Label htmlFor="name">Name</Label>
36+
</td>
37+
<td>
38+
<input
39+
id="name"
40+
value={value.name}
41+
onChange={(el) => {
42+
value.name = el.target.value;
43+
dispatch(edit({ ...value }));
44+
}}
45+
/>
46+
</td>
47+
</tr>
48+
<tr>
49+
<td>
50+
<Label htmlFor="phone" value={value.phone}>
51+
Phone
52+
</Label>
53+
</td>
54+
<td>
55+
<input
56+
id="phone"
57+
type="tel"
58+
value={value.phone}
59+
onChange={(el) => {
60+
value.phone = el.target.value;
61+
dispatch(edit({ ...value }));
62+
}}
63+
/>
64+
</td>
65+
</tr>
66+
<tr>
67+
<td>
68+
<Label htmlFor="email">
69+
Email
70+
</Label>
71+
</td>
72+
<td>
73+
<input
74+
id="email"
75+
type="email"
76+
value={value.email}
77+
onChange={(el) => {
78+
value.email = el.target.value;
79+
dispatch(edit({ ...value }));
80+
}}
81+
/>
82+
</td>
83+
</tr>
84+
<tr>
85+
<td>
86+
<Label htmlFor="favorite">
87+
Favorite
88+
</Label>
89+
</td>
90+
<td>
91+
<input
92+
id="favorite"
93+
type="checkbox"
94+
checked={value.isFavorite}
95+
onChange={(el) => {
96+
value.isFavorite = el.target.checked;
97+
dispatch(edit({ ...value }));
98+
}}
99+
/>
100+
</td>
101+
</tr>
102+
<tr>
103+
<td>
104+
<input label="Save" type='submit' />
105+
</td>
106+
</tr>
107+
</tbody>
108+
</table></form>);
109+
};
110+
111+
export default connect(state => ({
112+
show: state.editor.get('show'),
113+
value: state.editor.get('value')
114+
}))(ContactEditor);

src/components/controls/button.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ const resolveButtonLabel = (children, label) => {
1010
return 'Label';
1111
};
1212

13-
const Button = ({ children, label, onClick }) => (
14-
<button onClick={onClick}>{resolveButtonLabel(children, label)}</button>
13+
const Button = ({ className, children, label, onClick }) => (
14+
<button className={className} onClick={onClick}>{resolveButtonLabel(children, label)}</button>
1515
);
1616

1717
Button.propTypes = {

src/components/resets.less

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ span {
2424
font-size: 100%;
2525
}
2626

27-
div,
2827
article,
2928
nav,
3029
footer,
Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
import React, { PropTypes } from 'react';
22

3-
const Label = ({ children, className }) => (
4-
<label className={`typography label ${className}`}>{children}</label>
3+
const Label = ({ children, htmlFor, className }) => (
4+
<label className={`typography label ${className}`} htmlFor={htmlFor}>{children}</label>
55
);
66

77
Label.propTypes = {
88
children: PropTypes.string.isRequired,
9-
className: PropTypes.string
9+
className: PropTypes.string,
10+
htmlFor: PropTypes.string
1011
};
1112

1213
Label.defaultProps = {
13-
className: ''
14+
className: '',
15+
htmlFor: ''
1416
};
1517

1618
export default Label;

src/index.jsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
import React from 'react';
22
import { render } from 'react-dom';
3-
import App from './app/app';
43
import { createStore } from 'redux';
54
import { Provider } from 'react-redux';
6-
import reducer from './reducers'
5+
import App from './app/app';
6+
import reducer from './reducers';
77

8-
const store = createStore(reducer)
8+
const store = createStore(reducer);
99

1010
try {
1111
const appContainer = document.getElementById('app');
1212
render(
1313
<Provider store={store}>
1414
<App />
15-
</ Provider>,
15+
</Provider>,
1616
appContainer
1717
);
1818
} catch (e) {

0 commit comments

Comments
 (0)