1
0
mirror of https://github.com/ascribe/onion.git synced 2025-01-03 10:25:08 +01:00

Finalize submission flow with validation and thumbnail creation

This commit is contained in:
Tim Daubenschütz 2015-11-10 17:35:57 +01:00
parent 447ccabf45
commit ea321a4a77
10 changed files with 281 additions and 176 deletions

View File

@ -236,12 +236,12 @@ let Form = React.createClass({
}, },
renderChildren() { renderChildren() {
return ReactAddons.Children.map(this.props.children, (child) => { return ReactAddons.Children.map(this.props.children, (child, i) => {
if (child) { if (child) {
return ReactAddons.addons.cloneWithProps(child, { return ReactAddons.addons.cloneWithProps(child, {
handleChange: this.handleChangeChild, handleChange: this.handleChangeChild,
ref: child.props.name, ref: child.props.name,
key: i,
// We need this in order to make editable be overridable when setting it directly // We need this in order to make editable be overridable when setting it directly
// on Property // on Property
editable: child.props.overrideForm ? child.props.editable : !this.props.disabled editable: child.props.overrideForm ? child.props.editable : !this.props.disabled
@ -269,7 +269,13 @@ let Form = React.createClass({
} }
}, },
_validateRef(refToValidate) { /**
* Validates a single ref and returns a human-readable error message
* @param {object} refToValidate A customly constructed object to check
* @return {oneOfType([arrayOf(string), bool])} Either an error message or false, saying that
* everything is valid
*/
_hasRefErrors(refToValidate) {
let error = []; let error = [];
Object Object
@ -280,13 +286,13 @@ let Form = React.createClass({
if(!contraintValue) { if(!contraintValue) {
switch(constraintKey) { switch(constraintKey) {
case 'min' || 'max': case 'min' || 'max':
error.push(getLangText('The field you defined is not in the valid range.')); error.push(getLangText('The field you defined is not in the valid range'));
break; break;
case 'pattern': case 'pattern':
error.push(getLangText('The value you defined is not matching the valid pattern')); error.push(getLangText('The value you defined is not matching the valid pattern'));
break; break;
case 'required': case 'required':
error.push(getLangText('This field is required.')); error.push(getLangText('This field is required'));
break; break;
} }
} }
@ -295,7 +301,22 @@ let Form = React.createClass({
return error.length ? error : false; return error.length ? error : false;
}, },
valid() { /**
* This method validates all child inputs of the form.
*
* As of now, it only considers
* - `max`
* - `min`
* - `pattern`
* - `required`
*
* The idea is to enhance this method everytime we need more thorough validation.
* So feel free to add props that additionally should be checked, if they're present
* in the input's props.
*
* @return {[type]} [description]
*/
validate() {
this.clearErrors(); this.clearErrors();
const validatedFormInputs = {}; const validatedFormInputs = {};
@ -317,12 +338,12 @@ let Form = React.createClass({
refToValidate.max = type === 'number' ? parseInt(value) <= max : true; refToValidate.max = type === 'number' ? parseInt(value) <= max : true;
refToValidate.min = type === 'number' ? parseInt(value) >= min : true; refToValidate.min = type === 'number' ? parseInt(value) >= min : true;
const validatedRef = this._validateRef(refToValidate); const validatedRef = this._hasRefErrors(refToValidate);
validatedFormInputs[refName] = validatedRef; validatedFormInputs[refName] = validatedRef;
}); });
const errorMessagesForFields = sanitize(validatedFormInputs, (val) => !val); const errorMessagesForRefs = sanitize(validatedFormInputs, (val) => !val);
this.handleError({ json: { errors: errorMessagesForFields } }); this.handleError({ json: { errors: errorMessagesForRefs } });
return !Object.keys(errorMessagesForFields).length; return !Object.keys(errorMessagesForRefs).length;
}, },
render() { render() {

View File

@ -3,64 +3,82 @@
import React from 'react'; import React from 'react';
import ReactS3FineUploader from '../ascribe_uploader/react_s3_fine_uploader'; import ReactS3FineUploader from '../ascribe_uploader/react_s3_fine_uploader';
import FileDragAndDrop from '../ascribe_uploader/ascribe_file_drag_and_drop/file_drag_and_drop';
import AppConstants from '../../constants/application_constants'; import AppConstants from '../../constants/application_constants';
import { getCookie } from '../../utils/fetch_api_utils'; import { getCookie } from '../../utils/fetch_api_utils';
let InputFineUploader = React.createClass({
const { func, bool, object, shape, string, number, arrayOf } = React.PropTypes;
const InputFineUploader = React.createClass({
propTypes: { propTypes: {
setIsUploadReady: React.PropTypes.func, setIsUploadReady: func,
isReadyForFormSubmission: React.PropTypes.func, isReadyForFormSubmission: func,
submitFileName: React.PropTypes.func, submitFileName: func,
fileInputElement: func,
areAssetsDownloadable: React.PropTypes.bool, areAssetsDownloadable: bool,
onClick: React.PropTypes.func, onClick: func,
keyRoutine: React.PropTypes.shape({ keyRoutine: shape({
url: React.PropTypes.string, url: string,
fileClass: React.PropTypes.string fileClass: string
}), }),
createBlobRoutine: React.PropTypes.shape({ createBlobRoutine: shape({
url: React.PropTypes.string url: string
}), }),
validation: React.PropTypes.shape({ validation: shape({
itemLimit: React.PropTypes.number, itemLimit: number,
sizeLimit: React.PropTypes.string, sizeLimit: string,
allowedExtensions: React.PropTypes.arrayOf(React.PropTypes.string) allowedExtensions: arrayOf(string)
}), }),
// isFineUploaderActive is used to lock react fine uploader in case // isFineUploaderActive is used to lock react fine uploader in case
// a user is actually not logged in already to prevent him from droping files // a user is actually not logged in already to prevent him from droping files
// before login in // before login in
isFineUploaderActive: React.PropTypes.bool, isFineUploaderActive: bool,
onLoggedOut: React.PropTypes.func, onLoggedOut: func,
enableLocalHashing: React.PropTypes.bool, enableLocalHashing: bool,
// provided by Property // provided by Property
disabled: React.PropTypes.bool, disabled: bool,
// A class of a file the user has to upload // A class of a file the user has to upload
// Needs to be defined both in singular as well as in plural // Needs to be defined both in singular as well as in plural
fileClassToUpload: React.PropTypes.shape({ fileClassToUpload: shape({
singular: React.PropTypes.string, singular: string,
plural: React.PropTypes.string plural: string
}), }),
location: React.PropTypes.object location: object
},
getDefaultProps() {
return {
fileInputElement: FileDragAndDrop
};
}, },
getInitialState() { getInitialState() {
return { return {
value: null value: null,
file: null
}; };
}, },
submitFile(file){ submitFile(file) {
console.log(file);
this.setState({ this.setState({
file,
value: file.key value: file.key
}); });
if(this.state.value && typeof this.props.onChange === 'function') {
this.props.onChange({ target: { value: this.state.value } });
}
if(typeof this.props.submitFileName === 'function') { if(typeof this.props.submitFileName === 'function') {
this.props.submitFileName(file.originalName); this.props.submitFileName(file.originalName);
} }
@ -70,7 +88,26 @@ let InputFineUploader = React.createClass({
this.refs.fineuploader.reset(); this.refs.fineuploader.reset();
}, },
createBlobRoutine() {
const { fineuploader } = this.refs;
const { file } = this.state;
fineuploader.createBlob(file);
},
render() { render() {
const { fileInputElement,
onClick,
keyRoutine,
createBlobRoutine,
validation,
setIsUploadReady,
isReadyForFormSubmission,
areAssetsDownloadable,
onLoggedOut,
enableLocalHashing,
fileClassToUpload,
location } = this.props;
let editable = this.props.isFineUploaderActive; let editable = this.props.isFineUploaderActive;
// if disabled is actually set by property, we want to override // if disabled is actually set by property, we want to override
@ -82,14 +119,15 @@ let InputFineUploader = React.createClass({
return ( return (
<ReactS3FineUploader <ReactS3FineUploader
ref="fineuploader" ref="fineuploader"
onClick={this.props.onClick} fileInputElement={fileInputElement}
keyRoutine={this.props.keyRoutine} onClick={onClick}
createBlobRoutine={this.props.createBlobRoutine} keyRoutine={keyRoutine}
validation={this.props.validation} createBlobRoutine={createBlobRoutine}
validation={validation}
submitFile={this.submitFile} submitFile={this.submitFile}
setIsUploadReady={this.props.setIsUploadReady} setIsUploadReady={setIsUploadReady}
isReadyForFormSubmission={this.props.isReadyForFormSubmission} isReadyForFormSubmission={isReadyForFormSubmission}
areAssetsDownloadable={this.props.areAssetsDownloadable} areAssetsDownloadable={areAssetsDownloadable}
areAssetsEditable={editable} areAssetsEditable={editable}
signature={{ signature={{
endpoint: AppConstants.serverUrl + 's3/signature/', endpoint: AppConstants.serverUrl + 's3/signature/',
@ -105,10 +143,10 @@ let InputFineUploader = React.createClass({
'X-CSRFToken': getCookie(AppConstants.csrftoken) 'X-CSRFToken': getCookie(AppConstants.csrftoken)
} }
}} }}
onInactive={this.props.onLoggedOut} onInactive={onLoggedOut}
enableLocalHashing={this.props.enableLocalHashing} enableLocalHashing={enableLocalHashing}
fileClassToUpload={this.props.fileClassToUpload} fileClassToUpload={fileClassToUpload}
location={this.props.location}/> location={location}/>
); );
} }
}); });

View File

@ -255,8 +255,10 @@ let Property = React.createClass({
placement="top" placement="top"
overlay={tooltip}> overlay={tooltip}>
<div className={'ascribe-property ' + this.props.className}> <div className={'ascribe-property ' + this.props.className}>
{this.state.errors} <p>
<span>{this.props.label}</span> <span className="pull-left">{this.props.label}</span>
<span className="pull-right">{this.state.errors}</span>
</p>
{this.renderChildren(style)} {this.renderChildren(style)}
{footer} {footer}
</div> </div>

View File

@ -92,8 +92,14 @@ let UploadButton = React.createClass({
allowedExtensions allowedExtensions
} = this.props; } = this.props;
/*
* We do not want a button that submits here.
* As UploadButton could be used in forms that want to be submitted independent
* of clicking the selector.
* Therefore the wrapping component needs to be an `anchor` tag instead of a `button`
*/
return ( return (
<button <a
onClick={this.handleOnClick} onClick={this.handleOnClick}
className="btn btn-default btn-sm margin-left-2px" className="btn btn-default btn-sm margin-left-2px"
disabled={this.getUploadingFiles().length !== 0 || !!this.getUploadedFile()}> disabled={this.getUploadingFiles().length !== 0 || !!this.getUploadedFile()}>
@ -109,7 +115,7 @@ let UploadButton = React.createClass({
}} }}
onChange={this.handleDrop} onChange={this.handleDrop}
accept={allowedExtensions}/> accept={allowedExtensions}/>
</button> </a>
); );
} }
}); });

View File

@ -7,7 +7,8 @@ import Form from '../../../../../ascribe_forms/form';
import Property from '../../../../../ascribe_forms/property'; import Property from '../../../../../ascribe_forms/property';
import InputTextAreaToggable from '../../../../../ascribe_forms/input_textarea_toggable'; import InputTextAreaToggable from '../../../../../ascribe_forms/input_textarea_toggable';
import UploadFileButton from '../../../../../ascribe_buttons/upload_file_button'; import UploadButton from '../../../../../ascribe_uploader/ascribe_upload_button/upload_button';
import InputFineuploader from '../../../../../ascribe_forms/input_fineuploader';
import GlobalNotificationModel from '../../../../../../models/global_notification_model'; import GlobalNotificationModel from '../../../../../../models/global_notification_model';
import GlobalNotificationActions from '../../../../../../actions/global_notification_actions'; import GlobalNotificationActions from '../../../../../../actions/global_notification_actions';
@ -19,6 +20,7 @@ import requests from '../../../../../../utils/requests';
import { getLangText } from '../../../../../../utils/lang_utils'; import { getLangText } from '../../../../../../utils/lang_utils';
import { setCookie } from '../../../../../../utils/fetch_api_utils'; import { setCookie } from '../../../../../../utils/fetch_api_utils';
import { formSubmissionValidation } from '../../../../../ascribe_uploader/react_s3_fine_uploader_utils';
const { object } = React.PropTypes; const { object } = React.PropTypes;
@ -45,20 +47,28 @@ const PRRegisterPieceForm = React.createClass({
* second adding all the additional details * second adding all the additional details
*/ */
submit() { submit() {
if(!this.validateForms()) {
return;
}
const { currentUser } = this.props; const { currentUser } = this.props;
const { registerPieceForm, const { registerPieceForm,
digitalWorkForm,
proofOfPaymentForm,
supportingMaterialsForm,
additionalDataForm, additionalDataForm,
thumbnailForm } = this.refs; uploadersForm } = this.refs;
const { digitalWorkKey,
thumbnailKey,
supportingMaterials,
proofOfPayment } = uploadersForm.refs;
const additionalDataFormData = additionalDataForm.getFormData(); const additionalDataFormData = additionalDataForm.getFormData();
// composing data for piece registration // composing data for piece registration
let registerPieceFormData = registerPieceForm.getFormData(); let registerPieceFormData = registerPieceForm.getFormData();
registerPieceFormData.digital_work_key = digitalWorkForm.state.file ? digitalWorkForm.state.file.key : ''; registerPieceFormData.digital_work_key = digitalWorkKey.state.value;
registerPieceFormData.thumbnail_file = thumbnailKey.state.value;
registerPieceFormData.terms = true; registerPieceFormData.terms = true;
console.log(registerPieceFormData);
// submitting the piece // submitting the piece
requests requests
.post(ApiUrls.pieces_list, { body: registerPieceFormData }) .post(ApiUrls.pieces_list, { body: registerPieceFormData })
@ -67,9 +77,8 @@ const PRRegisterPieceForm = React.createClass({
this.setState({ this.setState({
piece piece
}, () => { }, () => {
supportingMaterialsForm.createBlobRoutine(); supportingMaterials.refs.input.createBlobRoutine();
proofOfPaymentForm.createBlobRoutine(); proofOfPayment.refs.input.createBlobRoutine();
thumbnailForm.createBlobRoutine();
}); });
//setCookie(currentUser.email, piece.id); //setCookie(currentUser.email, piece.id);
@ -90,24 +99,28 @@ const PRRegisterPieceForm = React.createClass({
.catch((err) => { .catch((err) => {
console.log(err); console.log(err);
}); });
}, },
getCreateBlobRoutine(fileClass) { validateForms() {
const { registerPieceForm,
additionalDataForm,
uploadersForm } = this.refs;
const registerPieceFormValidation = registerPieceForm.validate();
const additionalDataFormValidation = additionalDataForm.validate();
const uploaderFormValidation = uploadersForm.validate();
return registerPieceFormValidation && additionalDataFormValidation && uploaderFormValidation;
},
getCreateBlobRoutine() {
const { piece } = this.state; const { piece } = this.state;
if(piece && piece.id) { if(piece && piece.id) {
if(fileClass === 'other_data') { return {
return { url: ApiUrls.blob_otherdatas,
url: ApiUrls.blob_otherdatas, pieceId: piece.id
pieceId: piece.id };
};
} else if(fileClass === 'thumbnail') {
return {
url: ApiUrls.blob_thumbnails,
pieceId: piece.id
};
}
} else { } else {
return null; return null;
} }
@ -174,92 +187,116 @@ const PRRegisterPieceForm = React.createClass({
placeholder={getLangText('Enter exhibitions and publication history')}/> placeholder={getLangText('Enter exhibitions and publication history')}/>
</Property> </Property>
</Form> </Form>
<div className="input-upload-file-button-property"> <Form
{getLangText('Select the PDF with your work')} buttons={{}}
<UploadFileButton className="ascribe-form-bordered"
ref="digitalWorkForm" ref="uploadersForm">
createBlobRoutine={{ <Property
url: ApiUrls.blob_digitalworks name="digitalWorkKey"
}} className="input-upload-file-button-property">
keyRoutine={{ <span>{getLangText('Select the PDF with your work')}</span>
url: AppConstants.serverUrl + 's3/key/', <InputFineuploader
fileClass: 'digitalwork' fileInputElement={UploadButton}
}} isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
validation={{ setIsUploadReady={() =>{/* So that ReactS3FineUploader is not complaining */}}
itemLimit: AppConstants.fineUploader.validation.registerWork.itemLimit, createBlobRoutine={{
sizeLimit: AppConstants.fineUploader.validation.additionalData.sizeLimit, url: ApiUrls.blob_digitalworks
allowedExtensions: ['pdf'] }}
}} keyRoutine={{
location={location} url: AppConstants.serverUrl + 's3/key/',
fileClassToUpload={{ fileClass: 'digitalwork'
singular: getLangText('Upload the Portfolio'), }}
plural: getLangText('Upload the Portfolios') validation={{
}}/> itemLimit: AppConstants.fineUploader.validation.registerWork.itemLimit,
</div> sizeLimit: AppConstants.fineUploader.validation.additionalData.sizeLimit,
<div className="input-upload-file-button-property"> allowedExtensions: ['pdf']
{getLangText('Featured Cover photo')} }}
<UploadFileButton location={location}
ref="thumbnailForm" fileClassToUpload={{
createBlobRoutine={this.getCreateBlobRoutine('thumbnail')} singular: getLangText('Upload the Portfolio'),
keyRoutine={{ plural: getLangText('Upload the Portfolios')
url: AppConstants.serverUrl + 's3/key/', }}
fileClass: 'thumbnail' required/>
}} </Property>
validation={{ <Property
itemLimit: AppConstants.fineUploader.validation.registerWork.itemLimit, name="thumbnailKey"
sizeLimit: AppConstants.fineUploader.validation.additionalData.sizeLimit, className="input-upload-file-button-property">
allowedExtensions: ['png', 'jpg'] <span>{getLangText('Featured Cover photo')}</span>
}} <InputFineuploader
location={location} fileInputElement={UploadButton}
fileClassToUpload={{ createBlobRoutine={{
singular: getLangText('Upload cover photo'), url: ApiUrls.blob_thumbnails
plural: getLangText('Upload cover photos') }}
}}/> isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
</div> setIsUploadReady={() =>{/* So that ReactS3FineUploader is not complaining */}}
<div className="input-upload-file-button-property"> keyRoutine={{
{getLangText('Supporting Materials (Optional)')} url: AppConstants.serverUrl + 's3/key/',
<UploadFileButton fileClass: 'thumbnail'
ref="supportingMaterialsForm" }}
createBlobRoutine={this.getCreateBlobRoutine('other_data')} validation={{
keyRoutine={{ itemLimit: AppConstants.fineUploader.validation.registerWork.itemLimit,
url: AppConstants.serverUrl + 's3/key/', sizeLimit: AppConstants.fineUploader.validation.additionalData.sizeLimit,
fileClass: 'other_data' allowedExtensions: ['png', 'jpg']
}} }}
validation={{ location={location}
itemLimit: AppConstants.fineUploader.validation.registerWork.itemLimit, fileClassToUpload={{
sizeLimit: AppConstants.fineUploader.validation.additionalData.sizeLimit singular: getLangText('Upload cover photo'),
}} plural: getLangText('Upload cover photos')
location={location} }}/>
fileClassToUpload={{ </Property>
singular: getLangText('Upload'), <Property
plural: getLangText('Upload') name="supportingMaterials"
}}/> className="input-upload-file-button-property">
</div> <span>{getLangText('Supporting Materials (Optional)')}</span>
<div className="input-upload-file-button-property"> <InputFineuploader
{getLangText('Proof of payment')} fileInputElement={UploadButton}
<UploadFileButton isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
ref="proofOfPaymentForm" setIsUploadReady={() =>{/* So that ReactS3FineUploader is not complaining */}}
createBlobRoutine={this.getCreateBlobRoutine('other_data')} createBlobRoutine={this.getCreateBlobRoutine()}
keyRoutine={{ keyRoutine={{
url: AppConstants.serverUrl + 's3/key/', url: AppConstants.serverUrl + 's3/key/',
fileClass: 'other_data' fileClass: 'other_data'
}} }}
validation={{ validation={{
itemLimit: AppConstants.fineUploader.validation.registerWork.itemLimit, itemLimit: AppConstants.fineUploader.validation.registerWork.itemLimit,
sizeLimit: AppConstants.fineUploader.validation.additionalData.sizeLimit, sizeLimit: AppConstants.fineUploader.validation.additionalData.sizeLimit
allowedExtensions: ['png', 'jpg'] }}
}} location={location}
location={location} fileClassToUpload={{
fileClassToUpload={{ singular: getLangText('Upload'),
singular: getLangText('Upload Screenshot'), plural: getLangText('Upload')
plural: getLangText('Upload Screenshots') }}/>
}}/> </Property>
</div> <Property
name="proofOfPayment"
className="input-upload-file-button-property">
<span>{getLangText('Proof of payment')}</span>
<InputFineuploader
fileInputElement={UploadButton}
isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
setIsUploadReady={() =>{/* So that ReactS3FineUploader is not complaining */}}
createBlobRoutine={this.getCreateBlobRoutine()}
keyRoutine={{
url: AppConstants.serverUrl + 's3/key/',
fileClass: 'other_data'
}}
validation={{
itemLimit: AppConstants.fineUploader.validation.registerWork.itemLimit,
sizeLimit: AppConstants.fineUploader.validation.additionalData.sizeLimit,
allowedExtensions: ['png', 'jpg']
}}
location={location}
fileClassToUpload={{
singular: getLangText('Upload Screenshot'),
plural: getLangText('Upload Screenshots')
}}
required/>
</Property>
</Form>
<Form <Form
buttons={{}} buttons={{}}
className="ascribe-form-bordered"> className="ascribe-form-bordered">
<Property <Property
ref="termsForm"
name="terms" name="terms"
className="ascribe-property-collapsible-toggle" className="ascribe-property-collapsible-toggle"
style={{paddingBottom: 0}}> style={{paddingBottom: 0}}>

View File

@ -56,7 +56,7 @@ const PRRegisterPiece = React.createClass({
const { currentUser } = this.state; const { currentUser } = this.state;
const { location } = this.props; const { location } = this.props;
setDocumentTitle(getLangText('Submission form')); setDocumentTitle(getLangText('Submit to Portfolio Review'));
return ( return (
<Row> <Row>
<Col xs={6}> <Col xs={6}>

View File

@ -508,7 +508,10 @@ fieldset[disabled] .btn-secondary.active {
> pre, > pre,
> select, > select,
> span:not(.glyphicon), > span:not(.glyphicon),
> p,
> p > span,
> textarea { > textarea {
color: $ascribe-dark-blue;
font-family: $ascribe--font; font-family: $ascribe--font;
font-weight: $ascribe--font-weight-light; font-weight: $ascribe--font-weight-light;
} }

View File

@ -30,11 +30,14 @@ $ascribe-red-error: rgb(169, 68, 66);
border-left: 3px solid rgba($ascribe-red-error, 1); border-left: 3px solid rgba($ascribe-red-error, 1);
> div { > div {
> span { > p {
color: rgba($ascribe-red-error, 1); > span {
font-size: .9em; color: rgba($ascribe-red-error, 1);
margin-right: 1em; font-size: .9em;
margin-right: 1em;
}
} }
> input, > input,
> textarea { > textarea {
@ -74,6 +77,7 @@ $ascribe-red-error: rgb(169, 68, 66);
padding-left: 1.5em; padding-left: 1.5em;
padding-right: 1.5em; padding-right: 1.5em;
padding-top: 1em; padding-top: 1em;
padding-bottom: 1em;
text-align: left; text-align: left;
width: 100%; width: 100%;
@ -86,10 +90,13 @@ $ascribe-red-error: rgb(169, 68, 66);
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
} }
> span { > p {
color: rgba(0, 0, 0, .5); height: 20px;
font-size: .9em; margin-bottom: 0;
font-weight: normal; > span {
font-size: .9em;
font-weight: normal;
}
} }
> div { > div {
@ -139,7 +146,7 @@ $ascribe-red-error: rgb(169, 68, 66);
padding: 0; padding: 0;
} }
> textarea{ > textarea {
color: #666; color: #666;
margin-top: 1em; margin-top: 1em;
padding: 0; padding: 0;
@ -221,16 +228,14 @@ $ascribe-red-error: rgb(169, 68, 66);
} }
.input-upload-file-button-property { .input-upload-file-button-property {
background-color: white;
padding: 1em 0 1em 0;
text-align: right; text-align: right;
button { .btn {
font-size: 1em; font-size: 1em;
margin-right: 1em; margin-right: 1em;
} }
span + button { span + .btn {
margin-left: 1em; margin-left: 1em;
} }
} }

View File

@ -35,14 +35,7 @@
form { form {
border-top: none; border-top: none;
}
form:first-child {
border-bottom: none; border-bottom: none;
} }
form + form, form:last-child {
border-bottom: 1px solid rgba(0, 0, 0, .05);
}
} }
} }

View File

@ -178,7 +178,7 @@ $ikono--font: 'Helvetica Neue', 'Helvetica', sans-serif !important;
border: none; border: none;
} }
.ascribe-property > span { .ascribe-property > p > span {
color: white; color: white;
} }