mirror of
https://github.com/ascribe/onion.git
synced 2024-12-23 01:39:36 +01:00
form framework
- inputtext, area, btn - alertmixin - formmixin - 500 - transfer/share
This commit is contained in:
parent
0a4dae850b
commit
c3ea9aecbe
31
js/components/ascribe_forms/alert.js
Normal file
31
js/components/ascribe_forms/alert.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Alert from 'react-bootstrap/lib/Alert';
|
||||||
|
|
||||||
|
let AlertDismissable = React.createClass({
|
||||||
|
getInitialState() {
|
||||||
|
return {
|
||||||
|
alertVisible: true
|
||||||
|
};
|
||||||
|
},
|
||||||
|
show() {
|
||||||
|
this.setState({alertVisible: true});
|
||||||
|
},
|
||||||
|
hide() {
|
||||||
|
this.setState({alertVisible: false});
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
if (this.state.alertVisible) {
|
||||||
|
let key = this.props.error;
|
||||||
|
return (
|
||||||
|
<Alert bsStyle='danger' onDismiss={this.hide}>
|
||||||
|
{this.props.error}
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<span />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default AlertDismissable;
|
21
js/components/ascribe_forms/button_submit_close.js
Normal file
21
js/components/ascribe_forms/button_submit_close.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
let ButtonSubmitOrClose = React.createClass({
|
||||||
|
render() {
|
||||||
|
if (this.props.submitted){
|
||||||
|
return (
|
||||||
|
<div className="modal-footer">
|
||||||
|
Loading
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="modal-footer">
|
||||||
|
<button type="submit" className="btn btn-ascribe-inv">{this.props.text}</button>
|
||||||
|
<button className="btn btn-ascribe-inv" onClick={this.props.onClose}>CLOSE</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default ButtonSubmitOrClose;
|
@ -2,53 +2,49 @@ import fetch from 'isomorphic-fetch';
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import AppConstants from '../../constants/application_constants';
|
import ApiUrls from '../../constants/api_urls';
|
||||||
import FetchApiUtils from '../../utils/fetch_api_utils';
|
import FormMixin from '../../mixins/form_mixin';
|
||||||
import FormMixin from '../../mixins/form_alert_mixin';
|
import InputText from './input_text';
|
||||||
|
import InputTextArea from './input_textarea';
|
||||||
|
import ButtonSubmitOrClose from './button_submit_close';
|
||||||
|
|
||||||
let ShareForm = React.createClass({
|
let ShareForm = React.createClass({
|
||||||
mixins: [FormMixin],
|
mixins: [FormMixin],
|
||||||
|
|
||||||
submit(e) {
|
url() {
|
||||||
e.preventDefault();
|
return ApiUrls.ownership_shares_mail
|
||||||
fetch(AppConstants.baseUrl + 'ownership/shares/mail/', {
|
|
||||||
method: 'post',
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Basic ' + AppConstants.debugCredentialBase64,
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify(this.getFormData())
|
|
||||||
})
|
|
||||||
.then(
|
|
||||||
(response) => this.handleResponse(response)
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
getFormData() {
|
getFormData() {
|
||||||
return {
|
return {
|
||||||
bitcoin_id: this.props.edition.bitcoin_id,
|
bitcoin_id: this.props.edition.bitcoin_id,
|
||||||
share_emails: this.refs.share_emails.getDOMNode().value,
|
share_emails: this.refs.share_emails.state.value,
|
||||||
share_message: this.refs.share_message.getDOMNode().value
|
share_message: this.refs.share_message.state.value
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
renderMessage() {
|
renderForm() {
|
||||||
return "" +
|
let message = "Hi,\n" +
|
||||||
"Hi,\n" +
|
|
||||||
"\n" +
|
"\n" +
|
||||||
"I am sharing \"" + this.props.edition.title + "\" with you.\n" +
|
"I am sharing \"" + this.props.edition.title + "\" with you.\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"Truly yours,\n" +
|
"Truly yours,\n" +
|
||||||
this.props.currentUser.username;
|
this.props.currentUser.username;
|
||||||
},
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<form id="share_modal_content" role="form" onSubmit={this.submit}>
|
<form id="share_modal_content" role="form" key="share_modal_content" onSubmit={this.submit}>
|
||||||
{this.renderTextInput("share_emails", "email", "Comma separated emails", "required")}
|
<InputText
|
||||||
{this.renderTextArea("share_message", this.renderMessage(), "")}
|
ref="share_emails"
|
||||||
<div className="modal-footer">
|
placeHolder="Comma separated emails"
|
||||||
<button type="submit" className="btn btn-ascribe-inv">SHARE</button>
|
required="required"
|
||||||
<button className="btn btn-ascribe-inv" onClick={this.props.onRequestHide}>CLOSE</button>
|
type="text"
|
||||||
</div>
|
submitted={this.state.submitted}/>
|
||||||
|
<InputTextArea
|
||||||
|
ref="share_message"
|
||||||
|
defaultValue={message}
|
||||||
|
required=""
|
||||||
|
/>
|
||||||
|
<ButtonSubmitOrClose
|
||||||
|
text="SHARE"
|
||||||
|
onClose={this.props.onRequestHide}
|
||||||
|
submitted={this.state.submitted} />
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
68
js/components/ascribe_forms/form_transfer.js
Normal file
68
js/components/ascribe_forms/form_transfer.js
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import fetch from 'isomorphic-fetch';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import ApiUrls from '../../constants/api_urls';
|
||||||
|
import FormMixin from '../../mixins/form_mixin';
|
||||||
|
import InputText from './input_text';
|
||||||
|
import InputTextArea from './input_textarea';
|
||||||
|
import ButtonSubmitOrClose from './button_submit_close';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let TransferForm = React.createClass({
|
||||||
|
mixins: [FormMixin],
|
||||||
|
|
||||||
|
url() {
|
||||||
|
return ApiUrls.ownership_transfers
|
||||||
|
},
|
||||||
|
getFormData() {
|
||||||
|
return {
|
||||||
|
bitcoin_id: this.props.edition.bitcoin_id,
|
||||||
|
transferee: this.refs.transferee.state.value,
|
||||||
|
transfer_message: this.refs.transfer_message.state.value,
|
||||||
|
password: this.refs.password.state.value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
renderForm() {
|
||||||
|
let message = "Hi,\n" +
|
||||||
|
"\n" +
|
||||||
|
"I transfer ownership of \"" + this.props.edition.title + "\" to you.\n" +
|
||||||
|
"\n" +
|
||||||
|
"Truly yours,\n" +
|
||||||
|
this.props.currentUser.username;
|
||||||
|
return (
|
||||||
|
<form id="transfer_modal_content" role="form" onSubmit={this.submit}>
|
||||||
|
<input className="invisible" type="email" name="fake_transferee"/>
|
||||||
|
<input className="invisible" type="password" name="fake_password"/>
|
||||||
|
<InputText
|
||||||
|
ref="transferee"
|
||||||
|
placeHolder="Transferee email"
|
||||||
|
required="required"
|
||||||
|
type="email"
|
||||||
|
submitted={this.state.submitted}/>
|
||||||
|
<InputTextArea
|
||||||
|
ref="transfer_message"
|
||||||
|
defaultValue={message}
|
||||||
|
required=""
|
||||||
|
/>
|
||||||
|
<InputText
|
||||||
|
ref="password"
|
||||||
|
placeHolder="Password"
|
||||||
|
required="required"
|
||||||
|
type="password"
|
||||||
|
submitted={this.state.submitted}/>
|
||||||
|
<div>
|
||||||
|
Make sure that display instructions and technology details are correct.
|
||||||
|
They cannot be edited after the transfer.
|
||||||
|
</div>
|
||||||
|
<ButtonSubmitOrClose
|
||||||
|
text="TRANSFER"
|
||||||
|
onClose={this.props.onRequestHide}
|
||||||
|
submitted={this.state.submitted} />
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default TransferForm;
|
35
js/components/ascribe_forms/input_text.js
Normal file
35
js/components/ascribe_forms/input_text.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import AlertMixin from '../../mixins/alert_mixin'
|
||||||
|
|
||||||
|
let InputText = React.createClass({
|
||||||
|
|
||||||
|
mixins : [AlertMixin],
|
||||||
|
|
||||||
|
getInitialState() {
|
||||||
|
return {value: null,
|
||||||
|
alerts: null, // needed in AlertMixin
|
||||||
|
retry: 0 // needed in AlertMixin for generating unique alerts
|
||||||
|
};
|
||||||
|
},
|
||||||
|
handleChange(event) {
|
||||||
|
this.setState({value: event.target.value});
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
let className = "form-control input-text-ascribe";
|
||||||
|
let alerts = (this.props.submitted) ? null : this.state.alerts;
|
||||||
|
return (
|
||||||
|
<div className="form-group">
|
||||||
|
{alerts}
|
||||||
|
<input className={className}
|
||||||
|
placeholder={this.props.placeHolder}
|
||||||
|
required={this.props.required}
|
||||||
|
type={this.props.type}
|
||||||
|
onChange={this.handleChange}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default InputText;
|
35
js/components/ascribe_forms/input_textarea.js
Normal file
35
js/components/ascribe_forms/input_textarea.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import AlertMixin from '../../mixins/alert_mixin'
|
||||||
|
|
||||||
|
let InputTextArea = React.createClass({
|
||||||
|
|
||||||
|
mixins : [AlertMixin],
|
||||||
|
|
||||||
|
getInitialState() {
|
||||||
|
return {value: this.props.defaultValue,
|
||||||
|
alerts: null, // needed in AlertMixin
|
||||||
|
retry: 0 // needed in AlertMixin for generating unique alerts
|
||||||
|
};
|
||||||
|
},
|
||||||
|
handleChange(event) {
|
||||||
|
this.setState({value: event.target.value});
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
let className = "form-control input-text-ascribe textarea-ascribe-message";
|
||||||
|
|
||||||
|
let alerts = (this.props.submitted) ? null : this.state.alerts;
|
||||||
|
return (
|
||||||
|
<div className="form-group">
|
||||||
|
{alerts}
|
||||||
|
<textarea className={className}
|
||||||
|
defaultValue={this.props.defaultValue}
|
||||||
|
required={this.props.required}
|
||||||
|
onChange={this.handleChange}></textarea>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default InputTextArea;
|
@ -25,6 +25,7 @@ let ShareModalButton = React.createClass({
|
|||||||
|
|
||||||
let ShareModal = React.createClass({
|
let ShareModal = React.createClass({
|
||||||
onRequestHide(e){
|
onRequestHide(e){
|
||||||
|
if (e)
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.props.onRequestHide();
|
this.props.onRequestHide();
|
||||||
},
|
},
|
||||||
|
45
js/components/ascribe_modal/modal_transfer.js
Normal file
45
js/components/ascribe_modal/modal_transfer.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Modal from 'react-bootstrap/lib/Modal';
|
||||||
|
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
|
||||||
|
import ModalTrigger from 'react-bootstrap/lib/ModalTrigger';
|
||||||
|
import Tooltip from 'react-bootstrap/lib/Tooltip';
|
||||||
|
|
||||||
|
import TransferForm from '../ascribe_forms/form_transfer'
|
||||||
|
|
||||||
|
|
||||||
|
let TransferModalButton = React.createClass({
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<OverlayTrigger delay={500} placement="left"
|
||||||
|
overlay={<Tooltip>Transfer the ownership of the artwork</Tooltip>}>
|
||||||
|
<ModalTrigger modal={<TransferModal edition={this.props.edition}
|
||||||
|
currentUser={this.props.currentUser}/>}>
|
||||||
|
<div className="btn btn-ascribe-inv">
|
||||||
|
TRANSFER
|
||||||
|
</div>
|
||||||
|
</ModalTrigger>
|
||||||
|
</OverlayTrigger>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let TransferModal = React.createClass({
|
||||||
|
onRequestHide(e){
|
||||||
|
e.preventDefault();
|
||||||
|
this.props.onRequestHide();
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Modal {...this.props} title="Transfer artwork">
|
||||||
|
<div className="modal-body">
|
||||||
|
<TransferForm edition={this.props.edition}
|
||||||
|
currentUser={this.props.currentUser}
|
||||||
|
onRequestHide={this.onRequestHide}/>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export default TransferModalButton;
|
@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import ImageViewer from './ascribe_media/image_viewer';
|
import ImageViewer from './ascribe_media/image_viewer';
|
||||||
|
import TransferModalButton from './ascribe_modal/modal_transfer';
|
||||||
import ShareModalButton from './ascribe_modal/modal_share';
|
import ShareModalButton from './ascribe_modal/modal_share';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,6 +47,7 @@ let EditionDetails = React.createClass({
|
|||||||
<EditionDetailProperty label="id" value={ this.props.edition.bitcoin_id } />
|
<EditionDetailProperty label="id" value={ this.props.edition.bitcoin_id } />
|
||||||
<EditionDetailProperty label="owner" value={ this.props.edition.owner } />
|
<EditionDetailProperty label="owner" value={ this.props.edition.owner } />
|
||||||
<br/>
|
<br/>
|
||||||
|
<TransferModalButton edition={ this.props.edition } currentUser={ this.props.currentUser }/>
|
||||||
<ShareModalButton edition={ this.props.edition } currentUser={ this.props.currentUser }/>
|
<ShareModalButton edition={ this.props.edition } currentUser={ this.props.currentUser }/>
|
||||||
<hr/>
|
<hr/>
|
||||||
</div>
|
</div>
|
||||||
|
8
js/constants/api_urls.js
Normal file
8
js/constants/api_urls.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import AppConstants from './application_constants';
|
||||||
|
|
||||||
|
let apiUrls = {
|
||||||
|
'ownership_shares_mail' : AppConstants.baseUrl + 'ownership/shares/mail/',
|
||||||
|
'ownership_transfers' : AppConstants.baseUrl + 'ownership/transfers/'
|
||||||
|
};
|
||||||
|
|
||||||
|
export default apiUrls;
|
@ -1,6 +1,8 @@
|
|||||||
let constants = {
|
let constants = {
|
||||||
'baseUrl': 'http://staging.ascribe.io/api/',
|
'baseUrl': 'http://localhost:8000/api/',
|
||||||
|
//'baseUrl': 'http://staging.ascribe.io/api/',
|
||||||
'debugCredentialBase64': 'ZGltaUBtYWlsaW5hdG9yLmNvbTowMDAwMDAwMDAw' // dimi@mailinator:0000000000
|
'debugCredentialBase64': 'ZGltaUBtYWlsaW5hdG9yLmNvbTowMDAwMDAwMDAw' // dimi@mailinator:0000000000
|
||||||
};
|
|
||||||
|
|
||||||
|
};
|
||||||
export default constants;
|
export default constants;
|
||||||
|
|
||||||
|
21
js/mixins/alert_mixin.js
Normal file
21
js/mixins/alert_mixin.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import AlertDismissable from '../components/ascribe_forms/alert';
|
||||||
|
|
||||||
|
let AlertMixin = {
|
||||||
|
setAlerts(errors){
|
||||||
|
let alerts = errors.map(
|
||||||
|
function(error) {
|
||||||
|
let key = error + this.state.retry;
|
||||||
|
return <AlertDismissable error={error} key={key}/>;
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
|
this.setState({alerts: alerts, retry: this.state.retry + 1});
|
||||||
|
},
|
||||||
|
hideAlerts(){
|
||||||
|
for (alert in this.state.alerts){
|
||||||
|
alert.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AlertMixin;
|
@ -1,75 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import Alert from 'react-bootstrap/lib/Alert';
|
|
||||||
|
|
||||||
let FormMixin = {
|
|
||||||
getInitialState() {
|
|
||||||
return {errors: null,
|
|
||||||
retry: 0 // used for unique keying
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleResponse(response){
|
|
||||||
if (response.status >= 200 && response.status < 300)
|
|
||||||
return response;
|
|
||||||
this.handleError(response);
|
|
||||||
},
|
|
||||||
handleError(response){
|
|
||||||
response.json().then((response) => this.setState({errors: response.errors,
|
|
||||||
retry: this.state.retry + 1}))
|
|
||||||
},
|
|
||||||
renderAlert(id){
|
|
||||||
if (this.state.errors && id in this.state.errors) {
|
|
||||||
return this.state.errors[id].map(function(error) {
|
|
||||||
let key = error + this.state.retry;
|
|
||||||
return <AlertDismissable error={error} key={key} />;
|
|
||||||
}.bind(this)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return <span />
|
|
||||||
},
|
|
||||||
renderTextInput(id, type, placeHolder, required) {
|
|
||||||
return (
|
|
||||||
<div className="form-group">
|
|
||||||
{this.renderAlert(id)}
|
|
||||||
<input className="form-control input-text-ascribe" name={id} ref={id}
|
|
||||||
placeholder={placeHolder} required={required} type={type} />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
renderTextArea(id, placeHolder, required) {
|
|
||||||
return (
|
|
||||||
<div className="form-group">
|
|
||||||
{this.renderAlert(id)}
|
|
||||||
<textarea className="form-control input-text-ascribe textarea-ascribe-message" name={id} ref={id}
|
|
||||||
defaultValue={placeHolder} required={required}></textarea>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let AlertDismissable = React.createClass({
|
|
||||||
getInitialState() {
|
|
||||||
return {
|
|
||||||
alertVisible: true
|
|
||||||
};
|
|
||||||
},
|
|
||||||
show() {
|
|
||||||
this.setState({alertVisible: true});
|
|
||||||
},
|
|
||||||
hide() {
|
|
||||||
this.setState({alertVisible: false});
|
|
||||||
},
|
|
||||||
render() {
|
|
||||||
if (this.state.alertVisible) {
|
|
||||||
return (
|
|
||||||
<Alert bsStyle='danger' onDismiss={this.hide}>
|
|
||||||
{this.props.error}
|
|
||||||
</Alert>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<span />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default FormMixin;
|
|
67
js/mixins/form_mixin.js
Normal file
67
js/mixins/form_mixin.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import AppConstants from '../constants/application_constants'
|
||||||
|
import AlertDismissable from '../components/ascribe_forms/alert'
|
||||||
|
|
||||||
|
export const FormMixin = {
|
||||||
|
getInitialState() {
|
||||||
|
return {
|
||||||
|
submitted: false
|
||||||
|
, status: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
submit(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.setState({submitted: true});
|
||||||
|
fetch(this.url(), {
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Basic ' + AppConstants.debugCredentialBase64,
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(this.getFormData())
|
||||||
|
})
|
||||||
|
.then(
|
||||||
|
(response) => this.handleResponse(response)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
handleResponse(response){
|
||||||
|
if (response.status >= 200 && response.status < 300){
|
||||||
|
this.props.onRequestHide();
|
||||||
|
}
|
||||||
|
else if (response.status >= 400 && response.status < 500) {
|
||||||
|
this.handleError(response);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.setState({submitted: false, status: response.status});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleError(response){
|
||||||
|
response.json().then((response) => this.dispatchErrors(response.errors));
|
||||||
|
|
||||||
|
},
|
||||||
|
dispatchErrors(errors){
|
||||||
|
for (var input in errors){
|
||||||
|
if (this.refs && this.refs[input] && this.refs[input].state){
|
||||||
|
this.refs[input].setAlerts(errors[input]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.setState({submitted: false});
|
||||||
|
},
|
||||||
|
render(){
|
||||||
|
let alert = null;
|
||||||
|
if (this.state.status >= 500){
|
||||||
|
alert = <AlertDismissable error="Something went wrong, please try again later"/>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{alert}
|
||||||
|
{this.renderForm()}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FormMixin;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user