From faffb6767d58ad8acaef4f913fb9d97dfc215321 Mon Sep 17 00:00:00 2001 From: Petr Novak Date: Tue, 31 Jul 2018 13:37:41 +0200 Subject: [PATCH 1/8] features/users --- .../admin/users/components/UsersIndexPage.tsx | 103 +++++++++++++++++- client/admin/users/components/modal.tsx | 86 +++++++++++++++ 2 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 client/admin/users/components/modal.tsx diff --git a/client/admin/users/components/UsersIndexPage.tsx b/client/admin/users/components/UsersIndexPage.tsx index e8b1a53..6fe3390 100644 --- a/client/admin/users/components/UsersIndexPage.tsx +++ b/client/admin/users/components/UsersIndexPage.tsx @@ -1,8 +1,107 @@ import * as React from 'react'; import {WithAdminProps} from '@client/with/withAdmin'; +import {Paper, Table, TableHead, TableRow, TableCell, TableBody, Button} from '../../../../node_modules/@material-ui/core'; +import AddIcon from '@material-ui/icons/Add'; +import Icon from '@material-ui/core/Icon'; +import DeleteIcon from '@material-ui/icons/Delete'; +import {SimpleModal} from './modal'; +const styles = { + button: { + margin: '10px', + }, + paper: { + margin: '0 10px', + }, + button_edit: { + margin: '0 10px 0 0', + }, + modal: { + position: 'absolute', + width: '400px', + backgroundColor: 'white', + padding: '20px', + top: '25', + margin: '300px', + }, +}; +const initialState = { + contacts: [ + { + id: 1, + firstName: 'Petr', + lastName: 'Novak', + email: 'pronovaso@icloud.com', + phoneNumber: 777123456, + }, + { + id: 2, + firstName: 'Karel', + lastName: 'Omacka', + email: 'karel@gmail.com', + phoneNumber: 775345234, + }, + { + id: 3, + firstName: 'Martina', + lastName: 'Leva', + email: 'martina@seznam.cz', + phoneNumber: 603100800, + }, + ], + isOpen: false, +}; + +export class UsersIndexPage extends React.Component { + readonly state = initialState; + + handleOpen = () => { + this.setState({isOpen: true}); + }; + + handleClose = () => { + this.setState({isOpen: false}); + }; -export class UsersIndexPage extends React.Component { render() { - return
UsersIndexPage
; + const {contacts, isOpen} = this.state; + return ( + <> + + + + + + + First Name + Last Name + E-mail + Phone number + + + + + {contacts.map((contact) => ( + + {contact.firstName} + {contact.lastName} + {contact.email} + {contact.phoneNumber} + + + + + + ))} + +
+
+ + ); } } diff --git a/client/admin/users/components/modal.tsx b/client/admin/users/components/modal.tsx new file mode 100644 index 0000000..6c4e477 --- /dev/null +++ b/client/admin/users/components/modal.tsx @@ -0,0 +1,86 @@ +import * as React from 'react'; +import Typography from '@material-ui/core/Typography'; +import Modal from '@material-ui/core/Modal'; +import Slide from '@material-ui/core/Slide'; +import TextField from '../../../../node_modules/@material-ui/core/TextField'; +import Button from '../../../../node_modules/@material-ui/core/Button'; + +interface Props { + readonly isOpen: boolean; + readonly handleClose: () => void; + readonly firstName: string; + readonly lastName: string; + readonly email: string; + readonly phoneNumber: number; + readonly children?: React.ReactNode; + readonly contacts: {}; +} + +export const SimpleModal: React.SFC = ({firstName, lastName, email, phoneNumber, isOpen, handleClose}) => { + return ( +
+ + +
+ + Add new user to database + + +
+ +
+ +
+ +
+ +
+
+
+
+ ); +}; From 57e1500646c127600efe444ef981a458c4e16a23 Mon Sep 17 00:00:00 2001 From: Petr Novak Date: Tue, 31 Jul 2018 13:37:59 +0200 Subject: [PATCH 2/8] update modal --- client/admin/users/components/modal.tsx | 35 +++---------------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/client/admin/users/components/modal.tsx b/client/admin/users/components/modal.tsx index 6c4e477..d95de58 100644 --- a/client/admin/users/components/modal.tsx +++ b/client/admin/users/components/modal.tsx @@ -40,40 +40,13 @@ export const SimpleModal: React.SFC = ({firstName, lastName, email, phone Add new user to database - +
- +
- +
- +
+ + + + + ); + } +} diff --git a/client/admin/users/components/Styles.tsx b/client/admin/users/components/Styles.tsx new file mode 100644 index 0000000..18db417 --- /dev/null +++ b/client/admin/users/components/Styles.tsx @@ -0,0 +1,22 @@ +export const styles = { + button: { + margin: '10px', + }, + paper: { + margin: '0 10px', + }, + button_edit: { + margin: '0 10px 0 0', + }, + modal: { + position: 'absolute', + width: '400px', + backgroundColor: 'white', + padding: '20px', + top: '25', + margin: '300px', + }, + button_left: { + marginLeft: '10px', + }, +}; diff --git a/client/admin/users/components/UsersIndexPage.tsx b/client/admin/users/components/UsersIndexPage.tsx index 6fe3390..b9bc875 100644 --- a/client/admin/users/components/UsersIndexPage.tsx +++ b/client/admin/users/components/UsersIndexPage.tsx @@ -1,106 +1,34 @@ import * as React from 'react'; import {WithAdminProps} from '@client/with/withAdmin'; -import {Paper, Table, TableHead, TableRow, TableCell, TableBody, Button} from '../../../../node_modules/@material-ui/core'; -import AddIcon from '@material-ui/icons/Add'; +import {Button} from '@material-ui/core'; +import {Add, Delete as DeleteIcon} from '@material-ui/icons'; import Icon from '@material-ui/core/Icon'; -import DeleteIcon from '@material-ui/icons/Delete'; -import {SimpleModal} from './modal'; -const styles = { - button: { - margin: '10px', - }, - paper: { - margin: '0 10px', - }, - button_edit: { - margin: '0 10px 0 0', - }, - modal: { - position: 'absolute', - width: '400px', - backgroundColor: 'white', - padding: '20px', - top: '25', - margin: '300px', - }, -}; -const initialState = { - contacts: [ - { - id: 1, - firstName: 'Petr', - lastName: 'Novak', - email: 'pronovaso@icloud.com', - phoneNumber: 777123456, - }, - { - id: 2, - firstName: 'Karel', - lastName: 'Omacka', - email: 'karel@gmail.com', - phoneNumber: 775345234, - }, - { - id: 3, - firstName: 'Martina', - lastName: 'Leva', - email: 'martina@seznam.cz', - phoneNumber: 603100800, - }, - ], - isOpen: false, -}; +import {SimpleModal} from './SimpleModal'; +import {ContactList} from './ContactList'; +import {initialState} from './initialState'; +import {styles} from './Styles'; -export class UsersIndexPage extends React.Component { - readonly state = initialState; +type State = Readonly; - handleOpen = () => { +export class UsersIndexPage extends React.Component { + state = initialState; + handleOnOpen = () => { this.setState({isOpen: true}); }; - handleClose = () => { + handleOnClose = () => { this.setState({isOpen: false}); }; render() { - const {contacts, isOpen} = this.state; + const {isOpen} = this.state; return ( <> - - - - - - - First Name - Last Name - E-mail - Phone number - - - - - {contacts.map((contact) => ( - - {contact.firstName} - {contact.lastName} - {contact.email} - {contact.phoneNumber} - - - - - - ))} - -
-
+ + ); } diff --git a/client/admin/users/components/initialState.tsx b/client/admin/users/components/initialState.tsx new file mode 100644 index 0000000..c03736d --- /dev/null +++ b/client/admin/users/components/initialState.tsx @@ -0,0 +1,35 @@ +export interface ContactModel { + id: number; + firstName: string; + lastName: string; + email: string; + phoneNumber: number | string; +} + +export const initialState = { + contacts: [ + { + id: 1, + firstName: 'Petr', + lastName: 'Novak', + email: 'pronovaso@icloud.com', + phoneNumber: 777123456, + }, + { + id: 2, + firstName: 'Karel', + lastName: 'Omacka', + email: 'karel@gmail.com', + phoneNumber: 775345234, + }, + { + id: 3, + firstName: 'Martina', + lastName: 'Leva', + email: 'martina@seznam.cz', + phoneNumber: 603100800, + }, + ] as ContactModel[], + isOpen: false, + id: '', +}; diff --git a/client/admin/users/components/modal.tsx b/client/admin/users/components/modal.tsx deleted file mode 100644 index d95de58..0000000 --- a/client/admin/users/components/modal.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import * as React from 'react'; -import Typography from '@material-ui/core/Typography'; -import Modal from '@material-ui/core/Modal'; -import Slide from '@material-ui/core/Slide'; -import TextField from '../../../../node_modules/@material-ui/core/TextField'; -import Button from '../../../../node_modules/@material-ui/core/Button'; - -interface Props { - readonly isOpen: boolean; - readonly handleClose: () => void; - readonly firstName: string; - readonly lastName: string; - readonly email: string; - readonly phoneNumber: number; - readonly children?: React.ReactNode; - readonly contacts: {}; -} - -export const SimpleModal: React.SFC = ({firstName, lastName, email, phoneNumber, isOpen, handleClose}) => { - return ( -
- - -
- - Add new user to database - - -
- -
- -
- -
- -
-
-
-
- ); -}; From f1fd214c0a9e0b98d9b0a4f78071261aa0f690a1 Mon Sep 17 00:00:00 2001 From: Petr Novak Date: Wed, 1 Aug 2018 16:21:57 +0200 Subject: [PATCH 4/8] update --- client/admin/users/components/SimpleModal.tsx | 30 ++++++++++++------- .../admin/users/components/UsersIndexPage.tsx | 4 +-- .../admin/users/components/initialState.tsx | 3 +- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/client/admin/users/components/SimpleModal.tsx b/client/admin/users/components/SimpleModal.tsx index 08d3610..b50728f 100644 --- a/client/admin/users/components/SimpleModal.tsx +++ b/client/admin/users/components/SimpleModal.tsx @@ -6,7 +6,6 @@ import TextField from '../../../../node_modules/@material-ui/core/TextField'; import Button from '../../../../node_modules/@material-ui/core/Button'; import {initialState} from './initialState'; import {styles} from './Styles'; -import {WithAdminProps} from '@client/with'; interface Props { readonly isOpen: boolean; @@ -14,7 +13,7 @@ interface Props { readonly id?: number; } -type State = Readonly; +type State = typeof initialState; export class SimpleModal extends React.Component { state = initialState; @@ -22,19 +21,28 @@ export class SimpleModal extends React.Component { handleOnChange = (e: any) => { this.setState({ ...this.state, - contacts: { - ...this.state.contacts, + contact: { + ...this.state.contact, + id: new Date().getTime(), [e.target.name]: e.target.value, }, }); - // console.log(this.state.contacts); + // console.log(this.state.contact); + }; + + handleOnSave = () => { + const contact = this.state.contact; + this.setState((prevState) => ({contacts: prevState.contacts, contact})); + // console.log(this.state.contact); + console.log(this.state.contacts); + this.props.handleOnClose(); }; render() { // const indexSearchUser = this.state.contacts.filter((contact) => contact.id !== this.props.id); // const indexUser = indexSearchUser.join(); const {handleOnClose, isOpen} = this.props; - const {contacts} = this.state; + const {contact} = this.state; return ( { name="firstName" id="name" label="First Name" - value={contacts.firstName} + value={contact.firstName} onChange={this.handleOnChange} margin="normal" required @@ -71,24 +79,24 @@ export class SimpleModal extends React.Component { name="lastName" id="name" label="Last Name" - value={contacts.lastName} + value={contact.lastName} onChange={this.handleOnChange} margin="normal" required />
- +

- - - - -
- ); - } -} +export const SimpleModal: React.SFC = ({isOpen, handleOnClose, contact, handleOnChange, handleOnSave}) => { + return ( + + +
+ + Add new user to database + + +
+ +
+ +
+ +
+ + +
+
+
+ ); +}; diff --git a/client/admin/users/components/UsersIndexPage.tsx b/client/admin/users/components/UsersIndexPage.tsx index a847be0..1cf633a 100644 --- a/client/admin/users/components/UsersIndexPage.tsx +++ b/client/admin/users/components/UsersIndexPage.tsx @@ -1,34 +1,76 @@ import * as React from 'react'; import {WithAdminProps} from '@client/with/withAdmin'; -import {Button} from '@material-ui/core'; import {Add} from '@material-ui/icons'; - +import {Button} from '@material-ui/core'; +import {styles} from './Styles'; import {SimpleModal} from './SimpleModal'; import {ContactList} from './ContactList'; import {initialState} from './initialState'; -import {styles} from './Styles'; -type State = Readonly; - -export class UsersIndexPage extends React.Component { +export class UsersIndexPage extends React.Component { state = initialState; + + componentDidUpdate() { + return true; + } + handleOnOpen = () => { this.setState({isOpen: true}); }; handleOnClose = () => { - this.setState({isOpen: false}); + this.setState({isOpen: false, contact: ''}); + }; + + handleOnDelete = (id: number) => () => { + const contacts = [...this.state.contacts.filter((contact) => contact.id !== id)]; + this.setState({contacts, contact: ''}); + }; + + handleOnId = (id: number) => () => { + this.setState({id, isOpen: true}); + const user = this.state.contacts.filter((contact) => contact.id === id).find((contact) => contact.id === id); + this.setState({contact: user}); + }; + + handleOnChange = (e: any) => { + this.setState({ + contact: {...this.state.contact, [e.target.name]: e.target.value}, + }); + }; + + handleOnSave = () => { + const userId = new Date().getMilliseconds(); + const {contact, id} = this.state; + const contactsMap = this.state.contacts.map((user) => (user.id === id ? {...user, ...contact} : user)); + const contactFilter = contactsMap.filter((contacts) => contacts); + if (id > 0) { + this.setState({ + contacts: contactFilter, + isOpen: false, + contact: '', + }); + } + if (id === 0) { + this.setState({contacts: [...this.state.contacts, {...contact, id: userId}], isOpen: false, contact: ''}); + } }; render() { - const {isOpen} = this.state; + const {contacts, isOpen, contact} = this.state; return ( <> - - + + ); } diff --git a/client/admin/users/components/initialState.tsx b/client/admin/users/components/initialState.tsx index 931a551..6473de0 100644 --- a/client/admin/users/components/initialState.tsx +++ b/client/admin/users/components/initialState.tsx @@ -1,11 +1,3 @@ -export interface ContactModel { - id: number; - firstName: string; - lastName: string; - email: string; - phoneNumber: number | string; -} - export const initialState = { contacts: [ { @@ -29,8 +21,16 @@ export const initialState = { email: 'martina@seznam.cz', phoneNumber: 603100800, }, - ], - contact: {} as ContactModel, + ] as ContactModel[], isOpen: false, - id: '', + contact: {} as ContactModel, + id: 0, }; + +export interface ContactModel { + id: number; + firstName: string; + lastName: string; + email: string; + phoneNumber: number | string; +} From bf1c5f61a211d951f538d86c6136d369732332c4 Mon Sep 17 00:00:00 2001 From: Petr Novak Date: Wed, 8 Aug 2018 16:24:34 +0200 Subject: [PATCH 7/8] refactor,add HoC --- client/admin/users/components/ContactList.tsx | 56 ++++++------------- .../admin/users/components/ContactsList.tsx | 55 ++++++++++++++++++ client/admin/users/components/SimpleModal.tsx | 47 ++++++++-------- client/admin/users/components/Styles.tsx | 22 -------- .../admin/users/components/UsersIndexPage.tsx | 44 +++++++++------ .../users/components/buttons/AddButton.tsx | 21 +++++++ .../users/components/buttons/CancelButton.tsx | 20 +++++++ .../users/components/buttons/DeleteButton.tsx | 23 ++++++++ .../users/components/buttons/EditButton.tsx | 23 ++++++++ .../users/components/buttons/SubmitButton.tsx | 20 +++++++ .../admin/users/components/initialState.tsx | 36 ------------ client/admin/users/components/model/index.ts | 1 + .../users/components/model/initialState.ts | 31 ++++++++++ 13 files changed, 263 insertions(+), 136 deletions(-) create mode 100644 client/admin/users/components/ContactsList.tsx delete mode 100644 client/admin/users/components/Styles.tsx create mode 100644 client/admin/users/components/buttons/AddButton.tsx create mode 100644 client/admin/users/components/buttons/CancelButton.tsx create mode 100644 client/admin/users/components/buttons/DeleteButton.tsx create mode 100644 client/admin/users/components/buttons/EditButton.tsx create mode 100644 client/admin/users/components/buttons/SubmitButton.tsx delete mode 100644 client/admin/users/components/initialState.tsx create mode 100644 client/admin/users/components/model/index.ts create mode 100644 client/admin/users/components/model/initialState.ts diff --git a/client/admin/users/components/ContactList.tsx b/client/admin/users/components/ContactList.tsx index 29e43fe..04e65dd 100644 --- a/client/admin/users/components/ContactList.tsx +++ b/client/admin/users/components/ContactList.tsx @@ -1,46 +1,24 @@ import * as React from 'react'; -import {Delete as DeleteIcon} from '@material-ui/icons'; -import Icon from '@material-ui/core/Icon'; -import {Paper, Table, TableHead, TableRow, TableCell, TableBody, Button} from '@material-ui/core'; -import {styles} from './Styles'; -import {ContactModel} from './initialState'; +import {TableRow, TableCell} from '@material-ui/core'; +import {ContactModel} from './model'; +import {EditButton} from './buttons/EditButton'; +import {DeleteButton} from './buttons/DeleteButton'; interface Props { - contacts: ContactModel[]; + contact: ContactModel; deleteContact: (id: number) => () => void; - handleOnId: (id: number) => () => void; + handleOnUserId: (id: number) => () => void; } -export const ContactList: React.SFC = ({contacts, deleteContact, handleOnId}) => ( - - - - - First Name - Last Name - E-mail - Phone number - - - - - {contacts.map((contact: ContactModel) => ( - - {contact.firstName} - {contact.lastName} - {contact.email} - {contact.phoneNumber} - - - - - - ))} - -
-
+export const ContactList: React.SFC = ({contact, deleteContact, handleOnUserId}) => ( + + {contact.firstName} + {contact.lastName} + {contact.email} + {contact.phoneNumber} + + + + + ); diff --git a/client/admin/users/components/ContactsList.tsx b/client/admin/users/components/ContactsList.tsx new file mode 100644 index 0000000..b49d2f1 --- /dev/null +++ b/client/admin/users/components/ContactsList.tsx @@ -0,0 +1,55 @@ +import * as React from 'react'; +import {Paper, Table, TableHead, TableRow, TableCell, TableBody, withStyles} from '@material-ui/core'; +import {ContactModel} from './model'; +import {ContactList} from './ContactList'; + +interface Props { + contacts: ContactModel[]; + deleteContact: (id: number) => () => void; + handleOnUserId: (id: number) => () => void; + nadpis?: string; +} + +// type WithDataProps = Pick; + +const decorate = withStyles(() => ({ + paper: { + margin: '0 10px', + }, +})); + +const ContactsListData = decorate(({nadpis, classes, contacts, deleteContact, handleOnUserId}) => ( + + + + + First Name + Last Name + E-mail + Phone number + {nadpis} + + + + {contacts.map((contact: ContactModel) => ( + + ))} + +
+
+)); + +const WithDataComponent = (BaseComponent: React.ComponentType): React.ComponentClass => { + const initialState = { + nadpis: 'Icons', + }; + return class extends React.Component { + readonly state = initialState; + render() { + const {nadpis} = this.state; + return ; + } + }; +}; + +export const ContactsList = WithDataComponent(ContactsListData); diff --git a/client/admin/users/components/SimpleModal.tsx b/client/admin/users/components/SimpleModal.tsx index add4f3e..cf1e020 100644 --- a/client/admin/users/components/SimpleModal.tsx +++ b/client/admin/users/components/SimpleModal.tsx @@ -1,10 +1,12 @@ import * as React from 'react'; -import {Button, TextField} from '@material-ui/core'; +import {TextField, withStyles} from '@material-ui/core'; import Modal from '@material-ui/core/Modal'; import Slide from '@material-ui/core/Slide'; import Typography from '@material-ui/core/Typography'; -import {styles} from './Styles'; -import {ContactModel} from './initialState'; +import {CancelButton} from './buttons/CancelButton'; +import {ContactModel} from './model'; +import {SubmitButton} from './buttons/SubmitButton'; + interface Props { isOpen: boolean; handleOnClose: () => void; @@ -13,26 +15,31 @@ interface Props { handleOnSave: () => void; } -export const SimpleModal: React.SFC = ({isOpen, handleOnClose, contact, handleOnChange, handleOnSave}) => { +const decorate = withStyles(() => ({ + modal: { + top: '25%', + margin: 'auto', + width: '400px', + backgroundColor: 'white', + padding: '20px', + }, + modal_window: { + alignItems: 'center', + justifyContent: 'center', + }, +})); + +export const SimpleModal = decorate(({classes, isOpen, handleOnClose, contact, handleOnChange, handleOnSave}) => { return ( -
+
Add new user to database @@ -44,14 +51,10 @@ export const SimpleModal: React.SFC = ({isOpen, handleOnClose, contact, h

- - + +
); -}; +}); diff --git a/client/admin/users/components/Styles.tsx b/client/admin/users/components/Styles.tsx deleted file mode 100644 index 18db417..0000000 --- a/client/admin/users/components/Styles.tsx +++ /dev/null @@ -1,22 +0,0 @@ -export const styles = { - button: { - margin: '10px', - }, - paper: { - margin: '0 10px', - }, - button_edit: { - margin: '0 10px 0 0', - }, - modal: { - position: 'absolute', - width: '400px', - backgroundColor: 'white', - padding: '20px', - top: '25', - margin: '300px', - }, - button_left: { - marginLeft: '10px', - }, -}; diff --git a/client/admin/users/components/UsersIndexPage.tsx b/client/admin/users/components/UsersIndexPage.tsx index 1cf633a..0422d6f 100644 --- a/client/admin/users/components/UsersIndexPage.tsx +++ b/client/admin/users/components/UsersIndexPage.tsx @@ -1,18 +1,21 @@ import * as React from 'react'; import {WithAdminProps} from '@client/with/withAdmin'; -import {Add} from '@material-ui/icons'; -import {Button} from '@material-ui/core'; -import {styles} from './Styles'; -import {SimpleModal} from './SimpleModal'; -import {ContactList} from './ContactList'; -import {initialState} from './initialState'; +import {SimpleModal} from '@client/admin/users/components/SimpleModal'; +import {ContactsList} from '@client/admin/users/components/ContactsList'; +import {contactsList, ContactModel} from './model'; +import {AddButton} from './buttons/AddButton'; -export class UsersIndexPage extends React.Component { - state = initialState; +interface Props extends WithAdminProps { + contacts: ContactModel[]; +} - componentDidUpdate() { - return true; - } +class UsersPage extends React.Component { + state = { + contacts: this.props.contacts as ContactModel[], + isOpen: false, + contact: {} as ContactModel, + id: 0, + }; handleOnOpen = () => { this.setState({isOpen: true}); @@ -27,7 +30,7 @@ export class UsersIndexPage extends React.Component { this.setState({contacts, contact: ''}); }; - handleOnId = (id: number) => () => { + handleOnUserId = (id: number) => () => { this.setState({id, isOpen: true}); const user = this.state.contacts.filter((contact) => contact.id === id).find((contact) => contact.id === id); this.setState({contact: user}); @@ -57,13 +60,11 @@ export class UsersIndexPage extends React.Component { }; render() { - const {contacts, isOpen, contact} = this.state; + const {isOpen, contact, contacts} = this.state; return ( <> - - + + { ); } } + +const WithDataComponent = (BaseComponent: React.ComponentType): any => { + const initialState = { + contacts: contactsList, + }; + return (props: any) => ; +}; + +export const UsersIndexPage = WithDataComponent(UsersPage); diff --git a/client/admin/users/components/buttons/AddButton.tsx b/client/admin/users/components/buttons/AddButton.tsx new file mode 100644 index 0000000..cc8b628 --- /dev/null +++ b/client/admin/users/components/buttons/AddButton.tsx @@ -0,0 +1,21 @@ +import * as React from 'react'; +import {Add} from '@material-ui/icons'; +import {Button, withStyles} from '@material-ui/core'; + +interface Props { + handleOnOpen: () => void; +} + +const decorate = withStyles(() => ({ + button: { + margin: '10px', + }, +})); + +export const AddButton = decorate(({classes, handleOnOpen}) => { + return ( + + ); +}); diff --git a/client/admin/users/components/buttons/CancelButton.tsx b/client/admin/users/components/buttons/CancelButton.tsx new file mode 100644 index 0000000..651432f --- /dev/null +++ b/client/admin/users/components/buttons/CancelButton.tsx @@ -0,0 +1,20 @@ +import * as React from 'react'; +import {Button, withStyles} from '@material-ui/core'; + +interface Props { + handleOnClose: () => void; +} + +const decorate = withStyles(() => ({ + button: { + margin: '10px', + }, +})); + +export const CancelButton = decorate(({classes, handleOnClose}) => { + return ( + + ); +}); diff --git a/client/admin/users/components/buttons/DeleteButton.tsx b/client/admin/users/components/buttons/DeleteButton.tsx new file mode 100644 index 0000000..144db77 --- /dev/null +++ b/client/admin/users/components/buttons/DeleteButton.tsx @@ -0,0 +1,23 @@ +import * as React from 'react'; +import {Delete as DeleteIcon} from '@material-ui/icons'; +import {Button, withStyles} from '@material-ui/core'; +import {ContactModel} from '@client/admin/users/components/model'; + +interface Props { + contact: ContactModel; + deleteContact: (id: number) => () => void; +} + +const decorate = withStyles(() => ({ + button_edit: { + margin: '10px', + }, +})); + +export const DeleteButton = decorate(({deleteContact, contact}) => { + return ( + + ); +}); diff --git a/client/admin/users/components/buttons/EditButton.tsx b/client/admin/users/components/buttons/EditButton.tsx new file mode 100644 index 0000000..f968043 --- /dev/null +++ b/client/admin/users/components/buttons/EditButton.tsx @@ -0,0 +1,23 @@ +import * as React from 'react'; +import Icon from '@material-ui/core/Icon'; +import {Button, withStyles} from '@material-ui/core'; +import {ContactModel} from '@client/admin/users/components/model'; + +interface Props { + contact: ContactModel; + handleOnUserId: (id: number) => () => void; +} + +const decorate = withStyles(() => ({ + button_edit: { + margin: '10px', + }, +})); + +export const EditButton = decorate(({classes, handleOnUserId, contact}) => { + return ( + + ); +}); diff --git a/client/admin/users/components/buttons/SubmitButton.tsx b/client/admin/users/components/buttons/SubmitButton.tsx new file mode 100644 index 0000000..954ec11 --- /dev/null +++ b/client/admin/users/components/buttons/SubmitButton.tsx @@ -0,0 +1,20 @@ +import * as React from 'react'; +import {Button, withStyles} from '@material-ui/core'; + +interface Props { + handleOnSave: () => void; +} + +const decorate = withStyles(() => ({ + button: { + margin: '10px', + }, +})); + +export const SubmitButton = decorate(({handleOnSave}) => { + return ( + + ); +}); diff --git a/client/admin/users/components/initialState.tsx b/client/admin/users/components/initialState.tsx deleted file mode 100644 index 6473de0..0000000 --- a/client/admin/users/components/initialState.tsx +++ /dev/null @@ -1,36 +0,0 @@ -export const initialState = { - contacts: [ - { - id: 1, - firstName: 'Petr', - lastName: 'Novak', - email: 'pronovaso@icloud.com', - phoneNumber: 777123456, - }, - { - id: 2, - firstName: 'Karel', - lastName: 'Omacka', - email: 'karel@gmail.com', - phoneNumber: 775345234, - }, - { - id: 3, - firstName: 'Martina', - lastName: 'Leva', - email: 'martina@seznam.cz', - phoneNumber: 603100800, - }, - ] as ContactModel[], - isOpen: false, - contact: {} as ContactModel, - id: 0, -}; - -export interface ContactModel { - id: number; - firstName: string; - lastName: string; - email: string; - phoneNumber: number | string; -} diff --git a/client/admin/users/components/model/index.ts b/client/admin/users/components/model/index.ts new file mode 100644 index 0000000..901faad --- /dev/null +++ b/client/admin/users/components/model/index.ts @@ -0,0 +1 @@ +export * from './initialState'; diff --git a/client/admin/users/components/model/initialState.ts b/client/admin/users/components/model/initialState.ts new file mode 100644 index 0000000..108b47f --- /dev/null +++ b/client/admin/users/components/model/initialState.ts @@ -0,0 +1,31 @@ +export const contactsList = [ + { + id: 1, + firstName: 'Petr', + lastName: 'Novak', + email: 'pronovaso@icloud.com', + phoneNumber: 777123456, + }, + { + id: 2, + firstName: 'Karel', + lastName: 'Omacka', + email: 'karel@gmail.com', + phoneNumber: 775345234, + }, + { + id: 3, + firstName: 'Martina', + lastName: 'Leva', + email: 'martina@seznam.cz', + phoneNumber: 603100800, + }, +] as ContactModel[]; + +export interface ContactModel { + id: number; + firstName: string; + lastName: string; + email: string; + phoneNumber: number | string; +} From 6826aa5777e6f7a0184e654c4e75470356c28cd9 Mon Sep 17 00:00:00 2001 From: Petr Novak Date: Wed, 15 Aug 2018 14:39:55 +0200 Subject: [PATCH 8/8] add Redux --- client/Store.ts | 2 + client/admin/users/actions/ContactsActions.ts | 31 ++++++++ client/admin/users/actions/index.ts | 1 + .../admin/users/components/UsersIndexPage.tsx | 71 ++++++++----------- .../admin/users/reducers/ContactsReducer.ts | 69 ++++++++++++++++++ client/admin/users/reducers/index.ts | 1 + client/admin/users/store/ContactsStore.ts | 8 +++ client/admin/users/store/index.ts | 1 + client/rootReducer.ts | 2 + 9 files changed, 143 insertions(+), 43 deletions(-) create mode 100644 client/admin/users/actions/ContactsActions.ts create mode 100644 client/admin/users/actions/index.ts create mode 100644 client/admin/users/reducers/ContactsReducer.ts create mode 100644 client/admin/users/reducers/index.ts create mode 100644 client/admin/users/store/ContactsStore.ts create mode 100644 client/admin/users/store/index.ts diff --git a/client/Store.ts b/client/Store.ts index 177bf22..c7aadcd 100644 --- a/client/Store.ts +++ b/client/Store.ts @@ -1,5 +1,7 @@ +import {ContactsStore} from '@client/admin/users/store/ContactsStore'; import {ExampleStore} from '@client/admin/example/store'; export interface Store { example: ExampleStore; + contacts: ContactsStore; } diff --git a/client/admin/users/actions/ContactsActions.ts b/client/admin/users/actions/ContactsActions.ts new file mode 100644 index 0000000..f8b4d14 --- /dev/null +++ b/client/admin/users/actions/ContactsActions.ts @@ -0,0 +1,31 @@ +import {bindActionCreators, Dispatch} from 'redux'; +import {Action} from 'redux-actions'; + +const PREFIX = 'USERS_MODUL_'; + +export const ContactsActionType = { + DELETE_CONTACT: `${PREFIX}DELETE_CONTACT`, + ID: `${PREFIX}ID`, + EMPTY_CONTACT: `${PREFIX}EMPTY_CONTACT`, + ISOPEN: `${PREFIX}ISOPEN`, + ID_CONTACT: `${PREFIX}ID_CONTACT`, + HANDLE_CONTACT: `${PREFIX}HANDLE_CONTACT`, + SAVE_CONTACT: `${PREFIX}SAVE_CONTACT`, +}; + +const ContactsActionDispatch = { + isOpen: (): Action => ({type: ContactsActionType.ISOPEN}), + contact: (): Action => ({type: ContactsActionType.EMPTY_CONTACT, payload: ''}), + deleteContact: (id: number): Action => ({type: ContactsActionType.DELETE_CONTACT, payload: id}), + idContact: (id: number): Action => ({type: ContactsActionType.ID_CONTACT, payload: id}), + id: (id: number): Action => ({type: ContactsActionType.ID, payload: id}), + handleOnChange: (e: React.ChangeEvent): Action> => ({ + type: ContactsActionType.HANDLE_CONTACT, + payload: e, + }), + saveContact: (): Action => ({type: ContactsActionType.SAVE_CONTACT, payload: ''}), +}; + +export type ContactsAction = typeof ContactsActionDispatch; + +export const ContactsActionCreator = (dispatch: Dispatch) => bindActionCreators(ContactsActionDispatch, dispatch); diff --git a/client/admin/users/actions/index.ts b/client/admin/users/actions/index.ts new file mode 100644 index 0000000..db44149 --- /dev/null +++ b/client/admin/users/actions/index.ts @@ -0,0 +1 @@ +export * from './ContactsActions'; diff --git a/client/admin/users/components/UsersIndexPage.tsx b/client/admin/users/components/UsersIndexPage.tsx index 0422d6f..217652d 100644 --- a/client/admin/users/components/UsersIndexPage.tsx +++ b/client/admin/users/components/UsersIndexPage.tsx @@ -2,65 +2,54 @@ import * as React from 'react'; import {WithAdminProps} from '@client/with/withAdmin'; import {SimpleModal} from '@client/admin/users/components/SimpleModal'; import {ContactsList} from '@client/admin/users/components/ContactsList'; -import {contactsList, ContactModel} from './model'; import {AddButton} from './buttons/AddButton'; +import {ContactsStore} from '../store'; +import {connect} from 'react-redux'; +import {Store} from '@client/Store'; +import {ContactsActionCreator, ContactsAction} from '../actions'; -interface Props extends WithAdminProps { - contacts: ContactModel[]; +interface OwnProps extends WithAdminProps {} + +interface ConnectedState { + readonly contacts: ContactsStore; } -class UsersPage extends React.Component { - state = { - contacts: this.props.contacts as ContactModel[], - isOpen: false, - contact: {} as ContactModel, - id: 0, - }; +interface ConnectedDispatch extends ContactsAction {} + +type Props = ConnectedState & ConnectedDispatch & OwnProps; +class UsersPage extends React.Component { handleOnOpen = () => { - this.setState({isOpen: true}); + this.props.isOpen(); }; handleOnClose = () => { - this.setState({isOpen: false, contact: ''}); + this.props.isOpen(); + this.props.contact(); }; handleOnDelete = (id: number) => () => { - const contacts = [...this.state.contacts.filter((contact) => contact.id !== id)]; - this.setState({contacts, contact: ''}); + this.props.deleteContact(id); }; handleOnUserId = (id: number) => () => { - this.setState({id, isOpen: true}); - const user = this.state.contacts.filter((contact) => contact.id === id).find((contact) => contact.id === id); - this.setState({contact: user}); + this.props.id(id); + this.props.isOpen(); + this.props.idContact(id); }; handleOnChange = (e: any) => { - this.setState({ - contact: {...this.state.contact, [e.target.name]: e.target.value}, - }); + this.props.handleOnChange(e); }; handleOnSave = () => { - const userId = new Date().getMilliseconds(); - const {contact, id} = this.state; - const contactsMap = this.state.contacts.map((user) => (user.id === id ? {...user, ...contact} : user)); - const contactFilter = contactsMap.filter((contacts) => contacts); - if (id > 0) { - this.setState({ - contacts: contactFilter, - isOpen: false, - contact: '', - }); - } - if (id === 0) { - this.setState({contacts: [...this.state.contacts, {...contact, id: userId}], isOpen: false, contact: ''}); - } + this.props.saveContact(); }; render() { - const {isOpen, contact, contacts} = this.state; + const { + contacts: {contacts, isOpen, contact}, + } = this.props; return ( <> @@ -77,11 +66,7 @@ class UsersPage extends React.Component { } } -const WithDataComponent = (BaseComponent: React.ComponentType): any => { - const initialState = { - contacts: contactsList, - }; - return (props: any) => ; -}; - -export const UsersIndexPage = WithDataComponent(UsersPage); +export const UsersIndexPage = connect( + ({contacts}: Store) => ({contacts}), + ContactsActionCreator, +)(UsersPage); diff --git a/client/admin/users/reducers/ContactsReducer.ts b/client/admin/users/reducers/ContactsReducer.ts new file mode 100644 index 0000000..9631d4d --- /dev/null +++ b/client/admin/users/reducers/ContactsReducer.ts @@ -0,0 +1,69 @@ +import {contactsList} from './../components/model/initialState'; +import {handleActions} from 'redux-actions'; +import {ContactsActionType as ActionType} from '../actions'; +import {ContactsStore as Store} from '../store'; + +const initialState = { + contacts: contactsList, + contact: {}, + isOpen: false, + id: 0, +} as Store; + +export const ContactsReducer = handleActions( + { + [ActionType.ISOPEN]: (state: Store): Store => ({ + ...state, + isOpen: state.isOpen === false ? true : false, + }), + [ActionType.EMPTY_CONTACT]: (state: Store, {payload}): Store => ({ + ...state, + contact: payload, + }), + [ActionType.DELETE_CONTACT]: (state: Store, {payload}): Store => ({ + ...state, + contacts: [...state.contacts.filter((contact) => contact.id !== payload)], + }), + [ActionType.ID_CONTACT]: (state: Store, {payload}): Store => { + const user = state.contacts.filter((contact) => contact.id === payload).find((contact) => contact.id === payload); + if (user !== undefined) { + return { + ...state, + contact: user, + }; + } + return state; + }, + [ActionType.HANDLE_CONTACT]: (state: Store, {payload}): Store => ({ + ...state, + contact: {...state.contact, [payload.target.name]: payload.target.value}, + }), + [ActionType.ID]: (state: Store, {payload}): Store => ({ + ...state, + id: payload, + }), + [ActionType.SAVE_CONTACT]: (state: Store, {payload}): any => { + const userId = new Date().getMilliseconds(); + const {contact, id} = state; + const contactsMap = state.contacts.map((user) => (user.id === id ? {...user, ...contact} : user)); + const contactFilter = contactsMap.filter((contacts) => contacts); + if (id > 0) { + return { + ...state, + contacts: contactFilter, + isOpen: false, + contact: payload, + }; + } + if (id === 0) { + return { + ...state, + contacts: [...state.contacts, {...contact, id: userId}], + isOpen: false, + contact: payload, + }; + } + }, + }, + initialState, +); diff --git a/client/admin/users/reducers/index.ts b/client/admin/users/reducers/index.ts new file mode 100644 index 0000000..22c08ed --- /dev/null +++ b/client/admin/users/reducers/index.ts @@ -0,0 +1 @@ +export * from './ContactsReducer'; diff --git a/client/admin/users/store/ContactsStore.ts b/client/admin/users/store/ContactsStore.ts new file mode 100644 index 0000000..f848e64 --- /dev/null +++ b/client/admin/users/store/ContactsStore.ts @@ -0,0 +1,8 @@ +import {ContactModel} from '@client/admin/users/components/model'; + +export interface ContactsStore { + contacts: ContactModel[]; + contact: ContactModel; + isOpen: boolean; + id: number; +} diff --git a/client/admin/users/store/index.ts b/client/admin/users/store/index.ts new file mode 100644 index 0000000..3400fcd --- /dev/null +++ b/client/admin/users/store/index.ts @@ -0,0 +1 @@ +export * from './ContactsStore'; diff --git a/client/rootReducer.ts b/client/rootReducer.ts index 1cc1cfa..0bc7a2e 100644 --- a/client/rootReducer.ts +++ b/client/rootReducer.ts @@ -1,3 +1,4 @@ +import {ContactsReducer} from './admin/users/reducers/ContactsReducer'; import {combineReducers} from 'redux'; import {Store} from '@client/Store'; import {ExampleReducer} from '@client/admin/example/reducers'; @@ -6,4 +7,5 @@ type Reducers = {[P in keyof Store]: any}; export const rootReducer = combineReducers({ example: ExampleReducer, + contacts: ContactsReducer, });