diff --git a/docs/refactor-todo.md b/docs/refactor-todo.md index 8554f001..f7a5917b 100644 --- a/docs/refactor-todo.md +++ b/docs/refactor-todo.md @@ -2,16 +2,18 @@ *This should be a living document. So if you have any ideas for refactoring stuff, then feel free to add them to this document* -- Get rid of all Mixins. (making good progress there :)) - Make all standalone components independent from things like global utilities (GeneralUtils is maybe used in table for example) -- Check if all polyfills are appropriately initialized and available: Compare to this - Extract all standalone components to their own folder structure and write application independent tests (+ figure out how to do that in a productive way) (fetch lib especially) -- Refactor forms to generic-declarative form component - Check for mobile compatibility: Is site responsive anywhere? queryParams of the piece_list_store should all be reflected in the url and not a single component each should manipulate the URL bar (refactor pagination, use actions and state) - Refactor string-templating for api_urls - Use classNames plugin instead of if-conditional-classes +# Refactor DONE +- Refactor forms to generic-declarative form component ✓ +- Get rid of all Mixins (inject head is fine) ✓ +- Check if all polyfills are appropriately initialized and available: Compare to this ✓ + ## React-S3-Fineuploader - implementation should enable to define all important methods outside - and: maybe create a utility class for all methods to avoid code duplication diff --git a/js/components/ascribe_buttons/acl_button.js b/js/components/ascribe_buttons/acl_button.js index d9423889..2f5bb7b0 100644 --- a/js/components/ascribe_buttons/acl_button.js +++ b/js/components/ascribe_buttons/acl_button.js @@ -13,8 +13,10 @@ import AppConstants from '../../constants/application_constants'; import GlobalNotificationModel from '../../models/global_notification_model'; import GlobalNotificationActions from '../../actions/global_notification_actions'; -import { getLangText } from '../../utils/lang_utils.js'; -import apiUrls from '../../constants/api_urls'; +import ApiUrls from '../../constants/api_urls'; + +import { getAclFormMessage } from '../../utils/form_utils'; +import { getLangText } from '../../utils/lang_utils'; let AclButton = React.createClass({ propTypes: { @@ -34,15 +36,18 @@ let AclButton = React.createClass({ }, actionProperties(){ + + let message = getAclFormMessage(this.props.action, this.getTitlesString(), this.props.currentUser.username); + if (this.props.action === 'acl_consign'){ return { title: getLangText('Consign artwork'), tooltip: getLangText('Have someone else sell the artwork'), form: ( + url={ApiUrls.ownership_consigns}/> ), handleSuccess: this.showNotification }; @@ -53,9 +58,9 @@ let AclButton = React.createClass({ tooltip: getLangText('Have the owner manage his sales again'), form: ( + url={ApiUrls.ownership_unconsigns}/> ), handleSuccess: this.showNotification }; @@ -65,9 +70,9 @@ let AclButton = React.createClass({ tooltip: getLangText('Transfer the ownership of the artwork'), form: ( + url={ApiUrls.ownership_transfers}/> ), handleSuccess: this.showNotification }; @@ -77,9 +82,9 @@ let AclButton = React.createClass({ title: getLangText('Loan artwork'), tooltip: getLangText('Loan your artwork for a limited period of time'), form: ( + url={this.isPiece() ? ApiUrls.ownership_loans_pieces : ApiUrls.ownership_loans_editions}/> ), handleSuccess: this.showNotification }; @@ -90,9 +95,9 @@ let AclButton = React.createClass({ tooltip: getLangText('Share the artwork'), form: ( + url={this.isPiece() ? ApiUrls.ownership_shares_pieces : ApiUrls.ownership_shares_editions }/> ), handleSuccess: this.showNotification }; @@ -133,76 +138,6 @@ let AclButton = React.createClass({ } }, -// plz move to transfer form - getTransferMessage(){ - return ( - `${getLangText('Hi')}, - -${getLangText('I transfer ownership of')}: -${this.getTitlesString()} ${getLangText('to you')}. - -${getLangText('Truly yours')}, -${this.props.currentUser.username} - ` - ); - }, - - // plz move to transfer form - getLoanMessage(){ - return ( - `${getLangText('Hi')}, - -${getLangText('I loan')}: -${this.getTitlesString()} ${getLangText('to you')}. - -${getLangText('Truly yours')}, -${this.props.currentUser.username} - ` - ); - }, - - // plz move to consign form - getConsignMessage(){ - return ( - `${getLangText('Hi')}, - -${getLangText('I consign')}: -${this.getTitlesString()} ${getLangText('to you')}. - -${getLangText('Truly yours')}, -${this.props.currentUser.username} - ` - ); - }, - - // plz move to consign form - getUnConsignMessage(){ - return ( - `${getLangText('Hi')}, - -${getLangText('I un-consign')}: -${this.getTitlesString()} ${getLangText('from you')}. - -${getLangText('Truly yours')}, -${this.props.currentUser.username} - ` - ); - }, - -// plz move to share form - getShareMessage(){ - return ( - `${getLangText('Hi')}, - -${getLangText('I am sharing')}: -${this.getTitlesString()} ${getLangText('with you')}. - -${getLangText('Truly yours')}, -${this.props.currentUser.username} - ` - ); - }, - // Removes the acl_ prefix and converts to upper case sanitizeAction() { return this.props.action.split('acl_')[1].toUpperCase(); @@ -214,14 +149,13 @@ ${this.props.currentUser.username} return ( {this.sanitizeAction()} } handleSuccess={aclProps.handleSuccess} - title={aclProps.title} - tooltip={aclProps.tooltip}> + title={aclProps.title}> {aclProps.form} ); diff --git a/js/components/ascribe_buttons/delete_button.js b/js/components/ascribe_buttons/delete_button.js index a60344df..b0b64427 100644 --- a/js/components/ascribe_buttons/delete_button.js +++ b/js/components/ascribe_buttons/delete_button.js @@ -26,7 +26,7 @@ let DeleteButton = React.createClass({ mixins: [Router.Navigation], - render: function () { + render() { let availableAcls; let btnDelete; let content; @@ -61,13 +61,14 @@ let DeleteButton = React.createClass({ } btnDelete = ; - } - else { + + } else { return null; } + return ( {content} @@ -77,4 +78,3 @@ let DeleteButton = React.createClass({ }); export default DeleteButton; - diff --git a/js/components/ascribe_buttons/unconsign_request_button.js b/js/components/ascribe_buttons/unconsign_request_button.js index 11cbfa51..e5e1c661 100644 --- a/js/components/ascribe_buttons/unconsign_request_button.js +++ b/js/components/ascribe_buttons/unconsign_request_button.js @@ -8,7 +8,7 @@ import ModalWrapper from '../ascribe_modal/modal_wrapper'; import UnConsignRequestForm from './../ascribe_forms/form_unconsign_request'; import { getLangText } from '../../utils/lang_utils.js'; -import apiUrls from '../../constants/api_urls'; +import ApiUrls from '../../constants/api_urls'; let UnConsignRequestButton = React.createClass({ @@ -21,16 +21,15 @@ let UnConsignRequestButton = React.createClass({ render: function () { return ( REQUEST UNCONSIGN } handleSuccess={this.props.handleSuccess} - title='Request to Un-Consign' - tooltip='Ask the consignee to return the ownership of the work back to you'> + title='Request to Un-Consign'> {text} {this.props.title} -
+ {this.props.children} -
+ ); diff --git a/js/components/ascribe_detail/edition.js b/js/components/ascribe_detail/edition.js index 3aabc9e2..0f1477c0 100644 --- a/js/components/ascribe_detail/edition.js +++ b/js/components/ascribe_detail/edition.js @@ -36,7 +36,7 @@ import DeleteButton from '../ascribe_buttons/delete_button'; import GlobalNotificationModel from '../../models/global_notification_model'; import GlobalNotificationActions from '../../actions/global_notification_actions'; -import apiUrls from '../../constants/api_urls'; +import ApiUrls from '../../constants/api_urls'; import AppConstants from '../../constants/application_constants'; import { getLangText } from '../../utils/lang_utils'; @@ -233,10 +233,11 @@ let EditionSummary = React.createClass({ if (this.props.edition.status.length > 0 && this.props.edition.pending_new_owner && this.props.edition.acl.acl_withdraw_transfer) { withdrawButton = (
+ className='inline' + isInline={true}> @@ -335,7 +336,7 @@ let EditionPersonalNote = React.createClass({ if (this.props.currentUser.username && true || false) { return ( this[action](), 100); + + // selecting http method based on props + if(this[this.props.method]) { + window.setTimeout(() => this[this.props.method](), 100); + } else { + throw new Error('This HTTP method is not supported by form.js (' + this.props.method + ')'); + } }, - post(){ + + post() { requests .post(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 ref in this.refs){ @@ -79,6 +109,7 @@ let Form = React.createClass({ handleChangeChild(){ this.setState({edited: true}); }, + handleSuccess(response){ if ('handleSuccess' in this.props){ this.props.handleSuccess(response); @@ -88,8 +119,12 @@ let Form = React.createClass({ this.refs[ref].handleSuccess(); } } - this.setState({edited: false, submitted: false}); + this.setState({ + edited: false, + submitted: false + }); }, + handleError(err){ if (err.json) { for (var input in err.json.errors){ @@ -109,10 +144,18 @@ let Form = React.createClass({ } console.logGlobal(err, false, formData); - this.setState({errors: [getLangText('Something went wrong, please try again later')]}); + + 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 (var ref in this.refs){ if ('clearErrors' in this.refs[ref]){ @@ -121,6 +164,7 @@ let Form = React.createClass({ } this.setState({errors: []}); }, + getButtons() { if (this.state.submitted){ return this.props.spinner; @@ -143,6 +187,7 @@ let Form = React.createClass({ } return buttons; }, + getErrors() { let errors = null; if (this.state.errors.length > 0){ @@ -152,6 +197,7 @@ let Form = React.createClass({ } return errors; }, + renderChildren() { return ReactAddons.Children.map(this.props.children, (child) => { if (child) { @@ -162,6 +208,7 @@ let Form = React.createClass({ } }); }, + render() { let className = 'ascribe-form'; diff --git a/js/components/ascribe_forms/form_consign.js b/js/components/ascribe_forms/form_consign.js index 5815efdd..6f85adc2 100644 --- a/js/components/ascribe_forms/form_consign.js +++ b/js/components/ascribe_forms/form_consign.js @@ -8,7 +8,6 @@ import Form from './form'; import Property from './property'; import InputTextAreaToggable from './input_textarea_toggable'; - import AppConstants from '../../constants/application_constants'; import { getLangText } from '../../utils/lang_utils.js'; @@ -18,7 +17,6 @@ let ConsignForm = React.createClass({ url: React.PropTypes.string, id: React.PropTypes.object, message: React.PropTypes.string, - onRequestHide: React.PropTypes.func, handleSuccess: React.PropTypes.func }, @@ -27,7 +25,6 @@ let ConsignForm = React.createClass({ }, render() { - return ( - + type="submit"> + {getLangText('CONSIGN')} +

} spinner={ diff --git a/js/components/ascribe_forms/form_delete_edition.js b/js/components/ascribe_forms/form_delete_edition.js index c05a20bc..9eb721f6 100644 --- a/js/components/ascribe_forms/form_delete_edition.js +++ b/js/components/ascribe_forms/form_delete_edition.js @@ -2,33 +2,65 @@ import React from 'react'; -import requests from '../../utils/requests'; +import Form from './form'; + import ApiUrls from '../../constants/api_urls'; -import FormMixin from '../../mixins/form_mixin'; +import AppConstants from '../../constants/application_constants'; + import { getLangText } from '../../utils/lang_utils'; + let EditionDeleteForm = React.createClass({ - mixins: [FormMixin], + propTypes: { + editions: React.PropTypes.arrayOf(React.PropTypes.object), - url() { - return requests.prepareUrl(ApiUrls.edition_delete, {edition_id: this.getBitcoinIds().join()}); - }, - httpVerb(){ - return 'delete'; + // Propagated by ModalWrapper in most cases + handleSuccess: React.PropTypes.func }, - renderForm () { + getBitcoinIds() { + return this.props.editions.map(function(edition){ + return edition.bitcoin_id; + }); + }, + + // Since this form can be used for either deleting a single edition or multiple + // we need to call getBitcoinIds to get the value of edition_id + getFormData() { + return { + edition_id: this.getBitcoinIds().join(',') + }; + }, + + render () { return ( -
+ +

+ +

+
+ } + spinner={ +
+ +
+ }>

{getLangText('Are you sure you would like to permanently delete this edition')}?

{getLangText('This is an irrevocable action%s', '.')}

-
- - -
- + ); } }); diff --git a/js/components/ascribe_forms/form_delete_piece.js b/js/components/ascribe_forms/form_delete_piece.js index 168d9261..552c38c0 100644 --- a/js/components/ascribe_forms/form_delete_piece.js +++ b/js/components/ascribe_forms/form_delete_piece.js @@ -2,37 +2,56 @@ import React from 'react'; -import requests from '../../utils/requests'; +import Form from '../ascribe_forms/form'; + import ApiUrls from '../../constants/api_urls'; -import FormMixin from '../../mixins/form_mixin'; +import AppConstants from '../../constants/application_constants'; + import { getLangText } from '../../utils/lang_utils'; + let PieceDeleteForm = React.createClass({ propTypes: { - pieceId: React.PropTypes.number + pieceId: React.PropTypes.number, + + // Propagated by ModalWrapper in most cases + handleSuccess: React.PropTypes.func }, - mixins: [FormMixin], - - url() { - return requests.prepareUrl(ApiUrls.piece, {piece_id: this.props.pieceId}); + getFormData() { + return { + piece_id: this.props.pieceId + }; }, - httpVerb() { - return 'delete'; - }, - - renderForm () { + render() { return ( -
+
+

+ +

+
+ } + spinner={ +
+ +
+ }>

{getLangText('Are you sure you would like to permanently delete this piece')}?

{getLangText('This is an irrevocable action%s', '.')}

-
- - -
- + ); } }); diff --git a/js/components/ascribe_forms/form_loan.js b/js/components/ascribe_forms/form_loan.js index 7aae656a..f319c363 100644 --- a/js/components/ascribe_forms/form_loan.js +++ b/js/components/ascribe_forms/form_loan.js @@ -23,7 +23,6 @@ let LoanForm = React.createClass({ url: React.PropTypes.string, id: React.PropTypes.object, message: React.PropTypes.string, - onRequestHide: React.PropTypes.func, handleSuccess: React.PropTypes.func }, @@ -101,11 +100,9 @@ let LoanForm = React.createClass({

- + type="submit"> + {getLangText('LOAN')} +

} spinner={ diff --git a/js/components/ascribe_forms/form_login.js b/js/components/ascribe_forms/form_login.js index 24b0eb93..a3fd4d33 100644 --- a/js/components/ascribe_forms/form_login.js +++ b/js/components/ascribe_forms/form_login.js @@ -11,16 +11,14 @@ import UserActions from '../../actions/user_actions'; import Form from './form'; import Property from './property'; -import FormPropertyHeader from './form_property_header'; -import apiUrls from '../../constants/api_urls'; +import ApiUrls from '../../constants/api_urls'; import AppConstants from '../../constants/application_constants'; import { getLangText } from '../../utils/lang_utils'; let LoginForm = React.createClass({ - propTypes: { headerMessage: React.PropTypes.string, submitMessage: React.PropTypes.string, @@ -101,7 +99,7 @@ let LoginForm = React.createClass({
}> - +

{this.props.headerMessage}

- +
diff --git a/js/components/ascribe_forms/form_piece_extradata.js b/js/components/ascribe_forms/form_piece_extradata.js index bbec9dca..293ca09e 100644 --- a/js/components/ascribe_forms/form_piece_extradata.js +++ b/js/components/ascribe_forms/form_piece_extradata.js @@ -3,9 +3,9 @@ import React from 'react'; import requests from '../../utils/requests'; -import { getLangText } from '../../utils/lang_utils.js' +import { getLangText } from '../../utils/lang_utils.js'; -import apiUrls from '../../constants/api_urls'; +import ApiUrls from '../../constants/api_urls'; import Form from './form'; import Property from './property'; @@ -20,7 +20,8 @@ let PieceExtraDataForm = React.createClass({ title: React.PropTypes.string, editable: React.PropTypes.bool }, - getFormData(){ + + getFormData() { let extradata = {}; extradata[this.props.name] = this.refs.form.refs[this.props.name].state.value; return { @@ -28,12 +29,13 @@ let PieceExtraDataForm = React.createClass({ piece_id: this.props.pieceId }; }, + render() { let defaultValue = this.props.extraData[this.props.name] || ''; if (defaultValue.length === 0 && !this.props.editable){ return null; } - let url = requests.prepareUrl(apiUrls.piece_extradata, {piece_id: this.props.pieceId}); + let url = requests.prepareUrl(ApiUrls.piece_extradata, {piece_id: this.props.pieceId}); return ( - {this.props.children} - - ); - } -}); - -export default FormPropertyHeader; \ No newline at end of file diff --git a/js/components/ascribe_forms/form_register_piece.js b/js/components/ascribe_forms/form_register_piece.js index 853506f6..156642aa 100644 --- a/js/components/ascribe_forms/form_register_piece.js +++ b/js/components/ascribe_forms/form_register_piece.js @@ -7,12 +7,11 @@ import UserActions from '../../actions/user_actions'; import Form from './form'; import Property from './property'; -import FormPropertyHeader from './form_property_header'; import ReactS3FineUploader from '../ascribe_uploader/react_s3_fine_uploader'; import AppConstants from '../../constants/application_constants'; -import apiUrls from '../../constants/api_urls'; +import ApiUrls from '../../constants/api_urls'; import { getCookie } from '../../utils/fetch_api_utils'; import { getLangText } from '../../utils/lang_utils'; @@ -96,7 +95,7 @@ let RegisterPieceForm = React.createClass({ }> - +

{this.props.headerMessage}

- +
+ +

+ +

+ + } + spinner={ +
+ +
+ }>

{getLangText('Are you sure you would like to remove these editions from your collection')}?

{getLangText('This is an irrevocable action%s', '.')}

-
- - -
- + ); } }); diff --git a/js/components/ascribe_forms/form_remove_piece_from_collection.js b/js/components/ascribe_forms/form_remove_piece_from_collection.js index 905cfcf6..d827c2ee 100644 --- a/js/components/ascribe_forms/form_remove_piece_from_collection.js +++ b/js/components/ascribe_forms/form_remove_piece_from_collection.js @@ -2,38 +2,56 @@ import React from 'react'; -import { getLangText } from '../../utils/lang_utils.js'; -import requests from '../../utils/requests'; -import apiUrls from '../../constants/api_urls'; -import FormMixin from '../../mixins/form_mixin'; +import Form from './form'; + +import ApiUrls from '../../constants/api_urls'; +import AppConstants from '../../constants/application_constants'; + +import { getLangText } from '../../utils/lang_utils'; + let PieceRemoveFromCollectionForm = React.createClass({ - propTypes: { - pieceId: React.PropTypes.number + pieceId: React.PropTypes.number, + + // Propagated by ModalWrapper in most cases + handleSuccess: React.PropTypes.func }, - mixins: [FormMixin], - - url() { - return requests.prepareUrl(apiUrls.piece_remove_from_collection, {piece_id: this.props.pieceId}); - }, - - httpVerb(){ - return 'delete'; + getFormData() { + return { + piece_id: this.props.pieceId + }; }, - renderForm () { + render () { return ( -
+
+

+ +

+
+ } + spinner={ +
+ +
+ }>

{getLangText('Are you sure you would like to remove this piece from your collection')}?

{getLangText('This is an irrevocable action%s', '.')}

-
- - -
- + ); } }); diff --git a/js/components/ascribe_forms/form_request_action.js b/js/components/ascribe_forms/form_request_action.js index 622aa02f..cc4ad88a 100644 --- a/js/components/ascribe_forms/form_request_action.js +++ b/js/components/ascribe_forms/form_request_action.js @@ -2,48 +2,48 @@ import React from 'react'; -import Alert from 'react-bootstrap/lib/Alert'; - -import apiUrls from '../../constants/api_urls'; -import FormMixin from '../../mixins/form_mixin'; - import AclButton from './../ascribe_buttons/acl_button'; +import ActionPanel from '../ascribe_panel/action_panel'; +import Form from './form'; + +import GlobalNotificationModel from '../../models/global_notification_model'; +import GlobalNotificationActions from '../../actions/global_notification_actions'; + +import ApiUrls from '../../constants/api_urls'; -import AppConstants from '../../constants/application_constants'; import { getLangText } from '../../utils/lang_utils.js'; -let RequestActionForm = React.createClass({ - mixins: [FormMixin], - url(e){ - let edition = this.props.editions[0]; - if (e.target.id === 'request_accept'){ - if (edition.request_action === 'consign'){ - return apiUrls.ownership_consigns_confirm; - } - else if (edition.request_action === 'unconsign'){ - return apiUrls.ownership_unconsigns; - } - else if (edition.request_action === 'loan'){ - return apiUrls.ownership_loans_confirm; - } - } - else if(e.target.id === 'request_deny'){ - if (edition.request_action === 'consign') { - return apiUrls.ownership_consigns_deny; - } - else if (edition.request_action === 'unconsign') { - return apiUrls.ownership_unconsigns_deny; - } - else if (edition.request_action === 'loan') { - return apiUrls.ownership_loans_deny; - } - } +let RequestActionForm = React.createClass({ + propTypes: { + editions: React.PropTypes.arrayOf(React.PropTypes.object), + currentUser: React.PropTypes.object, + handleSuccess: React.PropTypes.func }, - handleRequest: function(e){ - e.preventDefault(); - this.submit(e); + getUrls() { + let edition = this.props.editions[0]; + let urls = {}; + + + if (edition.request_action === 'consign'){ + urls.accept = ApiUrls.ownership_consigns_confirm; + urls.deny = ApiUrls.ownership_consigns_deny; + } else if (edition.request_action === 'unconsign'){ + urls.accept = ApiUrls.ownership_unconsigns; + urls.deny = ApiUrls.ownership_unconsigns_deny; + } else if (edition.request_action === 'loan'){ + urls.accept = ApiUrls.ownership_loans_confirm; + urls.deny = ApiUrls.ownership_loans_deny; + } + + return urls; + }, + + getBitcoinIds(){ + return this.props.editions.map(function(edition){ + return edition.bitcoin_id; + }); }, getFormData() { @@ -52,16 +52,35 @@ let RequestActionForm = React.createClass({ }; }, - renderForm() { + showNotification(option, action, owner) { + return () => { + let message = getLangText('You have successfully') + ' ' + option + ' the ' + action + ' request ' + getLangText('from') + ' ' + owner; + + let notification = new GlobalNotificationModel(message, 'success'); + GlobalNotificationActions.appendGlobalNotification(notification); + + if(this.props.handleSuccess) { + this.props.handleSuccess(); + } + }; + }, + + getContent() { let edition = this.props.editions[0]; - let buttonAccept = ( -
{getLangText('ACCEPT')} -
); - if (edition.request_action === 'unconsign'){ - console.log(this.props) - buttonAccept = ( + let message = edition.owner + ' ' + getLangText('requests you') + ' ' + edition.request_action + ' ' + getLangText('this edition%s', '.'); + + return ( + + {message} + + ); + }, + + getAcceptButtonForm(urls) { + let edition = this.props.editions[0]; + + if(edition.request_action === 'unconsign') { + return ( ); - } - let buttons = ( - - - {buttonAccept} - - -
{getLangText('REJECT')}
-
-
- ); - if (this.state.submitted){ - buttons = ( - - - + } else { + return ( +
+ +
); } + }, + + getButtonForm() { + let edition = this.props.editions[0]; + + let urls = this.getUrls(); + let acceptButtonForm = this.getAcceptButtonForm(urls); + return ( - -
-
{ edition.owner } {getLangText('requests you')} { edition.request_action } {getLangText('this edition%s', '.')}  
- {buttons} -
-
+
+
+ +
+ {acceptButtonForm} +
+ ); + }, + + render() { + return ( + ); } }); diff --git a/js/components/ascribe_forms/form_share_email.js b/js/components/ascribe_forms/form_share_email.js index 881c9683..89884886 100644 --- a/js/components/ascribe_forms/form_share_email.js +++ b/js/components/ascribe_forms/form_share_email.js @@ -2,14 +2,14 @@ import React from 'react'; - - import Form from './form'; import Property from './property'; import InputTextAreaToggable from './input_textarea_toggable'; + import Button from 'react-bootstrap/lib/Button'; import AppConstants from '../../constants/application_constants'; + import { getLangText } from '../../utils/lang_utils.js'; @@ -20,7 +20,6 @@ let ShareForm = React.createClass({ message: React.PropTypes.string, editions: React.PropTypes.array, currentUser: React.PropTypes.object, - onRequestHide: React.PropTypes.func, handleSuccess: React.PropTypes.func }, @@ -41,11 +40,9 @@ let ShareForm = React.createClass({

- + type="submit"> + SHARE +

} spinner={ diff --git a/js/components/ascribe_forms/form_signup.js b/js/components/ascribe_forms/form_signup.js index 55aff4b8..67097b42 100644 --- a/js/components/ascribe_forms/form_signup.js +++ b/js/components/ascribe_forms/form_signup.js @@ -12,10 +12,9 @@ import GlobalNotificationActions from '../../actions/global_notification_actions import Form from './form'; import Property from './property'; -import FormPropertyHeader from './form_property_header'; import InputCheckbox from './input_checkbox'; -import apiUrls from '../../constants/api_urls'; +import ApiUrls from '../../constants/api_urls'; let SignupForm = React.createClass({ @@ -56,10 +55,6 @@ let SignupForm = React.createClass({ } }, - getFormData() { - return this.getQuery(); - }, - handleSuccess(response){ if (response.user) { let notification = new GlobalNotificationModel(getLangText('Sign up successful'), 'success', 50000); @@ -80,8 +75,8 @@ let SignupForm = React.createClass({
@@ -92,9 +87,9 @@ let SignupForm = React.createClass({ }> - +

{this.props.headerMessage}

- +
diff --git a/js/components/ascribe_forms/form_submit_to_prize.js b/js/components/ascribe_forms/form_submit_to_prize.js index 7f991af3..ff853c01 100644 --- a/js/components/ascribe_forms/form_submit_to_prize.js +++ b/js/components/ascribe_forms/form_submit_to_prize.js @@ -19,10 +19,7 @@ import requests from '../../utils/requests'; let PieceSubmitToPrizeForm = React.createClass({ propTypes: { piece: React.PropTypes.object, - handleSuccess: React.PropTypes.func, - - // this is set by ModalWrapper automatically - onRequestHide: React.PropTypes.func + handleSuccess: React.PropTypes.func }, render() { @@ -36,7 +33,9 @@ let PieceSubmitToPrizeForm = React.createClass({

+ type="submit"> + {getLangText('SUBMIT TO PRIZE')} +

} spinner={ @@ -80,7 +79,6 @@ let PieceSubmitToPrizeForm = React.createClass({

{getLangText('Are you sure you want to submit to the prize?')}

{getLangText('This is an irrevocable action%s', '.')}

- ); } diff --git a/js/components/ascribe_forms/form_transfer.js b/js/components/ascribe_forms/form_transfer.js index 07821475..6ec73d38 100644 --- a/js/components/ascribe_forms/form_transfer.js +++ b/js/components/ascribe_forms/form_transfer.js @@ -21,7 +21,6 @@ let TransferForm = React.createClass({ message: React.PropTypes.string, editions: React.PropTypes.array, currentUser: React.PropTypes.object, - onRequestHide: React.PropTypes.func, handleSuccess: React.PropTypes.func }, @@ -42,11 +41,9 @@ let TransferForm = React.createClass({

- + type="submit"> + {getLangText('TRANSFER')} +

} spinner={ diff --git a/js/components/ascribe_forms/form_unconsign.js b/js/components/ascribe_forms/form_unconsign.js index d33ccedf..9bc5b4bd 100644 --- a/js/components/ascribe_forms/form_unconsign.js +++ b/js/components/ascribe_forms/form_unconsign.js @@ -18,7 +18,6 @@ let UnConsignForm = React.createClass({ id: React.PropTypes.object, message: React.PropTypes.string, editions: React.PropTypes.array, - onRequestHide: React.PropTypes.func, handleSuccess: React.PropTypes.func }, @@ -39,11 +38,9 @@ let UnConsignForm = React.createClass({

- + type="submit"> + {getLangText('UNCONSIGN')} +

} spinner={ diff --git a/js/components/ascribe_forms/form_unconsign_request.js b/js/components/ascribe_forms/form_unconsign_request.js index 1978e151..c47b5411 100644 --- a/js/components/ascribe_forms/form_unconsign_request.js +++ b/js/components/ascribe_forms/form_unconsign_request.js @@ -3,7 +3,6 @@ import React from 'react'; import Button from 'react-bootstrap/lib/Button'; -import Alert from 'react-bootstrap/lib/Alert'; import Form from './form'; import Property from './property'; @@ -19,7 +18,6 @@ let UnConsignRequestForm = React.createClass({ url: React.PropTypes.string, id: React.PropTypes.object, message: React.PropTypes.string, - onRequestHide: React.PropTypes.func, handleSuccess: React.PropTypes.func }, @@ -40,11 +38,9 @@ let UnConsignRequestForm = React.createClass({

- + type="submit"> + {getLangText('REQUEST UNCONSIGN')} +

} spinner={ diff --git a/js/components/ascribe_forms/input_date.js b/js/components/ascribe_forms/input_date.js index 32ffb5eb..e77f70b7 100644 --- a/js/components/ascribe_forms/input_date.js +++ b/js/components/ascribe_forms/input_date.js @@ -7,7 +7,8 @@ import DatePicker from 'react-datepicker/dist/react-datepicker'; let InputDate = React.createClass({ propTypes: { submitted: React.PropTypes.bool, - placeholderText: React.PropTypes.string + placeholderText: React.PropTypes.string, + onChange: React.PropTypes.func }, getInitialState() { diff --git a/js/components/ascribe_forms/input_textarea_toggable.js b/js/components/ascribe_forms/input_textarea_toggable.js index fe372bdd..bc70c530 100644 --- a/js/components/ascribe_forms/input_textarea_toggable.js +++ b/js/components/ascribe_forms/input_textarea_toggable.js @@ -5,7 +5,6 @@ import React from 'react'; import TextareaAutosize from 'react-textarea-autosize'; let InputTextAreaToggable = React.createClass({ - propTypes: { editable: React.PropTypes.bool.isRequired, rows: React.PropTypes.number.isRequired, diff --git a/js/components/ascribe_forms/property_collapsible.js b/js/components/ascribe_forms/property_collapsible.js index ba6c0a1e..03ec404d 100644 --- a/js/components/ascribe_forms/property_collapsible.js +++ b/js/components/ascribe_forms/property_collapsible.js @@ -3,11 +3,9 @@ import React from 'react'; import ReactAddons from 'react/addons'; -import CollapsibleMixin from 'react-bootstrap/lib/CollapsibleMixin'; import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; import Tooltip from 'react-bootstrap/lib/Tooltip'; - -import classNames from 'classnames'; +import Panel from 'react-bootstrap/lib/Panel'; let PropertyCollapsile = React.createClass({ @@ -17,22 +15,12 @@ let PropertyCollapsile = React.createClass({ tooltip: React.PropTypes.string }, - mixins: [CollapsibleMixin], - getInitialState() { return { show: false }; }, - getCollapsibleDOMNode(){ - return React.findDOMNode(this.refs.panel); - }, - - getCollapsibleDimensionValue(){ - return React.findDOMNode(this.refs.panel).scrollHeight; - }, - handleFocus() { this.refs.checkboxCollapsible.getDOMNode().checked = !this.refs.checkboxCollapsible.getDOMNode().checked; this.setState({ @@ -85,11 +73,14 @@ let PropertyCollapsile = React.createClass({ {this.props.checkboxLabel} -
+ +
{this.renderChildren()} -
+
+ ); } diff --git a/js/components/ascribe_modal/modal_password_request_reset.js b/js/components/ascribe_modal/modal_password_request_reset.js index fffcb3d7..d941bcce 100644 --- a/js/components/ascribe_modal/modal_password_request_reset.js +++ b/js/components/ascribe_modal/modal_password_request_reset.js @@ -7,9 +7,13 @@ import PasswordResetRequestForm from '../ascribe_forms/form_password_reset_reque import GlobalNotificationModel from '../../models/global_notification_model'; import GlobalNotificationActions from '../../actions/global_notification_actions'; -import { getLangText } from '../../utils/lang_utils.js' +import { getLangText } from '../../utils/lang_utils.js'; let PasswordResetRequestModal = React.createClass({ + propTypes: { + button: React.PropTypes.element + }, + handleResetSuccess(){ let notificationText = getLangText('Request successfully sent, check your email'); let notification = new GlobalNotificationModel(notificationText, 'success', 50000); @@ -18,10 +22,9 @@ let PasswordResetRequestModal = React.createClass({ render() { return ( + handleSuccess={this.handleResetSuccess}> ); diff --git a/js/components/ascribe_modal/modal_wrapper.js b/js/components/ascribe_modal/modal_wrapper.js index a8f7b182..f00eee9e 100644 --- a/js/components/ascribe_modal/modal_wrapper.js +++ b/js/components/ascribe_modal/modal_wrapper.js @@ -4,92 +4,74 @@ import React from 'react'; import ReactAddons from 'react/addons'; 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 ModalMixin from '../../mixins/modal_mixin'; let ModalWrapper = React.createClass({ propTypes: { - title: React.PropTypes.string.isRequired, - onRequestHide: React.PropTypes.func, + trigger: React.PropTypes.element.isRequired, + title: React.PropTypes.oneOfType([ + React.PropTypes.arrayOf(React.PropTypes.element), + React.PropTypes.element, + React.PropTypes.string + ]).isRequired, handleSuccess: React.PropTypes.func.isRequired, - button: React.PropTypes.object.isRequired, - children: React.PropTypes.object, - tooltip: React.PropTypes.string + children: React.PropTypes.oneOfType([ + React.PropTypes.arrayOf(React.PropTypes.element), + React.PropTypes.element + ]) }, - getModalTrigger() { - return ( - - {this.props.children} - - }> - {this.props.button} - - ); + getInitialState() { + return { + showModal: false + }; }, - render() { - if(this.props.tooltip) { - return ( - {this.props.tooltip}}> - {this.getModalTrigger()} - - ); - } else { - return ( - - {/* This needs to be some kind of inline-block */} - {this.getModalTrigger()} - - ); - } - } -}); - - -let ModalBody = React.createClass({ - propTypes: { - onRequestHide: React.PropTypes.func, - handleSuccess: React.PropTypes.func, - children: React.PropTypes.object, - title: React.PropTypes.string.isRequired + show() { + this.setState({ + showModal: true + }); }, - mixins: [ModalMixin], + hide() { + this.setState({ + showModal: false + }); + }, handleSuccess(response){ this.props.handleSuccess(response); - this.props.onRequestHide(); + this.hide(); }, renderChildren() { return ReactAddons.Children.map(this.props.children, (child) => { return ReactAddons.addons.cloneWithProps(child, { - onRequestHide: this.props.onRequestHide, handleSuccess: this.handleSuccess }); }); }, render() { + // this adds the onClick method show of modal_wrapper to the trigger component + // which is in most cases a button. + let trigger = React.cloneElement(this.props.trigger, {onClick: this.show}); + return ( - -
- {this.renderChildren()} -
-
+ + {trigger} + + + + {this.props.title} + + +
+ {this.renderChildren()} +
+
+
); } }); - export default ModalWrapper; diff --git a/js/components/ascribe_panel/action_panel.js b/js/components/ascribe_panel/action_panel.js index 1aaef6f7..148ea03d 100644 --- a/js/components/ascribe_panel/action_panel.js +++ b/js/components/ascribe_panel/action_panel.js @@ -1,12 +1,15 @@ 'use strict'; import React from 'react'; - +import classnames from 'classnames'; let ActionPanel = React.createClass({ propTypes: { title: React.PropTypes.string, - content: React.PropTypes.string, + content: React.PropTypes.oneOfType([ + React.PropTypes.string, + React.PropTypes.element + ]), buttons: React.PropTypes.element, onClick: React.PropTypes.func, ignoreFocus: React.PropTypes.bool @@ -37,33 +40,17 @@ let ActionPanel = React.createClass({ }); }, - getClassName() { - if(this.state.isFocused) { - return 'is-focused'; - } else { - return ''; - } - }, - render() { - return ( -
-
-
-
-
- {this.props.title} -
-
- {this.props.content} -
-
+
+
+
+ {this.props.content}
-
-
- {this.props.buttons} -
+
+
+
+ {this.props.buttons}
diff --git a/js/components/coa_verify_container.js b/js/components/coa_verify_container.js index 8c2f2e77..96987323 100644 --- a/js/components/coa_verify_container.js +++ b/js/components/coa_verify_container.js @@ -10,7 +10,7 @@ import Form from './ascribe_forms/form'; import Property from './ascribe_forms/property'; import InputTextAreaToggable from './ascribe_forms/input_textarea_toggable'; -import apiUrls from '../constants/api_urls'; +import ApiUrls from '../constants/api_urls'; import { getLangText } from '../utils/lang_utils'; @@ -59,7 +59,7 @@ let CoaVerifyForm = React.createClass({ return (
; - } - - return ( - -
- This modal is controlled by our custom trigger component. -
-
- -
-
- ); - } -}); - -export default LoginModalHandler; \ No newline at end of file diff --git a/js/components/password_reset_container.js b/js/components/password_reset_container.js index 20851632..80a42a62 100644 --- a/js/components/password_reset_container.js +++ b/js/components/password_reset_container.js @@ -5,8 +5,7 @@ import Router from 'react-router'; import Form from './ascribe_forms/form'; import Property from './ascribe_forms/property'; -import FormPropertyHeader from './ascribe_forms/form_property_header'; -import apiUrls from '../constants/api_urls'; +import ApiUrls from '../constants/api_urls'; import GlobalNotificationModel from '../models/global_notification_model'; import GlobalNotificationActions from '../actions/global_notification_actions'; @@ -15,12 +14,15 @@ import { getLangText } from '../utils/lang_utils'; let PasswordResetContainer = React.createClass({ mixins: [Router.Navigation], + getInitialState() { return {isRequested: false}; }, - handleRequestSuccess(email){ + + handleRequestSuccess(email) { this.setState({isRequested: email}); }, + render() { if (this.props.query.email && this.props.query.token) { return ( @@ -57,17 +59,22 @@ let PasswordResetContainer = React.createClass({ }); let PasswordRequestResetForm = React.createClass({ + propTypes: { + handleRequestSuccess: React.PropTypes.func + }, + handleSuccess() { let notificationText = getLangText('If your email address exists in our database, you will receive a password recovery link in a few minutes.'); let notification = new GlobalNotificationModel(notificationText, 'success', 50000); GlobalNotificationActions.appendGlobalNotification(notification); this.props.handleRequestSuccess(this.refs.form.refs.email.state.value); }, + render() { return ( }> - +

{getLangText('Reset your password')}

- +
@@ -99,24 +106,31 @@ let PasswordRequestResetForm = React.createClass({ }); let PasswordResetForm = React.createClass({ + propTypes: { + email: React.PropTypes.string, + token: React.PropTypes.string + }, + mixins: [Router.Navigation], - getFormData(){ + getFormData() { return { email: this.props.email, token: this.props.token }; }, + handleSuccess() { this.transitionTo('pieces'); let notification = new GlobalNotificationModel(getLangText('password successfully updated'), 'success', 10000); GlobalNotificationActions.appendGlobalNotification(notification); }, + render() { return ( }> - +

{getLangText('Reset the password for')} {this.props.email}

- +
diff --git a/js/components/react_flow_type/react_flow_type.js b/js/components/react_flow_type/react_flow_type.js deleted file mode 100644 index 063eaf1c..00000000 --- a/js/components/react_flow_type/react_flow_type.js +++ /dev/null @@ -1,92 +0,0 @@ -'use strict'; -/** - * This component is essentially a port of https://github.com/simplefocus/FlowType.JS - * to Reactjs in order to not being forced to use jQuery - * - * Author: Tim Daubenschütz - * - * Thanks to the guys at Simple Focus http://simplefocus.com/ - */ - -import React from 'react'; -import ReactAddons from 'react/addons'; - -let FlowType = React.createClass({ - propTypes: { - - // standard FlowTypes.JS options - maximum: React.PropTypes.number, - minimum: React.PropTypes.number, - maxFont: React.PropTypes.number, - minFont: React.PropTypes.number, - fontRatio: React.PropTypes.number, - - // react specific options - children: React.PropTypes.element.isRequired // only supporting one child element at once right now - }, - - getDefaultProps() { - return { - maximum: 9999, - minimum: 1, - maxFont: 9999, - minFont: 1, - fontRatio: 35 - }; - }, - - getInitialState() { - return { - // 32 because that's the default font display size - // doesn't really matter though - fontSize: 0 - }; - }, - - componentDidMount() { - // Make changes upon resize, calculate changes and rerender - this.handleResize(); - window.addEventListener('resize', this.handleResize); - }, - - componentWillUnmount() { - // stop listening to window once the component was unmounted - window.removeEventListener('resize', this.handleResize); - }, - - handleResize() { - let elemWidth = this.refs.flowTypeElement.getDOMNode().offsetWidth; - let width = elemWidth > this.props.maximum ? this.props.maximum : elemWidth < this.props.minimum ? this.props.minimum : elemWidth; - let fontBase = width / this.props.fontRatio; - let fontSize = fontBase > this.props.maxFont ? this.props.maxFont : fontBase < this.props.minFont ? this.props.minFont : fontBase; - - this.setState({ fontSize }); - }, - - // The child the user passes to this component needs to have it's - // style.fontSize property to be updated - renderChildren() { - return ReactAddons.Children.map(this.props.children, (child) => { - return ReactAddons.addons.cloneWithProps(child, { - ref: 'flowTypeFontElement', - - }); - }); - }, - - render() { - return ( -
- {this.props.children} -
- ); - } -}); - -export default FlowType; \ No newline at end of file diff --git a/js/components/register_piece.js b/js/components/register_piece.js index f46ab99c..94ce4d33 100644 --- a/js/components/register_piece.js +++ b/js/components/register_piece.js @@ -23,7 +23,6 @@ import GlobalNotificationActions from '../actions/global_notification_actions'; import Property from './ascribe_forms/property'; import PropertyCollapsible from './ascribe_forms/property_collapsible'; import RegisterPieceForm from './ascribe_forms/form_register_piece'; -//import FormPropertyHeader from './ascribe_forms/form_property_header'; import LoginContainer from './login_container'; import SlidesContainer from './ascribe_slides_container/slides_container'; @@ -40,7 +39,9 @@ let RegisterPiece = React.createClass( { submitMessage: React.PropTypes.string, children: React.PropTypes.oneOfType([ React.PropTypes.arrayOf(React.PropTypes.element), - React.PropTypes.element]) + React.PropTypes.element, + React.PropTypes.string + ]) }, mixins: [Router.Navigation], diff --git a/js/components/settings_container.js b/js/components/settings_container.js index 76a6cf2c..f26e6472 100644 --- a/js/components/settings_container.js +++ b/js/components/settings_container.js @@ -24,10 +24,10 @@ import InputCheckbox from './ascribe_forms/input_checkbox'; import ActionPanel from './ascribe_panel/action_panel'; -import apiUrls from '../constants/api_urls'; +import ApiUrls from '../constants/api_urls'; import AppConstants from '../constants/application_constants'; -import { getLangText } from '../utils/lang_utils'; +import { getLangText } from '../utils/lang_utils'; import { getCookie } from '../utils/fetch_api_utils'; let SettingsContainer = React.createClass({ @@ -90,7 +90,7 @@ let AccountSettings = React.createClass({ if (this.state.currentUser.username) { content = ( +
+ {app.name} +
+
+ {'Bearer ' + app.bearer_token.token} +
+
+ } buttons={
- +
+ +
}/> ); @@ -366,15 +376,15 @@ let APISettings = React.createClass({ } return content; }, + render() { - return ( This is the submission page for Sluice_screens ↄc Prize 2015.

- + Sign up to submit diff --git a/js/components/whitelabel/prize/components/settings_container.js b/js/components/whitelabel/prize/components/settings_container.js index affae8f2..d2f24f0d 100644 --- a/js/components/whitelabel/prize/components/settings_container.js +++ b/js/components/whitelabel/prize/components/settings_container.js @@ -14,7 +14,6 @@ import CollapsibleParagraph from '../../../ascribe_collapsible/collapsible_parag import Form from '../../../ascribe_forms/form'; import Property from '../../../ascribe_forms/property'; -import FormPropertyHeader from '../../../ascribe_forms/form_property_header'; import ActionPanel from '../../../ascribe_panel/action_panel'; @@ -22,7 +21,7 @@ import GlobalNotificationModel from '../../../../models/global_notification_mode import GlobalNotificationActions from '../../../../actions/global_notification_actions'; import AppConstants from '../../../../constants/application_constants'; -import apiUrls from '../../../../constants/api_urls'; +import ApiUrls from '../../../../constants/api_urls'; import { getLangText } from '../../../../utils/lang_utils'; @@ -170,8 +169,16 @@ let PrizeJurySettings = React.createClass({ +
+ {member.email} +
+
+ {member.status} +
+
+ } buttons={
+ content={ +
+
+ {member.email} +
+
+ {member.status} +
+ } + buttons={ + }/> ); @@ -219,17 +232,23 @@ let PrizeJurySettings = React.createClass({ - + content={ +
+
+ {member.email} +
+
+ {member.status} +
+ } + buttons={ + }/> ); @@ -270,13 +289,13 @@ let PrizeJurySettings = React.createClass({ return (
- +

Jury Members

- +
diff --git a/js/constants/api_urls.js b/js/constants/api_urls.js index b4f7d926..c11a5df2 100644 --- a/js/constants/api_urls.js +++ b/js/constants/api_urls.js @@ -5,7 +5,7 @@ import getPrizeApiUrls from '../components/whitelabel/prize/constants/api_urls'; import { update } from '../utils/general_utils'; -let apiUrls = { +let ApiUrls = { 'applications': AppConstants.apiEndpoint + 'applications/', 'application_token_refresh': AppConstants.apiEndpoint + 'applications/refresh_token/', 'blob_digitalworks': AppConstants.apiEndpoint + 'blob/digitalworks/', @@ -63,7 +63,7 @@ export function updateApiUrls(type, subdomain) { if (type === 'prize') { newUrls = getPrizeApiUrls(subdomain); } - update(apiUrls, newUrls); + update(ApiUrls, newUrls); } -export default apiUrls; +export default ApiUrls; diff --git a/js/fetchers/user_fetcher.js b/js/fetchers/user_fetcher.js index a175c644..eca7494d 100644 --- a/js/fetchers/user_fetcher.js +++ b/js/fetchers/user_fetcher.js @@ -1,7 +1,7 @@ 'use strict'; import requests from '../utils/requests'; -import apiUrls from '../constants/api_urls'; +import ApiUrls from '../constants/api_urls'; let UserFetcher = { /** @@ -13,7 +13,7 @@ let UserFetcher = { }, logout() { - return requests.get(apiUrls.users_logout); + return requests.get(ApiUrls.users_logout); } }; diff --git a/js/mixins/alert_mixin.js b/js/mixins/alert_mixin.js deleted file mode 100644 index 2b9b5158..00000000 --- a/js/mixins/alert_mixin.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -import React from 'react'; -import AlertDismissable from '../components/ascribe_forms/alert'; - -let AlertMixin = { - setAlerts(errors){ - let alerts = errors.map((error) => { - return ; - }); - - this.setState({alerts: alerts}); - }, - - clearAlerts(){ - this.setState({alerts: null}); - } - -}; - -export default AlertMixin; \ No newline at end of file diff --git a/js/mixins/form_mixin.js b/js/mixins/form_mixin.js deleted file mode 100644 index c04f52ed..00000000 --- a/js/mixins/form_mixin.js +++ /dev/null @@ -1,108 +0,0 @@ -'use strict'; - -import requests from '../utils/requests'; -import React from 'react'; - -import AlertDismissable from '../components/ascribe_forms/alert'; -import { getLangText } from '../utils/lang_utils.js'; - -export const FormMixin = { - propTypes: { - editions: React.PropTypes.array, - currentUser: React.PropTypes.object - }, - - getInitialState() { - return { - submitted: false, - errors: [] - }; - }, - - submit(e) { - if (e) { - e.preventDefault(); - } - this.setState({submitted: true}); - this.clearErrors(); - let action = (this.httpVerb && this.httpVerb()) || 'post'; - this[action](e); - }, - - post(e){ - requests - .post(this.url(e), { body: this.getFormData() }) - .then(this.handleSuccess) - .catch(this.handleError); - }, - - delete(e){ - requests - .delete(this.url(e)) - .then(this.handleSuccess) - .catch(this.handleError); - }, - - clearErrors(){ - for (var ref in this.refs){ - if ('clearAlerts' in this.refs[ref]){ - this.refs[ref].clearAlerts(); - } - - } - this.setState({errors: []}); - }, - handleSuccess(response){ - if ('handleSuccess' in this.props){ - this.props.handleSuccess(response); - } - - }, - 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].setAlerts( err.json.errors[input]); - } else { - this.setState({errors: this.state.errors.concat(err.json.errors[input])}); - } - } - } - else { - // TODO translate? - this.setState({errors: ['Something went wrong, please try again later']}); - } - this.setState({submitted: false}); - }, - - getBitcoinIds(){ - return this.props.editions.map(function(edition){ - return edition.bitcoin_id; - }); - }, - - getTitlesString(){ - return this.props.editions.map(function(edition){ - return '- \"' + edition.title + ', ' + getLangText('edition') + ' ' + edition.edition_number + '\"\n'; - }); - }, - - render(){ - let alert = null; - if (this.state.errors.length > 0){ - alert = this.state.errors.map((error) => { - return ; - }); - } - - return ( -
- {alert} - {this.renderForm()} -
- ); - } -}; - -export default FormMixin; - diff --git a/js/mixins/modal_mixin.js b/js/mixins/modal_mixin.js deleted file mode 100644 index 6087f32c..00000000 --- a/js/mixins/modal_mixin.js +++ /dev/null @@ -1,12 +0,0 @@ -'use strict'; - -let ModalMixin = { - onRequestHide(e){ - if (e) { - e.preventDefault(); - } - this.props.onRequestHide(); - } -}; - -export default ModalMixin; \ No newline at end of file diff --git a/js/utils/form_utils.js b/js/utils/form_utils.js new file mode 100644 index 00000000..3a8861cc --- /dev/null +++ b/js/utils/form_utils.js @@ -0,0 +1,51 @@ +'use strict'; + +import { getLangText } from './lang_utils'; + +/** + * Generates a message for submitting a form + * @param {string} aclName Enum name of a acl + * @param {string} entities Already computed name of entities + * @param {string} senderName Name of the sender + * @return {string} Completed message + */ +export function getAclFormMessage(aclName, entities, senderName) { + let message = ''; + + message += getLangText('Hi'); + message += ',\n\n'; + + if(aclName === 'acl_transfer') { + message += getLangText('I transfer ownership of'); + } else if(aclName === 'acl_consign') { + message += getLangText('I consign'); + } else if(aclName === 'acl_unconsign') { + message += getLangText('I un-consign'); + } else if(aclName === 'acl_loan') { + message += getLangText('I loan'); + } else if(aclName === 'acl_share') { + message += getLangText('I share'); + } else { + throw new Error('Your specified aclName did not match a an acl class.'); + } + + message += ':\n'; + message += entities; + + if(aclName === 'acl_transfer' || aclName === 'acl_loan' || aclName === 'acl_consign') { + message += getLangText('to you'); + } else if(aclName === 'acl_unconsign') { + message += getLangText('from you'); + } else if(aclName === 'acl_share') { + message += getLangText('with you'); + } else { + throw new Error('Your specified aclName did not match a an acl class.'); + } + + message += '\n\n'; + message += getLangText('Truly yours,'); + message += '\n'; + message += senderName; + + return message; +} \ No newline at end of file diff --git a/js/utils/requests.js b/js/utils/requests.js index 8705bc0f..793e1f21 100644 --- a/js/utils/requests.js +++ b/js/utils/requests.js @@ -91,7 +91,7 @@ class Requests { } catch(err) { throw err; } - + newUrl = newUrl.replace(re, (match, key) => { let val = params[key]; if (!val) { diff --git a/package.json b/package.json index 4fdf87fb..0fc9844f 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "q": "^1.4.1", "raven-js": "^1.1.19", "react": "^0.13.2", - "react-bootstrap": "~0.22.6", + "react-bootstrap": "^0.24.3", "react-datepicker": "~0.8.0", "react-progressbar": "^1.1.0", "react-router": "^0.13.3", diff --git a/sass/ascribe_edition.scss b/sass/ascribe_edition.scss index ee082a04..fe647e2d 100644 --- a/sass/ascribe_edition.scss +++ b/sass/ascribe_edition.scss @@ -16,33 +16,35 @@ margin-top: 20px; } .ascribe-edition-collapsible-wrapper > div > span { - font-size: 1.2em; - margin-right: .5em; + font-size: 1.2em; + margin-right: .5em; } .ascribe-edition-collapsible-wrapper > div > span:nth-child(2) { - font-size: 0.9em; + font-size: 0.9em; } -.ascribe-edition-collapible-content { - width:100%; +.ascribe-edition-collapsible-content { + width:100%; + background: none; + border: none; } -.coa-file-wrapper{ - display: table; - height: 200px; - overflow: hidden; - margin: 0 auto; - width: 100%; - padding: 1em; +.coa-file-wrapper { + display: table; + height: 200px; + overflow: hidden; + margin: 0 auto; + width: 100%; + padding: 1em; } .coa-file { - display: table-cell; - vertical-align: middle; - border: 1px solid #CCC; - background-color: #F8F8F8; + display: table-cell; + vertical-align: middle; + border: 1px solid #CCC; + background-color: #F8F8F8; } .ascribe-button-list { - margin-top: 1em; + margin-top: 1em; } \ No newline at end of file diff --git a/sass/ascribe_panel.scss b/sass/ascribe_panel.scss index 8ad4249c..73fe572e 100644 --- a/sass/ascribe_panel.scss +++ b/sass/ascribe_panel.scss @@ -1,18 +1,94 @@ .ascribe-panel-wrapper { border: 1px solid #DDD; - padding: 1.4em; - margin: 1em 0 1em 0; + min-height: 5em; + height: 5em; + + margin-top: 1em; + + > div { + height: 100%; + float: left; + + &:first-child { + width: 60%; + } + + &:nth-child(2) { + width: 40%; + } + } } -.ascribe-panel-title { - margin-bottom: 0.5em; +.ascribe-panel-table { + display:table; + + > .ascribe-panel-content { + display: table-cell; + vertical-align: middle; + } + + @media(max-width:767px) { + &:first-child { + > div { + padding-left: 1em; + } + + } + + &:nth-child(2) { + > div { + padding-right: 1em; + + > button { + float:right; + } + } + } + + } + + @media(min-width:768px) { + &:first-child { + > div { + padding-left: 2em; + } + + } + + &:nth-child(2) { + > div { + padding-right: 2em; + + > button { + float:right; + } + } + } + } } -.ascribe-panel-content { - font-size: 0.9em; - color: rgba(0,0,0,0.5); +@media(max-width:767px) { + .ascribe-panel-title { + font-size: .9em; + } + + .ascribe-panel-subtitle { + font-size: .7em; + color: rgba(0,0,0,0.5); + } + } -.ascribe-panel-buttons { - margin-top: 0.5em; -} \ No newline at end of file +@media(min-width:768px) { + .ascribe-panel-title { + font-size: 1.1em; + } + + .ascribe-panel-subtitle { + font-size: .9em; + color: rgba(0,0,0,0.5); + } +} + + + diff --git a/sass/ascribe_theme.scss b/sass/ascribe_theme.scss index 9eb92a30..e02b97c6 100644 --- a/sass/ascribe_theme.scss +++ b/sass/ascribe_theme.scss @@ -2,4 +2,14 @@ .pager li a { color: white; +} + +.panel-default { + border: none; + box-shadow: none; + margin-bottom: 0; +} + +.panel-body { + padding:0; } \ No newline at end of file