'use strict'; import React from 'react'; import ReactAddons from 'react/addons'; import Button from 'react-bootstrap/lib/Button'; import AlertDismissable from './alert'; import GlobalNotificationModel from '../../models/global_notification_model'; import GlobalNotificationActions from '../../actions/global_notification_actions'; import requests from '../../utils/requests'; import { getLangText } from '../../utils/lang_utils'; import { sanitize } from '../../utils/general_utils'; let Form = React.createClass({ propTypes: { url: React.PropTypes.string, method: React.PropTypes.string, buttonSubmitText: React.PropTypes.string, handleSuccess: React.PropTypes.func, getFormData: React.PropTypes.func, children: React.PropTypes.oneOfType([ React.PropTypes.object, React.PropTypes.array ]), className: React.PropTypes.string, spinner: React.PropTypes.element, buttons: React.PropTypes.oneOfType([ React.PropTypes.element, React.PropTypes.arrayOf(React.PropTypes.element) ]), // Can be used to freeze the whole form disabled: React.PropTypes.bool, // You can use the form for inline requests, like the submit click on a button. // For the form to then not display the error on top, you need to enable this option. // It will make use of the GlobalNotification isInline: React.PropTypes.bool, autoComplete: React.PropTypes.string, onReset: React.PropTypes.func }, getDefaultProps() { return { method: 'post', buttonSubmitText: 'SAVE', autoComplete: 'off' }; }, getInitialState() { return { edited: false, submitted: false, errors: [] }; }, reset() { // If onReset prop is defined from outside, // notify component that a form reset is happening. if(typeof this.props.onReset === 'function') { this.props.onReset(); } for(let ref in this.refs) { if(typeof this.refs[ref].reset === 'function') { this.refs[ref].reset(); } } this.setState(this.getInitialState()); }, submit(event){ if(event) { event.preventDefault(); } this.setState({submitted: true}); this.clearErrors(); // selecting http method based on props if(this[this.props.method] && typeof this[this.props.method] === 'function') { window.setTimeout(() => this[this.props.method](), 100); } else { throw new Error('This HTTP method is not supported by form.js (' + this.props.method + ')'); } }, post() { requests .post(this.props.url, { body: this.getFormData() }) .then(this.handleSuccess) .catch(this.handleError); }, put() { requests .put(this.props.url, { body: this.getFormData() }) .then(this.handleSuccess) .catch(this.handleError); }, patch() { requests .patch(this.props.url, { body: this.getFormData() }) .then(this.handleSuccess) .catch(this.handleError); }, delete() { requests .delete(this.props.url, this.getFormData()) .then(this.handleSuccess) .catch(this.handleError); }, getFormData() { let data = {}; for (let refName in this.refs) { const ref = this.refs[refName]; if (ref.state && 'value' in ref.state) { // An input can also provide an `Object` as a value // which we're going to merge with `data` (overwrites) if(ref.state.value.constructor === Object) { Object.assign(data, ref.state.value); } else { data[ref.props.name] = ref.state.value; } } } if (typeof this.props.getFormData === 'function') { data = Object.assign(data, this.props.getFormData()); } return data; }, handleChangeChild(){ this.setState({ edited: true }); }, handleSuccess(response){ if(typeof this.props.handleSuccess === 'function') { this.props.handleSuccess(response); } for(let ref in this.refs) { if(this.refs[ref] && typeof this.refs[ref].handleSuccess === 'function'){ this.refs[ref].handleSuccess(); } } this.setState({ edited: false, submitted: false }); }, handleError(err) { if (err.json) { for (let 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 { let formData = this.getFormData(); // sentry shouldn't post the user's password if(formData.password) { delete formData.password; } console.logGlobal(err, false, formData); if(this.props.isInline) { let notification = new GlobalNotificationModel(getLangText('Something went wrong, please try again later'), 'danger'); GlobalNotificationActions.appendGlobalNotification(notification); } else { this.setState({errors: [getLangText('Something went wrong, please try again later')]}); } } this.setState({submitted: false}); }, clearErrors() { for(let ref in this.refs){ if (this.refs[ref] && typeof this.refs[ref].clearErrors === 'function'){ 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 && !this.props.disabled){ buttons = (