aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjloup <jeanloup.jamet@gmail.com>2018-05-07 17:39:58 +0200
committerjloup <jeanloup.jamet@gmail.com>2018-05-07 17:41:18 +0200
commit16e43cc77935a979c48e75f1ec8ed792952a4ae8 (patch)
tree3aae8d5ba819a791ef28e00d6536d38af5bbdf05
parentbf5deb87cef2b6f1370c808821dfb631d00590f6 (diff)
downloadFront-16e43cc77935a979c48e75f1ec8ed792952a4ae8.tar.gz
Front-16e43cc77935a979c48e75f1ec8ed792952a4ae8.tar.zst
Front-16e43cc77935a979c48e75f1ec8ed792952a4ae8.zip
Account page.
-rw-r--r--api/market_config.go10
-rw-r--r--cmd/app/main.go1
-rw-r--r--cmd/web/Makefile2
-rw-r--r--cmd/web/js/account.jsx177
-rw-r--r--cmd/web/js/header_footer.jsx14
-rw-r--r--cmd/web/js/main.jsx7
-rw-r--r--cmd/web/js/poloniex.jsx110
-rw-r--r--cmd/web/static/style.css20
8 files changed, 234 insertions, 107 deletions
diff --git a/api/market_config.go b/api/market_config.go
index 25e390d..c2248b3 100644
--- a/api/market_config.go
+++ b/api/market_config.go
@@ -45,6 +45,14 @@ func (q MarketConfigQuery) Run() (interface{}, *Error) {
45 45
46 } 46 }
47 47
48 if _, ok := config.Config["key"]; !ok {
49 config.Config["key"] = ""
50 }
51
52 if _, ok := config.Config["secret"]; !ok {
53 config.Config["secret"] = ""
54 }
55
48 return config.Config, nil 56 return config.Config, nil
49} 57}
50 58
@@ -76,7 +84,7 @@ func (q MarketBalanceQuery) Run() (interface{}, *Error) {
76 } 84 }
77 85
78 if config.Config["key"] == "" || config.Config["secret"] == "" { 86 if config.Config["key"] == "" || config.Config["secret"] == "" {
79 return nil, &Error{BadRequest, "your credentials for this market are not setup", fmt.Errorf("'%v' credentials are not setup", q.In.Market)} 87 return nil, &Error{InvalidMarketCredentials, "your credentials for this market are not setup", fmt.Errorf("'%v' credentials are not setup", q.In.Market)}
80 } 88 }
81 89
82 result := struct { 90 result := struct {
diff --git a/cmd/app/main.go b/cmd/app/main.go
index a0463d2..28eb775 100644
--- a/cmd/app/main.go
+++ b/cmd/app/main.go
@@ -143,6 +143,7 @@ func main() {
143 "/change-password", 143 "/change-password",
144 "/signout", 144 "/signout",
145 "/me", 145 "/me",
146 "/account",
146 "/otp/enroll", 147 "/otp/enroll",
147 "/otp/validate", 148 "/otp/validate",
148 "/not_confirmed", 149 "/not_confirmed",
diff --git a/cmd/web/Makefile b/cmd/web/Makefile
index 02ff826..3b100d1 100644
--- a/cmd/web/Makefile
+++ b/cmd/web/Makefile
@@ -4,7 +4,7 @@ export PATH := $(PATH):./node_modules/.bin
4 4
5SRC_DIR=js 5SRC_DIR=js
6BUILD_DIR=build/js 6BUILD_DIR=build/js
7JSX_SRC= header_footer.jsx main.jsx signup.jsx signin.jsx otp.jsx poloniex.jsx password_reset.jsx change_password.jsx 7JSX_SRC= header_footer.jsx main.jsx signup.jsx signin.jsx otp.jsx poloniex.jsx password_reset.jsx change_password.jsx account.jsx
8JS_SRC= cookies.js app.js api.js 8JS_SRC= cookies.js app.js api.js
9STATIC_FILES= index.html style.css fontello.css 9STATIC_FILES= index.html style.css fontello.css
10STATIC_FILES+=$(addprefix fonts/, fontello.eot fontello.svg fontello.ttf fontello.woff fontello.woff2) 10STATIC_FILES+=$(addprefix fonts/, fontello.eot fontello.svg fontello.ttf fontello.woff fontello.woff2)
diff --git a/cmd/web/js/account.jsx b/cmd/web/js/account.jsx
new file mode 100644
index 0000000..43e7083
--- /dev/null
+++ b/cmd/web/js/account.jsx
@@ -0,0 +1,177 @@
1import Api from './api.js';
2import React from 'react';
3
4class PoloniexConfiguration extends React.Component {
5 constructor(props) {
6 super(props);
7 this.state = {'apiKey': '', 'apiSecret': '', 'apiRequested': false, 'status': 'loading'};
8 }
9
10 checkCredentials = () => {
11 Api.Call('MARKET_BALANCE', {'name': 'poloniex', 'currency': 'BTC'}, function(err, status, data) {
12 if (err) {
13 console.error(err, data);
14 if (err.code === 'invalid_market_credentials') {
15 this.setState({'status': 'invalidCredentials'});
16 } else if (err.code === 'ip_restricted_api_key') {
17 this.setState({'status': 'ipRestricted'});
18 }
19 return;
20 }
21
22 this.setState({'status': 'ok'});
23 }.bind(this));
24 }
25
26 handleCredentialsChange = (key, secret) => {
27 this.setState({'apiKey': key, 'apiSecret': secret});
28 }
29
30 handleCredentialsSubmit = () => {
31
32 /*
33 *If (!this.state.apiKey || !this.state.apiSecret) {
34 * return;
35 *}
36 */
37
38 Api.Call('UPDATE_MARKET', {'key': this.state.apiKey, 'secret': this.state.apiSecret, 'name': 'poloniex'}, function(err, status, data) {
39 if (err) {
40 console.error(err, data);
41 return;
42 }
43
44 this.setState({'status': 'loading'});
45 this.checkCredentials();
46 }.bind(this));
47 }
48
49 componentDidMount = () => {
50 Api.Call('MARKET', {'name': 'poloniex'}, function(err, status, data) {
51 this.setState({'apiRequested': true});
52 if (err) {
53 console.error(err, data);
54 return;
55 }
56
57 var newStatus = this.state.status;
58 if (!data.key || !data.secret) {
59 newStatus = 'emptyCredentials';
60 } else {
61 this.checkCredentials();
62 }
63
64 this.setState({'apiKey': data.key, 'apiSecret': data.secret, 'status': newStatus});
65 }.bind(this));
66 }
67
68 render = () => {
69 var displayText = null;
70 switch (this.state.status) {
71 case 'loading':
72 displayText = 'Checking Poloniex credentials...';
73 break;
74 case 'invalidCredentials':
75 displayText = 'Invalid poloniex credentials';
76 break;
77 case 'ipRestricted':
78 displayText = 'Your API key is IP restricted.';
79 break;
80 case 'emptyCredentials':
81 displayText = 'Please provide poloniex credentials';
82 break;
83 case 'ok':
84 displayText = 'You are all set !';
85 break;
86 default:
87 console.error('unknown status', this.state.status);
88 displayText = null;
89 }
90 if (this.state.apiRequested === false) {
91 return <div></div>;
92 }
93 return (
94 <div>
95 <PoloniexCredentialsForm onLoadCredentials={this.onLoadCredentials}
96 onCredentialsSubmit={this.handleCredentialsSubmit}
97 onCredentialsChange={this.handleCredentialsChange}
98 apiSecret={this.state.apiSecret}
99 apiKey={this.state.apiKey}
100 status={this.state.status}
101 statusMessage={displayText}/>
102 </div>
103 );
104 }
105}
106
107class PoloniexCredentialsForm extends React.Component {
108 constructor(props) {
109 super(props);
110 this.state = {'editMode': false};
111 }
112
113 handleSubmit = (e) => {
114 this.props.onCredentialsSubmit();
115 this.setState({'editMode': false});
116 e.preventDefault();
117 }
118
119 handleApiKeyChange = (event) => {
120 this.props.onCredentialsChange(event.target.value, this.props.apiSecret);
121 }
122
123 handleApiSecretChange = (event) => {
124 this.props.onCredentialsChange(this.props.apiKey, event.target.value);
125 }
126
127 onEditClick = () => {
128 this.setState({'editMode': true});
129 }
130
131 render = () => {
132 var submitType = this.state.editMode === true ? 'submit' : 'hidden';
133 var buttonDisplay = this.state.editMode === true ? 'none' : 'inline';
134 var secretDisplayed = this.state.editMode === true ? this.props.apiSecret : 'XXXXXXX';
135 var keyDisplayed = this.state.editMode === true ? this.props.apiKey : 'XXXXXXX';
136
137 var iconName = 'icon-cancel-circled';
138 switch (this.props.status) {
139 case 'loading':
140 iconName = 'icon-hourglass-2';
141 break;
142 case 'ok':
143 iconName = 'icon-ok-circled';
144 break;
145 }
146
147 return (
148 <div className="row api-credentials-form">
149 <div className="offset-2 col-8 box">
150 <span className="text-center">Poloniex credentials</span>
151 <hr/>
152 <div className="row config-status">
153 <div className="col-12">
154 <span><i className={iconName}></i>{this.props.statusMessage}</span>
155 </div>
156 </div>
157 <div className="row">
158 <div className="col-12">
159 <form role="form" onSubmit={this.handleSubmit}>
160 <label className="w-100">Key:
161 <input className="form-control" type="text" placeholder="key" value={keyDisplayed} onChange={this.handleApiKeyChange} disabled={!this.state.editMode}/>
162 </label>
163 <label className="w-100">Secret:
164 <input className="form-control" type="text" placeholder="secret" value={secretDisplayed} onChange={this.handleApiSecretChange} disabled={!this.state.editMode}/>
165 </label>
166 <input className="form-control submit" type={submitType} value="Save" />
167 <button className="form-control submit" style={{display: buttonDisplay}} onSubmit={null} onClick={this.onEditClick} type="button">Show/Edit</button>
168 </form>
169 </div>
170 </div>
171 </div>
172 </div>
173 );
174 }
175}
176
177export default PoloniexConfiguration;
diff --git a/cmd/web/js/header_footer.jsx b/cmd/web/js/header_footer.jsx
index 3cb9937..f11ed06 100644
--- a/cmd/web/js/header_footer.jsx
+++ b/cmd/web/js/header_footer.jsx
@@ -6,6 +6,12 @@ class Signout extends React.Component {
6 } 6 }
7} 7}
8 8
9class MyAccount extends React.Component {
10 render = () => {
11 return <a href="/account"><u>Account</u></a>;
12 }
13}
14
9class Logo extends React.Component { 15class Logo extends React.Component {
10 render() { 16 render() {
11 return <div id="logo" className="w-100 h-100 align-self-center"> 17 return <div id="logo" className="w-100 h-100 align-self-center">
@@ -22,6 +28,7 @@ class Footer extends React.Component {
22 } 28 }
23 29
24 if (this.props.isLoggedIn === true) { 30 if (this.props.isLoggedIn === true) {
31 elements = elements.concat(<MyAccount />);
25 elements = elements.concat(<Signout />); 32 elements = elements.concat(<Signout />);
26 } 33 }
27 34
@@ -37,7 +44,7 @@ class Footer extends React.Component {
37 </div>; 44 </div>;
38 }); 45 });
39 return <div id="footer" className="row"> 46 return <div id="footer" className="row">
40 <div className="offset-4 col-4 d-sm-none"> 47 <div className="offset-4 col-4 d-md-none">
41 {rows} 48 {rows}
42 </div> 49 </div>
43 </div>; 50 </div>;
@@ -52,11 +59,12 @@ class Header extends React.Component {
52 } 59 }
53 60
54 if (this.props.isLoggedIn === true) { 61 if (this.props.isLoggedIn === true) {
62 elements = elements.concat(<MyAccount />);
55 elements = elements.concat(<Signout />); 63 elements = elements.concat(<Signout />);
56 } 64 }
57 65
58 elements = elements.map(function(element, i) { 66 elements = elements.map(function(element, i) {
59 return <div className="text-center" key={'header-el-' + i}> 67 return <div className="text-center header-menu-section" key={'header-el-' + i}>
60 {element} 68 {element}
61 </div>; 69 </div>;
62 }); 70 });
@@ -65,7 +73,7 @@ class Header extends React.Component {
65 <div className="offset-3 col-6 offset-md-4 col-md-4"> 73 <div className="offset-3 col-6 offset-md-4 col-md-4">
66 <Logo /> 74 <Logo />
67 </div> 75 </div>
68 <div className="col-3 col-md-4 align-self-center h-100 d-none d-sm-flex justify-content-end"> 76 <div className="col-3 col-md-4 align-self-center h-100 d-none d-md-flex justify-content-end">
69 {elements} 77 {elements}
70 </div> 78 </div>
71 </div>; 79 </div>;
diff --git a/cmd/web/js/main.jsx b/cmd/web/js/main.jsx
index 8a4fce8..84b5848 100644
--- a/cmd/web/js/main.jsx
+++ b/cmd/web/js/main.jsx
@@ -4,6 +4,7 @@ import PasswordResetForm from './password_reset.js';
4import ChangePasswordForm from './change_password.js'; 4import ChangePasswordForm from './change_password.js';
5import OtpEnrollForm from './otp.js'; 5import OtpEnrollForm from './otp.js';
6import PoloniexController from './poloniex.js'; 6import PoloniexController from './poloniex.js';
7import PoloniexConfiguration from './account.js';
7import App from './app.js'; 8import App from './app.js';
8import Api from './api.js'; 9import Api from './api.js';
9import cookies from './cookies.js'; 10import cookies from './cookies.js';
@@ -73,6 +74,12 @@ App.page('/me', true, function(context) {
73 </div>); 74 </div>);
74}); 75});
75 76
77App.page('/account', true, function(context) {
78 App.mount(<div>
79 <PoloniexConfiguration/>
80 </div>);
81});
82
76App.page('/not_confirmed', true, function(context) { 83App.page('/not_confirmed', true, function(context) {
77 App.mount(<div> 84 App.mount(<div>
78 <div className="row"> 85 <div className="row">
diff --git a/cmd/web/js/poloniex.jsx b/cmd/web/js/poloniex.jsx
index db6b1c4..76b68d8 100644
--- a/cmd/web/js/poloniex.jsx
+++ b/cmd/web/js/poloniex.jsx
@@ -4,26 +4,7 @@ import React from 'react';
4class PoloniexController extends React.Component { 4class PoloniexController extends React.Component {
5 constructor(props) { 5 constructor(props) {
6 super(props); 6 super(props);
7 this.state = {'apiKey': '', 'apiSecret': '', 'apiRequested': false, 'flag': 'loading', 'valueCurrency': null, 'balanceValue': null, 'balance': null}; 7 this.state = {'apiRequested': false, 'flag': 'loading', 'valueCurrency': null, 'balanceValue': null, 'balance': null};
8 }
9
10 handleCredentialsChange = (key, secret) => {
11 this.setState({'apiKey': key, 'apiSecret': secret});
12 }
13
14 handleCredentialsSubmit = () => {
15 if (!this.state.apiKey || !this.state.apiSecret) {
16 return;
17 }
18 Api.Call('UPDATE_MARKET', {'key': this.state.apiKey, 'secret': this.state.apiSecret, 'name': 'poloniex'}, function(err, status, data) {
19 if (err) {
20 console.error(err, data);
21 return;
22 }
23
24 this.setState({'flag': 'loading', 'valueCurrency': null, 'balanceValue': null, 'balance': null});
25 this.loadBalance();
26 }.bind(this));
27 } 8 }
28 9
29 loadBalance = () => { 10 loadBalance = () => {
@@ -31,34 +12,19 @@ class PoloniexController extends React.Component {
31 if (err) { 12 if (err) {
32 console.error(err, data); 13 console.error(err, data);
33 if (err.code === 'invalid_market_credentials') { 14 if (err.code === 'invalid_market_credentials') {
34 this.setState({'flag': 'invalidCredentials', 'valueCurrency': null, 'balanceValue': null, 'balance': null}); 15 this.setState({'flag': 'invalidCredentials', 'apiRequested': true, 'valueCurrency': null, 'balanceValue': null, 'balance': null});
35 } else if (err.code === 'ip_restricted_api_key') { 16 } else if (err.code === 'ip_restricted_api_key') {
36 this.setState({'flag': 'ipRestricted', 'valueCurrency': null, 'balanceValue': null, 'balance': null}); 17 this.setState({'flag': 'ipRestricted', 'apiRequested': true, 'valueCurrency': null, 'balanceValue': null, 'balance': null});
37 } 18 }
38 return; 19 return;
39 } 20 }
40 21
41 this.setState({'flag': 'ok', 'valueCurrency': data.valueCurrency, 'balanceValue': data.value, 'balance': data.balance}); 22 this.setState({'flag': 'ok', 'apiRequested': true, 'valueCurrency': data.valueCurrency, 'balanceValue': data.value, 'balance': data.balance});
42 }.bind(this)); 23 }.bind(this));
43 } 24 }
44 25
45 componentDidMount = () => { 26 componentDidMount = () => {
46 Api.Call('MARKET', {'name': 'poloniex'}, function(err, status, data) { 27 this.loadBalance();
47 this.setState({'apiRequested': true});
48 if (err) {
49 console.error(err, data);
50 return;
51 }
52
53 var flag = this.state.flag;
54 if (!data.key || !data.secret) {
55 flag = 'emptyCredentials';
56 } else {
57 this.loadBalance();
58 }
59
60 this.setState({'apiKey': data.key, 'apiSecret': data.secret, 'flag': flag});
61 }.bind(this));
62 } 28 }
63 29
64 render = () => { 30 render = () => {
@@ -68,13 +34,9 @@ class PoloniexController extends React.Component {
68 displayText = 'Loading data from poloniex...'; 34 displayText = 'Loading data from poloniex...';
69 break; 35 break;
70 case 'invalidCredentials': 36 case 'invalidCredentials':
71 displayText = 'Invalid poloniex credentials';
72 break;
73 case 'ipRestricted': 37 case 'ipRestricted':
74 displayText = 'Your API key is IP restricted. Please whitelist us.';
75 break;
76 case 'emptyCredentials': 38 case 'emptyCredentials':
77 displayText = 'Please provide poloniex credentials'; 39 displayText = <div>Please provide poloniex credentials in <a href="/account">Account</a> page.</div>;
78 break; 40 break;
79 default: 41 default:
80 displayText = null; 42 displayText = null;
@@ -88,11 +50,6 @@ class PoloniexController extends React.Component {
88 balanceValue={this.state.balanceValue} 50 balanceValue={this.state.balanceValue}
89 balance={this.state.balance} 51 balance={this.state.balance}
90 displayText={displayText}/> 52 displayText={displayText}/>
91 <PoloniexCredentialsForm onLoadCredentials={this.onLoadCredentials}
92 onCredentialsSubmit={this.handleCredentialsSubmit}
93 onCredentialsChange={this.handleCredentialsChange}
94 apiSecret={this.state.apiSecret}
95 apiKey={this.state.apiKey}/>
96 </div> 53 </div>
97 ); 54 );
98 } 55 }
@@ -141,14 +98,14 @@ class PoloniexBalance extends React.Component {
141 </div> 98 </div>
142 </div> 99 </div>
143 </div>; 100 </div>;
144} else { 101 } else {
145 dashboard = 102 dashboard =
146 <div className="row"> 103 <div className="row">
147 <div className="col-12 text-center"> 104 <div className="col-12 text-center">
148 <span>{this.props.displayText}</span> 105 <span>{this.props.displayText}</span>
149 </div> 106 </div>
150 </div>; 107 </div>;
151} 108 }
152 109
153 return ( 110 return (
154 <div className="row"> 111 <div className="row">
@@ -164,55 +121,4 @@ class PoloniexBalance extends React.Component {
164 } 121 }
165} 122}
166 123
167class PoloniexCredentialsForm extends React.Component {
168 constructor(props) {
169 super(props);
170 this.state = {'hideMsg': true, 'msg': '', 'editMode': false, 'msgOk': false};
171 }
172
173 handleSubmit = (e) => {
174 this.props.onCredentialsSubmit();
175 this.setState({'editMode': false});
176 e.preventDefault();
177 }
178
179 handleApiKeyChange = (event) => {
180 this.props.onCredentialsChange(event.target.value, this.props.apiSecret);
181 }
182
183 handleApiSecretChange = (event) => {
184 this.props.onCredentialsChange(this.props.apiKey, event.target.value);
185 }
186
187 onEditClick = () => {
188 this.setState({'editMode': true});
189 }
190
191 render = () => {
192 var submitType = this.state.editMode === true ? 'submit' : 'hidden';
193 var buttonDisplay = this.state.editMode === true ? 'none' : 'inline';
194 var secretDisplayed = this.state.editMode === true ? this.props.apiSecret : 'XXXXXXX';
195 var keyDisplayed = this.state.editMode === true ? this.props.apiKey : 'XXXXXXX';
196
197 return (
198 <div className="row api-credentials-form">
199 <div className="offset-2 col-8 box">
200 <span className="text-center">Poloniex credentials</span>
201 <hr/>
202 <form role="form" onSubmit={this.handleSubmit}>
203 <label className="w-100">Key:
204 <input className="form-control" type="text" placeholder="key" value={keyDisplayed} onChange={this.handleApiKeyChange} disabled={!this.state.editMode}/>
205 </label>
206 <label className="w-100">Secret:
207 <input className="form-control" type="text" placeholder="secret" value={secretDisplayed} onChange={this.handleApiSecretChange} disabled={!this.state.editMode}/>
208 </label>
209 <input className="form-control submit" type={submitType} value="Save" />
210 <button className="form-control submit" style={{display: buttonDisplay}} onSubmit={null} onClick={this.onEditClick} type="button">Show/Edit</button>
211 </form>
212 </div>
213 </div>
214 );
215 }
216}
217
218export default PoloniexController; 124export default PoloniexController;
diff --git a/cmd/web/static/style.css b/cmd/web/static/style.css
index 5e49c55..2212168 100644
--- a/cmd/web/static/style.css
+++ b/cmd/web/static/style.css
@@ -44,6 +44,10 @@ i.cc {
44 margin-bottom: 30px; 44 margin-bottom: 30px;
45} 45}
46 46
47.header-menu-section {
48 margin-right: 10px;
49}
50
47h1 { 51h1 {
48 font-size: 1.5em; 52 font-size: 1.5em;
49 margin-bottom: 5px; 53 margin-bottom: 5px;
@@ -57,6 +61,22 @@ h1 {
57 background-color: white; 61 background-color: white;
58} 62}
59 63
64.config-status {
65 margin-bottom: 10px;
66 font-size: 0.9em;
67}
68
69.config-status .icon-cancel-circled {
70 color: red;
71}
72
73.config-status .icon-ok-circled {
74 color: green;
75}
76.config-status i {
77 font-size: 1.2em;
78}
79
60.sign-in .submit { 80.sign-in .submit {
61 background-color: rgb(20, 20, 20); 81 background-color: rgb(20, 20, 20);
62 color: white; 82 color: white;