Skip to content

Commit

Permalink
Merge pull request #172 from hql287/edit-invoice
Browse files Browse the repository at this point in the history
Allow editing invoices
  • Loading branch information
hql287 authored Jan 18, 2018
2 parents 282e615 + 3cd5be6 commit 93ef227
Show file tree
Hide file tree
Showing 15 changed files with 564 additions and 15 deletions.
11 changes: 11 additions & 0 deletions app/actions/invoices.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@ export const saveInvoice = createAction(
invoiceData => invoiceData
);

// Edit an Invoice
export const editInvoice = createAction(
ACTION_TYPES.INVOICE_EDIT,
invoiceData => invoiceData
);

export const updateInvoice = createAction(
ACTION_TYPES.INVOICE_UPDATE,
(invoiceID, data) => ({ invoiceID, data })
);

// New Invoice from Contact
export const newInvoiceFromContact = createAction(
ACTION_TYPES.INVOICE_NEW_FROM_CONTACT,
Expand Down
12 changes: 12 additions & 0 deletions app/components/form/Discount.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Libraries
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { isEmpty } from 'lodash';

// Custom Components
import { Section } from '../shared/Section';
Expand Down Expand Up @@ -42,6 +43,17 @@ export class Discount extends Component {
});
}

// Handle Reset Form
componentWillReceiveProps(nextProps) {
const { discount } = nextProps;
if (isEmpty(discount)) {
this.setState({
amount: '',
type: 'percentage',
});
}
}

shouldComponentUpdate(nextProps, nextState) {
return (
this.state !== nextState || this.props.discount !== nextProps.discount
Expand Down
15 changes: 5 additions & 10 deletions app/components/invoices/Invoice.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Libs
import React, { Component } from 'react';
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { truncate } from 'lodash';
import styled from 'styled-components';
Expand Down Expand Up @@ -142,7 +142,7 @@ const Field = styled.div`
`;

// Component
class Invoice extends Component {
class Invoice extends PureComponent {
constructor(props) {
super(props);
this.viewInvoice = this.viewInvoice.bind(this);
Expand All @@ -151,20 +151,14 @@ class Invoice extends Component {
this.displayStatus = this.displayStatus.bind(this);
}

shouldComponentUpdate(nextProps) {
if (this.props.invoice.status !== nextProps.invoice.status) {
return true;
}
return false;
}

deleteInvoice() {
const { invoice, deleteInvoice } = this.props;
deleteInvoice(invoice._id);
}

editInvoice() {
// TODO
const { invoice, editInvoice } = this.props;
editInvoice(invoice);
}

viewInvoice() {
Expand Down Expand Up @@ -297,6 +291,7 @@ class Invoice extends Component {
}

Invoice.propTypes = {
editInvoice: PropTypes.func.isRequired,
deleteInvoice: PropTypes.func.isRequired,
invoice: PropTypes.object.isRequired,
setInvoiceStatus: PropTypes.func.isRequired,
Expand Down
15 changes: 15 additions & 0 deletions app/components/invoices/__tests__/Invoice.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const invoice = {
},
};

const editInvoice = jest.fn();
const deleteInvoice = jest.fn();
const setInvoiceStatus = jest.fn();
const dateFormat = 'MM/DD/YY';
Expand All @@ -43,6 +44,7 @@ describe('Renders correctly to the DOM', () => {
wrapper = shallow(
<Invoice
invoice={invoice}
editInvoice={editInvoice}
deleteInvoice={deleteInvoice}
setInvoiceStatus={setInvoiceStatus}
dateFormat={dateFormat}
Expand All @@ -54,6 +56,7 @@ describe('Renders correctly to the DOM', () => {
const mountWrapper = mount(
<Invoice
invoice={invoice}
editInvoice={editInvoice}
deleteInvoice={deleteInvoice}
setInvoiceStatus={setInvoiceStatus}
dateFormat={dateFormat}
Expand Down Expand Up @@ -90,6 +93,7 @@ describe('Renders correctly to the DOM', () => {
const paidInvoiceWapper = shallow(
<Invoice
invoice={paidInvoice}
editInvoice={editInvoice}
deleteInvoice={deleteInvoice}
setInvoiceStatus={setInvoiceStatus}
dateFormat={dateFormat}
Expand All @@ -110,6 +114,7 @@ describe('Renders correctly to the DOM', () => {
const cancelledInvoiceWapper = shallow(
<Invoice
invoice={cancelledInvoice}
editInvoice={editInvoice}
deleteInvoice={deleteInvoice}
setInvoiceStatus={setInvoiceStatus}
dateFormat={dateFormat}
Expand All @@ -128,6 +133,7 @@ describe('Renders correctly to the DOM', () => {
const refundedInvoiceWapper = shallow(
<Invoice
invoice={refundedInvoice}
editInvoice={editInvoice}
deleteInvoice={deleteInvoice}
setInvoiceStatus={setInvoiceStatus}
dateFormat={dateFormat}
Expand Down Expand Up @@ -156,6 +162,7 @@ describe('Renders correctly to the DOM', () => {
.create(
<Invoice
invoice={invoice}
editInvoice={editInvoice}
deleteInvoice={deleteInvoice}
setInvoiceStatus={setInvoiceStatus}
dateFormat={dateFormat}
Expand All @@ -174,6 +181,7 @@ describe('handle clicks correctly', () => {
wrapper = shallow(
<Invoice
invoice={invoice}
editInvoice={editInvoice}
deleteInvoice={deleteInvoice}
setInvoiceStatus={setInvoiceStatus}
dateFormat={dateFormat}
Expand All @@ -190,6 +198,13 @@ describe('handle clicks correctly', () => {
deleteBtn = wrapper.find(Button).first();
});

// TODO
it('handle edit action correctly', () => {
editBtn.simulate('click');
expect(editInvoice).toHaveBeenCalled();
expect(editInvoice).toHaveBeenCalledWith(invoice);
});

it('handle delete action correctly', () => {
deleteBtn.simulate('click');
expect(deleteInvoice).toHaveBeenCalled();
Expand Down
2 changes: 2 additions & 0 deletions app/constants/actions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export const SAVED_FORM_SETTING_UPDATE = 'SAVED_FORM_SETTING_UPDATE';

// INVOICE
// ===========================================================
export const INVOICE_EDIT = 'INVOICE_EDIT';
export const INVOICE_UPDATE = 'INVOICE_UPDATE';
export const INVOICE_SAVE = 'INVOICE_SAVE';
export const INVOICE_DELETE = 'INVOICE_DELETE';
export const INVOICE_GET_ALL = 'INVOICE_GET_ALL';
Expand Down
10 changes: 7 additions & 3 deletions app/containers/Form.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class Form extends Component {
settings,
savedSettings,
} = this.props.currentInvoice;
const { required_fields, open } = settings;
const { required_fields, open, editMode } = settings;
return (
<PageWrapper>
<PageHeader>
Expand All @@ -66,8 +66,12 @@ class Form extends Component {
<Button danger onClick={clearForm}>
Clear
</Button>
<Button primary onClick={saveFormData}>
Save & Preview
<Button
primary={editMode.active}
success={editMode.active === false}
onClick={saveFormData}
>
{editMode.active ? 'Update' : 'Save & Preview'}
</Button>
</PageHeaderActions>
</PageHeader>
Expand Down
7 changes: 7 additions & 0 deletions app/containers/Invoices.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
class Invoices extends PureComponent {
constructor(props) {
super(props);
this.editInvoice = this.editInvoice.bind(this);
this.deleteInvoice = this.deleteInvoice.bind(this);
this.setInvoiceStatus = this.setInvoiceStatus.bind(this);
}
Expand Down Expand Up @@ -72,13 +73,19 @@ class Invoices extends PureComponent {
dispatch(Actions.setInvoiceStatus(invoiceId, status));
}

editInvoice(invoice) {
const { dispatch } = this.props;
dispatch(Actions.editInvoice(invoice));
}

// Render
render() {
const { invoices, dateFormat } = this.props;
const invoicesComponent = invoices.map((invoice, index) => (
<Invoice
key={invoice._id}
deleteInvoice={this.deleteInvoice}
editInvoice={this.editInvoice}
setInvoiceStatus={this.setInvoiceStatus}
index={index}
dateFormat={dateFormat}
Expand Down
12 changes: 12 additions & 0 deletions app/helpers/__mocks__/pouchDB.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ const saveDoc = jest.fn(
})
);

const updateDoc = jest.fn(
(dbName, doc) =>
new Promise((resolve, reject) => {
!dbName && reject(new Error('No database found!'));
!doc && reject(new Error('No doc found!'));
dbName === 'contacts' && resolve([...mockData.contactsRecords]);
dbName === 'invoices' && resolve([...mockData.invoicesRecords]);
})
);

const deleteDoc = jest.fn(
(dbName, docId) =>
new Promise((resolve, reject) => {
Expand All @@ -70,9 +80,11 @@ const deleteDoc = jest.fn(
})
);


module.exports = {
getAllDocs,
saveDoc,
updateDoc,
deleteDoc,
mockData,
};
14 changes: 12 additions & 2 deletions app/middlewares/FormMW.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import * as FormActions from '../actions/form';
import * as InvoicesActions from '../actions/invoices';
import * as ContactsActions from '../actions/contacts';
import * as SettingsActions from '../actions/settings';
import * as UIActions from '../actions/ui';

// Helper
import { getInvoiceData, validateFormData } from '../helpers/form';
Expand All @@ -20,9 +21,18 @@ const FormMW = ({ dispatch, getState }) => next => action => {
const currentFormData = getState().form;
// Validate Form Data
if (!validateFormData(currentFormData)) return;
// Save Invoice To DB
const currentInvoiceData = getInvoiceData(currentFormData);
dispatch(InvoicesActions.saveInvoice(currentInvoiceData));
// Check Edit Mode
if (currentFormData.settings.editMode.active) {
const invoiceId = currentFormData.settings.editMode.data._id;
// Update existing invoice
dispatch(InvoicesActions.updateInvoice(invoiceId, currentInvoiceData));
// Change Tab to invoices
dispatch(UIActions.changeActiveTab('invoices'));
} else {
// Save Invoice To DB
dispatch(InvoicesActions.saveInvoice(currentInvoiceData));
}
// Save Contact to DB if it's a new one
if (currentFormData.recipient.newRecipient) {
const newContactData = currentFormData.recipient.new;
Expand Down
39 changes: 39 additions & 0 deletions app/middlewares/InvoicesMW.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const InvoicesMW = ({ dispatch }) => next => action => {
newRecipient: false,
})
);
break;
}

case ACTION_TYPES.INVOICE_GET_ALL: {
Expand Down Expand Up @@ -89,6 +90,44 @@ const InvoicesMW = ({ dispatch }) => next => action => {
});
}

case ACTION_TYPES.INVOICE_EDIT: {
// Continue
next(action);
// Change Tab to Form
dispatch(UIActions.changeActiveTab('form'));
break;
}

case ACTION_TYPES.INVOICE_UPDATE: {
return updateDoc('invoices', action.payload.invoiceID, {
...action.payload.data,
subtotal: getInvoiceValue(action.payload.data).subtotal,
grandTotal: getInvoiceValue(action.payload.data).grandTotal,
})
.then(docs => {
next({
type: ACTION_TYPES.INVOICE_UPDATE,
payload: docs,
});
dispatch({
type: ACTION_TYPES.UI_NOTIFICATION_NEW,
payload: {
type: 'success',
message: 'Invoice Updated Successfully',
},
});
})
.catch(err => {
next({
type: ACTION_TYPES.UI_NOTIFICATION_NEW,
payload: {
type: 'warning',
message: err.message,
},
});
});
}

case ACTION_TYPES.INVOICE_DELETE: {
return deleteDoc('invoices', action.payload)
.then(remainingDocs => {
Expand Down
Loading

0 comments on commit 93ef227

Please sign in to comment.