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 Route = Router.Route;
|
||||||
|
|
||||||
|
|
||||||
let routes = (
|
let routes = (
|
||||||
<Route name="app" handler={AscribeApp}>
|
<Route name="app" handler={AscribeApp}>
|
||||||
<Route name="pieces" path="/" handler={PieceList}>
|
<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