diff --git a/src/client/web/actions/event.js b/src/client/web/actions/event.js new file mode 100644 index 0000000..239a21a --- /dev/null +++ b/src/client/web/actions/event.js @@ -0,0 +1,67 @@ +import { requestJson } from './utils'; + +export const LOAD_SPENDINGS = 'spendings:load'; +export const loadSpendings = () => dispatch => { + requestJson({ + method: 'GET', + url: '/api/spendings', + }) + .then(data => dispatch(spendingsLoaded(data))) + .catch(() => alert('spendings:load ERROR')); +}; + +export const SPENDINGS_LOADED = 'spendings:loaded'; +export const spendingsLoaded = spendings => ({ + type: SPENDINGS_LOADED, + payload: { spendings }, +}); + +export const ADD_SPENDING = 'spending:add'; +export const addSpending = data => dispatch => { + requestJson({ + method: 'POST', + url: '/api/spendings', + body: data, + }) + .then(data => dispatch(spendingAdded(data))) + .catch(() => alert('spending:add ERROR')); +}; + +export const SPENDING_ADDED = 'spending:added'; +export const spendingAdded = spending => ({ + type: SPENDING_ADDED, + payload: { spending }, +}); + +export const UPDATE_SPENDING = 'spending:update'; +export const updateSpending = data => dispatch => { + requestJson({ + method: 'PATCH', + url: `/api/spendings/${data.id}`, + body: data, + }) + .then(data => dispatch(spendingUpdated(data))) + .catch(() => alert('spendings:update ERROR')); +}; + +export const SPENDING_UPDATED = 'spending:updated'; +export const spendingUpdated = spending => ({ + type: SPENDING_UPDATED, + payload: { spending }, +}); + +export const DELETE_SPENDING = 'spending:deleted'; +export const deleteSpending = id => dispatch => { + requestJson({ + method: 'DELETE', + url: `/api/spendings/${id}`, + }) + .then(data => dispatch(spendingDeleted(data))) + .catch(() => alert('spending:delete ERROR')); +}; + +export const SPENDING_DELETED = 'spending:deleted'; +export const spendingDeleted = ({ id }) => ({ + type: SPENDING_DELETED, + payload: { id }, +}); diff --git a/src/client/web/components/App/__tests__/__snapshots__/index.js.snap b/src/client/web/components/App/__tests__/__snapshots__/index.js.snap index 969099c..650c5c7 100644 --- a/src/client/web/components/App/__tests__/__snapshots__/index.js.snap +++ b/src/client/web/components/App/__tests__/__snapshots__/index.js.snap @@ -16,6 +16,12 @@ exports[`App should match snapshot 1`] = ` key="/events" path="/events" /> + { id="attendeeIds" name="attendeeIds" classes={classes} + multiple={true} component={SelectPeople} /> diff --git a/src/client/web/components/Event/Preview.js b/src/client/web/components/Event/Preview.js index 47195be..ef4c29a 100644 --- a/src/client/web/components/Event/Preview.js +++ b/src/client/web/components/Event/Preview.js @@ -27,9 +27,18 @@ const style = { }, }; -const Preview = ({ attendeeIds, createdAt, label, image, people, classes }) => { +const Preview = ({ + id, + attendeeIds, + createdAt, + label, + image, + people, + classes, + history, +}) => { return ( - + history.push(`/event/${id}`)}> { }; Preview.propTypes = { + id: PropTypes.string, attendeeIds: PropTypes.array, createdAt: PropTypes.string, label: PropTypes.string, image: PropTypes.string, people: PropTypes.array, classes: PropTypes.object, + history: PropTypes.object, }; export default injectSheet(style)(Preview); diff --git a/src/client/web/components/Spending/Add.js b/src/client/web/components/Spending/Add.js new file mode 100644 index 0000000..72226e1 --- /dev/null +++ b/src/client/web/components/Spending/Add.js @@ -0,0 +1,87 @@ +import React, { Fragment } from 'react'; +import PropTypes from 'prop-types'; +import injectSheet from 'react-jss'; +import { Formik } from 'formik'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import Button from 'material-ui/Button'; +import AddOrEdit from './AddOrEdit'; + +const styles = { + appBar: { + position: 'relative', + }, + flex: { + flex: 1, + }, + input: { + margin: 5, + }, + form: { + display: 'grid', + gridTemplateColumns: 'auto', + gridTemplateRows: 'auto', + gridTemplateAreas: "'label' 'currency' 'people'", + }, + formControl: { + margin: 5, + minWidth: 120, + }, +}; + +const Add = ({ eventId, addSpending, handleClose, classes }) => { + return ( + { + let errors = {}; + + if (!values.label) { + errors.label = 'Required field'; + } + + return errors; + }} + onSubmit={values => { + const newSpending = { + ...values, + }; + console.log(newSpending); + addSpending(newSpending); + handleClose(); + }} + render={({ handleSubmit }) => ( + + Fill the form + + + + + + + + + )} + /> + ); +}; + +Add.propTypes = { + eventId: PropTypes.string, + addSpending: PropTypes.func, + handleClose: PropTypes.func, + classes: PropTypes.object, +}; + +export default injectSheet(styles)(Add); diff --git a/src/client/web/components/Spending/AddOrEdit.js b/src/client/web/components/Spending/AddOrEdit.js new file mode 100644 index 0000000..351c0f6 --- /dev/null +++ b/src/client/web/components/Spending/AddOrEdit.js @@ -0,0 +1,94 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { map } from 'ramda'; +import { Form } from 'formik'; +import { Field, FieldArray } from 'formik'; +import Button from '@material-ui/core/Button'; +import { InputField, SelectField, SelectPeople } from '../../fields'; + +const AddOrEdit = ({ classes }) => { + const currencies = [ + { + value: 'USD', + label: '$', + }, + { + value: 'EUR', + label: '€', + }, + { + value: 'BTC', + label: '฿', + }, + { + value: 'JPY', + label: '¥', + }, + ]; + + return ( +
+ + + + { + const { attendees } = arrayHelper.form.values; + + return ( +
+ + {attendees && + attendees.map((attendee, index) => ( +
+ + + +
+ ))} +
+ ); + }} + /> + + ); +}; + +AddOrEdit.propTypes = { + classes: PropTypes.object, +}; + +export default AddOrEdit; diff --git a/src/client/web/components/Spending/Edit.js b/src/client/web/components/Spending/Edit.js new file mode 100644 index 0000000..5b5e7aa --- /dev/null +++ b/src/client/web/components/Spending/Edit.js @@ -0,0 +1,84 @@ +import React, { Fragment } from 'react'; +import PropTypes from 'prop-types'; +import injectSheet from 'react-jss'; +import { Formik } from 'formik'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import Button from 'material-ui/Button'; +import AddOrEdit from './AddOrEdit'; + +const styles = { + appBar: { + position: 'relative', + }, + flex: { + flex: 1, + }, + input: { + margin: 5, + }, + form: { + display: 'grid', + gridTemplateColumns: 'auto', + gridTemplateRows: 'auto', + gridTemplateAreas: "'label' 'currency' 'people'", + }, + formControl: { + margin: 5, + minWidth: 120, + }, +}; + +const Edit = ({ spending, updateSpending, handleClose, classes }) => { + console.log(spending); + return ( + { + let errors = {}; + + if (!values.label) { + errors.label = 'Name is required'; + } + + return errors; + }} + onSubmit={values => { + const newSpending = { + ...values, + }; + + updateSpending(newSpending); + handleClose(); + }} + render={({ handleSubmit }) => ( + + Edit the form + + + + + + + + + )} + /> + ); +}; + +Edit.propTypes = { + spending: PropTypes.object, + updateSpending: PropTypes.func, + handleClose: PropTypes.func, + classes: PropTypes.object, +}; + +export default injectSheet(styles)(Edit); diff --git a/src/client/web/components/Spending/Form.js b/src/client/web/components/Spending/Form.js new file mode 100644 index 0000000..4474585 --- /dev/null +++ b/src/client/web/components/Spending/Form.js @@ -0,0 +1,45 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Dialog from '@material-ui/core/Dialog'; +import Add from './Add'; +import Edit from './Edit'; + +const Form = ({ + id, + isOpen, + spending, + addSpending, + updateSpending, + handleClose, +}) => { + return ( + + {spending ? ( + + ) : ( + + )} + + ); +}; + +Form.propTypes = { + id: PropTypes.string, + isOpen: PropTypes.bool, + addSpending: PropTypes.func, + updateSpending: PropTypes.func, + handleClose: PropTypes.func, + spending: PropTypes.object, +}; + +export default Form; diff --git a/src/client/web/components/Spending/Preview.js b/src/client/web/components/Spending/Preview.js new file mode 100644 index 0000000..1fd3aab --- /dev/null +++ b/src/client/web/components/Spending/Preview.js @@ -0,0 +1,50 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { format } from 'date-fns'; +import Card, { CardHeader, CardContent } from 'material-ui/Card'; +import IconButton from '@material-ui/core/IconButton'; +import Icon from '@material-ui/core/Icon'; +import DeleteIcon from '@material-ui/icons/Delete'; + +const Preview = ({ + classes, + id, + label, + amount, + createdAt, + deleteSpending, + setId, +}) => { + return ( + + + setId(id)}> + edit_icon + + deleteSpending(id)}> + + + + } + subheader={format(createdAt, 'DD MMMM YYYY')} + title={label} + /> + {`${amount ? amount : 0} $`} + + ); +}; + +Preview.propTypes = { + classes: PropTypes.object, + id: PropTypes.string, + label: PropTypes.string, + amount: PropTypes.number, + deleteSpending: PropTypes.func, + setId: PropTypes.func, + createdAt: PropTypes.string, +}; + +export default Preview; diff --git a/src/client/web/fields/index.js b/src/client/web/fields/index.js index 3f8320e..0f97341 100644 --- a/src/client/web/fields/index.js +++ b/src/client/web/fields/index.js @@ -64,14 +64,14 @@ const mapStateToProps = state => ({ }); export const SelectPeople = connect(mapStateToProps)( - ({ people, field, classes }) => { + ({ people, field, classes, multiple }) => { return ( People