mirror of
https://github.com/ascribe/onion.git
synced 2024-12-22 17:33:14 +01:00
localization
This commit is contained in:
parent
3183dae054
commit
95f6c08706
153
js/components/ascribe_forms/form.js
Normal file
153
js/components/ascribe_forms/form.js
Normal file
@ -0,0 +1,153 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import ReactAddons from 'react/addons';
|
||||
|
||||
import Button from 'react-bootstrap/lib/Button';
|
||||
|
||||
import requests from '../../utils/requests';
|
||||
import AlertDismissable from './alert';
|
||||
|
||||
let Form = React.createClass({
|
||||
propTypes: {
|
||||
url: React.PropTypes.string,
|
||||
handleSuccess: React.PropTypes.func,
|
||||
getFormData: React.PropTypes.func,
|
||||
children: React.PropTypes.oneOfType([
|
||||
React.PropTypes.object,
|
||||
React.PropTypes.array
|
||||
])
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
edited: false,
|
||||
submitted: false,
|
||||
errors: []
|
||||
};
|
||||
},
|
||||
reset(){
|
||||
for (let ref in this.refs){
|
||||
if (typeof this.refs[ref].reset === 'function'){
|
||||
this.refs[ref].reset();
|
||||
}
|
||||
}
|
||||
},
|
||||
submit(event){
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
this.setState({submitted: true});
|
||||
this.clearErrors();
|
||||
let action = (this.httpVerb && this.httpVerb()) || 'post';
|
||||
this[action]();
|
||||
},
|
||||
post(){
|
||||
requests
|
||||
.post(this.props.url, { body: this.getFormData() })
|
||||
.then(this.handleSuccess)
|
||||
.catch(this.handleError);
|
||||
},
|
||||
|
||||
getFormData(){
|
||||
if ('getFormData' in this.props){
|
||||
return this.props.getFormData();
|
||||
}
|
||||
let data = {};
|
||||
for (let ref in this.refs){
|
||||
data[this.refs[ref].props.name] = this.refs[ref].state.value;
|
||||
}
|
||||
return data;
|
||||
},
|
||||
|
||||
handleChangeChild(){
|
||||
this.setState({edited: true});
|
||||
},
|
||||
handleSuccess(response){
|
||||
if ('handleSuccess' in this.props){
|
||||
this.props.handleSuccess(response);
|
||||
}
|
||||
for (var ref in this.refs){
|
||||
if ('handleSuccess' in this.refs[ref]){
|
||||
this.refs[ref].handleSuccess();
|
||||
}
|
||||
}
|
||||
this.setState({edited: false, submitted: false});
|
||||
},
|
||||
handleError(err){
|
||||
if (err.json) {
|
||||
for (var input in err.json.errors){
|
||||
if (this.refs && this.refs[input] && this.refs[input].state) {
|
||||
this.refs[input].setErrors( err.json.errors[input]);
|
||||
} else {
|
||||
this.setState({errors: this.state.errors.concat(err.json.errors[input])});
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.setState({errors: ['Something went wrong, please try again later']});
|
||||
}
|
||||
this.setState({submitted: false});
|
||||
},
|
||||
clearErrors(){
|
||||
for (var ref in this.refs){
|
||||
if ('clearErrors' in this.refs[ref]){
|
||||
this.refs[ref].clearErrors();
|
||||
}
|
||||
}
|
||||
this.setState({errors: []});
|
||||
},
|
||||
getButtons() {
|
||||
if (this.state.submitted){
|
||||
return this.props.spinner;
|
||||
}
|
||||
if (this.props.buttons){
|
||||
return this.props.buttons;
|
||||
}
|
||||
let buttons = null;
|
||||
|
||||
if (this.state.edited){
|
||||
buttons = (
|
||||
<div className="row" style={{margin: 0}}>
|
||||
<p className="pull-right">
|
||||
<Button className="ascribe-btn" type="submit">Save</Button>
|
||||
<Button className="ascribe-btn" onClick={this.reset}>Cancel</Button>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
}
|
||||
return buttons;
|
||||
},
|
||||
getErrors() {
|
||||
let errors = null;
|
||||
if (this.state.errors.length > 0){
|
||||
errors = this.state.errors.map((error) => {
|
||||
return <AlertDismissable error={error} key={error}/>;
|
||||
});
|
||||
}
|
||||
return errors;
|
||||
},
|
||||
renderChildren() {
|
||||
return ReactAddons.Children.map(this.props.children, (child) => {
|
||||
return ReactAddons.addons.cloneWithProps(child, {
|
||||
handleChange: this.handleChangeChild,
|
||||
ref: child.props.name
|
||||
});
|
||||
});
|
||||
},
|
||||
render() {
|
||||
|
||||
return (
|
||||
<form role="form" className="ascribe-form" onSubmit={this.submit} autoComplete="on">
|
||||
{this.getErrors()}
|
||||
{this.renderChildren()}
|
||||
{this.getButtons()}
|
||||
</form>
|
||||
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
export default Form;
|
279
js/components/settings_container.js
Normal file
279
js/components/settings_container.js
Normal file
@ -0,0 +1,279 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import Router from 'react-router';
|
||||
|
||||
import UserActions from '../actions/user_actions';
|
||||
import UserStore from '../stores/user_store';
|
||||
|
||||
import WalletSettingsActions from '../actions/wallet_settings_actions';
|
||||
import WalletSettingsStore from '../stores/wallet_settings_store';
|
||||
|
||||
import ApplicationActions from '../actions/application_actions';
|
||||
import ApplicationStore from '../stores/application_store';
|
||||
|
||||
import GlobalNotificationModel from '../models/global_notification_model';
|
||||
import GlobalNotificationActions from '../actions/global_notification_actions';
|
||||
|
||||
import CollapsibleParagraph from './ascribe_collapsible/collapsible_paragraph';
|
||||
import Form from './ascribe_forms/form';
|
||||
import Property from './ascribe_forms/property';
|
||||
|
||||
import apiUrls from '../constants/api_urls';
|
||||
import AppConstants from '../constants/application_constants';
|
||||
|
||||
|
||||
let SettingsContainer = React.createClass({
|
||||
mixins: [Router.Navigation],
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<AccountSettings />
|
||||
<BitcoinWalletSettings />
|
||||
<APISettings />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
let AccountSettings = React.createClass({
|
||||
getInitialState() {
|
||||
return UserStore.getState();
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
UserStore.listen(this.onChange);
|
||||
UserActions.fetchCurrentUser();
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
UserStore.unlisten(this.onChange);
|
||||
},
|
||||
|
||||
onChange(state) {
|
||||
this.setState(state);
|
||||
},
|
||||
|
||||
handleSuccess(){
|
||||
UserActions.fetchCurrentUser();
|
||||
let notification = new GlobalNotificationModel('username succesfully updated', 'success', 5000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
},
|
||||
render() {
|
||||
let content = <img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />;
|
||||
if (this.state.currentUser.username) {
|
||||
content = (
|
||||
<Form
|
||||
url={apiUrls.users_username}
|
||||
handleSuccess={this.handleSuccess}>
|
||||
<Property
|
||||
name='username'
|
||||
label="Username">
|
||||
<input
|
||||
type="text"
|
||||
defaultValue={this.state.currentUser.username}
|
||||
placeholder="Enter your username"
|
||||
required/>
|
||||
</Property>
|
||||
<Property
|
||||
name='email'
|
||||
label="Email"
|
||||
editable={false}>
|
||||
<input
|
||||
type="text"
|
||||
defaultValue={this.state.currentUser.email}
|
||||
placeholder="Enter your username"
|
||||
required/>
|
||||
</Property>
|
||||
<hr />
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<CollapsibleParagraph
|
||||
title="Account"
|
||||
show={true}
|
||||
defaultExpanded={true}>
|
||||
{content}
|
||||
<Form
|
||||
url={AppConstants.serverUrl + 'api/users/set_language/'}>
|
||||
<Property
|
||||
name='language'
|
||||
label="Choose your Language"
|
||||
editable={true}>
|
||||
<select id="select-lang" name="language">
|
||||
<option value="fr">
|
||||
Français
|
||||
</option>
|
||||
<option value="en"
|
||||
selected="selected">
|
||||
English
|
||||
</option>
|
||||
</select>
|
||||
</Property>
|
||||
<hr />
|
||||
</Form>
|
||||
</CollapsibleParagraph>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
let BitcoinWalletSettings = React.createClass({
|
||||
|
||||
getInitialState() {
|
||||
return WalletSettingsStore.getState();
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
WalletSettingsStore.listen(this.onChange);
|
||||
WalletSettingsActions.fetchWalletSettings();
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
WalletSettingsStore.unlisten(this.onChange);
|
||||
},
|
||||
|
||||
onChange(state) {
|
||||
this.setState(state);
|
||||
},
|
||||
|
||||
render() {
|
||||
let content = <img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />;
|
||||
if (this.state.walletSettings.btc_public_key) {
|
||||
content = (
|
||||
<Form >
|
||||
<Property
|
||||
name='btc_public_key'
|
||||
label="Bitcoin public key"
|
||||
editable={false}>
|
||||
<pre className="ascribe-pre">{this.state.walletSettings.btc_public_key}</pre>
|
||||
</Property>
|
||||
<Property
|
||||
name='btc_root_address'
|
||||
label="Root Address"
|
||||
editable={false}>
|
||||
<pre className="ascribe-pre">{this.state.walletSettings.btc_root_address}</pre>
|
||||
</Property>
|
||||
<hr />
|
||||
</Form>);
|
||||
}
|
||||
return (
|
||||
<CollapsibleParagraph
|
||||
title="Crypto Wallet"
|
||||
show={true}
|
||||
defaultExpanded={true}>
|
||||
{content}
|
||||
</CollapsibleParagraph>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
let ContractSettings = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
currentUser: React.PropTypes.object
|
||||
},
|
||||
|
||||
render() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>Username: {this.props.currentUser.username}</div>
|
||||
<div>Email: {this.props.currentUser.email}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
let APISettings = React.createClass({
|
||||
getInitialState() {
|
||||
return ApplicationStore.getState();
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
ApplicationStore.listen(this.onChange);
|
||||
ApplicationActions.fetchApplication();
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
ApplicationStore.unlisten(this.onChange);
|
||||
},
|
||||
|
||||
onChange(state) {
|
||||
this.setState(state);
|
||||
},
|
||||
handleCreateSuccess: function(){
|
||||
ApplicationActions.fetchApplication();
|
||||
let notification = new GlobalNotificationModel('Application successfully created', 'success', 5000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
},
|
||||
|
||||
handleTokenRefresh: function(event){
|
||||
let applicationName = event.target.getAttribute('data-id');
|
||||
ApplicationActions.refreshApplicationToken(applicationName);
|
||||
let notification = new GlobalNotificationModel('Token refreshed', 'success', 2000);
|
||||
GlobalNotificationActions.appendGlobalNotification(notification);
|
||||
},
|
||||
render() {
|
||||
let content = <img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />;
|
||||
if (this.state.applications.length > -1) {
|
||||
content = this.state.applications.map(function(app) {
|
||||
return (
|
||||
<Property
|
||||
name={app.name}
|
||||
label={app.name}>
|
||||
<div className="row-same-height">
|
||||
<div className="col-xs-6 col-xs-height col-middle">
|
||||
{'Bearer ' + app.bearer_token.token}
|
||||
</div>
|
||||
<div className="col-xs-6 col-xs-height">
|
||||
<button
|
||||
className="pull-right btn btn-default btn-sm"
|
||||
onClick={this.handleTokenRefresh}
|
||||
data-id={app.name}>
|
||||
REFRESH
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Property>);
|
||||
}, this);
|
||||
content = (
|
||||
<div>
|
||||
<Form>
|
||||
{content}
|
||||
<hr />
|
||||
</Form>
|
||||
</div>);
|
||||
}
|
||||
return (
|
||||
<CollapsibleParagraph
|
||||
title="API Integration"
|
||||
show={true}
|
||||
defaultExpanded={true}>
|
||||
<Form
|
||||
url={apiUrls.applications}
|
||||
handleSuccess={this.handleCreateSuccess}>
|
||||
<Property
|
||||
name='name'
|
||||
label='Application Name'>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Enter the name of your app"
|
||||
required/>
|
||||
</Property>
|
||||
<hr />
|
||||
</Form>
|
||||
<pre>
|
||||
Usage: curl <url> -H 'Authorization: Bearer <token>'
|
||||
</pre>
|
||||
{content}
|
||||
</CollapsibleParagraph>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default SettingsContainer;
|
@ -7,7 +7,6 @@ import EditionContainer from './components/edition_container';
|
||||
|
||||
let Route = Router.Route;
|
||||
|
||||
|
||||
let routes = (
|
||||
<Route name="app" handler={AscribeApp}>
|
||||
<Route name="pieces" path="/" handler={PieceList}>
|
||||
|
123
sass/ascribe_settings.scss
Normal file
123
sass/ascribe_settings.scss
Normal file
@ -0,0 +1,123 @@
|
||||
|
||||
.ascribe-settings-wrapper {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
padding-bottom: 1em;
|
||||
|
||||
background-color: white;
|
||||
|
||||
border-left: 3px solid rgba(0,0,0,0);
|
||||
|
||||
&div:last-of-type {
|
||||
border-bottom: 1px solid rgba(0,0,0,.2);
|
||||
}
|
||||
&:hover{
|
||||
border-left: 3px solid rgba(2, 182, 163, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
.is-hidden{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.is-focused {
|
||||
background-color: rgba(2, 182, 163, 0.05);
|
||||
border-left: 3px solid rgba(2, 182, 163, 1)!important;
|
||||
}
|
||||
|
||||
.is-error {
|
||||
background-color: rgba(169, 68, 66, 0.05);
|
||||
border-left: 3px solid rgba(169, 68, 66, 1);
|
||||
div {
|
||||
span {
|
||||
color: rgba(169, 68, 66, 1);
|
||||
font-size: 0.9em;
|
||||
margin-right: 1em;
|
||||
}
|
||||
input, textarea {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover{
|
||||
border-left: 3px solid rgba(169, 68, 66, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.is-fixed {
|
||||
cursor: default;
|
||||
div {
|
||||
cursor: default;
|
||||
span {
|
||||
cursor: default;
|
||||
}
|
||||
input, div, pre, textarea {
|
||||
cursor: default;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.ascribe-settings-property {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
border-top: 1px solid rgba(0,0,0,.2);
|
||||
|
||||
padding-top: 1em;
|
||||
padding-left: 1.5em;
|
||||
|
||||
cursor:pointer;
|
||||
|
||||
input, div, span, pre, textarea, select {
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
span {
|
||||
font-weight: normal;
|
||||
font-size: 0.9em;
|
||||
color: rgba(0,0,0,.7);
|
||||
}
|
||||
|
||||
div {
|
||||
margin-top: 10px;
|
||||
div {
|
||||
padding-left: 0;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-weight: normal;
|
||||
font-size: 1.1em;
|
||||
cursor: default;
|
||||
color: rgba(0, 0, 0, .7);
|
||||
}
|
||||
}
|
||||
|
||||
input, pre, textarea, select {
|
||||
font-weight: 400;
|
||||
font-size: 1.1em;
|
||||
width:100%;
|
||||
margin-top: .5em;
|
||||
border: 0;
|
||||
background-color: rgba(0,0,0,0);
|
||||
color: #38BAAD;
|
||||
padding-left: 0;
|
||||
|
||||
&:focus {
|
||||
border:0;
|
||||
outline:0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
&::selection {
|
||||
color: white;
|
||||
background-color: rgba(0,0,0,1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
textarea{
|
||||
color: #666;
|
||||
margin-top: 1em;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user