Skip to content

Commit

Permalink
Inital code commit
Browse files Browse the repository at this point in the history
  • Loading branch information
overint committed Nov 17, 2017
1 parent f5f32b5 commit 81b6b0f
Show file tree
Hide file tree
Showing 24 changed files with 7,132 additions and 0 deletions.
22 changes: 22 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.

# dependencies
/node_modules

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
/.idea

npm-debug.log*
yarn-debug.log*
yarn-error.log*
22 changes: 22 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "ppcalc",
"version": "0.1.0",
"private": true,
"dependencies": {
"axios": "^0.16.2",
"react": "^15.6.1",
"react-dom": "^15.6.1",
"react-loading": "^0.1.4",
"react-select": "^1.0.0-rc.5"
},
"devDependencies": {
"react-scripts": "1.0.7"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"deploy": "npm run build && cd build && mv index.html 200.html && surge . --domain ppfeecalc.surge.sh"
}
}
Binary file added public/favicon.ico
Binary file not shown.
17 changes: 17 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<title>PayPal Fee Calculator</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
</body>
</html>
15 changes: 15 additions & 0 deletions public/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"short_name": "PayPalFeeCalc",
"name": "PayPal Fee Calculator",
"icons": [
{
"src": "favicon.ico",
"sizes": "192x192",
"type": "image/png"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
18 changes: 18 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';
import './css/App.css';
import './css/bootstrap/bootstrap.min.css';
import 'react-select/dist/react-select.css';

import Calculator from "./components/Calculator";

class App extends React.Component {
render() {
return (
<div className="container" style={{paddingTop: 100}}>
<Calculator/>
</div>
);
}
}

export default App;
8 changes: 8 additions & 0 deletions src/App.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
});
200 changes: 200 additions & 0 deletions src/components/Calculator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import React from 'react';

import Header from './Header';
import Options from "./Options";
import Results from "./Results";

import CurrencyConversion from "../http/CurrencyConversion"
import fees from '../data/fees'

// PayPal adds ~2.5% on top of the market exchange rates when converting currencies.
const INTL_FEE = 2.5;

class Calculator extends React.Component {
constructor(props) {
super(props);

this.state = {
amount: null,
results: null,
loading: false,
feeFixed: 0.30,
feePercent: 2.9,
customFee: false,
currencyFrom: "USD",
currencyTo: "USD",
rate: 1
};

this.updateAmount = this.updateAmount.bind(this);
this.updateFromCurrency = this.updateFromCurrency.bind(this);
this.updateToCurrency = this.updateToCurrency.bind(this);
this.updateCustomPercent = this.updateCustomPercent.bind(this);
this.updateSelectedCountry = this.updateSelectedCountry.bind(this);
}

askFee(amount, feePercent, feeFixed, needsConversion) {
feePercent = needsConversion ? feePercent + INTL_FEE : feePercent;
let r = amount * (feePercent / 100) + feeFixed;
let i = amount - r;
return {
fee: r.toFixed(2),
value: i.toFixed(2)
}
}

wantFee(amount, feePercent, feeFixed, needsConversion) {
feePercent = needsConversion ? feePercent + INTL_FEE : feePercent;
let r = (100 - feePercent) / 100;
let i = (amount + feeFixed) / r;
let s = i - amount;
return {
fee: s.toFixed(2),
value: i.toFixed(2)
}
}

calculateFees(amount) {
if (!amount) {
return;
}

let rate = this.state.rate;
let needsConversion = rate !== 1;


let fees = {
ask: this.askFee(amount, this.state.feePercent, this.state.feeFixed / rate, needsConversion),
want: this.wantFee(amount, this.state.feePercent, this.state.feeFixed, needsConversion)
};

if (rate !== 1) {
fees.ask.raw = fees.ask.fee;
fees.want.raw = (fees.want.fee / rate).toFixed(2);
fees.ask.value = (fees.ask.value * rate).toFixed(2);
fees.ask.fee = (fees.ask.fee * rate).toFixed(2);
fees.want.value = (fees.want.value / rate).toFixed(2);
}
return fees;
}

updateSelectedCountry(event) {
this.updateThenCalculate({
feePercent: event.value
});
this.setState({
customFee: false
});
}

updateFromCurrency(event) {
this.setState({
currencyFrom: event.value
});
this.refreshRates(event.value, this.state.currencyTo)
}

updateToCurrency(event) {
this.setState({
currencyTo: event.value,
feeFixed: fees.fixed[event.value]
});
this.refreshRates(this.state.currencyFrom, event.value)
}

refreshRates(from, to) {
this.setState({
loading: true
});

CurrencyConversion.rates(from, to).then((rate) => {
this.updateThenCalculate({
rate,
loading: false
});
});
}

updateThenCalculate(newState) {
new Promise((resolve) => {
resolve(this.setState(newState));
}).then(() => {
this.setState({
results: this.calculateFees(this.state.amount)
});
});
}

updateCustomPercent(event) {
let val = event.target.value ? event.target.value : 0;

if (val < 0 || val > 15)
{
this.setState({
feePercent: this.state.feePercent
});
return;
}

this.updateThenCalculate({
feePercent: parseFloat(val),
customFee: true
});
}

updateAmount(event) {
let amount = parseFloat(event.target.value);

if (!amount || amount <= 0) {
this.setState({
amount: null,
results: null
});
return;
}

this.setState({
amount,
results: this.calculateFees(amount)
});
}

render() {
return (
<div className="row">
<div className="col-md-8 col-md-offset-2">
<Header
updateAmount={this.updateAmount}
feePercent={this.state.feePercent}
feeFixed={this.state.feeFixed}
currencyTo={this.state.currencyTo}
fxFee={this.state.rate !== 1}
/>

<Options
updateSelectedCountry={this.updateSelectedCountry}
updateCustomPercent={this.updateCustomPercent}
updateFromCurrency={this.updateFromCurrency}
updateToCurrency={this.updateToCurrency}
feePercent={this.state.feePercent}
customFee={this.state.customFee}
feeFixed={this.state.feeFixed}
currencyTo={this.state.currencyTo}
currencyFrom={this.state.currencyFrom}
/>

<Results
currencyTo={this.state.currencyTo}
currencyFrom={this.state.currencyFrom}
results={this.state.results}
loading={this.state.loading}
amount={this.state.amount}
fxFee={this.state.rate !== 1}
/>
</div>
</div>
);
}
}

export default Calculator;
23 changes: 23 additions & 0 deletions src/components/Header.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';

function Header(props) {
return (
<div className="panel panel-default">
<div className="panel-heading text-center">
<h4>PayPal Fee Calculator</h4>
</div>
<div className="panel-body">
<div className="form-group col-md-6">
<input type="number" className="form-control" placeholder="Amount in $"
onChange={props.updateAmount}/>
</div>
<div className="form-group col-md-6">
<input type="number" className="form-control text-center" readOnly
placeholder={`${props.feePercent}% + ${props.fxFee ? '2.5% +': ''} ${props.feeFixed} ${props.currencyTo}`}/>
</div>
</div>
</div>
)
}

export default Header;
59 changes: 59 additions & 0 deletions src/components/Options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React from 'react';
import Select from 'react-select';

import fees from '../data/fees'
import currency from '../data/currency'

function Options(props) {
return (
<div className="panel panel-default">
<div className="panel-body">
<div>
<div className="row">
<div className="form-group col-md-7">
<label>Select your Country</label>
<Select
clearable={false}
placeholder={`Using a custom fee of ${props.feePercent}%`}
onChange={props.updateSelectedCountry}
value={props.feePercent}
options={fees.countries}
/>
</div>
<div className="col-md-1 text-center">
<br />
<h4><strong>Or</strong></h4>
</div>
<div className="form-group col-md-4">
<label>Custom Percentage Fee</label>
<input type="number" min="1" max="10" step ="0.01" className="form-control" placeholder="2.9%"
onChange={props.updateCustomPercent} value={props.customFee ? props.feePercent : ''} style={{height: 36}}/>
</div>
</div>
<div className="row">
<div className="form-group col-md-6">
<label>Currency From</label>
<Select
clearable={false}
onChange={props.updateFromCurrency}
value={props.currencyFrom}
options={currency.symbols}
/>
</div>
<div className="form-group col-md-6">
<label>Currency To</label>
<Select
clearable={false}
onChange={props.updateToCurrency}
value={props.currencyTo}
options={currency.symbols}
/>
</div>
</div>
</div>
</div>
</div>
)
}

export default Options;
Loading

0 comments on commit 81b6b0f

Please sign in to comment.