diff --git a/app.js b/app.js
index 12398142..4a7d28dc 100644
--- a/app.js
+++ b/app.js
@@ -208,6 +208,7 @@ function setInitialValues() {
},
decimalFractions: 2,
currencyPlacement: 'before',
+ decimalSeparator: 'dot',
},
};
diff --git a/app/actions/__tests__/settings.spec.js b/app/actions/__tests__/settings.spec.js
index 8c14fe92..55b8be36 100644
--- a/app/actions/__tests__/settings.spec.js
+++ b/app/actions/__tests__/settings.spec.js
@@ -30,3 +30,11 @@ it('saveSettings should create SAVE_SETTINGS action', () => {
payload: settingsData,
});
});
+
+it('notifyInvalidDecimalFractions should create UI_NOTIFICATION_NEW action', () => {
+ const settingsData = {};
+ expect(actions.notifyInvalidDecimalFractions(settingsData)).toEqual({
+ type: ACTION_TYPES.UI_NOTIFICATION_NEW,
+ payload: {message: 'Invalid decimal fractions specified! Please correct settings.', type: 'warning'},
+ });
+});
diff --git a/app/actions/settings.jsx b/app/actions/settings.jsx
index 9bccc62a..192fba07 100644
--- a/app/actions/settings.jsx
+++ b/app/actions/settings.jsx
@@ -17,3 +17,9 @@ export const saveSettings = createAction(
ACTION_TYPES.SETTINGS_SAVE,
data => data
);
+
+// Notify user about invalid decimal fractions
+export const notifyInvalidDecimalFractions = createAction(
+ ACTION_TYPES.UI_NOTIFICATION_NEW,
+ (type, message) => ({type: 'warning', message: 'Invalid decimal fractions specified! Please correct settings.'})
+);
diff --git a/app/components/invoices/__tests__/Invoice.spec.js b/app/components/invoices/__tests__/Invoice.spec.js
index d797b4bd..a628288a 100644
--- a/app/components/invoices/__tests__/Invoice.spec.js
+++ b/app/components/invoices/__tests__/Invoice.spec.js
@@ -36,6 +36,7 @@ const editInvoice = jest.fn();
const deleteInvoice = jest.fn();
const setInvoiceStatus = jest.fn();
const dateFormat = 'MM/DD/YY';
+const currencyPlacement = 'before';
// Tests
describe('Renders correctly to the DOM', () => {
@@ -48,6 +49,7 @@ describe('Renders correctly to the DOM', () => {
deleteInvoice={deleteInvoice}
setInvoiceStatus={setInvoiceStatus}
dateFormat={dateFormat}
+ currencyPlacement={currencyPlacement}
/>
);
});
@@ -60,6 +62,7 @@ describe('Renders correctly to the DOM', () => {
deleteInvoice={deleteInvoice}
setInvoiceStatus={setInvoiceStatus}
dateFormat={dateFormat}
+ currencyPlacement={currencyPlacement}
/>
);
expect(mountWrapper.prop('invoice')).toEqual(invoice);
@@ -97,6 +100,7 @@ describe('Renders correctly to the DOM', () => {
deleteInvoice={deleteInvoice}
setInvoiceStatus={setInvoiceStatus}
dateFormat={dateFormat}
+ currencyPlacement={currencyPlacement}
/>
);
expect(
@@ -118,6 +122,7 @@ describe('Renders correctly to the DOM', () => {
deleteInvoice={deleteInvoice}
setInvoiceStatus={setInvoiceStatus}
dateFormat={dateFormat}
+ currencyPlacement={currencyPlacement}
/>
);
expect(
@@ -137,6 +142,7 @@ describe('Renders correctly to the DOM', () => {
deleteInvoice={deleteInvoice}
setInvoiceStatus={setInvoiceStatus}
dateFormat={dateFormat}
+ currencyPlacement={currencyPlacement}
/>
);
expect(
@@ -166,6 +172,7 @@ describe('Renders correctly to the DOM', () => {
deleteInvoice={deleteInvoice}
setInvoiceStatus={setInvoiceStatus}
dateFormat={dateFormat}
+ currencyPlacement={currencyPlacement}
/>
)
.toJSON();
@@ -185,6 +192,7 @@ describe('handle clicks correctly', () => {
deleteInvoice={deleteInvoice}
setInvoiceStatus={setInvoiceStatus}
dateFormat={dateFormat}
+ currencyPlacement={currencyPlacement}
/>
);
viewBtn = wrapper
diff --git a/app/components/invoices/__tests__/__snapshots__/Invoice.spec.js.snap b/app/components/invoices/__tests__/__snapshots__/Invoice.spec.js.snap
index fcd0ebca..a125ad09 100644
--- a/app/components/invoices/__tests__/__snapshots__/Invoice.spec.js.snap
+++ b/app/components/invoices/__tests__/__snapshots__/Invoice.spec.js.snap
@@ -71,7 +71,9 @@ exports[`Renders correctly to the DOM matches snapshot 1`] = `
USD
- 3,843
+ 3,843.00
+
+
diff --git a/app/components/settings/Invoice.jsx b/app/components/settings/Invoice.jsx
index bc3fd764..377f3ebf 100644
--- a/app/components/settings/Invoice.jsx
+++ b/app/components/settings/Invoice.jsx
@@ -66,14 +66,34 @@ class Invoice extends Component {
}
handleInputChange(event) {
+ const { setSavable } = this.props;
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
+
+ const canSave = this.canSave(name, value);
+ setSavable(canSave);
+ if (!canSave) {
+ // Notifi
+ console.log('Cant save');
+ }
+
this.setState({ [name]: value }, () => {
this.props.updateSettings('invoice', this.state);
});
}
+ canSave(ctrlName, value) {
+ let valid = true;
+ if (ctrlName === 'decimalFractions' && Number(value) < 0){
+ valid = false;
+ const { invalidFractionMsg } = this.props;
+ invalidFractionMsg();
+ }
+
+ return valid;
+ }
+
handleTaxChange(event) {
const target = event.target;
const name = target.name;
@@ -145,6 +165,7 @@ class Invoice extends Component {
dateFormat,
decimalFractions,
currencyPlacement,
+ decimalSeparator,
} = this.state;
return (
@@ -359,6 +380,22 @@ class Invoice extends Component {
+
+
+ Decimal Separator
+
+ Dot (.)
+ Comma (,)
+
+
+ {/* To prevent stretching the first field. Use for next paramter in future and remove comment afterwards */}
+
+
+
);
diff --git a/app/containers/Settings.jsx b/app/containers/Settings.jsx
index 927f0d0d..e4723c57 100644
--- a/app/containers/Settings.jsx
+++ b/app/containers/Settings.jsx
@@ -34,8 +34,9 @@ import _withFadeInAnimation from '../components/shared/hoc/_withFadeInAnimation'
class Settings extends Component {
constructor(props) {
super(props);
- this.state = { visibleTab: 1 };
+ this.state = { visibleTab: 1, canSave: true };
this.saveSettingsState = this.saveSettingsState.bind(this);
+ this.setSavable = this.setSavable.bind(this);
}
// Check if settings have been saved
@@ -55,15 +56,20 @@ class Settings extends Component {
this.setState({ visibleTab: tabNum });
}
+ // controls if save button appears
+ setSavable(settingsValid) {
+ this.setState({canSave: settingsValid});
+ }
+
// Render Main Content
renderSettingsContent() {
const { profile, general, invoice } = this.props.currentSettings;
- const { updateSettings } = this.props.boundActionCreators;
+ const { updateSettings, notifyInvalidDecimalFractions } = this.props.boundActionCreators;
return (
Settings
- {!this.settingsSaved() && (
+ {!this.settingsSaved() && this.state.canSave && (
Save
@@ -97,13 +103,13 @@ class Settings extends Component {
{this.state.visibleTab === 1 && (
-
+
)}
{this.state.visibleTab === 2 && (
-
+
)}
{this.state.visibleTab === 3 && (
-
+
)}
diff --git a/app/helpers/__tests__/form.spec.js b/app/helpers/__tests__/form.spec.js
index b9ccaebf..a8990aab 100644
--- a/app/helpers/__tests__/form.spec.js
+++ b/app/helpers/__tests__/form.spec.js
@@ -65,6 +65,9 @@ describe('getInvoiceData', () => {
tax: false,
note: false,
},
+ decimalFractions: 2,
+ currencyPlacement: 'before',
+ decimalSeparator: 'dot',
},
savedSettings: {
tax: {},
@@ -76,6 +79,9 @@ describe('getInvoiceData', () => {
tax: false,
note: false,
},
+ decimalFractions: 2,
+ currencyPlacement: 'before',
+ decimalSeparator: 'dot',
},
};
});
@@ -277,6 +283,9 @@ describe('validateFormData', () => {
tax: true,
note: true,
},
+ decimalFractions: 2,
+ currencyPlacement: 'before',
+ decimalSeparator: 'dot',
},
savedSettings: {
tax: {
@@ -292,6 +301,9 @@ describe('validateFormData', () => {
tax: true,
note: true,
},
+ decimalFractions: 2,
+ currencyPlacement: 'before',
+ decimalSeparator: 'dot',
},
};
});
diff --git a/app/helpers/number.js b/app/helpers/number.js
index 00727699..9a0756b5 100644
--- a/app/helpers/number.js
+++ b/app/helpers/number.js
@@ -1,4 +1,6 @@
const appConfig = require('electron').remote.require('electron-settings');
+import * as UIActions from '../actions/ui';
+import { createAction } from 'redux-actions';
function roundValue(value) {
// This variable my get value from setting in future version
@@ -9,19 +11,53 @@ function roundValue(value) {
function localeOrDefault(locale) {
let usedLocale = locale;
- if (!usedLocale) {
+ console.log('Config: ', appConfig.get('general.language'));
+ if (!usedLocale || usedLocale === undefined) {
usedLocale = appConfig.get('general.language') || 'en';
}
}
-function formatNumber(number, locale) {
- const usedLocale = localeOrDefault(locale);
- const fractions = appConfig.get('invoice.decimalFractions') || 2;
-
- return roundValue(number).toLocaleString(locale, {
+function replaceSeparators(formatted, decimalSeparator, thousandsSeparator) {
+ let formattedNumber = formatted;
+ if (decimalSeparator) {
+ formattedNumber = formattedNumber.replace(/\./, decimalSeparator);
+ }
+ if (thousandsSeparator) {
+ formattedNumber = formattedNumber.replace(/,/g, thousandsSeparator);
+ }
+
+ return formattedNumber;
+}
+
+function isNumber(n) {
+ return !isNaN(parseFloat(n)) && !isNaN(n - 0)
+}
+
+function formatNumber(number) {
+ // use en-us as locale to have the same separator and savely replace it
+ const usedLocale = 'en';
+ let fractions = appConfig.get('invoice.decimalFractions') || 2;
+ if (!isNumber(fractions) || fractions < 0) {
+ fractions = 2;
+ }
+ const decimalSeparatorConfig = appConfig.get('invoice.decimalSeparator') || 'dot';
+
+ let decimalSeparator = '.';
+ let replaceThousandsSeparator = false;
+
+ if (decimalSeparatorConfig === 'comma') {
+ decimalSeparator = ',';
+ replaceThousandsSeparator = true;
+ }
+
+ const formattedNumber = roundValue(number).toLocaleString(usedLocale, {
minimumFractionDigits: fractions,
maximumFractionDigits: fractions
});
+
+ const outputNumber = replaceSeparators(formattedNumber, decimalSeparator, replaceThousandsSeparator ? ',' : '');
+
+ return outputNumber;
}
export { formatNumber };
diff --git a/app/reducers/SettingsReducer.jsx b/app/reducers/SettingsReducer.jsx
index a3eb09d6..a97b9147 100644
--- a/app/reducers/SettingsReducer.jsx
+++ b/app/reducers/SettingsReducer.jsx
@@ -47,3 +47,8 @@ export const getCurrencyPlacement = createSelector(
getSettingsState,
settings => settings.saved.invoice.currencyPlacement
);
+
+export const getDecimalSeparator = createSelector(
+ getSettingsState,
+ settings => settings.saved.invoice.decimalSeparator
+);